summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Serialization/ASTReader.h20
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp6
-rw-r--r--lib/Serialization/ASTReader.cpp9
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp67
-rw-r--r--lib/Serialization/ASTWriter.cpp44
-rw-r--r--test/Modules/Inputs/update-exception-spec/a.h2
-rw-r--r--test/Modules/Inputs/update-exception-spec/b.h3
-rw-r--r--test/Modules/Inputs/update-exception-spec/c.h3
-rw-r--r--test/Modules/Inputs/update-exception-spec/module.modulemap3
-rw-r--r--test/Modules/update-exception-spec.cpp6
10 files changed, 99 insertions, 64 deletions
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<void *, PendingFakeDefinitionKind> 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<Decl *, FunctionDecl *, 4> 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<IdentifierInfo *, SmallVector<NamedDecl*, 2> >
+ llvm::SmallMapVector<IdentifierInfo *, SmallVector<NamedDecl*, 2>, 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 <typename Fn>
+ void forEachFormerlyCanonicalImportedDecl(const Decl *D, Fn Visit) {
+ D = D->getCanonicalDecl();
+ if (D->isFromASTFile())
+ Visit(D);
+
+ auto It = MergedDecls.find(const_cast<Decl*>(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<FunctionDecl>(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<FunctionDecl>(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<FunctionProtoType>();
+ 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 <typename DeclT>
static void attachPreviousDeclImpl(ASTReader &Reader,
- Redeclarable<DeclT> *D, Decl *Previous);
+ Redeclarable<DeclT> *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 <typename DeclT>
static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest);
@@ -2820,14 +2822,14 @@ Decl *ASTReader::getMostRecentExistingDecl(Decl *D) {
template<typename DeclT>
void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader,
Redeclarable<DeclT> *D,
- Decl *Previous) {
+ Decl *Previous, Decl *Canon) {
D->RedeclLink.setPrevious(cast<DeclT>(Previous));
}
namespace clang {
template<>
void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader,
Redeclarable<FunctionDecl> *D,
- Decl *Previous) {
+ Decl *Previous, Decl *Canon) {
FunctionDecl *FD = static_cast<FunctionDecl*>(D);
FunctionDecl *PrevFD = cast<FunctionDecl>(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<FunctionProtoType>();
auto *PrevFPT = PrevFD->getType()->getAs<FunctionProtoType>();
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<TYPE##Decl>(D), Previous); \
+#define DECL(TYPE, BASE) \
+ case Decl::TYPE: \
+ attachPreviousDeclImpl(Reader, cast<TYPE##Decl>(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<QualType, 8> ExceptionStorage;
Reader.readExceptionSpec(ModuleFile, ExceptionStorage, ESI, Record, Idx);
- for (auto *Redecl : merged_redecls(D)) {
- auto *FD = cast<FunctionDecl>(Redecl);
- auto *FPT = FD->getType()->castAs<FunctionProtoType>();
- 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<FunctionDecl>(D);
+ auto *FPT = FD->getType()->castAs<FunctionProtoType>();
+ // 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<FunctionDecl>(D)
+ ->getType()
+ ->castAs<FunctionProtoType>()
+ ->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); }