diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index f630698757c5f..ab432606edbc4 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -90,12 +90,18 @@ def note_constexpr_pointer_subtraction_not_same_array : Note< def note_constexpr_pointer_subtraction_zero_size : Note< "subtraction of pointers to type %0 of zero size">; def note_constexpr_pointer_comparison_unspecified : Note< - "comparison between '%0' and '%1' has unspecified value">; + "comparison between pointers to unrelated objects '%0' and '%1' has unspecified value">; +def note_constexpr_pointer_arith_unspecified : Note< + "arithmetic involving unrelated objects '%0' and '%1' has unspecified value">; def note_constexpr_pointer_constant_comparison : Note< "comparison of numeric address '%0' with pointer '%1' can only be performed " "at runtime">; def note_constexpr_literal_comparison : Note< - "comparison of addresses of literals has unspecified value">; + "comparison of addresses of potentially overlapping literals has unspecified value">; +def note_constexpr_literal_arith : Note< + "arithmetic on addresses of potentially overlapping literals has unspecified value">; +def note_constexpr_repeated_literal_eval : Note< + "repeated evaluation of the same literal expression can produce different objects">; def note_constexpr_opaque_call_comparison : Note< "comparison against opaque constant address '%0' can only be performed at " "runtime">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e220f69b3a4f5..5768bb12ee38e 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14588,8 +14588,24 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { return Error(E); const Expr *LHSExpr = LHSValue.Base.dyn_cast(); const Expr *RHSExpr = RHSValue.Base.dyn_cast(); + + auto DiagArith = [&](unsigned DiagID) { + std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType()); + std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType()); + Info.FFDiag(E, DiagID) << LHS << RHS; + if (LHSExpr && LHSExpr == RHSExpr) + Info.Note(LHSExpr->getExprLoc(), + diag::note_constexpr_repeated_literal_eval) + << LHSExpr->getSourceRange(); + return false; + }; + if (!LHSExpr || !RHSExpr) - return Error(E); + return DiagArith(diag::note_constexpr_pointer_arith_unspecified); + + if (ArePotentiallyOverlappingStringLiterals(Info, LHSValue, RHSValue)) + return DiagArith(diag::note_constexpr_literal_arith); + const AddrLabelExpr *LHSAddrExpr = dyn_cast(LHSExpr); const AddrLabelExpr *RHSAddrExpr = dyn_cast(RHSExpr); if (!LHSAddrExpr || !RHSAddrExpr) diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index 723764010d9a3..7034e8c56bd62 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -1010,7 +1010,7 @@ namespace FunctionStart { void a(void) {} static_assert(__builtin_function_start(a) == a, ""); // both-error {{not an integral constant expression}} \ // ref-note {{comparison against opaque constant address '&__builtin_function_start(a)'}} \ - // expected-note {{comparison of addresses of literals has unspecified value}} + // expected-note {{comparison of addresses of potentially overlapping literals has unspecified value}} } namespace BuiltinInImplicitCtor { diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp index dea4055c531d2..268362ceff635 100644 --- a/clang/test/AST/ByteCode/cxx20.cpp +++ b/clang/test/AST/ByteCode/cxx20.cpp @@ -115,7 +115,7 @@ constexpr auto name1() { return "name1"; } constexpr auto name2() { return "name2"; } constexpr auto b3 = name1() == name1(); // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{comparison of addresses of literals}} + // ref-note {{comparison of addresses of potentially overlapping literals}} constexpr auto b4 = name1() == name2(); static_assert(!b4); diff --git a/clang/test/AST/ByteCode/functions.cpp b/clang/test/AST/ByteCode/functions.cpp index 10bea3a0d017e..66693a1fd7e32 100644 --- a/clang/test/AST/ByteCode/functions.cpp +++ b/clang/test/AST/ByteCode/functions.cpp @@ -208,11 +208,11 @@ namespace Comparison { constexpr bool u13 = pf < pg; // both-warning {{ordered comparison of function pointers}} \ // both-error {{must be initialized by a constant expression}} \ - // both-note {{comparison between '&f' and '&g' has unspecified value}} + // both-note {{comparison between pointers to unrelated objects '&f' and '&g' has unspecified value}} constexpr bool u14 = pf < (void(*)())nullptr; // both-warning {{ordered comparison of function pointers}} \ // both-error {{must be initialized by a constant expression}} \ - // both-note {{comparison between '&f' and 'nullptr' has unspecified value}} + // both-note {{comparison between pointers to unrelated objects '&f' and 'nullptr' has unspecified value}} diff --git a/clang/test/AST/ByteCode/literals.cpp b/clang/test/AST/ByteCode/literals.cpp index 662823c49cd4a..3d415a93a392a 100644 --- a/clang/test/AST/ByteCode/literals.cpp +++ b/clang/test/AST/ByteCode/literals.cpp @@ -194,7 +194,7 @@ namespace PointerComparison { constexpr void *qv = (void*)&s.b; constexpr bool v1 = null < (int*)0; constexpr bool v2 = null < pv; // both-error {{must be initialized by a constant expression}} \ - // both-note {{comparison between 'nullptr' and '&s.a' has unspecified value}} + // both-note {{comparison between pointers to unrelated objects 'nullptr' and '&s.a' has unspecified value}} constexpr bool v3 = null == pv; // ok constexpr bool v4 = qv == pv; // ok @@ -202,7 +202,7 @@ namespace PointerComparison { constexpr bool v5 = qv >= pv; constexpr bool v8 = qv > (void*)&s.a; constexpr bool v6 = qv > null; // both-error {{must be initialized by a constant expression}} \ - // both-note {{comparison between '&s.b' and 'nullptr' has unspecified value}} + // both-note {{comparison between pointers to unrelated objects '&s.b' and 'nullptr' has unspecified value}} constexpr bool v7 = qv <= (void*)&s.b; // ok diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 67160ba571f33..df5ce108aca82 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -510,22 +510,22 @@ namespace UnspecifiedRelations { // different objects that are not members of the same array or to different // functions, or if only one of them is null, the results of pq, p<=q, // and p>=q are unspecified. - constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} - constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} - constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} - constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} - constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} - constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} - constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} - constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} - constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} - constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} - constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} - constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} + constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}} + constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}} + constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}} + constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}} + constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}} + constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}} + constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}} + constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}} + constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}} + constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}} + constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}} + constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}} void f(), g(); constexpr void (*pf)() = &f, (*pg)() = &g; - constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison between '&f' and '&g' has unspecified value}} + constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&f' and '&g' has unspecified value}} // expected-warning@-1 {{ordered comparison of function pointers}} constexpr bool u14 = pf == pg; @@ -578,11 +578,11 @@ namespace UnspecifiedRelations { constexpr void *pv = (void*)&s.a; constexpr void *qv = (void*)&s.b; constexpr bool v1 = null < (int*)0; - constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&s.a' has unspecified value}} + constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&s.a' has unspecified value}} constexpr bool v3 = null == pv; constexpr bool v4 = qv == pv; constexpr bool v5 = qv >= pv; - constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison between '&s.b' and 'nullptr' has unspecified value}} + constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&s.b' and 'nullptr' has unspecified value}} constexpr bool v7 = qv <= (void*)&s.b; constexpr bool v8 = qv > (void*)&s.a; } diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 0c349333d89d4..c990dc78deb9b 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -380,20 +380,36 @@ static_assert(string == string, ""); static_assert(string == also_string, ""); // These strings may overlap, and so the result of the comparison is unknown. -constexpr bool may_overlap_1 = +"foo" == +"foo"; // expected-error {{}} expected-note {{addresses of literals}} -constexpr bool may_overlap_2 = +"foo" == +"foo\0bar"; // expected-error {{}} expected-note {{addresses of literals}} -constexpr bool may_overlap_3 = +"foo" == "bar\0foo" + 4; // expected-error {{}} expected-note {{addresses of literals}} -constexpr bool may_overlap_4 = "xfoo" + 1 == "xfoo" + 1; // expected-error {{}} expected-note {{addresses of literals}} +constexpr bool may_overlap_1 = +"foo" == +"foo"; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}} +constexpr bool may_overlap_2 = +"foo" == +"foo\0bar"; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}} +constexpr bool may_overlap_3 = +"foo" == "bar\0foo" + 4; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}} +constexpr bool may_overlap_4 = "xfoo" + 1 == "xfoo" + 1; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}} // These may overlap even though they have different encodings. // One of these two comparisons is non-constant, but due to endianness we don't // know which one. constexpr bool may_overlap_different_encoding[] = {fold((const char*)u"A" != (const char*)"xA\0\0\0x" + 1), fold((const char*)u"A" != (const char*)"x\0A\0\0x" + 1)}; - // expected-error@-2 {{}} expected-note@-1 {{addresses of literals}} + // expected-error@-2 {{}} expected-note@-1 {{addresses of potentially overlapping literals}} } +constexpr const char *getStr() { + return "abc"; // expected-note {{repeated evaluation of the same literal expression can produce different objects}} +} +constexpr int strMinus() { + (void)(getStr() - getStr()); // expected-note {{arithmetic on addresses of potentially overlapping literals has unspecified value}} \ + // cxx11-warning {{C++14 extension}} + return 0; +} +static_assert(strMinus() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} + +constexpr int a = 0; +constexpr int b = 1; +constexpr int n = &b - &a; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{arithmetic involving unrelated objects '&b' and '&a' has unspecified value}} + namespace MaterializeTemporary { constexpr int f(const int &r) { return r; } diff --git a/clang/test/SemaCXX/constant-expression-cxx14.cpp b/clang/test/SemaCXX/constant-expression-cxx14.cpp index 936d3600953b9..579883ae52cce 100644 --- a/clang/test/SemaCXX/constant-expression-cxx14.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp @@ -1315,7 +1315,7 @@ constexpr bool different_in_loop(bool b = false) { const char *p[2] = {}; for (const char *&r : p) r = "hello"; - return p[0] == p[1]; // expected-note {{addresses of literals}} + return p[0] == p[1]; // expected-note {{addresses of potentially overlapping literals}} } constexpr bool check = different_in_loop(); // expected-error@-1 {{}} expected-note@-1 {{in call}}