Diagnose C++ [temp.class.spec]p9b3, where a class template partial
specialization's arguments are identical to the implicit template
arguments of the primary template. Typically, this is meant to be a
declaration/definition of the primary template, so we give that
advice.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@73259 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 7291d91..16d490f 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -765,6 +765,10 @@
 def err_dependent_typed_non_type_arg_in_partial_spec : Error<
     "non-type template argument specializes a template parameter with "
     "dependent type %0">;
+def err_partial_spec_args_match_primary_template : Error<
+    "class template partial specialization does not specialize any template "
+    "argument; to %select{declare|define}0 the primary template, remove the "
+    "template argument list">; 
 def unsup_template_partial_spec_ordering : Error<
   "partial ordering of class template partial specializations is not yet "
   "supported">;
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 2b47139..3315952 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1954,7 +1954,8 @@
 
   bool CheckClassTemplatePartialSpecializationArgs(
                                         TemplateParameterList *TemplateParams,
-                                        const TemplateArgument *TemplateArgs);
+                                        const TemplateArgument *TemplateArgs,
+                                        bool &MirrorsPrimaryTemplate);
 
   virtual DeclResult
   ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 113ed98..b1a8ef2 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -2006,22 +2006,68 @@
 /// \brief Check the non-type template arguments of a class template
 /// partial specialization according to C++ [temp.class.spec]p9.
 ///
+/// \param TemplateParams the template parameters of the primary class
+/// template.
+///
+/// \param TemplateArg the template arguments of the class template
+/// partial specialization.
+///
+/// \param MirrorsPrimaryTemplate will be set true if the class
+/// template partial specialization arguments are identical to the
+/// implicit template arguments of the primary template. This is not
+/// necessarily an error (C++0x), and it is left to the caller to diagnose
+/// this condition when it is an error.
+///
 /// \returns true if there was an error, false otherwise.
 bool Sema::CheckClassTemplatePartialSpecializationArgs(
                                         TemplateParameterList *TemplateParams,
-                                        const TemplateArgument *TemplateArgs) {
+                                        const TemplateArgument *TemplateArgs,
+                                        bool &MirrorsPrimaryTemplate) {
   // FIXME: the interface to this function will have to change to
   // accommodate variadic templates.
-
+  MirrorsPrimaryTemplate = true;
   for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) {
+    // Determine whether the template argument list of the partial
+    // specialization is identical to the implicit argument list of
+    // the primary template. The caller may need to diagnostic this as
+    // an error per C++ [temp.class.spec]p9b3.
+    if (MirrorsPrimaryTemplate) {
+      if (TemplateTypeParmDecl *TTP 
+            = dyn_cast<TemplateTypeParmDecl>(TemplateParams->getParam(I))) {
+        if (Context.getCanonicalType(Context.getTypeDeclType(TTP)) !=
+              Context.getCanonicalType(TemplateArgs[I].getAsType()))
+          MirrorsPrimaryTemplate = false;
+      } else if (TemplateTemplateParmDecl *TTP
+                   = dyn_cast<TemplateTemplateParmDecl>(
+                                                 TemplateParams->getParam(I))) {
+        // FIXME: We should settle on either Declaration storage or
+        // Expression storage for template template parameters.
+        TemplateTemplateParmDecl *ArgDecl 
+          = dyn_cast_or_null<TemplateTemplateParmDecl>(
+                                                  TemplateArgs[I].getAsDecl());
+        if (!ArgDecl)
+          if (DeclRefExpr *DRE 
+                = dyn_cast_or_null<DeclRefExpr>(TemplateArgs[I].getAsExpr()))
+            ArgDecl = dyn_cast<TemplateTemplateParmDecl>(DRE->getDecl());
+
+        if (!ArgDecl ||
+            ArgDecl->getIndex() != TTP->getIndex() ||
+            ArgDecl->getDepth() != TTP->getDepth())
+          MirrorsPrimaryTemplate = false;
+      }
+    }
+
     NonTypeTemplateParmDecl *Param 
       = dyn_cast<NonTypeTemplateParmDecl>(TemplateParams->getParam(I));
-    if (!Param)
+    if (!Param) {
       continue;
-    
+    }
+
     Expr *ArgExpr = TemplateArgs[I].getAsExpr();
-    if (!ArgExpr)
+    if (!ArgExpr) {
+      MirrorsPrimaryTemplate = false;
       continue;
+    }
 
     // C++ [temp.class.spec]p8:
     //   A non-type argument is non-specialized if it is the name of a
@@ -2032,8 +2078,15 @@
     // specialized non-type arguments, so skip any non-specialized
     // arguments.
     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ArgExpr))
-      if (isa<NonTypeTemplateParmDecl>(DRE->getDecl()))
+      if (NonTypeTemplateParmDecl *NTTP 
+            = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl())) {
+        if (MirrorsPrimaryTemplate && 
+            (Param->getIndex() != NTTP->getIndex() ||
+             Param->getDepth() != NTTP->getDepth()))
+          MirrorsPrimaryTemplate = false;
+
         continue;
+      }
 
     // C++ [temp.class.spec]p9:
     //   Within the argument list of a class template partial
@@ -2060,6 +2113,8 @@
       Diag(Param->getLocation(), diag::note_template_param_here);
       return true;
     }
+
+    MirrorsPrimaryTemplate = false;
   }
 
   return false;
@@ -2179,11 +2234,30 @@
   // corresponds to these arguments.
   llvm::FoldingSetNodeID ID;
   if (isPartialSpecialization) {
+    bool MirrorsPrimaryTemplate;
     if (CheckClassTemplatePartialSpecializationArgs(
                                          ClassTemplate->getTemplateParameters(),
-                                   ConvertedTemplateArgs.getFlatArgumentList()))
+                                    ConvertedTemplateArgs.getFlatArgumentList(),
+                                         MirrorsPrimaryTemplate))
       return true;
 
+    if (MirrorsPrimaryTemplate) {
+      // C++ [temp.class.spec]p9b3:
+      //
+      //   -- The argument list of the specialization shall not be identical 
+      //      to the implicit argument list of the primary template. 
+      Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template)
+        << (TK == TK_Definition)
+        << CodeModificationHint::CreateRemoval(SourceRange(LAngleLoc, 
+                                                           RAngleLoc));
+      return ActOnClassTemplate(S, TagSpec, TK, KWLoc, SS,
+                                ClassTemplate->getIdentifier(),
+                                TemplateNameLoc,
+                                Attr,
+                                move(TemplateParameterLists),
+                                AS_none);
+    }
+
     // FIXME: Template parameter list matters, too
     ClassTemplatePartialSpecializationDecl::Profile(ID, 
                                     ConvertedTemplateArgs.getFlatArgumentList(),
diff --git a/test/SemaTemplate/temp_class_spec_neg.cpp b/test/SemaTemplate/temp_class_spec_neg.cpp
index 5fd95a7..d303146 100644
--- a/test/SemaTemplate/temp_class_spec_neg.cpp
+++ b/test/SemaTemplate/temp_class_spec_neg.cpp
@@ -1,5 +1,4 @@
 // RUN: clang-cc -fsyntax-only -verify %s
-
 template<typename T> struct vector;
 
 // C++ [temp.class.spec]p9
@@ -19,10 +18,14 @@
 int array[5]; 
 template< int X > class A2<X,&array> { }; // expected-error{{specializes}}
 
-// C++ [temp.class.spec]p10
 template<typename T, int N, template<typename X> class TT>
 struct Test0;
 
+//   bullet 3
+template<typename T, int N, template<typename X> class TT>
+struct Test0<T, N, TT>; // expected-error{{does not specialize}}
+
+// C++ [temp.class.spec]p10
 template<typename T = int, // expected-error{{default template argument}}
          int N = 17, // expected-error{{default template argument}}
          template<typename X> class TT = ::vector> // expected-error{{default template argument}}
diff --git a/www/cxx_status.html b/www/cxx_status.html
index b2ba8a3..3a9a729 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -1888,11 +1888,11 @@
   <td class="complete" align="center"></td>  
   <td class="complete" align="center"></td>
   <td class="medium" align="center"></td>
-  <td class="broken" align="center"></td>
+  <td class="na" align="center">N/A</td>
   <td></td>
 </tr>
 <tr>
-  <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;14.5.5.1 [temp.class.spec.match]</td>
+  <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;14.5.4.1 [temp.class.spec.match]</td>
   <td class="na" align="center">N/A</td>  
   <td class="na" align="center">N/A</td>
   <td class="medium" align="center"></td>
@@ -1900,7 +1900,7 @@
   <td></td>
 </tr>
 <tr>
-  <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;14.5.5.2 [temp.class.order]</td>
+  <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;14.5.4.2 [temp.class.order]</td>
   <td class="broken" align="center"></td>  
   <td class="broken" align="center"></td>
   <td class="broken" align="center"></td>