diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-07-31 23:46:44 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-07-31 23:46:44 +0000 |
commit | 6de7a247824b4d15e97c551a919410cbce90a24f (patch) | |
tree | 217dfefa68c7c49319cbe2c2bc8a725a293d033c /clang | |
parent | b7e2f6015ae8fe44580ed242a0bca9c0001612ac (diff) | |
download | bcm5719-llvm-6de7a247824b4d15e97c551a919410cbce90a24f.tar.gz bcm5719-llvm-6de7a247824b4d15e97c551a919410cbce90a24f.zip |
[modules] Maintain an AST invariant across module load/save: if any declaration
of a function has a resolved exception specification, then all declarations of
the function do.
We should probably improve the AST representation to make this implicit (perhaps
only store the exception specification on the canonical declaration), but this
fixes things for now.
The testcase for this (which used to assert) also exposes the actual bug I was
trying to reduce here: we sometimes fail to emit the body of an imported
special member function definition. Fix for that to follow.
llvm-svn: 214458
Diffstat (limited to 'clang')
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 17 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 155 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/cxx-irgen-left.h | 9 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/cxx-irgen-right.h | 10 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/cxx-irgen-top.h | 18 | ||||
-rw-r--r-- | clang/test/Modules/cxx-irgen.cpp | 30 |
6 files changed, 189 insertions, 50 deletions
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 192b273c7c3..e4963b13d64 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -135,13 +135,16 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { void Sema::UpdateExceptionSpec(FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI) { - const FunctionProtoType *Proto = - FD->getType()->castAs<FunctionProtoType>(); - - // Overwrite the exception spec and rebuild the function type. - FD->setType(Context.getFunctionType( - Proto->getReturnType(), Proto->getParamTypes(), - Proto->getExtProtoInfo().withExceptionSpec(ESI))); + for (auto *Redecl : FD->redecls()) { + auto *RedeclFD = dyn_cast<FunctionDecl>(Redecl); + const FunctionProtoType *Proto = + RedeclFD->getType()->castAs<FunctionProtoType>(); + + // Overwrite the exception spec and rebuild the function type. + RedeclFD->setType(Context.getFunctionType( + Proto->getReturnType(), Proto->getParamTypes(), + Proto->getExtProtoInfo().withExceptionSpec(ESI))); + } // If we've fully resolved the exception specification, notify listeners. if (!isUnresolvedExceptionSpec(ESI.Type)) diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index f68284b9dde..d1906e0d005 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -199,9 +199,10 @@ namespace clang { TypeIDForTypeDecl(0), HasPendingBody(false) { } template <typename DeclT> - static void attachPreviousDeclImpl(Redeclarable<DeclT> *D, Decl *Previous); - static void attachPreviousDeclImpl(...); - static void attachPreviousDecl(Decl *D, Decl *previous); + static void attachPreviousDeclImpl(ASTReader &Reader, + Redeclarable<DeclT> *D, Decl *Previous); + static void attachPreviousDeclImpl(ASTReader &Reader, ...); + static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous); template <typename DeclT> static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest); @@ -2484,22 +2485,68 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) { } template<typename DeclT> -void ASTDeclReader::attachPreviousDeclImpl(Redeclarable<DeclT> *D, +void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, + Redeclarable<DeclT> *D, Decl *Previous) { D->RedeclLink.setPrevious(cast<DeclT>(Previous)); } -void ASTDeclReader::attachPreviousDeclImpl(...) { +template<> +void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, + Redeclarable<FunctionDecl> *D, + Decl *Previous) { + FunctionDecl *FD = static_cast<FunctionDecl*>(D); + FunctionDecl *PrevFD = cast<FunctionDecl>(Previous); + + FD->RedeclLink.setPrevious(PrevFD); + + // If the previous declaration is an inline function declaration, then this + // declaration is too. + if (PrevFD->IsInline != FD->IsInline) { + // FIXME: [dcl.fct.spec]p4: + // If a function with external linkage is declared inline in one + // translation unit, it shall be declared inline in all translation + // units in which it appears. + // + // Be careful of this case: + // + // module A: + // template<typename T> struct X { void f(); }; + // template<typename T> inline void X<T>::f() {} + // + // module B instantiates the declaration of X<int>::f + // module C instantiates the definition of X<int>::f + // + // If module B and C are merged, we do not have a violation of this rule. + FD->IsInline = true; + } + + // If this declaration has an unresolved exception specification but the + // previous declaration had a resolved one, resolve the exception + // specification now. + auto *FPT = FD->getType()->getAs<FunctionProtoType>(); + auto *PrevFPT = PrevFD->getType()->getAs<FunctionProtoType>(); + if (FPT && PrevFPT && + isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) && + !isUnresolvedExceptionSpec(PrevFPT->getExceptionSpecType())) { + FunctionProtoType::ExtProtoInfo EPI = PrevFPT->getExtProtoInfo(); + FD->setType(Reader.Context.getFunctionType( + FPT->getReturnType(), FPT->getParamTypes(), + FPT->getExtProtoInfo().withExceptionSpec(EPI.ExceptionSpec))); + } +} +void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader, ...) { llvm_unreachable("attachPreviousDecl on non-redeclarable declaration"); } -void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *Previous) { +void ASTDeclReader::attachPreviousDecl(ASTReader &Reader, Decl *D, + Decl *Previous) { assert(D && Previous); switch (D->getKind()) { #define ABSTRACT_DECL(TYPE) -#define DECL(TYPE, BASE) \ - case Decl::TYPE: \ - attachPreviousDeclImpl(cast<TYPE##Decl>(D), Previous); \ +#define DECL(TYPE, BASE) \ + case Decl::TYPE: \ + attachPreviousDeclImpl(Reader, cast<TYPE##Decl>(D), Previous); \ break; #include "clang/AST/DeclNodes.inc" } @@ -2517,32 +2564,6 @@ void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *Previous) { // be too. if (Previous->Used) D->Used = true; - - // If the previous declaration is an inline function declaration, then this - // declaration is too. - if (auto *FD = dyn_cast<FunctionDecl>(D)) { - if (cast<FunctionDecl>(Previous)->IsInline != FD->IsInline) { - // FIXME: [dcl.fct.spec]p4: - // If a function with external linkage is declared inline in one - // translation unit, it shall be declared inline in all translation - // units in which it appears. - // - // Be careful of this case: - // - // module A: - // template<typename T> struct X { void f(); }; - // template<typename T> inline void X<T>::f() {} - // - // module B instantiates the declaration of X<int>::f - // module C instantiates the definition of X<int>::f - // - // If module B and C are merged, we do not have a violation of this rule. - // - //if (!FD->IsInline || Previous->getOwningModule()) - // Diag(FD->getLocation(), diag::err_odr_differing_inline); - FD->IsInline = true; - } - } } template<typename DeclT> @@ -3041,7 +3062,7 @@ void ASTReader::loadPendingDeclChain(serialization::GlobalDeclID ID) { if (Chain[I] == CanonDecl) continue; - ASTDeclReader::attachPreviousDecl(Chain[I], MostRecent); + ASTDeclReader::attachPreviousDecl(*this, Chain[I], MostRecent); MostRecent = Chain[I]; } @@ -3171,6 +3192,46 @@ void ASTReader::loadObjCCategories(serialization::GlobalDeclID ID, ModuleMgr.visit(ObjCCategoriesVisitor::visit, &Visitor); } +namespace { +/// Iterator over the redeclarations of a declaration that have already +/// been merged into the same redeclaration chain. +template<typename DeclT> +class MergedRedeclIterator { + DeclT *Start, *Canonical, *Current; +public: + MergedRedeclIterator() : Current(nullptr) {} + MergedRedeclIterator(DeclT *Start) + : Start(Start), Canonical(nullptr), Current(Start) {} + + DeclT *operator*() { return Current; } + + MergedRedeclIterator &operator++() { + if (Current->isFirstDecl()) + Canonical = Current; + Current = Current->getPreviousDecl(); + + // If we started in the merged portion, we'll reach our start position + // eventually. Otherwise, we'll never reach it, but the second declaration + // we reached was the canonical declaration, so stop when we see that one + // again. + if (Current == Start || Current == Canonical) + Current = nullptr; + return *this; + } + + friend bool operator!=(const MergedRedeclIterator &A, + const MergedRedeclIterator &B) { + return A.Current != B.Current; + } +}; +} +template<typename DeclT> +llvm::iterator_range<MergedRedeclIterator<DeclT>> merged_redecls(DeclT *D) { + return llvm::iterator_range<MergedRedeclIterator<DeclT>>( + MergedRedeclIterator<DeclT>(D), + MergedRedeclIterator<DeclT>()); +} + void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, const RecordData &Record) { while (Idx < Record.size()) { @@ -3288,14 +3349,24 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, } case UPD_CXX_RESOLVED_EXCEPTION_SPEC: { - auto *FD = cast<FunctionDecl>(D); - auto *FPT = FD->getType()->castAs<FunctionProtoType>(); - SmallVector<QualType, 8> ExceptionStorage; + // 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); - FD->setType(Reader.Context.getFunctionType( - FPT->getReturnType(), FPT->getParamTypes(), - FPT->getExtProtoInfo().withExceptionSpec(ESI))); + 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; + } + FD->setType(Reader.Context.getFunctionType( + FPT->getReturnType(), FPT->getParamTypes(), + FPT->getExtProtoInfo().withExceptionSpec(ESI))); + } break; } diff --git a/clang/test/Modules/Inputs/cxx-irgen-left.h b/clang/test/Modules/Inputs/cxx-irgen-left.h index ceb50846bbf..fb36b218a49 100644 --- a/clang/test/Modules/Inputs/cxx-irgen-left.h +++ b/clang/test/Modules/Inputs/cxx-irgen-left.h @@ -9,3 +9,12 @@ inline int instantiate_min() { inline int instantiate_CtorInit(CtorInit<int> i = CtorInit<int>()) { return i.a; } + +namespace ImplicitSpecialMembers { + inline void create_left() { + // Trigger declaration, but not definition, of special members. + B b(0); C c(0); D d(0); + // Trigger definition of copy constructor. + C c2(c); D d2(d); + } +} diff --git a/clang/test/Modules/Inputs/cxx-irgen-right.h b/clang/test/Modules/Inputs/cxx-irgen-right.h index 4400c760f07..30686a12479 100644 --- a/clang/test/Modules/Inputs/cxx-irgen-right.h +++ b/clang/test/Modules/Inputs/cxx-irgen-right.h @@ -1,3 +1,13 @@ #include "cxx-irgen-top.h" inline int h() { return S<int>::f(); } + +namespace ImplicitSpecialMembers { + inline void create_right() { + // Trigger declaration, but not definition, of special members. + B b(0); C c(0); D d(0); + // Trigger definition of move constructor. + B b2(static_cast<B&&>(b)); + D d2(static_cast<D&&>(d)); + } +} diff --git a/clang/test/Modules/Inputs/cxx-irgen-top.h b/clang/test/Modules/Inputs/cxx-irgen-top.h index 8753d8daa3d..0ab586f7ae1 100644 --- a/clang/test/Modules/Inputs/cxx-irgen-top.h +++ b/clang/test/Modules/Inputs/cxx-irgen-top.h @@ -14,3 +14,21 @@ template<typename T> struct CtorInit { int a; CtorInit() : a(f()) {} }; + +namespace ImplicitSpecialMembers { + struct A { + A(const A&); + }; + struct B { + A a; + B(int); + }; + struct C { + A a; + C(int); + }; + struct D { + A a; + D(int); + }; +} diff --git a/clang/test/Modules/cxx-irgen.cpp b/clang/test/Modules/cxx-irgen.cpp index 4c61a3a3583..eea6b316e3f 100644 --- a/clang/test/Modules/cxx-irgen.cpp +++ b/clang/test/Modules/cxx-irgen.cpp @@ -13,10 +13,38 @@ CtorInit<int> x; // CHECK-DAG: define available_externally hidden {{signext i32|i32}} @_ZN1SIiE1gEv({{.*}} #[[ALWAYS_INLINE:.*]] align int a = S<int>::g(); -// CHECK-DAG: define available_externally {{signext i32|i32}} @_ZN1SIiE1fEv({{.*}} #[[ALWAYS_INLINE]] align int b = h(); // CHECK-DAG: define linkonce_odr {{signext i32|i32}} @_Z3minIiET_S0_S0_(i32 int c = min(1, 2); +namespace ImplicitSpecialMembers { + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1DC2EOS0_( + // CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1DC2ERKS0_( + // CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1CC2EOS0_( + // CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1CC2ERKS0_( + // CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1BC2EOS0_( + // CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1BC2ERKS0_( + // FIXME CHECK-NOT: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_( + + extern B b1; + B b2(b1); + B b3(static_cast<B&&>(b1)); + + extern C c1; + C c2(c1); + C c3(static_cast<C&&>(c1)); + + extern D d1; + D d2(d1); + D d3(static_cast<D&&>(d1)); +} + +// CHECK: define available_externally {{signext i32|i32}} @_ZN1SIiE1fEv({{.*}} #[[ALWAYS_INLINE]] align + // CHECK: attributes #[[ALWAYS_INLINE]] = {{.*}} alwaysinline |