From: Douglas Gregor Date: Fri, 12 Jun 2009 21:21:02 +0000 (+0000) Subject: Diagnose the incorrect use of non-type template arguments for class X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e94866ffc12d33d30b351f30aac4aa3828bc05d7;p=clang Diagnose the incorrect use of non-type template arguments for class template partial specializations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@73254 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1a1fdbf77c..7291d9148b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -759,6 +759,12 @@ def err_template_spec_redecl_global_scope : Error< // C++ Class Template Partial Specialization def err_default_arg_in_partial_spec : Error< "default template argument in a class template partial specialization">; +def err_dependent_non_type_arg_in_partial_spec : Error< + "non-type template argument depends on a template parameter of the " + "partial specialization">; +def err_dependent_typed_non_type_arg_in_partial_spec : Error< + "non-type template argument specializes a template parameter with " + "dependent type %0">; 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 03ff7a2015..2b4713980b 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1952,6 +1952,10 @@ public: SourceRange ScopeSpecifierRange, bool ExplicitInstantiation); + bool CheckClassTemplatePartialSpecializationArgs( + TemplateParameterList *TemplateParams, + const TemplateArgument *TemplateArgs); + virtual DeclResult ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 5fb1fb89f0..113ed98eb3 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2003,6 +2003,68 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, return false; } +/// \brief Check the non-type template arguments of a class template +/// partial specialization according to C++ [temp.class.spec]p9. +/// +/// \returns true if there was an error, false otherwise. +bool Sema::CheckClassTemplatePartialSpecializationArgs( + TemplateParameterList *TemplateParams, + const TemplateArgument *TemplateArgs) { + // FIXME: the interface to this function will have to change to + // accommodate variadic templates. + + for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { + NonTypeTemplateParmDecl *Param + = dyn_cast(TemplateParams->getParam(I)); + if (!Param) + continue; + + Expr *ArgExpr = TemplateArgs[I].getAsExpr(); + if (!ArgExpr) + continue; + + // C++ [temp.class.spec]p8: + // A non-type argument is non-specialized if it is the name of a + // non-type parameter. All other non-type arguments are + // specialized. + // + // Below, we check the two conditions that only apply to + // specialized non-type arguments, so skip any non-specialized + // arguments. + if (DeclRefExpr *DRE = dyn_cast(ArgExpr)) + if (isa(DRE->getDecl())) + continue; + + // C++ [temp.class.spec]p9: + // Within the argument list of a class template partial + // specialization, the following restrictions apply: + // -- A partially specialized non-type argument expression + // shall not involve a template parameter of the partial + // specialization except when the argument expression is a + // simple identifier. + if (ArgExpr->isTypeDependent() || ArgExpr->isValueDependent()) { + Diag(ArgExpr->getLocStart(), + diag::err_dependent_non_type_arg_in_partial_spec) + << ArgExpr->getSourceRange(); + return true; + } + + // -- The type of a template parameter corresponding to a + // specialized non-type argument shall not be dependent on a + // parameter of the specialization. + if (Param->getType()->isDependentType()) { + Diag(ArgExpr->getLocStart(), + diag::err_dependent_typed_non_type_arg_in_partial_spec) + << Param->getType() + << ArgExpr->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + } + + return false; +} + Sema::DeclResult Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, @@ -2117,6 +2179,11 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, // corresponds to these arguments. llvm::FoldingSetNodeID ID; if (isPartialSpecialization) { + if (CheckClassTemplatePartialSpecializationArgs( + ClassTemplate->getTemplateParameters(), + ConvertedTemplateArgs.getFlatArgumentList())) + return true; + // FIXME: Template parameter list matters, too ClassTemplatePartialSpecializationDecl::Profile(ID, ConvertedTemplateArgs.getFlatArgumentList(), diff --git a/lib/Sema/SemaTemplateInstantiateExpr.cpp b/lib/Sema/SemaTemplateInstantiateExpr.cpp index fa5fdee2c1..3c67f2ad0d 100644 --- a/lib/Sema/SemaTemplateInstantiateExpr.cpp +++ b/lib/Sema/SemaTemplateInstantiateExpr.cpp @@ -112,6 +112,14 @@ TemplateExprInstantiator::VisitDeclRefExpr(DeclRefExpr *E) { if (NonTypeTemplateParmDecl *NTTP = dyn_cast(D)) { assert(NTTP->getDepth() == 0 && "No nested templates yet"); const TemplateArgument &Arg = TemplateArgs[NTTP->getPosition()]; + + // The template argument itself might be an expression, in which + // case we just return that expression. + if (Arg.getKind() == TemplateArgument::Expression) + // FIXME: Clone the expression! + return SemaRef.Owned(Arg.getAsExpr()); + + assert(Arg.getKind() == TemplateArgument::Integral); QualType T = Arg.getIntegralType(); if (T->isCharType() || T->isWideCharType()) return SemaRef.Owned(new (SemaRef.Context) CharacterLiteral( diff --git a/test/SemaTemplate/temp_class_spec.cpp b/test/SemaTemplate/temp_class_spec.cpp index 0d83a9bcc3..ce1459b1d2 100644 --- a/test/SemaTemplate/temp_class_spec.cpp +++ b/test/SemaTemplate/temp_class_spec.cpp @@ -255,48 +255,3 @@ int is_nested_value_type_identity1[ //int is_nested_value_type_identity2[ // is_nested_value_type_identity::value? -1 : 1]; -// FIXME: The tests that follow are stress-tests for the substitution -// of deduced template arguments into the template argument list of a -// partial specialization. I believe that we'll need this code for -// substitution into function templates, but note that the examples -// below are ill-formed and should eventually be removed. -template -struct is_sizeof_T { - static const bool value = false; -}; - -template -struct is_sizeof_T { - static const bool value = true; -}; - -int is_sizeof_T0[is_sizeof_T::value? 1 : -1]; -int is_sizeof_T1[is_sizeof_T::value? -1 : 1]; - -template -struct HasStaticOfT { - static T value; - static T other_value; -}; - -struct DerivedStaticOfInt : HasStaticOfT { }; - -template -struct is_static_int_val { - static const bool value = false; -}; - -template -struct is_static_int_val { - static const bool value = true; -}; - -int is_static_int_val0[ - is_static_int_val, int, - &HasStaticOfT::value>::value ? 1 : -1]; -int is_static_int_val1[ - is_static_int_val, int, - &HasStaticOfT::other_value>::value ? -1 : 1]; -int is_static_int_val2[ - is_static_int_val::value>::value ? 1 : -1]; diff --git a/test/SemaTemplate/temp_class_spec_neg.cpp b/test/SemaTemplate/temp_class_spec_neg.cpp index e9114349a3..5fd95a7d2b 100644 --- a/test/SemaTemplate/temp_class_spec_neg.cpp +++ b/test/SemaTemplate/temp_class_spec_neg.cpp @@ -2,6 +2,24 @@ template struct vector; +// C++ [temp.class.spec]p9 + +// bullet 1 +template struct A {}; +template struct A {}; // expected-error{{depends on}} +template struct B {}; +template struct B {}; //OK + +// bullet 2 +template struct C {}; // expected-note{{declared here}} +template struct C; // expected-error{{specializes}} +template struct C; // okay + +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;