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">, 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/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(E)) { + unsigned ExprIsRHS = true; + const Expr *ExprSide; + + if (isa(Op->getRHS())) { + ExprIsRHS = false; + ExprSide = Op->getLHS(); + } else if (isa(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(ExprSide)) + return; + + if (isa(ExprSide)) + return; + + // Evaluate the expression again + Expr::EvalResult EvalResult; + SmallVector 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(InnerCond) && !isa(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/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/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 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 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/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().f() == 1); // expected-error {{static assertion failed}} + static_assert(A().f() == 1); // expected-error {{static assertion failed}} \ + // expected-note {{left-hand side of operator '==' evaluates to '0'}} #endif } 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 == 1, ""); // expected-note {{in instantiation of}} expected-error {{not an integral constant}} template void f() { - static_assert(a == 0, ""); // expected-error {{static assertion failed due to requirement 'a == 0'}} + static_assert(a == 0, ""); // expected-error {{static assertion failed due to requirement 'a == 0'}} \ + // expected-note {{evaluates to '1'}} } } 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::specialization == 0); - static_assert(trait::specialization == 1); // FIXME expected-error {{failed}} - static_assert(trait::specialization == 2); // FIXME expected-error {{failed}} + static_assert(trait::specialization == 1); // FIXME expected-error {{failed}} \ + // expected-note {{evaluates to '0'}} + static_assert(trait::specialization == 2); // FIXME expected-error {{failed}} \ + // expected-note {{evaluates to '0'}} static_assert(trait::specialization == 0); // FIXME-error {{ambiguous partial specialization}} }