From 1fef4e60e7e884803977a8376c172ea584f8a5d1 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 7 Oct 2009 22:35:40 +0000 Subject: [PATCH] Type checking for specializations of member functions of class templates. Previously, these weren't handled as specializations at all. The AST for representing these as specializations is still a work in progress. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83498 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 8 +- lib/Sema/Sema.h | 5 +- lib/Sema/SemaDecl.cpp | 38 +++++--- lib/Sema/SemaTemplate.cpp | 97 +++++++++++++++++-- test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp | 14 ++- test/SemaTemplate/class-template-spec.cpp | 5 +- 6 files changed, 137 insertions(+), 30 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6c8b631e39..a93955be77 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -922,8 +922,8 @@ def err_template_spec_unknown_kind : Error< "instantiation|instantiation}0 for a class template, function template, or " "a member function, static data member, or member class of a class template">; def note_specialized_entity : Note< - "explicit %select{||specialized|instantiated|instantiated}0 is " - "here">; + "explicitly %select{||specialized|instantiated|instantiated}0 " + "declaration is here">; def err_template_spec_decl_function_scope : Error< "explicit %select{||specialization|instantiation|" "instantiation}0 of %1 in function scope">; @@ -946,6 +946,10 @@ def err_template_spec_redecl_global_scope : Error< "%select{class template|class template partial|function template|member " "function|static data member|member class}0 specialization of %1 must occur " "at global scope">; +def err_function_spec_not_instantiated : Error< + "specialization of member function %q0 does not specialize an instantiated " + "member function">; +def note_specialized_decl : Note<"attempt to specialize declaration here">; // C++ class template specializations and out-of-line definitions def err_template_spec_needs_header : Error< diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2056d39132..6142eacd57 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2433,7 +2433,8 @@ public: MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, const CXXScopeSpec &SS, TemplateParameterList **ParamLists, - unsigned NumParamLists); + unsigned NumParamLists, + bool &IsExplicitSpecialization); DeclResult CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, const CXXScopeSpec &SS, @@ -2518,6 +2519,8 @@ public: unsigned NumExplicitTemplateArgs, SourceLocation RAngleLoc, NamedDecl *&PrevDecl); + bool CheckMemberFunctionSpecialization(CXXMethodDecl *FD, + NamedDecl *&PrevDecl); virtual DeclResult ActOnExplicitInstantiation(Scope *S, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a1fcf89c54..4743720c62 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2235,12 +2235,15 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. + // FIXME: Actually record when this is an explicit specialization! + bool isExplicitSpecialization = false; if (TemplateParameterList *TemplateParams - = MatchTemplateParametersToScopeSpecifier( + = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getSourceRange().getBegin(), - D.getCXXScopeSpec(), + D.getCXXScopeSpec(), (TemplateParameterList**)TemplateParamLists.get(), - TemplateParamLists.size())) { + TemplateParamLists.size(), + isExplicitSpecialization)) { if (TemplateParams->size() > 0) { // There is no such thing as a variable template. Diag(D.getIdentifierLoc(), diag::err_template_variable) @@ -2256,6 +2259,8 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, << II << SourceRange(TemplateParams->getTemplateLoc(), TemplateParams->getRAngleLoc()); + + isExplicitSpecialization = true; } } @@ -2660,13 +2665,15 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. FunctionTemplateDecl *FunctionTemplate = 0; + bool isExplicitSpecialization = false; bool isFunctionTemplateSpecialization = false; if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getSourceRange().getBegin(), D.getCXXScopeSpec(), (TemplateParameterList**)TemplateParamLists.get(), - TemplateParamLists.size())) { + TemplateParamLists.size(), + isExplicitSpecialization)) { if (TemplateParams->size() > 0) { // This is a function template @@ -2847,7 +2854,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, RAngleLoc = TemplateId->RAngleLoc; if (FunctionTemplate) { - // FIXME: Diagnostic function template with explicit template + // FIXME: Diagnose function template with explicit template // arguments. HasExplicitTemplateArgs = false; } else if (!isFunctionTemplateSpecialization && @@ -2865,13 +2872,17 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } } - if (isFunctionTemplateSpecialization && - CheckFunctionTemplateSpecialization(NewFD, HasExplicitTemplateArgs, - LAngleLoc, TemplateArgs.data(), - TemplateArgs.size(), RAngleLoc, - PrevDecl)) + if (isFunctionTemplateSpecialization) { + if (CheckFunctionTemplateSpecialization(NewFD, HasExplicitTemplateArgs, + LAngleLoc, TemplateArgs.data(), + TemplateArgs.size(), RAngleLoc, + PrevDecl)) + NewFD->setInvalidDecl(); + } else if (isExplicitSpecialization && isa(NewFD) && + CheckMemberFunctionSpecialization(cast(NewFD), + PrevDecl)) NewFD->setInvalidDecl(); - + // Perform semantic checking on the function declaration. bool OverloadableAttrRequired = false; // FIXME: HACK! CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration, @@ -4161,11 +4172,14 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, OwnedDecl = false; TagDecl::TagKind Kind = TagDecl::getTagKindForTypeSpec(TagSpec); + // FIXME: Check explicit specializations more carefully. + bool isExplicitSpecialization = false; if (TUK != TUK_Reference) { if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier(KWLoc, SS, (TemplateParameterList**)TemplateParameterLists.get(), - TemplateParameterLists.size())) { + TemplateParameterLists.size(), + isExplicitSpecialization)) { if (TemplateParams->size() > 0) { // This is a declaration or definition of a class template (which may // be a member of another template). diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 6792b28d1c..f3cd2e41fb 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -933,6 +933,9 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, /// /// \param NumParamLists the number of template parameter lists in ParamLists. /// +/// \param IsExplicitSpecialization will be set true if the entity being +/// declared is an explicit specialization, false otherwise. +/// /// \returns the template parameter list, if any, that corresponds to the /// name that is preceded by the scope specifier @p SS. This template /// parameter list may be have template parameters (if we're declaring a @@ -943,7 +946,10 @@ TemplateParameterList * Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, const CXXScopeSpec &SS, TemplateParameterList **ParamLists, - unsigned NumParamLists) { + unsigned NumParamLists, + bool &IsExplicitSpecialization) { + IsExplicitSpecialization = false; + // Find the template-ids that occur within the nested-name-specifier. These // template-ids will match up with the template parameter lists. llvm::SmallVector @@ -1000,6 +1006,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, << SS.getRange() << CodeModificationHint::CreateInsertion(FirstTemplateLoc, "template<> "); + IsExplicitSpecialization = true; } return 0; } @@ -1031,6 +1038,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, diag::err_template_param_list_matches_nontemplate) << TemplateId << ParamLists[Idx]->getSourceRange(); + else + IsExplicitSpecialization = true; } // If there were at least as many template-ids as there were template @@ -2399,11 +2408,14 @@ static bool CheckTemplateSpecializationScope(Sema &S, // Keep these "kind" numbers in sync with the %select statements in the // various diagnostics emitted by this routine. int EntityKind = 0; - if (isa(Specialized)) + bool isTemplateSpecialization = false; + if (isa(Specialized)) { EntityKind = IsPartialSpecialization? 1 : 0; - else if (isa(Specialized)) + isTemplateSpecialization = true; + } else if (isa(Specialized)) { EntityKind = 2; - else if (isa(Specialized)) + isTemplateSpecialization = true; + } else if (isa(Specialized)) EntityKind = 3; else if (isa(Specialized)) EntityKind = 4; @@ -2464,7 +2476,8 @@ static bool CheckTemplateSpecializationScope(Sema &S, << EntityKind << Specialized << cast(SpecializedContext); - S.Diag(Specialized->getLocation(), diag::note_template_decl_here); + S.Diag(Specialized->getLocation(), diag::note_specialized_entity) + << TSK; ComplainedAboutScope = true; } } @@ -2492,7 +2505,7 @@ static bool CheckTemplateSpecializationScope(Sema &S, << EntityKind << Specialized << cast(SpecializedContext); - S.Diag(Specialized->getLocation(), diag::note_template_decl_here); + S.Diag(Specialized->getLocation(), diag::note_specialized_entity) << TSK; } // FIXME: check for specialization-after-instantiation errors and such. @@ -2640,6 +2653,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, ClassTemplateDecl *ClassTemplate = cast(Name.getAsTemplateDecl()); + bool isExplicitSpecialization = false; bool isPartialSpecialization = false; // Check the validity of the template headers that introduce this @@ -2649,7 +2663,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier(TemplateNameLoc, SS, (TemplateParameterList**)TemplateParameterLists.get(), - TemplateParameterLists.size()); + TemplateParameterLists.size(), + isExplicitSpecialization); if (TemplateParams && TemplateParams->size() > 0) { isPartialSpecialization = true; @@ -2684,9 +2699,11 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, } } } - } else if (!TemplateParams && TUK != TUK_Friend) + } else if (!TemplateParams && TUK != TUK_Friend) { Diag(KWLoc, diag::err_template_spec_needs_header) << CodeModificationHint::CreateInsertion(KWLoc, "template<> "); + isExplicitSpecialization = true; + } // Check that the specialization uses the same tag kind as the // original template. @@ -3101,6 +3118,70 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, return false; } +/// \brief Perform semantic analysis for the given member function +/// specialization. +/// +/// This routine performs all of the semantic analysis required for an +/// explicit member function specialization. On successful completion, +/// the function declaration \p FD will become a member function +/// specialization. +/// +/// \param FD the function declaration, which will be updated to become a +/// function template specialization. +/// +/// \param PrevDecl the set of declarations, one of which may be specialized +/// by this function specialization. +bool +Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD, + NamedDecl *&PrevDecl) { + // Try to find the member function we are instantiating. + CXXMethodDecl *Instantiation = 0; + for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) { + if (CXXMethodDecl *Method = dyn_cast(*Ovl)) { + if (Context.hasSameType(FD->getType(), Method->getType())) { + Instantiation = Method; + break; + } + } + } + + if (!Instantiation) { + // There is no previous declaration that matches. Since member function + // specializations are always out-of-line, the caller will complain about + // this mismatch later. + return false; + } + + // FIXME: Check if the prior declaration has a point of instantiation. + // If so, we have run afoul of C++ [temp.expl.spec]p6. + + // Make sure that this is a specialization of a member function. + FunctionDecl *FunctionInTemplate + = Instantiation->getInstantiatedFromMemberFunction(); + if (!FunctionInTemplate) { + Diag(FD->getLocation(), diag::err_function_spec_not_instantiated) + << FD; + Diag(Instantiation->getLocation(), diag::note_specialized_decl); + return true; + } + + // Check the scope of this explicit specialization. + if (CheckTemplateSpecializationScope(*this, + FunctionInTemplate, + Instantiation, FD->getLocation(), + false, TSK_ExplicitSpecialization)) + return true; + + // FIXME: Mark the new declaration as a member function specialization. + // We may also want to mark the original instantiation as having been + // explicitly specialized. + + // Save the caller the trouble of having to figure out which declaration + // this specialization matches. + PrevDecl = Instantiation; + return false; +} + // Explicit instantiation of a class template specialization // FIXME: Implement extern template semantics Sema::DeclResult diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp index 77df60a25d..553d62c9e5 100644 --- a/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp +++ b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp @@ -51,7 +51,7 @@ template struct X0 { // expected-note 2{{here}} static T member; - void f1(T t) { + void f1(T t) { // expected-note{{explicitly specialized declaration is here}} t = 17; } @@ -85,17 +85,21 @@ namespace N0 { template<> struct X0; } -template<> struct N0::X0 { }; +template<> struct N0::X0 { + void f1(void *); +}; // -- member function of a class template -// FIXME: this should complain about the scope of f1, but we don't seem -// to recognize it as a specialization. Hrm? -template<> void N0::X0::f1(void *) { } +template<> void N0::X0::f1(void *) { } // expected-error{{member function specialization}} void test_spec(N0::X0 xvp, void *vp) { xvp.f1(vp); } +namespace N0 { + template<> void X0::f1(void *) { } // expected-error{{no function template matches}} +} + #if 0 // FIXME: update the remainder of this test to check for scopes properly. // -- static data member of a class template diff --git a/test/SemaTemplate/class-template-spec.cpp b/test/SemaTemplate/class-template-spec.cpp index e4d917f775..05ddb05325 100644 --- a/test/SemaTemplate/class-template-spec.cpp +++ b/test/SemaTemplate/class-template-spec.cpp @@ -1,5 +1,6 @@ // RUN: clang-cc -fsyntax-only -verify %s -template struct A; // expected-note 2{{template is declared here}} +template struct A; // expected-note {{template is declared here}} \ + // expected-note{{explicitly specialized}} template<> struct A; // expected-note{{forward declaration}} @@ -74,7 +75,7 @@ struct A { }; // expected-error{{template specialization requires 'templ template<> struct ::A; namespace N { - template struct B; // expected-note 2{{template is declared here}} + template struct B; // expected-note 2{{explicitly specialized}} template<> struct ::N::B; // okay template<> struct ::N::B; // okay