summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2013-09-24 04:49:23 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2013-09-24 04:49:23 +0000
commitecbce69e354c77bf2d359111bad0c77c516e16f0 (patch)
tree074c7cd3b390ac05673a3d0020e5cf13de90b084
parentf7b9a5a37fc9d1f6356afa14865129fb4a617932 (diff)
downloadclang-ecbce69e354c77bf2d359111bad0c77c516e16f0.tar.gz
clang-ecbce69e354c77bf2d359111bad0c77c516e16f0.tar.bz2
clang-ecbce69e354c77bf2d359111bad0c77c516e16f0.tar.xz
Implement restriction that a partial specialization must actually specialize
something, for variable templates. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191278 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td10
-rw-r--r--lib/Sema/SemaTemplate.cpp85
-rw-r--r--test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp29
-rw-r--r--test/CodeGenCXX/static-member-variable-explicit-specialization.cpp2
-rw-r--r--test/PCH/cxx1y-variable-templates.cpp18
-rw-r--r--test/SemaCXX/cxx1y-variable-templates_in_class.cpp37
-rw-r--r--test/SemaCXX/cxx1y-variable-templates_top_level.cpp3
7 files changed, 146 insertions, 38 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 8486ff33c5..2ac197fd52 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3127,12 +3127,12 @@ 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">;
+ "%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">;
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index a7a6bb4f0c..09dd28e091 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -2273,6 +2273,69 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized,
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::ActOnVarTemplateSpecialization(
<< 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::ActOnVarTemplateSpecialization(
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(Scope *S, unsigned TagSpec,
// -- 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(Scope *S, unsigned TagSpec,
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]) {
diff --git a/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp b/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp
new file mode 100644
index 0000000000..24513b78fa
--- /dev/null
+++ b/test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp
@@ -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@-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@-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@-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@-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}}
diff --git a/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp b/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
index f4b72469c7..50772bf647 100644
--- a/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
+++ b/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
@@ -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]]
diff --git a/test/PCH/cxx1y-variable-templates.cpp b/test/PCH/cxx1y-variable-templates.cpp
index 0c600b29d3..77eeea22a2 100644
--- a/test/PCH/cxx1y-variable-templates.cpp
+++ b/test/PCH/cxx1y-variable-templates.cpp
@@ -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
diff --git a/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
index 139dd982b3..0058c90418 100644
--- a/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
+++ b/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
@@ -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()... };
diff --git a/test/SemaCXX/cxx1y-variable-templates_top_level.cpp b/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
index c98400cd87..b6e8762f5d 100644
--- a/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
+++ b/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
@@ -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'}}