Index: include/clang/AST/LambdaCapture.h =================================================================== --- include/clang/AST/LambdaCapture.h +++ include/clang/AST/LambdaCapture.h @@ -21,6 +21,7 @@ namespace clang { + /// \brief Describes the capture of a variable or of \c this, or of a /// C++1y init-capture. class LambdaCapture { @@ -35,11 +36,21 @@ /// This includes the case of a non-reference init-capture. Capture_ByCopy = 0x02 }; + struct LLVM_ALIGNAS(4) OpaqueCapturedEntity {}; + static OpaqueCapturedEntity ThisSentinel; + static OpaqueCapturedEntity VLASentinel; + + // Captured Entity could represent: + // - a VarDecl* that represents the variable that was captured or the + // init-capture. + // - or, points to the ThisSentinel if this represents a capture of '*this' + // by value or reference. + // - or, points to the VLASentinel if this represents a by VLA capture. + llvm::PointerIntPair CapturedEntityAndBits; - llvm::PointerIntPair DeclAndBits; SourceLocation Loc; SourceLocation EllipsisLoc; - + friend class ASTStmtReader; friend class ASTStmtWriter; @@ -69,20 +80,21 @@ /// \brief Determine whether this capture handles the C++ \c this /// pointer. bool capturesThis() const { - return (DeclAndBits.getPointer() == nullptr) && - !(DeclAndBits.getInt() & Capture_ByCopy); + return CapturedEntityAndBits.getPointer() == &ThisSentinel; } - + /// \brief Determine whether this capture handles a variable. bool capturesVariable() const { - return dyn_cast_or_null(DeclAndBits.getPointer()); + void *Ptr = CapturedEntityAndBits.getPointer(); + if (Ptr != &ThisSentinel && Ptr != &VLASentinel) + return dyn_cast_or_null(static_cast(Ptr)); + return false; } /// \brief Determine whether this captures a variable length array bound /// expression. bool capturesVLAType() const { - return (DeclAndBits.getPointer() == nullptr) && - (DeclAndBits.getInt() & Capture_ByCopy); + return CapturedEntityAndBits.getPointer() == &VLASentinel; } /// \brief Retrieve the declaration of the local variable being @@ -91,13 +103,15 @@ /// This operation is only valid if this capture is a variable capture /// (other than a capture of \c this). VarDecl *getCapturedVar() const { - assert(capturesVariable() && "No variable available for 'this' capture"); - return cast(DeclAndBits.getPointer()); + assert(capturesVariable() && "No variable available for capture"); + return static_cast(CapturedEntityAndBits.getPointer()); } /// \brief Determine whether this was an implicit capture (not /// written between the square brackets introducing the lambda). - bool isImplicit() const { return DeclAndBits.getInt() & Capture_Implicit; } + bool isImplicit() const { + return CapturedEntityAndBits.getInt() & Capture_Implicit; + } /// \brief Determine whether this was an explicit capture (written /// between the square brackets introducing the lambda). Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -766,6 +766,9 @@ def err_lambda_missing_parens : Error< "lambda requires '()' before %select{'mutable'|return type|" "attribute specifier}0">; +// C++1z lambda expressions +def err_expected_star_this_capture : Error< + "expected 'this' following '*' in lambda capture list">; // Availability attribute def err_expected_version : Error< Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5978,6 +5978,13 @@ "cannot deduce type for lambda capture %0 from initializer of type %2">; def err_init_capture_deduction_failure_from_init_list : Error< "cannot deduce type for lambda capture %0 from initializer list">; + + // C++1z star-this captures. + def warn_cxx14_compat_star_this_lambda_capture : Warning< + "by value capture of '*this' is incompatible with C++ standards before C++1z">, + InGroup, DefaultIgnore; + def ext_star_this_lambda_capture_cxx1z : ExtWarn< + "by value capture of '*this' is a C++1z extension">, InGroup; } def err_return_in_captured_stmt : Error< Index: include/clang/Basic/Lambda.h =================================================================== --- include/clang/Basic/Lambda.h +++ include/clang/Basic/Lambda.h @@ -32,7 +32,8 @@ /// by reference. C++1y also allows "init-capture", where the initializer /// is an expression. enum LambdaCaptureKind { - LCK_This, ///< Capturing the \c this pointer + LCK_This, ///< Capturing the \c *this object by reference + LCK_StarThis, /// < Capturing the \c *this object by value LCK_ByCopy, ///< Capturing by copy (a.k.a., by value) LCK_ByRef, ///< Capturing by reference LCK_VLAType ///< Capturing variable-length array type Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -412,18 +412,20 @@ // variables of reference type are captured by reference, and other // variables are captured by copy. enum CaptureKind { - Cap_ByCopy, Cap_ByRef, Cap_Block, Cap_This + Cap_ByCopy, Cap_ByRef, Cap_Block, Cap_VLA }; - - /// The variable being captured (if we are not capturing 'this') and whether - /// this is a nested capture. - llvm::PointerIntPair VarAndNested; - /// Expression to initialize a field of the given type, and the kind of /// capture (if this is a capture and not an init-capture). The expression /// is only required if we are capturing ByVal and the variable's type has /// a non-trivial copy constructor. llvm::PointerIntPair InitExprAndCaptureKind; + enum { + IsNestedCapture = 0x1, + IsThisCaptured = 0x2 + }; + /// The variable being captured (if we are not capturing 'this') and whether + /// this is a nested capture, and whether we are capturing 'this' + llvm::PointerIntPair VarAndNestedAndThis; /// \brief The source location at which the first capture occurred. SourceLocation Loc; @@ -439,27 +441,28 @@ Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested, SourceLocation Loc, SourceLocation EllipsisLoc, QualType CaptureType, Expr *Cpy) - : VarAndNested(Var, IsNested), - InitExprAndCaptureKind(Cpy, Block ? Cap_Block : - ByRef ? Cap_ByRef : Cap_ByCopy), + : VarAndNestedAndThis(Var, IsNested ? IsNestedCapture : 0), + InitExprAndCaptureKind( + Cpy, !Var ? Cap_VLA : Block ? Cap_Block : ByRef ? Cap_ByRef + : Cap_ByCopy), Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {} enum IsThisCapture { ThisCapture }; Capture(IsThisCapture, bool IsNested, SourceLocation Loc, - QualType CaptureType, Expr *Cpy) - : VarAndNested(nullptr, IsNested), - InitExprAndCaptureKind(Cpy, Cap_This), + QualType CaptureType, Expr *Cpy, const bool ByCopy) + : VarAndNestedAndThis( + nullptr, (IsThisCaptured | (IsNested ? IsNestedCapture : 0))), + InitExprAndCaptureKind(Cpy, ByCopy ? Cap_ByCopy : Cap_ByRef), Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {} bool isThisCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_This; + return VarAndNestedAndThis.getInt() & IsThisCaptured; } bool isVariableCapture() const { - return InitExprAndCaptureKind.getInt() != Cap_This && !isVLATypeCapture(); + return !isThisCapture() && !isVLATypeCapture(); } bool isCopyCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_ByCopy && - !isVLATypeCapture(); + return InitExprAndCaptureKind.getInt() == Cap_ByCopy; } bool isReferenceCapture() const { return InitExprAndCaptureKind.getInt() == Cap_ByRef; @@ -468,13 +471,14 @@ return InitExprAndCaptureKind.getInt() == Cap_Block; } bool isVLATypeCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_ByCopy && - getVariable() == nullptr; + return InitExprAndCaptureKind.getInt() == Cap_VLA; + } + bool isNested() const { + return VarAndNestedAndThis.getInt() & IsNestedCapture; } - bool isNested() const { return VarAndNested.getInt(); } VarDecl *getVariable() const { - return VarAndNested.getPointer(); + return VarAndNestedAndThis.getPointer(); } /// \brief Retrieve the location at which this variable was captured. @@ -534,7 +538,7 @@ } void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType, - Expr *Cpy); + Expr *Cpy, bool ByCopy); /// \brief Determine whether the C++ 'this' is captured. bool isCXXThisCaptured() const { return CXXThisCaptureIndex != 0; } @@ -854,9 +858,10 @@ inline void CapturingScopeInfo::addThisCapture(bool isNested, SourceLocation Loc, - QualType CaptureType, Expr *Cpy) { + QualType CaptureType, Expr *Cpy, + const bool ByCopy) { Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, CaptureType, - Cpy)); + Cpy, ByCopy)); CXXThisCaptureIndex = Captures.size(); } Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4619,7 +4619,8 @@ /// \return returns 'true' if failed, 'false' if success. bool CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false, bool BuildAndDiagnose = true, - const unsigned *const FunctionScopeIndexToStopAt = nullptr); + const unsigned *const FunctionScopeIndexToStopAt = nullptr, + bool ByCopy = false); /// \brief Determine whether the given type is the type of *this that is used /// outside of the body of a member function for a type that is currently Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -879,20 +879,27 @@ } } +LambdaCapture::OpaqueCapturedEntity LambdaCapture::ThisSentinel; +LambdaCapture::OpaqueCapturedEntity LambdaCapture::VLASentinel; + LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit, LambdaCaptureKind Kind, VarDecl *Var, SourceLocation EllipsisLoc) - : DeclAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc) + : CapturedEntityAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc) { unsigned Bits = 0; if (Implicit) Bits |= Capture_Implicit; switch (Kind) { + case LCK_StarThis: + Bits |= Capture_ByCopy; + // Fall through case LCK_This: assert(!Var && "'this' capture cannot have a variable!"); + CapturedEntityAndBits.setPointer(&ThisSentinel); break; - + case LCK_ByCopy: Bits |= Capture_ByCopy; // Fall through @@ -901,18 +908,20 @@ break; case LCK_VLAType: assert(!Var && "VLA type capture cannot have a variable!"); - Bits |= Capture_ByCopy; + CapturedEntityAndBits.setPointer(&VLASentinel); break; } - DeclAndBits.setInt(Bits); + CapturedEntityAndBits.setInt(Bits); } LambdaCaptureKind LambdaCapture::getCaptureKind() const { - Decl *D = DeclAndBits.getPointer(); - bool CapByCopy = DeclAndBits.getInt() & Capture_ByCopy; - if (!D) - return CapByCopy ? LCK_VLAType : LCK_This; - + void *Ptr = CapturedEntityAndBits.getPointer(); + if (Ptr == &VLASentinel) + return LCK_VLAType; + const unsigned Bits = CapturedEntityAndBits.getInt(); + bool CapByCopy = Bits & Capture_ByCopy; + if (Ptr == &ThisSentinel) + return CapByCopy ? LCK_StarThis : LCK_This; return CapByCopy ? LCK_ByCopy : LCK_ByRef; } Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2008,7 +2008,9 @@ case LCK_This: OS << "this"; break; - + case LCK_StarThis: + OS << "*this"; + break; case LCK_ByRef: if (Node->getCaptureDefault() != LCD_ByRef || Node->isInitCapture(C)) OS << '&'; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1257,6 +1257,7 @@ C != CEnd; ++C) { ID.AddInteger(C->getCaptureKind()); switch (C->getCaptureKind()) { + case LCK_StarThis: case LCK_This: break; case LCK_ByRef: Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -825,10 +825,22 @@ MD->getParent()->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); if (LambdaThisCaptureField) { - // If this lambda captures this, load it. - LValue ThisLValue = EmitLValueForLambdaField(LambdaThisCaptureField); - CXXThisValue = EmitLoadOfLValue(ThisLValue, - SourceLocation()).getScalarVal(); + // If the lambda captures the object referred to by '*this' - either by + // value or by reference, make sure CXXThisValue points to the correct + // object. + + // Get the lvalue for the field (which is a copy of the enclosing object + // or contains the address of the enclosing object). + LValue ThisFieldLValue = EmitLValueForLambdaField(LambdaThisCaptureField); + if (!LambdaThisCaptureField->getType()->isPointerType()) { + // If the enclosing object was captured by value, just use its address. + CXXThisValue = ThisFieldLValue.getAddress().getPointer(); + } else { + // Load the lvalue pointed to by the field, since '*this' was captured + // by reference. + CXXThisValue = + EmitLoadOfLValue(ThisFieldLValue, SourceLocation()).getScalarVal(); + } } for (auto *FD : MD->getParent()->fields()) { if (FD->hasCapturedVLAType()) { Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -846,8 +846,16 @@ IdentifierInfo *Id = nullptr; SourceLocation EllipsisLoc; ExprResult Init; - - if (Tok.is(tok::kw_this)) { + + if (Tok.is(tok::star)) { + Loc = ConsumeToken(); // Consume '*' + if (Tok.is(tok::kw_this)) { + ConsumeToken(); // Consume 'this' + Kind = LCK_StarThis; + } else { + return DiagResult(diag::err_expected_star_this_capture); + } + } else if (Tok.is(tok::kw_this)) { Kind = LCK_This; Loc = ConsumeToken(); } else { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10990,7 +10990,8 @@ } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), - S.getCurrentThisType(), /*Expr*/ nullptr); + S.getCurrentThisType(), /*Expr*/ nullptr, + C.getCaptureKind() == LCK_StarThis); } else { LSI->addVLATypeCapture(C.getLocation(), I->getType()); } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13253,6 +13253,7 @@ /// \brief Create a field within the lambda class for the variable /// being captured. +// FIXME: Delete VarDecl *Var below, it is not used in the function. static void addAsFieldToClosureType(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var, QualType FieldType, QualType DeclRefType, SourceLocation Loc, Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -841,15 +841,39 @@ if (ThisTy.isNull()) { if (isGenericLambdaCallOperatorSpecialization(CurContext) && CurContext->getParent()->getParent()->isRecord()) { + // This is a generic lambda call operator that is being instantiated // within a default initializer - so use the enclosing class as 'this'. // There is no enclosing member function to retrieve the 'this' pointer // from. + + // FIXME: This looks wrong. If we're in a lambda within a lambda within a + // default member initializer, we need to recurse up more parents to find + // the right context. Looks like we should be walking up to the parent of + // the closure type, checking whether that is itself a lambda, and if so, + // recursing, until we reach a class or a function that isn't a lambda + // call operator. And we should accumulate the constness of *this on the + // way. + QualType ClassTy = Context.getTypeDeclType( cast(CurContext->getParent()->getParent())); // There are no cv-qualifiers for 'this' within default initializers, // per [expr.prim.general]p4. - return Context.getPointerType(ClassTy); + ThisTy = Context.getPointerType(ClassTy); + } + } + // Add const for '* this' capture if not mutable. + if (isLambdaCallOperator(CurContext)) { + LambdaScopeInfo *LSI = getCurLambda(); + assert(LSI); + if (LSI->isCXXThisCaptured()) { + auto C = LSI->getCXXThisCapture(); + QualType BaseType = ThisTy->getPointeeType(); + if ((C.isThisCapture() && C.isCopyCapture()) && + LSI->CallOperator->isConst() && !BaseType.isConstQualified()) { + BaseType.addConst(); + ThisTy = Context.getPointerType(BaseType); + } } } return ThisTy; @@ -884,28 +908,71 @@ } } -static Expr *captureThis(ASTContext &Context, RecordDecl *RD, - QualType ThisTy, SourceLocation Loc) { +static Expr *captureThis(Sema &S, ASTContext &Context, RecordDecl *RD, + QualType ThisTy, SourceLocation Loc, + const bool ByCopy) { + QualType CaptureThisTy = ByCopy ? ThisTy->getPointeeType() : ThisTy; + FieldDecl *Field - = FieldDecl::Create(Context, RD, Loc, Loc, nullptr, ThisTy, - Context.getTrivialTypeSourceInfo(ThisTy, Loc), + = FieldDecl::Create(Context, RD, Loc, Loc, nullptr, CaptureThisTy, + Context.getTrivialTypeSourceInfo(CaptureThisTy, Loc), nullptr, false, ICIS_NoInit); Field->setImplicit(true); Field->setAccess(AS_private); RD->addDecl(Field); - return new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); + Expr *This = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); + if (ByCopy) { + Expr *StarThis = S.CreateBuiltinUnaryOp(Loc, + UO_Deref, + This).get(); + InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture( + nullptr, CaptureThisTy, Loc); + InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc); + InitializationSequence Init(S, Entity, InitKind, StarThis); + ExprResult ER = Init.Perform(S, Entity, InitKind, StarThis); + if (ER.isInvalid()) return nullptr; + return ER.get(); + } + return This; } -bool Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit, - bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt) { +bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, + bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt, + const bool ByCopy) { // We don't need to capture this in an unevaluated context. if (isUnevaluatedContext() && !Explicit) return true; + + assert((!ByCopy || Explicit) && "cannot implicitly capture *this by value"); const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt ? - *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; - // Otherwise, check that we can capture 'this'. - unsigned NumClosures = 0; + *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; + + // Check that we can capture the *enclosing object* (referred to by '*this') + // by the capturing-entity/closure (lambda/block/etc) at + // MaxFunctionScopesIndex-deep on the FunctionScopes stack. + + // Note: The *enclosing object* can only be captured by-value by a + // closure that is a lambda, using the explicit notation: + // [*this] { ... }. + // Every other capture of the *enclosing object* results in its by-reference + // capture. + + // For a closure 'L' (at MaxFunctionScopesIndex in the FunctionScopes + // stack), we can capture the *enclosing object* only if: + // - 'L' has an explicit byref or byval capture of the *enclosing object* + // - or, 'L' has an implicit capture. + // AND + // -- there is no enclosing closure + // -- or, there is some enclosing closure 'E' that has already captured the + // *enclosing object*, and every intervening closure (if any) between 'E' + // and 'L' can implicitly capture the *enclosing object*. + // -- or, every enclosing closure can implicitly capture the + // *enclosing object* + + // Respect the explicit capture flag only for the first iteration. + bool UseExplicitFlag = Explicit; + unsigned NumCapturingClosures = 0; for (unsigned idx = MaxFunctionScopesIndex; idx != 0; idx--) { if (CapturingScopeInfo *CSI = dyn_cast(FunctionScopes[idx])) { @@ -917,17 +984,20 @@ if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) { // This context can't implicitly capture 'this'; fail out. if (BuildAndDiagnose) - Diag(Loc, diag::err_this_capture) << Explicit; + Diag(Loc, diag::err_this_capture) << UseExplicitFlag; return true; } if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion || - Explicit) { + UseExplicitFlag) { + assert(!UseExplicitFlag || idx == MaxFunctionScopesIndex); // This closure can capture 'this'; continue looking upwards. - NumClosures++; - Explicit = false; + NumCapturingClosures++; + // Only the first iteration through can be an explicit capture, + // all enclosing closures, if any, must perform implicit captures. + UseExplicitFlag = false; continue; } // This context can't implicitly capture 'this'; fail out. @@ -938,23 +1008,40 @@ break; } if (!BuildAndDiagnose) return false; - // Mark that we're implicitly capturing 'this' in all the scopes we skipped. + + // If we got here, then the lambda (i.e capturing-entity) at + // MaxFunctionScopesIndex on the FunctionScopes stack, can capture the + // *enclosing object*, so capture it (including implicit by-reference captures + // in any enclosing lambdas). + // FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated // contexts. - for (unsigned idx = MaxFunctionScopesIndex; NumClosures; - --idx, --NumClosures) { + + // Respect the ByCopy flag only for the lambda requesting the capture (i.e. + // first iteration through the loop below). Treat it as false for all + // enclosing lambda's upto NumClosures (since they must be implicitly + // capturing the *enclosing object* by reference (see loop above)). + bool UseByCopyFlag = ByCopy; + for (unsigned idx = MaxFunctionScopesIndex; NumCapturingClosures; + --idx, --NumCapturingClosures) { CapturingScopeInfo *CSI = cast(FunctionScopes[idx]); Expr *ThisExpr = nullptr; QualType ThisTy = getCurrentThisType(); - if (LambdaScopeInfo *LSI = dyn_cast(CSI)) + if (LambdaScopeInfo *LSI = dyn_cast(CSI)) { // For lambda expressions, build a field and an initializing expression. - ThisExpr = captureThis(Context, LSI->Lambda, ThisTy, Loc); - else if (CapturedRegionScopeInfo *RSI + ThisExpr = captureThis(*this, Context, LSI->Lambda, ThisTy, Loc, + UseByCopyFlag); + // If we must capture the *enclosing object* in outer lambdas, they must + // be implicit captures, which must be by-reference captures. + UseByCopyFlag = false; + } else if (CapturedRegionScopeInfo *RSI = dyn_cast(FunctionScopes[idx])) - ThisExpr = captureThis(Context, RSI->TheRecordDecl, ThisTy, Loc); + ThisExpr = + captureThis(*this, Context, RSI->TheRecordDecl, ThisTy, Loc, + false/*ByCopy*/); - bool isNested = NumClosures > 1; - CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr); + bool isNested = NumCapturingClosures > 1; + CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr, ByCopy); } return false; } @@ -968,6 +1055,7 @@ if (ThisTy.isNull()) return Diag(Loc, diag::err_invalid_this_use); CheckCXXThisCapture(Loc); + return new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false); } Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -924,7 +924,12 @@ = Intro.Default == LCD_None? Intro.Range.getBegin() : Intro.DefaultLoc; for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; PrevCaptureLoc = C->Loc, ++C) { - if (C->Kind == LCK_This) { + if (C->Kind == LCK_This || C->Kind == LCK_StarThis) { + if (C->Kind == LCK_StarThis) + Diag(C->Loc, !getLangOpts().CPlusPlus1z + ? diag::ext_star_this_lambda_capture_cxx1z + : diag::warn_cxx14_compat_star_this_lambda_capture); + // C++11 [expr.prim.lambda]p8: // An identifier or this shall not appear more than once in a // lambda-capture. @@ -936,10 +941,12 @@ continue; } - // C++11 [expr.prim.lambda]p8: - // If a lambda-capture includes a capture-default that is =, the - // lambda-capture shall not contain this [...]. - if (Intro.Default == LCD_ByCopy) { + // C++1z [expr.prim.lambda]p8: + // If a lambda-capture includes a capture-default that is =, each + // simple-capture of that lambda-capture shall be of the form "& + // identifier" or "* this". [ Note: The form [&,this] is redundant but + // accepted for compatibility with ISO C++14. --end note ] + if (Intro.Default == LCD_ByCopy && C->Kind != LCK_StarThis) { Diag(C->Loc, diag::err_this_capture_with_copy_default) << FixItHint::CreateRemoval( SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc)); @@ -955,7 +962,9 @@ continue; } - CheckCXXThisCapture(C->Loc, /*Explicit=*/true); + CheckCXXThisCapture(C->Loc, /*Explicit=*/true, /*BuildAndDiagnose*/ true, + /*FunctionScopeIndexToStopAtPtr*/ nullptr, + C->Kind == LCK_StarThis); continue; } @@ -1529,10 +1538,9 @@ // Handle 'this' capture. if (From.isThisCapture()) { Captures.push_back( - LambdaCapture(From.getLocation(), IsImplicit, LCK_This)); - CaptureInits.push_back(new (Context) CXXThisExpr(From.getLocation(), - getCurrentThisType(), - /*isImplicit=*/true)); + LambdaCapture(From.getLocation(), IsImplicit, + From.isCopyCapture() ? LCK_StarThis : LCK_This)); + CaptureInits.push_back(From.getInitExpr()); ArrayIndexStarts.push_back(ArrayIndexVars.size()); continue; } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -10060,7 +10060,9 @@ // Capturing 'this' is trivial. if (C->capturesThis()) { - getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit()); + getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit(), + /*BuildAndDiagnose*/ true, nullptr, + C->getCaptureKind() == LCK_StarThis); continue; } // Captured expression will be recaptured during captured variables Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1497,10 +1497,12 @@ bool IsImplicit = Record[Idx++]; LambdaCaptureKind Kind = static_cast(Record[Idx++]); switch (Kind) { + case LCK_StarThis: case LCK_This: case LCK_VLAType: *ToCapture++ = Capture(Loc, IsImplicit, Kind, nullptr,SourceLocation()); break; + case LCK_ByCopy: case LCK_ByRef: VarDecl *Var = ReadDeclAs(Record, Idx); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5631,6 +5631,7 @@ Record.push_back(Capture.isImplicit()); Record.push_back(Capture.getCaptureKind()); switch (Capture.getCaptureKind()) { + case LCK_StarThis: case LCK_This: case LCK_VLAType: break; Index: test/CodeGenCXX/cxx1z-lambda-star-this.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cxx1z-lambda-star-this.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++1y -triple i686-pc-windows-msvc -emit-llvm %s -o - | FileCheck %s +//CHECK: %[[A_LAMBDA:.*]] = type { %struct.A } +//CHECK: %[[B_LAMBDA:.*]] = type { %struct.B* } +struct A { + double a = 111; + auto foo() { return [*this] { return a; }; } +}; + +namespace ns1 { +int X = A{}.foo()(); +} //end ns1 + +//CHECK: @"\01?foo@A@@QAE?A?@@XZ"(%struct.A* %this, %class.anon* noalias sret %[[A_LAMBDA_RETVAL:.*]]) +// get the first object with the closure type, which is of type 'struct.A' +//CHECK: %0 = getelementptr inbounds %[[A_LAMBDA]], %[[A_LAMBDA]]* %[[A_LAMBDA_RETVAL]], i32 0, i32 0 +//CHECK: %1 = bitcast %struct.A* %0 to i8* +//CHECK: %2 = bitcast %struct.A* %this1 to i8* +// copy the contents ... +//CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %1, i8* %2, i32 8, i32 8, i1 false) + +struct B { + double b = 222; + auto bar() { return [this] { return b; }; }; +}; + +namespace ns2 { +int X = B{}.bar()(); +} +//CHECK: @"\01?bar@B@@QAE?A?@@XZ"(%struct.B* %this, %class.anon.0* noalias sret %agg.result) +//CHECK: %0 = getelementptr inbounds %class.anon.0, %class.anon.0* %agg.result, i32 0, i32 0 +//CHECK: store %struct.B* %this1, %struct.B** %0, align 4 \ No newline at end of file Index: test/SemaCXX/cxx1z-lambda-star-this.cpp =================================================================== --- /dev/null +++ test/SemaCXX/cxx1z-lambda-star-this.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -emit-llvm-only %s +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -DDELAYED_TEMPLATE_PARSING +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING + + +namespace test_star_this { +namespace ns1 { +class A { + int x = 345; + auto foo() { + (void) [*this, this] { }; //expected-error{{'this' can appear only once}} + (void) [this] { ++x; }; + (void) [*this] { ++x; }; //expected-error{{read-only variable}} + (void) [*this] () mutable { ++x; }; + (void) [=] { return x; }; + (void) [&, this] { return x; }; + (void) [=, *this] { return x; }; + (void) [&, *this] { return x; }; + } +}; +} // end ns1 + +namespace ns2 { + class B { + B(const B&) = delete; //expected-note{{deleted here}} + int *x = (int *) 456; + void foo() { + (void)[this] { return x; }; + (void)[*this] { return x; }; //expected-error{{call to deleted}} + } + }; +} // end ns2 +namespace ns3 { + class B { + B(const B&) = delete; //expected-note2{{deleted here}} + + int *x = (int *) 456; + public: + template + void foo() { + (void)[this] { return x; }; + (void)[*this] { return x; }; //expected-error2{{call to deleted}} + } + + B() = default; + } b; + B *c = (b.foo(), nullptr); //expected-note{{in instantiation}} +} // end ns3 + +namespace ns4 { +template +class B { + B(const B&) = delete; //expected-note{{deleted here}} + double d = 3.14; + public: + template + auto foo() { + const auto &L = [*this] (auto a) mutable { //expected-error{{call to deleted}} + d += a; + return [this] (auto b) { return d +=b; }; + }; + } + + B() = default; +}; +void main() { + B b; + b.foo(); //expected-note{{in instantiation}} +} // end main +} // end ns4 +} //end ns test_star_this