loladiro updated this revision to Diff 36332. loladiro added a comment. Address review comments. I had to add a special case to checkNewAttributesAfterDef if we want to use attribute merging for explicit template instantiations, because the Microsoft ABI allows adding dll attributes to the explicit template definition, but not the declaration (which clang considers to be the record's definition).
Repository: rL LLVM http://reviews.llvm.org/D13330 Files: include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AttributeList.h lib/AST/ASTContext.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp lib/Sema/SemaTemplate.cpp test/CodeGenCXX/unique-instantiation.cpp test/SemaCXX/unique-instantiations.cpp
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<int>; // expected-error{{requires a previous declaration}} + +template < typename T > struct foo2 { }; +extern template struct foo2<int>; // expected-note{{previous explicit instantiation is here}} +template struct __attribute__((unique_instantiation)) foo2<int>; // expected-error{{must be specified on all declarations}} + +template < typename T > struct foo3 { }; +extern template struct __attribute__((unique_instantiation)) foo3<int>; // expected-note{{previous explicit instantiation is here}} +extern template struct foo3<int>; // 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<int>; // expected-note{{previous explicit instantiation is here}} +template struct foo5<int>; // expected-error{{must be specified on all declarations}} Index: test/CodeGenCXX/unique-instantiation.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/unique-instantiation.cpp @@ -0,0 +1,15 @@ +// 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; } +}; +// CHECK: define i32 @_ZN3fooIiE4getXEv +// CHECK-NOT: define weak_odr i32 @_ZN3fooIiE4getXEv +extern template struct __attribute__((unique_instantiation)) foo<int>; +template struct __attribute__((unique_instantiation)) foo<int>; + +int footest() { + auto var = foo<int>{5}; + return var.getX(); +} Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -7321,20 +7321,22 @@ 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. Specialization->setLexicalDeclContext(CurContext); CurContext->addDecl(Specialization); // 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; } @@ -7388,14 +7390,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; Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4529,6 +4529,29 @@ Attr.getAttributeSpellingListIndex())); } + +static void handleUniqueInstantiation(Sema &S, Decl *D, + const AttributeList &Attr) { + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(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); + } + handleSimpleAttribute<UniqueInstantiationAttr>(S,D,Attr); + } else { + 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. @@ -4904,6 +4927,9 @@ case AttributeList::AT_Weak: handleSimpleAttribute<WeakAttr>(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/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<UniqueInstantiationAttr>() && New->hasAttr<UniqueInstantiationAttr>()) { + S.Diag(New->getAttr<UniqueInstantiationAttr>()->getLocation(),diag::err_unique_instantiation_not_previous); + S.Diag(Old->getLocStart(),diag::note_previous_explicit_instantiation); + } else if (!New->hasAttr<UniqueInstantiationAttr>() && Old->hasAttr<UniqueInstantiationAttr>()) { + 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, bool Override) { InheritableAttr *NewAttr = nullptr; @@ -2263,6 +2274,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<ClassTemplateSpecializationDecl>(New)) { + TemplateSpecializationKind kind = CTSD->getSpecializationKind(); + if (kind == TSK_ExplicitInstantiationDeclaration || + kind == TSK_ExplicitInstantiationDefinition) + return; + } + const Decl *Def = getDefinition(Old); if (!Def || Def == New) return; @@ -2356,6 +2379,8 @@ // attributes declared post-definition are currently ignored checkNewAttributesAfterDef(*this, New, Old); + checkUniqueInstantiationAttrs(*this, New, Old); + if (!Old->hasAttrs()) return; Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8240,6 +8240,14 @@ break; case TSK_ExplicitInstantiationDefinition: + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(FD->getDeclContext())) { + if (CTSD->hasAttr<UniqueInstantiationAttr>()) { + // 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: @@ -8340,6 +8348,11 @@ : GVA_StrongExternal; case TSK_ExplicitInstantiationDefinition: + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(VD->getDeclContext())) { + if (CTSD->hasAttr<UniqueInstantiationAttr>()) { + return GVA_StrongExternal; + } + } return GVA_StrongODR; case TSK_ExplicitInstantiationDeclaration: Index: include/clang/Sema/AttributeList.h =================================================================== --- include/clang/Sema/AttributeList.h +++ include/clang/Sema/AttributeList.h @@ -852,7 +852,8 @@ ExpectedStructOrUnionOrTypedef, ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, - ExpectedKernelFunction + ExpectedKernelFunction, + ExpectedExplicitInstantiation, }; } // end namespace clang Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2341,7 +2341,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}1">, InGroup<IgnoredAttributes>; def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>; def warn_type_attribute_wrong_type : Warning< @@ -2440,6 +2441,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/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -181,9 +181,9 @@ def EnableIfDocs : Documentation { let Category = DocCatFunction; let Content = [{ -.. Note:: Some features of this attribute are experimental. The meaning of - multiple enable_if attributes on a single declaration is subject to change in - a future version of clang. Also, the ABI is not standardized and the name +.. Note:: Some features of this attribute are experimental. The meaning of + multiple enable_if attributes on a single declaration is subject to change in + a future version of clang. Also, the ABI is not standardized and the name mangling may change in future versions. To avoid that, use asm labels. The ``enable_if`` attribute can be placed on function declarations to control @@ -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<int>; + + // Explicit template definition (in exactly ONE .cpp file) + template struct __attribute__((unique_instantiation)) my_template<int>; + + +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/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<[CXXRecord]>; + let Documentation = [UniqueInstantiationDocs]; +} + def WeakImport : InheritableAttr { let Spellings = [GNU<"weak_import">]; let Documentation = [Undocumented];
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits