diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1532,6 +1532,7 @@ 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_static_assert_requirement_context : Note<"with '%0' equal to %1">; def note_expr_evaluates_to : Note< "expression evaluates to '%0 %1 %2'">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3980,7 +3980,9 @@ /// Find the failed Boolean condition within a given Boolean /// constant expression, and describe it with a string. - std::pair findFailedBooleanCondition(Expr *Cond); + std::pair + findFailedBooleanCondition(Expr *Cond, + SmallVectorImpl &Notes); /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any /// non-ArgDependent DiagnoseIfAttrs. @@ -7502,7 +7504,9 @@ StringLiteral *AssertMessageExpr, SourceLocation RParenLoc, bool Failed); - void DiagnoseStaticAssertDetails(const Expr *E); + void + DiagnoseFailedBoolExprDetails(const Expr *E, + SmallVectorImpl &Notes); FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart, SourceLocation FriendLoc, diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16670,8 +16670,9 @@ } /// Try to print more useful information about a failed static_assert -/// with expression \E -void Sema::DiagnoseStaticAssertDetails(const Expr *E) { +/// with expression \p E and if found append the diagnostic to \p Notes. +void Sema::DiagnoseFailedBoolExprDetails( + const Expr *E, SmallVectorImpl &Notes) { if (const auto *Op = dyn_cast_or_null(E)) { const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); @@ -16701,9 +16702,11 @@ DiagSide[I].Result.Val, Side->getType(), DiagSide[I].ValueString); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Notes.emplace_back(Op->getExprLoc(), PDiag(diag::note_expr_evaluates_to) + << DiagSide[0].ValueString + << Op->getOpcodeStr() + << DiagSide[1].ValueString + << Op->getSourceRange()); } } } @@ -16751,8 +16754,9 @@ Expr *InnerCond = nullptr; std::string InnerCondDescription; + SmallVector Notes; std::tie(InnerCond, InnerCondDescription) = - findFailedBooleanCondition(Converted.get()); + findFailedBooleanCondition(Converted.get(), Notes); if (InnerCond && isa(InnerCond)) { // Drill down into concept specialization expressions to see why they // weren't satisfied. @@ -16766,11 +16770,12 @@ 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(); } + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); Failed = true; } } else { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3631,8 +3631,10 @@ // for actual types. class FailedBooleanConditionPrinterHelper : public PrinterHelper { public: - explicit FailedBooleanConditionPrinterHelper(const PrintingPolicy &P) - : Policy(P) {} + explicit FailedBooleanConditionPrinterHelper( + const PrintingPolicy &P, Sema &S, + SmallVectorImpl &Notes) + : Policy(P), S(S), Notes(Notes) {} bool handledStmt(Stmt *E, raw_ostream &OS) override { const auto *DR = dyn_cast(E); @@ -3651,17 +3653,35 @@ } return true; } + + if (auto *UE = dyn_cast(E)) { + Expr::EvalResult Result; + if (UE->EvaluateAsConstantExpr(Result, S.Context) && Result.Val.isInt()) { + std::string ExprStr; + llvm::raw_string_ostream ExprStream(ExprStr); + UE->printPretty(ExprStream, nullptr, Policy); + ExprStream.flush(); + Notes.push_back(PartialDiagnosticAt( + UE->getExprLoc(), + S.PDiag(diag::note_static_assert_requirement_context) + << ExprStr << toString(Result.Val.getInt(), 10) + << UE->getSourceRange())); + } + } return false; } private: const PrintingPolicy Policy; + Sema &S; + SmallVectorImpl &Notes; }; } // end anonymous namespace std::pair -Sema::findFailedBooleanCondition(Expr *Cond) { +Sema::findFailedBooleanCondition(Expr *Cond, + SmallVectorImpl &Notes) { Cond = lookThroughRangesV3Condition(PP, Cond); // Separate out all of the terms in a conjunction. @@ -3693,12 +3713,15 @@ if (!FailedCond) FailedCond = Cond->IgnoreParenImpCasts(); + // Create an "evaluates to" note for complex binary expressions. + DiagnoseFailedBoolExprDetails(FailedCond, Notes); + std::string Description; { llvm::raw_string_ostream Out(Description); PrintingPolicy Policy = getPrintingPolicy(); Policy.PrintCanonicalTypes = true; - FailedBooleanConditionPrinterHelper Helper(Policy); + FailedBooleanConditionPrinterHelper Helper(Policy, *this, Notes); FailedCond->printPretty(Out, &Helper, Policy, 0, "\n", nullptr); } return { FailedCond, Description }; @@ -3786,8 +3809,10 @@ == TemplateArgument::Expression) { Expr *FailedCond; std::string FailedDescription; + SmallVector Notes; std::tie(FailedCond, FailedDescription) = - findFailedBooleanCondition(TemplateArgs[0].getSourceExpression()); + findFailedBooleanCondition( + TemplateArgs[0].getSourceExpression(), Notes); // Remove the old SFINAE diagnostic. PartialDiagnosticAt OldDiag = @@ -3801,6 +3826,8 @@ PDiag(diag::err_typename_nested_not_found_requirement) << FailedDescription << FailedCond->getSourceRange()); + for (const PartialDiagnosticAt &Note : Notes) + (*DeductionInfo)->addSFINAEDiagnostic(Note.first, Note.second); } } } @@ -10713,13 +10740,16 @@ if (Cond) { Expr *FailedCond; std::string FailedDescription; + SmallVector Notes; std::tie(FailedCond, FailedDescription) = - findFailedBooleanCondition(Cond); + findFailedBooleanCondition(Cond, Notes); Diag(FailedCond->getExprLoc(), diag::err_typename_nested_not_found_requirement) << FailedDescription << FailedCond->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); return QualType(); } diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp @@ -99,7 +99,8 @@ // Use a failing test to ensure the type isn't considered dependent. static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}} \ - // expected-note {{evaluates to '12 == 13'}} + // expected-note {{evaluates to '12 == 13'}} \ + // expected-note{{with 'sizeof (arr2)' equal to 12}} } void g() { f(); } // expected-note {{in instantiation of}} diff --git a/clang/test/Parser/objc-static-assert.mm b/clang/test/Parser/objc-static-assert.mm --- a/clang/test/Parser/objc-static-assert.mm +++ b/clang/test/Parser/objc-static-assert.mm @@ -27,7 +27,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}} \ - // expected-note {{evaluates to '4 == 3'}} + // expected-note {{evaluates to '4 == 3'}} \ + // expected-note {{with 'sizeof (a)' equal to 4}} } static_assert(1, ""); @@ -42,7 +43,8 @@ _Static_assert(1, ""); static_assert(sizeof(b) == 4, ""); static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \ - // expected-note {{evaluates to '4 == 3'}} + // expected-note {{evaluates to '4 == 3'}} \ + // expected-note{{with 'sizeof (b)' equal to 4}} } static_assert(1, ""); @@ -59,7 +61,8 @@ int b; static_assert(sizeof(b) == 4, ""); static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \ - // expected-note {{evaluates to '4 == 3'}} + // expected-note {{evaluates to '4 == 3'}} \ + // expected-note{{with 'sizeof (b)' equal to 4}} } @end diff --git a/clang/test/Sema/static-assert.c b/clang/test/Sema/static-assert.c --- a/clang/test/Sema/static-assert.c +++ b/clang/test/Sema/static-assert.c @@ -56,7 +56,9 @@ UNION(char[2], short) u2 = { .one = { 'a', 'b' } }; // ext-warning 3 {{'_Static_assert' is a C11 extension}} cxx-warning {{designated initializers are a C++20 extension}} typedef UNION(char, short) U3; // expected-error {{static assertion failed due to requirement 'sizeof(char) == sizeof(short)': type size mismatch}} \ // expected-note{{evaluates to '1 == 2'}} \ - // ext-warning 3 {{'_Static_assert' is a C11 extension}} + // ext-warning 3 {{'_Static_assert' is a C11 extension}} \ + // expected-note {{with 'sizeof(char)' equal to 1}} \ + // expected-note {{with 'sizeof(short)' equal to 2}} typedef UNION(float, 0.5f) U4; // expected-error {{expected a type}} \ // ext-warning 3 {{'_Static_assert' is a C11 extension}} diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1918,12 +1918,14 @@ // cxx11-note@-2 {{call to virtual function}} // cxx20_2b-error@-3 {{static assertion failed}} // cxx20_2b-note@-4 {{8 == 16}} + // cxx20_2b-note@-5 {{with 'sizeof(VirtualFromBase::X)' equal to 16}} // Non-virtual f(), OK. constexpr X> xxs2; constexpr X *q = const_cast>*>(&xxs2); static_assert(q->f() == sizeof(S2), ""); // cxx20_2b-error {{static assertion failed}} \ // cxx20_2b-note {{16 == 8}} + // cxx20_2b-note@-2 {{with 'sizeof(VirtualFromBase::S2)' equal to 8}} } namespace ConstexprConstructorRecovery { diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp --- a/clang/test/SemaCXX/static-assert-cxx17.cpp +++ b/clang/test/SemaCXX/static-assert-cxx17.cpp @@ -89,7 +89,8 @@ // 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-note@-1 {{evaluates to '8 == 0'}} + // expected-note@-1 {{evaluates to '8 == 0'}} \ + // expected-note@-1 {{with 'sizeof(X)' equal to 8}} static_assert((const X *)nullptr); // expected-error@-1{{static assertion failed due to requirement '(const X *)nullptr'}} static_assert(static_cast *>(nullptr)); @@ -98,7 +99,8 @@ // 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-note@-1 {{evaluates to '8 == 0'}} + // expected-note@-1 {{evaluates to '8 == 0'}} \ + // expected-note@-1 {{with 'sizeof(X)' equal to 8}} static_assert(constexpr_return_false()); // expected-error@-1{{static assertion failed due to requirement 'constexpr_return_false()'}} } diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp --- a/clang/test/SemaCXX/static-assert.cpp +++ b/clang/test/SemaCXX/static-assert.cpp @@ -23,7 +23,9 @@ template struct S { static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static assertion failed due to requirement 'sizeof(char) > sizeof(char)': Type not big enough!}} \ - // expected-note {{1 > 1}} + // expected-note {{1 > 1}} \ + // expected-note {{with 'sizeof(char)' equal to 1}} \ + // expected-note {{with 'sizeof(char)' equal to 1}} }; S s1; // expected-note {{in instantiation of template class 'S' requested here}} @@ -264,3 +266,30 @@ } + +struct IntAndPointer { + int i; + void *p; +}; +static_assert(sizeof(IntAndPointer) == 4, "message"); +// expected-error@-1{{static assertion failed}} \ +// expected-note@-1{{expression evaluates to '16 == 4'}} \ +// expected-note@-1{{with 'sizeof(IntAndPointer)' equal to 16}} +static_assert(alignof(IntAndPointer) == 4, "message"); +// expected-error@-1{{static assertion failed}} \ +// expected-note@-1{{expression evaluates to '8 == 4'}} \ +// expected-note@-1{{with 'alignof(IntAndPointer)' equal to 8}} +static_assert(alignof(IntAndPointer) == sizeof(IntAndPointer), "message"); +// expected-error@-1{{static assertion failed}} \ +// expected-note@-1{{expression evaluates to '8 == 16'}} \ +// expected-note@-1{{with 'alignof(IntAndPointer)' equal to 8}} \ +// expected-note@-1{{with 'sizeof(IntAndPointer)' equal to 16}} +static_assert(alignof(IntAndPointer) + sizeof(int) == sizeof(IntAndPointer), "message"); +// expected-error@-1{{static assertion failed}} \ +// expected-note@-1{{expression evaluates to '12 == 16'}} \ +// expected-note@-1{{with 'alignof(IntAndPointer)' equal to 8}} \ +// expected-note@-1{{with 'sizeof(int)' equal to 4}} \ +// expected-note@-1{{with 'sizeof(IntAndPointer)' equal to 16}} +/// Should not print the sizeof(int) value here since it's not evaluated. +static_assert(std::is_same::value, "message"); +// expected-error@-1{{static assertion failed}} diff --git a/clang/test/SemaTemplate/overload-candidates.cpp b/clang/test/SemaTemplate/overload-candidates.cpp --- a/clang/test/SemaTemplate/overload-candidates.cpp +++ b/clang/test/SemaTemplate/overload-candidates.cpp @@ -62,6 +62,8 @@ template struct NonTemplateFunction { typename boost::enable_if::type f(); // expected-error{{failed requirement 'sizeof(char) == 4'; 'enable_if' cannot be used to disable this declaration}} + // expected-note@-1{{expression evaluates to '1 == 4'}} + // expected-note@-2{{with 'sizeof(char)' equal to 1}} }; NonTemplateFunction NTFC; // expected-note{{here}}