Skip to content

Commit

Permalink
[Sema] Further improvements to to static_assert diagnostics.
Browse files Browse the repository at this point in the history
Summary:
We're now handling cases like `static_assert(!expr)` and
static_assert(!(expr))`.

Reviewers: aaron.ballman, Quuxplusone

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D55270

llvm-svn: 348741
  • Loading branch information
legrosbuffle committed Dec 10, 2018
1 parent 7b475f3 commit 057f769
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 48 deletions.
6 changes: 1 addition & 5 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -2861,11 +2861,7 @@ class Sema {

/// 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);
std::pair<Expr *, std::string> findFailedBooleanCondition(Expr *Cond);

/// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
/// non-ArgDependent DiagnoseIfAttrs.
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Sema/SemaDeclCXX.cpp
Expand Up @@ -13878,8 +13878,7 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
Expr *InnerCond = nullptr;
std::string InnerCondDescription;
std::tie(InnerCond, InnerCondDescription) =
findFailedBooleanCondition(Converted.get(),
/*AllowTopLevelCond=*/false);
findFailedBooleanCondition(Converted.get());
if (InnerCond) {
Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)
<< InnerCondDescription << !AssertMessage
Expand Down
77 changes: 41 additions & 36 deletions clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -3052,30 +3052,42 @@ static Expr *lookThroughRangesV3Condition(Preprocessor &PP, Expr *Cond) {
return Cond;
}

// Print a diagnostic for the failing static_assert expression. Defaults to
// pretty-printing the expression.
static void prettyPrintFailedBooleanCondition(llvm::raw_string_ostream &OS,
const Expr *FailedCond,
const PrintingPolicy &Policy) {
const auto *DR = dyn_cast<DeclRefExpr>(FailedCond);
if (DR && DR->getQualifier()) {
// If this is a qualified name, expand the template arguments in nested
// qualifiers.
DR->getQualifier()->print(OS, Policy, true);
// Then print the decl itself.
const ValueDecl *VD = DR->getDecl();
OS << VD->getName();
if (const auto *IV = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
// This is a template variable, print the expanded template arguments.
printTemplateArgumentList(OS, IV->getTemplateArgs().asArray(), Policy);
namespace {

// A PrinterHelper that prints more helpful diagnostics for some sub-expressions
// within failing boolean expression, such as substituting template parameters
// for actual types.
class FailedBooleanConditionPrinterHelper : public PrinterHelper {
public:
explicit FailedBooleanConditionPrinterHelper(const PrintingPolicy &Policy)
: Policy(Policy) {}

bool handledStmt(Stmt *E, raw_ostream &OS) override {
const auto *DR = dyn_cast<DeclRefExpr>(E);
if (DR && DR->getQualifier()) {
// If this is a qualified name, expand the template arguments in nested
// qualifiers.
DR->getQualifier()->print(OS, Policy, true);
// Then print the decl itself.
const ValueDecl *VD = DR->getDecl();
OS << VD->getName();
if (const auto *IV = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
// This is a template variable, print the expanded template arguments.
printTemplateArgumentList(OS, IV->getTemplateArgs().asArray(), Policy);
}
return true;
}
return;
return false;
}
FailedCond->printPretty(OS, nullptr, Policy);
}

private:
const PrintingPolicy &Policy;
};

} // end anonymous namespace

std::pair<Expr *, std::string>
Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) {
Sema::findFailedBooleanCondition(Expr *Cond) {
Cond = lookThroughRangesV3Condition(PP, Cond);

// Separate out all of the terms in a conjunction.
Expand All @@ -3087,11 +3099,6 @@ Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) {
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(
Expand All @@ -3104,18 +3111,18 @@ Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) {
break;
}
}

if (!FailedCond) {
if (!AllowTopLevelCond)
return { nullptr, "" };

if (!FailedCond)
FailedCond = Cond->IgnoreParenImpCasts();
}

// Literals are uninteresting.
if (isa<CXXBoolLiteralExpr>(FailedCond) || isa<IntegerLiteral>(FailedCond))
return {nullptr, ""};

std::string Description;
{
llvm::raw_string_ostream Out(Description);
prettyPrintFailedBooleanCondition(Out, FailedCond, getPrintingPolicy());
FailedBooleanConditionPrinterHelper Helper(getPrintingPolicy());
FailedCond->printPretty(Out, &Helper, getPrintingPolicy());
}
return { FailedCond, Description };
}
Expand Down Expand Up @@ -3199,9 +3206,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
Expr *FailedCond;
std::string FailedDescription;
std::tie(FailedCond, FailedDescription) =
findFailedBooleanCondition(
TemplateArgs[0].getSourceExpression(),
/*AllowTopLevelCond=*/true);
findFailedBooleanCondition(TemplateArgs[0].getSourceExpression());

// Remove the old SFINAE diagnostic.
PartialDiagnosticAt OldDiag =
Expand Down Expand Up @@ -9649,7 +9654,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
Expr *FailedCond;
std::string FailedDescription;
std::tie(FailedCond, FailedDescription) =
findFailedBooleanCondition(Cond, /*AllowTopLevelCond=*/true);
findFailedBooleanCondition(Cond);

Diag(FailedCond->getExprLoc(),
diag::err_typename_nested_not_found_requirement)
Expand Down
4 changes: 2 additions & 2 deletions clang/test/PCH/cxx-static_assert.cpp
Expand Up @@ -3,7 +3,7 @@

// Test with pch.
// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -include-pch %t -verify -std=c++11 %s
// RUN: %clang_cc1 -include-pch %t -verify -std=c++11 %s

#ifndef HEADER
#define HEADER
Expand All @@ -14,7 +14,7 @@ template<int N> struct T {

#else

// expected-error@12 {{static_assert failed "N is not 2!"}}
// expected-error@12 {{static_assert failed due to requirement '1 == 2' "N is not 2!"}}
T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}}
T<2> t2;

Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/static-assert.c
Expand Up @@ -38,5 +38,5 @@ struct A {

typedef UNION(unsigned, struct A) U1;
UNION(char[2], short) u2 = { .one = { 'a', 'b' } };
typedef UNION(char, short) U3; // expected-error {{static_assert failed "type size mismatch"}}
typedef UNION(char, short) U3; // expected-error {{static_assert failed due to requirement 'sizeof(char) == sizeof(short)' "type size mismatch"}}
typedef UNION(float, 0.5f) U4; // expected-error {{expected a type}}
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/static-assert-cxx17.cpp
Expand Up @@ -45,3 +45,12 @@ void foo4() {
};
template void foo4<float>();
// expected-note@-1{{in instantiation of function template specialization 'foo4<float>' requested here}}


template <typename U, typename V>
void foo5() {
static_assert(!!(global_inline_var<U, V>));
// expected-error@-1{{static_assert failed due to requirement '!!(global_inline_var<int, float>)'}}
}
template void foo5<int, float>();
// expected-note@-1{{in instantiation of function template specialization 'foo5<int, float>' requested here}}
12 changes: 10 additions & 2 deletions clang/test/SemaCXX/static-assert.cpp
Expand Up @@ -15,14 +15,14 @@ class C {
};

template<int N> struct T {
static_assert(N == 2, "N is not 2!"); // expected-error {{static_assert failed "N is not 2!"}}
static_assert(N == 2, "N is not 2!"); // expected-error {{static_assert failed due to requirement '1 == 2' "N is not 2!"}}
};

T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}}
T<2> t2;

template<typename T> struct S {
static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed "Type not big enough!"}}
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!"}}
};

S<char> s1; // expected-note {{in instantiation of template class 'S<char>' requested here}}
Expand Down Expand Up @@ -111,6 +111,14 @@ static_assert(std::is_same<ExampleTypes::T, ExampleTypes::U>::value, "message");
// expected-error@-1{{static_assert failed due to requirement 'std::is_same<int, float>::value' "message"}}
static_assert(std::is_const<ExampleTypes::T>::value, "message");
// expected-error@-1{{static_assert failed due to requirement 'std::is_const<int>::value' "message"}}
static_assert(!std::is_const<const ExampleTypes::T>::value, "message");
// expected-error@-1{{static_assert failed due to requirement '!std::is_const<const int>::value' "message"}}
static_assert(!(std::is_const<const ExampleTypes::T>::value), "message");
// expected-error@-1{{static_assert failed due to requirement '!(std::is_const<const int>::value)' "message"}}
static_assert(std::is_const<const ExampleTypes::T>::value == false, "message");
// expected-error@-1{{static_assert failed due to requirement 'std::is_const<const int>::value == false' "message"}}
static_assert(!(std::is_const<const ExampleTypes::T>::value == true), "message");
// expected-error@-1{{static_assert failed due to requirement '!(std::is_const<const int>::value == true)' "message"}}

struct BI_tag {};
struct RAI_tag : BI_tag {};
Expand Down

0 comments on commit 057f769

Please sign in to comment.