diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-09-24 04:49:23 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-09-24 04:49:23 +0000 |
commit | ecbce69e354c77bf2d359111bad0c77c516e16f0 (patch) | |
tree | 074c7cd3b390ac05673a3d0020e5cf13de90b084 | |
parent | f7b9a5a37fc9d1f6356afa14865129fb4a617932 (diff) | |
download | clang-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.td | 10 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 85 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp | 29 | ||||
-rw-r--r-- | test/CodeGenCXX/static-member-variable-explicit-specialization.cpp | 2 | ||||
-rw-r--r-- | test/PCH/cxx1y-variable-templates.cpp | 18 | ||||
-rw-r--r-- | test/SemaCXX/cxx1y-variable-templates_in_class.cpp | 37 | ||||
-rw-r--r-- | test/SemaCXX/cxx1y-variable-templates_top_level.cpp | 3 |
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'}} |