From baec00022d6400f335079c04207349c21f1e8f41 Mon Sep 17 00:00:00 2001 From: Clement Courbet Date: Tue, 11 Dec 2018 08:39:11 +0000 Subject: [PATCH] Reland r348741 "[Sema] Further improvements to to static_assert diagnostics." Fix a dangling reference to temporary, never return nullptr. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@348834 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 6 +-- lib/Sema/SemaDeclCXX.cpp | 6 +-- lib/Sema/SemaTemplate.cpp | 68 +++++++++++++++------------- test/PCH/cxx-static_assert.cpp | 4 +- test/Sema/static-assert.c | 2 +- test/SemaCXX/static-assert-cxx17.cpp | 9 ++++ test/SemaCXX/static-assert.cpp | 12 ++++- 7 files changed, 63 insertions(+), 44 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8e29447f1f..e67e918e99 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2861,11 +2861,7 @@ public: /// 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); + std::pair findFailedBooleanCondition(Expr *Cond); /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any /// non-ArgDependent DiagnoseIfAttrs. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 16e4d84122..7914a5d40e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -13913,9 +13913,9 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *InnerCond = nullptr; std::string InnerCondDescription; std::tie(InnerCond, InnerCondDescription) = - findFailedBooleanCondition(Converted.get(), - /*AllowTopLevelCond=*/false); - if (InnerCond) { + findFailedBooleanCondition(Converted.get()); + if (InnerCond && !isa(InnerCond) + && !isa(InnerCond)) { Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed) << InnerCondDescription << !AssertMessage << Msg.str() << InnerCond->getSourceRange(); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 56302d6248..a3dbb44ba3 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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(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(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 &P) + : Policy(P) {} + + bool handledStmt(Stmt *E, raw_ostream &OS) override { + const auto *DR = dyn_cast(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(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 -Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) { +Sema::findFailedBooleanCondition(Expr *Cond) { Cond = lookThroughRangesV3Condition(PP, Cond); // Separate out all of the terms in a conjunction. @@ -3104,18 +3116,14 @@ Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) { break; } } - - if (!FailedCond) { - if (!AllowTopLevelCond) - return { nullptr, "" }; - + if (!FailedCond) FailedCond = Cond->IgnoreParenImpCasts(); - } std::string Description; { llvm::raw_string_ostream Out(Description); - prettyPrintFailedBooleanCondition(Out, FailedCond, getPrintingPolicy()); + FailedBooleanConditionPrinterHelper Helper(getPrintingPolicy()); + FailedCond->printPretty(Out, &Helper, getPrintingPolicy()); } return { FailedCond, Description }; } @@ -3199,9 +3207,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 = @@ -9649,7 +9655,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) diff --git a/test/PCH/cxx-static_assert.cpp b/test/PCH/cxx-static_assert.cpp index 8049525fb5..be6c0ab2a7 100644 --- a/test/PCH/cxx-static_assert.cpp +++ b/test/PCH/cxx-static_assert.cpp @@ -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 @@ -14,7 +14,7 @@ template 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; diff --git a/test/Sema/static-assert.c b/test/Sema/static-assert.c index 87fa0504b2..e8cfb1fa58 100644 --- a/test/Sema/static-assert.c +++ b/test/Sema/static-assert.c @@ -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}} diff --git a/test/SemaCXX/static-assert-cxx17.cpp b/test/SemaCXX/static-assert-cxx17.cpp index 7dcdb89719..67b3541bea 100644 --- a/test/SemaCXX/static-assert-cxx17.cpp +++ b/test/SemaCXX/static-assert-cxx17.cpp @@ -45,3 +45,12 @@ void foo4() { }; template void foo4(); // expected-note@-1{{in instantiation of function template specialization 'foo4' requested here}} + + +template +void foo5() { + static_assert(!!(global_inline_var)); + // expected-error@-1{{static_assert failed due to requirement '!!(global_inline_var)'}} +} +template void foo5(); +// expected-note@-1{{in instantiation of function template specialization 'foo5' requested here}} diff --git a/test/SemaCXX/static-assert.cpp b/test/SemaCXX/static-assert.cpp index 38f82091ae..b43d56a922 100644 --- a/test/SemaCXX/static-assert.cpp +++ b/test/SemaCXX/static-assert.cpp @@ -15,14 +15,14 @@ class C { }; template 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 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 s1; // expected-note {{in instantiation of template class 'S' requested here}} @@ -111,6 +111,14 @@ static_assert(std::is_same::value, "message"); // expected-error@-1{{static_assert failed due to requirement 'std::is_same::value' "message"}} static_assert(std::is_const::value, "message"); // expected-error@-1{{static_assert failed due to requirement 'std::is_const::value' "message"}} +static_assert(!std::is_const::value, "message"); +// expected-error@-1{{static_assert failed due to requirement '!std::is_const::value' "message"}} +static_assert(!(std::is_const::value), "message"); +// expected-error@-1{{static_assert failed due to requirement '!(std::is_const::value)' "message"}} +static_assert(std::is_const::value == false, "message"); +// expected-error@-1{{static_assert failed due to requirement 'std::is_const::value == false' "message"}} +static_assert(!(std::is_const::value == true), "message"); +// expected-error@-1{{static_assert failed due to requirement '!(std::is_const::value == true)' "message"}} struct BI_tag {}; struct RAI_tag : BI_tag {}; -- 2.40.0