From: Faisal Vali Date: Thu, 5 Dec 2013 01:40:41 +0000 (+0000) Subject: Fix init-captures for generic lambdas. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cedc8af40e36c81d08b06652e75449a1b15909f2;p=clang Fix init-captures for generic lambdas. For an init capture, process the initialization expression right away. For lambda init-captures such as the following: const int x = 10; auto L = [i = x+1](int a) { return [j = x+2, &k = x](char b) { }; }; keep in mind that each lambda init-capture has to have: - its initialization expression executed in the context of the enclosing/parent decl-context. - but the variable itself has to be 'injected' into the decl-context of its lambda's call-operator (which has not yet been created). Each init-expression is a full-expression that has to get Sema-analyzed (for capturing etc.) before its lambda's call-operator's decl-context, scope & scopeinfo are pushed on their respective stacks. Thus if any variable is odr-used in the init-capture it will correctly get captured in the enclosing lambda, if one exists. The init-variables above are created later once the lambdascope and call-operators decl-context is pushed onto its respective stack. Since the lambda init-capture's initializer expression occurs in the context of the enclosing function or lambda, therefore we can not wait till a lambda scope has been pushed on before deciding whether the variable needs to be captured. We also need to process all lvalue-to-rvalue conversions and discarded-value conversions, so that we can avoid capturing certain constant variables. For e.g., void test() { const int x = 10; auto L = [&z = x](char a) { <-- don't capture by the current lambda return [y = x](int i) { <-- don't capture by enclosing lambda return y; } }; If x was not const, the second use would require 'L' to capture, and that would be an error. Make sure TranformLambdaExpr is also aware of this. Patch approved by Richard (Thanks!!) http://llvm-reviews.chandlerc.com/D2092 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196454 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index f197dd4fd0..8f6bd186a7 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -2166,12 +2166,13 @@ struct LambdaCapture { IdentifierInfo *Id; SourceLocation EllipsisLoc; ExprResult Init; - + ParsedType InitCaptureType; LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc, - IdentifierInfo* Id = 0, - SourceLocation EllipsisLoc = SourceLocation(), - ExprResult Init = ExprResult()) - : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc), Init(Init) + IdentifierInfo* Id, + SourceLocation EllipsisLoc, + ExprResult Init, ParsedType InitCaptureType) + : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc), Init(Init), + InitCaptureType(InitCaptureType) {} }; @@ -2188,10 +2189,12 @@ struct LambdaIntroducer { /// \brief Append a capture in a lambda introducer. void addCapture(LambdaCaptureKind Kind, SourceLocation Loc, - IdentifierInfo* Id = 0, - SourceLocation EllipsisLoc = SourceLocation(), - ExprResult Init = ExprResult()) { - Captures.push_back(LambdaCapture(Kind, Loc, Id, EllipsisLoc, Init)); + IdentifierInfo* Id, + SourceLocation EllipsisLoc, + ExprResult Init, + ParsedType InitCaptureType) { + Captures.push_back(LambdaCapture(Kind, Loc, Id, EllipsisLoc, Init, + InitCaptureType)); } }; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 3a1e78dd12..83fb2be5f1 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -116,8 +116,8 @@ private: }; struct C { - /// \brief The variable being captured by an EK_LambdaCapture. - VarDecl *Var; + /// \brief The name of the variable being captured by an EK_LambdaCapture. + IdentifierInfo *VarID; /// \brief The source location at which the capture occurs. unsigned Location; @@ -183,10 +183,10 @@ private: const InitializedEntity &Parent); /// \brief Create the initialization entity for a lambda capture. - InitializedEntity(VarDecl *Var, FieldDecl *Field, SourceLocation Loc) - : Kind(EK_LambdaCapture), Parent(0), Type(Field->getType()) + InitializedEntity(IdentifierInfo *VarID, QualType FieldType, SourceLocation Loc) + : Kind(EK_LambdaCapture), Parent(0), Type(FieldType) { - Capture.Var = Var; + Capture.VarID = VarID; Capture.Location = Loc.getRawEncoding(); } @@ -309,10 +309,10 @@ public: } /// \brief Create the initialization entity for a lambda capture. - static InitializedEntity InitializeLambdaCapture(VarDecl *Var, - FieldDecl *Field, + static InitializedEntity InitializeLambdaCapture(IdentifierInfo *VarID, + QualType FieldType, SourceLocation Loc) { - return InitializedEntity(Var, Field, Loc); + return InitializedEntity(VarID, FieldType, Loc); } /// \brief Create the entity for a compound literal initializer. @@ -402,13 +402,11 @@ public: getKind() == EK_ComplexElement); this->Index = Index; } - - /// \brief Retrieve the variable for a captured variable in a lambda. - VarDecl *getCapturedVar() const { + /// \brief For a lambda capture, return the capture's name. + StringRef getCapturedVarName() const { assert(getKind() == EK_LambdaCapture && "Not a lambda capture!"); - return Capture.Var; + return Capture.VarID->getName(); } - /// \brief Determine the location of the capture when initializing /// field from a captured variable in a lambda. SourceLocation getCaptureLoc() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1ad3194a79..2fc6a4dc1e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -281,6 +281,12 @@ public: /// element type here is ExprWithCleanups::Object. SmallVector ExprCleanupObjects; + /// \brief Store a list of either DeclRefExprs or MemberExprs + /// that contain a reference to a variable (constant) that may or may not + /// be odr-used in this Expr, and we won't know until all lvalue-to-rvalue + /// and discarded value conversions have been applied to all subexpressions + /// of the enclosing full expression. This is cleared at the end of each + /// full expression. llvm::SmallPtrSet MaybeODRUseExprs; /// \brief Stack containing information about each of the nested @@ -4316,7 +4322,8 @@ public: } ExprResult ActOnFinishFullExpr(Expr *Expr, SourceLocation CC, bool DiscardedValue = false, - bool IsConstexpr = false); + bool IsConstexpr = false, + bool IsLambdaInitCaptureInitializer = false); StmtResult ActOnFinishFullStmt(Stmt *Stmt); // Marks SS invalid if it represents an incomplete type. @@ -4511,10 +4518,18 @@ public: bool ExplicitResultType, bool Mutable); - /// \brief Check an init-capture and build the implied variable declaration - /// with the specified name and initializer. - VarDecl *checkInitCapture(SourceLocation Loc, bool ByRef, - IdentifierInfo *Id, Expr *Init); + /// \brief Perform initialization analysis of the init-capture and perform + /// any implicit conversions such as an lvalue-to-rvalue conversion if + /// not being used to initialize a reference. + QualType performLambdaInitCaptureInitialization(SourceLocation Loc, + bool ByRef, IdentifierInfo *Id, Expr *&Init); + /// \brief Create a dummy variable within the declcontext of the lambda's + /// call operator, for name lookup purposes for a lambda init capture. + /// + /// CodeGen handles emission of lambda captures, ignoring these dummy + /// variables appropriately. + VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc, + QualType InitCaptureType, IdentifierInfo *Id, Expr *Init); /// \brief Build the implicit field for an init-capture. FieldDecl *buildInitCaptureField(sema::LambdaScopeInfo *LSI, VarDecl *Var); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 9489b4787e..3f6d36c8b8 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -637,8 +637,7 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) { ExprResult Parser::ParseLambdaExpression() { // Parse lambda-introducer. LambdaIntroducer Intro; - - Optional DiagID(ParseLambdaIntroducer(Intro)); + Optional DiagID = ParseLambdaIntroducer(Intro); if (DiagID) { Diag(Tok, DiagID.getValue()); SkipUntil(tok::r_square, StopAtSemi); @@ -678,7 +677,7 @@ ExprResult Parser::TryParseLambdaExpression() { if (Next.is(tok::identifier) && After.is(tok::identifier)) { return ExprEmpty(); } - + // Here, we're stuck: lambda introducers and Objective-C message sends are // unambiguous, but it requires arbitrary lookhead. [a,b,c,d,e,f,g] is a // lambda, and [a,b,c,d,e,f,g h] is a Objective-C message send. Instead of @@ -688,6 +687,7 @@ ExprResult Parser::TryParseLambdaExpression() { LambdaIntroducer Intro; if (TryParseLambdaIntroducer(Intro)) return ExprEmpty(); + return ParseLambdaExpressionAfterIntroducer(Intro); } @@ -813,6 +813,11 @@ Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, Exprs); } } else if (Tok.is(tok::l_brace) || Tok.is(tok::equal)) { + // Each lambda init-capture forms its own full expression, which clears + // Actions.MaybeODRUseExprs. So create an expression evaluation context + // to save the necessary state, and restore it later. + EnterExpressionEvaluationContext EC(Actions, + Sema::PotentiallyEvaluated); if (Tok.is(tok::equal)) ConsumeToken(); @@ -866,13 +871,61 @@ Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, } else if (Tok.is(tok::ellipsis)) EllipsisLoc = ConsumeToken(); } - - Intro.addCapture(Kind, Loc, Id, EllipsisLoc, Init); + // If this is an init capture, process the initialization expression + // right away. For lambda init-captures such as the following: + // const int x = 10; + // auto L = [i = x+1](int a) { + // return [j = x+2, + // &k = x](char b) { }; + // }; + // keep in mind that each lambda init-capture has to have: + // - its initialization expression executed in the context + // of the enclosing/parent decl-context. + // - but the variable itself has to be 'injected' into the + // decl-context of its lambda's call-operator (which has + // not yet been created). + // Each init-expression is a full-expression that has to get + // Sema-analyzed (for capturing etc.) before its lambda's + // call-operator's decl-context, scope & scopeinfo are pushed on their + // respective stacks. Thus if any variable is odr-used in the init-capture + // it will correctly get captured in the enclosing lambda, if one exists. + // The init-variables above are created later once the lambdascope and + // call-operators decl-context is pushed onto its respective stack. + + // Since the lambda init-capture's initializer expression occurs in the + // context of the enclosing function or lambda, therefore we can not wait + // till a lambda scope has been pushed on before deciding whether the + // variable needs to be captured. We also need to process all + // lvalue-to-rvalue conversions and discarded-value conversions, + // so that we can avoid capturing certain constant variables. + // For e.g., + // void test() { + // const int x = 10; + // auto L = [&z = x](char a) { <-- don't capture by the current lambda + // return [y = x](int i) { <-- don't capture by enclosing lambda + // return y; + // } + // }; + // If x was not const, the second use would require 'L' to capture, and + // that would be an error. + + ParsedType InitCaptureParsedType; + if (Init.isUsable()) { + // Get the pointer and store it in an lvalue, so we can use it as an + // out argument. + Expr *InitExpr = Init.get(); + // This performs any lvalue-to-rvalue conversions if necessary, which + // can affect what gets captured in the containing decl-context. + QualType InitCaptureType = Actions.performLambdaInitCaptureInitialization( + Loc, Kind == LCK_ByRef, Id, InitExpr); + Init = InitExpr; + InitCaptureParsedType.set(InitCaptureType); + } + Intro.addCapture(Kind, Loc, Id, EllipsisLoc, Init, InitCaptureParsedType); } T.consumeClose(); Intro.Range.setEnd(T.getCloseLocation()); - return DiagResult(); } diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 974f3b42db..61dc157f85 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -1650,9 +1650,9 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, } case InitializedEntity::EK_LambdaCapture: { - const VarDecl *Var = Entity.getCapturedVar(); + StringRef VarName = Entity.getCapturedVarName(); PD = PDiag(diag::err_access_lambda_capture); - PD << Var->getName() << Entity.getType() << getSpecialMember(Constructor); + PD << VarName << Entity.getType() << getSpecialMember(Constructor); break; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 84487cc973..00db12e22c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11692,7 +11692,8 @@ static ExprResult addAsFieldToClosureType(Sema &S, SmallVector Entities; Entities.reserve(1 + IndexVariables.size()); Entities.push_back( - InitializedEntity::InitializeLambdaCapture(Var, Field, Loc)); + InitializedEntity::InitializeLambdaCapture(Var->getIdentifier(), + Field->getType(), Loc)); for (unsigned I = 0, N = IndexVariables.size(); I != N; ++I) Entities.push_back(InitializedEntity::InitializeElement(S.Context, 0, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 6273ce19f5..95a5cbb20e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5955,13 +5955,30 @@ static void CheckLambdaCaptures(Expr *const FE, ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC, bool DiscardedValue, - bool IsConstexpr) { + bool IsConstexpr, + bool IsLambdaInitCaptureInitializer) { ExprResult FullExpr = Owned(FE); if (!FullExpr.get()) return ExprError(); - - if (DiagnoseUnexpandedParameterPack(FullExpr.get())) + + // If we are an init-expression in a lambdas init-capture, we should not + // diagnose an unexpanded pack now (will be diagnosed once lambda-expr + // containing full-expression is done). + // template void test(Ts ... t) { + // test([&a(t)]() { <-- (t) is an init-expr that shouldn't be diagnosed now. + // return a; + // }() ...); + // } + // FIXME: This is a hack. It would be better if we pushed the lambda scope + // when we parse the lambda introducer, and teach capturing (but not + // unexpanded pack detection) to walk over LambdaScopeInfos which don't have a + // corresponding class yet (that is, have LambdaScopeInfo either represent a + // lambda where we've entered the introducer but not the body, or represent a + // lambda where we've entered the body, depending on where the + // parser/instantiation has got to). + if (!IsLambdaInitCaptureInitializer && + DiagnoseUnexpandedParameterPack(FullExpr.get())) return ExprError(); // Top-level expressions default to 'id' when we're in a debugger. diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 8d78eacd93..782b930a5e 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2508,7 +2508,7 @@ DeclarationName InitializedEntity::getName() const { return VariableOrMember->getDeclName(); case EK_LambdaCapture: - return Capture.Var->getDeclName(); + return DeclarationName(Capture.VarID); case EK_Result: case EK_Exception: @@ -2610,7 +2610,7 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { case EK_BlockElement: OS << "Block"; break; case EK_LambdaCapture: OS << "LambdaCapture "; - getCapturedVar()->printName(OS); + OS << DeclarationName(Capture.VarID); break; } diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 6db37ecf1b..a7d5b65264 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -609,12 +609,20 @@ void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) { } } -VarDecl *Sema::checkInitCapture(SourceLocation Loc, bool ByRef, - IdentifierInfo *Id, Expr *Init) { - // C++1y [expr.prim.lambda]p11: - // An init-capture behaves as if it declares and explicitly captures - // a variable of the form - // "auto init-capture;" +QualType Sema::performLambdaInitCaptureInitialization(SourceLocation Loc, + bool ByRef, + IdentifierInfo *Id, + Expr *&Init) { + + // We do not need to distinguish between direct-list-initialization + // and copy-list-initialization here, because we will always deduce + // std::initializer_list, and direct- and copy-list-initialization + // always behave the same for such a type. + // FIXME: We should model whether an '=' was present. + const bool IsDirectInit = isa(Init) || isa(Init); + + // Create an 'auto' or 'auto&' TypeSourceInfo that we can use to + // deduce against. QualType DeductType = Context.getAutoDeductType(); TypeLocBuilder TLB; TLB.pushTypeSpec(DeductType).setNameLoc(Loc); @@ -625,24 +633,100 @@ VarDecl *Sema::checkInitCapture(SourceLocation Loc, bool ByRef, } TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType); + // Are we a non-list direct initialization? + ParenListExpr *CXXDirectInit = dyn_cast(Init); + + Expr *DeduceInit = Init; + // Initializer could be a C++ direct-initializer. Deduction only works if it + // contains exactly one expression. + if (CXXDirectInit) { + if (CXXDirectInit->getNumExprs() == 0) { + Diag(CXXDirectInit->getLocStart(), diag::err_init_capture_no_expression) + << DeclarationName(Id) << TSI->getType() << Loc; + return QualType(); + } else if (CXXDirectInit->getNumExprs() > 1) { + Diag(CXXDirectInit->getExpr(1)->getLocStart(), + diag::err_init_capture_multiple_expressions) + << DeclarationName(Id) << TSI->getType() << Loc; + return QualType(); + } else { + DeduceInit = CXXDirectInit->getExpr(0); + } + } + + // Now deduce against the initialization expression and store the deduced + // type below. + QualType DeducedType; + if (DeduceAutoType(TSI, DeduceInit, DeducedType) == DAR_Failed) { + if (isa(Init)) + Diag(Loc, diag::err_init_capture_deduction_failure_from_init_list) + << DeclarationName(Id) + << (DeduceInit->getType().isNull() ? TSI->getType() + : DeduceInit->getType()) + << DeduceInit->getSourceRange(); + else + Diag(Loc, diag::err_init_capture_deduction_failure) + << DeclarationName(Id) << TSI->getType() + << (DeduceInit->getType().isNull() ? TSI->getType() + : DeduceInit->getType()) + << DeduceInit->getSourceRange(); + } + if (DeducedType.isNull()) + return QualType(); + + // Perform initialization analysis and ensure any implicit conversions + // (such as lvalue-to-rvalue) are enforced. + InitializedEntity Entity = + InitializedEntity::InitializeLambdaCapture(Id, DeducedType, Loc); + InitializationKind Kind = + IsDirectInit + ? (CXXDirectInit ? InitializationKind::CreateDirect( + Loc, Init->getLocStart(), Init->getLocEnd()) + : InitializationKind::CreateDirectList(Loc)) + : InitializationKind::CreateCopy(Loc, Init->getLocStart()); + + MultiExprArg Args = Init; + if (CXXDirectInit) + Args = + MultiExprArg(CXXDirectInit->getExprs(), CXXDirectInit->getNumExprs()); + QualType DclT; + InitializationSequence InitSeq(*this, Entity, Kind, Args); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT); + + if (Result.isInvalid()) + return QualType(); + Init = Result.takeAs(); + + // The init-capture initialization is a full-expression that must be + // processed as one before we enter the declcontext of the lambda's + // call-operator. + Result = ActOnFinishFullExpr(Init, Loc, /*DiscardedValue*/ false, + /*IsConstexpr*/ false, + /*IsLambdaInitCaptureInitalizer*/ true); + if (Result.isInvalid()) + return QualType(); + + Init = Result.takeAs(); + return DeducedType; +} + +VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc, + QualType InitCaptureType, IdentifierInfo *Id, Expr *Init) { + + TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, + Loc); // Create a dummy variable representing the init-capture. This is not actually // used as a variable, and only exists as a way to name and refer to the // init-capture. // FIXME: Pass in separate source locations for '&' and identifier. VarDecl *NewVD = VarDecl::Create(Context, CurContext, Loc, - Loc, Id, TSI->getType(), TSI, SC_Auto); + Loc, Id, InitCaptureType, TSI, SC_Auto); NewVD->setInitCapture(true); NewVD->setReferenced(true); NewVD->markUsed(Context); - - // We do not need to distinguish between direct-list-initialization - // and copy-list-initialization here, because we will always deduce - // std::initializer_list, and direct- and copy-list-initialization - // always behave the same for such a type. - // FIXME: We should model whether an '=' was present. - bool DirectInit = isa(Init) || isa(Init); - AddInitializerToDecl(NewVD, Init, DirectInit, /*ContainsAuto*/true); + NewVD->setInit(Init); return NewVD; + } FieldDecl *Sema::buildInitCaptureField(LambdaScopeInfo *LSI, VarDecl *Var) { @@ -816,7 +900,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, if (C->Init.isInvalid()) continue; - VarDecl *Var; + VarDecl *Var = 0; if (C->Init.isUsable()) { Diag(C->Loc, getLangOpts().CPlusPlus1y ? diag::warn_cxx11_compat_init_capture @@ -824,9 +908,15 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, if (C->Init.get()->containsUnexpandedParameterPack()) ContainsUnexpandedParameterPack = true; - - Var = checkInitCapture(C->Loc, C->Kind == LCK_ByRef, - C->Id, C->Init.take()); + // If the initializer expression is usable, but the InitCaptureType + // is not, then an error has occurred - so ignore the capture for now. + // for e.g., [n{0}] { }; <-- if no is included. + // FIXME: we should create the init capture variable and mark it invalid + // in this case. + if (C->InitCaptureType.get().isNull()) + continue; + Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(), + C->Id, C->Init.take()); // C++1y [expr.prim.lambda]p11: // An init-capture behaves as if it declares and explicitly // captures a variable [...] whose declarative region is the diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 7dc8d2b195..53ea8c7b05 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -917,7 +917,8 @@ namespace { } ExprResult TransformLambdaScope(LambdaExpr *E, - CXXMethodDecl *NewCallOperator) { + CXXMethodDecl *NewCallOperator, + ArrayRef InitCaptureExprsAndTypes) { CXXMethodDecl *const OldCallOperator = E->getCallOperator(); // In the generic lambda case, we set the NewTemplate to be considered // an "instantiation" of the OldTemplate. @@ -936,7 +937,8 @@ namespace { NewCallOperator->setInstantiationOfMemberFunction(OldCallOperator, TSK_ImplicitInstantiation); - return inherited::TransformLambdaScope(E, NewCallOperator); + return inherited::TransformLambdaScope(E, NewCallOperator, + InitCaptureExprsAndTypes); } TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *OrigTPL) { diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index e9cb95006f..aa25cbe163 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -593,9 +593,11 @@ public: StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E); - + + typedef std::pair InitCaptureInfoTy; /// \brief Transform the captures and body of a lambda expression. - ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator); + ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator, + ArrayRef InitCaptureExprsAndTypes); TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *TPL) { @@ -8263,7 +8265,40 @@ TreeTransform::TransformCXXTemporaryObjectExpr( template ExprResult TreeTransform::TransformLambdaExpr(LambdaExpr *E) { - + + // Transform any init-capture expressions before entering the scope of the + // lambda body, because they are not semantically within that scope. + SmallVector InitCaptureExprsAndTypes; + InitCaptureExprsAndTypes.resize(E->explicit_capture_end() - + E->explicit_capture_begin()); + + for (LambdaExpr::capture_iterator C = E->capture_begin(), + CEnd = E->capture_end(); + C != CEnd; ++C) { + if (!C->isInitCapture()) + continue; + EnterExpressionEvaluationContext EEEC(getSema(), + Sema::PotentiallyEvaluated); + ExprResult NewExprInitResult = getDerived().TransformInitializer( + C->getCapturedVar()->getInit(), + C->getCapturedVar()->getInitStyle() == VarDecl::CallInit); + + if (NewExprInitResult.isInvalid()) + return ExprError(); + Expr *NewExprInit = NewExprInitResult.get(); + + VarDecl *OldVD = C->getCapturedVar(); + QualType NewInitCaptureType = + getSema().performLambdaInitCaptureInitialization(C->getLocation(), + OldVD->getType()->isReferenceType(), OldVD->getIdentifier(), + NewExprInit); + NewExprInitResult = NewExprInit; + VarDecl *NewVD = 0; + InitCaptureExprsAndTypes[C - E->capture_begin()] = + std::make_pair(NewExprInitResult, NewInitCaptureType); + + } + LambdaScopeInfo *LSI = getSema().PushLambdaScope(); // Transform the template parameters, and add them to the current // instantiation scope. The null case is handled correctly. @@ -8358,31 +8393,17 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); - return getDerived().TransformLambdaScope(E, NewCallOperator); + return getDerived().TransformLambdaScope(E, NewCallOperator, + InitCaptureExprsAndTypes); } template ExprResult TreeTransform::TransformLambdaScope(LambdaExpr *E, - CXXMethodDecl *CallOperator) { + CXXMethodDecl *CallOperator, + ArrayRef InitCaptureExprsAndTypes) { bool Invalid = false; - // Transform any init-capture expressions before entering the scope of the - // lambda. - SmallVector InitCaptureExprs; - InitCaptureExprs.resize(E->explicit_capture_end() - - E->explicit_capture_begin()); - for (LambdaExpr::capture_iterator C = E->capture_begin(), - CEnd = E->capture_end(); - C != CEnd; ++C) { - if (!C->isInitCapture()) - continue; - InitCaptureExprs[C - E->capture_begin()] = - getDerived().TransformInitializer( - C->getCapturedVar()->getInit(), - C->getCapturedVar()->getInitStyle() == VarDecl::CallInit); - } - // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), CallOperator); @@ -8415,19 +8436,24 @@ TreeTransform::TransformLambdaScope(LambdaExpr *E, // Rebuild init-captures, including the implied field declaration. if (C->isInitCapture()) { - ExprResult Init = InitCaptureExprs[C - E->capture_begin()]; - if (Init.isInvalid()) { + + InitCaptureInfoTy InitExprTypePair = + InitCaptureExprsAndTypes[C - E->capture_begin()]; + ExprResult Init = InitExprTypePair.first; + QualType InitQualType = InitExprTypePair.second; + if (Init.isInvalid() || InitQualType.isNull()) { Invalid = true; continue; } VarDecl *OldVD = C->getCapturedVar(); - VarDecl *NewVD = getSema().checkInitCapture( - C->getLocation(), OldVD->getType()->isReferenceType(), - OldVD->getIdentifier(), Init.take()); + VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl( + OldVD->getLocation(), InitExprTypePair.second, + OldVD->getIdentifier(), Init.get()); if (!NewVD) Invalid = true; - else + else { getDerived().transformedLocalDecl(OldVD, NewVD); + } getSema().buildInitCaptureField(LSI, NewVD); continue; } diff --git a/test/SemaCXX/cxx1y-init-captures.cpp b/test/SemaCXX/cxx1y-init-captures.cpp index d737a4ae62..2cb4d31ffc 100644 --- a/test/SemaCXX/cxx1y-init-captures.cpp +++ b/test/SemaCXX/cxx1y-init-captures.cpp @@ -1,14 +1,169 @@ -// RUN: %clang_cc1 -std=c++1y %s -verify +// RUN: %clang_cc1 -std=c++1y %s -verify -emit-llvm-only -// expected-no-diagnostics namespace variadic_expansion { - void f(int &, char &); - - template void g(T &... t) { + int f(int &, char &) { return 0; } + template char fv(Ts ... ts) { return 0; } + // FIXME: why do we get 2 error messages + template void g(T &... t) { //expected-note3{{declared here}} f([&a(t)]()->decltype(auto) { return a; }() ...); + + auto L = [x = f([&a(t)]()->decltype(auto) { return a; }()...)]() { return x; }; + const int y = 10; + auto M = [x = y, + &z = y](T& ... t) { }; + auto N = [x = y, + &z = y, n = f(t...), + o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...), t...](T& ... s) { + fv([&a(t)]()->decltype(auto) { + return a; + }() ...); + }; + auto N2 = [x = y, //expected-note3{{begins here}} + &z = y, n = f(t...), + o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...)](T& ... s) { + fv([&a(t)]()->decltype(auto) { //expected-error 3{{captured}} + return a; + }() ...); + }; + + } + + void h(int i, char c) { g(i, c); } //expected-note{{in instantiation}} +} + +namespace odr_use_within_init_capture { + +int test() { + + { // no captures + const int x = 10; + auto L = [z = x + 2](int a) { + auto M = [y = x - 2](char b) { + return y; + }; + return M; + }; + + } + { // should not capture + const int x = 10; + auto L = [&z = x](int a) { + return a;; + }; + + } + { + const int x = 10; + auto L = [k = x](char a) { //expected-note {{declared}} + return [](int b) { //expected-note {{begins}} + return [j = k](int c) { //expected-error {{cannot be implicitly captured}} + return c; + }; + }; + }; + } + { + const int x = 10; + auto L = [k = x](char a) { + return [=](int b) { + return [j = k](int c) { + return c; + }; + }; + }; } + { + const int x = 10; + auto L = [k = x](char a) { + return [k](int b) { + return [j = k](int c) { + return c; + }; + }; + }; + } + + return 0; +} - void h(int i, char c) { g(i, c); } +int run = test(); + +} + +namespace odr_use_within_init_capture_template { + +template +int test(T t = T{}) { + + { // no captures + const T x = 10; + auto L = [z = x](char a) { + auto M = [y = x](T b) { + return y; + }; + return M; + }; + + } + { // should not capture + const T x = 10; + auto L = [&z = x](T a) { + return a;; + }; + + } + { // will need to capture x in outer lambda + const T x = 10; //expected-note {{declared}} + auto L = [z = x](char a) { //expected-note {{begins}} + auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}} + return y; + }; + return M; + }; + + } + { // will need to capture x in outer lambda + const T x = 10; + auto L = [=,z = x](char a) { + auto M = [&y = x](T b) { + return y; + }; + return M; + }; + + } + { // will need to capture x in outer lambda + const T x = 10; + auto L = [x, z = x](char a) { + auto M = [&y = x](T b) { + return y; + }; + return M; + }; + } + { // will need to capture x in outer lambda + const int x = 10; //expected-note 2{{declared}} + auto L = [z = x](char a) { //expected-note 2{{begins}} + auto M = [&y = x](T b) { //expected-error 2{{cannot be implicitly captured}} + return y; + }; + return M; + }; + } + { + // no captures + const T x = 10; + auto L = [z = + [z = x, &y = x](char a) { return z + y; }('a')](char a) + { return z; }; + + } + + return 0; } + +int run = test(); //expected-note {{instantiation}} + +} \ No newline at end of file