Author: faisalv Date: Mon Mar 21 04:25:37 2016 New Revision: 263921 URL: http://llvm.org/viewvc/llvm-project?rev=263921&view=rev Log: [Cxx1z] Implement Lambda Capture of *this by Value as [=,*this] (P0018R3)
Implement lambda capture of *this by copy. For e.g.: struct A { int d = 10; auto foo() { return [*this] (auto a) mutable { d+=a; return d; }; } }; auto L = A{}.foo(); // A{}'s lifetime is gone. // Below is still ok, because *this was captured by value. assert(L(10) == 20); assert(L(100) == 120); If the capture was implicit, or [this] (i.e. *this was captured by reference), this code would be otherwise undefined. Implementation Strategy: - amend the parser to accept *this in the lambda introducer - add a new king of capture LCK_StarThis - teach Sema::CheckCXXThisCapture to handle by copy captures of the enclosing object (i.e. *this) - when CheckCXXThisCapture does capture by copy, the corresponding initializer expression for the closure's data member direct-initializes it thus making a copy of '*this'. - in codegen, when assigning to CXXThisValue, if *this was captured by copy, make sure it points to the corresponding field member, and not, unlike when captured by reference, what the field member points to. - mark feature as implemented in svn Much gratitude to Richard Smith for his carefully illuminating reviews! Added: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp cfe/trunk/test/CodeGenCXX/cxx1z-lambda-star-this.cpp cfe/trunk/test/SemaCXX/cxx1z-lambda-star-this.cpp Modified: cfe/trunk/include/clang/AST/LambdaCapture.h cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Basic/Lambda.h cfe/trunk/include/clang/Sema/ScopeInfo.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/AST/ExprCXX.cpp cfe/trunk/lib/AST/StmtPrinter.cpp cfe/trunk/lib/AST/StmtProfile.cpp cfe/trunk/lib/CodeGen/CodeGenFunction.cpp cfe/trunk/lib/Parse/ParseExprCXX.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/lib/Sema/SemaExprCXX.cpp cfe/trunk/lib/Sema/SemaLambda.cpp cfe/trunk/lib/Sema/TreeTransform.h cfe/trunk/lib/Serialization/ASTReaderDecl.cpp cfe/trunk/lib/Serialization/ASTWriter.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/LambdaCapture.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/LambdaCapture.h?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/LambdaCapture.h (original) +++ cfe/trunk/include/clang/AST/LambdaCapture.h Mon Mar 21 04:25:37 2016 @@ -35,8 +35,18 @@ class LambdaCapture { /// 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 capture of a VLA type. + llvm::PointerIntPair<void*, 2> CapturedEntityAndBits; - llvm::PointerIntPair<Decl *, 2> DeclAndBits; SourceLocation Loc; SourceLocation EllipsisLoc; @@ -69,20 +79,21 @@ public: /// \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<VarDecl>(DeclAndBits.getPointer()); + void *Ptr = CapturedEntityAndBits.getPointer(); + if (Ptr != &ThisSentinel && Ptr != &VLASentinel) + return dyn_cast_or_null<VarDecl>(static_cast<Decl *>(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 +102,15 @@ public: /// 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<VarDecl>(DeclAndBits.getPointer()); + assert(capturesVariable() && "No variable available for capture"); + return static_cast<VarDecl *>(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). Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Mon Mar 21 04:25:37 2016 @@ -766,6 +766,9 @@ def warn_cxx98_compat_lambda : Warning< 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< Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Mar 21 04:25:37 2016 @@ -5991,6 +5991,13 @@ let CategoryName = "Lambda Issue" in { "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 '*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<CXXPre1zCompat>, DefaultIgnore; + def ext_star_this_lambda_capture_cxx1z : ExtWarn< + "capture of '*this' by copy is a C++1z extension">, InGroup<CXX1z>; } def err_return_in_captured_stmt : Error< Modified: cfe/trunk/include/clang/Basic/Lambda.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Lambda.h?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Lambda.h (original) +++ cfe/trunk/include/clang/Basic/Lambda.h Mon Mar 21 04:25:37 2016 @@ -32,7 +32,8 @@ enum LambdaCaptureDefault { /// 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 copy LCK_ByCopy, ///< Capturing by copy (a.k.a., by value) LCK_ByRef, ///< Capturing by reference LCK_VLAType ///< Capturing variable-length array type Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/ScopeInfo.h (original) +++ cfe/trunk/include/clang/Sema/ScopeInfo.h Mon Mar 21 04:25:37 2016 @@ -420,18 +420,20 @@ public: // 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<VarDecl*, 1, bool> 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<void *, 2, CaptureKind> 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<VarDecl*, 2> VarAndNestedAndThis; /// \brief The source location at which the first capture occurred. SourceLocation Loc; @@ -447,27 +449,28 @@ public: 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; @@ -476,13 +479,14 @@ public: 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. @@ -542,7 +546,7 @@ public: } 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; } @@ -862,9 +866,10 @@ void FunctionScopeInfo::recordUseOfWeak( 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(); } Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Mon Mar 21 04:25:37 2016 @@ -4623,7 +4623,8 @@ public: /// \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 Modified: cfe/trunk/lib/AST/ExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprCXX.cpp (original) +++ cfe/trunk/lib/AST/ExprCXX.cpp Mon Mar 21 04:25:37 2016 @@ -879,18 +879,25 @@ CXXConstructExpr::CXXConstructExpr(const } } +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: @@ -901,18 +908,20 @@ LambdaCapture::LambdaCapture(SourceLocat 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; } Modified: cfe/trunk/lib/AST/StmtPrinter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtPrinter.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/AST/StmtPrinter.cpp (original) +++ cfe/trunk/lib/AST/StmtPrinter.cpp Mon Mar 21 04:25:37 2016 @@ -2008,7 +2008,9 @@ void StmtPrinter::VisitLambdaExpr(Lambda case LCK_This: OS << "this"; break; - + case LCK_StarThis: + OS << "*this"; + break; case LCK_ByRef: if (Node->getCaptureDefault() != LCD_ByRef || Node->isInitCapture(C)) OS << '&'; Modified: cfe/trunk/lib/AST/StmtProfile.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtProfile.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/AST/StmtProfile.cpp (original) +++ cfe/trunk/lib/AST/StmtProfile.cpp Mon Mar 21 04:25:37 2016 @@ -1258,6 +1258,7 @@ StmtProfiler::VisitLambdaExpr(const Lamb C != CEnd; ++C) { ID.AddInteger(C->getCaptureKind()); switch (C->getCaptureKind()) { + case LCK_StarThis: case LCK_This: break; case LCK_ByRef: Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original) +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Mon Mar 21 04:25:37 2016 @@ -823,10 +823,22 @@ void CodeGenFunction::StartFunction(Glob 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()) { Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Mon Mar 21 04:25:37 2016 @@ -846,8 +846,16 @@ Optional<unsigned> Parser::ParseLambdaIn IdentifierInfo *Id = nullptr; SourceLocation EllipsisLoc; ExprResult Init; - - if (Tok.is(tok::kw_this)) { + + if (Tok.is(tok::star)) { + Loc = ConsumeToken(); + if (Tok.is(tok::kw_this)) { + ConsumeToken(); + 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 { Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Mar 21 04:25:37 2016 @@ -11010,7 +11010,8 @@ static void RebuildLambdaScopeInfo(CXXMe } 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()); } Modified: cfe/trunk/lib/Sema/SemaExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) +++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Mar 21 04:25:37 2016 @@ -13249,6 +13249,7 @@ static bool captureInCapturedRegion(Capt /// \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, Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Mon Mar 21 04:25:37 2016 @@ -845,11 +845,34 @@ QualType Sema::getCurrentThisType() { // 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<CXXRecordDecl>(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 +907,70 @@ Sema::CXXThisScopeRAII::~CXXThisScopeRAI } } -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* + + + unsigned NumCapturingClosures = 0; for (unsigned idx = MaxFunctionScopesIndex; idx != 0; idx--) { if (CapturingScopeInfo *CSI = dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) { @@ -917,44 +982,69 @@ bool Sema::CheckCXXThisCapture(SourceLoc 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) + << (Explicit && idx == MaxFunctionScopesIndex); return true; } if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion || - Explicit) { + (Explicit && idx == MaxFunctionScopesIndex)) { + // Regarding (Explicit && idx == MaxFunctionScopesIndex): only the first + // iteration through can be an explicit capture, all enclosing closures, + // if any, must perform implicit captures. + // This closure can capture 'this'; continue looking upwards. - NumClosures++; - Explicit = false; + NumCapturingClosures++; continue; } // This context can't implicitly capture 'this'; fail out. if (BuildAndDiagnose) - Diag(Loc, diag::err_this_capture) << Explicit; + Diag(Loc, diag::err_this_capture) + << (Explicit && idx == MaxFunctionScopesIndex); return true; } break; } if (!BuildAndDiagnose) return false; - // Mark that we're implicitly capturing 'this' in all the scopes we skipped. + + // If we got here, then the closure at MaxFunctionScopesIndex on the + // FunctionScopes stack, can capture the *enclosing object*, so capture it + // (including implicit by-reference captures in any enclosing closures). + + // In the loop below, respect the ByCopy flag only for the closure requesting + // the capture (i.e. first iteration through the loop below). Ignore it for + // all enclosing closure's upto NumCapturingClosures (since they must be + // implicitly capturing the *enclosing object* by reference (see loop + // above)). + assert((!ByCopy || + dyn_cast<LambdaScopeInfo>(FunctionScopes[MaxFunctionScopesIndex])) && + "Only a lambda can capture the enclosing object (referred to by " + "*this) by copy"); // FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated // contexts. - for (unsigned idx = MaxFunctionScopesIndex; NumClosures; - --idx, --NumClosures) { + + for (unsigned idx = MaxFunctionScopesIndex; NumCapturingClosures; + --idx, --NumCapturingClosures) { CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]); Expr *ThisExpr = nullptr; QualType ThisTy = getCurrentThisType(); - if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) - // For lambda expressions, build a field and an initializing expression. - ThisExpr = captureThis(Context, LSI->Lambda, ThisTy, Loc); - else if (CapturedRegionScopeInfo *RSI + if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) { + // For lambda expressions, build a field and an initializing expression, + // and capture the *enclosing object* by copy only if this is the first + // iteration. + ThisExpr = captureThis(*this, Context, LSI->Lambda, ThisTy, Loc, + ByCopy && idx == MaxFunctionScopesIndex); + + } else if (CapturedRegionScopeInfo *RSI = dyn_cast<CapturedRegionScopeInfo>(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; } Modified: cfe/trunk/lib/Sema/SemaLambda.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaLambda.cpp (original) +++ cfe/trunk/lib/Sema/SemaLambda.cpp Mon Mar 21 04:25:37 2016 @@ -924,7 +924,12 @@ void Sema::ActOnStartOfLambdaDefinition( = 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 @@ void Sema::ActOnStartOfLambdaDefinition( 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 @@ void Sema::ActOnStartOfLambdaDefinition( continue; } - CheckCXXThisCapture(C->Loc, /*Explicit=*/true); + CheckCXXThisCapture(C->Loc, /*Explicit=*/true, /*BuildAndDiagnose*/ true, + /*FunctionScopeIndexToStopAtPtr*/ nullptr, + C->Kind == LCK_StarThis); continue; } @@ -1529,10 +1538,9 @@ ExprResult Sema::BuildLambdaExpr(SourceL // 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; } Modified: cfe/trunk/lib/Sema/TreeTransform.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Sema/TreeTransform.h (original) +++ cfe/trunk/lib/Sema/TreeTransform.h Mon Mar 21 04:25:37 2016 @@ -10089,7 +10089,9 @@ TreeTransform<Derived>::TransformLambdaE // 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 Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original) +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Mon Mar 21 04:25:37 2016 @@ -1497,6 +1497,7 @@ void ASTDeclReader::ReadCXXDefinitionDat bool IsImplicit = Record[Idx++]; LambdaCaptureKind Kind = static_cast<LambdaCaptureKind>(Record[Idx++]); switch (Kind) { + case LCK_StarThis: case LCK_This: case LCK_VLAType: *ToCapture++ = Capture(Loc, IsImplicit, Kind, nullptr,SourceLocation()); Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTWriter.cpp (original) +++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon Mar 21 04:25:37 2016 @@ -5631,6 +5631,7 @@ void ASTWriter::AddCXXDefinitionData(con Record.push_back(Capture.isImplicit()); Record.push_back(Capture.getCaptureKind()); switch (Capture.getCaptureKind()) { + case LCK_StarThis: case LCK_This: case LCK_VLAType: break; Added: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp?rev=263921&view=auto ============================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp (added) +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp Mon Mar 21 04:25:37 2016 @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++1z %s -verify + +class NonCopyable { + NonCopyable(const NonCopyable&) = delete; //expected-note3{{explicitly marked deleted here}} + int x = 10; + void foo() { + auto L = [this] { return x; }; + const auto &M = [*this] { return x; };//expected-error{{call to deleted}} + const auto &M2 = [this] () -> auto&& { + ++x; + return [*this] { //expected-error{{call to deleted}} expected-warning{{reference to local}} + return ++x; //expected-error{{read-only}} + }; + }; + const auto &M3 = [*this] () mutable -> auto&& { //expected-error{{call to deleted}} + ++x; + return [this] { // expected-warning{{reference to local}} + return x; + }; + }; + } +}; Added: cfe/trunk/test/CodeGenCXX/cxx1z-lambda-star-this.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx1z-lambda-star-this.cpp?rev=263921&view=auto ============================================================================== --- cfe/trunk/test/CodeGenCXX/cxx1z-lambda-star-this.cpp (added) +++ cfe/trunk/test/CodeGenCXX/cxx1z-lambda-star-this.cpp Mon Mar 21 04:25:37 2016 @@ -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?<auto>@@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?<auto>@@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 Added: cfe/trunk/test/SemaCXX/cxx1z-lambda-star-this.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-lambda-star-this.cpp?rev=263921&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/cxx1z-lambda-star-this.cpp (added) +++ cfe/trunk/test/SemaCXX/cxx1z-lambda-star-this.cpp Mon Mar 21 04:25:37 2016 @@ -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<class T = int> + 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 U> +class B { + B(const B&) = delete; //expected-note{{deleted here}} + double d = 3.14; + public: + template<class T = int> + 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<int*> b; + b.foo(); //expected-note{{in instantiation}} +} // end main +} // end ns4 +} //end ns test_star_this Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=263921&r1=263920&r2=263921&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Mon Mar 21 04:25:37 2016 @@ -659,7 +659,7 @@ as the draft C++1z standard evolves.</p> <tr> <td>Lambda capture of <tt>*this</tt></td> <td><a href="http://wg21.link/p0018r3">P0018R3</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Direct-list-initialization of <tt>enum</tt>s</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits