Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2360,6 +2360,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 @@ -1489,6 +1489,12 @@ let Documentation = [Undocumented]; } +def UniqueInstantiation : InheritableAttr { + let Spellings = [GNU<"unique_instantiation">]; + let Subjects = SubjectList<[Var, 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 @@ -1719,6 +1719,39 @@ return callee(); // This call is tail-call optimized. } }; + }]; +} + +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 @@ -2424,7 +2424,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|non-K&R-style functions}1">, + "interface or protocol declarations|kernel functions|non-K&R-style 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< @@ -2529,6 +2530,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 @@ -853,7 +853,9 @@ ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, ExpectedKernelFunction, - ExpectedFunctionWithProtoType + ExpectedFunctionWithProtoType, + ExpectedExplicitInstantiation, + ExpectedFunctionOrClass }; } // end namespace clang Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8254,6 +8254,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()) @@ -8267,6 +8278,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: @@ -8369,6 +8388,9 @@ : GVA_StrongExternal; case TSK_ExplicitInstantiationDefinition: + if (VD->hasAttr() || + 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 @@ -741,6 +741,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; @@ -759,7 +763,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 @@ -2188,6 +2188,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) { @@ -2289,6 +2302,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; @@ -2382,6 +2407,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 @@ -4757,6 +4757,38 @@ D->addAttr(Internal); } +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); + } else if (auto *VD = dyn_cast(D)) { + if (VD->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDefinition || + VD->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. @@ -5138,6 +5170,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 @@ -7398,10 +7398,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. @@ -7410,8 +7414,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; } @@ -7465,14 +7467,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; @@ -7602,6 +7597,26 @@ return TagD; } +static void +DiagnoseUniqueInstantiation(Sema &S, Declarator &D, + TemplateSpecializationKind TSK, bool HadDeclaration, + UniqueInstantiationAttr *NewUniqueInstantiation, + UniqueInstantiationAttr *OldUniqueInstantiation, + clang::SourceLocation OldUniqueInstantiationLoc) { + if (NewUniqueInstantiation) { + if (TSK == TSK_ExplicitInstantiationDefinition && !HadDeclaration) + S.Diag(NewUniqueInstantiation->getLocation(), + diag::err_unique_instantiation_no_declaration); + else if (HadDeclaration && !OldUniqueInstantiation) + S.Diag(NewUniqueInstantiation->getLocation(), + diag::err_unique_instantiation_not_previous); + } else if (OldUniqueInstantiation) { + S.Diag(D.getIdentifierLoc(), diag::err_unique_instantiation_not_previous); + S.Diag(OldUniqueInstantiationLoc, + diag::note_previous_explicit_instantiation); + } +} + DeclResult Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, SourceLocation TemplateLoc, @@ -7676,6 +7691,11 @@ LookupResult Previous(*this, NameInfo, LookupOrdinaryName); LookupParsedName(Previous, S, &D.getCXXScopeSpec()); + // For diagnosing incorrect uses of unique_instantiation + UniqueInstantiationAttr *OldUniqueInstantiation = nullptr; + SourceLocation OldUniqueInstantiationLoc; + bool HadDeclaration; + if (!R->isFunctionType()) { // C++ [temp.explicit]p1: // A [...] static data member of a class template can be explicitly @@ -7748,6 +7768,14 @@ // Ignore access control bits, we don't need them for redeclaration // checking. Prev = cast(Res.get()); + + HadDeclaration = Prev->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDeclaration; + OldUniqueInstantiation = Prev->getAttr(); + if (OldUniqueInstantiation) { + OldUniqueInstantiationLoc = OldUniqueInstantiation->getLocation(); + Prev->dropAttr(); + } } // C++0x [temp.explicit]p2: @@ -7780,11 +7808,17 @@ // Instantiate static data member or variable template. Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); + // Merge attributes. + if (AttributeList *Attr = D.getDeclSpec().getAttributes().getList()) + ProcessDeclAttributeList(S, Prev, Attr); + if (PrevTemplate) { - // Merge attributes. - if (AttributeList *Attr = D.getDeclSpec().getAttributes().getList()) - ProcessDeclAttributeList(S, Prev, Attr); + DiagnoseUniqueInstantiation(*this, D, TSK, HadDeclaration, + Prev->getAttr(), + OldUniqueInstantiation, + OldUniqueInstantiationLoc); } + if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateVariableDefinition(D.getIdentifierLoc(), Prev); } @@ -7923,11 +7957,24 @@ return (Decl*) nullptr; } + HadDeclaration = Specialization->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDeclaration; + OldUniqueInstantiation = Specialization->getAttr(); + if (OldUniqueInstantiation) { + OldUniqueInstantiationLoc = OldUniqueInstantiation->getLocation(); + Specialization->dropAttr(); + } + Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); AttributeList *Attr = D.getDeclSpec().getAttributes().getList(); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + DiagnoseUniqueInstantiation( + *this, D, TSK, HadDeclaration, + Specialization->getAttr(), + OldUniqueInstantiation, OldUniqueInstantiationLoc); + 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,45 @@ +// RUN: %clang -std=c++14 -emit-llvm -c -S -o - %s | FileCheck %s + +// CHECK: @_Z2piIfE = global float +// CHECK-NOT: @_Z2piIfE = weak_odr global float +template +T pi = T(3.1415926535897932385); +extern template __attribute__((unique_instantiation)) float pi; +template __attribute__((unique_instantiation)) float pi; + +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,48 @@ +// RUN: %clang -cc1 -std=c++14 -fsyntax-only -verify %s + +// Correct usage +template +struct foo {}; +extern template struct __attribute__((unique_instantiation)) foo; +template struct __attribute__((unique_instantiation)) foo; + +template +T pi = T(3.1415926535897932385); +extern template __attribute__((unique_instantiation)) float pi; +template __attribute__((unique_instantiation)) float pi; + +// Usages on non-templates +float __attribute__((unique_instantiation)) notpi(2.71828182845904523536028747135); // expected-error{{'unique_instantiation' attribute only applies to explicit template declarations or definitions}} +struct __attribute__((unique_instantiation)) bar {}; // expected-error{{'unique_instantiation' attribute only applies to explicit template declarations or definitions}} +void __attribute__((unique_instantiation)) func() {} // expected-error{{'unique_instantiation' attribute only applies to explicit template declarations or definitions}} + +// Usages that violate one of the conditions required conditions +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 +T pi1 = T(3.1415926535897932385); +template __attribute__((unique_instantiation)) float pi1; // 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}} 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: