[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!
llvm-svn: 263921
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index ea98334..b1b91f7 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -879,18 +879,25 @@
}
}
+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 @@
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;
}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6938aef..cc38374 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/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 << '&';
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 327b4c0..30e094c 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1258,6 +1258,7 @@
C != CEnd; ++C) {
ID.AddInteger(C->getCaptureKind());
switch (C->getCaptureKind()) {
+ case LCK_StarThis:
case LCK_This:
break;
case LCK_ByRef: