diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bd7a4b20242fd..2add72a1654b1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -967,6 +967,7 @@ Bug Fixes to C++ Support - Fix a crash with NTTP when instantiating local class. - Fixed a crash involving list-initialization of an empty class with a non-empty initializer list. (#GH147949) +- Fixed constant evaluation of equality comparisons of constexpr-unknown references. (#GH147663) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 419dd5dbdc695..1b33b6706e204 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14478,12 +14478,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)) { @@ -14545,6 +14539,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, (LHSValue.Base && isZeroSized(RHSValue))) return DiagComparison( diag::note_constexpr_pointer_comparison_zero_sized); + if (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown) + 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-cxx14.cpp b/clang/test/SemaCXX/constant-expression-cxx14.cpp index e16a69df3830d..e93b98c185a82 100644 --- a/clang/test/SemaCXX/constant-expression-cxx14.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp @@ -1321,3 +1321,18 @@ constexpr bool check = different_in_loop(); // expected-error@-1 {{}} expected-note@-1 {{in call}} } + +namespace comparison_dead_variable { + constexpr bool f() { + int *p1 = 0, *p2 = 0; + { + int x = 0; p1 = &x; + } + { + int x = 0; p2 = &x; + } + return p1 != p2; + } + // FIXME: This should fail. + static_assert(f(),""); +} diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp index dffb386f530f4..03fea91169787 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,30 @@ 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}} + 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; // nointerpreter-note {{comparison between pointers to unrelated objects '&x' and '&extern_n' has unspecified value}} \ + // interpreter-note {{initializer of 'extern_n' is unknown}} + } + static_assert(!f3()); // expected-error {{static assertion expression is not an integral constant expression}} \ + // expected-note {{in call to 'f3()'}} + constexpr int f4() { + int *p = new int; + bool b = p == &extern_n; // nointerpreter-note {{comparison between pointers to unrelated objects '&{*new int#0}' and '&extern_n' has unspecified value}} \ + // interpreter-note {{initializer of 'extern_n' is unknown}} + delete p; + return b; + } + static_assert(!f4()); // expected-error {{static assertion expression is not an integral constant expression}} \ + // expected-note {{in call to 'f4()'}} }