For instantiations of static data members of class templates, keep
track of the kind of specialization or instantiation. Also, check the
scope of the specialization and ensure that a specialization
declaration without an initializer is not a definition.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83533 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 1fa492c..85b4fd6 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -232,9 +232,10 @@
   InitBuiltinType(NullPtrTy,           BuiltinType::NullPtr);
 }
 
-VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
+MemberSpecializationInfo *
+ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
   assert(Var->isStaticDataMember() && "Not a static data member");
-  llvm::DenseMap<VarDecl *, VarDecl *>::iterator Pos
+  llvm::DenseMap<VarDecl *, MemberSpecializationInfo *>::iterator Pos
     = InstantiatedFromStaticDataMember.find(Var);
   if (Pos == InstantiatedFromStaticDataMember.end())
     return 0;
@@ -243,12 +244,14 @@
 }
 
 void
-ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) {
+ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
+                                                TemplateSpecializationKind TSK) {
   assert(Inst->isStaticDataMember() && "Not a static data member");
   assert(Tmpl->isStaticDataMember() && "Not a static data member");
   assert(!InstantiatedFromStaticDataMember[Inst] &&
          "Already noted what static data member was instantiated from");
-  InstantiatedFromStaticDataMember[Inst] = Tmpl;
+  InstantiatedFromStaticDataMember[Inst] 
+    = new (*this) MemberSpecializationInfo(Tmpl, TSK);
 }
 
 UnresolvedUsingDecl *
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index a5c9fa4..5584e4c 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -371,7 +371,26 @@
 }
 
 VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
-  return getASTContext().getInstantiatedFromStaticDataMember(this);
+  if (MemberSpecializationInfo *MSI
+        = getASTContext().getInstantiatedFromStaticDataMember(this))
+    return cast<VarDecl>(MSI->getInstantiatedFrom());
+  
+  return 0;
+}
+
+TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() {
+  if (MemberSpecializationInfo *MSI
+        = getASTContext().getInstantiatedFromStaticDataMember(this))
+    return MSI->getTemplateSpecializationKind();
+  
+  return TSK_Undeclared;
+}
+
+void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
+  MemberSpecializationInfo *MSI
+    = getASTContext().getInstantiatedFromStaticDataMember(this);
+  assert(MSI && "Not an instantiated static data member?");
+  MSI->setTemplateSpecializationKind(TSK);
 }
 
 bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 6142eac..08cb06d 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2519,8 +2519,7 @@
                                            unsigned NumExplicitTemplateArgs,
                                            SourceLocation RAngleLoc,
                                            NamedDecl *&PrevDecl);
-  bool CheckMemberFunctionSpecialization(CXXMethodDecl *FD, 
-                                         NamedDecl *&PrevDecl);
+  bool CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl);
     
   virtual DeclResult
   ActOnExplicitInstantiation(Scope *S,
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d612bb8..2070335 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2193,7 +2193,6 @@
 
   // Match up the template parameter lists with the scope specifier, then
   // determine whether we have a template or a template specialization.
-  // FIXME: Actually record when this is an explicit specialization!
   bool isExplicitSpecialization = false;
   if (TemplateParameterList *TemplateParams
         = MatchTemplateParametersToScopeSpecifier(
@@ -2259,7 +2258,7 @@
       !(NewVD->hasLinkage() &&
         isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
     PrevDecl = 0;
-
+  
   // Merge the decl with the existing one if appropriate.
   if (PrevDecl) {
     if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) {
@@ -2281,6 +2280,11 @@
 
   CheckVariableDeclaration(NewVD, PrevDecl, Redeclaration);
 
+  // This is an explicit specialization of a static data member. Check it.
+  if (isExplicitSpecialization && !NewVD->isInvalidDecl() &&
+      CheckMemberSpecialization(NewVD, PrevDecl))
+    NewVD->setInvalidDecl();
+  
   // attributes declared post-definition are currently ignored
   if (PrevDecl) {
     const VarDecl *Def = 0, *PrevVD = dyn_cast<VarDecl>(PrevDecl);
@@ -2837,8 +2841,7 @@
                                               PrevDecl))
         NewFD->setInvalidDecl();
   } else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD) &&
-             CheckMemberFunctionSpecialization(cast<CXXMethodDecl>(NewFD),
-                                               PrevDecl))
+             CheckMemberSpecialization(NewFD, PrevDecl))
     NewFD->setInvalidDecl();
            
   // Perform semantic checking on the function declaration.
@@ -3400,6 +3403,15 @@
       return;
     }
 
+    // C++ [temp.expl.spec]p15:
+    //   An explicit specialization of a static data member of a template is a
+    //   definition if the declaration includes an initializer; otherwise, it 
+    //   is a declaration.
+    if (Var->isStaticDataMember() &&
+        Var->getInstantiatedFromStaticDataMember() &&
+        Var->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+      return;
+    
     // C++ [dcl.init]p9:
     //   If no initializer is specified for an object, and the object
     //   is of (possibly cv-qualified) non-POD class type (or array
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 3bd72d0..c9525f3 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -6209,11 +6209,9 @@
 
   if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
     // Implicit instantiation of static data members of class templates.
-    // FIXME: distinguish between implicit instantiations (which we need to
-    // actually instantiate) and explicit specializations.
-    // FIXME: extern templates
     if (Var->isStaticDataMember() &&
-        Var->getInstantiatedFromStaticDataMember())
+        Var->getInstantiatedFromStaticDataMember() &&
+        Var->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
       PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
 
     // FIXME: keep track of references to static data?
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 9d3efa6..5c6ec00 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -2365,8 +2365,9 @@
     return CTS->getSpecializationKind();
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
     return Function->getTemplateSpecializationKind();
-
-  // FIXME: static data members!
+  if (VarDecl *Var = dyn_cast<VarDecl>(D))
+    return Var->getTemplateSpecializationKind();
+  
   // FIXME: member classes of class templates!
   return TSK_Undeclared;
 }
@@ -3118,7 +3119,7 @@
   return false;
 }
 
-/// \brief Perform semantic analysis for the given member function
+/// \brief Perform semantic analysis for the given non-template member
 /// specialization.
 ///
 /// This routine performs all of the semantic analysis required for an 
@@ -3126,27 +3127,45 @@
 /// the function declaration \p FD will become a member function
 /// specialization.
 ///
-/// \param FD the function declaration, which will be updated to become a
-/// function template specialization.
+/// \param Member the member declaration, which will be updated to become a
+/// specialization.
 ///
 /// \param PrevDecl the set of declarations, one of which may be specialized
 /// by this function specialization.
 bool 
-Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD,
-                                        NamedDecl *&PrevDecl) {
-  // Try to find the member function we are instantiating.
-  CXXMethodDecl *Instantiation = 0;
-  for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) {
-    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*Ovl)) {
-      if (Context.hasSameType(FD->getType(), Method->getType())) {
-        Instantiation = Method;
-        break;
+Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
+  assert(!isa<TemplateDecl>(Member) && "Only for non-template members");
+         
+  // Try to find the member we are instantiating.
+  NamedDecl *Instantiation = 0;
+  NamedDecl *InstantiatedFrom = 0;
+  if (!PrevDecl) {
+    // Nowhere to look anyway.
+  } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) {
+    for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) {
+      if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*Ovl)) {
+        if (Context.hasSameType(Function->getType(), Method->getType())) {
+          Instantiation = Method;
+          InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
+          break;
+        }
       }
     }
+  } else if (isa<VarDecl>(Member)) {
+    if (VarDecl *PrevVar = dyn_cast<VarDecl>(PrevDecl))
+      if (PrevVar->isStaticDataMember()) {
+        Instantiation = PrevDecl;
+        InstantiatedFrom = PrevVar->getInstantiatedFromStaticDataMember();
+      }
+  } else if (isa<RecordDecl>(Member)) {
+    if (CXXRecordDecl *PrevRecord = dyn_cast<CXXRecordDecl>(PrevDecl)) {
+      Instantiation = PrevDecl;
+      InstantiatedFrom = PrevRecord->getInstantiatedFromMemberClass();
+    }
   }
   
   if (!Instantiation) {
-    // There is no previous declaration that matches. Since member function
+    // There is no previous declaration that matches. Since member
     // specializations are always out-of-line, the caller will complain about
     // this mismatch later.
     return false;
@@ -3155,30 +3174,43 @@
   // FIXME: Check if the prior declaration has a point of instantiation.
   // If so, we have run afoul of C++ [temp.expl.spec]p6.
   
-  // Make sure that this is a specialization of a member function.
-  FunctionDecl *FunctionInTemplate
-    = Instantiation->getInstantiatedFromMemberFunction();
-  if (!FunctionInTemplate) {
-    Diag(FD->getLocation(), diag::err_function_spec_not_instantiated)
-      << FD;
+  // Make sure that this is a specialization of a member.
+  if (!InstantiatedFrom) {
+    Diag(Member->getLocation(), diag::err_spec_member_not_instantiated)
+      << Member;
     Diag(Instantiation->getLocation(), diag::note_specialized_decl);
     return true;
   }
   
   // Check the scope of this explicit specialization.
   if (CheckTemplateSpecializationScope(*this, 
-                                       FunctionInTemplate,
-                                       Instantiation, FD->getLocation(), 
+                                       InstantiatedFrom,
+                                       Instantiation, Member->getLocation(), 
                                        false, TSK_ExplicitSpecialization))
     return true;
 
   // FIXME: Check for specialization-after-instantiation errors and such.
   
-  // Note that this function is an explicit instantiation of a member function.
-  Instantiation->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
-  FD->setInstantiationOfMemberFunction(FunctionInTemplate, 
-                                       TSK_ExplicitSpecialization);
-  
+  // Note that this is an explicit instantiation of a member.
+  if (isa<FunctionDecl>(Member)) {
+    // FIXME: We're also setting the original instantiation we found to be
+    // an explicit specialization, although I'd rather not have to do this.
+    cast<FunctionDecl>(Instantiation)->setTemplateSpecializationKind(
+                                                    TSK_ExplicitSpecialization);
+    cast<FunctionDecl>(Member)->setInstantiationOfMemberFunction(
+                                        cast<CXXMethodDecl>(InstantiatedFrom),
+                                                  TSK_ExplicitSpecialization);
+  } else if (isa<VarDecl>(Member)) {
+    Context.setInstantiatedFromStaticDataMember(cast<VarDecl>(Member),
+                                                cast<VarDecl>(InstantiatedFrom),
+                                                TSK_ExplicitSpecialization);
+  } else {
+    assert(isa<CXXRecordDecl>(Member) && "Only member classes remain");
+    // FIXME: Record TSK_ExplicitSpecialization.
+    cast<CXXRecordDecl>(Member)->setInstantiationOfMemberClass(
+                                        cast<CXXRecordDecl>(InstantiatedFrom));
+  }
+             
   // Save the caller the trouble of having to figure out which declaration
   // this specialization matches.
   PrevDecl = Instantiation;
@@ -3547,7 +3579,8 @@
     }
     
     // Instantiate static data member.
-    // FIXME: Note that this is an explicit instantiation.
+    // FIXME: Check for prior specializations and such.
+    Prev->setTemplateSpecializationKind(TSK);
     if (TSK == TSK_ExplicitInstantiationDefinition)
       InstantiateStaticDataMemberDefinition(D.getIdentifierLoc(), Prev, false);
     
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index ee6600a..2f7af60 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -982,9 +982,12 @@
       if (!Function->getBody() && TSK != TSK_ExplicitInstantiationDeclaration)
         InstantiateFunctionDefinition(PointOfInstantiation, Function);
     } else if (VarDecl *Var = dyn_cast<VarDecl>(*D)) {
-      if (Var->isStaticDataMember() && 
-          TSK != TSK_ExplicitInstantiationDeclaration)
-        InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
+      if (Var->isStaticDataMember()) {
+        Var->setTemplateSpecializationKind(TSK);
+        
+        if (TSK != TSK_ExplicitInstantiationDeclaration)
+          InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
+      }        
     } else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(*D)) {
       if (Record->isInjectedClassName())
         continue;
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index fcacb4a..bafcea0 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -155,6 +155,12 @@
     Owner->addDecl(Var);
   }
 
+  // Link instantiations of static data members back to the template from
+  // which they were instantiated.
+  if (Var->isStaticDataMember())
+    SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D, 
+                                                        TSK_ImplicitInstantiation);
+  
   if (D->getInit()) {
     OwningExprResult Init
       = SemaRef.SubstExpr(D->getInit(), TemplateArgs);
@@ -191,11 +197,6 @@
   } else if (!Var->isStaticDataMember() || Var->isOutOfLine())
     SemaRef.ActOnUninitializedDecl(Sema::DeclPtrTy::make(Var), false);
 
-  // Link instantiations of static data members back to the template from
-  // which they were instantiated.
-  if (Var->isStaticDataMember())
-    SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D);
-
   return Var;
 }
 
@@ -977,6 +978,10 @@
 
   assert(!Function->getBody() && "Already instantiated!");
 
+  // Never instantiate an explicit specialization.
+  if (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+    return;
+  
   // Find the function body that we'll be substituting.
   const FunctionDecl *PatternDecl = 0;
   if (FunctionTemplateDecl *Primary = Function->getPrimaryTemplate()) {
@@ -1084,7 +1089,6 @@
     return;
 
   // Find the out-of-line definition of this static data member.
-  // FIXME: Do we have to look for specializations separately?
   VarDecl *Def = Var->getInstantiatedFromStaticDataMember();
   bool FoundOutOfLineDef = false;
   assert(Def && "This data member was not instantiated from a template?");
@@ -1106,7 +1110,17 @@
     return;
   }
 
-  // FIXME: extern templates
+  // Never instantiate an explicit specialization.
+  if (Def->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+    return;
+  
+  // 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 (Def->getTemplateSpecializationKind() 
+        == TSK_ExplicitInstantiationDeclaration)
+    return;
 
   InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
   if (Inst)