!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())
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>
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) =
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);
}
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);
};
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() {}
// 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,
redeclDefinitionEmit();
}
+void testInlineRedecl() {
+ outOfLineInlineUseLeftF();
+ outOfLineInlineUseRightG();
+
+ outOfLineInlineUseRightF();
+ outOfLineInlineUseLeftG();
+}
+
// CHECK-NOT: @_ZN21ExplicitInstantiationILb0ELb0EE1fEv(
// CHECK: declare {{.*}}@_ZN21ExplicitInstantiationILb1ELb0EE1fEv(
// CHECK: define {{.*}}@_ZN21ExplicitInstantiationILb1ELb1EE1fEv(