-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
[clang][ExprConst] Add diagnostics for invalid binary arithmetic #118475
Conversation
@llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) Changes... 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: ./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 int a, b;
constexpr int n = &b - &a is now: ./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.
Full diff: https://github.com/llvm/llvm-project/pull/118475.diff 2 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index f630698757c5fb..4d078fc9ca6bb7 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -91,11 +91,15 @@ 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">;
+def note_constexpr_pointer_arith_unspecified : Note<
+ "arithmetic involving '%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">;
+def note_constexpr_literal_arith : Note<
+ "arithmetic on addresses of literals has unspecified value">;
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 6b5b95aee35522..2aefcd870ebaf8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14548,8 +14548,21 @@ 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 *>();
- if (!LHSExpr || !RHSExpr)
- return Error(E);
+ if (!LHSExpr || !RHSExpr) {
+ std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType());
+ std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType());
+ Info.FFDiag(E, diag::note_constexpr_pointer_arith_unspecified)
+ << LHS << RHS;
+ return false;
+ }
+
+ if (ArePotentiallyOverlappingStringLiterals(Info, LHSValue, RHSValue)) {
+ std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType());
+ std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType());
+ Info.FFDiag(E, diag::note_constexpr_literal_arith) << LHS << RHS;
+ return false;
+ }
+
const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
if (!LHSAddrExpr || !RHSAddrExpr)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I think this is an improvement as-is, but while we're looking at this it'd be useful to make the existing diagnostics (and the new ones that are mirroring them) a little bit more precise about what the problem is.
clang/lib/AST/ExprConstant.cpp
Outdated
return false; | ||
} | ||
|
||
if (ArePotentiallyOverlappingStringLiterals(Info, LHSValue, RHSValue)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be useful -- both here and in the comparison logic -- to detect the case where LHSExpr == RHSExpr
and produce a different diagnostic (or maybe a note attached to the literal_arith
diagnostic) that mentions that repeated evaluation of the same literal can produce different objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I liked the idea and did it for the arithmetic case, but I'm not sure about the order, i.e. the new note is printed after the backtrace of the first error:
./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 potentially overlapping 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 | }());
| ~~~
./array.cpp:72:71: note: repeated evaluation of the same literal expression can produce different objects
72 | constexpr operator ab<g<l<decltype(""[0])>::e>::e>() { return t(""); }
| ^~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's a bit unfortunate. But it's how we order diagnostics in general: the "context" notes for an error (eg, template instantiation, macro expansion) appear between the error diagnostic and its proper notes. It'd be nice to address that in general somehow, but I think the order is correct, because the context notes explain how we got to the immediately preceding diagnostic. (Another weird thing: the "In file included from" context appears before the diagnostic whereas other context appears afterwards.)
I've been wondering if we should split the "note" diagnostic category into "notes" that provide additional information about the diagnostic, and "context" diagnostics that provide additional information about the location of the previous diagnostic. We could then de-emphasize the context diagnostics in some way -- perhaps using a less bright color for them, and perhaps omitting the snippet -- to make it easier to scan from a diagnostic to its notes.
But obviously none of that is in scope for this PR :)
4f58533
to
dd5e19b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I missing something, it seems like several of the new diagnostics don't have tests that cover them.
dd5e19b
to
798c3e6
Compare
Ping |
1 similar comment
Ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a release note?
LGTM otherwise
... between unrelated declarations or literals.
798c3e6
to
9178a28
Compare
... 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:
The output for
is now: