Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2308,6 +2308,7 @@ /// when it is called. void AddDeallocation(void (*Callback)(void*), void *Data); + static bool containedInUniqueInstantiation(const Decl *D); GVALinkage GetGVALinkageForFunction(const FunctionDecl *FD) const; GVALinkage GetGVALinkageForVariable(const VarDecl *VD); Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1458,6 +1458,12 @@ let Documentation = [Undocumented]; } +def UniqueInstantiation : InheritableAttr { + let Spellings = [GNU<"unique_instantiation">]; + let Subjects = SubjectList<[Function, CXXRecord], ErrorDiag, "ExpectedFunctionOrClass">; + let Documentation = [UniqueInstantiationDocs]; +} + def WeakImport : InheritableAttr { let Spellings = [GNU<"weak_import">]; let Documentation = [Undocumented]; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1612,3 +1612,37 @@ arguments, with arbitrary offsets. }]; } + +def UniqueInstantiationDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``unique_instantiation`` attribute may only be applied to explicit template +declarations and definitions, i.e. expressions of the form: + + .. code-block:: c++ + + // Explicit template declaration (usually found in a .h file) + extern template struct __attribute__((unique_instantiation)) my_template; + + // Explicit template definition (in exactly ONE .cpp file) + template struct __attribute__((unique_instantiation)) my_template; + + +When the unique_instantiation attribute is specified on an explicit template +instantiation, the compiler is given license to emit strong symbols for +this specific explicit template instantiation. + +If the attribute is present on one such definition or declaration for a given +entity, it must be present on all. + +Note that to ensure correct execution the user MUST make certain that no +other translation unit has an implicit instantiation of the same entity. In +particular this means that any usage of the entity has to be preceeded by an +appropriate explicit template declaration or definition. + +It is thus recommended that explicit template declarations are placed in headers +to suppress any potential implicit instantiation of the entity. In order to +encourage this programming style, any explicit template definition with this +attribute MUST be preceeded by an appropriate declaration. + }]; +} Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2344,7 +2344,8 @@ "Objective-C instance methods|init methods of interface or class extension declarations|" "variables, functions and classes|Objective-C protocols|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" - "interface or protocol declarations|kernel functions}1">, + "interface or protocol declarations|kernel functions|" + "explicit template declarations or definitions|functions and classes}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< @@ -2447,6 +2448,12 @@ "%plural{0:no parameters to index into|" "1:can only be 1, since there is one parameter|" ":must be between 1 and %2}2">; +def err_unique_instantiation_wrong_decl : Error< + "unique_instantiation attribute on something that is not a explicit template declaration or instantiation.">; +def err_unique_instantiation_no_declaration : Error< + "A unique_instantiation attribute on an explicit template instantiation requires a previous declaration.">; +def err_unique_instantiation_not_previous : Error< + "The unique_instantiation must be specified on all declarations and definitions of a particular explicit template instantiation.">; // Thread Safety Analysis def warn_unlock_but_no_lock : Warning<"releasing %0 '%1' that was not held">, Index: include/clang/Sema/AttributeList.h =================================================================== --- include/clang/Sema/AttributeList.h +++ include/clang/Sema/AttributeList.h @@ -852,7 +852,9 @@ ExpectedStructOrUnionOrTypedef, ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, - ExpectedKernelFunction + ExpectedKernelFunction, + ExpectedExplicitInstantiation, + ExpectedFunctionOrClass }; } // end namespace clang Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8238,6 +8238,19 @@ return getFunctionType(ResType, ArgTypes, EPI); } +bool ASTContext::containedInUniqueInstantiation(const Decl *D) { + const RecordDecl *RD; + while ((RD = dyn_cast(D->getDeclContext()))) { + if (auto *CTSD = dyn_cast(RD)) { + if (CTSD->hasAttr()) { + return true; + } + } + D = RD; + } + return false; +} + static GVALinkage basicGVALinkageForFunction(const ASTContext &Context, const FunctionDecl *FD) { if (!FD->isExternallyVisible()) @@ -8251,6 +8264,13 @@ break; case TSK_ExplicitInstantiationDefinition: + if (FD->hasAttr() || + Context.containedInUniqueInstantiation(FD)) { + // We return GVA_StrongExternal here, instead of going through the logic below, + // because even if the definition is available inline, since the source specified + // an explicit template instantiation, we want to make the symbol available. + return GVA_StrongExternal; + } return GVA_StrongODR; // C++11 [temp.explicit]p10: @@ -8351,6 +8371,8 @@ : GVA_StrongExternal; case TSK_ExplicitInstantiationDefinition: + if (Context.containedInUniqueInstantiation(VD)) + return GVA_StrongExternal; return GVA_StrongODR; case TSK_ExplicitInstantiationDeclaration: Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -739,6 +739,11 @@ llvm::Function::InternalLinkage; case TSK_ExplicitInstantiationDefinition: + // If the key function has strong linkage (say due to + // UniqueInstantiationAttr), the VTable should too. + keyFunction->dump(); + if (Context.containedInUniqueInstantiation(keyFunction)) + return llvm::GlobalVariable::ExternalLinkage; return !Context.getLangOpts().AppleKext ? llvm::GlobalVariable::WeakODRLinkage : llvm::Function::InternalLinkage; @@ -757,7 +762,10 @@ llvm::GlobalValue::LinkOnceODRLinkage; llvm::GlobalVariable::LinkageTypes NonDiscardableODRLinkage = llvm::GlobalValue::WeakODRLinkage; - if (RD->hasAttr()) { + if (RD->hasAttr() || + Context.containedInUniqueInstantiation(RD)) { + NonDiscardableODRLinkage = llvm::GlobalVariable::ExternalLinkage; + } else if (RD->hasAttr()) { // Cannot discard exported vtables. DiscardableODRLinkage = NonDiscardableODRLinkage; } else if (RD->hasAttr()) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2174,6 +2174,17 @@ return AnyAdded; } +static void checkUniqueInstantiationAttrs(Sema &S, const Decl *New, const Decl *Old) { + // Check that any previous definitions also had this attribute set. + if (!Old->hasAttr() && New->hasAttr()) { + S.Diag(New->getAttr()->getLocation(),diag::err_unique_instantiation_not_previous); + S.Diag(Old->getLocStart(),diag::note_previous_explicit_instantiation); + } else if (!New->hasAttr() && Old->hasAttr()) { + S.Diag(New->getLocStart(),diag::err_unique_instantiation_not_previous); + S.Diag(Old->getLocStart(),diag::note_previous_explicit_instantiation); + } +} + static bool mergeDeclAttribute(Sema &S, NamedDecl *D, const InheritableAttr *Attr, Sema::AvailabilityMergeKind AMK) { @@ -2266,6 +2277,18 @@ if (!New->hasAttrs()) return; + // Explicit template instantiations need special handling because in certain + // ABIs explicit template definitions may add attributes over explicit template + // declarations. In clang getDefinition() will get the ClassTemplateSpecializationDecl + // associated with the class template declaration, so we'd give incorrect warnings + // here. + if (auto *CTSD = dyn_cast(New)) { + TemplateSpecializationKind kind = CTSD->getSpecializationKind(); + if (kind == TSK_ExplicitInstantiationDeclaration || + kind == TSK_ExplicitInstantiationDefinition) + return; + } + const Decl *Def = getDefinition(Old); if (!Def || Def == New) return; @@ -2359,6 +2382,8 @@ // attributes declared post-definition are currently ignored checkNewAttributesAfterDef(*this, New, Old); + checkUniqueInstantiationAttrs(*this, New, Old); + if (!Old->hasAttrs()) return; Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4544,6 +4544,32 @@ Attr.getAttributeSpellingListIndex())); } + +static void handleUniqueInstantiation(Sema &S, Decl *D, + const AttributeList &Attr) { + if (auto *CTSD = dyn_cast(D)) { + // If this is an explicit instantiation definition. Check that it was preceeded + // by an ExplicitInstantiationDeclaration. Note, this + // requirement encourages a programming style that uses unique explicit + // instantiation declarations (typically in a header) to suppress + // implicit instantiations of a template or its members, so that the + // unique explicit instantiation definition of that template or its members + // is unique. + if (CTSD->getSpecializationKind() == TSK_ExplicitInstantiationDefinition) { + if (!CTSD->getPreviousDecl()) + S.Diag(Attr.getLoc(),diag::err_unique_instantiation_no_declaration); + } + return handleSimpleAttribute(S,D,Attr); + } else if (auto *FD = dyn_cast(D)) { + if (FD->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDefinition || + FD->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDeclaration) + return handleSimpleAttribute(S,D,Attr); + } + S.Diag(Attr.getLoc(), + diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedExplicitInstantiation; +} + /// Handles semantic checking for features that are common to all attributes, /// such as checking whether a parameter was properly specified, or the correct /// number of arguments were passed, etc. @@ -4919,6 +4945,9 @@ case AttributeList::AT_Weak: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_UniqueInstantiation: + handleUniqueInstantiation(S, D, Attr); + break; case AttributeList::AT_WeakRef: handleWeakRefAttr(S, D, Attr); break; Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -7330,10 +7330,14 @@ Specialization->setExternLoc(ExternLoc); Specialization->setTemplateKeywordLoc(TemplateLoc); Specialization->setRBraceLoc(SourceLocation()); + Specialization->setTemplateSpecializationKind(TSK); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + if (PrevDecl) + mergeDeclAttributes(Specialization, PrevDecl); + // Add the explicit instantiation into its lexical context. However, // since explicit instantiations are never found by name lookup, we // just put it into the declaration context directly. @@ -7342,8 +7346,6 @@ // Syntax is now OK, so return if it has no other effect on semantics. if (HasNoEffect) { - // Set the template specialization kind. - Specialization->setTemplateSpecializationKind(TSK); return Specialization; } @@ -7397,14 +7399,7 @@ } } - // Set the template specialization kind. Make sure it is set before - // instantiating the members which will trigger ASTConsumer callbacks. - Specialization->setTemplateSpecializationKind(TSK); InstantiateClassTemplateSpecializationMembers(TemplateNameLoc, Def, TSK); - } else { - - // Set the template specialization kind. - Specialization->setTemplateSpecializationKind(TSK); } return Specialization; @@ -7855,11 +7850,34 @@ return (Decl*) nullptr; } + bool HadDeclaration = Specialization->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDeclaration; + bool HadUniqueInstantiation = Specialization->hasAttr(); + SourceLocation OldUniqueInstantiationLoc; + if (HadUniqueInstantiation) { + OldUniqueInstantiationLoc = + Specialization->getAttr()->getLocation(); + Specialization->dropAttr(); + } + Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); AttributeList *Attr = D.getDeclSpec().getAttributes().getList(); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + if (Specialization->hasAttr()) { + if (TSK == TSK_ExplicitInstantiationDefinition && !HadDeclaration) + Diag(Specialization->getAttr()->getLocation(), + diag::err_unique_instantiation_no_declaration); + else if (HadDeclaration && !HadUniqueInstantiation) { + Diag(Specialization->getAttr()->getLocation(), + diag::err_unique_instantiation_not_previous); + } + } else if (HadUniqueInstantiation) { + Diag(D.getIdentifierLoc(),diag::err_unique_instantiation_not_previous); + Diag(OldUniqueInstantiationLoc,diag::note_previous_explicit_instantiation); + } + if (Specialization->isDefined()) { // Let the ASTConsumer know that this function has been explicitly // instantiated now, and its linkage might have changed. Index: test/CodeGenCXX/unique-instantiation.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/unique-instantiation.cpp @@ -0,0 +1,35 @@ +// RUN: %clang -std=c++11 -emit-llvm -O0 -c -S -o - %s | FileCheck %s + +template < typename T > struct foo { + T x; + T getX() { return x; } + struct bar { + T y; + bar(T y) : y(y) {} + }; +}; +template < typename T > T bar(); + +// CHECK: define i32 @_ZN3fooIiE4getXEv +// CHECK: define void @_ZN3fooIiE3barC2Ei +// CHECK-NOT: define weak_odr i32 @_ZN3fooIiE4getXEv +// CHECK-NOT: define weak_odr void @_ZN3fooIiE3barC2Ei +extern template struct __attribute__((unique_instantiation)) foo; +template struct __attribute__((unique_instantiation)) foo; + +extern template __attribute__((unique_instantiation)) int bar(); + +template < typename T > T bar() { + return (T)0; +} + +// CHECK: define i32 @_Z3barIiET_v() +// CHECK-NOT: define weak_odr i32 @_Z3barIiET_v() +template __attribute__((unique_instantiation)) int bar(); + +int footest() { + auto var = foo{5}; + auto var2 = foo::bar{5}; + auto x = bar(); + return var.getX(); +} Index: test/SemaCXX/unique-instantiations.cpp =================================================================== --- /dev/null +++ test/SemaCXX/unique-instantiations.cpp @@ -0,0 +1,18 @@ +// RUN: %clang -cc1 -std=c++11 -fsyntax-only -verify %s + +template < typename T > struct foo1 { }; +template struct __attribute__((unique_instantiation)) foo1; // expected-error{{requires a previous declaration}} + +template < typename T > struct foo2 { }; +extern template struct foo2; // expected-note{{previous explicit instantiation is here}} +template struct __attribute__((unique_instantiation)) foo2; // expected-error{{must be specified on all declarations}} + +template < typename T > struct foo3 { }; +extern template struct __attribute__((unique_instantiation)) foo3; // expected-note{{previous explicit instantiation is here}} +extern template struct foo3; // expected-error{{must be specified on all declarations}} + +template < typename T > struct __attribute__((unique_instantiation)) foo4 { }; // expected-error{{only applies to explicit template declarations or definitions}} + +template < typename T > struct foo5 { }; +extern template struct __attribute__((unique_instantiation)) foo5; // expected-note{{previous explicit instantiation is here}} +template struct foo5; // expected-error{{must be specified on all declarations}}