r263921 - [Cxx1z] Implement Lambda Capture of *this by Value as [=, *this] (P0018R3)
Faisal Vali via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 21 02:25:40 PDT 2016
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 at 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 at 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>
More information about the cfe-commits
mailing list