diff options
| author | Faisal Vali <faisalv@yahoo.com> | 2016-06-11 16:41:54 +0000 |
|---|---|---|
| committer | Faisal Vali <faisalv@yahoo.com> | 2016-06-11 16:41:54 +0000 |
| commit | 67b04465c00e0be4fe0b6b30f27f3112d5ee875f (patch) | |
| tree | 6561d7561c55c96c4cc7afd01f83b849d793e269 /clang/lib | |
| parent | c702b8b3d76fe74db5fa4bf0e84858d83b16e619 (diff) | |
| download | bcm5719-llvm-67b04465c00e0be4fe0b6b30f27f3112d5ee875f.tar.gz bcm5719-llvm-67b04465c00e0be4fe0b6b30f27f3112d5ee875f.zip | |
Fix cv-qualification of '*this' captures and nasty bug PR27507
The bug report by Gonzalo (https://llvm.org/bugs/show_bug.cgi?id=27507 -- which results in clang crashing when generic lambdas that capture 'this' are instantiated in contexts where the Functionscopeinfo stack is not in a reliable state - yet getCurrentThisType expects it to be) - unearthed some additional bugs in regards to maintaining proper cv qualification through 'this' when performing by value captures of '*this'.
This patch attempts to correct those bugs and makes the following changes:
o) when capturing 'this', we do not need to remember the type of 'this' within the LambdaScopeInfo's Capture - it is never really used for a this capture - so remove it.
o) teach getCurrentThisType to walk the stack of lambdas (even in scenarios where we run out of LambdaScopeInfo's such as when instantiating call operators) looking for by copy captures of '*this' and resetting the type of 'this' based on the constness of that capturing lambda's call operator.
This patch has been baking in review-hell for > 6 weeks - all the comments so far have been addressed and the bug (that it addresses in passing, and I regret not submitting as a separate patch initially) has been reported twice independently, so is frequent and important for us not to just sit on. I merged the cv qualification-fix and the PR-fix initially in one patch, since they resulted from my initial implementation of star-this and so were related. If someone really feels strongly, I can put in the time to revert this - separate the two out - and recommit. I won't claim it's immunized against all bugs, but I feel confident enough about the fix to land it for now.
llvm-svn: 272480
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 141 |
2 files changed, 118 insertions, 25 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fab72f36605..18185b07dbe 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11177,7 +11177,7 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator, } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), - S.getCurrentThisType(), /*Expr*/ nullptr, + /*Expr*/ nullptr, C.getCaptureKind() == LCK_StarThis); } else { LSI->addVLATypeCapture(C.getLocation(), I->getType()); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 2e19f233910..d6c16795b1c 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -872,6 +872,92 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, return false; } +static QualType adjustCVQualifiersForCXXThisWithinLambda( + ArrayRef<FunctionScopeInfo *> FunctionScopes, QualType ThisTy, + DeclContext *CurSemaContext, ASTContext &ASTCtx) { + + QualType ClassType = ThisTy->getPointeeType(); + LambdaScopeInfo *CurLSI = nullptr; + DeclContext *CurDC = CurSemaContext; + + // Iterate through the stack of lambdas starting from the innermost lambda to + // the outermost lambda, checking if '*this' is ever captured by copy - since + // that could change the cv-qualifiers of the '*this' object. + // The object referred to by '*this' starts out with the cv-qualifiers of its + // member function. We then start with the innermost lambda and iterate + // outward checking to see if any lambda performs a by-copy capture of '*this' + // - and if so, any nested lambda must respect the 'constness' of that + // capturing lamdbda's call operator. + // + + // The issue is that we cannot rely entirely on the FunctionScopeInfo stack + // since ScopeInfos are pushed on during parsing and treetransforming. But + // since a generic lambda's call operator can be instantiated anywhere (even + // end of the TU) we need to be able to examine its enclosing lambdas and so + // we use the DeclContext to get a hold of the closure-class and query it for + // capture information. The reason we don't just resort to always using the + // DeclContext chain is that it is only mature for lambda expressions + // enclosing generic lambda's call operators that are being instantiated. + + for (int I = FunctionScopes.size(); + I-- && isa<LambdaScopeInfo>(FunctionScopes[I]); + CurDC = getLambdaAwareParentOfDeclContext(CurDC)) { + CurLSI = cast<LambdaScopeInfo>(FunctionScopes[I]); + + if (!CurLSI->isCXXThisCaptured()) + continue; + + auto C = CurLSI->getCXXThisCapture(); + + if (C.isCopyCapture()) { + ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask); + if (CurLSI->CallOperator->isConst()) + ClassType.addConst(); + return ASTCtx.getPointerType(ClassType); + } + } + // We've run out of ScopeInfos but check if CurDC is a lambda (which can + // happen during instantiation of generic lambdas) + if (isLambdaCallOperator(CurDC)) { + assert(CurLSI); + assert(isGenericLambdaCallOperatorSpecialization(CurLSI->CallOperator)); + assert(CurDC == getLambdaAwareParentOfDeclContext(CurLSI->CallOperator)); + + auto IsThisCaptured = + [](CXXRecordDecl *Closure, bool &IsByCopy, bool &IsConst) { + IsConst = false; + IsByCopy = false; + for (auto &&C : Closure->captures()) { + if (C.capturesThis()) { + if (C.getCaptureKind() == LCK_StarThis) + IsByCopy = true; + if (Closure->getLambdaCallOperator()->isConst()) + IsConst = true; + return true; + } + } + return false; + }; + + bool IsByCopyCapture = false; + bool IsConstCapture = false; + CXXRecordDecl *Closure = cast<CXXRecordDecl>(CurDC->getParent()); + while (Closure && + IsThisCaptured(Closure, IsByCopyCapture, IsConstCapture)) { + if (IsByCopyCapture) { + ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask); + if (IsConstCapture) + ClassType.addConst(); + return ASTCtx.getPointerType(ClassType); + } + Closure = isLambdaCallOperator(Closure->getParent()) + ? cast<CXXRecordDecl>(Closure->getParent()->getParent()) + : nullptr; + } + } + return ASTCtx.getPointerType(ClassType); +} + QualType Sema::getCurrentThisType() { DeclContext *DC = getFunctionLevelDeclContext(); QualType ThisTy = CXXThisTypeOverride; @@ -902,20 +988,13 @@ QualType Sema::getCurrentThisType() { 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); - } - } - } + + // If we are within a lambda's call operator, the cv-qualifiers of 'this' + // might need to be adjusted if the lambda or any of its enclosing lambda's + // captures '*this' by copy. + if (!ThisTy.isNull() && isLambdaCallOperator(CurContext)) + return adjustCVQualifiersForCXXThisWithinLambda(FunctionScopes, ThisTy, + CurContext, Context); return ThisTy; } @@ -953,22 +1032,36 @@ Sema::CXXThisScopeRAII::~CXXThisScopeRAII() { 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, CaptureThisTy, - Context.getTrivialTypeSourceInfo(CaptureThisTy, Loc), - nullptr, false, ICIS_NoInit); + QualType AdjustedThisTy = ThisTy; + // The type of the corresponding data member (not a 'this' pointer if 'by + // copy'). + QualType CaptureThisFieldTy = ThisTy; + if (ByCopy) { + // If we are capturing the object referred to by '*this' by copy, ignore any + // cv qualifiers inherited from the type of the member function for the type + // of the closure-type's corresponding data member and any use of 'this'. + CaptureThisFieldTy = ThisTy->getPointeeType(); + CaptureThisFieldTy.removeLocalCVRQualifiers(Qualifiers::CVRMask); + AdjustedThisTy = Context.getPointerType(CaptureThisFieldTy); + } + + FieldDecl *Field = FieldDecl::Create( + Context, RD, Loc, Loc, nullptr, CaptureThisFieldTy, + Context.getTrivialTypeSourceInfo(CaptureThisFieldTy, Loc), nullptr, false, + ICIS_NoInit); + Field->setImplicit(true); Field->setAccess(AS_private); RD->addDecl(Field); - Expr *This = 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); + nullptr, CaptureThisFieldTy, Loc); InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc); InitializationSequence Init(S, Entity, InitKind, StarThis); ExprResult ER = Init.Perform(S, Entity, InitKind, StarThis); @@ -1067,12 +1160,12 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, "*this) by copy"); // FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated // contexts. - + QualType ThisTy = getCurrentThisType(); 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, // and capture the *enclosing object* by copy only if this is the first @@ -1087,7 +1180,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, false/*ByCopy*/); bool isNested = NumCapturingClosures > 1; - CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr, ByCopy); + CSI->addThisCapture(isNested, Loc, ThisExpr, ByCopy); } return false; } |

