diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 60c658a8d8f99..6ade7e6ec8a6a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14477,12 +14477,6 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK) return false; - // If we have Unknown pointers we should fail if they are not global values. - if (!(IsGlobalLValue(LHSValue.getLValueBase()) && - IsGlobalLValue(RHSValue.getLValueBase())) && - (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown)) - return false; - // Reject differing bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { @@ -14544,6 +14538,23 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, (LHSValue.Base && isZeroSized(RHSValue))) return DiagComparison( diag::note_constexpr_pointer_comparison_zero_sized); + // A constexpr-unknown reference can be equal to any other lvalue, except + // for variables allocated during constant evaluation. (The "lifetime + // [...] includes the entire constant evaluation", so it has to be + // distinct from anything allocated during constant evaluation.) + // + // Theoretically we could handle other cases, but the standard doesn't say + // what other cases we need to handle; it just says an "equality + // operator where the result is unspecified" isn't a constant expression. + auto AllocatedDuringEval = [](LValue &Value) { + return Value.Base.is() || + Value.getLValueCallIndex(); + }; + if ((LHSValue.AllowConstexprUnknown && !AllocatedDuringEval(RHSValue)) || + (RHSValue.AllowConstexprUnknown && !AllocatedDuringEval(LHSValue))) + return DiagComparison( + diag::note_constexpr_pointer_comparison_unspecified); + // FIXME: Verify both variables are live. return Success(CmpResult::Unequal, E); } diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp index dffb386f530f4..640ac18aad738 100644 --- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp +++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp @@ -319,7 +319,7 @@ namespace casting { } namespace pointer_comparisons { - extern int &extern_n; // interpreter-note 2 {{declared here}} + extern int &extern_n; // interpreter-note 4 {{declared here}} extern int &extern_n2; constexpr int f1(bool b, int& n) { if (b) { @@ -330,14 +330,29 @@ namespace pointer_comparisons { // FIXME: interpreter incorrectly rejects; both sides are the same constexpr-unknown value. static_assert(f1(false, extern_n)); // interpreter-error {{static assertion expression is not an integral constant expression}} \ // interpreter-note {{initializer of 'extern_n' is unknown}} - // FIXME: We should diagnose this: we don't know if the references bind - // to the same object. - static_assert(&extern_n != &extern_n2); // interpreter-error {{static assertion expression is not an integral constant expression}} \ + static_assert(&extern_n != &extern_n2); // expected-error {{static assertion expression is not an integral constant expression}} \ + // nointerpreter-note {{comparison between pointers to unrelated objects '&extern_n' and '&extern_n2' has unspecified value}} \ // interpreter-note {{initializer of 'extern_n' is unknown}} void f2(const int &n) { - // FIXME: We should not diagnose this: the two objects provably have - // different addresses because the lifetime of "n" extends across - // the initialization. - constexpr int x = &x == &n; // nointerpreter-error {{must be initialized by a constant expression}} + // We can prove these two aren't equal, but for now we don't try. + constexpr int x = &x == &n; // nointerpreter-error {{must be initialized by a constant expression}} \ + // nointerpreter-note {{comparison between pointers to unrelated objects '&x' and '&n' has unspecified value}} + // Distinct variables are not equal, even if they're local variables. + constexpr int y = &x == &y; + static_assert(!y); } + constexpr int f3() { + int x; + return &x == &extern_n; // interpreter-note {{initializer of 'extern_n' is unknown}} + } + static_assert(!f3()); // interpreter-error {{static assertion expression is not an integral constant expression}} \ + // interpreter-note {{in call to 'f3()'}} + constexpr int f4() { + int *p = new int; + bool b = p == &extern_n; // interpreter-note {{initializer of 'extern_n' is unknown}} + delete p; + return b; + } + static_assert(!f4()); // interpreter-error {{static assertion expression is not an integral constant expression}} \ + // interpreter-note {{in call to 'f4()'}} }