[cfe-commits] r76692 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaType.cpp test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp

Douglas Gregor dgregor at apple.com
Tue Jul 21 16:53:32 PDT 2009


Author: dgregor
Date: Tue Jul 21 18:53:31 2009
New Revision: 76692

URL: http://llvm.org/viewvc/llvm-project?rev=76692&view=rev
Log:
Basic parsing and semantic analysis for out-of-line definitions of the
member functions of class templates, e.g.,

  template<typename T> 
  struct X {
    void f(T);
  };

  template<typename T> X<T>::f(T) { /* ... */ }


Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaType.cpp

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Jul 21 18:53:31 2009
@@ -796,8 +796,12 @@
 // C++ class template specialization
 def err_template_spec_needs_header : Error<
   "template specialization requires 'template<>'">;
+def err_template_spec_needs_template_parameters : Error<
+  "template specialization or definition requires a template parameter list"
+  "corresponding to the nested type %0">;
 def err_template_spec_extra_headers : Error<
-  "template specialization must have a single 'template<>' header">;
+  "extraneous template parameter list in template specialization or "
+  "out-of-line template definition">;
 def err_template_spec_decl_out_of_scope_global : Error<
   "class template %select{|partial }0specialization of %1 must occur in the "
   "global scope">;

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Jul 21 18:53:31 2009
@@ -1775,7 +1775,8 @@
 
   bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
   
-  DeclContext *computeDeclContext(const CXXScopeSpec &SS);
+  DeclContext *computeDeclContext(const CXXScopeSpec &SS, 
+                                  bool EnteringContext = false);
   bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
   CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
   bool isUnknownSpecialization(const CXXScopeSpec &SS);
@@ -2064,7 +2065,12 @@
                              SourceLocation RAngleLoc);
   bool CheckTemplateParameterList(TemplateParameterList *NewParams,
                                   TemplateParameterList *OldParams);
-
+  TemplateParameterList *
+  MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
+                                          const CXXScopeSpec &SS,
+                                          TemplateParameterList **ParamLists,
+                                          unsigned NumParamLists);
+                                                
   virtual DeclResult
   ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
                      SourceLocation KWLoc, const CXXScopeSpec &SS,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Tue Jul 21 18:53:31 2009
@@ -21,7 +21,19 @@
 
 /// \brief Compute the DeclContext that is associated with the given
 /// scope specifier.
-DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
+///
+/// \param SS the C++ scope specifier as it appears in the source
+///
+/// \param EnteringContext when true, we will be entering the context of
+/// this scope specifier, so we can retrieve the declaration context of a
+/// class template or class template partial specialization even if it is
+/// not the current instantiation.
+///
+/// \returns the declaration context represented by the scope specifier @p SS,
+/// or NULL if the declaration context cannot be computed (e.g., because it is
+/// dependent and not the current instantiation).
+DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
+                                      bool EnteringContext) {
   if (!SS.isSet() || SS.isInvalid())
     return 0;
 
@@ -32,8 +44,29 @@
     // instantiation, return its DeclContext.
     if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
       return Record;
-    else
-      return 0;
+    
+    if (EnteringContext) {
+      // We are entering the context of the nested name specifier, so try to
+      // match the nested name specifier to either a primary class template
+      // or a class template partial specialization
+      if (const TemplateSpecializationType *SpecType
+            = dyn_cast_or_null<TemplateSpecializationType>(NNS->getAsType())) {
+        if (ClassTemplateDecl *ClassTemplate 
+              = dyn_cast_or_null<ClassTemplateDecl>(
+                            SpecType->getTemplateName().getAsTemplateDecl())) {
+          // If the type of the nested name specifier is the same as the
+          // injected class name of the named class template, we're entering
+          // into that class template definition.
+          QualType Injected = ClassTemplate->getInjectedClassNameType(Context);
+          if (Context.hasSameType(Injected, QualType(SpecType, 0)))
+            return ClassTemplate->getTemplatedDecl();
+                
+          // FIXME: Class template partial specializations
+        }
+      }
+    }
+    
+    return 0;
   }
 
   switch (NNS->getKind()) {
@@ -89,7 +122,10 @@
   assert(getLangOptions().CPlusPlus && "Only callable in C++");
   assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
 
-  QualType T = QualType(NNS->getAsType(), 0);
+  if (!NNS->getAsType())
+    return 0;
+  
+  QualType T = Context.getCanonicalType(QualType(NNS->getAsType(), 0));
   // If the nested name specifier does not refer to a type, then it
   // does not refer to the current instantiation.
   if (T.isNull())
@@ -138,7 +174,7 @@
     // nested-name-specifier type that is equivalent to the
     // injected-class-name of one of the types that is currently in
     // our context.
-    if (Context.getTypeDeclType(Record) == T)
+    if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T)
       return Record;
     
     if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
@@ -286,7 +322,7 @@
 /// The 'SS' should be a non-empty valid CXXScopeSpec.
 void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
-  if (DeclContext *DC = computeDeclContext(SS))
+  if (DeclContext *DC = computeDeclContext(SS, true))
     EnterDeclaratorContext(S, DC);
   else
     const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
@@ -299,7 +335,7 @@
 /// defining scope.
 void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
-  assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS)) && 
+  assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) && 
          "Context imbalance!");
   if (!SS.isInvalid())
     ExitDeclaratorContext(S);

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Jul 21 18:53:31 2009
@@ -707,8 +707,7 @@
     const CXXMethodDecl* OldMethod = dyn_cast<CXXMethodDecl>(Old);
     const CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
     if (OldMethod && NewMethod && 
-        OldMethod->getLexicalDeclContext() == 
-          NewMethod->getLexicalDeclContext()) {
+        NewMethod->getLexicalDeclContext()->isRecord()) {
       //    -- Member function declarations with the same name and the 
       //       same parameter types cannot be overloaded if any of them 
       //       is a static member function declaration.
@@ -1445,7 +1444,7 @@
                           NameKind == LookupRedeclarationWithLinkage,
                           D.getIdentifierLoc());
   } else { // Something like "int foo::x;"
-    DC = computeDeclContext(D.getCXXScopeSpec());
+    DC = computeDeclContext(D.getCXXScopeSpec(), true);
     // FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
     PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
 
@@ -2191,14 +2190,15 @@
   // from the semantic context.
   NewFD->setLexicalDeclContext(CurContext);
 
-  // If there is a template parameter list, then we are dealing with a 
-  // template declaration or specialization.
+  // Match up the template parameter lists with the scope specifier, then
+  // determine whether we have a template or a template specialization.
   FunctionTemplateDecl *FunctionTemplate = 0;
-  if (TemplateParamLists.size()) {
-    // FIXME: member templates!
-    TemplateParameterList *TemplateParams 
-      = static_cast<TemplateParameterList *>(*TemplateParamLists.release());
-    
+  if (TemplateParameterList *TemplateParams
+        = MatchTemplateParametersToScopeSpecifier(
+                                  D.getDeclSpec().getSourceRange().getBegin(),
+                                  D.getCXXScopeSpec(),
+                        (TemplateParameterList**)TemplateParamLists.release(),
+                                                  TemplateParamLists.size())) {
     if (TemplateParams->size() > 0) {
       // This is a function template
       FunctionTemplate = FunctionTemplateDecl::Create(Context, CurContext,
@@ -2210,6 +2210,7 @@
       // FIXME: Handle function template specializations
     }
   }
+        
   
   // C++ [dcl.fct.spec]p5:
   //   The virtual specifier shall only be used in declarations of
@@ -3517,7 +3518,7 @@
     if (RequireCompleteDeclContext(SS))
       return DeclPtrTy::make((Decl *)0);
 
-    DC = computeDeclContext(SS);
+    DC = computeDeclContext(SS, true);
     SearchDC = DC;
     // Look-up name inside 'foo::'.
     PrevDecl 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Jul 21 18:53:31 2009
@@ -739,6 +739,123 @@
   return Invalid;
 }
 
+/// \brief Match the given template parameter lists to the given scope 
+/// specifier, returning the template parameter list that applies to the
+/// name.
+///
+/// \param DeclStartLoc the start of the declaration that has a scope
+/// specifier or a template parameter list.
+/// 
+/// \param SS the scope specifier that will be matched to the given template
+/// parameter lists. This scope specifier precedes a qualified name that is
+/// being declared.
+///
+/// \param ParamLists the template parameter lists, from the outermost to the
+/// innermost template parameter lists.
+///
+/// \param NumParamLists the number of template parameter lists in ParamLists.
+///
+/// \returns the template parameter list, if any, that corresponds to the 
+/// name that is preceded by the scope specifier @p SS. This template
+/// parameter list may be have template parameters (if we're declaring a
+/// template) or may have no template parameters (if we're declaring a 
+/// template specialization), or may be NULL (if we were's declaring isn't
+/// itself a template).
+TemplateParameterList *
+Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
+                                              const CXXScopeSpec &SS,
+                                          TemplateParameterList **ParamLists,
+                                              unsigned NumParamLists) {
+  // FIXME: This routine will need a lot more testing once we have support for
+  // member templates.
+  
+  // Find the template-ids that occur within the nested-name-specifier. These
+  // template-ids will match up with the template parameter lists.
+  llvm::SmallVector<const TemplateSpecializationType *, 4>
+    TemplateIdsInSpecifier;
+  for (NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep();
+       NNS; NNS = NNS->getPrefix()) {
+    if (const TemplateSpecializationType *SpecType 
+          = dyn_cast_or_null<TemplateSpecializationType>(NNS->getAsType())) {
+      TemplateDecl *Template = SpecType->getTemplateName().getAsTemplateDecl();
+      if (!Template)
+        continue; // FIXME: should this be an error? probably...
+   
+      if (const RecordType *Record = SpecType->getAsRecordType()) {
+        ClassTemplateSpecializationDecl *SpecDecl
+          = cast<ClassTemplateSpecializationDecl>(Record->getDecl());
+        // If the nested name specifier refers to an explicit specialization,
+        // we don't need a template<> header.
+        if (SpecDecl->getSpecializationKind() == TSK_ExplicitSpecialization)
+          continue;
+      }
+      
+      TemplateIdsInSpecifier.push_back(SpecType);
+    }
+  }
+  
+  // Reverse the list of template-ids in the scope specifier, so that we can
+  // more easily match up the template-ids and the template parameter lists.
+  std::reverse(TemplateIdsInSpecifier.begin(), TemplateIdsInSpecifier.end());
+  
+  SourceLocation FirstTemplateLoc = DeclStartLoc;
+  if (NumParamLists)
+    FirstTemplateLoc = ParamLists[0]->getTemplateLoc();
+      
+  // Match the template-ids found in the specifier to the template parameter
+  // lists.
+  unsigned Idx = 0;
+  for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size();
+       Idx != NumTemplateIds; ++Idx) {
+    bool DependentTemplateId = TemplateIdsInSpecifier[Idx]->isDependentType();
+    if (Idx >= NumParamLists) {
+      // We have a template-id without a corresponding template parameter
+      // list.
+      if (DependentTemplateId) {
+        // FIXME: the location information here isn't great. 
+        Diag(SS.getRange().getBegin(), 
+             diag::err_template_spec_needs_template_parameters)
+          << QualType(TemplateIdsInSpecifier[Idx], 0)
+          << SS.getRange();
+      } else {
+        Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_header)
+          << SS.getRange()
+          << CodeModificationHint::CreateInsertion(FirstTemplateLoc,
+                                                   "template<> ");
+      }
+      return 0;
+    }
+    
+    // Check the template parameter list against its corresponding template-id.
+    TemplateDecl *Template 
+      = TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl();
+    TemplateParameterListsAreEqual(ParamLists[Idx], 
+                                   Template->getTemplateParameters(),
+                                   true);
+  }
+  
+  // If there were at least as many template-ids as there were template
+  // parameter lists, then there are no template parameter lists remaining for
+  // the declaration itself.
+  if (Idx >= NumParamLists)
+    return 0;
+  
+  // If there were too many template parameter lists, complain about that now.
+  if (Idx != NumParamLists - 1) {
+    while (Idx < NumParamLists - 1) {
+      Diag(ParamLists[Idx]->getTemplateLoc(), 
+           diag::err_template_spec_extra_headers)
+        << SourceRange(ParamLists[Idx]->getTemplateLoc(),
+                       ParamLists[Idx]->getRAngleLoc());
+      ++Idx;
+    }
+  }
+  
+  // Return the last template parameter list, which corresponds to the
+  // entity being declared.
+  return ParamLists[NumParamLists - 1];
+}
+
 /// \brief Translates template arguments as provided by the parser
 /// into template arguments used by semantic analysis.
 static void 
@@ -2572,12 +2689,12 @@
   DeclPtrTy DP = HandleDeclarator(ParentScope, D, 
                                   move(TemplateParameterLists),
                                   /*IsFunctionDefinition=*/true);
-  FunctionTemplateDecl *FunctionTemplate 
-    = cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>());
-  if (FunctionTemplate)
+  if (FunctionTemplateDecl *FunctionTemplate 
+        = dyn_cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>()))
     return ActOnStartOfFunctionDef(FnBodyScope, 
                       DeclPtrTy::make(FunctionTemplate->getTemplatedDecl()));
-
+  if (FunctionDecl *Function = dyn_cast_or_null<FunctionDecl>(DP.getAs<Decl>()))
+    return ActOnStartOfFunctionDef(FnBodyScope, DeclPtrTy::make(Function));
   return DeclPtrTy();
 }
 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Tue Jul 21 18:53:31 2009
@@ -1151,7 +1151,8 @@
         D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
         ((D.getContext() != Declarator::MemberContext &&
           (!D.getCXXScopeSpec().isSet() ||
-           !computeDeclContext(D.getCXXScopeSpec())->isRecord())) ||
+           !computeDeclContext(D.getCXXScopeSpec(), /*FIXME:*/true)
+              ->isRecord())) ||
          D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
       if (D.isFunctionDeclarator())
         Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp?rev=76692&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp Tue Jul 21 18:53:31 2009
@@ -0,0 +1,12 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+// XFAIL
+
+template<typename T>
+struct X0 {
+  typedef int size_type;
+  
+  size_type f0() const;
+};
+
+template<typename T>
+typename X0<T>::size_type X0<T>::f0() const { }
\ No newline at end of file

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

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp Tue Jul 21 18:53:31 2009
@@ -0,0 +1,45 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T, typename U> // expected-note{{previous template}}
+class X0 {
+public:
+  typedef int size_type;
+  
+  void f0(const T&, const U&);
+  
+  T& operator[](int i) const;
+  
+  void f1(size_type) const;
+  void f2(size_type) const;
+  void f3(size_type) const;
+  
+  T value;
+};
+
+template<typename T, typename U>
+void X0<T, U>::f0(const T&, const U&) { // expected-note{{previous definition}}
+}
+
+template<class X, class Y>
+X& X0<X, Y>::operator[](int i) const {
+  (void)i;
+  return value;
+}
+
+template<class X, class Y>
+void X0<X, Y>::f1(int) const { }
+
+template<class X, class Y>
+void X0<X, Y>::f2(size_type) const { }
+
+template<class X, class Y, class Z> // expected-error{{too many template parameters}}
+void X0<X, Y>::f3(size_type) const {
+}
+
+// FIXME: error message should probably say, "redefinition of 'X0<T, U>::f0'"
+// rather than just "redefinition of 'f0'"
+template<typename T, typename U>
+void X0<T, U>::f0(const T&, const U&) { // expected-error{{redefinition}}
+}
+
+// FIXME: test out-of-line constructors, destructors





More information about the cfe-commits mailing list