]> granicus.if.org Git - clang/commitdiff
[Cxx1z] Implement Lambda Capture of *this by Value as [=,*this] (P0018R3)
authorFaisal Vali <faisalv@yahoo.com>
Mon, 21 Mar 2016 09:25:37 +0000 (09:25 +0000)
committerFaisal Vali <faisalv@yahoo.com>
Mon, 21 Mar 2016 09:25:37 +0000 (09:25 +0000)
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!

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263921 91177308-0d34-0410-b5e6-96231b3b80d8

22 files changed:
include/clang/AST/LambdaCapture.h
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/Lambda.h
include/clang/Sema/ScopeInfo.h
include/clang/Sema/Sema.h
lib/AST/ExprCXX.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaLambda.cpp
lib/Sema/TreeTransform.h
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriter.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp [new file with mode: 0644]
test/CodeGenCXX/cxx1z-lambda-star-this.cpp [new file with mode: 0644]
test/SemaCXX/cxx1z-lambda-star-this.cpp [new file with mode: 0644]
www/cxx_status.html

index ddefa88a6b697d1e95247d6df43dacda7c923f77..c71de96299e31b026268d514a363871d2605c253 100644 (file)
@@ -35,8 +35,18 @@ class LambdaCapture {
     /// This includes the case of a non-reference init-capture.
     Capture_ByCopy = 0x02
   };
+  struct LLVM_ALIGNAS(4) OpaqueCapturedEntity {};\r
+  static OpaqueCapturedEntity ThisSentinel;\r
+  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).
index 27398efbcf0c4e8dd069b146a12cbe2045444a02..1116f3205a026b7ad8904892a5e61452f2389418 100644 (file)
@@ -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\r
+def err_expected_star_this_capture : Error<
+  "expected 'this' following '*' in lambda capture list">;
 
 // Availability attribute
 def err_expected_version : Error<
index e8038b63b65ec4eda89c61d884c281b4bac35f1f..c763611d73a2e788b5b7c472edb3aa5a24ba8ced 100644 (file)
@@ -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<\r
+    "by value capture of '*this' is incompatible with C++ standards before C++1z">,\r
+     InGroup<CXXPre1zCompat>, DefaultIgnore;\r
+  def ext_star_this_lambda_capture_cxx1z : ExtWarn<\r
+    "capture of '*this' by copy is a C++1z extension">, InGroup<CXX1z>;
 }
 
 def err_return_in_captured_stmt : Error<
index e676e726dd7a93b3f91f0e72605dd704a57ed508..1c19f1dcc8b1c2b2e0ee5446206c73d34951532e 100644 (file)
@@ -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
index 30047cddf289cbc21b3af1eeb9df3bfd39875958..639d87d6dbb417cac7d40129f9e24d3346682e57 100644 (file)
@@ -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(const ExprT *E, bool IsRead) {
 
 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 a0d2b6f73f7854a1c28f9b6ecd2184340b5bbe77..209dd43cb45aef5f3617cc76372d664e8a3b0023 100644 (file)
@@ -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
index ea983340a293c5659ca1c7d94ef738433f1176ed..b1b91f78d7a61c7fe4cf67f74529a5169b5ed56b 100644 (file)
@@ -879,18 +879,25 @@ CXXConstructExpr::CXXConstructExpr(const ASTContext &C, StmtClass SC,
   }
 }
 
+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(SourceLocation Loc, bool Implicit,
     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 6938aef57eafe7a490a3aa569467426f0546d295..cc38374092693f6ab211d3e3fa14b67daf18ca7e 100644 (file)
@@ -2008,7 +2008,9 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
     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 327b4c091e900cc4d8e4721f8a0fce0556d2ca7c..30e094c68ed7c3247aa6a2a359cb2229236f3419 100644 (file)
@@ -1258,6 +1258,7 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
        C != CEnd; ++C) {
     ID.AddInteger(C->getCaptureKind());
     switch (C->getCaptureKind()) {
+    case LCK_StarThis:
     case LCK_This:
       break;
     case LCK_ByRef:
index ac0322303a2a876c74122c3ba93ac6e5fe5193d3..71c364cabc55f69faafa62ab8abb6f99d85cf98f 100644 (file)
@@ -823,10 +823,22 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
       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 558d2371f6c5b6f2e7fa24df5ff76d894836e5dc..8e5f35ad65ce2d199296e9b8901433a4488c146c 100644 (file)
@@ -846,8 +846,16 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
     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 {
index d8740d2beb1cf7d25ee9c57e743b99615aabafc6..6b1182fa4cb46f77ea61384b5f94d27feda6c0a0 100644 (file)
@@ -11010,7 +11010,8 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
 
     } 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 324556e8620eb13b7d95a884b89d4c75a2ab7be0..4e79b9e93edce12e3a96cd2b56396f0a5dfed777 100644 (file)
@@ -13249,6 +13249,7 @@ static bool captureInCapturedRegion(CapturedRegionScopeInfo *RSI,
 
 /// \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 709718f26c88bdb9b774d14c1fb0f8ae63efff9e..129969469abe8882dfd3c99743dd942e92a11e64 100644 (file)
@@ -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::~CXXThisScopeRAII() {
   }
 }
 
-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(SourceLocation Loc, bool Explicit,
       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;
 }
index 1a62f0dfcbabf3b82380e73db90b56f676167505..0f5eb4135267558b7719d5d8368022be8cac7668 100644 (file)
@@ -924,7 +924,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
     = 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(LambdaIntroducer &Intro,
         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(LambdaIntroducer &Intro,
         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(SourceLocation StartLoc, SourceLocation EndLoc,
       // 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 fc201292146ac026e2fdcc1ea2608741a9f75d46..7ed52ebb12aa5cf322949fab892e3f0d03f986dd 100644 (file)
@@ -10089,7 +10089,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
 
     // 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 1e93627df8db69ea477173d020c208fa80f82d03..e580eb03e85acecf9a14bfa4df6a303e8b3131b5 100644 (file)
@@ -1497,6 +1497,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
       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());
index 3e04bc5ad7d4012311df093a0e39cd4a82392d9a..d0b3d279a2a01588833fa8e8692c14ee6804faaf 100644 (file)
@@ -5631,6 +5631,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
       Record.push_back(Capture.isImplicit());
       Record.push_back(Capture.getCaptureKind());
       switch (Capture.getCaptureKind()) {
+      case LCK_StarThis:
       case LCK_This:
       case LCK_VLAType:
         break;
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p15-star-this-capture.cpp
new file mode 100644 (file)
index 0000000..bae1e25
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++1z %s -verify\r
+\r
+class NonCopyable {\r
+  NonCopyable(const NonCopyable&) = delete; //expected-note3{{explicitly marked deleted here}}\r
+  int x = 10;\r
+  void foo() {\r
+    auto L = [this] { return x; };\r
+    const auto &M = [*this] { return x; };//expected-error{{call to deleted}}\r
+    const auto &M2 = [this] () -> auto&& {\r
+      ++x;\r
+      return [*this] {  //expected-error{{call to deleted}} expected-warning{{reference to local}}\r
+         return ++x; //expected-error{{read-only}}\r
+      }; \r
+    };\r
+    const auto &M3 = [*this] () mutable -> auto&& { //expected-error{{call to deleted}} \r
+      ++x;\r
+      return [this] {  // expected-warning{{reference to local}}\r
+         return x;\r
+      }; \r
+    };\r
+  }  \r
+};\r
diff --git a/test/CodeGenCXX/cxx1z-lambda-star-this.cpp b/test/CodeGenCXX/cxx1z-lambda-star-this.cpp
new file mode 100644 (file)
index 0000000..f379a33
--- /dev/null
@@ -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
diff --git a/test/SemaCXX/cxx1z-lambda-star-this.cpp b/test/SemaCXX/cxx1z-lambda-star-this.cpp
new file mode 100644 (file)
index 0000000..1163722
--- /dev/null
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -emit-llvm-only %s\r
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -DDELAYED_TEMPLATE_PARSING\r
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS\r
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING\r
+\r
+\r
+namespace test_star_this {\r
+namespace ns1 {\r
+class A {\r
+  int x = 345;\r
+  auto foo() {\r
+    (void) [*this, this] { };  //expected-error{{'this' can appear only once}}\r
+    (void) [this] { ++x; };\r
+    (void) [*this] { ++x; };  //expected-error{{read-only variable}}\r
+    (void) [*this] () mutable { ++x; };\r
+    (void) [=] { return x; };\r
+    (void) [&, this] { return x; };\r
+    (void) [=, *this] { return x; };\r
+    (void) [&, *this] { return x; };\r
+  }\r
+};\r
+} // end ns1\r
+\r
+namespace ns2 {\r
+  class B {\r
+    B(const B&) = delete; //expected-note{{deleted here}}\r
+    int *x = (int *) 456;\r
+    void foo() {\r
+      (void)[this] { return x; };\r
+      (void)[*this] { return x; }; //expected-error{{call to deleted}}\r
+    }\r
+  };\r
+} // end ns2\r
+namespace ns3 {\r
+  class B {\r
+    B(const B&) = delete; //expected-note2{{deleted here}}\r
+    \r
+    int *x = (int *) 456;\r
+    public: \r
+    template<class T = int>\r
+    void foo() {\r
+      (void)[this] { return x; };\r
+      (void)[*this] { return x; }; //expected-error2{{call to deleted}}\r
+    }\r
+    \r
+    B() = default;\r
+  } b;\r
+  B *c = (b.foo(), nullptr); //expected-note{{in instantiation}}\r
+} // end ns3\r
+\r
+namespace ns4 {\r
+template<class U>\r
+class B {\r
+  B(const B&) = delete; //expected-note{{deleted here}}\r
+  double d = 3.14;\r
+  public: \r
+  template<class T = int>\r
+  auto foo() {\r
+    const auto &L = [*this] (auto a) mutable { //expected-error{{call to deleted}}\r
+      d += a; \r
+      return [this] (auto b) { return d +=b; }; \r
+    }; \r
+  }\r
+  \r
+  B() = default;\r
+};\r
+void main() {\r
+  B<int*> b;\r
+  b.foo(); //expected-note{{in instantiation}}\r
+} // end main  \r
+} // end ns4\r
+} //end ns test_star_this\r
index 301b08ab477b8d9dd7c4741997380f3b76325b47..f079b7e414190158b4fa6993e7c40c0e520ae966 100644 (file)
@@ -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>