]> granicus.if.org Git - clang/commitdiff
Support out-of-line definitions of the members of class template
authorDouglas Gregor <dgregor@apple.com>
Thu, 30 Jul 2009 17:40:51 +0000 (17:40 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 30 Jul 2009 17:40:51 +0000 (17:40 +0000)
partial specializations.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@77606 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclTemplate.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ASTContext.cpp
lib/AST/DeclTemplate.cpp
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaTemplate.cpp
test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp [new file with mode: 0644]
test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp [new file with mode: 0644]

index 7cf8bfdecfc525edc3650d6a29c3b301c2326ff6..501f82d6ba82e08b54384d99febf3c37928c109e 100644 (file)
@@ -1057,6 +1057,16 @@ public:
     return CommonPtr->PartialSpecializations;
   }
 
+  /// \brief Find a class template partial specialization with the given
+  /// type T.
+  ///
+  /// \brief A dependent type that names a specialization of this class
+  /// template.
+  ///
+  /// \returns the class template partial specialization that exactly matches
+  /// the type \p T, or NULL if no such partial specialization exists.
+  ClassTemplatePartialSpecializationDecl *findPartialSpecialization(QualType T);
+  
   /// \brief Retrieve the type of the injected-class-name for this
   /// class template.
   ///
index ab454d0e471315db1fce537d4344ec28c4913f1f..25e331721ae8f021bf82b05fa053e131924d056f 100644 (file)
@@ -858,6 +858,9 @@ def err_template_spec_needs_header : Error<
 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_param_list_matches_nontemplate : Error<
+  "template parameter list matching the non-templated nested type %0 should "
+  "be empty ('template<>')">;
 def err_template_spec_extra_headers : Error<
   "extraneous template parameter list in template specialization or "
   "out-of-line template definition">;
index 66aa363c282a62d8f05e4e98cdd52880fc0d3fa2..e9bcc04c28abe9615ceff789f88bb1e0b33c9268 100644 (file)
@@ -1669,9 +1669,10 @@ ASTContext::getTemplateSpecializationType(TemplateName Template,
                                           const TemplateArgument *Args,
                                           unsigned NumArgs,
                                           QualType Canon) {
-  if (Canon.isNull()) {
-    // Build the canonical template specialization type, since no type
-    // was provided.
+  if (!Canon.isNull())
+    Canon = getCanonicalType(Canon);
+  else {
+    // Build the canonical template specialization type.
     TemplateName CanonTemplate = getCanonicalTemplateName(Template);
     llvm::SmallVector<TemplateArgument, 4> CanonArgs;
     CanonArgs.reserve(NumArgs);
@@ -1695,16 +1696,16 @@ ASTContext::getTemplateSpecializationType(TemplateName Template,
                            8);
       Spec = new (Mem) TemplateSpecializationType(*this, CanonTemplate, 
                                                   CanonArgs.data(), NumArgs,
-                                                  QualType());
+                                                  Canon);
       Types.push_back(Spec);
       TemplateSpecializationTypes.InsertNode(Spec, InsertPos);      
     }
     
-    Canon = QualType(Spec, 0);
+    if (Canon.isNull())
+      Canon = QualType(Spec, 0);
     assert(Canon->isDependentType() && 
            "Non-dependent template-id type must have a canonical type");
-  } else
-    Canon = getCanonicalType(Canon);
+  }
 
   // Allocate the (non-canonical) template specialization type, but don't
   // try to unique it: these types typically have location information that
index 4677d17dc9351da26620ac2ba3acce0839474e26..6b3ea437ab694a22ca8440976013c8d1fc9d9f20 100644 (file)
@@ -161,6 +161,21 @@ void ClassTemplateDecl::Destroy(ASTContext& C) {
   C.Deallocate((void*)this);
 }
 
+ClassTemplatePartialSpecializationDecl *
+ClassTemplateDecl::findPartialSpecialization(QualType T) {
+  ASTContext &Context = getASTContext();
+  typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator
+    partial_spec_iterator;
+  for (partial_spec_iterator P = getPartialSpecializations().begin(),
+                          PEnd = getPartialSpecializations().end();
+       P != PEnd; ++P) {
+    if (Context.hasSameType(Context.getTypeDeclType(&*P), T))
+      return &*P;
+  }
+  
+  return 0;
+}
+
 QualType ClassTemplateDecl::getInjectedClassNameType(ASTContext &Context) {
   if (!CommonPtr->InjectedClassNameType.isNull())
     return CommonPtr->InjectedClassNameType;
index a5a20307ac00130c29d498d710dde000775ff385..1580e721d35981e4e98489fe0622e20e5ab228b6 100644 (file)
@@ -55,14 +55,23 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
         if (ClassTemplateDecl *ClassTemplate 
               = dyn_cast_or_null<ClassTemplateDecl>(
                             SpecType->getTemplateName().getAsTemplateDecl())) {
+          QualType ContextType
+            = Context.getCanonicalType(QualType(SpecType, 0));
+
           // 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)))
+          if (Context.hasSameType(Injected, ContextType))
             return ClassTemplate->getTemplatedDecl();
                 
-          // FIXME: Class template partial specializations
+          // If the type of the nested name specifier is the same as the
+          // type of one of the class template's class template partial
+          // specializations, we're entering into the definition of that
+          // class template partial specialization.
+          if (ClassTemplatePartialSpecializationDecl *PartialSpec
+                = ClassTemplate->findPartialSpecialization(ContextType))
+            return PartialSpec;
         }
       }
       
@@ -195,6 +204,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
       if (T == Context.getCanonicalType(InjectedClassName))
         return Template->getTemplatedDecl();
     }
+    // FIXME: check for class template partial specializations
   }
 
   return 0;
index 425b50263fe76fb0451cbf7dc14f8abdb54f5ce8..4264f32e9d40676201cba46095d292486f38ec00 100644 (file)
@@ -815,6 +815,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
           = cast<ClassTemplateSpecializationDecl>(Record->getDecl());
         // If the nested name specifier refers to an explicit specialization,
         // we don't need a template<> header.
+        // FIXME: revisit this approach once we cope with specialization 
+        // properly.
         if (SpecDecl->getSpecializationKind() == TSK_ExplicitSpecialization)
           continue;
       }
@@ -836,7 +838,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
   unsigned Idx = 0;
   for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size();
        Idx != NumTemplateIds; ++Idx) {
-    bool DependentTemplateId = TemplateIdsInSpecifier[Idx]->isDependentType();
+    QualType TemplateId = QualType(TemplateIdsInSpecifier[Idx], 0);
+    bool DependentTemplateId = TemplateId->isDependentType();
     if (Idx >= NumParamLists) {
       // We have a template-id without a corresponding template parameter
       // list.
@@ -844,7 +847,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
         // FIXME: the location information here isn't great. 
         Diag(SS.getRange().getBegin(), 
              diag::err_template_spec_needs_template_parameters)
-          << QualType(TemplateIdsInSpecifier[Idx], 0)
+          << TemplateId
           << SS.getRange();
       } else {
         Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_header)
@@ -856,11 +859,32 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
     }
     
     // Check the template parameter list against its corresponding template-id.
-    TemplateDecl *Template 
-      = TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl();
-    TemplateParameterListsAreEqual(ParamLists[Idx], 
-                                   Template->getTemplateParameters(),
-                                   true);
+    if (DependentTemplateId) {
+      TemplateDecl *Template 
+        = TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl();
+
+      if (ClassTemplateDecl *ClassTemplate 
+            = dyn_cast<ClassTemplateDecl>(Template)) {
+        TemplateParameterList *ExpectedTemplateParams = 0;
+        // Is this template-id naming the primary template?
+        if (Context.hasSameType(TemplateId,
+                             ClassTemplate->getInjectedClassNameType(Context)))
+          ExpectedTemplateParams = ClassTemplate->getTemplateParameters();
+        // ... or a partial specialization?
+        else if (ClassTemplatePartialSpecializationDecl *PartialSpec
+                   = ClassTemplate->findPartialSpecialization(TemplateId))
+          ExpectedTemplateParams = PartialSpec->getTemplateParameters();
+
+        if (ExpectedTemplateParams)
+          TemplateParameterListsAreEqual(ParamLists[Idx], 
+                                         ExpectedTemplateParams,
+                                         true);
+      } 
+    } else if (ParamLists[Idx]->size() > 0)
+      Diag(ParamLists[Idx]->getTemplateLoc(), 
+           diag::err_template_param_list_matches_nontemplate)
+        << TemplateId
+        << ParamLists[Idx]->getSourceRange();
   }
   
   // If there were at least as many template-ids as there were template
@@ -2493,6 +2517,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
                                             /*ExplicitInstantiation=*/false))
     return true;
 
+  // The canonical type
+  QualType CanonType;
   if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) {
     // Since the only prior class template specialization with these
     // arguments was referenced but not declared, reuse that
@@ -2501,7 +2527,15 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
     Specialization = PrevDecl;
     Specialization->setLocation(TemplateNameLoc);
     PrevDecl = 0;
+    CanonType = Context.getTypeDeclType(Specialization);
   } else if (isPartialSpecialization) {
+    // Build the canonical type that describes the converted template
+    // arguments of the class template partial specialization.
+    CanonType = Context.getTemplateSpecializationType(
+                                                  TemplateName(ClassTemplate),
+                                                  Converted.getFlatArguments(),
+                                                  Converted.flatSize());
+
     // Create a new class template partial specialization declaration node.
     TemplateParameterList *TemplateParams 
       = static_cast<TemplateParameterList*>(*TemplateParameterLists.get());
@@ -2554,7 +2588,6 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
         }
       }
     }
-
   } else {
     // Create a new class template specialization declaration node for
     // this explicit specialization.
@@ -2573,6 +2606,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
       ClassTemplate->getSpecializations().InsertNode(Specialization, 
                                                      InsertPos);
     }
+
+    CanonType = Context.getTypeDeclType(Specialization);
   }
 
   // Note that this is an explicit specialization.
@@ -2603,7 +2638,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
     = Context.getTemplateSpecializationType(Name, 
                                             TemplateArgs.data(),
                                             TemplateArgs.size(),
-                                  Context.getTypeDeclType(Specialization));
+                                            CanonType);
   Specialization->setTypeAsWritten(WrittenTy);
   TemplateArgsIn.release();
 
diff --git a/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp b/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp
new file mode 100644 (file)
index 0000000..47cf837
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T, int N>
+struct A;
+
+template<typename T> // expected-note{{previous template declaration}}
+struct A<T*, 2> {
+  void f0();
+  void f1();
+  void f2();
+};
+
+template<>
+struct A<int, 1> {
+  void g0();
+};
+
+// FIXME: We should probably give more precise diagnostics here, but the
+// diagnostics we give aren't terrible.
+// FIXME: why not point to the first parameter that's "too many"?
+template<typename T, int N> // expected-error{{too many template parameters}}
+void A<T*, 2>::f0() { }
+
+template<typename T, int N>
+void A<T, N>::f1() { } // expected-error{{out-of-line definition}}
diff --git a/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp b/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp
new file mode 100644 (file)
index 0000000..0321a7c
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T, int N>
+struct A;
+
+template<typename T>
+struct A<T*, 2> {
+  void f(T*);
+  
+  static T value;
+};
+
+template<class X> void A<X*, 2>::f(X*) { }
+
+template<class X> X A<X*, 2>::value;