loladiro updated this revision to Diff 41265.
loladiro added a comment.

Add support for variable templates


http://reviews.llvm.org/D13330

Files:
  include/clang/AST/ASTContext.h
  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/CodeGen/CGVTables.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaTemplate.cpp
  test/CodeGenCXX/unique-instantiation.cpp
  test/SemaCXX/unique-instantiations.cpp
  utils/TableGen/ClangAttrEmitter.cpp

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:
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 <typename T>
+struct foo {};
+extern template struct __attribute__((unique_instantiation)) foo<int>;
+template struct __attribute__((unique_instantiation)) foo<int>;
+
+template <typename T>
+T pi = T(3.1415926535897932385);
+extern template __attribute__((unique_instantiation)) float pi<float>;
+template __attribute__((unique_instantiation)) float pi<float>;
+
+// 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 <typename T>
+struct foo1 {};
+template struct __attribute__((unique_instantiation)) foo1<int>; // expected-error{{'unique_instantiation' attribute on an explicit instantiation requires a previous explicit instantiation declaration}}
+
+template <typename T>
+T pi1 = T(3.1415926535897932385);
+template __attribute__((unique_instantiation)) float pi1<float>; // expected-error{{'unique_instantiation' attribute on an explicit instantiation requires a previous explicit instantiation 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{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}}
+
+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{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}}
+
+template <typename T>
+struct __attribute__((unique_instantiation)) foo4 {}; // expected-error{{'unique_instantiation' attribute 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{{'unique_instantiation' attribute must be specified for all declarations and definitions of this explicit template instantiation}}
+
+template <typename T>
+struct foo6 {};
+extern template struct __attribute__((unique_instantiation(16))) foo6<int>; // expected-error{{'unique_instantiation' attribute takes no arguments}}
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 <typename T>
+T pi = T(3.1415926535897932385);
+extern template __attribute__((unique_instantiation)) float pi<float>;
+template __attribute__((unique_instantiation)) float pi<float>;
+
+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<int>;
+template struct __attribute__((unique_instantiation)) foo<int>;
+
+extern template __attribute__((unique_instantiation)) int bar<int>();
+
+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>();
+
+int footest() {
+  auto var = foo<int>{5};
+  auto var2 = foo<int>::bar{5};
+  auto x = bar<int>();
+  return var.getX();
+}
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -7398,20 +7398,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;
   }
 
@@ -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<VarDecl>(Res.get());
+
+      HadDeclaration = Prev->getTemplateSpecializationKind() ==
+                       TSK_ExplicitInstantiationDeclaration;
+      OldUniqueInstantiation = Prev->getAttr<UniqueInstantiationAttr>();
+      if (OldUniqueInstantiation) {
+        OldUniqueInstantiationLoc = OldUniqueInstantiation->getLocation();
+        Prev->dropAttr<UniqueInstantiationAttr>();
+      }
     }
 
     // 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<UniqueInstantiationAttr>(),
+                                    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<UniqueInstantiationAttr>();
+  if (OldUniqueInstantiation) {
+    OldUniqueInstantiationLoc = OldUniqueInstantiation->getLocation();
+    Specialization->dropAttr<UniqueInstantiationAttr>();
+  }
+
   Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc());
   AttributeList *Attr = D.getDeclSpec().getAttributes().getList();
   if (Attr)
     ProcessDeclAttributeList(S, Specialization, Attr);
 
+  DiagnoseUniqueInstantiation(
+      *this, D, TSK, HadDeclaration,
+      Specialization->getAttr<UniqueInstantiationAttr>(),
+      OldUniqueInstantiation, OldUniqueInstantiationLoc);
+
   if (Specialization->isDefined()) {
     // Let the ASTConsumer know that this function has been explicitly
     // instantiated now, and its linkage might have changed.
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<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);
+    }
+    return handleSimpleAttribute<UniqueInstantiationAttr>(S, D, Attr);
+  } else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+    if (FD->getTemplateSpecializationKind() ==
+            TSK_ExplicitInstantiationDefinition ||
+        FD->getTemplateSpecializationKind() ==
+            TSK_ExplicitInstantiationDeclaration)
+      return handleSimpleAttribute<UniqueInstantiationAttr>(S, D, Attr);
+  } else if (auto *VD = dyn_cast<VarDecl>(D)) {
+    if (VD->getTemplateSpecializationKind() ==
+            TSK_ExplicitInstantiationDefinition ||
+        VD->getTemplateSpecializationKind() ==
+            TSK_ExplicitInstantiationDeclaration)
+      return handleSimpleAttribute<UniqueInstantiationAttr>(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<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
@@ -2188,6 +2188,19 @@
   return AnyAdded;
 }
 
+static void checkUniqueInstantiationAttrs(Sema &S, const Decl *New,
+                                          const Decl *Old) {
+  Attr *NewAttr = New->getAttr<UniqueInstantiationAttr>();
+
+  // Check that any previous definitions also had this attribute set.
+  if (Old->hasAttr<UniqueInstantiationAttr>() == (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<ClassTemplateSpecializationDecl>(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/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<DLLExportAttr>()) {
+  if (RD->hasAttr<UniqueInstantiationAttr>() ||
+      Context.containedInUniqueInstantiation(RD)) {
+    NonDiscardableODRLinkage = llvm::GlobalVariable::ExternalLinkage;
+  } else if (RD->hasAttr<DLLExportAttr>()) {
     // Cannot discard exported vtables.
     DiscardableODRLinkage = NonDiscardableODRLinkage;
   } else if (RD->hasAttr<DLLImportAttr>()) {
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<RecordDecl>(D->getDeclContext()))) {
+    auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+    if (CTSD && CTSD->hasAttr<UniqueInstantiationAttr>())
+      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<UniqueInstantiationAttr>() ||
+        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<UniqueInstantiationAttr>() ||
+        Context.containedInUniqueInstantiation(VD))
+      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
@@ -853,7 +853,9 @@
   ExpectedStructOrTypedef,
   ExpectedObjectiveCInterfaceOrProtocol,
   ExpectedKernelFunction,
-  ExpectedFunctionWithProtoType
+  ExpectedFunctionWithProtoType,
+  ExpectedExplicitInstantiation,
+  ExpectedFunctionOrClass
 };
 
 }  // end namespace clang
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<IgnoredAttributes>;
 def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>;
 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/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<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
@@ -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/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);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to