From: Douglas Gregor Date: Fri, 12 Jun 2009 22:08:06 +0000 (+0000) Subject: Diagnose C++ [temp.class.spec]p9b3, where a class template partial X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6aa75cfbdd473cb8fb2a2261abf7e9d3c8389bca;p=clang 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 7291d9148b..16d490f09b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -765,6 +765,10 @@ def err_dependent_non_type_arg_in_partial_spec : Error< 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 2b4713980b..3315952723 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1954,7 +1954,8 @@ public: 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 113ed98eb3..b1a8ef2f7f 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2006,22 +2006,68 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, /// \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(TemplateParams->getParam(I))) { + if (Context.getCanonicalType(Context.getTypeDeclType(TTP)) != + Context.getCanonicalType(TemplateArgs[I].getAsType())) + MirrorsPrimaryTemplate = false; + } else if (TemplateTemplateParmDecl *TTP + = dyn_cast( + TemplateParams->getParam(I))) { + // FIXME: We should settle on either Declaration storage or + // Expression storage for template template parameters. + TemplateTemplateParmDecl *ArgDecl + = dyn_cast_or_null( + TemplateArgs[I].getAsDecl()); + if (!ArgDecl) + if (DeclRefExpr *DRE + = dyn_cast_or_null(TemplateArgs[I].getAsExpr())) + ArgDecl = dyn_cast(DRE->getDecl()); + + if (!ArgDecl || + ArgDecl->getIndex() != TTP->getIndex() || + ArgDecl->getDepth() != TTP->getDepth()) + MirrorsPrimaryTemplate = false; + } + } + NonTypeTemplateParmDecl *Param = dyn_cast(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 @@ bool Sema::CheckClassTemplatePartialSpecializationArgs( // specialized non-type arguments, so skip any non-specialized // arguments. if (DeclRefExpr *DRE = dyn_cast(ArgExpr)) - if (isa(DRE->getDecl())) + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(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 @@ bool Sema::CheckClassTemplatePartialSpecializationArgs( Diag(Param->getLocation(), diag::note_template_param_here); return true; } + + MirrorsPrimaryTemplate = false; } return false; @@ -2179,11 +2234,30 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, // 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 5fd95a7d2b..d303146dce 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 struct vector; // C++ [temp.class.spec]p9 @@ -19,10 +18,14 @@ template< int X, int (*array_ptr)[X] > class A2 {}; // expected-note{{here}} int array[5]; template< int X > class A2 { }; // expected-error{{specializes}} -// C++ [temp.class.spec]p10 template class TT> struct Test0; +// bullet 3 +template class TT> +struct Test0; // expected-error{{does not specialize}} + +// C++ [temp.class.spec]p10 template class TT = ::vector> // expected-error{{default template argument}} diff --git a/www/cxx_status.html b/www/cxx_status.html index b2ba8a35df..3a9a7290fc 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -1888,11 +1888,11 @@ welcome!

- + N/A -       14.5.5.1 [temp.class.spec.match] +       14.5.4.1 [temp.class.spec.match] N/A N/A @@ -1900,7 +1900,7 @@ welcome!

-       14.5.5.2 [temp.class.order] +       14.5.4.2 [temp.class.order]