From dd0574e76439f31c02ba54bd7708725176f9531f Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 10 Feb 2009 00:24:35 +0000 Subject: [PATCH] Check template template arguments against their corresponding template template parameters. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64188 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.def | 12 ++++ lib/Sema/Sema.h | 4 +- lib/Sema/SemaTemplate.cpp | 78 +++++++++++++++++---- test/SemaTemplate/temp_arg_template.cpp | 42 +++++++++++ 4 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 test/SemaTemplate/temp_arg_template.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index aa56be8bc8..d4d68a4d34 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -479,12 +479,18 @@ DIAG(err_template_unnamed_class, ERROR, "cannot declare a class template with no name") DIAG(err_template_param_list_different_arity, ERROR, "%select{too few|too many}0 template parameters in template %select{|template parameter }1redeclaration") +DIAG(note_template_param_list_different_arity, NOTE, + "%select{too few|too many}0 template parameters in template template argument") DIAG(note_template_prev_declaration, NOTE, "previous template %select{declaration|template parameter}0 is here") DIAG(err_template_param_different_kind, ERROR, "template parameter has a different kind in template %select{|template parameter }0redeclaration") +DIAG(note_template_param_different_kind, NOTE, + "template parameter has a different kind in template argument") DIAG(err_template_nontype_parm_different_type, ERROR, "template non-type parameter has a different type %0 in template %select{|template parameter }1redeclaration") +DIAG(note_template_nontype_parm_different_type, NOTE, + "template non-type parameter has a different type %0 in template argument") DIAG(note_template_nontype_parm_prev_declaration, NOTE, "previous non-type template parameter with type %0 is here") @@ -507,6 +513,12 @@ DIAG(err_template_arg_unnamed_type, ERROR, "template argument uses unnamed type") DIAG(note_template_unnamed_type_here, NOTE, "unnamed type used in template argument was declared here") +DIAG(err_template_arg_not_class_template, ERROR, + "template argument does not refer to a class template") +DIAG(note_template_arg_refers_here, NOTE, + "template argument refers to function template %0, here") +DIAG(err_template_arg_template_params_mismatch, ERROR, + "template template argument has different template parameters than its corresponding template template parameter") DIAG(err_unexpected_typedef, ERROR, "unexpected type name %0: expected expression") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 353f891391..e3d132efbb 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1538,7 +1538,9 @@ public: bool TemplateParameterListsAreEqual(TemplateParameterList *New, TemplateParameterList *Old, bool Complain, - bool IsTemplateTemplateParm = false); + bool IsTemplateTemplateParm = false, + SourceLocation TemplateArgLoc + = SourceLocation()); bool CheckTemplateDeclScope(Scope *S, MultiTemplateParamsArg &TemplateParameterLists); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 297410471b..1a2da84642 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -555,7 +555,27 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, /// It returns true if an error occurred, and false otherwise. bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg) { - return false; + assert(isa(Arg->getDecl()) && "Only template decls allowed"); + TemplateDecl *Template = cast(Arg->getDecl()); + + // C++ [temp.arg.template]p1: + // A template-argument for a template template-parameter shall be + // the name of a class template, expressed as id-expression. Only + // primary class templates are considered when matching the + // template template argument with the corresponding parameter; + // partial specializations are not considered even if their + // parameter lists match that of the template template parameter. + if (!isa(Template)) { + assert(isa(Template) && + "Only function templates are possible here"); + Diag(Arg->getSourceRange().getBegin(), diag::note_template_arg_refers_here) + << Template; + } + + return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), + Param->getTemplateParameters(), + true, true, + Arg->getSourceRange().getBegin()); } /// \brief Determine whether the given template parameter lists are @@ -571,19 +591,35 @@ bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, /// \param Complain If true, this routine will produce a diagnostic if /// the template parameter lists are not equivalent. /// +/// \param IsTemplateTemplateParm If true, this routine is being +/// called to compare the template parameter lists of a template +/// template parameter. +/// +/// \param TemplateArgLoc If this source location is valid, then we +/// are actually checking the template parameter list of a template +/// argument (New) against the template parameter list of its +/// corresponding template template parameter (Old). We produce +/// slightly different diagnostics in this scenario. +/// /// \returns True if the template parameter lists are equal, false /// otherwise. bool Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, TemplateParameterList *Old, bool Complain, - bool IsTemplateTemplateParm) { + bool IsTemplateTemplateParm, + SourceLocation TemplateArgLoc) { if (Old->size() != New->size()) { if (Complain) { - Diag(New->getTemplateLoc(), diag::err_template_param_list_different_arity) - << (New->size() > Old->size()) - << IsTemplateTemplateParm - << SourceRange(New->getTemplateLoc(), New->getRAngleLoc()); + unsigned NextDiag = diag::err_template_param_list_different_arity; + if (TemplateArgLoc.isValid()) { + Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_param_list_different_arity; + } + Diag(New->getTemplateLoc(), NextDiag) + << (New->size() > Old->size()) + << IsTemplateTemplateParm + << SourceRange(New->getTemplateLoc(), New->getRAngleLoc()); Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) << IsTemplateTemplateParm << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc()); @@ -596,7 +632,12 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, OldParmEnd = Old->end(), NewParm = New->begin(); OldParm != OldParmEnd; ++OldParm, ++NewParm) { if ((*OldParm)->getKind() != (*NewParm)->getKind()) { - Diag((*NewParm)->getLocation(), diag::err_template_param_different_kind) + unsigned NextDiag = diag::err_template_param_different_kind; + if (TemplateArgLoc.isValid()) { + Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_param_different_kind; + } + Diag((*NewParm)->getLocation(), NextDiag) << IsTemplateTemplateParm; Diag((*OldParm)->getLocation(), diag::note_template_prev_declaration) << IsTemplateTemplateParm; @@ -605,8 +646,15 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, if (isa(*OldParm)) { // Okay; all template type parameters are equivalent (since we - // know we're at the same depth/level). -#ifndef NDEBUG + // know we're at the same index). +#if 0 + // FIXME: Enable this code in debug mode *after* we properly go + // through and "instantiate" the template parameter lists of + // template template parameters. It's only after this + // instantiation that (1) any dependent types within the + // template parameter list of the template template parameter + // can be checked, and (2) the template type parameter depths + // will match up. QualType OldParmType = Context.getTypeDeclType(cast(*OldParm)); QualType NewParmType @@ -623,8 +671,13 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, if (Context.getCanonicalType(OldNTTP->getType()) != Context.getCanonicalType(NewNTTP->getType())) { if (Complain) { - Diag(NewNTTP->getLocation(), - diag::err_template_nontype_parm_different_type) + unsigned NextDiag = diag::err_template_nontype_parm_different_type; + if (TemplateArgLoc.isValid()) { + Diag(TemplateArgLoc, + diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_nontype_parm_different_type; + } + Diag(NewNTTP->getLocation(), NextDiag) << NewNTTP->getType() << IsTemplateTemplateParm; Diag(OldNTTP->getLocation(), @@ -646,7 +699,8 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, if (!TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), OldTTP->getTemplateParameters(), Complain, - /*IsTemplateTemplateParm=*/true)) + /*IsTemplateTemplateParm=*/true, + TemplateArgLoc)) return false; } } diff --git a/test/SemaTemplate/temp_arg_template.cpp b/test/SemaTemplate/temp_arg_template.cpp new file mode 100644 index 0000000000..3e8e6f25ce --- /dev/null +++ b/test/SemaTemplate/temp_arg_template.cpp @@ -0,0 +1,42 @@ +// RUN: clang -fsyntax-only -verify %s + +template class X> struct A; // expected-note 2{{previous template template parameter is here}} + +template class X> struct B; // expected-note{{previous template template parameter is here}} + +template class X> struct C; // expected-note{{previous non-type template parameter with type 'int' is here}} + +template struct X; // expected-note{{too few template parameters in template template argument}} +template struct Y; // expected-note{{template parameter has a different kind in template argument}} +template struct Ylong; // expected-note{{template non-type parameter has a different type 'long' in template argument}} + +namespace N { + template struct Z; +} +template struct TooMany; // expected-note{{too many template parameters in template template argument}} + + +A *a1; +A *a2; +A< ::N::Z> *a3; + +A *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ + // FIXME::expected-error{{expected unqualified-id}} +A *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ + // FIXME::expected-error{{expected unqualified-id}} +B *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ + // FIXME::expected-error{{expected unqualified-id}} +C *a7; +C *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ + // FIXME::expected-error{{expected unqualified-id}} + +template void f(int); + +// FIXME: we're right to provide an error message, but it should say +// that we need a class template. We won't get this right until name +// lookup of 'f' returns a TemplateDecl. +A *a9; // expected-error{{template argument for template template parameter must be a template}} + +// FIXME: The code below is ill-formed, because of the evil digraph '<:'. +// We should provide a much better error message than we currently do. +// A<::N::Z> *a10; -- 2.40.0