When merging functions across modules (and in particular, instantiations of
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 29 May 2014 03:15:31 +0000 (03:15 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 29 May 2014 03:15:31 +0000 (03:15 +0000)
member functions), ensure that the redecl chain never transitions from 'inline'
to 'not inline', since that violates an AST invariant.

llvm-svn: 209794

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/test/Modules/Inputs/templates-left.h
clang/test/Modules/Inputs/templates-right.h
clang/test/Modules/Inputs/templates-top.h
clang/test/Modules/templates.mm

index be2db41..4017745 100644 (file)
@@ -3377,8 +3377,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
       !PatternDecl->getReturnType()->getContainedAutoType())
     return;
 
-  if (PatternDecl->isInlined())
-    Function->setImplicitlyInline();
+  if (PatternDecl->isInlined()) {
+    // Function, and all later redeclarations of it (from imported modules,
+    // for instance), are now implicitly inline.
+    for (auto *D = Function->getMostRecentDecl(); /**/;
+         D = D->getPreviousDecl()) {
+      D->setImplicitlyInline();
+      if (D == Function)
+        break;
+    }
+  }
 
   InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
   if (Inst.isInvalid())
index 8a0849c..7e652c4 100644 (file)
@@ -2491,6 +2491,32 @@ void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *Previous) {
   D->IdentifierNamespace |=
       Previous->IdentifierNamespace &
       (Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Type);
+
+  // 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>
@@ -3162,8 +3188,17 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
         return;
       }
 
-      if (Record[Idx++])
-        FD->setImplicitlyInline();
+      if (Record[Idx++]) {
+        // Maintain AST consistency: any later redeclarations of this function
+        // are inline if this one is. (We might have merged another declaration
+        // into this one.)
+        for (auto *D = FD->getMostRecentDecl(); /**/;
+             D = D->getPreviousDecl()) {
+          D->setImplicitlyInline();
+          if (D == FD)
+            break;
+        }
+      }
       FD->setInnerLocStart(Reader.ReadSourceLocation(ModuleFile, Record, Idx));
       if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
         std::tie(CD->CtorInitializers, CD->NumCtorInitializers) =
index ae9d8bd..2bd79be 100644 (file)
@@ -56,3 +56,7 @@ template<typename> struct DelayUpdates;
 template<> struct DelayUpdates<int>;
 template<typename T> struct DelayUpdates<T*>;
 template<typename T> void testDelayUpdates(DelayUpdates<T> *p = 0) {}
+
+void outOfLineInlineUseLeftF(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::f);
+void outOfLineInlineUseLeftG(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::g);
+void outOfLineInlineUseLeftH(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::h);
index c8056d6..5907cbc 100644 (file)
@@ -39,3 +39,7 @@ int defineListDoubleRight() {
 }
 
 template<typename T> struct MergePatternDecl;
+
+void outOfLineInlineUseRightF(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::f);
+void outOfLineInlineUseRightG(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::g);
+void outOfLineInlineUseRightH(void (OutOfLineInline<int>::*)() = &OutOfLineInline<int>::h);
index 168155b..1216266 100644 (file)
@@ -31,3 +31,12 @@ template<bool, bool> struct ExplicitInstantiation {
 };
 
 template<typename> struct DelayUpdates {};
+
+template<typename T> struct OutOfLineInline {
+  void f();
+  void g();
+  void h();
+};
+template<typename T> inline void OutOfLineInline<T>::f() {}
+template<typename T> inline void OutOfLineInline<T>::g() {}
+template<typename T> inline void OutOfLineInline<T>::h() {}
index 350d88e..645e171 100644 (file)
@@ -4,6 +4,12 @@
 // expected-no-diagnostics
 
 @import templates_left;
+
+void testInlineRedeclEarly() {
+  // instantiate definition now, we'll add another declaration in _right.
+  OutOfLineInline<int>().h();
+}
+
 @import templates_right;
 
 // CHECK: @list_left = global { %{{.*}}*, i32, [4 x i8] } { %{{.*}}* null, i32 8,
@@ -39,6 +45,14 @@ void testRedeclDefinition() {
   redeclDefinitionEmit();
 }
 
+void testInlineRedecl() {
+  outOfLineInlineUseLeftF();
+  outOfLineInlineUseRightG();
+
+  outOfLineInlineUseRightF();
+  outOfLineInlineUseLeftG();
+}
+
 // CHECK-NOT: @_ZN21ExplicitInstantiationILb0ELb0EE1fEv(
 // CHECK: declare {{.*}}@_ZN21ExplicitInstantiationILb1ELb0EE1fEv(
 // CHECK: define {{.*}}@_ZN21ExplicitInstantiationILb1ELb1EE1fEv(