tbaeder created this revision.
tbaeder added a reviewer: aaron.ballman.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
For
c++
static constexpr m = 10;
static_assert(m == 11);
the output is before:
./test.cpp:2:1: error: static_assert failed due to requirement 'm == 11'
static_assert(m == 11);
^ ~~~~~~~
1 error generated.
and after:
./test.cpp:2:1: error: static assertion failed due to requirement 'm == 11'
static_assert(m == 11);
^ ~~~~~~~
./test.cpp:2:15: note: left-hand side of operator '==' evaluates to '10'
static_assert(m == 11);
^
1 error generated.
The patch adds a new function `Sema::DiagnoseStaticAssertDetails()`, which
currently only handles binary operators with an integer literal on one side and
a non-integer literal on the other side. This is intentionally kept simple, but
can be improved incrementally over time of course.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D130894
Files:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaDeclCXX.cpp
clang/test/CXX/drs/dr7xx.cpp
clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
clang/test/PCH/cxx-templates.cpp
clang/test/SemaTemplate/instantiate-var-template.cpp
clang/test/SemaTemplate/instantiation-dependence.cpp
Index: clang/test/SemaTemplate/instantiation-dependence.cpp
===================================================================
--- clang/test/SemaTemplate/instantiation-dependence.cpp
+++ clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -68,8 +68,10 @@
struct D : B, C {};
static_assert(trait<A>::specialization == 0);
- static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}}
- static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}}
+ static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}} \
+ // expected-note {{evaluates to '0'}}
+ static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}} \
+ // expected-note {{evaluates to '0'}}
static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous partial specialization}}
}
Index: clang/test/SemaTemplate/instantiate-var-template.cpp
===================================================================
--- clang/test/SemaTemplate/instantiate-var-template.cpp
+++ clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -31,7 +31,8 @@
static_assert(b<char> == 1, ""); // expected-note {{in instantiation of}} expected-error {{not an integral constant}}
template<typename T> void f() {
- static_assert(a<sizeof(sizeof(f(T())))> == 0, ""); // expected-error {{static assertion failed due to requirement 'a<sizeof (sizeof (f(type-parameter-0-0())))> == 0'}}
+ static_assert(a<sizeof(sizeof(f(T())))> == 0, ""); // expected-error {{static assertion failed due to requirement 'a<sizeof (sizeof (f(type-parameter-0-0())))> == 0'}} \
+ // expected-note {{evaluates to '1'}}
}
}
Index: clang/test/PCH/cxx-templates.cpp
===================================================================
--- clang/test/PCH/cxx-templates.cpp
+++ clang/test/PCH/cxx-templates.cpp
@@ -167,7 +167,8 @@
// This used to mark 'f' invalid without producing any diagnostic. That's a
// little hard to detect, but we can make sure that constexpr evaluation
// fails when it should.
- static_assert(A<int>().f() == 1); // expected-error {{static assertion failed}}
+ static_assert(A<int>().f() == 1); // expected-error {{static assertion failed}} \
+ // expected-note {{left-hand side of operator '==' evaluates to '0'}}
#endif
}
Index: clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
+++ clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
@@ -40,8 +40,10 @@
template<int ...a> constexpr auto x = [...z = a] (auto F) { return F(z...); };
static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 123);
-static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}}
+static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}} \
+ // expected-note {{evaluates to '123'}}
template<int ...a> constexpr auto y = [z = a...] (auto F) { return F(z...); }; // expected-error {{must appear before the name of the capture}}
static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 123);
-static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}}
+static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}} \
+ // expected-note {{evaluates to '123'}}
Index: clang/test/CXX/drs/dr7xx.cpp
===================================================================
--- clang/test/CXX/drs/dr7xx.cpp
+++ clang/test/CXX/drs/dr7xx.cpp
@@ -178,7 +178,8 @@
static_assert(B<0>().v<1> == 3, "");
static_assert(B<0>().v<0> == 4, "");
#if __cplusplus < 201702L
- // expected-error@-2 {{failed}}
+ // expected-error@-2 {{failed}} \
+ // expected-note@-2 {{left-hand side of operator '==' evaluates to '2'}}
#endif
static_assert(B<1>().w<1> == 1, "");
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -16554,6 +16554,50 @@
AssertMessage, RParenLoc, false);
}
+/// Try to print more useful information about a failed static_assert
+/// with expression \E
+void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
+ if (const BinaryOperator *Op = dyn_cast_or_null<BinaryOperator>(E)) {
+ unsigned ExprIsRHS = true;
+ const Expr *ExprSide;
+
+ if (isa<IntegerLiteral>(Op->getRHS())) {
+ ExprIsRHS = false;
+ ExprSide = Op->getLHS();
+ } else if (isa<IntegerLiteral>(Op->getLHS())) {
+ ExprIsRHS = true;
+ ExprSide = Op->getRHS();
+ } else {
+ return;
+ }
+
+ // We already print enough info about these earlier,
+ // e.g. "requirement '2 == 1' failed" where 2 is
+ // a substituted template parameter.
+ if (isa<SubstNonTypeTemplateParmExpr>(ExprSide))
+ return;
+
+ if (isa<IntegerLiteral>(ExprSide))
+ return;
+
+ // Evaluate the expression again
+ Expr::EvalResult EvalResult;
+ SmallVector<PartialDiagnosticAt, 0> Notes;
+ EvalResult.Diag = &Notes;
+ ExprSide->EvaluateAsRValue(EvalResult, Context, true);
+
+ SmallString<12> ValueString;
+ if (EvalResult.Val.isInt()) {
+ EvalResult.Val.getInt().toString(ValueString);
+ } else {
+ assert(false);
+ }
+
+ Diag(ExprSide->getExprLoc(), diag::note_bin_op_evaluates)
+ << (unsigned)ExprIsRHS << Op->getOpcodeStr() << ValueString;
+ }
+}
+
Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
Expr *AssertExpr,
StringLiteral *AssertMessage,
@@ -16609,9 +16653,11 @@
DiagnoseUnsatisfiedConstraint(Satisfaction);
} else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)
&& !isa<IntegerLiteral>(InnerCond)) {
+
Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)
<< InnerCondDescription << !AssertMessage
<< Msg.str() << InnerCond->getSourceRange();
+ DiagnoseStaticAssertDetails(InnerCond);
} else {
Diag(StaticAssertLoc, diag::err_static_assert_failed)
<< !AssertMessage << Msg.str() << AssertExpr->getSourceRange();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -7482,6 +7482,7 @@
StringLiteral *AssertMessageExpr,
SourceLocation RParenLoc,
bool Failed);
+ void DiagnoseStaticAssertDetails(const Expr *E);
FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart,
SourceLocation FriendLoc,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1532,6 +1532,8 @@
def err_static_assert_failed : Error<"static assertion failed%select{: %1|}0">;
def err_static_assert_requirement_failed : Error<
"static assertion failed due to requirement '%0'%select{: %2|}1">;
+def note_bin_op_evaluates : Note<
+ "%select{left-hand|right-hand}0 side of operator '%1' evaluates to '%2'">;
def warn_consteval_if_always_true : Warning<
"consteval if is always true in an %select{unevaluated|immediate}0 context">,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits