From e3f470a718ec00eb8b546e405fa59bc2df2d7c46 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 11 Jul 2012 22:37:56 +0000 Subject: [PATCH] Stop instantiating a class if we hit a static_assert failure. Also, if the static_assert fails when parsing the template, don't diagnose it again on every instantiation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160088 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 20 +++++++------- include/clang/Sema/Sema.h | 5 ++++ lib/AST/DeclCXX.cpp | 8 +++--- lib/Sema/SemaDeclCXX.cpp | 34 ++++++++++++++++-------- lib/Sema/SemaTemplateInstantiate.cpp | 9 ++++++- lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 +++---- lib/Serialization/ASTReaderDecl.cpp | 3 ++- lib/Serialization/ASTWriterDecl.cpp | 1 + test/SemaCXX/static-assert.cpp | 14 ++++++++++ 9 files changed, 73 insertions(+), 30 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 9130e28a47..0d498fe4e5 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -2892,31 +2892,33 @@ public: /// \brief Represents a C++11 static_assert declaration. class StaticAssertDecl : public Decl { virtual void anchor(); - Expr *AssertExpr; + llvm::PointerIntPair AssertExprAndFailed; StringLiteral *Message; SourceLocation RParenLoc; StaticAssertDecl(DeclContext *DC, SourceLocation StaticAssertLoc, - Expr *assertexpr, StringLiteral *message, - SourceLocation RParenLoc) - : Decl(StaticAssert, DC, StaticAssertLoc), AssertExpr(assertexpr), - Message(message), RParenLoc(RParenLoc) { } + Expr *AssertExpr, StringLiteral *Message, + SourceLocation RParenLoc, bool Failed) + : Decl(StaticAssert, DC, StaticAssertLoc), + AssertExprAndFailed(AssertExpr, Failed), Message(Message), + RParenLoc(RParenLoc) { } public: static StaticAssertDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation StaticAssertLoc, Expr *AssertExpr, StringLiteral *Message, - SourceLocation RParenLoc); + SourceLocation RParenLoc, bool Failed); static StaticAssertDecl *CreateDeserialized(ASTContext &C, unsigned ID); - Expr *getAssertExpr() { return AssertExpr; } - const Expr *getAssertExpr() const { return AssertExpr; } + Expr *getAssertExpr() { return AssertExprAndFailed.getPointer(); } + const Expr *getAssertExpr() const { return AssertExprAndFailed.getPointer(); } StringLiteral *getMessage() { return Message; } const StringLiteral *getMessage() const { return Message; } + bool isFailed() const { return AssertExprAndFailed.getInt(); } + SourceLocation getRParenLoc() const { return RParenLoc; } - void setRParenLoc(SourceLocation L) { RParenLoc = L; } SourceRange getSourceRange() const LLVM_READONLY { return SourceRange(getLocation(), getRParenLoc()); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6096466f08..35bbc0104f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4306,6 +4306,11 @@ public: Expr *AssertExpr, Expr *AssertMessageExpr, SourceLocation RParenLoc); + Decl *BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, + Expr *AssertExpr, + StringLiteral *AssertMessageExpr, + SourceLocation RParenLoc, + bool Failed); FriendDecl *CheckFriendTypeDecl(SourceLocation Loc, SourceLocation FriendLoc, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 11a5260836..77e693c2c7 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -2005,15 +2005,17 @@ StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StaticAssertLoc, Expr *AssertExpr, StringLiteral *Message, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, + bool Failed) { return new (C) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message, - RParenLoc); + RParenLoc, Failed); } StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(StaticAssertDecl)); - return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0,SourceLocation()); + return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0, + SourceLocation(), false); } static const char *getAccessName(AccessSpecifier AS) { diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index fa42fdd827..ebd9180ec6 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9736,37 +9736,49 @@ Decl *Sema::ActOnExceptionDeclarator(Scope *S, Declarator &D) { Decl *Sema::ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, - Expr *AssertMessageExpr_, + Expr *AssertMessageExpr, SourceLocation RParenLoc) { - StringLiteral *AssertMessage = cast(AssertMessageExpr_); + StringLiteral *AssertMessage = cast(AssertMessageExpr); - if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent()) { + if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression)) + return 0; + + return BuildStaticAssertDeclaration(StaticAssertLoc, AssertExpr, + AssertMessage, RParenLoc, false); +} + +Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, + Expr *AssertExpr, + StringLiteral *AssertMessage, + SourceLocation RParenLoc, + bool Failed) { + if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent() && + !Failed) { // In a static_assert-declaration, the constant-expression shall be a // constant expression that can be contextually converted to bool. ExprResult Converted = PerformContextuallyConvertToBool(AssertExpr); if (Converted.isInvalid()) - return 0; + Failed = true; llvm::APSInt Cond; - if (VerifyIntegerConstantExpression(Converted.get(), &Cond, + if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond, diag::err_static_assert_expression_is_not_constant, /*AllowFold=*/false).isInvalid()) - return 0; + Failed = true; - if (!Cond) { + if (!Failed && !Cond) { llvm::SmallString<256> MsgBuffer; llvm::raw_svector_ostream Msg(MsgBuffer); AssertMessage->printPretty(Msg, Context, 0, getPrintingPolicy()); Diag(StaticAssertLoc, diag::err_static_assert_failed) << Msg.str() << AssertExpr->getSourceRange(); + Failed = true; } } - if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression)) - return 0; - Decl *Decl = StaticAssertDecl::Create(Context, CurContext, StaticAssertLoc, - AssertExpr, AssertMessage, RParenLoc); + AssertExpr, AssertMessage, RParenLoc, + Failed); CurContext->addDecl(Decl); return Decl; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 239a0d73eb..0a0016c50b 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1904,7 +1904,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, continue; if ((*Member)->isInvalidDecl()) { - Instantiation->setInvalidDecl(); + Instantiation->setInvalidDecl(); continue; } @@ -1928,6 +1928,13 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, MSInfo->setTemplateSpecializationKind(TSK_ImplicitInstantiation); MSInfo->setPointOfInstantiation(PointOfInstantiation); } + } else if (StaticAssertDecl *SA = dyn_cast(NewMember)) { + if (SA->isFailed()) { + // A static_assert failed. Bail out; instantiating this + // class is probably not meaningful. + Instantiation->setInvalidDecl(); + break; + } } if (NewMember->isInvalidDecl()) diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index cce0b0d093..0d3e8a0ba8 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -552,12 +552,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { if (InstantiatedAssertExpr.isInvalid()) return 0; - ExprResult Message(D->getMessage()); - D->getMessage(); - return SemaRef.ActOnStaticAssertDeclaration(D->getLocation(), + return SemaRef.BuildStaticAssertDeclaration(D->getLocation(), InstantiatedAssertExpr.get(), - Message.get(), - D->getRParenLoc()); + D->getMessage(), + D->getRParenLoc(), + D->isFailed()); } Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index f5f9ba2598..25f89adc7f 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1501,7 +1501,8 @@ void ASTDeclReader::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { VisitDecl(D); - D->AssertExpr = Reader.ReadExpr(F); + D->AssertExprAndFailed.setPointer(Reader.ReadExpr(F)); + D->AssertExprAndFailed.setInt(Record[Idx++]); D->Message = cast(Reader.ReadExpr(F)); D->RParenLoc = ReadSourceLocation(Record, Idx); } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 96b602221e..21ba4d0374 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -1222,6 +1222,7 @@ void ASTDeclWriter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) { VisitDecl(D); Writer.AddStmt(D->getAssertExpr()); + Record.push_back(D->isFailed()); Writer.AddStmt(D->getMessage()); Writer.AddSourceLocation(D->getRParenLoc(), Record); Code = serialization::DECL_STATIC_ASSERT; diff --git a/test/SemaCXX/static-assert.cpp b/test/SemaCXX/static-assert.cpp index 364e4e4bef..4a7560ba5b 100644 --- a/test/SemaCXX/static-assert.cpp +++ b/test/SemaCXX/static-assert.cpp @@ -34,3 +34,17 @@ static_assert(false, u"\U000317FF"); // expected-error {{static_assert failed u" static_assert(false, u8"Ω"); // expected-error {{static_assert failed u8"\316\251"}} static_assert(false, L"\u1234"); // expected-error {{static_assert failed L"\x1234"}} static_assert(false, L"\x1ff" "0\x123" "fx\xfffff" "goop"); // expected-error {{static_assert failed L"\x1FF""0\x123""fx\xFFFFFgoop"}} + +template struct AlwaysFails { + // Only give one error here. + static_assert(false, ""); // expected-error {{static_assert failed}} +}; +AlwaysFails alwaysFails; + +template struct StaticAssertProtected { + static_assert(__is_literal(T), ""); // expected-error {{static_assert failed}} + static constexpr T t = {}; // no error here +}; +struct X { ~X(); }; +StaticAssertProtected sap1; +StaticAssertProtected sap2; // expected-note {{instantiation}}