Explicit instantiation suppresses the instantiation of non-inline
function template specializations and member functions of class
template specializations.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85300 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 8071aba..8efd552 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -1123,7 +1123,17 @@
     return TemplateOrSpecialization.
              dyn_cast<FunctionTemplateSpecializationInfo*>();
   }
-                       
+
+  /// \brief Determines whether this function is a function template
+  /// specialization or a member of a class template specialization that can
+  /// be implicitly instantiated.
+  bool isImplicitlyInstantiable() const;
+              
+  /// \brief Retrieve the function declaration from which this function could
+  /// be instantiated, if it is an instantiation (rather than a non-template
+  /// or a specialization, for example).
+  FunctionDecl *getTemplateInstantiationPattern() const;
+
   /// \brief Retrieve the primary template that this function template
   /// specialization either specializes or was instantiated from.
   ///
@@ -1176,7 +1186,7 @@
   /// instantiated from a template; otherwie, returns an invalid source 
   /// location.
   SourceLocation getPointOfInstantiation() const;
-
+                       
   /// \brief Determine whether this is or was instantiated from an out-of-line 
   /// definition of a member function.
   bool isOutOfLine() const;
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 38eb337..7b0a84c 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -753,6 +753,60 @@
   TemplateOrSpecialization = Info;
 }
 
+bool FunctionDecl::isImplicitlyInstantiable() const {
+  // If this function already has a definition or is invalid, it can't be
+  // implicitly instantiated.
+  if (isInvalidDecl() || getBody())
+    return false;
+  
+  switch (getTemplateSpecializationKind()) {
+  case TSK_Undeclared:
+  case TSK_ExplicitSpecialization:
+  case TSK_ExplicitInstantiationDefinition:
+    return false;
+      
+  case TSK_ImplicitInstantiation:
+    return true;
+
+  case TSK_ExplicitInstantiationDeclaration:
+    // Handled below.
+    break;
+  }
+
+  // Find the actual template from which we will instantiate.
+  const FunctionDecl *PatternDecl = getTemplateInstantiationPattern();
+  Stmt *Pattern = 0;
+  if (PatternDecl)
+    Pattern = PatternDecl->getBody(PatternDecl);
+  
+  // C++0x [temp.explicit]p9:
+  //   Except for inline functions, other explicit instantiation declarations
+  //   have the effect of suppressing the implicit instantiation of the entity
+  //   to which they refer. 
+  if (!Pattern || !PatternDecl)
+    return true;
+
+  return PatternDecl->isInline() || 
+    (isa<CXXMethodDecl>(PatternDecl) && !PatternDecl->isOutOfLine());
+}                      
+   
+FunctionDecl *FunctionDecl::getTemplateInstantiationPattern() const {
+  if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) {
+    while (Primary->getInstantiatedFromMemberTemplate()) {
+      // If we have hit a point where the user provided a specialization of
+      // this template, we're done looking.
+      if (Primary->isMemberSpecialization())
+        break;
+      
+      Primary = Primary->getInstantiatedFromMemberTemplate();
+    }
+    
+    return Primary->getTemplatedDecl();
+  } 
+    
+  return getInstantiatedFromMemberFunction();
+}
+
 FunctionTemplateDecl *FunctionDecl::getPrimaryTemplate() const {
   if (FunctionTemplateSpecializationInfo *Info
         = TemplateOrSpecialization
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 5c3f5e7..f4e58f0 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -6295,21 +6295,21 @@
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
     // Implicit instantiation of function templates and member functions of
     // class templates.
-    if (!Function->getBody() &&
-        Function->getTemplateSpecializationKind() 
-                                                == TSK_ImplicitInstantiation) {
+    if (!Function->getBody() && Function->isImplicitlyInstantiable()) {
       bool AlreadyInstantiated = false;
       if (FunctionTemplateSpecializationInfo *SpecInfo
                                 = Function->getTemplateSpecializationInfo()) {
         if (SpecInfo->getPointOfInstantiation().isInvalid())
           SpecInfo->setPointOfInstantiation(Loc);
-        else
+        else if (SpecInfo->getTemplateSpecializationKind() 
+                   == TSK_ImplicitInstantiation)
           AlreadyInstantiated = true;
       } else if (MemberSpecializationInfo *MSInfo 
                                   = Function->getMemberSpecializationInfo()) {
         if (MSInfo->getPointOfInstantiation().isInvalid())
           MSInfo->setPointOfInstantiation(Loc);
-        else
+        else if (MSInfo->getTemplateSpecializationKind() 
+                   == TSK_ImplicitInstantiation)
           AlreadyInstantiated = true;
       }
       
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c9319c5..8606fb0 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1097,20 +1097,7 @@
     return;
   
   // Find the function body that we'll be substituting.
-  const FunctionDecl *PatternDecl = 0;
-  if (FunctionTemplateDecl *Primary = Function->getPrimaryTemplate()) {
-    while (Primary->getInstantiatedFromMemberTemplate()) {
-      // If we have hit a point where the user provided a specialization of
-      // this template, we're done looking.
-      if (Primary->isMemberSpecialization())
-        break;
-      
-      Primary = Primary->getInstantiatedFromMemberTemplate();
-    }
-    
-    PatternDecl = Primary->getTemplatedDecl();
-  } else
-    PatternDecl = Function->getInstantiatedFromMemberFunction();
+  const FunctionDecl *PatternDecl = Function->getTemplateInstantiationPattern();
   Stmt *Pattern = 0;
   if (PatternDecl)
     Pattern = PatternDecl->getBody(PatternDecl);
diff --git a/test/CXX/temp/temp.spec/temp.explicit/p9.cpp b/test/CXX/temp/temp.spec/temp.explicit/p9.cpp
new file mode 100644
index 0000000..a53113f
--- /dev/null
+++ b/test/CXX/temp/temp.spec/temp.explicit/p9.cpp
@@ -0,0 +1,59 @@
+// RUN: clang-cc -fsyntax-only -std=c++0x -verify %s
+
+template<typename T>
+struct X0 {
+  void f(T &t) {
+    t = 1; // expected-error{{incompatible type}}
+  }
+  
+  void g(T &t);
+  
+  void h(T &t);
+  
+  static T static_var;
+};
+
+template<typename T>
+inline void X0<T>::g(T & t) {
+  t = 1; // expected-error{{incompatible type}}
+}
+
+template<typename T>
+void X0<T>::h(T & t) {
+  t = 1;
+}
+
+template<typename T>
+T X0<T>::static_var = 1;
+
+extern template struct X0<int*>;
+
+int *&test(X0<int*> xi, int *ip) {
+  xi.f(ip); // expected-note{{instantiation}}
+  xi.g(ip); // expected-note{{instantiation}}
+  xi.h(ip);
+  return X0<int*>::static_var;
+}
+
+template<typename T>
+void f0(T& t) {
+  t = 1; // expected-error{{incompatible type}}
+}
+
+template<typename T>
+inline void f1(T& t) {
+  t = 1; // expected-error 2{{incompatible type}}
+}
+
+extern template void f0<>(int *&);
+extern template void f1<>(int *&);
+
+void test_f0(int *ip, float *fp) {
+  f0(ip);
+  f0(fp); // expected-note{{instantiation}}
+}
+
+void test_f1(int *ip, float *fp) {
+  f1(ip); // expected-note{{instantiation}}
+  f1(fp); // expected-note{{instantiation}}
+}