From 3e308b1fbabe8f0d0a0cec1d99d53d4d8f14b316 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 14 Feb 2012 19:27:52 +0000 Subject: [PATCH] Implement support for lambda capture pack expansions, e.g., [&values...] { print(values...); } llvm-svn: 150497 --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/include/clang/Sema/DeclSpec.h | 13 +++-- clang/include/clang/Sema/ScopeInfo.h | 20 +++++-- clang/include/clang/Sema/Sema.h | 3 +- clang/lib/Parse/ParseExprCXX.cpp | 8 ++- clang/lib/Sema/SemaExpr.cpp | 5 +- clang/lib/Sema/SemaLambda.cpp | 22 ++++++- clang/lib/Sema/TreeTransform.h | 51 ++++++++++++++-- .../expr/expr.prim/expr.prim.lambda/p23.cpp | 58 +++++++++++++++++++ 9 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f99021200f69..513b44e7e920 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4032,7 +4032,9 @@ def err_lambda_return_init_list : Error< "cannot deduce lambda return type from initializer list">; def err_lambda_capture_default_arg : Error< "lambda expression in default argument cannot capture any entity">; - +def err_lambda_unexpanded_pack : Error< + "unexpanded function parameter pack capture is unsupported">; + def err_operator_arrow_circular : Error< "circular pointer delegation detected">; def err_pseudo_dtor_base_not_scalar : Error< diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 8abb893c6dc6..45cf8e3e6855 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1923,10 +1923,12 @@ struct LambdaCapture { LambdaCaptureKind Kind; SourceLocation Loc; IdentifierInfo* Id; - + SourceLocation EllipsisLoc; + LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc, - IdentifierInfo* Id = 0) - : Kind(Kind), Loc(Loc), Id(Id) + IdentifierInfo* Id = 0, + SourceLocation EllipsisLoc = SourceLocation()) + : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc) {} }; @@ -1943,8 +1945,9 @@ struct LambdaIntroducer { /// addCapture - Append a capture in a lambda introducer. void addCapture(LambdaCaptureKind Kind, SourceLocation Loc, - IdentifierInfo* Id = 0) { - Captures.push_back(LambdaCapture(Kind, Loc, Id)); + IdentifierInfo* Id = 0, + SourceLocation EllipsisLoc = SourceLocation()) { + Captures.push_back(LambdaCapture(Kind, Loc, Id, EllipsisLoc)); } }; diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 57a49e6114e3..46b97e7113c8 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -151,16 +151,19 @@ public: /// \brief The source location at which the first capture occurred.. SourceLocation Loc; + /// \brief The location of the ellipsis that expands a parameter pack. + SourceLocation EllipsisLoc; + public: Capture(VarDecl *Var, bool block, bool byRef, bool isNested, - SourceLocation Loc, Expr *Cpy) + SourceLocation Loc, SourceLocation EllipsisLoc, Expr *Cpy) : VarAndKind(Var, block ? Cap_Block : byRef ? Cap_ByRef : Cap_ByCopy), - CopyExprAndNested(Cpy, isNested) {} + CopyExprAndNested(Cpy, isNested), Loc(Loc), EllipsisLoc(EllipsisLoc) {} enum IsThisCapture { ThisCapture }; Capture(IsThisCapture, bool isNested, SourceLocation Loc, Expr *Cpy) - : VarAndKind(0, Cap_This), CopyExprAndNested(Cpy, isNested), Loc(Loc) { - } + : VarAndKind(0, Cap_This), CopyExprAndNested(Cpy, isNested), Loc(Loc), + EllipsisLoc() { } bool isThisCapture() const { return VarAndKind.getInt() == Cap_This; } bool isVariableCapture() const { return !isThisCapture(); } @@ -176,6 +179,10 @@ public: /// \brief Retrieve the location at which this variable was captured. SourceLocation getLocation() const { return Loc; } + /// \brief Retrieve the source location of the ellipsis, whose presence + /// indicates that the capture is a pack expansion. + SourceLocation getEllipsisLoc() const { return EllipsisLoc; } + Expr *getCopyExpr() const { return CopyExprAndNested.getPointer(); } @@ -205,8 +212,9 @@ public: QualType ReturnType; void AddCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested, - SourceLocation Loc, Expr *Cpy) { - Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc, Cpy)); + SourceLocation Loc, SourceLocation EllipsisLoc, Expr *Cpy) { + Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc, + EllipsisLoc, Cpy)); CaptureMap[Var] = Captures.size(); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6014fefe1b6f..47fa85b9820d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2298,7 +2298,8 @@ public: TryCapture_Implicit, TryCapture_ExplicitByVal, TryCapture_ExplicitByRef }; void TryCaptureVar(VarDecl *var, SourceLocation loc, - TryCaptureKind Kind = TryCapture_Implicit); + TryCaptureKind Kind = TryCapture_Implicit, + SourceLocation EllipsisLoc = SourceLocation()); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index fe212f4ebcf6..380237082b48 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -651,7 +651,8 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) LambdaCaptureKind Kind = LCK_ByCopy; SourceLocation Loc; IdentifierInfo* Id = 0; - + SourceLocation EllipsisLoc; + if (Tok.is(tok::kw_this)) { Kind = LCK_This; Loc = ConsumeToken(); @@ -664,6 +665,9 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) if (Tok.is(tok::identifier)) { Id = Tok.getIdentifierInfo(); Loc = ConsumeToken(); + + if (Tok.is(tok::ellipsis)) + EllipsisLoc = ConsumeToken(); } else if (Tok.is(tok::kw_this)) { // FIXME: If we want to suggest a fixit here, will need to return more // than just DiagnosticID. Perhaps full DiagnosticBuilder that can be @@ -674,7 +678,7 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) } } - Intro.addCapture(Kind, Loc, Id); + Intro.addCapture(Kind, Loc, Id, EllipsisLoc); } T.consumeClose(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4dc8a7bedd28..371a4e39ebd8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9831,7 +9831,7 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit, // Check if the variable needs to be captured; if so, try to perform // the capture. void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, - TryCaptureKind Kind) { + TryCaptureKind Kind, SourceLocation EllipsisLoc) { QualType type; unsigned functionScopesIndex; bool Nested; @@ -9909,7 +9909,8 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, } } - CSI->AddCapture(var, hasBlocksAttr, byRef, Nested, loc, copyExpr); + CSI->AddCapture(var, hasBlocksAttr, byRef, Nested, loc, EllipsisLoc, + copyExpr); Nested = true; if (shouldAddConstForScope(CSI, var)) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 0ce1c741638a..c75c3c5b7d7c 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -291,9 +291,26 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, continue; } + // C++11 [expr.prim.lambda]p23: + // A capture followed by an ellipsis is a pack expansion (14.5.3). + SourceLocation EllipsisLoc; + if (C->EllipsisLoc.isValid()) { + if (Var->isParameterPack()) { + EllipsisLoc = C->EllipsisLoc; + } else { + Diag(C->EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) + << SourceRange(C->Loc); + + // Just ignore the ellipsis. + } + } else if (Var->isParameterPack()) { + Diag(C->Loc, diag::err_lambda_unexpanded_pack); + continue; + } + TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef : TryCapture_ExplicitByVal; - TryCaptureVar(Var, C->Loc, Kind); + TryCaptureVar(Var, C->Loc, Kind, EllipsisLoc); } finishLambdaExplicitCaptures(LSI); @@ -380,10 +397,9 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, } VarDecl *Var = From.getVariable(); - // FIXME: Handle pack expansions. LambdaCaptureKind Kind = From.isCopyCapture()? LCK_ByCopy : LCK_ByRef; Captures.push_back(LambdaExpr::Capture(From.getLocation(), IsImplicit, - Kind, Var)); + Kind, Var, From.getEllipsisLoc())); CaptureInits.push_back(From.getCopyExpr()); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a63b9c804ac0..71037b6b3e39 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7706,6 +7706,49 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { continue; } + // Determine the capture kind for Sema. + Sema::TryCaptureKind Kind + = C->isImplicit()? Sema::TryCapture_Implicit + : C->getCaptureKind() == LCK_ByCopy + ? Sema::TryCapture_ExplicitByVal + : Sema::TryCapture_ExplicitByRef; + SourceLocation EllipsisLoc; + if (C->isPackExpansion()) { + UnexpandedParameterPack Unexpanded(C->getCapturedVar(), C->getLocation()); + bool ShouldExpand = false; + bool RetainExpansion = false; + llvm::Optional NumExpansions; + if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(), + C->getLocation(), + Unexpanded, + ShouldExpand, RetainExpansion, + NumExpansions)) + return ExprError(); + + if (ShouldExpand) { + // The transform has determined that we should perform an expansion; + // transform and capture each of the arguments. + // expansion of the pattern. Do so. + VarDecl *Pack = C->getCapturedVar(); + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); + VarDecl *CapturedVar + = cast_or_null(getDerived().TransformDecl(C->getLocation(), + Pack)); + if (!CapturedVar) { + Invalid = true; + continue; + } + + // Capture the transformed variable. + getSema().TryCaptureVar(CapturedVar, C->getLocation(), Kind); + } + continue; + } + + EllipsisLoc = C->getEllipsisLoc(); + } + // Transform the captured variable. VarDecl *CapturedVar = cast_or_null(getDerived().TransformDecl(C->getLocation(), @@ -7714,13 +7757,9 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { Invalid = true; continue; } - + // Capture the transformed variable. - getSema().TryCaptureVar(CapturedVar, C->getLocation(), - C->isImplicit()? Sema::TryCapture_Implicit - : C->getCaptureKind() == LCK_ByCopy - ? Sema::TryCapture_ExplicitByVal - : Sema::TryCapture_ExplicitByRef); + getSema().TryCaptureVar(CapturedVar, C->getLocation(), Kind); } if (!FinishedExplicitCaptures) getSema().finishLambdaExplicitCaptures(LSI); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp new file mode 100644 index 000000000000..174db257c891 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify + +void print(); + +template +void print(T first, Ts... rest) { + (void)first; + print(rest...); +} + +template +void unsupported(Ts ...values) { + auto unsup = [values] {}; // expected-error{{unexpanded function parameter pack capture is unsupported}} +} + +template +void implicit_capture(Ts ...values) { + auto implicit = [&] { print(values...); }; + implicit(); +} + +template +void do_print(Ts... values) { + auto bycopy = [values...]() { print(values...); }; + bycopy(); + auto byref = [&values...]() { print(values...); }; + byref(); + + auto bycopy2 = [=]() { print(values...); }; + bycopy2(); + auto byref2 = [&]() { print(values...); }; + byref2(); +} + +template void do_print(int, float, double); + +template +void bogus_expansions(T x) { + auto l1 = [x...] {}; // expected-error{{pack expansion does not contain any unexpanded parameter packs}} + auto l2 = [Values...] {}; // expected-error{{'Values' in capture list does not name a variable}} +} + +void g(int*, float*, double*); + +template +void std_example(Args... args) { + auto lm = [&, args...] { return g(args...); }; +}; + +template void std_example(int*, float*, double*); + +template +void variadic_lambda(Args... args) { + auto lambda = [](Args... inner_args) { return g(inner_args...); }; + lambda(args...); +} + +template void variadic_lambda(int*, float*, double*); -- GitLab