[cfe-commits] r77002 - in /cfe/trunk: include/clang/AST/ASTContext.h include/clang/AST/Decl.h include/clang/Basic/DiagnosticSemaKinds.td lib/AST/ASTContext.cpp lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaExpr.cpp lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp

Douglas Gregor dgregor at apple.com
Fri Jul 24 13:34:43 PDT 2009


Author: dgregor
Date: Fri Jul 24 15:34:43 2009
New Revision: 77002

URL: http://llvm.org/viewvc/llvm-project?rev=77002&view=rev
Log:
Template instantiation for static data members that are defined out-of-line.

Note that this also fixes a bug that affects non-template code, where we 
were not treating out-of-line static data members are "file-scope" variables,
and therefore not checking their initializers.

Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Fri Jul 24 15:34:43 2009
@@ -136,6 +136,32 @@
   /// wasting space in the Decl class.
   llvm::DenseMap<const Decl*, Attr*> DeclAttrs;
   
+  /// \brief Keeps track of the static data member templates from which
+  /// static data members of class template specializations were instantiated.
+  ///
+  /// This data structure stores the mapping from instantiations of static
+  /// data members to the static data member representations within the
+  /// class template from which they were instantiated. 
+  ///
+  /// Given the following example:
+  ///
+  /// \code
+  /// template<typename T>
+  /// struct X {
+  ///   static T value;
+  /// };
+  ///
+  /// template<typename T>
+  ///   T X<T>::value = T(17);
+  ///
+  /// int *x = &X<int>::value;
+  /// \endcode
+  ///
+  /// This mapping will contain an entry that maps from the VarDecl for 
+  /// X<int>::value to the corresponding VarDecl for X<T>::value (within the
+  /// class template X). 
+  llvm::DenseMap<VarDecl *, VarDecl *> InstantiatedFromStaticDataMember;
+  
   TranslationUnitDecl *TUDecl;
 
   /// SourceMgr - The associated SourceManager object.
@@ -193,6 +219,15 @@
   /// \brief Erase the attributes corresponding to the given declaration.
   void eraseDeclAttrs(const Decl *D) { DeclAttrs.erase(D); }
   
+  /// \brief If this variable is an instantiated static data member of a
+  /// class template specialization, returns the templated static data member 
+  /// from which it was instantiated.
+  VarDecl *getInstantiatedFromStaticDataMember(VarDecl *Var);
+  
+  /// \brief Note that the static data member \p Inst is an instantiation of
+  /// the static data member template \p Tmpl of a class template.
+  void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl);  
+  
   TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
 
   const char *getCommentForDecl(const Decl *D);

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Fri Jul 24 15:34:43 2009
@@ -477,6 +477,11 @@
     return getDeclContext()->isRecord();
   }
 
+  /// \brief If this variable is an instantiated static data member of a
+  /// class template specialization, returns the templated static data member 
+  /// from which it was instantiated.
+  VarDecl *getInstantiatedFromStaticDataMember(); 
+  
   /// isFileVarDecl - Returns true for file scoped variable declaration.
   bool isFileVarDecl() const {
     if (getKind() != Decl::Var)
@@ -486,6 +491,9 @@
       if (isa<TranslationUnitDecl>(Ctx) || isa<NamespaceDecl>(Ctx) )
         return true;
     }
+    if (isStaticDataMember() && isOutOfLine())
+      return true;
+    
     return false;
   }
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jul 24 15:34:43 2009
@@ -888,6 +888,8 @@
   "in instantiation of member function %q0 requested here">;
 def note_function_template_spec_here : Note<
   "in instantiation of function template specialization %q0 requested here">;
+def note_template_static_data_member_def_here : Note<
+  "in instantiation of static data member %q0 requested here">;
   
 def note_default_arg_instantiation_here : Note<
   "in instantiation of default argument for '%0' required here">;

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jul 24 15:34:43 2009
@@ -220,6 +220,25 @@
   InitBuiltinType(NullPtrTy,           BuiltinType::NullPtr);
 }
 
+VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
+  assert(Var->isStaticDataMember() && "Not a static data member");
+  llvm::DenseMap<VarDecl *, VarDecl *>::iterator Pos
+    = InstantiatedFromStaticDataMember.find(Var);
+  if (Pos == InstantiatedFromStaticDataMember.end())
+    return 0;
+  
+  return Pos->second;
+}
+
+void 
+ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) {
+  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;
+}
+
 namespace {
   class BeforeInTranslationUnit 
     : std::binary_function<SourceRange, SourceRange, bool> {

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Fri Jul 24 15:34:43 2009
@@ -341,6 +341,10 @@
   return SourceRange(getLocation(), getLocation());
 }
 
+VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
+  return getASTContext().getInstantiatedFromStaticDataMember(this);
+}
+
 bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
   if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
     return false;

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri Jul 24 15:34:43 2009
@@ -2732,7 +2732,10 @@
   void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
                                      FunctionDecl *Function,
                                      bool Recursive = false);
-  void InstantiateVariableDefinition(VarDecl *Var);
+  void InstantiateStaticDataMemberDefinition(
+                                          SourceLocation PointOfInstantiation,
+                                             VarDecl *Var,
+                                             bool Recursive = false);
 
   NamedDecl *InstantiateCurrentDeclRef(NamedDecl *D);
     

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Jul 24 15:34:43 2009
@@ -5780,10 +5780,17 @@
   }
   
   if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
-    (void)Var;
-    // FIXME: implicit template instantiation
+    // Implicit instantiation of static data members of class templates.
+    // FIXME: distinguish between implicit instantiations (which we need to
+    // actually instantiate) and explicit specializations.
+    if (Var->isStaticDataMember() && 
+        Var->getInstantiatedFromStaticDataMember())
+      PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
+    
     // FIXME: keep track of references to static data?
+
     D->setUsed(true);
-  }
+    return;
+}
 }
 

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Jul 24 15:34:43 2009
@@ -189,8 +189,7 @@
                      DiagID)
           << Context.getTypeDeclType(Record)
           << Active->InstantiationRange;
-      } else {
-        FunctionDecl *Function = cast<FunctionDecl>(D);
+      } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
         unsigned DiagID;
         if (Function->getPrimaryTemplate())
           DiagID = diag::note_function_template_spec_here;
@@ -200,6 +199,11 @@
                      DiagID)
           << Function
           << Active->InstantiationRange;
+      } else {
+        Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
+                     diag::note_template_static_data_member_def_here)
+          << cast<VarDecl>(D)
+          << Active->InstantiationRange;
       }
       break;
     }
@@ -1059,9 +1063,8 @@
       if (!Function->getBody())
         InstantiateFunctionDefinition(PointOfInstantiation, Function);
     } else if (VarDecl *Var = dyn_cast<VarDecl>(*D)) {
-      const VarDecl *Def = 0;
-      if (!Var->getDefinition(Def))
-        InstantiateVariableDefinition(Var);
+      if (Var->isStaticDataMember())
+        InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
     } else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(*D)) {
       if (!Record->isInjectedClassName() && !Record->getDefinition(Context)) {
         assert(Record->getInstantiatedFromMemberClass() && 

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Fri Jul 24 15:34:43 2009
@@ -120,12 +120,24 @@
   Var->setCXXDirectInitializer(D->hasCXXDirectInitializer());
   Var->setDeclaredInCondition(D->isDeclaredInCondition());
  
+  // If we are instantiating a static data member defined 
+  // out-of-line, the instantiation will have the same lexical
+  // context (which will be a namespace scope) as the template.
+  if (D->isOutOfLine())
+    Var->setLexicalDeclContext(D->getLexicalDeclContext());
+  
   // FIXME: In theory, we could have a previous declaration for variables that
   // are not static data members.
   bool Redeclaration = false;
   SemaRef.CheckVariableDeclaration(Var, 0, Redeclaration);
-  Owner->addDecl(Var);
-
+  
+  if (D->isOutOfLine()) {
+    D->getLexicalDeclContext()->addDecl(Var);
+    Owner->makeDeclVisibleInContext(Var);
+  } else {
+    Owner->addDecl(Var);
+  }
+  
   if (D->getInit()) {
     OwningExprResult Init 
       = SemaRef.InstantiateExpr(D->getInit(), TemplateArgs);
@@ -138,6 +150,11 @@
     // FIXME: Call ActOnUninitializedDecl? (Not always)
   }
 
+  // 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;
 }
 
@@ -374,6 +391,12 @@
                             D->isInline());
   Method->setInstantiationOfMemberFunction(D);
 
+  // If we are instantiating a member function defined 
+  // out-of-line, the instantiation will have the same lexical
+  // context (which will be a namespace scope) as the template.
+  if (D->isOutOfLine())
+    Method->setLexicalDeclContext(D->getLexicalDeclContext());
+  
   // Attach the parameters
   for (unsigned P = 0; P < Params.size(); ++P)
     Params[P]->setOwningFunction(Method);
@@ -773,9 +796,103 @@
 /// \brief Instantiate the definition of the given variable from its
 /// template.
 ///
-/// \param Var the already-instantiated declaration of a variable.
-void Sema::InstantiateVariableDefinition(VarDecl *Var) {
-  // FIXME: Implement this!
+/// \param PointOfInstantiation the point at which the instantiation was
+/// required. Note that this is not precisely a "point of instantiation"
+/// for the function, but it's close.
+///
+/// \param Var the already-instantiated declaration of a static member
+/// variable of a class template specialization.
+///
+/// \param Recursive if true, recursively instantiates any functions that
+/// are required by this instantiation.
+void Sema::InstantiateStaticDataMemberDefinition(
+                                          SourceLocation PointOfInstantiation,
+                                                 VarDecl *Var,
+                                                 bool Recursive) {
+  if (Var->isInvalidDecl())
+    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?");
+  assert(Def->isStaticDataMember() && "Not a static data member?"); 
+  for (VarDecl::redecl_iterator RD = Def->redecls_begin(), 
+                             RDEnd = Def->redecls_end();
+       RD != RDEnd; ++RD) {
+    if (RD->getLexicalDeclContext()->isFileContext()) {
+      Def = *RD;
+      FoundOutOfLineDef = true;
+    }
+  }
+  
+  if (!FoundOutOfLineDef) {
+    // We did not find an out-of-line definition of this static data member,
+    // so we won't perform any instantiation. Rather, we rely on the user to
+    // instantiate this definition (or provide a specialization for it) in 
+    // another translation unit. 
+    return;
+  }
+
+  InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
+  if (Inst)
+    return;
+  
+  // If we're performing recursive template instantiation, create our own
+  // queue of pending implicit instantiations that we will instantiate later,
+  // while we're still within our own instantiation context.
+  std::deque<PendingImplicitInstantiation> SavedPendingImplicitInstantiations;
+  if (Recursive)
+    PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
+    
+  // Enter the scope of this instantiation. We don't use
+  // PushDeclContext because we don't have a scope.
+  DeclContext *PreviousContext = CurContext;
+  CurContext = Var->getDeclContext();
+  
+#if 0
+  // Instantiate the initializer of this static data member.
+  OwningExprResult Init 
+    = InstantiateExpr(Def->getInit(), getTemplateInstantiationArgs(Var));
+  if (Init.isInvalid()) {
+    // If instantiation of the initializer failed, mark the declaration invalid
+    // and don't instantiate anything else that was triggered by this 
+    // instantiation.
+    Var->setInvalidDecl();
+
+    // Restore the set of pending implicit instantiations.
+    PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
+    
+    return;
+  } 
+  
+  // Type-check the initializer.
+  if (Init.get())
+    AddInitializerToDecl(DeclPtrTy::make(Var), move(Init),
+                         Def->hasCXXDirectInitializer());
+  else 
+    ActOnUninitializedDecl(DeclPtrTy::make(Var), false);
+#else
+  Var = cast_or_null<VarDecl>(InstantiateDecl(Def, Var->getDeclContext(),
+                                          getTemplateInstantiationArgs(Var)));
+#endif
+  
+  CurContext = PreviousContext;
+
+  if (Var) {
+    DeclGroupRef DG(Var);
+    Consumer.HandleTopLevelDecl(DG);
+  }
+  
+  if (Recursive) {
+    // Instantiate any pending implicit instantiations found during the
+    // instantiation of this template. 
+    PerformPendingImplicitInstantiations();
+    
+    // Restore the set of pending implicit instantiations.
+    PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
+  }  
 }
 
 static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
@@ -794,6 +911,11 @@
     return Enum->getInstantiatedFromMemberEnum()->getCanonicalDecl()
              == D->getCanonicalDecl();
 
+  if (VarDecl *Var = dyn_cast<VarDecl>(Other))
+    if (Var->isStaticDataMember())
+      return Var->getInstantiatedFromStaticDataMember()->getCanonicalDecl()
+               == D->getCanonicalDecl();
+      
   // FIXME: How can we find instantiations of anonymous unions?
 
   return D->getDeclName() && isa<NamedDecl>(Other) &&
@@ -912,10 +1034,16 @@
     PendingImplicitInstantiation Inst = PendingImplicitInstantiations.front();
     PendingImplicitInstantiations.pop_front();
     
-    if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first))
+    // Instantiate function definitions
+    if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first)) {
       if (!Function->getBody())
         InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true);
+      continue;
+    }
     
-    // FIXME: instantiate static member variables
+    // Instantiate static data member definitions.
+    VarDecl *Var = cast<VarDecl>(Inst.first);
+    assert(Var->isStaticDataMember() && "Not a static data member?");
+    InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true);
   }
 }

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp?rev=77002&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp Fri Jul 24 15:34:43 2009
@@ -0,0 +1,28 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// Test instantiation of static data members declared out-of-line.
+
+template<typename T>
+struct X {
+  static T value;
+};
+
+template<typename T> 
+  T X<T>::value = 17; // expected-error{{initialize}}
+
+struct InitOkay {
+  InitOkay(int) { }
+};
+
+struct CannotInit { };
+
+int &returnInt() { return X<int>::value; }
+float &returnFloat() { return X<float>::value; }
+
+InitOkay &returnInitOkay() { return X<InitOkay>::value; }
+
+unsigned long sizeOkay() { return sizeof(X<CannotInit>::value); }
+  
+CannotInit &returnError() {
+  return X<CannotInit>::value; // expected-note{{instantiation}}
+}
\ No newline at end of file

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp?rev=77002&r1=77001&r2=77002&view=diff

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp Fri Jul 24 15:34:43 2009
@@ -6,7 +6,7 @@
 };
 
 template<typename T>
-T X0<T>::value = 0;
+T X0<T>::value = 0; // expected-error{{initialize}}
 
 struct X1 { 
   X1(int);
@@ -20,7 +20,7 @@
 double*& get_double_ptr() { return X0<int*>::value; } // expected-error{{initialized}}
 
 X2& get_X2() { 
-  return X0<X2>::value; // FIXME: instantiation should fail!
+  return X0<X2>::value; // expected-note{{instantiation}}
 }
   
 template<typename T> T x; // expected-error{{variable 'x' declared as a template}}
\ No newline at end of file





More information about the cfe-commits mailing list