[clang] fd6baa4 - [clang][ExprConst] Add diagnostics for invalid binary arithmetic (#118475)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 9 02:42:39 PST 2025
Author: Timm Baeder
Date: 2025-01-09T11:42:35+01:00
New Revision: fd6baa477fa13a4b893aeeba7fce92eb6a1f4962
URL: https://github.com/llvm/llvm-project/commit/fd6baa477fa13a4b893aeeba7fce92eb6a1f4962
DIFF: https://github.com/llvm/llvm-project/commit/fd6baa477fa13a4b893aeeba7fce92eb6a1f4962.diff
LOG: [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
https://github.com/llvm/llvm-project/issues/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.
```
Added:
Modified:
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/ExprConstant.cpp
clang/test/AST/ByteCode/builtin-functions.cpp
clang/test/AST/ByteCode/cxx20.cpp
clang/test/AST/ByteCode/functions.cpp
clang/test/AST/ByteCode/literals.cpp
clang/test/CXX/expr/expr.const/p2-0x.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp
clang/test/SemaCXX/constant-expression-cxx14.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index f630698757c5fb..ab432606edbc4c 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
diff erent 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 e220f69b3a4f58..5768bb12ee38e8 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 *>();
const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>();
+
+ 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<AddrLabelExpr>(LHSExpr);
const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
if (!LHSAddrExpr || !RHSAddrExpr)
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 723764010d9a3a..7034e8c56bd620 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 dea4055c531d23..268362ceff635b 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 10bea3a0d017e2..66693a1fd7e32a 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 662823c49cd4a0..3d415a93a392a5 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 67160ba571f33c..df5ce108aca82e 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 {
//
diff erent objects that are not members of the same array or to
diff erent
// functions, or if only one of them is null, the results of p<q, p>q, 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 at -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 0c349333d89d44..c990dc78deb9b3 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
diff erent encodings.
// One of these two comparisons is non-constant, but due to endianness we don't
// know which one.
constexpr bool may_overlap_
diff erent_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 at -2 {{}} expected-note at -1 {{addresses of literals}}
+ // expected-error at -2 {{}} expected-note at -1 {{addresses of potentially overlapping literals}}
}
+constexpr const char *getStr() {
+ return "abc"; // expected-note {{repeated evaluation of the same literal expression can produce
diff erent 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 936d3600953b9a..579883ae52ccee 100644
--- a/clang/test/SemaCXX/constant-expression-cxx14.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp
@@ -1315,7 +1315,7 @@ constexpr bool
diff erent_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 =
diff erent_in_loop();
// expected-error at -1 {{}} expected-note at -1 {{in call}}
More information about the cfe-commits
mailing list