Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2355,6 +2355,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 @@ -1475,6 +1475,12 @@ let Documentation = [Undocumented]; } +def UniqueInstantiation : InheritableAttr { + let Spellings = [GNU<"unique_instantiation">]; + let Subjects = SubjectList<[Function, CXXRecord], ErrorDiag>; + 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 @@ -1620,3 +1620,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 @@ -2348,7 +2348,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< @@ -2451,6 +2452,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 only applies to an explicit template declaration or instantiation">; +def err_unique_instantiation_no_declaration : Error< + "'unique_instantiation' attribute on an explicit instantiation requires a previous explicit instantiation declaration">; +def err_unique_instantiation_not_previous : Error< + "'unique_instantiation' attribute must be specified for all declarations and definitions of this 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 @@ -8241,6 +8241,17 @@ return getFunctionType(ResType, ArgTypes, EPI); } +bool ASTContext::containedInUniqueInstantiation(const Decl *D) { + const RecordDecl *RD; + while ((RD = dyn_cast(D->getDeclContext()))) { + auto *CTSD = dyn_cast(RD); + if (CTSD && CTSD->hasAttr()) + return true; + D = RD; + } + return false; +} + static GVALinkage basicGVALinkageForFunction(const ASTContext &Context, const FunctionDecl *FD) { if (!FD->isExternallyVisible()) @@ -8254,6 +8265,14 @@ 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: @@ -8356,6 +8375,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,10 @@ llvm::Function::InternalLinkage; case TSK_ExplicitInstantiationDefinition: + // If the key function has strong linkage (say due to + // UniqueInstantiationAttr), the VTable should too. + if (Context.containedInUniqueInstantiation(keyFunction)) + return llvm::GlobalVariable::ExternalLinkage; return !Context.getLangOpts().AppleKext ? llvm::GlobalVariable::WeakODRLinkage : llvm::Function::InternalLinkage; @@ -757,7 +761,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,19 @@ return AnyAdded; } +static void checkUniqueInstantiationAttrs(Sema &S, const Decl *New, + const Decl *Old) { + Attr *NewAttr = New->getAttr(); + + // Check that any previous definitions also had this attribute set. + if (Old->hasAttr() == (NewAttr != nullptr)) + return; + + SourceLocation NewLoc = NewAttr ? NewAttr->getLocation() : New->getLocStart(); + S.Diag(NewLoc, 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 +2279,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 +2384,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 @@ -4634,6 +4634,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. @@ -5009,6 +5035,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,35 @@ return (Decl*) nullptr; } + bool HadDeclaration = Specialization->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDeclaration; + auto *OldUniqueInstantiation = + Specialization->getAttr(); + SourceLocation OldUniqueInstantiationLoc; + if (OldUniqueInstantiation) { + OldUniqueInstantiationLoc = OldUniqueInstantiation->getLocation(); + Specialization->dropAttr(); + } + Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); AttributeList *Attr = D.getDeclSpec().getAttributes().getList(); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + auto *NewUniqueInstantiation = + Specialization->getAttr(); + if (NewUniqueInstantiation) { + if (TSK == TSK_ExplicitInstantiationDefinition && !HadDeclaration) + Diag(NewUniqueInstantiation->getLocation(), + diag::err_unique_instantiation_no_declaration); + else if (HadDeclaration && !OldUniqueInstantiation) + Diag(NewUniqueInstantiation->getLocation(), + diag::err_unique_instantiation_not_previous); + } else if (OldUniqueInstantiation) { + 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,38 @@ +// RUN: %clang -std=c++11 -emit-llvm -c -S -o - %s | FileCheck %s + +template +struct foo { + T x; + T getX() { return x; } + struct bar { + T y; + bar(T y) : y(y) {} + }; +}; +template +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 +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,28 @@ +// RUN: %clang -cc1 -std=c++11 -fsyntax-only -verify %s + +template +struct foo1 {}; +template struct __attribute__((unique_instantiation)) foo1; // expected-error{{'unique_instantiation' attribute on an explicit instantiation requires a previous explicit instantiation declaration}} + +template +struct foo2 {}; +extern template struct foo2; // expected-note{{previous explicit instantiation is here}} +template struct __attribute__((unique_instantiation)) foo2; // expected-error{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}} + +template +struct foo3 {}; +extern template struct __attribute__((unique_instantiation)) foo3; // expected-note{{previous explicit instantiation is here}} +extern template struct foo3; // expected-error{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}} + +template +struct __attribute__((unique_instantiation)) foo4 {}; // expected-error{{'unique_instantiation' attribute only applies to explicit template declarations or definitions}} + +template +struct foo5 {}; +extern template struct __attribute__((unique_instantiation)) foo5; // expected-note{{previous explicit instantiation is here}} +template struct foo5; // expected-error{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}} + +template +struct foo6 {}; +extern template struct __attribute__((unique_instantiation(16))) foo6; // expected-error{{'unique_instantiation' attribute takes no arguments}} +template struct __attribute__((unique_instantiation("Hello World"))) foo6; // expected-error{{'unique_instantiation' attribute takes no arguments}} Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -2371,6 +2371,8 @@ case Func | ObjCMethod | Param: return "ExpectedFunctionMethodOrParameter"; case Func | ObjCMethod: return "ExpectedFunctionOrMethod"; case Func | Var: return "ExpectedVariableOrFunction"; + case Func | Class: + return "ExpectedFunctionOrClass"; // If not compiling for C++, the class portion does not apply. case Func | Var | Class: