diff options
author | Eli Friedman <eli.friedman@gmail.com> | 2012-01-11 02:36:31 +0000 |
---|---|---|
committer | Eli Friedman <eli.friedman@gmail.com> | 2012-01-11 02:36:31 +0000 |
commit | b69b42c55d56815bab62991bf839cdb41634d3af (patch) | |
tree | cf53ef3a01c1b51064cf66532e9dc00fa4369ecf | |
parent | 52f220df136eaa96b648d8ce0cc29b62c48d645c (diff) |
Start refactoring code for capturing variables and 'this' so that it is shared between lambda expressions and block literals.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147917 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Sema/ScopeInfo.h | 145 | ||||
-rw-r--r-- | lib/Driver/ToolChains.cpp | 39 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 30 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 76 | ||||
-rw-r--r-- | lib/Sema/SemaExprObjC.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 5 |
6 files changed, 164 insertions, 134 deletions
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 4d49a5a88a..3b3bc5caf2 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -117,8 +117,86 @@ public: static bool classof(const FunctionScopeInfo *FSI) { return true; } }; +class CapturingScopeInfo : public FunctionScopeInfo { +public: + enum ImplicitCaptureStyle { + ImpCap_None, ImpCap_LambdaByval, ImpCap_LambdaByref, ImpCap_Block + }; + + ImplicitCaptureStyle ImpCaptureStyle; + + class Capture { + enum CaptureKind { + Cap_This, Cap_ByVal, Cap_ByRef + }; + + // The variable being captured (if we are not capturing 'this'), + // and misc bits descibing the capture. + llvm::PointerIntPair<VarDecl*, 2, CaptureKind> VarAndKind; + + // Expression to initialize a field of the given type, and whether this + // is a nested capture; the expression is only required if we are + // capturing ByVal and the variable's type has a non-trivial + // copy constructor. + llvm::PointerIntPair<Expr*, 1, bool> CopyExprAndNested; + + public: + Capture(VarDecl *Var, bool isByref, bool isNested, Expr *Cpy) + : VarAndKind(Var, isByref ? Cap_ByRef : Cap_ByVal), + CopyExprAndNested(Cpy, isNested) {} + + enum IsThisCapture { ThisCapture }; + Capture(IsThisCapture, bool isNested) + : VarAndKind(0, Cap_This), + CopyExprAndNested(0, isNested) { + } + + bool isThisCapture() const { return VarAndKind.getInt() == Cap_This; } + bool isVariableCapture() const { return !isThisCapture(); } + bool isCopyCapture() const { return VarAndKind.getInt() == Cap_ByVal; } + bool isReferenceCapture() const { return VarAndKind.getInt() == Cap_ByRef; } + bool isNested() { return CopyExprAndNested.getInt(); } + + VarDecl *getVariable() const { + return VarAndKind.getPointer(); + } + Expr *getCopyExpr() const { + return CopyExprAndNested.getPointer(); + } + }; + + CapturingScopeInfo(DiagnosticsEngine &Diag, ImplicitCaptureStyle Style) + : FunctionScopeInfo(Diag), ImpCaptureStyle(Style), CXXThisCaptureIndex(0) + {} + + /// CaptureMap - A map of captured variables to (index+1) into Captures. + llvm::DenseMap<VarDecl*, unsigned> CaptureMap; + + /// CXXThisCaptureIndex - The (index+1) of the capture of 'this'; + /// zero if 'this' is not captured. + unsigned CXXThisCaptureIndex; + + /// Captures - The captures. + SmallVector<Capture, 4> Captures; + + void AddCapture(VarDecl *Var, bool isByref, bool isNested, Expr *Cpy) { + Captures.push_back(Capture(Var, isByref, isNested, Cpy)); + CaptureMap[Var] = Captures.size(); + } + + void AddThisCapture(bool isNested) { + Captures.push_back(Capture(Capture::ThisCapture, isNested)); + CXXThisCaptureIndex = Captures.size(); + } + + static bool classof(const FunctionScopeInfo *FSI) { + return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda; + } + static bool classof(const CapturingScopeInfo *BSI) { return true; } +}; + /// \brief Retains information about a block that is currently being parsed. -class BlockScopeInfo : public FunctionScopeInfo { +class BlockScopeInfo : public CapturingScopeInfo { public: BlockDecl *TheDecl; @@ -134,18 +212,9 @@ public: /// Its return type may be BuiltinType::Dependent. QualType FunctionType; - /// CaptureMap - A map of captured variables to (index+1) into Captures. - llvm::DenseMap<VarDecl*, unsigned> CaptureMap; - - /// Captures - The captured variables. - SmallVector<BlockDecl::Capture, 4> Captures; - - /// CapturesCXXThis - Whether this block captures 'this'. - bool CapturesCXXThis; - BlockScopeInfo(DiagnosticsEngine &Diag, Scope *BlockScope, BlockDecl *Block) - : FunctionScopeInfo(Diag), TheDecl(Block), TheScope(BlockScope), - CapturesCXXThis(false) + : CapturingScopeInfo(Diag, ImpCap_Block), TheDecl(Block), + TheScope(BlockScope) { Kind = SK_Block; } @@ -158,70 +227,30 @@ public: static bool classof(const BlockScopeInfo *BSI) { return true; } }; -class LambdaScopeInfo : public FunctionScopeInfo { +class LambdaScopeInfo : public CapturingScopeInfo { public: - - class Capture { - llvm::PointerIntPair<VarDecl*, 2, LambdaCaptureKind> InitAndKind; - - public: - Capture(VarDecl *Var, LambdaCaptureKind Kind) - : InitAndKind(Var, Kind) {} - - enum IsThisCapture { ThisCapture }; - Capture(IsThisCapture) - : InitAndKind(0, LCK_This) {} - - bool isThisCapture() const { return InitAndKind.getInt() == LCK_This; } - bool isVariableCapture() const { return !isThisCapture(); } - bool isCopyCapture() const { return InitAndKind.getInt() == LCK_ByCopy; } - bool isReferenceCapture() const { return InitAndKind.getInt() == LCK_ByRef; } - - VarDecl *getVariable() const { - return InitAndKind.getPointer(); - } - - }; - /// \brief The class that describes the lambda. CXXRecordDecl *Lambda; - /// \brief A mapping from the set of captured variables to the - /// fields (within the lambda class) that represent the captured variables. - llvm::DenseMap<VarDecl *, FieldDecl *> CapturedVariables; - - /// \brief The list of captured variables, starting with the explicit - /// captures and then finishing with any implicit captures. - llvm::SmallVector<Capture, 4> Captures; - - // \brief Whether we have already captured 'this'. - bool CapturesCXXThis; - /// \brief The number of captures in the \c Captures list that are /// explicit captures. unsigned NumExplicitCaptures; - LambdaCaptureDefault Default; - - /// \brief The field associated with the captured 'this' pointer. - FieldDecl *ThisCapture; - /// \brief - Whether the return type of the lambda is implicit bool HasImplicitReturnType; /// ReturnType - The return type of the lambda, or null if unknown. QualType ReturnType; - LambdaScopeInfo(DiagnosticsEngine &Diag, CXXRecordDecl *Lambda) - : FunctionScopeInfo(Diag), Lambda(Lambda), CapturesCXXThis(false), - NumExplicitCaptures(0), Default(LCD_None), ThisCapture(0), - HasImplicitReturnType(false) + LambdaScopeInfo(DiagnosticsEngine &Diag, CXXRecordDecl *Lambda) + : CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda), + NumExplicitCaptures(0), HasImplicitReturnType(false) { Kind = SK_Lambda; } - + virtual ~LambdaScopeInfo(); - + static bool classof(const FunctionScopeInfo *FSI) { return FSI->Kind == SK_Lambda; } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 3dcdf13f00..3ec98dd976 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -533,24 +533,15 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { Arg *iOSVersion = Args.getLastArg(options::OPT_miphoneos_version_min_EQ); Arg *iOSSimVersion = Args.getLastArg( options::OPT_mios_simulator_version_min_EQ); - - // FIXME: HACK! When compiling for the simulator we don't get a - // '-miphoneos-version-min' to help us know whether there is an ARC runtime - // or not; try to parse a __IPHONE_OS_VERSION_MIN_REQUIRED - // define passed in command-line. - if (!iOSVersion && !iOSSimVersion) { + + if (!iOSSimVersion) { + // As a legacy hack, treat -D__IPHONE_OS_VERSION_MIN_REQUIRED=40201 as + // equivalent to -mios-simulator-version-min. for (arg_iterator it = Args.filtered_begin(options::OPT_D), ie = Args.filtered_end(); it != ie; ++it) { StringRef define = (*it)->getValue(Args); if (define.startswith(SimulatorVersionDefineName())) { - unsigned Major = 0, Minor = 0, Micro = 0; - if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && - Major < 10 && Minor < 100 && Micro < 100) { - ARCRuntimeForSimulator = Major < 5 ? ARCSimulator_NoARCRuntime - : ARCSimulator_HasARCRuntime; - LibCXXForSimulator = Major < 5 ? LibCXXSimulator_NotAvailable - : LibCXXSimulator_Available; - } + iOSSimVersion = *it; break; } } @@ -661,11 +652,21 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { } else { const Arg *Version = iOSVersion ? iOSVersion : iOSSimVersion; assert(Version && "Unknown target platform!"); - if (!Driver::GetReleaseVersion(Version->getValue(Args), Major, Minor, - Micro, HadExtra) || HadExtra || - Major >= 10 || Minor >= 100 || Micro >= 100) - getDriver().Diag(diag::err_drv_invalid_version_number) - << Version->getAsString(Args); + if (Version->getOption().getID() == options::OPT_D) { + // If the simulator version comes from a define, parse that. + if (!GetVersionFromSimulatorDefine(Version->getValue(Args), Major, + Minor, Micro) || + Major >= 10 || Minor >= 100 || Micro >= 100) + getDriver().Diag(diag::err_drv_invalid_version_number) + << Version->getAsString(Args); + } else { + // Otherwise, use the normal version parsing code. + if (!Driver::GetReleaseVersion(Version->getValue(Args), Major, Minor, + Micro, HadExtra) || + HadExtra || Major >= 10 || Minor >= 100 || Micro >= 100) + getDriver().Diag(diag::err_drv_invalid_version_number) + << Version->getAsString(Args); + } } bool IsIOSSim = bool(iOSSimVersion); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index f9fb3bc9fc..ceb836b3e8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1248,20 +1248,16 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, /// There is a well-formed capture at a particular scope level; /// propagate it through all the nested blocks. static CaptureResult propagateCapture(Sema &S, unsigned ValidScopeIndex, - const BlockDecl::Capture &Capture) { - VarDecl *var = Capture.getVariable(); - + const CapturingScopeInfo::Capture &Cap) { // Update all the inner blocks with the capture information. for (unsigned i = ValidScopeIndex + 1, e = S.FunctionScopes.size(); i != e; ++i) { BlockScopeInfo *innerBlock = cast<BlockScopeInfo>(S.FunctionScopes[i]); - innerBlock->Captures.push_back( - BlockDecl::Capture(Capture.getVariable(), Capture.isByRef(), - /*nested*/ true, Capture.getCopyExpr())); - innerBlock->CaptureMap[var] = innerBlock->Captures.size(); // +1 + innerBlock->AddCapture(Cap.getVariable(), Cap.isReferenceCapture(), + /*nested*/ true, Cap.getCopyExpr()); } - return Capture.isByRef() ? CR_CaptureByRef : CR_Capture; + return Cap.isReferenceCapture() ? CR_CaptureByRef : CR_Capture; } /// shouldCaptureValueReference - Determine if a reference to the @@ -1372,9 +1368,7 @@ static CaptureResult shouldCaptureValueReference(Sema &S, SourceLocation loc, cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]); // Build a valid capture in this scope. - blockScope->Captures.push_back( - BlockDecl::Capture(var, byRef, /*nested*/ false, copyExpr)); - blockScope->CaptureMap[var] = blockScope->Captures.size(); // +1 + blockScope->AddCapture(var, byRef, /*nested*/ false, copyExpr); // Propagate that to inner captures if necessary. return propagateCapture(S, functionScopesIndex, @@ -8861,8 +8855,18 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, QualType BlockTy; // Set the captured variables on the block. - BSI->TheDecl->setCaptures(Context, BSI->Captures.begin(), BSI->Captures.end(), - BSI->CapturesCXXThis); + // FIXME: Share capture structure between BlockDecl and CapturingScopeInfo! + SmallVector<BlockDecl::Capture, 4> Captures; + for (unsigned i = 0, e = BSI->Captures.size(); i != e; i++) { + CapturingScopeInfo::Capture &Cap = BSI->Captures[i]; + if (Cap.isThisCapture()) + continue; + BlockDecl::Capture NewCap(Cap.getVariable(), Cap.isReferenceCapture(), + Cap.isNested(), Cap.getCopyExpr()); + Captures.push_back(NewCap); + } + BSI->TheDecl->setCaptures(Context, Captures.begin(), Captures.end(), + BSI->CXXThisCaptureIndex != 0); // If the user wrote a function type in some form, try to use that. if (!BSI->FunctionType.isNull()) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index d4efa78b23..da7ba1a2b3 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -680,19 +680,21 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc) { // Otherwise, check that we can capture 'this'. unsigned NumClosures = 0; for (unsigned idx = FunctionScopes.size() - 1; idx != 0; idx--) { - if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(FunctionScopes[idx])) { - if (LSI->CapturesCXXThis) { - // This lambda already captures 'this'; there isn't anything more to do. + if (CapturingScopeInfo *CSI = + dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) { + if (CSI->CXXThisCaptureIndex != 0) { + // 'this' is already being captured; there isn't anything more to do. break; } - if (LSI->Default == LCD_ByRef) { - // This lambda can implicitly capture 'this'; continue looking upwards. + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || + CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block) { + // This closure can implicitly capture 'this'; continue looking upwards. // FIXME: Is this check correct? The rules in the standard are a bit // unclear. NumClosures++; continue; } - // This lambda can't implicitly capture 'this'; fail out. + // This context can't implicitly capture 'this'; fail out. // (We need to delay the diagnostic in the // PotentiallyPotentiallyEvaluated case because it doesn't apply to // unevaluated contexts.) @@ -703,10 +705,6 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc) { Diag(Loc, diag::err_implicit_this_capture); return; } - if (isa<BlockScopeInfo>(FunctionScopes[idx])) { - NumClosures++; - continue; - } break; } @@ -715,14 +713,9 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc) { // contexts. for (unsigned idx = FunctionScopes.size() - 1; NumClosures; --idx, --NumClosures) { - if (BlockScopeInfo *BSI = dyn_cast<BlockScopeInfo>(FunctionScopes[idx])) { - BSI->CapturesCXXThis = true; - } else { - LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(FunctionScopes[idx]); - assert(LSI && "Unexpected closure"); - LSI->CapturesCXXThis = true; - LSI->Captures.push_back(LambdaScopeInfo::Capture::ThisCapture); - } + CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]); + bool isNested = NumClosures > 1; + CSI->AddThisCapture(isNested); } } @@ -4831,8 +4824,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, CurContext->addDecl(Class); QualType ThisCaptureType; + llvm::DenseMap<VarDecl*, unsigned> CaptureMap; + unsigned CXXThisCaptureIndex = 0; llvm::SmallVector<LambdaScopeInfo::Capture, 4> Captures; - llvm::DenseMap<const IdentifierInfo*, SourceLocation> CapturesSoFar; for (llvm::SmallVector<LambdaCapture, 4>::const_iterator C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) { if (C->Kind == LCK_This) { @@ -4853,7 +4847,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, } CheckCXXThisCapture(C->Loc); - Captures.push_back(LambdaScopeInfo::Capture::ThisCapture); + // FIXME: Need getCurCapture(). + bool isNested = getCurBlock() || getCurLambda(); + CapturingScopeInfo::Capture Cap(CapturingScopeInfo::Capture::ThisCapture, + isNested); + Captures.push_back(Cap); + CXXThisCaptureIndex = Captures.size(); continue; } @@ -4867,16 +4866,6 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, continue; } - llvm::DenseMap<const IdentifierInfo*, SourceLocation>::iterator Appearance; - bool IsFirstAppearance; - llvm::tie(Appearance, IsFirstAppearance) - = CapturesSoFar.insert(std::make_pair(C->Id, C->Loc)); - - if (!IsFirstAppearance) { - Diag(C->Loc, diag::err_capture_more_than_once) << C->Id; - continue; - } - DeclarationNameInfo Name(C->Id, C->Loc); LookupResult R(*this, Name, LookupOrdinaryName); CXXScopeSpec ScopeSpec; @@ -4893,16 +4882,22 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, continue; } + if (CaptureMap.count(Var)) { + Diag(C->Loc, diag::err_capture_more_than_once) << C->Id; + continue; + } + if (!Var->hasLocalStorage()) { Diag(C->Loc, diag::err_capture_non_automatic_variable) << C->Id; continue; } - // FIXME: Actually capturing a variable is much more complicated than this - // in the general case; see shouldCaptureValueReference. - // FIXME: Should we be building a DeclRefExpr here? We don't really need - // it until the point where we're actually building the LambdaExpr. - Captures.push_back(LambdaScopeInfo::Capture(Var, C->Kind)); + // FIXME: This is completely wrong for nested captures and variables + // with a non-trivial constructor. + // FIXME: We should refuse to capture __block variables. + Captures.push_back(LambdaScopeInfo::Capture(Var, C->Kind == LCK_ByRef, + /*isNested*/false, 0)); + CaptureMap[Var] = Captures.size(); } // Build the call operator; we don't really have all the relevant information @@ -4982,15 +4977,20 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, PushLambdaScope(Class); LambdaScopeInfo *LSI = getCurLambda(); - LSI->Default = Intro.Default; - if (!ThisCaptureType.isNull()) - LSI->CapturesCXXThis = true; + LSI->CXXThisCaptureIndex = CXXThisCaptureIndex; + std::swap(LSI->CaptureMap, CaptureMap); std::swap(LSI->Captures, Captures); + LSI->NumExplicitCaptures = Captures.size(); + if (Intro.Default == LCD_ByCopy) + LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByval; + else if (Intro.Default == LCD_ByRef) + LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByref; const FunctionType *Fn = MethodTy->getAs<FunctionType>(); QualType RetTy = Fn->getResultType(); if (RetTy != Context.DependentTy) { LSI->ReturnType = RetTy; + } else { LSI->HasImplicitReturnType = true; } diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 20c3b75431..d229c18c93 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -265,8 +265,7 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf() { if (captureIndex) break; bool nested = isa<BlockScopeInfo>(FunctionScopes[idx-1]); - blockScope->Captures.push_back( - BlockDecl::Capture(self, /*byref*/ false, nested, /*copy*/ 0)); + blockScope->AddCapture(self, /*byref*/ false, nested, /*copy*/ 0); captureIndex = blockScope->Captures.size(); // +1 } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 7995b0ba77..4908e883b8 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1890,6 +1890,7 @@ public: ExprResult RebuildCXXThisExpr(SourceLocation ThisLoc, QualType ThisType, bool isImplicit) { + getSema().CheckCXXThisCapture(ThisLoc); return getSema().Owned( new (getSema().Context) CXXThisExpr(ThisLoc, ThisType, isImplicit)); @@ -8114,10 +8115,6 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) { BlockScopeInfo *blockScope = SemaRef.getCurBlock(); blockScope->TheDecl->setIsVariadic(oldBlock->isVariadic()); - // We built a new blockScopeInfo in call to ActOnBlockStart - // in above, CapturesCXXThis need be set here from the block - // expression. - blockScope->CapturesCXXThis = oldBlock->capturesCXXThis(); blockScope->TheDecl->setBlockMissingReturnType( oldBlock->blockMissingReturnType()); |