diff options
author | Faisal Vali <faisalv@yahoo.com> | 2016-03-21 09:25:37 +0000 |
---|---|---|
committer | Faisal Vali <faisalv@yahoo.com> | 2016-03-21 09:25:37 +0000 |
commit | dc6b596ebbd3c594c75fc6c6bc6cb7c14fdcec24 (patch) | |
tree | 22cb8f82b4795afc128f86720b6ca4f148211e8f /clang/lib/Sema | |
parent | 2c8b5993bebd0b5523e254a06f82322c46f1221b (diff) | |
download | bcm5719-llvm-dc6b596ebbd3c594c75fc6c6bc6cb7c14fdcec24.tar.gz bcm5719-llvm-dc6b596ebbd3c594c75fc6c6bc6cb7c14fdcec24.zip |
[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
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 142 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLambda.cpp | 28 | ||||
-rw-r--r-- | clang/lib/Sema/TreeTransform.h | 4 |
5 files changed, 140 insertions, 38 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d8740d2beb1..6b1182fa4cb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11010,7 +11010,8 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator, } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), - S.getCurrentThisType(), /*Expr*/ nullptr); + S.getCurrentThisType(), /*Expr*/ nullptr, + C.getCaptureKind() == LCK_StarThis); } else { LSI->addVLATypeCapture(C.getLocation(), I->getType()); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 324556e8620..4e79b9e93ed 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13249,6 +13249,7 @@ static bool captureInCapturedRegion(CapturedRegionScopeInfo *RSI, /// \brief Create a field within the lambda class for the variable /// being captured. +// FIXME: Delete VarDecl *Var below, it is not used in the function. static void addAsFieldToClosureType(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var, QualType FieldType, QualType DeclRefType, SourceLocation Loc, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 709718f26c8..129969469ab 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -845,11 +845,34 @@ QualType Sema::getCurrentThisType() { // within a default initializer - so use the enclosing class as 'this'. // There is no enclosing member function to retrieve the 'this' pointer // from. + + // FIXME: This looks wrong. If we're in a lambda within a lambda within a + // default member initializer, we need to recurse up more parents to find + // the right context. Looks like we should be walking up to the parent of + // the closure type, checking whether that is itself a lambda, and if so, + // recursing, until we reach a class or a function that isn't a lambda + // call operator. And we should accumulate the constness of *this on the + // way. + QualType ClassTy = Context.getTypeDeclType( cast<CXXRecordDecl>(CurContext->getParent()->getParent())); // There are no cv-qualifiers for 'this' within default initializers, // per [expr.prim.general]p4. - return Context.getPointerType(ClassTy); + ThisTy = Context.getPointerType(ClassTy); + } + } + // Add const for '* this' capture if not mutable. + if (isLambdaCallOperator(CurContext)) { + LambdaScopeInfo *LSI = getCurLambda(); + assert(LSI); + if (LSI->isCXXThisCaptured()) { + auto C = LSI->getCXXThisCapture(); + QualType BaseType = ThisTy->getPointeeType(); + if ((C.isThisCapture() && C.isCopyCapture()) && + LSI->CallOperator->isConst() && !BaseType.isConstQualified()) { + BaseType.addConst(); + ThisTy = Context.getPointerType(BaseType); + } } } return ThisTy; @@ -884,28 +907,70 @@ Sema::CXXThisScopeRAII::~CXXThisScopeRAII() { } } -static Expr *captureThis(ASTContext &Context, RecordDecl *RD, - QualType ThisTy, SourceLocation Loc) { +static Expr *captureThis(Sema &S, ASTContext &Context, RecordDecl *RD, + QualType ThisTy, SourceLocation Loc, + const bool ByCopy) { + QualType CaptureThisTy = ByCopy ? ThisTy->getPointeeType() : ThisTy; + FieldDecl *Field - = FieldDecl::Create(Context, RD, Loc, Loc, nullptr, ThisTy, - Context.getTrivialTypeSourceInfo(ThisTy, Loc), + = FieldDecl::Create(Context, RD, Loc, Loc, nullptr, CaptureThisTy, + Context.getTrivialTypeSourceInfo(CaptureThisTy, Loc), nullptr, false, ICIS_NoInit); Field->setImplicit(true); Field->setAccess(AS_private); RD->addDecl(Field); - return new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); + Expr *This = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); + if (ByCopy) { + Expr *StarThis = S.CreateBuiltinUnaryOp(Loc, + UO_Deref, + This).get(); + InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture( + nullptr, CaptureThisTy, Loc); + InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc); + InitializationSequence Init(S, Entity, InitKind, StarThis); + ExprResult ER = Init.Perform(S, Entity, InitKind, StarThis); + if (ER.isInvalid()) return nullptr; + return ER.get(); + } + return This; } -bool Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit, - bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt) { +bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, + bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt, + const bool ByCopy) { // We don't need to capture this in an unevaluated context. if (isUnevaluatedContext() && !Explicit) return true; + + assert((!ByCopy || Explicit) && "cannot implicitly capture *this by value"); const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt ? - *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; - // Otherwise, check that we can capture 'this'. - unsigned NumClosures = 0; + *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; + + // Check that we can capture the *enclosing object* (referred to by '*this') + // by the capturing-entity/closure (lambda/block/etc) at + // MaxFunctionScopesIndex-deep on the FunctionScopes stack. + + // Note: The *enclosing object* can only be captured by-value by a + // closure that is a lambda, using the explicit notation: + // [*this] { ... }. + // Every other capture of the *enclosing object* results in its by-reference + // capture. + + // For a closure 'L' (at MaxFunctionScopesIndex in the FunctionScopes + // stack), we can capture the *enclosing object* only if: + // - 'L' has an explicit byref or byval capture of the *enclosing object* + // - or, 'L' has an implicit capture. + // AND + // -- there is no enclosing closure + // -- or, there is some enclosing closure 'E' that has already captured the + // *enclosing object*, and every intervening closure (if any) between 'E' + // and 'L' can implicitly capture the *enclosing object*. + // -- or, every enclosing closure can implicitly capture the + // *enclosing object* + + + unsigned NumCapturingClosures = 0; for (unsigned idx = MaxFunctionScopesIndex; idx != 0; idx--) { if (CapturingScopeInfo *CSI = dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) { @@ -917,44 +982,69 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit, if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) { // This context can't implicitly capture 'this'; fail out. if (BuildAndDiagnose) - Diag(Loc, diag::err_this_capture) << Explicit; + Diag(Loc, diag::err_this_capture) + << (Explicit && idx == MaxFunctionScopesIndex); return true; } if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion || - Explicit) { + (Explicit && idx == MaxFunctionScopesIndex)) { + // Regarding (Explicit && idx == MaxFunctionScopesIndex): only the first + // iteration through can be an explicit capture, all enclosing closures, + // if any, must perform implicit captures. + // This closure can capture 'this'; continue looking upwards. - NumClosures++; - Explicit = false; + NumCapturingClosures++; continue; } // This context can't implicitly capture 'this'; fail out. if (BuildAndDiagnose) - Diag(Loc, diag::err_this_capture) << Explicit; + Diag(Loc, diag::err_this_capture) + << (Explicit && idx == MaxFunctionScopesIndex); return true; } break; } if (!BuildAndDiagnose) return false; - // Mark that we're implicitly capturing 'this' in all the scopes we skipped. + + // If we got here, then the closure at MaxFunctionScopesIndex on the + // FunctionScopes stack, can capture the *enclosing object*, so capture it + // (including implicit by-reference captures in any enclosing closures). + + // In the loop below, respect the ByCopy flag only for the closure requesting + // the capture (i.e. first iteration through the loop below). Ignore it for + // all enclosing closure's upto NumCapturingClosures (since they must be + // implicitly capturing the *enclosing object* by reference (see loop + // above)). + assert((!ByCopy || + dyn_cast<LambdaScopeInfo>(FunctionScopes[MaxFunctionScopesIndex])) && + "Only a lambda can capture the enclosing object (referred to by " + "*this) by copy"); // FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated // contexts. - for (unsigned idx = MaxFunctionScopesIndex; NumClosures; - --idx, --NumClosures) { + + for (unsigned idx = MaxFunctionScopesIndex; NumCapturingClosures; + --idx, --NumCapturingClosures) { CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]); Expr *ThisExpr = nullptr; QualType ThisTy = getCurrentThisType(); - if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) - // For lambda expressions, build a field and an initializing expression. - ThisExpr = captureThis(Context, LSI->Lambda, ThisTy, Loc); - else if (CapturedRegionScopeInfo *RSI + if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) { + // For lambda expressions, build a field and an initializing expression, + // and capture the *enclosing object* by copy only if this is the first + // iteration. + ThisExpr = captureThis(*this, Context, LSI->Lambda, ThisTy, Loc, + ByCopy && idx == MaxFunctionScopesIndex); + + } else if (CapturedRegionScopeInfo *RSI = dyn_cast<CapturedRegionScopeInfo>(FunctionScopes[idx])) - ThisExpr = captureThis(Context, RSI->TheRecordDecl, ThisTy, Loc); + ThisExpr = + captureThis(*this, Context, RSI->TheRecordDecl, ThisTy, Loc, + false/*ByCopy*/); - bool isNested = NumClosures > 1; - CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr); + bool isNested = NumCapturingClosures > 1; + CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr, ByCopy); } return false; } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 1a62f0dfcba..0f5eb413526 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -924,7 +924,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, = Intro.Default == LCD_None? Intro.Range.getBegin() : Intro.DefaultLoc; for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; PrevCaptureLoc = C->Loc, ++C) { - if (C->Kind == LCK_This) { + if (C->Kind == LCK_This || C->Kind == LCK_StarThis) { + if (C->Kind == LCK_StarThis) + Diag(C->Loc, !getLangOpts().CPlusPlus1z + ? diag::ext_star_this_lambda_capture_cxx1z + : diag::warn_cxx14_compat_star_this_lambda_capture); + // C++11 [expr.prim.lambda]p8: // An identifier or this shall not appear more than once in a // lambda-capture. @@ -936,10 +941,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, continue; } - // C++11 [expr.prim.lambda]p8: - // If a lambda-capture includes a capture-default that is =, the - // lambda-capture shall not contain this [...]. - if (Intro.Default == LCD_ByCopy) { + // C++1z [expr.prim.lambda]p8: + // If a lambda-capture includes a capture-default that is =, each + // simple-capture of that lambda-capture shall be of the form "& + // identifier" or "* this". [ Note: The form [&,this] is redundant but + // accepted for compatibility with ISO C++14. --end note ] + if (Intro.Default == LCD_ByCopy && C->Kind != LCK_StarThis) { Diag(C->Loc, diag::err_this_capture_with_copy_default) << FixItHint::CreateRemoval( SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc)); @@ -955,7 +962,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, continue; } - CheckCXXThisCapture(C->Loc, /*Explicit=*/true); + CheckCXXThisCapture(C->Loc, /*Explicit=*/true, /*BuildAndDiagnose*/ true, + /*FunctionScopeIndexToStopAtPtr*/ nullptr, + C->Kind == LCK_StarThis); continue; } @@ -1529,10 +1538,9 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, // Handle 'this' capture. if (From.isThisCapture()) { Captures.push_back( - LambdaCapture(From.getLocation(), IsImplicit, LCK_This)); - CaptureInits.push_back(new (Context) CXXThisExpr(From.getLocation(), - getCurrentThisType(), - /*isImplicit=*/true)); + LambdaCapture(From.getLocation(), IsImplicit, + From.isCopyCapture() ? LCK_StarThis : LCK_This)); + CaptureInits.push_back(From.getInitExpr()); ArrayIndexStarts.push_back(ArrayIndexVars.size()); continue; } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index fc201292146..7ed52ebb12a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -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 |