r191278 - Implement restriction that a partial specialization must actually specialize

Richard Smith richard-llvm at metafoo.co.uk
Mon Sep 23 21:49:24 PDT 2013


Author: rsmith
Date: Mon Sep 23 23:49:23 2013
New Revision: 191278

URL: http://llvm.org/viewvc/llvm-project?rev=191278&view=rev
Log:
Implement restriction that a partial specialization must actually specialize
something, for variable templates.

Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
    cfe/trunk/test/PCH/cxx1y-variable-templates.cpp
    cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
    cfe/trunk/test/SemaCXX/cxx1y-variable-templates_top_level.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Sep 23 23:49:23 2013
@@ -3127,12 +3127,12 @@ def err_dependent_typed_non_type_arg_in_
     "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">; 
+    "%select{class|variable}0 template partial specialization does not "
+    "specialize any template argument; to %select{declare|define}1 the "
+    "primary template, remove the template argument list">; 
 def warn_partial_specs_not_deducible : Warning<
-    "class template partial specialization contains "
-    "%select{a template parameter|template parameters}0 that can not be "
+    "%select{class|variable}0 template partial specialization contains "
+    "%select{a template parameter|template parameters}1 that can not be "
     "deduced; this partial specialization will never be used">;
 def note_partial_spec_unused_parameter : Note<
     "non-deducible template parameter %0">;

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Mon Sep 23 23:49:23 2013
@@ -2273,6 +2273,69 @@ static bool CheckTemplateSpecializationS
 
 static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
 
+static bool isTemplateArgumentTemplateParameter(
+    const TemplateArgument &Arg, unsigned Depth, unsigned Index) {
+  switch (Arg.getKind()) {
+  case TemplateArgument::Null:
+  case TemplateArgument::NullPtr:
+  case TemplateArgument::Integral:
+  case TemplateArgument::Declaration:
+  case TemplateArgument::Pack:
+  case TemplateArgument::TemplateExpansion:
+    return false;
+
+  case TemplateArgument::Type: {
+    QualType Type = Arg.getAsType();
+    const TemplateTypeParmType *TPT =
+        Arg.getAsType()->getAs<TemplateTypeParmType>();
+    return TPT && !Type.hasQualifiers() &&
+           TPT->getDepth() == Depth && TPT->getIndex() == Index;
+  }
+
+  case TemplateArgument::Expression: {
+    DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg.getAsExpr());
+    if (!DRE || !DRE->getDecl())
+      return false;
+    const NonTypeTemplateParmDecl *NTTP =
+        dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl());
+    return NTTP && NTTP->getDepth() == Depth && NTTP->getIndex() == Index;
+  }
+
+  case TemplateArgument::Template:
+    const TemplateTemplateParmDecl *TTP =
+        dyn_cast_or_null<TemplateTemplateParmDecl>(
+            Arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl());
+    return TTP && TTP->getDepth() == Depth && TTP->getIndex() == Index;
+  }
+  llvm_unreachable("unexpected kind of template argument");
+}
+
+static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
+                                    ArrayRef<TemplateArgument> Args) {
+  if (Params->size() != Args.size())
+    return false;
+
+  unsigned Depth = Params->getDepth();
+
+  for (unsigned I = 0, N = Args.size(); I != N; ++I) {
+    TemplateArgument Arg = Args[I];
+
+    // If the parameter is a pack expansion, the argument must be a pack
+    // whose only element is a pack expansion.
+    if (Params->getParam(I)->isParameterPack()) {
+      if (Arg.getKind() != TemplateArgument::Pack || Arg.pack_size() != 1 ||
+          !Arg.pack_begin()->isPackExpansion())
+        return false;
+      Arg = Arg.pack_begin()->getPackExpansionPattern();
+    }
+
+    if (!isTemplateArgumentTemplateParameter(Arg, Depth, I))
+      return false;
+  }
+
+  return true;
+}
+
 DeclResult Sema::ActOnVarTemplateSpecialization(
     Scope *S, VarTemplateDecl *VarTemplate, Declarator &D, TypeSourceInfo *DI,
     SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
@@ -2341,6 +2404,21 @@ DeclResult Sema::ActOnVarTemplateSpecial
           << VarTemplate->getDeclName();
       IsPartialSpecialization = false;
     }
+
+    if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(),
+                                Converted)) {
+      // 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)
+        << /*variable template*/ 1
+        << /*is definition*/(SC != SC_Extern && !CurContext->isRecord())
+        << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc));
+      // FIXME: Recover from this by treating the declaration as a redeclaration
+      // of the primary template.
+      return true;
+    }
   }
 
   void *InsertPos = 0;
@@ -2402,7 +2480,8 @@ DeclResult Sema::ActOnVarTemplateSpecial
       unsigned NumNonDeducible =
           DeducibleParams.size() - DeducibleParams.count();
       Diag(TemplateNameLoc, diag::warn_partial_specs_not_deducible)
-          << (NumNonDeducible > 1) << SourceRange(TemplateNameLoc, RAngleLoc);
+        << /*variable template*/ 1 << (NumNonDeducible > 1)
+        << SourceRange(TemplateNameLoc, RAngleLoc);
       for (unsigned I = 0, N = DeducibleParams.size(); I != N; ++I) {
         if (!DeducibleParams[I]) {
           NamedDecl *Param = cast<NamedDecl>(TemplateParams->getParam(I));
@@ -5881,7 +5960,7 @@ Sema::ActOnClassTemplateSpecialization(S
       //   -- 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)
-        << (TUK == TUK_Definition)
+        << /*class template*/0 << (TUK == TUK_Definition)
         << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc));
       return CheckClassTemplate(S, TagSpec, TUK, KWLoc, SS,
                                 ClassTemplate->getIdentifier(),
@@ -5935,7 +6014,7 @@ Sema::ActOnClassTemplateSpecialization(S
     if (!DeducibleParams.all()) {
       unsigned NumNonDeducible = DeducibleParams.size()-DeducibleParams.count();
       Diag(TemplateNameLoc, diag::warn_partial_specs_not_deducible)
-        << (NumNonDeducible > 1)
+        << /*class template*/0 << (NumNonDeducible > 1)
         << SourceRange(TemplateNameLoc, RAngleLoc);
       for (unsigned I = 0, N = DeducibleParams.size(); I != N; ++I) {
         if (!DeducibleParams[I]) {

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp?rev=191278&view=auto
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp Mon Sep 23 23:49:23 2013
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++1y -fsyntax-only -verify %s
+
+// -- The argument list of the specialization shall not be identical
+//    to the implicit argument list of the primary template.
+
+template<typename T, int N, template<typename> class X> int v1;
+template<typename T, int N, template<typename> class X> int v1<T, N, X>;
+// expected-error at -1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}}
+
+template<typename...T> int v2;
+template<typename...T> int v2<T...>;
+// expected-error at -1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}}
+
+template<int...N> int v3;
+template<int...N> int v3<N...>;
+// expected-error at -1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}}
+
+template<template<typename> class...X> int v4;
+template<template<typename> class...X> int v4<X...>;
+// expected-error at -1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}}
+
+template<typename Outer> struct X {
+  template<typename Inner> static int y;
+  template<typename Inner> static int y<Outer>; // expected-warning {{can not be deduced}} expected-note {{'Inner'}}
+  template<typename Inner> static int y<Inner>; // expected-error {{does not specialize}}
+};
+template<typename Outer> template<typename Inner> int X<Outer>::y<Outer>; // expected-warning {{can not be deduced}} expected-note {{'Inner'}}
+template<typename Outer> template<typename Inner> int X<Outer>::y<Inner>; // expected-error {{does not specialize}}
+template<> template<typename Inner> int X<int>::y<Inner>; // expected-error {{does not specialize}}

Modified: cfe/trunk/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp Mon Sep 23 23:49:23 2013
@@ -49,7 +49,7 @@ template struct a<int>;
 struct b {
   template <typename T> static T i;
 };
-template<typename T> T b::i<T> = foo();
+template<typename T> T b::i = foo();
 template int b::i<int>;
 }
 // CHECK: define internal void @[[unordered1]]

Modified: cfe/trunk/test/PCH/cxx1y-variable-templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx1y-variable-templates.cpp?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/test/PCH/cxx1y-variable-templates.cpp (original)
+++ cfe/trunk/test/PCH/cxx1y-variable-templates.cpp Mon Sep 23 23:49:23 2013
@@ -58,7 +58,7 @@ namespace spec {
   template<typename T> T vc = T();
 
   template<typename T> constexpr T vd = T(10);
-  template<typename T> T* vd<T> = new T();
+  template<typename T> T* vd<T*> = new T();
 }
 
 namespace spec_join1 {
@@ -72,7 +72,7 @@ namespace spec_join1 {
   template<typename T> T vc = T(10);
 
   template<typename T> T vd = T(10);
-  template<typename T> extern T* vd<T>;
+  template<typename T> extern T* vd<T*>;
 }
 
 #endif
@@ -108,7 +108,7 @@ namespace spec_join1 {
   template int vc<int>;
   
   template<typename T> extern T vd;
-  template<typename T> T* vd<T> = new T();
+  template<typename T> T* vd<T*> = new T();
 }
 
 #endif
@@ -146,16 +146,16 @@ namespace spec {
   static_assert(va<float> == 1.5, "");
   static_assert(va<int> == 10, "");
 
-  template<typename T> T* vb<T> = new T();
-  int* intpb = vb<int>;
+  template<typename T> T* vb<T*> = new T();
+  int* intpb = vb<int*>;
   static_assert(vb<float> == 1.5, "");
 
-  template<typename T> T* vc<T> = new T();
+  template<typename T> T* vc<T*> = new T();
   template<> constexpr float vc<float> = 1.5;
-  int* intpc = vc<int>;
+  int* intpc = vc<int*>;
   static_assert(vc<float> == 1.5, "");
 
-  char* intpd = vd<char>;
+  char* intpd = vd<char*>;
 }
 
 namespace spec_join1 {
@@ -165,7 +165,7 @@ namespace spec_join1 {
   template<typename T> extern T vb;
   int b = vb<int>;
 
-  int* intpb = vd<int>;
+  int* intpb = vd<int*>;
 }
 
 #endif

Modified: cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp Mon Sep 23 23:49:23 2013
@@ -101,29 +101,29 @@ namespace non_const_init {
     
     struct C1a {
       template<typename U> static U Data;
-      template<typename U> static U* Data<U>;   // Okay, with out-of-line definition
+      template<typename U> static U* Data<U*>;   // Okay, with out-of-line definition
     };
-    template<typename T> T* C1a::Data<T> = new T();
-    template int* C1a::Data<int>;
+    template<typename T> T* C1a::Data<T*> = new T();
+    template int* C1a::Data<int*>;
     
     struct C1b {
       template<typename U> static U Data;
-      template<typename U> static CONST U* Data<U>;   // Okay, with out-of-line definition
+      template<typename U> static CONST U* Data<U*>;   // Okay, with out-of-line definition
     };
-    template<typename T> CONST T* C1b::Data<T> = (T*)(0);
-    template CONST int* C1b::Data<int>;
+    template<typename T> CONST T* C1b::Data<T*> = (T*)(0);
+    template CONST int* C1b::Data<int*>;
 
     struct C2a {
-      template<typename U> static U Data;
-      template<typename U> static U* Data<U> = new U();   // expected-error {{non-const static data member must be initialized out of line}}
+      template<typename U> static int Data;
+      template<typename U> static U* Data<U*> = new U();   // expected-error {{non-const static data member must be initialized out of line}}
     };
-    template int* C2a::Data<int>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2a::Data<int>' requested here}}
+    template int* C2a::Data<int*>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2a::Data<int *>' requested here}}
     
     struct C2b {  // FIXME: ?!? Should this be an error? pointer-types are automatically non-const?
-      template<typename U> static U Data;
-      template<typename U> static CONST U* Data<U> = (U*)(0); // expected-error {{non-const static data member must be initialized out of line}}
+      template<typename U> static int Data;
+      template<typename U> static CONST U* Data<U*> = (U*)(0); // expected-error {{non-const static data member must be initialized out of line}}
     };
-    template CONST int* C2b::Data<int>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2b::Data<int>' requested here}}
+    template CONST int* C2b::Data<int*>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2b::Data<int *>' requested here}}
   }
 }
 
@@ -211,16 +211,17 @@ namespace in_class_template {
   }
   
   namespace other_bugs {
-    // FIXME: This fails to properly initialize the variable 'k'.
-    
+    // FIXME: This fails to properly initialize the variables 'k1' and 'k2'.
+
     template<typename A> struct S { 
-      template<typename B> static int V;
       template<typename B> static int V0;
+      template<typename B> static int V1;
     };
     template struct S<int>;
     template<typename A> template<typename B> int S<A>::V0 = 123;
-    template<typename A> template<typename B> int S<A>::V<B> = 123;
-    int k = S<int>::V<void>;
+    template<typename A> template<typename B> int S<A>::V1<B*> = 123;
+    int k1 = S<int>::V0<void>;
+    int k2 = S<int>::V1<void*>;
   }
 
   namespace incomplete_array {
@@ -244,7 +245,7 @@ namespace in_class_template {
 
     // FIXME: These cases should be accepted.
     int *use_before_definition = A<int>::x<char>;
-    template<typename T> template<typename U> T A<T>::x<U>[sizeof(U)];
+    template<typename T> template<typename U> T A<T>::x[sizeof(U)];
     static_assert(sizeof(A<int>::x<char>) == 1, ""); // expected-error {{incomplete}}
 
     template<typename T> template<typename...U> T A<T>::y<tuple<U...> >[] = { U()... };

Modified: cfe/trunk/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-variable-templates_top_level.cpp?rev=191278&r1=191277&r2=191278&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-variable-templates_top_level.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-variable-templates_top_level.cpp Mon Sep 23 23:49:23 2013
@@ -315,8 +315,7 @@ namespace explicit_specialization {
   
   namespace diff_type {
     // TODO:
-    template<typename T> T var = T();
-    template<typename T> T* var<T> = new T();
+    template<typename T> T* var = new T();
 #ifndef PRECXX11
     template<typename T> auto var<T*> = T();  // expected-note {{previous definition is here}}
     template<typename T> T var<T*> = T();     // expected-error {{redefinition of 'var' with a different type: 'T' vs 'auto'}}





More information about the cfe-commits mailing list