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,48 @@ 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()->IgnoreParenImpCasts())) { + ExprIsRHS = false; + ExprSide = Op->getLHS(); + } else if (isa(Op->getLHS()->IgnoreParenImpCasts())) { + 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 + return; + + Diag(ExprSide->getExprLoc(), diag::note_bin_op_evaluates) + << (unsigned)ExprIsRHS << Op->getOpcodeStr() << ValueString; + } +} + Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, StringLiteral *AssertMessage, @@ -16612,6 +16654,7 @@ 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/dcl.decl/dcl.meaning/dcl.array/p3.cpp =================================================================== --- clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp +++ clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp @@ -98,7 +98,8 @@ static_assert(sizeof(arr2) == 12, ""); // Use a failing test to ensure the type isn't considered dependent. - static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}} + static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to '12'}} } void g() { f(); } // expected-note {{in instantiation of}} 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/Parser/objc-static-assert.mm =================================================================== --- clang/test/Parser/objc-static-assert.mm +++ clang/test/Parser/objc-static-assert.mm @@ -26,7 +26,8 @@ static_assert(a, ""); // expected-error {{static assertion expression is not an integral constant expression}} static_assert(sizeof(a) == 4, ""); - static_assert(sizeof(a) == 3, ""); // expected-error {{static assertion failed}} + static_assert(sizeof(a) == 3, ""); // expected-error {{static assertion failed}} \ + // expected-note {{evaluates to '4'}} } static_assert(1, ""); @@ -40,7 +41,8 @@ static_assert(1, ""); _Static_assert(1, ""); static_assert(sizeof(b) == 4, ""); - static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} + static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \ + // expected-note {{evaluates to '4'}} } static_assert(1, ""); @@ -56,7 +58,8 @@ @interface B () { int b; static_assert(sizeof(b) == 4, ""); - static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} + static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \ + // expected-note {{evaluates to '4'}} } @end Index: clang/test/SemaCXX/static-assert-cxx17.cpp =================================================================== --- clang/test/SemaCXX/static-assert-cxx17.cpp +++ clang/test/SemaCXX/static-assert-cxx17.cpp @@ -88,7 +88,8 @@ static_assert(typename T::T(0)); // expected-error@-1{{static assertion failed due to requirement 'int(0)'}} static_assert(sizeof(X) == 0); - // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} + // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} \ + // expected-note@-1 {{evaluates to '8'}} static_assert((const X *)nullptr); // expected-error@-1{{static assertion failed due to requirement '(const X *)nullptr'}} static_assert(static_cast *>(nullptr)); @@ -96,7 +97,8 @@ static_assert((const X[]){} == nullptr); // expected-error@-1{{static assertion failed due to requirement '(const X[0]){} == nullptr'}} static_assert(sizeof(X().X::~X())>) == 0); - // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} + // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} \ + // expected-note@-1 {{evaluates to '8'}} static_assert(constexpr_return_false()); // expected-error@-1{{static assertion failed due to requirement 'constexpr_return_false()'}} } 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}} }