From 78a07ba4385d12b559dca55e4b8cd696a4f85ccb Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 15 Aug 2017 19:11:21 +0000 Subject: [PATCH] PR33082: Improve tracking of unexpanded parameter packs within variadic generic lambdas. llvm-svn: 310946 --- clang/include/clang/Sema/ScopeInfo.h | 6 + clang/lib/Sema/SemaTemplateVariadic.cpp | 124 ++++++++++++------ .../cxx1y-generic-lambdas-variadics.cpp | 24 ++++ 3 files changed, 116 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 4251fa649a82..6bb667deb229 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -833,6 +833,12 @@ public: return FSI->Kind == SK_Lambda; } + /// Is this scope known to be for a generic lambda? (This will be false until + /// we parse the first 'auto'-typed parameter. + bool isGenericLambda() const { + return !AutoTemplateParams.empty() || GLTemplateParameterList; + } + /// /// \brief Add a variable that might potentially be captured by the /// lambda and therefore the enclosing lambdas. diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 9f572007d8a7..e3925e812ac0 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -26,6 +26,19 @@ using namespace clang; // Visitor that collects unexpanded parameter packs //---------------------------------------------------------------------------- +/// \brief Retrieve the depth and index of a parameter pack. +static std::pair +getDepthAndIndex(NamedDecl *ND) { + if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) + return std::make_pair(TTP->getDepth(), TTP->getIndex()); + + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(ND)) + return std::make_pair(NTTP->getDepth(), NTTP->getIndex()); + + TemplateTemplateParmDecl *TTP = cast(ND); + return std::make_pair(TTP->getDepth(), TTP->getIndex()); +} + namespace { /// \brief A class that collects unexpanded parameter packs. class CollectUnexpandedParameterPacksVisitor : @@ -36,15 +49,36 @@ namespace { SmallVectorImpl &Unexpanded; - bool InLambda; + bool InLambda = false; + unsigned DepthLimit = (unsigned)-1; + void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) { + if (auto *PVD = dyn_cast(ND)) { + // For now, the only problematic case is a generic lambda's templated + // call operator, so we don't need to look for all the other ways we + // could have reached a dependent parameter pack. + auto *FD = dyn_cast(PVD->getDeclContext()); + auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr; + if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit) + return; + } else if (getDepthAndIndex(ND).first >= DepthLimit) + return; + + Unexpanded.push_back({ND, Loc}); + } + void addUnexpanded(const TemplateTypeParmType *T, + SourceLocation Loc = SourceLocation()) { + if (T->getDepth() < DepthLimit) + Unexpanded.push_back({T, Loc}); + } + public: explicit CollectUnexpandedParameterPacksVisitor( - SmallVectorImpl &Unexpanded) - : Unexpanded(Unexpanded), InLambda(false) { } + SmallVectorImpl &Unexpanded) + : Unexpanded(Unexpanded) {} bool shouldWalkTypesOfTypeLocs() const { return false; } - + //------------------------------------------------------------------------ // Recording occurrences of (unexpanded) parameter packs. //------------------------------------------------------------------------ @@ -52,7 +86,7 @@ namespace { /// \brief Record occurrences of template type parameter packs. bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { if (TL.getTypePtr()->isParameterPack()) - Unexpanded.push_back(std::make_pair(TL.getTypePtr(), TL.getNameLoc())); + addUnexpanded(TL.getTypePtr(), TL.getNameLoc()); return true; } @@ -63,7 +97,7 @@ namespace { /// Ideally, this routine would never be used. bool VisitTemplateTypeParmType(TemplateTypeParmType *T) { if (T->isParameterPack()) - Unexpanded.push_back(std::make_pair(T, SourceLocation())); + addUnexpanded(T); return true; } @@ -72,18 +106,18 @@ namespace { /// parameter packs in an expression. bool VisitDeclRefExpr(DeclRefExpr *E) { if (E->getDecl()->isParameterPack()) - Unexpanded.push_back(std::make_pair(E->getDecl(), E->getLocation())); + addUnexpanded(E->getDecl(), E->getLocation()); return true; } /// \brief Record occurrences of template template parameter packs. bool TraverseTemplateName(TemplateName Template) { - if (TemplateTemplateParmDecl *TTP - = dyn_cast_or_null( - Template.getAsTemplateDecl())) + if (auto *TTP = dyn_cast_or_null( + Template.getAsTemplateDecl())) { if (TTP->isParameterPack()) - Unexpanded.push_back(std::make_pair(TTP, SourceLocation())); + addUnexpanded(TTP); + } return inherited::TraverseTemplateName(Template); } @@ -141,7 +175,13 @@ namespace { /// \brief Suppress traversal of non-parameter declarations, since /// they cannot contain unexpanded parameter packs. bool TraverseDecl(Decl *D) { - if ((D && isa(D)) || InLambda) + auto *PVD = dyn_cast_or_null(D); + // A function parameter pack is a pack expansion, so cannot contain + // an unexpanded parameter pack. + if (PVD && PVD->isParameterPack()) + return true; + + if (PVD || InLambda) return inherited::TraverseDecl(D); return true; @@ -175,25 +215,26 @@ namespace { return true; bool WasInLambda = InLambda; - InLambda = true; + unsigned OldDepthLimit = DepthLimit; - // If any capture names a function parameter pack, that pack is expanded - // when the lambda is expanded. - for (LambdaExpr::capture_iterator I = Lambda->capture_begin(), - E = Lambda->capture_end(); - I != E; ++I) { - if (I->capturesVariable()) { - VarDecl *VD = I->getCapturedVar(); - if (VD->isParameterPack()) - Unexpanded.push_back(std::make_pair(VD, I->getLocation())); - } - } + InLambda = true; + if (auto *TPL = Lambda->getTemplateParameterList()) + DepthLimit = TPL->getDepth(); inherited::TraverseLambdaExpr(Lambda); InLambda = WasInLambda; + DepthLimit = OldDepthLimit; return true; } + + /// Suppress traversal within pack expansions in lambda captures. + bool TraverseLambdaCapture(LambdaExpr *Lambda, const LambdaCapture *C, + Expr *Init) { + if (C->isPackExpansion()) + return true; + return inherited::TraverseLambdaCapture(Lambda, C, Init); + } }; } @@ -220,13 +261,33 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, if (Unexpanded.empty()) return false; - // If we are within a lambda expression, that lambda contains an unexpanded + // If we are within a lambda expression and referencing a pack that is not + // a parameter of the lambda itself, that lambda contains an unexpanded // parameter pack, and we are done. // FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it // later. + SmallVector GenericLambdaParamReferences; for (unsigned N = FunctionScopes.size(); N; --N) { if (sema::LambdaScopeInfo *LSI = dyn_cast(FunctionScopes[N-1])) { + if (LSI->isGenericLambda()) { + for (auto &Param : Unexpanded) { + auto *PD = dyn_cast_or_null( + Param.first.dyn_cast()); + if (PD && PD->getDeclContext() == LSI->CallOperator) + GenericLambdaParamReferences.push_back(Param); + } + } + + // If we have references to a parameter of a generic lambda, only + // diagnose those ones. We don't know whether any other unexpanded + // parameters referenced herein are actually unexpanded; they might + // be expanded at an outer level. + if (!GenericLambdaParamReferences.empty()) { + Unexpanded = GenericLambdaParamReferences; + break; + } + LSI->ContainsUnexpandedParameterPack = true; return false; } @@ -520,19 +581,6 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc, PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, NumExpansions); } -/// \brief Retrieve the depth and index of a parameter pack. -static std::pair -getDepthAndIndex(NamedDecl *ND) { - if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) - return std::make_pair(TTP->getDepth(), TTP->getIndex()); - - if (NonTypeTemplateParmDecl *NTTP = dyn_cast(ND)) - return std::make_pair(NTTP->getDepth(), NTTP->getIndex()); - - TemplateTemplateParmDecl *TTP = cast(ND); - return std::make_pair(TTP->getDepth(), TTP->getIndex()); -} - bool Sema::CheckParameterPacksForExpansion( SourceLocation EllipsisLoc, SourceRange PatternRange, ArrayRef Unexpanded, diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp index b0b86e387721..b8022d209122 100644 --- a/clang/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp @@ -98,3 +98,27 @@ namespace variadic_expansion { } #endif +namespace PR33082 { + template void a() { + int arr[] = { [](auto ...K) { (void)I; } ... }; // expected-error {{no viable conversion}} expected-note {{candidate}} + } + + template struct Pack {}; + template void b(Pack, T ...t) { + int arr[] = {[t...]() { // expected-error 2{{cannot initialize an array element of type 'int' with}} + U u; + return u; + }()...}; + } + + void c() { + int arr[] = {[](auto... v) { + v; // expected-error {{unexpanded parameter pack 'v'}} + }...}; // expected-error {{pack expansion does not contain any unexpanded parameter packs}} + } + + void run() { + a<1>(); // expected-note {{instantiation of}} + b(Pack(), 1, 2, 3); // expected-note {{instantiation of}} + } +}