From ecbce69e354c77bf2d359111bad0c77c516e16f0 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 24 Sep 2013 04:49:23 +0000 Subject: [PATCH] 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 --- include/clang/Basic/DiagnosticSemaKinds.td | 10 +-- lib/Sema/SemaTemplate.cpp | 85 ++++++++++++++++++- .../temp/temp.decls/temp.class.spec/p8-1y.cpp | 29 +++++++ ...ember-variable-explicit-specialization.cpp | 2 +- test/PCH/cxx1y-variable-templates.cpp | 18 ++-- .../cxx1y-variable-templates_in_class.cpp | 37 ++++---- .../cxx1y-variable-templates_top_level.cpp | 3 +- 7 files changed, 146 insertions(+), 38 deletions(-) create mode 100644 test/CXX/temp/temp.decls/temp.class.spec/p8-1y.cpp 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(); + return TPT && !Type.hasQualifiers() && + TPT->getDepth() == Depth && TPT->getIndex() == Index; + } + + case TemplateArgument::Expression: { + DeclRefExpr *DRE = dyn_cast(Arg.getAsExpr()); + if (!DRE || !DRE->getDecl()) + return false; + const NonTypeTemplateParmDecl *NTTP = + dyn_cast(DRE->getDecl()); + return NTTP && NTTP->getDepth() == Depth && NTTP->getIndex() == Index; + } + + case TemplateArgument::Template: + const TemplateTemplateParmDecl *TTP = + dyn_cast_or_null( + Arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()); + return TTP && TTP->getDepth() == Depth && TTP->getIndex() == Index; + } + llvm_unreachable("unexpected kind of template argument"); +} + +static bool isSameAsPrimaryTemplate(TemplateParameterList *Params, + ArrayRef 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(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 class X> int v1; +template class X> int v1; +// 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 v2; +template int v2; +// 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 v3; +template int v3; +// expected-error@-1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template class...X> int v4; +template class...X> int v4; +// expected-error@-1{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template struct X { + template static int y; + template static int y; // expected-warning {{can not be deduced}} expected-note {{'Inner'}} + template static int y; // expected-error {{does not specialize}} +}; +template template int X::y; // expected-warning {{can not be deduced}} expected-note {{'Inner'}} +template template int X::y; // expected-error {{does not specialize}} +template<> template int X::y; // 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; struct b { template static T i; }; -template T b::i = foo(); +template T b::i = foo(); template int b::i; } // 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 T vc = T(); template constexpr T vd = T(10); - template T* vd = new T(); + template T* vd = new T(); } namespace spec_join1 { @@ -72,7 +72,7 @@ namespace spec_join1 { template T vc = T(10); template T vd = T(10); - template extern T* vd; + template extern T* vd; } #endif @@ -108,7 +108,7 @@ namespace spec_join1 { template int vc; template extern T vd; - template T* vd = new T(); + template T* vd = new T(); } #endif @@ -146,16 +146,16 @@ namespace spec { static_assert(va == 1.5, ""); static_assert(va == 10, ""); - template T* vb = new T(); - int* intpb = vb; + template T* vb = new T(); + int* intpb = vb; static_assert(vb == 1.5, ""); - template T* vc = new T(); + template T* vc = new T(); template<> constexpr float vc = 1.5; - int* intpc = vc; + int* intpc = vc; static_assert(vc == 1.5, ""); - char* intpd = vd; + char* intpd = vd; } namespace spec_join1 { @@ -165,7 +165,7 @@ namespace spec_join1 { template extern T vb; int b = vb; - int* intpb = vd; + int* intpb = vd; } #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 static U Data; - template static U* Data; // Okay, with out-of-line definition + template static U* Data; // Okay, with out-of-line definition }; - template T* C1a::Data = new T(); - template int* C1a::Data; + template T* C1a::Data = new T(); + template int* C1a::Data; struct C1b { template static U Data; - template static CONST U* Data; // Okay, with out-of-line definition + template static CONST U* Data; // Okay, with out-of-line definition }; - template CONST T* C1b::Data = (T*)(0); - template CONST int* C1b::Data; + template CONST T* C1b::Data = (T*)(0); + template CONST int* C1b::Data; struct C2a { - template static U Data; - template static U* Data = new U(); // expected-error {{non-const static data member must be initialized out of line}} + template static int Data; + template static U* Data = new U(); // expected-error {{non-const static data member must be initialized out of line}} }; - template int* C2a::Data; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2a::Data' requested here}} + template int* C2a::Data; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2a::Data' requested here}} struct C2b { // FIXME: ?!? Should this be an error? pointer-types are automatically non-const? - template static U Data; - template static CONST U* Data = (U*)(0); // expected-error {{non-const static data member must be initialized out of line}} + template static int Data; + template static CONST U* Data = (U*)(0); // expected-error {{non-const static data member must be initialized out of line}} }; - template CONST int* C2b::Data; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2b::Data' requested here}} + template CONST int* C2b::Data; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2b::Data' 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 struct S { - template static int V; template static int V0; + template static int V1; }; template struct S; template template int S::V0 = 123; - template template int S::V = 123; - int k = S::V; + template template int S::V1 = 123; + int k1 = S::V0; + int k2 = S::V1; } namespace incomplete_array { @@ -244,7 +245,7 @@ namespace in_class_template { // FIXME: These cases should be accepted. int *use_before_definition = A::x; - template template T A::x[sizeof(U)]; + template template T A::x[sizeof(U)]; static_assert(sizeof(A::x) == 1, ""); // expected-error {{incomplete}} template template T A::y >[] = { 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 T var = T(); - template T* var = new T(); + template T* var = new T(); #ifndef PRECXX11 template auto var = T(); // expected-note {{previous definition is here}} template T var = T(); // expected-error {{redefinition of 'var' with a different type: 'T' vs 'auto'}} -- 2.40.0