Skip to content

Commit fd6baa4

Browse files
authored
[clang][ExprConst] Add diagnostics for invalid binary arithmetic (#118475)
... between unrelated declarations or literals. Leaving this small (I haven't run the whole test suite locally) to get some feedback on the wording and implementation first. The output of the sample in #117409 is now: ```console ./array.cpp:57:6: warning: expression result unused [-Wunused-value] 57 | am - aj.af(); | ~~ ^ ~~~~~~~ ./array.cpp:70:8: error: call to consteval function 'L::L<bx>' is not a constant expression 70 | q(0, [] { | ^ ./array.cpp:57:6: note: arithmetic on addresses of literals has unspecified value 57 | am - aj.af(); | ^ ./array.cpp:62:5: note: in call to 'al(&""[0], {&""[0]})' 62 | al(bp.af(), k); | ^~~~~~~~~~~~~~ ./array.cpp:70:8: note: in call to 'L<bx>({})' 70 | q(0, [] { | ^~~~ 71 | struct bx { | ~~~~~~~~~~~ 72 | constexpr operator ab<g<l<decltype(""[0])>::e>::e>() { return t(""); } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | }; | ~~ 74 | return bx(); | ~~~~~~~~~~~~ 75 | }()); | ~~~ ``` The output for ```c++ int a, b; constexpr int n = &b - &a ``` is now: ```console ./array.cpp:80:15: error: constexpr variable 'n' must be initialized by a constant expression 80 | constexpr int n = &b - &a; | ^ ~~~~~~~ ./array.cpp:80:22: note: arithmetic involving '&b' and '&a' has unspecified value 80 | constexpr int n = &b - &a; | ^ 1 error generated. ```
1 parent b79ed87 commit fd6baa4

File tree

9 files changed

+68
-30
lines changed

9 files changed

+68
-30
lines changed

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,18 @@ def note_constexpr_pointer_subtraction_not_same_array : Note<
9090
def note_constexpr_pointer_subtraction_zero_size : Note<
9191
"subtraction of pointers to type %0 of zero size">;
9292
def note_constexpr_pointer_comparison_unspecified : Note<
93-
"comparison between '%0' and '%1' has unspecified value">;
93+
"comparison between pointers to unrelated objects '%0' and '%1' has unspecified value">;
94+
def note_constexpr_pointer_arith_unspecified : Note<
95+
"arithmetic involving unrelated objects '%0' and '%1' has unspecified value">;
9496
def note_constexpr_pointer_constant_comparison : Note<
9597
"comparison of numeric address '%0' with pointer '%1' can only be performed "
9698
"at runtime">;
9799
def note_constexpr_literal_comparison : Note<
98-
"comparison of addresses of literals has unspecified value">;
100+
"comparison of addresses of potentially overlapping literals has unspecified value">;
101+
def note_constexpr_literal_arith : Note<
102+
"arithmetic on addresses of potentially overlapping literals has unspecified value">;
103+
def note_constexpr_repeated_literal_eval : Note<
104+
"repeated evaluation of the same literal expression can produce different objects">;
99105
def note_constexpr_opaque_call_comparison : Note<
100106
"comparison against opaque constant address '%0' can only be performed at "
101107
"runtime">;

clang/lib/AST/ExprConstant.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14588,8 +14588,24 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
1458814588
return Error(E);
1458914589
const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr *>();
1459014590
const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>();
14591+
14592+
auto DiagArith = [&](unsigned DiagID) {
14593+
std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType());
14594+
std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType());
14595+
Info.FFDiag(E, DiagID) << LHS << RHS;
14596+
if (LHSExpr && LHSExpr == RHSExpr)
14597+
Info.Note(LHSExpr->getExprLoc(),
14598+
diag::note_constexpr_repeated_literal_eval)
14599+
<< LHSExpr->getSourceRange();
14600+
return false;
14601+
};
14602+
1459114603
if (!LHSExpr || !RHSExpr)
14592-
return Error(E);
14604+
return DiagArith(diag::note_constexpr_pointer_arith_unspecified);
14605+
14606+
if (ArePotentiallyOverlappingStringLiterals(Info, LHSValue, RHSValue))
14607+
return DiagArith(diag::note_constexpr_literal_arith);
14608+
1459314609
const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
1459414610
const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
1459514611
if (!LHSAddrExpr || !RHSAddrExpr)

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ namespace FunctionStart {
10101010
void a(void) {}
10111011
static_assert(__builtin_function_start(a) == a, ""); // both-error {{not an integral constant expression}} \
10121012
// ref-note {{comparison against opaque constant address '&__builtin_function_start(a)'}} \
1013-
// expected-note {{comparison of addresses of literals has unspecified value}}
1013+
// expected-note {{comparison of addresses of potentially overlapping literals has unspecified value}}
10141014
}
10151015

10161016
namespace BuiltinInImplicitCtor {

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ constexpr auto name1() { return "name1"; }
115115
constexpr auto name2() { return "name2"; }
116116

117117
constexpr auto b3 = name1() == name1(); // ref-error {{must be initialized by a constant expression}} \
118-
// ref-note {{comparison of addresses of literals}}
118+
// ref-note {{comparison of addresses of potentially overlapping literals}}
119119
constexpr auto b4 = name1() == name2();
120120
static_assert(!b4);
121121

clang/test/AST/ByteCode/functions.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,11 @@ namespace Comparison {
208208

209209
constexpr bool u13 = pf < pg; // both-warning {{ordered comparison of function pointers}} \
210210
// both-error {{must be initialized by a constant expression}} \
211-
// both-note {{comparison between '&f' and '&g' has unspecified value}}
211+
// both-note {{comparison between pointers to unrelated objects '&f' and '&g' has unspecified value}}
212212

213213
constexpr bool u14 = pf < (void(*)())nullptr; // both-warning {{ordered comparison of function pointers}} \
214214
// both-error {{must be initialized by a constant expression}} \
215-
// both-note {{comparison between '&f' and 'nullptr' has unspecified value}}
215+
// both-note {{comparison between pointers to unrelated objects '&f' and 'nullptr' has unspecified value}}
216216

217217

218218

clang/test/AST/ByteCode/literals.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,15 @@ namespace PointerComparison {
194194
constexpr void *qv = (void*)&s.b;
195195
constexpr bool v1 = null < (int*)0;
196196
constexpr bool v2 = null < pv; // both-error {{must be initialized by a constant expression}} \
197-
// both-note {{comparison between 'nullptr' and '&s.a' has unspecified value}}
197+
// both-note {{comparison between pointers to unrelated objects 'nullptr' and '&s.a' has unspecified value}}
198198

199199
constexpr bool v3 = null == pv; // ok
200200
constexpr bool v4 = qv == pv; // ok
201201

202202
constexpr bool v5 = qv >= pv;
203203
constexpr bool v8 = qv > (void*)&s.a;
204204
constexpr bool v6 = qv > null; // both-error {{must be initialized by a constant expression}} \
205-
// both-note {{comparison between '&s.b' and 'nullptr' has unspecified value}}
205+
// both-note {{comparison between pointers to unrelated objects '&s.b' and 'nullptr' has unspecified value}}
206206

207207
constexpr bool v7 = qv <= (void*)&s.b; // ok
208208

clang/test/CXX/expr/expr.const/p2-0x.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -510,22 +510,22 @@ namespace UnspecifiedRelations {
510510
// different objects that are not members of the same array or to different
511511
// functions, or if only one of them is null, the results of p<q, p>q, p<=q,
512512
// and p>=q are unspecified.
513-
constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}}
514-
constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}}
515-
constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}}
516-
constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}}
517-
constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}}
518-
constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}}
519-
constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}}
520-
constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}}
521-
constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}}
522-
constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}}
523-
constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}}
524-
constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}}
513+
constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}}
514+
constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}}
515+
constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}}
516+
constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and '&b' has unspecified value}}
517+
constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}}
518+
constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}}
519+
constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}}
520+
constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&a' and 'nullptr' has unspecified value}}
521+
constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}}
522+
constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}}
523+
constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}}
524+
constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&b' has unspecified value}}
525525
void f(), g();
526526

527527
constexpr void (*pf)() = &f, (*pg)() = &g;
528-
constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison between '&f' and '&g' has unspecified value}}
528+
constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&f' and '&g' has unspecified value}}
529529
// expected-warning@-1 {{ordered comparison of function pointers}}
530530
constexpr bool u14 = pf == pg;
531531

@@ -578,11 +578,11 @@ namespace UnspecifiedRelations {
578578
constexpr void *pv = (void*)&s.a;
579579
constexpr void *qv = (void*)&s.b;
580580
constexpr bool v1 = null < (int*)0;
581-
constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&s.a' has unspecified value}}
581+
constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects 'nullptr' and '&s.a' has unspecified value}}
582582
constexpr bool v3 = null == pv;
583583
constexpr bool v4 = qv == pv;
584584
constexpr bool v5 = qv >= pv;
585-
constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison between '&s.b' and 'nullptr' has unspecified value}}
585+
constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison between pointers to unrelated objects '&s.b' and 'nullptr' has unspecified value}}
586586
constexpr bool v7 = qv <= (void*)&s.b;
587587
constexpr bool v8 = qv > (void*)&s.a;
588588
}

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -380,20 +380,36 @@ static_assert(string == string, "");
380380
static_assert(string == also_string, "");
381381

382382
// These strings may overlap, and so the result of the comparison is unknown.
383-
constexpr bool may_overlap_1 = +"foo" == +"foo"; // expected-error {{}} expected-note {{addresses of literals}}
384-
constexpr bool may_overlap_2 = +"foo" == +"foo\0bar"; // expected-error {{}} expected-note {{addresses of literals}}
385-
constexpr bool may_overlap_3 = +"foo" == "bar\0foo" + 4; // expected-error {{}} expected-note {{addresses of literals}}
386-
constexpr bool may_overlap_4 = "xfoo" + 1 == "xfoo" + 1; // expected-error {{}} expected-note {{addresses of literals}}
383+
constexpr bool may_overlap_1 = +"foo" == +"foo"; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}}
384+
constexpr bool may_overlap_2 = +"foo" == +"foo\0bar"; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}}
385+
constexpr bool may_overlap_3 = +"foo" == "bar\0foo" + 4; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}}
386+
constexpr bool may_overlap_4 = "xfoo" + 1 == "xfoo" + 1; // expected-error {{}} expected-note {{addresses of potentially overlapping literals}}
387387

388388
// These may overlap even though they have different encodings.
389389
// One of these two comparisons is non-constant, but due to endianness we don't
390390
// know which one.
391391
constexpr bool may_overlap_different_encoding[] =
392392
{fold((const char*)u"A" != (const char*)"xA\0\0\0x" + 1), fold((const char*)u"A" != (const char*)"x\0A\0\0x" + 1)};
393-
// expected-error@-2 {{}} expected-note@-1 {{addresses of literals}}
393+
// expected-error@-2 {{}} expected-note@-1 {{addresses of potentially overlapping literals}}
394394

395395
}
396396

397+
constexpr const char *getStr() {
398+
return "abc"; // expected-note {{repeated evaluation of the same literal expression can produce different objects}}
399+
}
400+
constexpr int strMinus() {
401+
(void)(getStr() - getStr()); // expected-note {{arithmetic on addresses of potentially overlapping literals has unspecified value}} \
402+
// cxx11-warning {{C++14 extension}}
403+
return 0;
404+
}
405+
static_assert(strMinus() == 0, ""); // expected-error {{not an integral constant expression}} \
406+
// expected-note {{in call to}}
407+
408+
constexpr int a = 0;
409+
constexpr int b = 1;
410+
constexpr int n = &b - &a; // expected-error {{must be initialized by a constant expression}} \
411+
// expected-note {{arithmetic involving unrelated objects '&b' and '&a' has unspecified value}}
412+
397413
namespace MaterializeTemporary {
398414

399415
constexpr int f(const int &r) { return r; }

clang/test/SemaCXX/constant-expression-cxx14.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,7 @@ constexpr bool different_in_loop(bool b = false) {
13151315
const char *p[2] = {};
13161316
for (const char *&r : p)
13171317
r = "hello";
1318-
return p[0] == p[1]; // expected-note {{addresses of literals}}
1318+
return p[0] == p[1]; // expected-note {{addresses of potentially overlapping literals}}
13191319
}
13201320
constexpr bool check = different_in_loop();
13211321
// expected-error@-1 {{}} expected-note@-1 {{in call}}

0 commit comments

Comments
 (0)