forked from OSchip/llvm-project
[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
This commit is contained in:
parent
2c8b5993be
commit
dc6b596ebb
|
@ -35,8 +35,18 @@ class LambdaCapture {
|
||||||
/// This includes the case of a non-reference init-capture.
|
/// This includes the case of a non-reference init-capture.
|
||||||
Capture_ByCopy = 0x02
|
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 Loc;
|
||||||
SourceLocation EllipsisLoc;
|
SourceLocation EllipsisLoc;
|
||||||
|
|
||||||
|
@ -69,20 +79,21 @@ public:
|
||||||
/// \brief Determine whether this capture handles the C++ \c this
|
/// \brief Determine whether this capture handles the C++ \c this
|
||||||
/// pointer.
|
/// pointer.
|
||||||
bool capturesThis() const {
|
bool capturesThis() const {
|
||||||
return (DeclAndBits.getPointer() == nullptr) &&
|
return CapturedEntityAndBits.getPointer() == &ThisSentinel;
|
||||||
!(DeclAndBits.getInt() & Capture_ByCopy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Determine whether this capture handles a variable.
|
/// \brief Determine whether this capture handles a variable.
|
||||||
bool capturesVariable() const {
|
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
|
/// \brief Determine whether this captures a variable length array bound
|
||||||
/// expression.
|
/// expression.
|
||||||
bool capturesVLAType() const {
|
bool capturesVLAType() const {
|
||||||
return (DeclAndBits.getPointer() == nullptr) &&
|
return CapturedEntityAndBits.getPointer() == &VLASentinel;
|
||||||
(DeclAndBits.getInt() & Capture_ByCopy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Retrieve the declaration of the local variable being
|
/// \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
|
/// This operation is only valid if this capture is a variable capture
|
||||||
/// (other than a capture of \c this).
|
/// (other than a capture of \c this).
|
||||||
VarDecl *getCapturedVar() const {
|
VarDecl *getCapturedVar() const {
|
||||||
assert(capturesVariable() && "No variable available for 'this' capture");
|
assert(capturesVariable() && "No variable available for capture");
|
||||||
return cast<VarDecl>(DeclAndBits.getPointer());
|
return static_cast<VarDecl *>(CapturedEntityAndBits.getPointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Determine whether this was an implicit capture (not
|
/// \brief Determine whether this was an implicit capture (not
|
||||||
/// written between the square brackets introducing the lambda).
|
/// 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
|
/// \brief Determine whether this was an explicit capture (written
|
||||||
/// between the square brackets introducing the lambda).
|
/// between the square brackets introducing the lambda).
|
||||||
|
|
|
@ -766,6 +766,9 @@ def warn_cxx98_compat_lambda : Warning<
|
||||||
def err_lambda_missing_parens : Error<
|
def err_lambda_missing_parens : Error<
|
||||||
"lambda requires '()' before %select{'mutable'|return type|"
|
"lambda requires '()' before %select{'mutable'|return type|"
|
||||||
"attribute specifier}0">;
|
"attribute specifier}0">;
|
||||||
|
// C++1z lambda expressions
|
||||||
|
def err_expected_star_this_capture : Error<
|
||||||
|
"expected 'this' following '*' in lambda capture list">;
|
||||||
|
|
||||||
// Availability attribute
|
// Availability attribute
|
||||||
def err_expected_version : Error<
|
def err_expected_version : Error<
|
||||||
|
|
|
@ -5991,6 +5991,13 @@ let CategoryName = "Lambda Issue" in {
|
||||||
"cannot deduce type for lambda capture %0 from initializer of type %2">;
|
"cannot deduce type for lambda capture %0 from initializer of type %2">;
|
||||||
def err_init_capture_deduction_failure_from_init_list : Error<
|
def err_init_capture_deduction_failure_from_init_list : Error<
|
||||||
"cannot deduce type for lambda capture %0 from initializer list">;
|
"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<
|
def err_return_in_captured_stmt : Error<
|
||||||
|
|
|
@ -32,7 +32,8 @@ enum LambdaCaptureDefault {
|
||||||
/// by reference. C++1y also allows "init-capture", where the initializer
|
/// by reference. C++1y also allows "init-capture", where the initializer
|
||||||
/// is an expression.
|
/// is an expression.
|
||||||
enum LambdaCaptureKind {
|
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_ByCopy, ///< Capturing by copy (a.k.a., by value)
|
||||||
LCK_ByRef, ///< Capturing by reference
|
LCK_ByRef, ///< Capturing by reference
|
||||||
LCK_VLAType ///< Capturing variable-length array type
|
LCK_VLAType ///< Capturing variable-length array type
|
||||||
|
|
|
@ -420,18 +420,20 @@ public:
|
||||||
// variables of reference type are captured by reference, and other
|
// variables of reference type are captured by reference, and other
|
||||||
// variables are captured by copy.
|
// variables are captured by copy.
|
||||||
enum CaptureKind {
|
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
|
/// 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
|
/// 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
|
/// is only required if we are capturing ByVal and the variable's type has
|
||||||
/// a non-trivial copy constructor.
|
/// a non-trivial copy constructor.
|
||||||
llvm::PointerIntPair<void *, 2, CaptureKind> InitExprAndCaptureKind;
|
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.
|
/// \brief The source location at which the first capture occurred.
|
||||||
SourceLocation Loc;
|
SourceLocation Loc;
|
||||||
|
@ -447,27 +449,28 @@ public:
|
||||||
Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested,
|
Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested,
|
||||||
SourceLocation Loc, SourceLocation EllipsisLoc,
|
SourceLocation Loc, SourceLocation EllipsisLoc,
|
||||||
QualType CaptureType, Expr *Cpy)
|
QualType CaptureType, Expr *Cpy)
|
||||||
: VarAndNested(Var, IsNested),
|
: VarAndNestedAndThis(Var, IsNested ? IsNestedCapture : 0),
|
||||||
InitExprAndCaptureKind(Cpy, Block ? Cap_Block :
|
InitExprAndCaptureKind(
|
||||||
ByRef ? Cap_ByRef : Cap_ByCopy),
|
Cpy, !Var ? Cap_VLA : Block ? Cap_Block : ByRef ? Cap_ByRef
|
||||||
|
: Cap_ByCopy),
|
||||||
Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {}
|
Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {}
|
||||||
|
|
||||||
enum IsThisCapture { ThisCapture };
|
enum IsThisCapture { ThisCapture };
|
||||||
Capture(IsThisCapture, bool IsNested, SourceLocation Loc,
|
Capture(IsThisCapture, bool IsNested, SourceLocation Loc,
|
||||||
QualType CaptureType, Expr *Cpy)
|
QualType CaptureType, Expr *Cpy, const bool ByCopy)
|
||||||
: VarAndNested(nullptr, IsNested),
|
: VarAndNestedAndThis(
|
||||||
InitExprAndCaptureKind(Cpy, Cap_This),
|
nullptr, (IsThisCaptured | (IsNested ? IsNestedCapture : 0))),
|
||||||
|
InitExprAndCaptureKind(Cpy, ByCopy ? Cap_ByCopy : Cap_ByRef),
|
||||||
Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {}
|
Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {}
|
||||||
|
|
||||||
bool isThisCapture() const {
|
bool isThisCapture() const {
|
||||||
return InitExprAndCaptureKind.getInt() == Cap_This;
|
return VarAndNestedAndThis.getInt() & IsThisCaptured;
|
||||||
}
|
}
|
||||||
bool isVariableCapture() const {
|
bool isVariableCapture() const {
|
||||||
return InitExprAndCaptureKind.getInt() != Cap_This && !isVLATypeCapture();
|
return !isThisCapture() && !isVLATypeCapture();
|
||||||
}
|
}
|
||||||
bool isCopyCapture() const {
|
bool isCopyCapture() const {
|
||||||
return InitExprAndCaptureKind.getInt() == Cap_ByCopy &&
|
return InitExprAndCaptureKind.getInt() == Cap_ByCopy;
|
||||||
!isVLATypeCapture();
|
|
||||||
}
|
}
|
||||||
bool isReferenceCapture() const {
|
bool isReferenceCapture() const {
|
||||||
return InitExprAndCaptureKind.getInt() == Cap_ByRef;
|
return InitExprAndCaptureKind.getInt() == Cap_ByRef;
|
||||||
|
@ -476,13 +479,14 @@ public:
|
||||||
return InitExprAndCaptureKind.getInt() == Cap_Block;
|
return InitExprAndCaptureKind.getInt() == Cap_Block;
|
||||||
}
|
}
|
||||||
bool isVLATypeCapture() const {
|
bool isVLATypeCapture() const {
|
||||||
return InitExprAndCaptureKind.getInt() == Cap_ByCopy &&
|
return InitExprAndCaptureKind.getInt() == Cap_VLA;
|
||||||
getVariable() == nullptr;
|
}
|
||||||
|
bool isNested() const {
|
||||||
|
return VarAndNestedAndThis.getInt() & IsNestedCapture;
|
||||||
}
|
}
|
||||||
bool isNested() const { return VarAndNested.getInt(); }
|
|
||||||
|
|
||||||
VarDecl *getVariable() const {
|
VarDecl *getVariable() const {
|
||||||
return VarAndNested.getPointer();
|
return VarAndNestedAndThis.getPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Retrieve the location at which this variable was captured.
|
/// \brief Retrieve the location at which this variable was captured.
|
||||||
|
@ -542,7 +546,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType,
|
void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType,
|
||||||
Expr *Cpy);
|
Expr *Cpy, bool ByCopy);
|
||||||
|
|
||||||
/// \brief Determine whether the C++ 'this' is captured.
|
/// \brief Determine whether the C++ 'this' is captured.
|
||||||
bool isCXXThisCaptured() const { return CXXThisCaptureIndex != 0; }
|
bool isCXXThisCaptured() const { return CXXThisCaptureIndex != 0; }
|
||||||
|
@ -862,9 +866,10 @@ void FunctionScopeInfo::recordUseOfWeak(const ExprT *E, bool IsRead) {
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
CapturingScopeInfo::addThisCapture(bool isNested, SourceLocation Loc,
|
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,
|
Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, CaptureType,
|
||||||
Cpy));
|
Cpy, ByCopy));
|
||||||
CXXThisCaptureIndex = Captures.size();
|
CXXThisCaptureIndex = Captures.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4623,7 +4623,8 @@ public:
|
||||||
/// \return returns 'true' if failed, 'false' if success.
|
/// \return returns 'true' if failed, 'false' if success.
|
||||||
bool CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false,
|
bool CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false,
|
||||||
bool BuildAndDiagnose = true,
|
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
|
/// \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
|
/// outside of the body of a member function for a type that is currently
|
||||||
|
|
|
@ -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,
|
LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit,
|
||||||
LambdaCaptureKind Kind, VarDecl *Var,
|
LambdaCaptureKind Kind, VarDecl *Var,
|
||||||
SourceLocation EllipsisLoc)
|
SourceLocation EllipsisLoc)
|
||||||
: DeclAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc)
|
: CapturedEntityAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc)
|
||||||
{
|
{
|
||||||
unsigned Bits = 0;
|
unsigned Bits = 0;
|
||||||
if (Implicit)
|
if (Implicit)
|
||||||
Bits |= Capture_Implicit;
|
Bits |= Capture_Implicit;
|
||||||
|
|
||||||
switch (Kind) {
|
switch (Kind) {
|
||||||
|
case LCK_StarThis:
|
||||||
|
Bits |= Capture_ByCopy;
|
||||||
|
// Fall through
|
||||||
case LCK_This:
|
case LCK_This:
|
||||||
assert(!Var && "'this' capture cannot have a variable!");
|
assert(!Var && "'this' capture cannot have a variable!");
|
||||||
|
CapturedEntityAndBits.setPointer(&ThisSentinel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LCK_ByCopy:
|
case LCK_ByCopy:
|
||||||
|
@ -901,18 +908,20 @@ LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit,
|
||||||
break;
|
break;
|
||||||
case LCK_VLAType:
|
case LCK_VLAType:
|
||||||
assert(!Var && "VLA type capture cannot have a variable!");
|
assert(!Var && "VLA type capture cannot have a variable!");
|
||||||
Bits |= Capture_ByCopy;
|
CapturedEntityAndBits.setPointer(&VLASentinel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DeclAndBits.setInt(Bits);
|
CapturedEntityAndBits.setInt(Bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaCaptureKind LambdaCapture::getCaptureKind() const {
|
LambdaCaptureKind LambdaCapture::getCaptureKind() const {
|
||||||
Decl *D = DeclAndBits.getPointer();
|
void *Ptr = CapturedEntityAndBits.getPointer();
|
||||||
bool CapByCopy = DeclAndBits.getInt() & Capture_ByCopy;
|
if (Ptr == &VLASentinel)
|
||||||
if (!D)
|
return LCK_VLAType;
|
||||||
return CapByCopy ? LCK_VLAType : LCK_This;
|
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;
|
return CapByCopy ? LCK_ByCopy : LCK_ByRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2008,7 +2008,9 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
|
||||||
case LCK_This:
|
case LCK_This:
|
||||||
OS << "this";
|
OS << "this";
|
||||||
break;
|
break;
|
||||||
|
case LCK_StarThis:
|
||||||
|
OS << "*this";
|
||||||
|
break;
|
||||||
case LCK_ByRef:
|
case LCK_ByRef:
|
||||||
if (Node->getCaptureDefault() != LCD_ByRef || Node->isInitCapture(C))
|
if (Node->getCaptureDefault() != LCD_ByRef || Node->isInitCapture(C))
|
||||||
OS << '&';
|
OS << '&';
|
||||||
|
|
|
@ -1258,6 +1258,7 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
|
||||||
C != CEnd; ++C) {
|
C != CEnd; ++C) {
|
||||||
ID.AddInteger(C->getCaptureKind());
|
ID.AddInteger(C->getCaptureKind());
|
||||||
switch (C->getCaptureKind()) {
|
switch (C->getCaptureKind()) {
|
||||||
|
case LCK_StarThis:
|
||||||
case LCK_This:
|
case LCK_This:
|
||||||
break;
|
break;
|
||||||
case LCK_ByRef:
|
case LCK_ByRef:
|
||||||
|
|
|
@ -823,10 +823,22 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
|
||||||
MD->getParent()->getCaptureFields(LambdaCaptureFields,
|
MD->getParent()->getCaptureFields(LambdaCaptureFields,
|
||||||
LambdaThisCaptureField);
|
LambdaThisCaptureField);
|
||||||
if (LambdaThisCaptureField) {
|
if (LambdaThisCaptureField) {
|
||||||
// If this lambda captures this, load it.
|
// If the lambda captures the object referred to by '*this' - either by
|
||||||
LValue ThisLValue = EmitLValueForLambdaField(LambdaThisCaptureField);
|
// value or by reference, make sure CXXThisValue points to the correct
|
||||||
CXXThisValue = EmitLoadOfLValue(ThisLValue,
|
// object.
|
||||||
SourceLocation()).getScalarVal();
|
|
||||||
|
// 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()) {
|
for (auto *FD : MD->getParent()->fields()) {
|
||||||
if (FD->hasCapturedVLAType()) {
|
if (FD->hasCapturedVLAType()) {
|
||||||
|
|
|
@ -847,7 +847,15 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
|
||||||
SourceLocation EllipsisLoc;
|
SourceLocation EllipsisLoc;
|
||||||
ExprResult Init;
|
ExprResult Init;
|
||||||
|
|
||||||
|
if (Tok.is(tok::star)) {
|
||||||
|
Loc = ConsumeToken();
|
||||||
if (Tok.is(tok::kw_this)) {
|
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;
|
Kind = LCK_This;
|
||||||
Loc = ConsumeToken();
|
Loc = ConsumeToken();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11010,7 +11010,8 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
|
||||||
|
|
||||||
} else if (C.capturesThis()) {
|
} else if (C.capturesThis()) {
|
||||||
LSI->addThisCapture(/*Nested*/ false, C.getLocation(),
|
LSI->addThisCapture(/*Nested*/ false, C.getLocation(),
|
||||||
S.getCurrentThisType(), /*Expr*/ nullptr);
|
S.getCurrentThisType(), /*Expr*/ nullptr,
|
||||||
|
C.getCaptureKind() == LCK_StarThis);
|
||||||
} else {
|
} else {
|
||||||
LSI->addVLATypeCapture(C.getLocation(), I->getType());
|
LSI->addVLATypeCapture(C.getLocation(), I->getType());
|
||||||
}
|
}
|
||||||
|
|
|
@ -13249,6 +13249,7 @@ static bool captureInCapturedRegion(CapturedRegionScopeInfo *RSI,
|
||||||
|
|
||||||
/// \brief Create a field within the lambda class for the variable
|
/// \brief Create a field within the lambda class for the variable
|
||||||
/// being captured.
|
/// being captured.
|
||||||
|
// FIXME: Delete VarDecl *Var below, it is not used in the function.
|
||||||
static void addAsFieldToClosureType(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var,
|
static void addAsFieldToClosureType(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var,
|
||||||
QualType FieldType, QualType DeclRefType,
|
QualType FieldType, QualType DeclRefType,
|
||||||
SourceLocation Loc,
|
SourceLocation Loc,
|
||||||
|
|
|
@ -845,11 +845,34 @@ QualType Sema::getCurrentThisType() {
|
||||||
// within a default initializer - so use the enclosing class as 'this'.
|
// within a default initializer - so use the enclosing class as 'this'.
|
||||||
// There is no enclosing member function to retrieve the 'this' pointer
|
// There is no enclosing member function to retrieve the 'this' pointer
|
||||||
// from.
|
// 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(
|
QualType ClassTy = Context.getTypeDeclType(
|
||||||
cast<CXXRecordDecl>(CurContext->getParent()->getParent()));
|
cast<CXXRecordDecl>(CurContext->getParent()->getParent()));
|
||||||
// There are no cv-qualifiers for 'this' within default initializers,
|
// There are no cv-qualifiers for 'this' within default initializers,
|
||||||
// per [expr.prim.general]p4.
|
// 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;
|
return ThisTy;
|
||||||
|
@ -884,28 +907,70 @@ Sema::CXXThisScopeRAII::~CXXThisScopeRAII() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expr *captureThis(ASTContext &Context, RecordDecl *RD,
|
static Expr *captureThis(Sema &S, ASTContext &Context, RecordDecl *RD,
|
||||||
QualType ThisTy, SourceLocation Loc) {
|
QualType ThisTy, SourceLocation Loc,
|
||||||
|
const bool ByCopy) {
|
||||||
|
QualType CaptureThisTy = ByCopy ? ThisTy->getPointeeType() : ThisTy;
|
||||||
|
|
||||||
FieldDecl *Field
|
FieldDecl *Field
|
||||||
= FieldDecl::Create(Context, RD, Loc, Loc, nullptr, ThisTy,
|
= FieldDecl::Create(Context, RD, Loc, Loc, nullptr, CaptureThisTy,
|
||||||
Context.getTrivialTypeSourceInfo(ThisTy, Loc),
|
Context.getTrivialTypeSourceInfo(CaptureThisTy, Loc),
|
||||||
nullptr, false, ICIS_NoInit);
|
nullptr, false, ICIS_NoInit);
|
||||||
Field->setImplicit(true);
|
Field->setImplicit(true);
|
||||||
Field->setAccess(AS_private);
|
Field->setAccess(AS_private);
|
||||||
RD->addDecl(Field);
|
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 Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
|
||||||
bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt) {
|
bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt,
|
||||||
|
const bool ByCopy) {
|
||||||
// We don't need to capture this in an unevaluated context.
|
// We don't need to capture this in an unevaluated context.
|
||||||
if (isUnevaluatedContext() && !Explicit)
|
if (isUnevaluatedContext() && !Explicit)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
assert((!ByCopy || Explicit) && "cannot implicitly capture *this by value");
|
||||||
|
|
||||||
const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt ?
|
const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt ?
|
||||||
*FunctionScopeIndexToStopAt : FunctionScopes.size() - 1;
|
*FunctionScopeIndexToStopAt : FunctionScopes.size() - 1;
|
||||||
// Otherwise, check that we can capture 'this'.
|
|
||||||
unsigned NumClosures = 0;
|
// 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--) {
|
for (unsigned idx = MaxFunctionScopesIndex; idx != 0; idx--) {
|
||||||
if (CapturingScopeInfo *CSI =
|
if (CapturingScopeInfo *CSI =
|
||||||
dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) {
|
dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) {
|
||||||
|
@ -917,44 +982,69 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit,
|
||||||
if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) {
|
if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) {
|
||||||
// This context can't implicitly capture 'this'; fail out.
|
// This context can't implicitly capture 'this'; fail out.
|
||||||
if (BuildAndDiagnose)
|
if (BuildAndDiagnose)
|
||||||
Diag(Loc, diag::err_this_capture) << Explicit;
|
Diag(Loc, diag::err_this_capture)
|
||||||
|
<< (Explicit && idx == MaxFunctionScopesIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref ||
|
if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref ||
|
||||||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval ||
|
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval ||
|
||||||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block ||
|
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block ||
|
||||||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion ||
|
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.
|
// This closure can capture 'this'; continue looking upwards.
|
||||||
NumClosures++;
|
NumCapturingClosures++;
|
||||||
Explicit = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// This context can't implicitly capture 'this'; fail out.
|
// This context can't implicitly capture 'this'; fail out.
|
||||||
if (BuildAndDiagnose)
|
if (BuildAndDiagnose)
|
||||||
Diag(Loc, diag::err_this_capture) << Explicit;
|
Diag(Loc, diag::err_this_capture)
|
||||||
|
<< (Explicit && idx == MaxFunctionScopesIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!BuildAndDiagnose) return false;
|
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
|
// FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated
|
||||||
// contexts.
|
// contexts.
|
||||||
for (unsigned idx = MaxFunctionScopesIndex; NumClosures;
|
|
||||||
--idx, --NumClosures) {
|
for (unsigned idx = MaxFunctionScopesIndex; NumCapturingClosures;
|
||||||
|
--idx, --NumCapturingClosures) {
|
||||||
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
|
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
|
||||||
Expr *ThisExpr = nullptr;
|
Expr *ThisExpr = nullptr;
|
||||||
QualType ThisTy = getCurrentThisType();
|
QualType ThisTy = getCurrentThisType();
|
||||||
if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI))
|
if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
|
||||||
// For lambda expressions, build a field and an initializing expression.
|
// For lambda expressions, build a field and an initializing expression,
|
||||||
ThisExpr = captureThis(Context, LSI->Lambda, ThisTy, Loc);
|
// and capture the *enclosing object* by copy only if this is the first
|
||||||
else if (CapturedRegionScopeInfo *RSI
|
// iteration.
|
||||||
= dyn_cast<CapturedRegionScopeInfo>(FunctionScopes[idx]))
|
ThisExpr = captureThis(*this, Context, LSI->Lambda, ThisTy, Loc,
|
||||||
ThisExpr = captureThis(Context, RSI->TheRecordDecl, ThisTy, Loc);
|
ByCopy && idx == MaxFunctionScopesIndex);
|
||||||
|
|
||||||
bool isNested = NumClosures > 1;
|
} else if (CapturedRegionScopeInfo *RSI
|
||||||
CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr);
|
= dyn_cast<CapturedRegionScopeInfo>(FunctionScopes[idx]))
|
||||||
|
ThisExpr =
|
||||||
|
captureThis(*this, Context, RSI->TheRecordDecl, ThisTy, Loc,
|
||||||
|
false/*ByCopy*/);
|
||||||
|
|
||||||
|
bool isNested = NumCapturingClosures > 1;
|
||||||
|
CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr, ByCopy);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -924,7 +924,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
||||||
= Intro.Default == LCD_None? Intro.Range.getBegin() : Intro.DefaultLoc;
|
= Intro.Default == LCD_None? Intro.Range.getBegin() : Intro.DefaultLoc;
|
||||||
for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E;
|
for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E;
|
||||||
PrevCaptureLoc = C->Loc, ++C) {
|
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:
|
// C++11 [expr.prim.lambda]p8:
|
||||||
// An identifier or this shall not appear more than once in a
|
// An identifier or this shall not appear more than once in a
|
||||||
// lambda-capture.
|
// lambda-capture.
|
||||||
|
@ -936,10 +941,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// C++11 [expr.prim.lambda]p8:
|
// C++1z [expr.prim.lambda]p8:
|
||||||
// If a lambda-capture includes a capture-default that is =, the
|
// If a lambda-capture includes a capture-default that is =, each
|
||||||
// lambda-capture shall not contain this [...].
|
// simple-capture of that lambda-capture shall be of the form "&
|
||||||
if (Intro.Default == LCD_ByCopy) {
|
// 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)
|
Diag(C->Loc, diag::err_this_capture_with_copy_default)
|
||||||
<< FixItHint::CreateRemoval(
|
<< FixItHint::CreateRemoval(
|
||||||
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
|
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
|
||||||
|
@ -955,7 +962,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckCXXThisCapture(C->Loc, /*Explicit=*/true);
|
CheckCXXThisCapture(C->Loc, /*Explicit=*/true, /*BuildAndDiagnose*/ true,
|
||||||
|
/*FunctionScopeIndexToStopAtPtr*/ nullptr,
|
||||||
|
C->Kind == LCK_StarThis);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1529,10 +1538,9 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
|
||||||
// Handle 'this' capture.
|
// Handle 'this' capture.
|
||||||
if (From.isThisCapture()) {
|
if (From.isThisCapture()) {
|
||||||
Captures.push_back(
|
Captures.push_back(
|
||||||
LambdaCapture(From.getLocation(), IsImplicit, LCK_This));
|
LambdaCapture(From.getLocation(), IsImplicit,
|
||||||
CaptureInits.push_back(new (Context) CXXThisExpr(From.getLocation(),
|
From.isCopyCapture() ? LCK_StarThis : LCK_This));
|
||||||
getCurrentThisType(),
|
CaptureInits.push_back(From.getInitExpr());
|
||||||
/*isImplicit=*/true));
|
|
||||||
ArrayIndexStarts.push_back(ArrayIndexVars.size());
|
ArrayIndexStarts.push_back(ArrayIndexVars.size());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10089,7 +10089,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||||
|
|
||||||
// Capturing 'this' is trivial.
|
// Capturing 'this' is trivial.
|
||||||
if (C->capturesThis()) {
|
if (C->capturesThis()) {
|
||||||
getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit());
|
getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit(),
|
||||||
|
/*BuildAndDiagnose*/ true, nullptr,
|
||||||
|
C->getCaptureKind() == LCK_StarThis);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Captured expression will be recaptured during captured variables
|
// Captured expression will be recaptured during captured variables
|
||||||
|
|
|
@ -1497,6 +1497,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
|
||||||
bool IsImplicit = Record[Idx++];
|
bool IsImplicit = Record[Idx++];
|
||||||
LambdaCaptureKind Kind = static_cast<LambdaCaptureKind>(Record[Idx++]);
|
LambdaCaptureKind Kind = static_cast<LambdaCaptureKind>(Record[Idx++]);
|
||||||
switch (Kind) {
|
switch (Kind) {
|
||||||
|
case LCK_StarThis:
|
||||||
case LCK_This:
|
case LCK_This:
|
||||||
case LCK_VLAType:
|
case LCK_VLAType:
|
||||||
*ToCapture++ = Capture(Loc, IsImplicit, Kind, nullptr,SourceLocation());
|
*ToCapture++ = Capture(Loc, IsImplicit, Kind, nullptr,SourceLocation());
|
||||||
|
|
|
@ -5631,6 +5631,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
|
||||||
Record.push_back(Capture.isImplicit());
|
Record.push_back(Capture.isImplicit());
|
||||||
Record.push_back(Capture.getCaptureKind());
|
Record.push_back(Capture.getCaptureKind());
|
||||||
switch (Capture.getCaptureKind()) {
|
switch (Capture.getCaptureKind()) {
|
||||||
|
case LCK_StarThis:
|
||||||
case LCK_This:
|
case LCK_This:
|
||||||
case LCK_VLAType:
|
case LCK_VLAType:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
|
@ -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
|
|
@ -659,7 +659,7 @@ as the draft C++1z standard evolves.</p>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Lambda capture of <tt>*this</tt></td>
|
<td>Lambda capture of <tt>*this</tt></td>
|
||||||
<td><a href="http://wg21.link/p0018r3">P0018R3</a></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>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Direct-list-initialization of <tt>enum</tt>s</td>
|
<td>Direct-list-initialization of <tt>enum</tt>s</td>
|
||||||
|
|
Loading…
Reference in New Issue