Diagnostic specific failed condition in a static_assert.
When a static_assert fails, dig out a specific condition to diagnose, using the same logic that we use to find the enable_if condition to diagnose. llvm-svn: 313315
This commit is contained in:
parent
bb26f86e6f
commit
672281a511
|
@ -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<CXX17>;
|
||||
def warn_cxx14_compat_static_assert_no_message : Warning<
|
||||
|
|
|
@ -2783,6 +2783,14 @@ public:
|
|||
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> 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<Expr *, std::string>
|
||||
findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond);
|
||||
|
||||
/// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
|
||||
/// non-ArgDependent DiagnoseIfAttrs.
|
||||
///
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Expr *, std::string>
|
||||
findFailedEnableIfCondition(Sema &S, Expr *Cond) {
|
||||
Cond = lookThroughRangesV3Condition(S.PP, Cond);
|
||||
std::pair<Expr *, std::string>
|
||||
Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) {
|
||||
Cond = lookThroughRangesV3Condition(PP, Cond);
|
||||
|
||||
// Separate out all of the terms in a conjunction.
|
||||
SmallVector<Expr *, 4> 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<CXXBoolLiteralExpr>(TermAsWritten) ||
|
||||
isa<IntegerLiteral>(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)
|
||||
|
|
|
@ -51,3 +51,20 @@ StaticAssertProtected<X> 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<typename T> struct first_trait {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct first_trait<X> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template<typename T> struct second_trait {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
static_assert(first_trait<X>::value && second_trait<X>::value, "message"); // expected-error{{static_assert failed due to requirement 'second_trait<X>::value' "message"}}
|
||||
|
|
Loading…
Reference in New Issue