From: John McCall Date: Wed, 20 Oct 2010 05:44:58 +0000 (+0000) Subject: When matching template parameter lists to template-ids in a scope specifier X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4e2cbb28baa0342b51336e55c519cd06308c4df2;p=clang When matching template parameter lists to template-ids in a scope specifier on a friend declaration, skip template-ids which do not depend on the current parameter list. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116911 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 66ded48c24..cd67955a22 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -20,6 +20,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TypeVisitor.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ParsedTemplate.h" @@ -1217,6 +1218,70 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, return Invalid; } +namespace { + +/// A class which looks for a use of a certain level of template +/// parameter. +struct DependencyChecker : RecursiveASTVisitor { + typedef RecursiveASTVisitor super; + + unsigned Depth; + bool Match; + + DependencyChecker(TemplateParameterList *Params) : Match(false) { + NamedDecl *ND = Params->getParam(0); + if (TemplateTypeParmDecl *PD = dyn_cast(ND)) { + Depth = PD->getDepth(); + } else if (NonTypeTemplateParmDecl *PD = + dyn_cast(ND)) { + Depth = PD->getDepth(); + } else { + Depth = cast(ND)->getDepth(); + } + } + + bool Matches(unsigned ParmDepth) { + if (ParmDepth >= Depth) { + Match = true; + return true; + } + return false; + } + + bool VisitTemplateTypeParmType(const TemplateTypeParmType *T) { + return !Matches(T->getDepth()); + } + + bool TraverseTemplateName(TemplateName N) { + if (TemplateTemplateParmDecl *PD = + dyn_cast_or_null(N.getAsTemplateDecl())) + if (Matches(PD->getDepth())) return false; + return super::TraverseTemplateName(N); + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + if (NonTypeTemplateParmDecl *PD = + dyn_cast(E->getDecl())) { + if (PD->getDepth() == Depth) { + Match = true; + return false; + } + } + return super::VisitDeclRefExpr(E); + } +}; +} + +/// Determines whether a template-id depends on the given parameter +/// list. +static bool +DependsOnTemplateParameters(const TemplateSpecializationType *TemplateId, + TemplateParameterList *Params) { + DependencyChecker Checker(Params); + Checker.TraverseType(QualType(TemplateId, 0)); + return Checker.Match; +} + /// \brief Match the given template parameter lists to the given scope /// specifier, returning the template parameter list that applies to the /// name. @@ -1310,12 +1375,24 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, // Match the template-ids found in the specifier to the template parameter // lists. - unsigned Idx = 0; + unsigned ParamIdx = 0, TemplateIdx = 0; for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size(); - Idx != NumTemplateIds; ++Idx) { - QualType TemplateId = QualType(TemplateIdsInSpecifier[Idx], 0); + TemplateIdx != NumTemplateIds; ++TemplateIdx) { + const TemplateSpecializationType *TemplateId + = TemplateIdsInSpecifier[TemplateIdx]; bool DependentTemplateId = TemplateId->isDependentType(); - if (Idx >= NumParamLists) { + + // In friend declarations we can have template-ids which don't + // depend on the corresponding template parameter lists. But + // assume that empty parameter lists are supposed to match this + // template-id. + if (IsFriend && ParamIdx < NumParamLists && ParamLists[ParamIdx]->size()) { + if (!DependentTemplateId || + !DependsOnTemplateParameters(TemplateId, ParamLists[ParamIdx])) + continue; + } + + if (ParamIdx >= NumParamLists) { // We have a template-id without a corresponding template parameter // list. @@ -1329,7 +1406,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, // FIXME: the location information here isn't great. Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_template_parameters) - << TemplateId + << QualType(TemplateId, 0) << SS.getRange(); Invalid = true; } else { @@ -1358,35 +1435,38 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, } if (ExpectedTemplateParams) - TemplateParameterListsAreEqual(ParamLists[Idx], + TemplateParameterListsAreEqual(ParamLists[ParamIdx], ExpectedTemplateParams, true, TPL_TemplateMatch); - CheckTemplateParameterList(ParamLists[Idx], 0, TPC_ClassTemplateMember); - } else if (ParamLists[Idx]->size() > 0) - Diag(ParamLists[Idx]->getTemplateLoc(), + CheckTemplateParameterList(ParamLists[ParamIdx], 0, + TPC_ClassTemplateMember); + } else if (ParamLists[ParamIdx]->size() > 0) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), diag::err_template_param_list_matches_nontemplate) << TemplateId - << ParamLists[Idx]->getSourceRange(); + << ParamLists[ParamIdx]->getSourceRange(); else IsExplicitSpecialization = true; + + ++ParamIdx; } // If there were at least as many template-ids as there were template // parameter lists, then there are no template parameter lists remaining for // the declaration itself. - if (Idx >= NumParamLists) + if (ParamIdx >= NumParamLists) return 0; // If there were too many template parameter lists, complain about that now. - if (Idx != NumParamLists - 1) { - while (Idx < NumParamLists - 1) { - bool isExplicitSpecHeader = ParamLists[Idx]->size() == 0; - Diag(ParamLists[Idx]->getTemplateLoc(), + if (ParamIdx != NumParamLists - 1) { + while (ParamIdx < NumParamLists - 1) { + bool isExplicitSpecHeader = ParamLists[ParamIdx]->size() == 0; + Diag(ParamLists[ParamIdx]->getTemplateLoc(), isExplicitSpecHeader? diag::warn_template_spec_extra_headers : diag::err_template_spec_extra_headers) - << SourceRange(ParamLists[Idx]->getTemplateLoc(), - ParamLists[Idx]->getRAngleLoc()); + << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), + ParamLists[ParamIdx]->getRAngleLoc()); if (isExplicitSpecHeader && !ExplicitSpecializationsInSpecifier.empty()) { Diag(ExplicitSpecializationsInSpecifier.back()->getLocation(), @@ -1401,7 +1481,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, if (!isExplicitSpecHeader) Invalid = true; - ++Idx; + ++ParamIdx; } } diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 5e5d7869ff..578de2952d 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -307,3 +307,28 @@ namespace test14 { template class B; // expected-note {{in instantiation}} } + +namespace test15 { + template class B; + template class A { + friend void B::foo(); + + // This shouldn't be misrecognized as a templated-scoped reference. + template friend void B::bar(U); + + static void foo(); // expected-note {{declared private here}} + }; + + template class B { + void foo() { return A::foo(); } // expected-error {{'foo' is a private member of 'test15::A'}} + }; + + template <> class B { + void foo() { return A::foo(); } + template void bar(U u) { + (void) A::foo(); + } + }; + + template class B; // expected-note {{in instantiation}} +}