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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits