From 903d93dbb0e71cd47676b5e9f18f038ee2f56dd5 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 21 Mar 2015 00:58:54 +0000 Subject: [PATCH] [modules] When either redecl chain merging or an update record causes us to give an exception specification to a declaration that didn't have an exception specification in any of our imported modules, emit an update record ourselves. Without this, code importing the current module would not see an exception specification that we could see and might have relied on. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@232870 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Serialization/ASTReader.h | 20 +++++- lib/Sema/SemaExceptionSpec.cpp | 6 +- lib/Serialization/ASTReader.cpp | 9 ++- lib/Serialization/ASTReaderDecl.cpp | 67 +++++++++---------- lib/Serialization/ASTWriter.cpp | 44 ++++++------ test/Modules/Inputs/update-exception-spec/a.h | 2 + test/Modules/Inputs/update-exception-spec/b.h | 3 + test/Modules/Inputs/update-exception-spec/c.h | 3 + .../update-exception-spec/module.modulemap | 3 + test/Modules/update-exception-spec.cpp | 6 ++ 10 files changed, 99 insertions(+), 64 deletions(-) create mode 100644 test/Modules/Inputs/update-exception-spec/a.h create mode 100644 test/Modules/Inputs/update-exception-spec/b.h create mode 100644 test/Modules/Inputs/update-exception-spec/c.h create mode 100644 test/Modules/Inputs/update-exception-spec/module.modulemap create mode 100644 test/Modules/update-exception-spec.cpp diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 1b93702e1b..c631059519 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -446,6 +446,12 @@ private: /// that we needed but hadn't loaded yet. llvm::DenseMap PendingFakeDefinitionData; + /// \brief Exception specification updates that have been loaded but not yet + /// propagated across the relevant redeclaration chain. The map key is the + /// canonical declaration (used only for deduplication) and the value is a + /// declaration that has an exception specification. + llvm::SmallMapVector PendingExceptionSpecUpdates; + struct ReplacedDeclInfo { ModuleFile *Mod; uint64_t Offset; @@ -950,7 +956,7 @@ private: /// \brief The set of lookup results that we have faked in order to support /// merging of partially deserialized decls but that we have not yet removed. - llvm::MapVector > + llvm::SmallMapVector, 16> PendingFakeLookupResults; /// \brief The generation number of each identifier, which keeps track of @@ -1179,6 +1185,18 @@ private: /// merged into its redecl chain. Decl *getMostRecentExistingDecl(Decl *D); + template + void forEachFormerlyCanonicalImportedDecl(const Decl *D, Fn Visit) { + D = D->getCanonicalDecl(); + if (D->isFromASTFile()) + Visit(D); + + auto It = MergedDecls.find(const_cast(D)); + if (It != MergedDecls.end()) + for (auto ID : It->second) + Visit(GetExistingDecl(ID)); + } + RecordLocation DeclCursorForID(serialization::DeclID ID, unsigned &RawLocation); void loadDeclUpdateRecords(serialization::DeclID ID, Decl *D); diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 41abd49627..51d6acebad 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -167,13 +167,13 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { void Sema::UpdateExceptionSpec(FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI) { - for (auto *Redecl : FD->redecls()) - Context.adjustExceptionSpec(cast(Redecl), ESI); - // If we've fully resolved the exception specification, notify listeners. if (!isUnresolvedExceptionSpec(ESI.Type)) if (auto *Listener = getASTMutationListener()) Listener->ResolvedExceptionSpec(FD); + + for (auto *Redecl : FD->redecls()) + Context.adjustExceptionSpec(cast(Redecl), ESI); } /// Determine whether a function has an implicitly-generated exception diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index bd54779513..a26f251cbd 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1,4 +1,4 @@ -//===--- ASTReader.cpp - AST File Reader ----------------------------------===// +//===-- ASTReader.cpp - AST File Reader ----------------------------------===// // // The LLVM Compiler Infrastructure // @@ -8617,6 +8617,13 @@ void ASTReader::FinishedDeserializing() { --NumCurrentElementsDeserializing; if (NumCurrentElementsDeserializing == 0) { + // Propagate exception specification updates along redeclaration chains. + for (auto Update : PendingExceptionSpecUpdates) { + auto *FPT = Update.second->getType()->castAs(); + SemaObj->UpdateExceptionSpec(Update.second, + FPT->getExtProtoInfo().ExceptionSpec); + } + diagnoseOdrViolations(); // We are not in recursive loading, so it's safe to pass the "interesting" diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 9c3776268d..5c6820f483 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -228,9 +228,11 @@ namespace clang { template static void attachPreviousDeclImpl(ASTReader &Reader, - Redeclarable *D, Decl *Previous); + Redeclarable *D, Decl *Previous, + Decl *Canon); static void attachPreviousDeclImpl(ASTReader &Reader, ...); - static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous); + static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous, + Decl *Canon); template static void attachLatestDeclImpl(Redeclarable *D, Decl *Latest); @@ -2820,14 +2822,14 @@ Decl *ASTReader::getMostRecentExistingDecl(Decl *D) { template void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, Redeclarable *D, - Decl *Previous) { + Decl *Previous, Decl *Canon) { D->RedeclLink.setPrevious(cast(Previous)); } namespace clang { template<> void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, Redeclarable *D, - Decl *Previous) { + Decl *Previous, Decl *Canon) { FunctionDecl *FD = static_cast(D); FunctionDecl *PrevFD = cast(Previous); @@ -2854,25 +2856,17 @@ void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, FD->IsInline = true; } - // If this declaration has an unresolved exception specification but the - // previous declaration had a resolved one, resolve the exception - // specification now. If this declaration has a resolved exception - // specification but the previous declarations did not, apply our exception - // specification to all prior ones now. + // If we need to propagate an exception specification along the redecl + // chain, make a note of that so that we can do so later. auto *FPT = FD->getType()->getAs(); auto *PrevFPT = PrevFD->getType()->getAs(); if (FPT && PrevFPT) { - bool WasUnresolved = isUnresolvedExceptionSpec(FPT->getExceptionSpecType()); - bool IsUnresolved = isUnresolvedExceptionSpec(PrevFPT->getExceptionSpecType()); - if (WasUnresolved && !IsUnresolved) { - Reader.Context.adjustExceptionSpec( - FD, PrevFPT->getExtProtoInfo().ExceptionSpec); - } else if (!WasUnresolved && IsUnresolved) { - FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); - for (FunctionDecl *PrevFDToUpdate = PrevFD; PrevFDToUpdate; - PrevFDToUpdate = PrevFDToUpdate->getPreviousDecl()) - Reader.Context.adjustExceptionSpec(PrevFDToUpdate, EPI.ExceptionSpec); - } + bool IsUnresolved = isUnresolvedExceptionSpec(FPT->getExceptionSpecType()); + bool WasUnresolved = + isUnresolvedExceptionSpec(PrevFPT->getExceptionSpecType()); + if (IsUnresolved != WasUnresolved) + Reader.PendingExceptionSpecUpdates.insert( + std::make_pair(Canon, IsUnresolved ? PrevFD : FD)); } } } @@ -2881,14 +2875,14 @@ void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, ...) { } void ASTDeclReader::attachPreviousDecl(ASTReader &Reader, Decl *D, - Decl *Previous) { + Decl *Previous, Decl *Canon) { assert(D && Previous); switch (D->getKind()) { #define ABSTRACT_DECL(TYPE) -#define DECL(TYPE, BASE) \ - case Decl::TYPE: \ - attachPreviousDeclImpl(Reader, cast(D), Previous); \ +#define DECL(TYPE, BASE) \ + case Decl::TYPE: \ + attachPreviousDeclImpl(Reader, cast(D), Previous, Canon); \ break; #include "clang/AST/DeclNodes.inc" } @@ -3392,7 +3386,7 @@ void ASTReader::loadPendingDeclChain(Decl *CanonDecl) { if (Chain[I] == CanonDecl) continue; - ASTDeclReader::attachPreviousDecl(*this, Chain[I], MostRecent); + ASTDeclReader::attachPreviousDecl(*this, Chain[I], MostRecent, CanonDecl); MostRecent = Chain[I]; } ASTDeclReader::attachLatestDecl(CanonDecl, MostRecent); @@ -3736,23 +3730,24 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, } case UPD_CXX_RESOLVED_EXCEPTION_SPEC: { - // FIXME: This doesn't send the right notifications if there are - // ASTMutationListeners other than an ASTWriter. FunctionProtoType::ExceptionSpecInfo ESI; SmallVector ExceptionStorage; Reader.readExceptionSpec(ModuleFile, ExceptionStorage, ESI, Record, Idx); - for (auto *Redecl : merged_redecls(D)) { - auto *FD = cast(Redecl); - auto *FPT = FD->getType()->castAs(); - if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) { - // AST invariant: if any exception spec in the redecl chain is - // resolved, all are resolved. We don't need to go any further. - // FIXME: If the exception spec is resolved, check that it matches. - break; - } + + // Update this declaration's exception specification, if needed. + auto *FD = cast(D); + auto *FPT = FD->getType()->castAs(); + // FIXME: If the exception specification is already present, check that it + // matches. + if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) { FD->setType(Reader.Context.getFunctionType( FPT->getReturnType(), FPT->getParamTypes(), FPT->getExtProtoInfo().withExceptionSpec(ESI))); + + // When we get to the end of deserializing, see if there are other decls + // that we need to propagate this exception specification onto. + Reader.PendingExceptionSpecUpdates.insert( + std::make_pair(FD->getCanonicalDecl(), FD)); } break; } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index a8e92b8151..a1fadb258a 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5804,37 +5804,36 @@ void ASTWriter::AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD, } void ASTWriter::ResolvedExceptionSpec(const FunctionDecl *FD) { - assert(!WritingAST && "Already writing the AST!"); - FD = FD->getCanonicalDecl(); - if (!FD->isFromASTFile()) - return; // Not a function declared in PCH and defined outside. - - DeclUpdates[FD].push_back(UPD_CXX_RESOLVED_EXCEPTION_SPEC); + assert(!DoneWritingDeclsAndTypes && "Already done writing updates!"); + if (!Chain) return; + Chain->forEachFormerlyCanonicalImportedDecl(FD, [&](const Decl *D) { + // If we don't already know the exception specification for this redecl + // chain, add an update record for it. + if (isUnresolvedExceptionSpec(cast(D) + ->getType() + ->castAs() + ->getExceptionSpecType())) + DeclUpdates[D].push_back(UPD_CXX_RESOLVED_EXCEPTION_SPEC); + }); } void ASTWriter::DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) { assert(!WritingAST && "Already writing the AST!"); - FD = FD->getCanonicalDecl(); - if (!FD->isFromASTFile()) - return; // Not a function declared in PCH and defined outside. - - DeclUpdates[FD].push_back(DeclUpdate(UPD_CXX_DEDUCED_RETURN_TYPE, ReturnType)); + if (!Chain) return; + Chain->forEachFormerlyCanonicalImportedDecl(FD, [&](const Decl *D) { + DeclUpdates[D].push_back( + DeclUpdate(UPD_CXX_DEDUCED_RETURN_TYPE, ReturnType)); + }); } void ASTWriter::ResolvedOperatorDelete(const CXXDestructorDecl *DD, const FunctionDecl *Delete) { assert(!WritingAST && "Already writing the AST!"); assert(Delete && "Not given an operator delete"); - for (auto *D : DD->redecls()) { - if (D->isFromASTFile()) { - // We added an operator delete that some imported destructor didn't - // know about. Add an update record to let importers of us and that - // declaration know about it. - DeclUpdates[DD].push_back( - DeclUpdate(UPD_CXX_RESOLVED_DTOR_DELETE, Delete)); - return; - } - } + if (!Chain) return; + Chain->forEachFormerlyCanonicalImportedDecl(DD, [&](const Decl *D) { + DeclUpdates[D].push_back(DeclUpdate(UPD_CXX_RESOLVED_DTOR_DELETE, Delete)); + }); } void ASTWriter::CompletedImplicitDefinition(const FunctionDecl *D) { @@ -5851,8 +5850,7 @@ void ASTWriter::FunctionDefinitionInstantiated(const FunctionDecl *D) { if (!D->isFromASTFile()) return; - DeclUpdates[D].push_back( - DeclUpdate(UPD_CXX_ADDED_FUNCTION_DEFINITION)); + DeclUpdates[D].push_back(DeclUpdate(UPD_CXX_ADDED_FUNCTION_DEFINITION)); } void ASTWriter::StaticDataMemberInstantiated(const VarDecl *D) { diff --git a/test/Modules/Inputs/update-exception-spec/a.h b/test/Modules/Inputs/update-exception-spec/a.h new file mode 100644 index 0000000000..078ebf9aec --- /dev/null +++ b/test/Modules/Inputs/update-exception-spec/a.h @@ -0,0 +1,2 @@ +struct A { ~A() throw(int); }; +struct B { A a; }; diff --git a/test/Modules/Inputs/update-exception-spec/b.h b/test/Modules/Inputs/update-exception-spec/b.h new file mode 100644 index 0000000000..f75b559bee --- /dev/null +++ b/test/Modules/Inputs/update-exception-spec/b.h @@ -0,0 +1,3 @@ +struct A { ~A() throw(int); }; +struct B { A a; }; +inline void f(B *p) { p->~B(); } diff --git a/test/Modules/Inputs/update-exception-spec/c.h b/test/Modules/Inputs/update-exception-spec/c.h new file mode 100644 index 0000000000..067dbb6505 --- /dev/null +++ b/test/Modules/Inputs/update-exception-spec/c.h @@ -0,0 +1,3 @@ +#include "a.h" +#include "b.h" +inline void g(B *p) { p->~B(); } diff --git a/test/Modules/Inputs/update-exception-spec/module.modulemap b/test/Modules/Inputs/update-exception-spec/module.modulemap new file mode 100644 index 0000000000..880ae38b97 --- /dev/null +++ b/test/Modules/Inputs/update-exception-spec/module.modulemap @@ -0,0 +1,3 @@ +module a { header "a.h" } +module b { header "b.h" } +module c { header "c.h" } diff --git a/test/Modules/update-exception-spec.cpp b/test/Modules/update-exception-spec.cpp new file mode 100644 index 0000000000..bccdddc9c0 --- /dev/null +++ b/test/Modules/update-exception-spec.cpp @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fmodules -fmodules-cache-path=%t -I%S/Inputs/update-exception-spec -emit-llvm-only %s +#include "a.h" +void use(B *p); +#include "c.h" +void use(B *p) { g(p); } -- 2.50.1