class BlockDecl : public Decl, public DeclContext {
// FIXME: This can be packed into the bitfields in Decl.
bool IsVariadic : 1;
+ bool CapturesCXXThis : 1;
/// ParamInfo - new[]'d array of pointers to ParmVarDecls for the formal
/// parameters of this function. This is null if a prototype or if there are
/// no formals.
Stmt *Body;
TypeSourceInfo *SignatureAsWritten;
+ VarDecl **CapturedDecls;
+ unsigned NumCapturedDecls;
+
protected:
BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
: Decl(Block, DC, CaretLoc), DeclContext(Block),
- IsVariadic(false), ParamInfo(0), NumParams(0), Body(0),
- SignatureAsWritten(0) {}
+ IsVariadic(false), CapturesCXXThis(false),
+ ParamInfo(0), NumParams(0), Body(0),
+ SignatureAsWritten(0), CapturedDecls(0), NumCapturedDecls(0) {}
public:
static BlockDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L);
param_const_iterator param_begin() const { return ParamInfo; }
param_const_iterator param_end() const { return ParamInfo+param_size(); }
- unsigned getNumParams() const;
+ unsigned getNumParams() const { return NumParams; }
const ParmVarDecl *getParamDecl(unsigned i) const {
assert(i < getNumParams() && "Illegal param #");
return ParamInfo[i];
}
void setParams(ParmVarDecl **NewParamInfo, unsigned NumParams);
+ /// hasCaptures - True if this block (or its nested blocks) captures
+ /// anything of local storage from its enclosing scopes.
+ bool hasCaptures() const { return NumCapturedDecls != 0 || CapturesCXXThis; }
+
+ unsigned getNumCapturedDecls() const { return NumCapturedDecls; }
+
+ typedef VarDecl * const *capture_iterator;
+ typedef VarDecl const * const *capture_const_iterator;
+ capture_iterator capture_begin() { return CapturedDecls; }
+ capture_iterator capture_end() { return CapturedDecls + NumCapturedDecls; }
+ capture_const_iterator capture_begin() const { return CapturedDecls; }
+ capture_const_iterator capture_end() const {
+ return CapturedDecls + NumCapturedDecls;
+ }
+
+ bool capturesCXXThis() const { return CapturesCXXThis; }
+
+ void setCapturedDecls(ASTContext &Context,
+ VarDecl * const *begin,
+ VarDecl * const *end,
+ bool capturesCXXThis);
+
virtual SourceRange getSourceRange() const;
// Implement isa/cast/dyncast/etc.
StringTokLocs.size()));
}
-/// ShouldSnapshotBlockValueReference - Return true if a reference inside of
-/// CurBlock to VD should cause it to be snapshotted (as we do for auto
-/// variables defined outside the block) or false if this is not needed (e.g.
-/// for values inside the block or for globals).
+enum CaptureResult {
+ /// No capture is required.
+ CR_NoCapture,
+
+ /// A capture is required.
+ CR_Capture,
+
+ /// An error occurred when trying to capture the given variable.
+ CR_Error
+};
+
+/// Diagnose an uncapturable value reference.
///
-/// This also keeps the 'hasBlockDeclRefExprs' in the BlockScopeInfo records
-/// up-to-date.
+/// \param var - the variable referenced
+/// \param DC - the context which we couldn't capture through
+static CaptureResult
+DiagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
+ VarDecl *var, DeclContext *DC) {
+ switch (S.ExprEvalContexts.back().Context) {
+ case Sema::Unevaluated:
+ // The argument will never be evaluated, so don't complain.
+ return CR_NoCapture;
+
+ case Sema::PotentiallyEvaluated:
+ case Sema::PotentiallyEvaluatedIfUsed:
+ break;
+
+ case Sema::PotentiallyPotentiallyEvaluated:
+ // FIXME: delay these!
+ break;
+ }
+
+ // Don't diagnose about capture if we're not actually in code right
+ // now; in general, there are more appropriate places that will
+ // diagnose this.
+ if (!S.CurContext->isFunctionOrMethod()) return CR_NoCapture;
+
+ // This particular madness can happen in ill-formed default
+ // arguments; claim it's okay and let downstream code handle it.
+ if (isa<ParmVarDecl>(var) &&
+ S.CurContext == var->getDeclContext()->getParent())
+ return CR_NoCapture;
+
+ DeclarationName functionName;
+ if (FunctionDecl *fn = dyn_cast<FunctionDecl>(var->getDeclContext()))
+ functionName = fn->getDeclName();
+ // FIXME: variable from enclosing block that we couldn't capture from!
+
+ S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_function)
+ << var->getIdentifier() << functionName;
+ S.Diag(var->getLocation(), diag::note_local_variable_declared_here)
+ << var->getIdentifier();
+
+ return CR_Error;
+}
+
+/// ShouldCaptureValueReference - Determine if a reference to the
+/// given value in the current context requires a variable capture.
///
-static bool ShouldSnapshotBlockValueReference(Sema &S, BlockScopeInfo *CurBlock,
- ValueDecl *VD) {
- // If the value is defined inside the block, we couldn't snapshot it even if
- // we wanted to.
- if (CurBlock->TheDecl == VD->getDeclContext())
- return false;
+/// This also keeps the captures set in the BlockScopeInfo records
+/// up-to-date.
+static CaptureResult ShouldCaptureValueReference(Sema &S, SourceLocation loc,
+ ValueDecl *value) {
+ // Only variables ever require capture.
+ VarDecl *var = dyn_cast<VarDecl>(value);
+ if (!var || isa<NonTypeTemplateParmDecl>(var)) return CR_NoCapture;
- // If this is an enum constant or function, it is constant, don't snapshot.
- if (isa<EnumConstantDecl>(VD) || isa<FunctionDecl>(VD))
- return false;
+ // Fast path: variables from the current context never require capture.
+ DeclContext *DC = S.CurContext;
+ if (var->getDeclContext() == DC) return CR_NoCapture;
- // If this is a reference to an extern, static, or global variable, no need to
- // snapshot it.
+ // Only variables with local storage require capture.
// FIXME: What about 'const' variables in C++?
- if (const VarDecl *Var = dyn_cast<VarDecl>(VD))
- if (!Var->hasLocalStorage())
- return false;
+ if (!var->hasLocalStorage()) return CR_NoCapture;
- // Blocks that have these can't be constant.
- CurBlock->hasBlockDeclRefExprs = true;
+ // Otherwise, we need to capture.
- // If we have nested blocks, the decl may be declared in an outer block (in
- // which case that outer block doesn't get "hasBlockDeclRefExprs") or it may
- // be defined outside all of the current blocks (in which case the blocks do
- // all get the bit). Walk the nesting chain.
- for (unsigned I = S.FunctionScopes.size() - 1; I; --I) {
- BlockScopeInfo *NextBlock = dyn_cast<BlockScopeInfo>(S.FunctionScopes[I]);
+ unsigned functionScopesIndex = S.FunctionScopes.size() - 1;
- if (!NextBlock)
- continue;
+ do {
+ // Only blocks (and eventually C++0x closures) can capture; other
+ // scopes don't work.
+ if (!isa<BlockDecl>(DC))
+ return DiagnoseUncapturableValueReference(S, loc, var, DC);
- // If we found the defining block for the variable, don't mark the block as
- // having a reference outside it.
- if (NextBlock->TheDecl == VD->getDeclContext())
- break;
+ BlockScopeInfo *blockScope =
+ cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
+ assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC));
- // Otherwise, the DeclRef from the inner block causes the outer one to need
- // a snapshot as well.
- NextBlock->hasBlockDeclRefExprs = true;
- }
+ // Try to capture it in this block. If we've already captured at
+ // this level, we're done.
+ if (!blockScope->Captures.insert(var))
+ return CR_Capture;
- return true;
-}
+ functionScopesIndex--;
+ DC = cast<BlockDecl>(DC)->getDeclContext();
+ } while (var->getDeclContext() != DC);
+ return CR_Capture;
+}
ExprResult
Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
// Non-type template parameters can be referenced anywhere they are
// visible.
Ty = Ty.getNonLValueExprType(Context);
- } else if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) {
- if (const FunctionDecl *FD = MD->getParent()->isLocalClass()) {
- if (VD->hasLocalStorage() && VD->getDeclContext() != CurContext) {
- Diag(NameInfo.getLoc(),
- diag::err_reference_to_local_var_in_enclosing_function)
- << D->getIdentifier() << FD->getDeclName();
- Diag(D->getLocation(), diag::note_local_variable_declared_here)
- << D->getIdentifier();
- return ExprError();
- }
- }
// This ridiculousness brought to you by 'extern void x;' and the
// GNU compiler collection.
// We do not do this for things like enum constants, global variables, etc,
// as they do not get snapshotted.
//
- if (getCurBlock() &&
- ShouldSnapshotBlockValueReference(*this, getCurBlock(), VD)) {
- if (VD->getType().getTypePtr()->isVariablyModifiedType()) {
- Diag(Loc, diag::err_ref_vm_type);
- Diag(D->getLocation(), diag::note_declared_at);
- return ExprError();
- }
+ switch (ShouldCaptureValueReference(*this, NameInfo.getLoc(), VD)) {
+ case CR_Error:
+ return ExprError();
- if (VD->getType()->isArrayType()) {
- Diag(Loc, diag::err_ref_array_type);
- Diag(D->getLocation(), diag::note_declared_at);
- return ExprError();
- }
+ case CR_NoCapture:
+ // If this reference is not in a block or if the referenced
+ // variable is within the block, create a normal DeclRefExpr.
+ return BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), VK,
+ NameInfo, &SS);
- MarkDeclarationReferenced(Loc, VD);
- QualType ExprTy = VD->getType().getNonReferenceType();
+ case CR_Capture:
+ break;
+ }
+
+ // If we got here, we need to capture.
- // The BlocksAttr indicates the variable is bound by-reference.
- bool byrefVar = (VD->getAttr<BlocksAttr>() != 0);
- QualType T = VD->getType();
- BlockDeclRefExpr *BDRE;
+ if (VD->getType().getTypePtr()->isVariablyModifiedType()) {
+ Diag(Loc, diag::err_ref_vm_type);
+ Diag(D->getLocation(), diag::note_declared_at);
+ return ExprError();
+ }
+
+ if (VD->getType()->isArrayType()) {
+ Diag(Loc, diag::err_ref_array_type);
+ Diag(D->getLocation(), diag::note_declared_at);
+ return ExprError();
+ }
+
+ MarkDeclarationReferenced(Loc, VD);
+ QualType ExprTy = VD->getType().getNonReferenceType();
+
+ // The BlocksAttr indicates the variable is bound by-reference.
+ bool byrefVar = (VD->getAttr<BlocksAttr>() != 0);
+ QualType T = VD->getType();
+ BlockDeclRefExpr *BDRE;
- if (!byrefVar) {
- // This is to record that a 'const' was actually synthesize and added.
- bool constAdded = !ExprTy.isConstQualified();
- // Variable will be bound by-copy, make it const within the closure.
- ExprTy.addConst();
- BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK,
- Loc, false, constAdded);
- }
- else
- BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK, Loc, true);
+ if (!byrefVar) {
+ // This is to record that a 'const' was actually synthesize and added.
+ bool constAdded = !ExprTy.isConstQualified();
+ // Variable will be bound by-copy, make it const within the closure.
+ ExprTy.addConst();
+ BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK,
+ Loc, false, constAdded);
+ }
+ else
+ BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK, Loc, true);
- if (getLangOptions().CPlusPlus) {
- if (!T->isDependentType() && !T->isReferenceType()) {
- Expr *E = new (Context)
- DeclRefExpr(const_cast<ValueDecl*>(BDRE->getDecl()), T,
- VK, SourceLocation());
- if (T->getAs<RecordType>())
- if (!T->isUnionType()) {
- ExprResult Res = PerformCopyInitialization(
- InitializedEntity::InitializeBlock(VD->getLocation(),
+ if (getLangOptions().CPlusPlus) {
+ if (!T->isDependentType() && !T->isReferenceType()) {
+ Expr *E = new (Context)
+ DeclRefExpr(const_cast<ValueDecl*>(BDRE->getDecl()), T,
+ VK, SourceLocation());
+ if (T->isStructureOrClassType()) {
+ ExprResult Res = PerformCopyInitialization(
+ InitializedEntity::InitializeBlock(VD->getLocation(),
T, false),
- SourceLocation(),
- Owned(E));
- if (!Res.isInvalid()) {
- Res = MaybeCreateExprWithCleanups(Res);
- Expr *Init = Res.takeAs<Expr>();
- BDRE->setCopyConstructorExpr(Init);
- }
+ SourceLocation(),
+ Owned(E));
+ if (!Res.isInvalid()) {
+ Res = MaybeCreateExprWithCleanups(Res);
+ Expr *Init = Res.takeAs<Expr>();
+ BDRE->setCopyConstructorExpr(Init);
}
}
}
- return Owned(BDRE);
}
- // If this reference is not in a block or if the referenced variable is
- // within the block, create a normal DeclRefExpr.
- return BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), VK,
- NameInfo, &SS);
+ return Owned(BDRE);
}
ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc,
bool NoReturn = BSI->TheDecl->getAttr<NoReturnAttr>();
QualType BlockTy;
+ // Set the captured variables on the block.
+ BSI->TheDecl->setCapturedDecls(Context,
+ BSI->Captures.begin(),
+ BSI->Captures.end(),
+ BSI->CapturesCXXThis);
+
// If the user wrote a function type in some form, try to use that.
if (!BSI->FunctionType.isNull()) {
const FunctionType *FTy = BSI->FunctionType->getAs<FunctionType>();
return ExprError();
}
- BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy,
- BSI->hasBlockDeclRefExprs);
+ BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy);
// Issue any analysis-based warnings.
const sema::AnalysisBasedWarnings::Policy &WP =