]> granicus.if.org Git - clang/commitdiff
[modules] Maintain an AST invariant across module load/save: if any declaration
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 31 Jul 2014 23:46:44 +0000 (23:46 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 31 Jul 2014 23:46:44 +0000 (23:46 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@214458 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaExceptionSpec.cpp
lib/Serialization/ASTReaderDecl.cpp
test/Modules/Inputs/cxx-irgen-left.h
test/Modules/Inputs/cxx-irgen-right.h
test/Modules/Inputs/cxx-irgen-top.h
test/Modules/cxx-irgen.cpp

index 192b273c7c3593ce7bd0e37a2603f460d75ad0dc..e4963b13d641da6c4e7a1d8a91ebe2a387c855cf 100644 (file)
@@ -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))
index f68284b9dde2f52ef56d259ea66c1fc1c5b57b6e..d1906e0d00575d417b1ff4e2d45c023ec49a3c40 100644 (file)
@@ -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;
     }
 
index ceb50846bbf4e74525812168119cdf7bf1d255d4..fb36b218a49d59d194bb0c35d512dc5334883960 100644 (file)
@@ -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);
+  }
+}
index 4400c760f07a23ccd62caeab0b05c6c3c4f8972b..30686a12479a29bc2b72ecafff541ea97dcbc772 100644 (file)
@@ -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));
+  }
+}
index 8753d8daa3dcddad868ba97533aa12a1f212afa0..0ab586f7ae16bc9bc5ff2b19619374c291282278 100644 (file)
@@ -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);
+  };
+}
index 4c61a3a3583be3c378be02c38e35e0c4bced6cc3..eea6b316e3f4418faef2e713fdbf99beb697a9d0 100644 (file)
@@ -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