loladiro created this revision.
loladiro added a reviewer: doug.gregor.
loladiro added a subscriber: cfe-commits.
loladiro set the repository for this revision to rL LLVM.

This implements a proposal by Doug Gregor on cfe-dev a couple of years ago, to 
allow the compiler to emit strong symbols for explicit template instantiations. 
Doug's spec, which I have tried to follow can be found here: 
http://lists.llvm.org/pipermail/cfe-dev/attachments/20111203/5e1c6c35/attachment.html.
 I usually work over in LLVM-land and have only contributed the occasional bug 
fix to clang, so please let me know how this patch can be improved. I'm also 
not fully done testing it yet (only did so on the toy example I included as a 
test case), but I wanted to get this out there to get feedback. 




Repository:
  rL LLVM

http://reviews.llvm.org/D13330

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/ASTContext.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,22 @@
+// 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{{not a explicit template declaration}}
+
+template < typename T > struct foo5 { };
+extern template struct foo5<int>; // expected-note{{previous explicit instantiation is here}}
+template struct __attribute__((unique_instantiation)) foo5<int>; // expected-error{{must be specified on all declarations}}
+
+template < typename T > struct foo6 { };
+extern template struct  __attribute__((unique_instantiation)) foo6<int>; // expected-note{{previous explicit instantiation is here}}
+template struct foo6<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,25 @@
   Specialization->setExternLoc(ExternLoc);
   Specialization->setTemplateKeywordLoc(TemplateLoc);
   Specialization->setRBraceLoc(SourceLocation());
+  Specialization->setTemplateSpecializationKind(TSK);
 
   if (Attr)
     ProcessDeclAttributeList(S, Specialization, Attr);
 
+  if (PrevDecl && PrevDecl->hasAttr<UniqueInstantiationAttr>() &&
+      !Specialization->hasAttr<UniqueInstantiationAttr>()) {
+    Diag(Specialization->getLocStart(),diag::err_unique_instantiation_not_previous);
+    Diag(PrevDecl->getLocStart(),diag::note_previous_explicit_instantiation);
+  }
+
   // 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 +7393,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,40 @@
                                 Attr.getAttributeSpellingListIndex()));
 }
 
+
+static void handleUniqueInstantiation(Sema &S, Decl *D,
+                                      const AttributeList &Attr) {
+  ClassTemplateSpecializationDecl *CTSD;
+  if ((CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))) {
+    // If this is an explicit instantiation definition. Check that it was preceeded
+    // by an ExplicitInstantiationDeclaration.
+    if (CTSD->getSpecializationKind() == TSK_ExplicitInstantiationDefinition) {
+      if (!CTSD->getPreviousDecl())
+        S.Diag(Attr.getLoc(),diag::err_unique_instantiation_no_declaration);
+    }
+    // Now check that any previous definitions also had this attribute set. This
+    // implicitly checks that all declarations have this attribute set, since we will
+    // have performed the same check on the previous declaration here.
+    CXXRecordDecl *Previous = CTSD->getPreviousDecl();
+    if (Previous) {
+      if (!isa<ClassTemplateSpecializationDecl>(Previous)) {
+        // - ClassTemplateSpecializationDecl 0xbad  <- We want this
+        //    - UniqueInstantiationAttr
+        //    - CXXRecordDecl 0xbeef prev 0xbad     <- We have this
+        Previous = Previous->getPreviousDecl();
+      }
+      assert(isa<ClassTemplateSpecializationDecl>(Previous));
+      if (!Previous->hasAttr<UniqueInstantiationAttr>()) {
+        S.Diag(Attr.getLoc(),diag::err_unique_instantiation_not_previous);
+        S.Diag(Previous->getLocStart(),diag::note_previous_explicit_instantiation);
+      }
+    }
+    handleSimpleAttribute<UniqueInstantiationAttr>(S,D,Attr);
+  } else {
+    S.Diag(Attr.getLoc(),diag::err_unique_instantiation_wrong_decl);
+  }
+}
+
 /// 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 +4938,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/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -8233,14 +8233,23 @@
     return GVA_Internal;
 
   GVALinkage External = GVA_StrongExternal;
+  const ClassTemplateSpecializationDecl *CTSD;
   switch (FD->getTemplateSpecializationKind()) {
   case TSK_Undeclared:
   case TSK_ExplicitSpecialization:
     External = GVA_StrongExternal;
     break;
 
   case TSK_ExplicitInstantiationDefinition:
-    return GVA_StrongODR;
+    CTSD = dyn_cast<ClassTemplateSpecializationDecl>(FD->getDeclContext());
+    if (!CTSD || !CTSD->hasAttr<UniqueInstantiationAttr>())
+      return GVA_StrongODR;
+    else {
+      // 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;
+    }
 
   // C++11 [temp.explicit]p10:
   //   [ Note: The intent is that an inline function that is the subject of
@@ -8330,6 +8339,7 @@
   if (Context.isMSStaticDataMemberInlineDefinition(VD))
     return GVA_DiscardableODR;
 
+  const ClassTemplateSpecializationDecl *CTSD;
   switch (VD->getTemplateSpecializationKind()) {
   case TSK_Undeclared:
     return GVA_StrongExternal;
@@ -8340,7 +8350,11 @@
                : GVA_StrongExternal;
 
   case TSK_ExplicitInstantiationDefinition:
-    return GVA_StrongODR;
+    CTSD = dyn_cast<ClassTemplateSpecializationDecl>(VD->getDeclContext());
+    if (!CTSD || !CTSD->hasAttr<UniqueInstantiationAttr>())
+      return GVA_StrongODR;
+    else
+      return GVA_StrongExternal;
 
   case TSK_ExplicitInstantiationDeclaration:
     return GVA_AvailableExternally;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2440,6 +2440,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/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1458,6 +1458,12 @@
   let Documentation = [Undocumented];
 }
 
+def UniqueInstantiation : InheritableAttr {
+  let Spellings = [GCC<"unique_instantiation">];
+  let Subjects = SubjectList<[CXXRecord]>;
+  let Documentation = [Undocumented];
+}
+
 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

Reply via email to