142 changes: 116 additions & 26 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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])) {
Expand All @@ -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;
}
Expand Down
28 changes: 18 additions & 10 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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));
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
};
};
}
};
31 changes: 31 additions & 0 deletions clang/test/CodeGenCXX/cxx1z-lambda-star-this.cpp
Original file line number Diff line number Diff line change
@@ -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
72 changes: 72 additions & 0 deletions clang/test/SemaCXX/cxx1z-lambda-star-this.cpp
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ <h2 id="cxx17">C++1z implementation status</h2>
<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>
Expand Down