From 612409ece080e814f79e06772c690d603f45fbd6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 25 Jul 2012 03:56:55 +0000 Subject: [PATCH] PR12057: Allow variadic template pack expansions to cross lambda boundaries. Rather than adding a ContainsUnexpandedParameterPack bit to essentially every AST node, we tunnel the bit directly up to the surrounding lambda expression when we reach a context where an unexpanded pack can not normally appear. Thus any statement or declaration within a lambda can now potentially contain an unexpanded parameter pack. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160705 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ExprCXX.h | 6 +- include/clang/Sema/ScopeInfo.h | 5 +- include/clang/Sema/Sema.h | 4 +- include/clang/Sema/Template.h | 6 +- lib/AST/ExprCXX.cpp | 10 ++- lib/Sema/SemaLambda.cpp | 28 +++--- lib/Sema/SemaTemplateInstantiate.cpp | 13 +++ lib/Sema/SemaTemplateVariadic.cpp | 89 +++++++++++++------ lib/Sema/TreeTransform.h | 18 ++-- .../expr/expr.prim/expr.prim.lambda/blocks.mm | 19 +++- .../expr/expr.prim/expr.prim.lambda/p23.cpp | 4 +- test/SemaCXX/lambda-expressions.cpp | 76 ++++++++++++++++ 12 files changed, 216 insertions(+), 62 deletions(-) diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 1e2e7af433..7a1ca21e92 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1254,7 +1254,8 @@ private: ArrayRef CaptureInits, ArrayRef ArrayIndexVars, ArrayRef ArrayIndexStarts, - SourceLocation ClosingBrace); + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack); /// \brief Construct an empty lambda expression. LambdaExpr(EmptyShell Empty, unsigned NumCaptures, bool HasArrayIndexVars) @@ -1292,7 +1293,8 @@ public: ArrayRef CaptureInits, ArrayRef ArrayIndexVars, ArrayRef ArrayIndexStarts, - SourceLocation ClosingBrace); + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack); /// \brief Construct a new lambda expression that will be deserialized from /// an external source. diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 97c76a584f..37d7cae56b 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -344,6 +344,9 @@ public: /// \brief Whether any of the capture expressions requires cleanups. bool ExprNeedsCleanups; + /// \brief Whether the lambda contains an unexpanded parameter pack. + bool ContainsUnexpandedParameterPack; + /// \brief Variables used to index into by-copy array captures. llvm::SmallVector ArrayIndexVars; @@ -355,7 +358,7 @@ public: CXXMethodDecl *CallOperator) : CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda), CallOperator(CallOperator), NumExplicitCaptures(0), Mutable(false), - ExprNeedsCleanups(false) + ExprNeedsCleanups(false), ContainsUnexpandedParameterPack(false) { Kind = SK_Lambda; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a3be78c4df..e5b3b5a012 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5003,7 +5003,9 @@ public: /// parameter packs. /// /// \param Unexpanded the set of unexpanded parameter packs. - void DiagnoseUnexpandedParameterPacks(SourceLocation Loc, + /// + /// \returns true if an error occurred, false otherwise. + bool DiagnoseUnexpandedParameterPacks(SourceLocation Loc, UnexpandedParameterPackContext UPPC, ArrayRef Unexpanded); diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 87d71b8995..273374dfd8 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -372,8 +372,10 @@ namespace clang { public: TemplateDeclInstantiator(Sema &SemaRef, DeclContext *Owner, const MultiLevelTemplateArgumentList &TemplateArgs) - : SemaRef(SemaRef), SubstIndex(SemaRef, -1), Owner(Owner), - TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0) { } + : SemaRef(SemaRef), + SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex), + Owner(Owner), TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0) + { } // FIXME: Once we get closer to completion, replace these manually-written // declarations with automatically-generated ones from diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 73347b2e01..fc52649b9c 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -790,10 +790,11 @@ LambdaExpr::LambdaExpr(QualType T, ArrayRef CaptureInits, ArrayRef ArrayIndexVars, ArrayRef ArrayIndexStarts, - SourceLocation ClosingBrace) + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) : Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(), T->isDependentType(), T->isDependentType(), - /*ContainsUnexpandedParameterPack=*/false), + ContainsUnexpandedParameterPack), IntroducerRange(IntroducerRange), NumCaptures(Captures.size()), CaptureDefault(CaptureDefault), @@ -850,7 +851,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, ArrayRef CaptureInits, ArrayRef ArrayIndexVars, ArrayRef ArrayIndexStarts, - SourceLocation ClosingBrace) { + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) { // Determine the type of the expression (i.e., the type of the // function object we're creating). QualType T = Context.getTypeDeclType(Class); @@ -863,7 +865,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault, Captures, ExplicitParams, ExplicitResultType, CaptureInits, ArrayIndexVars, ArrayIndexStarts, - ClosingBrace); + ClosingBrace, ContainsUnexpandedParameterPack); } LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures, diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 25d27f44ee..3a10c2af91 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -375,6 +375,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, TypeSourceInfo *MethodTyInfo; bool ExplicitParams = true; bool ExplicitResultType = true; + bool ContainsUnexpandedParameterPack = false; SourceLocation EndLoc; llvm::ArrayRef Params; if (ParamInfo.getNumTypeObjects() == 0) { @@ -416,21 +417,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Proto.getNumArgs()); // Check for unexpanded parameter packs in the method type. - // FIXME: We should allow unexpanded parameter packs here, but that would, - // in turn, make the lambda expression contain unexpanded parameter packs. - if (DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo, - UPPC_Lambda)) { - // Drop the parameters. - Params = llvm::ArrayRef(); - FunctionProtoType::ExtProtoInfo EPI; - EPI.HasTrailingReturn = false; - EPI.TypeQuals |= DeclSpec::TQ_const; - QualType MethodTy = Context.getFunctionType(Context.DependentTy, - /*Args=*/0, /*NumArgs=*/0, EPI); - MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy); - ExplicitParams = false; - ExplicitResultType = false; - } + if (MethodTyInfo->getType()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; } CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, @@ -571,8 +559,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // Just ignore the ellipsis. } } else if (Var->isParameterPack()) { - Diag(C->Loc, diag::err_lambda_unexpanded_pack); - continue; + ContainsUnexpandedParameterPack = true; } TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef : @@ -581,6 +568,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, } finishLambdaExplicitCaptures(LSI); + LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; + // Add lambda parameters into scope. addLambdaParameters(Method, CurScope); @@ -743,6 +732,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, bool ExplicitParams; bool ExplicitResultType; bool LambdaExprNeedsCleanups; + bool ContainsUnexpandedParameterPack; llvm::SmallVector ArrayIndexVars; llvm::SmallVector ArrayIndexStarts; { @@ -753,6 +743,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, ExplicitParams = LSI->ExplicitParams; ExplicitResultType = !LSI->HasImplicitReturnType; LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups; + ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack; ArrayIndexVars.swap(LSI->ArrayIndexVars); ArrayIndexStarts.swap(LSI->ArrayIndexStarts); @@ -867,7 +858,8 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, CaptureDefault, Captures, ExplicitParams, ExplicitResultType, CaptureInits, ArrayIndexVars, - ArrayIndexStarts, Body->getLocEnd()); + ArrayIndexStarts, Body->getLocEnd(), + ContainsUnexpandedParameterPack); // C++11 [expr.prim.lambda]p2: // A lambda-expression shall not appear in an unevaluated operand diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index d009e2f81b..082b2e847f 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -838,6 +838,19 @@ namespace { return move(Result); } + ExprResult TransformLambdaExpr(LambdaExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform::TransformLambdaExpr(E); + } + + ExprResult TransformLambdaScope(LambdaExpr *E, + CXXMethodDecl *CallOperator) { + CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(), + TSK_ImplicitInstantiation); + return TreeTransform:: + TransformLambdaScope(E, CallOperator); + } + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 0d0f992bed..aece90b785 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -12,6 +12,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/AST/Expr.h" @@ -34,10 +35,12 @@ namespace { SmallVectorImpl &Unexpanded; + bool InLambda; + public: explicit CollectUnexpandedParameterPacksVisitor( SmallVectorImpl &Unexpanded) - : Unexpanded(Unexpanded) { } + : Unexpanded(Unexpanded), InLambda(false) { } bool shouldWalkTypesOfTypeLocs() const { return false; } @@ -107,17 +110,17 @@ namespace { /// \brief Suppress traversal into statements and expressions that /// do not contain unexpanded parameter packs. bool TraverseStmt(Stmt *S) { - if (Expr *E = dyn_cast_or_null(S)) - if (E->containsUnexpandedParameterPack()) - return inherited::TraverseStmt(E); + Expr *E = dyn_cast_or_null(S); + if ((E && E->containsUnexpandedParameterPack()) || InLambda) + return inherited::TraverseStmt(S); - return true; + return true; } /// \brief Suppress traversal into types that do not contain /// unexpanded parameter packs. bool TraverseType(QualType T) { - if (!T.isNull() && T->containsUnexpandedParameterPack()) + if ((!T.isNull() && T->containsUnexpandedParameterPack()) || InLambda) return inherited::TraverseType(T); return true; @@ -126,8 +129,9 @@ namespace { /// \brief Suppress traversel into types with location information /// that do not contain unexpanded parameter packs. bool TraverseTypeLoc(TypeLoc TL) { - if (!TL.getType().isNull() && - TL.getType()->containsUnexpandedParameterPack()) + if ((!TL.getType().isNull() && + TL.getType()->containsUnexpandedParameterPack()) || + InLambda) return inherited::TraverseTypeLoc(TL); return true; @@ -136,10 +140,10 @@ namespace { /// \brief Suppress traversal of non-parameter declarations, since /// they cannot contain unexpanded parameter packs. bool TraverseDecl(Decl *D) { - if (D && isa(D)) + if ((D && isa(D)) || InLambda) return inherited::TraverseDecl(D); - return true; + return true; } /// \brief Suppress traversal of template argument pack expansions. @@ -157,17 +161,57 @@ namespace { return inherited::TraverseTemplateArgumentLoc(ArgLoc); } + + /// \brief Note whether we're traversing a lambda containing an unexpanded + /// parameter pack. In this case, the unexpanded pack can occur anywhere, + /// including all the places where we normally wouldn't look. Within a + /// lambda, we don't propagate the 'contains unexpanded parameter pack' bit + /// outside an expression. + bool TraverseLambdaExpr(LambdaExpr *Lambda) { + // The ContainsUnexpandedParameterPack bit on a lambda is always correct, + // even if it's contained within another lambda. + if (!Lambda->containsUnexpandedParameterPack()) + return true; + + bool WasInLambda = InLambda; + InLambda = true; + + // 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 (VarDecl *VD = I->getCapturedVar()) + if (VD->isParameterPack()) + Unexpanded.push_back(std::make_pair(VD, I->getLocation())); + + inherited::TraverseLambdaExpr(Lambda); + + InLambda = WasInLambda; + return true; + } }; } /// \brief Diagnose all of the unexpanded parameter packs in the given /// vector. -void +bool Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, UnexpandedParameterPackContext UPPC, ArrayRef Unexpanded) { if (Unexpanded.empty()) - return; + return false; + + // If we are within a lambda expression, 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. + for (unsigned N = FunctionScopes.size(); N; --N) { + if (sema::LambdaScopeInfo *LSI = + dyn_cast(FunctionScopes[N-1])) { + LSI->ContainsUnexpandedParameterPack = true; + return false; + } + } SmallVector Locations; SmallVector Names; @@ -200,6 +244,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, for (unsigned I = 0, N = Locations.size(); I != N; ++I) DB << SourceRange(Locations[I]); + return true; } bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, @@ -215,8 +260,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc( T->getTypeLoc()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(Expr *E, @@ -230,8 +274,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E, SmallVector Unexpanded; CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS, @@ -247,9 +290,8 @@ bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseNestedNameSpecifier(SS.getScopeRep()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(), - UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(), + UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo, @@ -284,8 +326,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseType(NameInfo.getName().getCXXNameType()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, @@ -299,8 +340,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseTemplateName(Template); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg, @@ -313,8 +353,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseTemplateArgumentLoc(Arg); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded); } void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg, diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index fff3b3f4bf..0dd9d9c191 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -571,6 +571,9 @@ public: StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E); + /// \brief Transform the captures and body of a lambda expression. + ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator); + #define STMT(Node, Parent) \ StmtResult Transform##Node(Node *S); #define EXPR(Node, Parent) \ @@ -7894,14 +7897,13 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { return ExprError(); // Transform lambda parameters. - bool Invalid = false; llvm::SmallVector ParamTypes; llvm::SmallVector Params; if (getDerived().TransformFunctionTypeParams(E->getLocStart(), E->getCallOperator()->param_begin(), E->getCallOperator()->param_size(), 0, ParamTypes, &Params)) - Invalid = true; + return ExprError(); // Build the call operator. CXXMethodDecl *CallOperator @@ -7910,11 +7912,14 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { E->getCallOperator()->getLocEnd(), Params); getDerived().transformAttrs(E->getCallOperator(), CallOperator); - - // FIXME: Instantiation-specific. - CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(), - TSK_ImplicitInstantiation); + return getDerived().TransformLambdaScope(E, CallOperator); +} + +template +ExprResult +TreeTransform::TransformLambdaScope(LambdaExpr *E, + CXXMethodDecl *CallOperator) { // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), CallOperator); @@ -7927,6 +7932,7 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { E->isMutable()); // Transform captures. + bool Invalid = false; bool FinishedExplicitCaptures = false; for (LambdaExpr::capture_iterator C = E->capture_begin(), CEnd = E->capture_end(); diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm index 6a6e0d9c90..0db2bf5646 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm @@ -89,6 +89,8 @@ namespace overloading { namespace PR13117 { struct A { + template static void f(Args...); + template static void f1() { (void)^(Args args) { // expected-error{{block contains unexpanded parameter pack 'Args'}} @@ -97,9 +99,24 @@ namespace PR13117 { template static void f2() { - (void)[](Args args) { // expected-error{{lambda contains unexpanded parameter pack 'Args'}} + // FIXME: Allow this. + f( + ^(Args args) // expected-error{{block contains unexpanded parameter pack 'Args'}} + { } + ... // expected-error{{pack expansion does not contain any unexpanded parameter packs}} + ); + } + + template static void f3() + { + (void)[](Args args) { // expected-error{{expression contains unexpanded parameter pack 'Args'}} }; } + + template static void f4() + { + f([](Args args) { } ...); + } }; void g() { diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp index 174db257c8..82fc04a48f 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp @@ -9,8 +9,8 @@ void print(T first, Ts... rest) { } template -void unsupported(Ts ...values) { - auto unsup = [values] {}; // expected-error{{unexpanded function parameter pack capture is unsupported}} +void unexpanded_capture(Ts ...values) { + auto unexp = [values] {}; // expected-error{{initializer contains unexpanded parameter pack 'values'}} } template diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index f5eee6a529..198f8cf1fe 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -145,3 +145,79 @@ namespace ModifyingCapture { }; } } + +namespace VariadicPackExpansion { + template using Fst = T; + template bool g(Fst ...bools); + template bool f(Ts &&...ts) { + return g([&ts] { + if (!ts) + return false; + --ts; + return true; + } () ...); + } + void h() { + int a = 5, b = 2, c = 3; + while (f(a, b, c)) { + } + } + + struct sink { + template sink(Ts &&...) {} + }; + + template void local_class() { + sink { + [] (Ts t) { + struct S : Ts { + void f(Ts t) { + Ts &that = *this; + that = t; + } + Ts g() { return *this; }; + }; + S s; + s.f(t); + return s; + } (Ts()).g() ... + }; + }; + struct X {}; struct Y {}; + template void local_class(); + + template void nested(Ts ...ts) { + f( + // Each expansion of this lambda implicitly captures all of 'ts', because + // the inner lambda also expands 'ts'. + [&] { + return ts + [&] { return f(ts...); } (); + } () ... + ); + } + template void nested(int, int, int); + + template void nested2(Ts ...ts) { // expected-note 2{{here}} + // Capture all 'ts', use only one. + f([&ts...] { return ts; } ()...); + // Capture each 'ts', use it. + f([&ts] { return ts; } ()...); + // Capture all 'ts', use all of them. + f([&ts...] { return (int)f(ts...); } ()); + // Capture each 'ts', use all of them. Ill-formed. In more detail: + // + // We instantiate two lambdas here; the first captures ts$0, the second + // captures ts$1. Both of them reference both ts parameters, so both are + // ill-formed because ts can't be implicitly captured. + // + // FIXME: This diagnostic does not explain what's happening. We should + // specify which 'ts' we're referring to in its diagnostic name. We should + // also say which slice of the pack expansion is being performed in the + // instantiation backtrace. + f([&ts] { return (int)f(ts...); } ()...); // \ + // expected-error 2{{'ts' cannot be implicitly captured}} \ + // expected-note 2{{lambda expression begins here}} + } + template void nested2(int); // ok + template void nested2(int, int); // expected-note {{in instantiation of}} +} -- 2.40.0