diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fb1ebd90491c..06c304f43597 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1219,6 +1219,8 @@ def warn_messaging_unqualified_id : Warning< def err_static_assert_expression_is_not_constant : Error< "static_assert expression is not an integral constant expression">; 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 ext_static_assert_no_message : ExtWarn< "static_assert with no message is a C++17 extension">, InGroup; def warn_cxx14_compat_static_assert_no_message : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 69c48203579e..fc34f6816f83 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2783,6 +2783,14 @@ public: EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef Args, bool MissingImplicitThis = false); + /// Find the failed Boolean condition within a given Boolean + /// constant expression, and describe it with a string. + /// + /// \param AllowTopLevelCond Whether to allow the result to be the + /// complete top-level condition. + std::pair + findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond); + /// 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 index c05e5f020708..941930c51ae1 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13296,8 +13296,20 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, llvm::raw_svector_ostream Msg(MsgBuffer); if (AssertMessage) AssertMessage->printPretty(Msg, nullptr, getPrintingPolicy()); - Diag(StaticAssertLoc, diag::err_static_assert_failed) - << !AssertMessage << Msg.str() << AssertExpr->getSourceRange(); + + Expr *InnerCond = nullptr; + std::string InnerCondDescription; + std::tie(InnerCond, InnerCondDescription) = + findFailedBooleanCondition(Converted.get(), + /*AllowTopLevelCond=*/false); + if (InnerCond) { + Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed) + << InnerCondDescription << !AssertMessage + << Msg.str() << InnerCond->getSourceRange(); + } else { + Diag(StaticAssertLoc, diag::err_static_assert_failed) + << !AssertMessage << Msg.str() << AssertExpr->getSourceRange(); + } Failed = true; } } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1fcc79403f88..050e18944a7e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2863,11 +2863,9 @@ static Expr *lookThroughRangesV3Condition(Preprocessor &PP, Expr *Cond) { return Cond; } -/// Find the failed subexpression within enable_if, and describe it -/// with a string. -static std::pair -findFailedEnableIfCondition(Sema &S, Expr *Cond) { - Cond = lookThroughRangesV3Condition(S.PP, Cond); +std::pair +Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) { + Cond = lookThroughRangesV3Condition(PP, Cond); // Separate out all of the terms in a conjunction. SmallVector Terms; @@ -2876,27 +2874,37 @@ findFailedEnableIfCondition(Sema &S, Expr *Cond) { // Determine which term failed. Expr *FailedCond = nullptr; for (Expr *Term : Terms) { + Expr *TermAsWritten = Term->IgnoreParenImpCasts(); + + // Literals are uninteresting. + if (isa(TermAsWritten) || + isa(TermAsWritten)) + continue; + // The initialization of the parameter from the argument is // a constant-evaluated context. EnterExpressionEvaluationContext ConstantEvaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); bool Succeeded; - if (Term->EvaluateAsBooleanCondition(Succeeded, S.Context) && + if (Term->EvaluateAsBooleanCondition(Succeeded, Context) && !Succeeded) { - FailedCond = Term->IgnoreParenImpCasts(); + FailedCond = TermAsWritten; break; } } - if (!FailedCond) + if (!FailedCond) { + if (!AllowTopLevelCond) + return { nullptr, "" }; + FailedCond = Cond->IgnoreParenImpCasts(); + } std::string Description; { llvm::raw_string_ostream Out(Description); - FailedCond->printPretty(Out, nullptr, - PrintingPolicy(S.Context.getLangOpts())); + FailedCond->printPretty(Out, nullptr, getPrintingPolicy()); } return { FailedCond, Description }; } @@ -2980,8 +2988,9 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, Expr *FailedCond; std::string FailedDescription; std::tie(FailedCond, FailedDescription) = - findFailedEnableIfCondition( - *this, TemplateArgs[0].getSourceExpression()); + findFailedBooleanCondition( + TemplateArgs[0].getSourceExpression(), + /*AllowTopLevelCond=*/true); // Remove the old SFINAE diagnostic. PartialDiagnosticAt OldDiag = @@ -9513,7 +9522,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, Expr *FailedCond; std::string FailedDescription; std::tie(FailedCond, FailedDescription) = - findFailedEnableIfCondition(*this, Cond); + findFailedBooleanCondition(Cond, /*AllowTopLevelCond=*/true); Diag(FailedCond->getExprLoc(), diag::err_typename_nested_not_found_requirement) diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp index 196375c3d687..846303807a02 100644 --- a/clang/test/SemaCXX/static-assert.cpp +++ b/clang/test/SemaCXX/static-assert.cpp @@ -51,3 +51,20 @@ StaticAssertProtected sap2; // expected-note {{instantiation}} static_assert(true); // expected-warning {{C++17 extension}} static_assert(false); // expected-error-re {{failed{{$}}}} expected-warning {{extension}} + + +// Diagnostics for static_assert with multiple conditions +template struct first_trait { + static const bool value = false; +}; + +template<> +struct first_trait { + static const bool value = true; +}; + +template struct second_trait { + static const bool value = false; +}; + +static_assert(first_trait::value && second_trait::value, "message"); // expected-error{{static_assert failed due to requirement 'second_trait::value' "message"}}