Skip to content

[clang][ExprConst] Add diagnostics for invalid binary arithmetic #118475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
18 changes: 17 additions & 1 deletion clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ByteCode/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ByteCode/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions clang/test/AST/ByteCode/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}



Expand Down
4 changes: 2 additions & 2 deletions clang/test/AST/ByteCode/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,15 @@ 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

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

Expand Down
30 changes: 15 additions & 15 deletions clang/test/CXX/expr/expr.const/p2-0x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 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@-1 {{ordered comparison of function pointers}}
constexpr bool u14 = pf == pg;

Expand Down Expand Up @@ -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;
}
Expand Down
26 changes: 21 additions & 5 deletions clang/test/SemaCXX/constant-expression-cxx11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/constant-expression-cxx14.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
Loading