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 @@ -1501,6 +1501,7 @@ def err_static_assert_failed : Error<"static_assert failed%select{ %1|}0">; def err_static_assert_requirement_failed : Error< "static_assert failed due to requirement '%0'%select{ %2|}1">; +def note_static_assert_requirement_context : Note<"with '%0' equal to %1">; def ext_inline_variable : ExtWarn< "inline variables are a C++17 extension">, InGroup; 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 @@ -3739,7 +3739,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. 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 @@ -16366,8 +16366,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. @@ -16385,6 +16386,8 @@ 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 @@ -3574,8 +3574,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); @@ -3593,18 +3595,34 @@ IV->getSpecializedTemplate()->getTemplateParameters()); } return true; + } else 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. @@ -3641,7 +3659,7 @@ 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 }; @@ -3729,8 +3747,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 = @@ -3744,6 +3764,8 @@ PDiag(diag::err_typename_nested_not_found_requirement) << FailedDescription << FailedCond->getSourceRange()); + for (const PartialDiagnosticAt &Note : Notes) + (*DeductionInfo)->addSFINAEDiagnostic(Note.first, Note.second); } } } @@ -10608,13 +10630,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 @@ -98,7 +98,7 @@ 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{{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 @@ -26,7 +26,7 @@ static_assert(a, ""); // expected-error {{static_assert expression is not an integral constant expression}} static_assert(sizeof(a) == 4, ""); - static_assert(sizeof(a) == 3, ""); // expected-error {{static_assert failed}} + static_assert(sizeof(a) == 3, ""); // expected-error {{static_assert failed}} expected-note{{with 'sizeof (a)' equal to 4}} } static_assert(1, ""); @@ -40,7 +40,7 @@ static_assert(1, ""); _Static_assert(1, ""); static_assert(sizeof(b) == 4, ""); - static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} + static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} expected-note{{with 'sizeof (b)' equal to 4}} } static_assert(1, ""); @@ -56,7 +56,7 @@ @interface B () { int b; static_assert(sizeof(b) == 4, ""); - static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} + static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} 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 @@ -55,7 +55,9 @@ typedef UNION(unsigned, struct A) U1; // ext-warning 3 {{'_Static_assert' is a C11 extension}} 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_assert failed due to requirement 'sizeof(char) == sizeof(short)' "type size mismatch"}} \ - // 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 @@ -1908,11 +1908,13 @@ // cxx11-error@-1 {{not an integral constant expression}} // cxx11-note@-2 {{call to virtual function}} // cxx20_2b-error@-3 {{static_assert failed}} + // cxx20_2b-note@-4 {{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_assert failed}} + // cxx20_2b-note@-1 {{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,6 +89,7 @@ // expected-error@-1{{static_assert failed due to requirement 'int(0)'}} static_assert(sizeof(X) == 0); // expected-error@-1{{static_assert failed due to requirement 'sizeof(X) == 0'}} + // expected-note@-2{{with 'sizeof(X)' equal to 8}} static_assert((const X *)nullptr); // expected-error@-1{{static_assert failed due to requirement '(const X *)nullptr'}} static_assert(static_cast *>(nullptr)); @@ -97,6 +98,7 @@ // expected-error@-1{{static_assert failed due to requirement '(X const[0]){} == nullptr'}} static_assert(sizeof(X().X::~X())>) == 0); // expected-error@-1{{static_assert failed due to requirement 'sizeof(X) == 0'}} + // expected-note@-2{{with 'sizeof(X)' equal to 8}} static_assert(constexpr_return_false()); // expected-error@-1{{static_assert 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 @@ -21,8 +21,10 @@ T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}} T<2> t2; -template struct S { - static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed due to requirement 'sizeof(char) > sizeof(char)' "Type not big enough!"}} +template struct S { + static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed due to requirement 'sizeof(char) > sizeof(char)' "Type not big enough!"}} + // expected-note@-1{{with 'sizeof(char)' equal to 1}} + // expected-note@-2{{with 'sizeof(char)' equal to 1}} }; S s1; // expected-note {{in instantiation of template class 'S' requested here}} @@ -199,3 +201,18 @@ constexpr NotBool constexprNotBool; static_assert(notBool, "message"); // expected-error {{value of type 'struct NotBool' is not contextually convertible to 'bool'}} static_assert(constexprNotBool, "message"); // expected-error {{value of type 'const NotBool' is not contextually convertible to 'bool'}} + +struct IntAndPointer { + int i; + void *p; +}; +static_assert(sizeof(IntAndPointer) == 4, "message"); +// expected-error@-1{{static_assert failed due to requirement 'sizeof(IntAndPointer) == 4' "message"}} +// expected-note@-2{{with 'sizeof(IntAndPointer)' equal to 16}} +static_assert(alignof(IntAndPointer) == 4, "message"); +// expected-error@-1{{static_assert failed due to requirement 'alignof(IntAndPointer) == 4' "message"}} +// expected-note@-2{{with 'alignof(IntAndPointer)' equal to 8}} +static_assert(alignof(IntAndPointer) == sizeof(IntAndPointer), "message"); +// expected-error@-1{{static_assert failed due to requirement 'alignof(IntAndPointer) == sizeof(IntAndPointer)' "message"}} +// expected-note@-2{{with 'alignof(IntAndPointer)' equal to 8}} +// expected-note@-3{{with 'sizeof(IntAndPointer)' equal to 16}} 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,7 @@ 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{{with 'sizeof(char)' equal to 1}} }; NonTemplateFunction NTFC; // expected-note{{here}}