An insomniac stab at making block declarations list the variables they close

on, as well as more reliably limiting invalid references to locals from
nested scopes.

llvm-svn: 124721
This commit is contained in:
John McCall 2011-02-02 13:00:07 +00:00
parent fdfdbd091d
commit c63de66c4f
14 changed files with 249 additions and 137 deletions

View File

@ -2495,6 +2495,7 @@ public:
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.
@ -2504,11 +2505,15 @@ class BlockDecl : public Decl, public DeclContext {
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);
@ -2537,7 +2542,7 @@ public:
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];
@ -2548,6 +2553,28 @@ public:
}
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.

View File

@ -3528,12 +3528,11 @@ public:
class BlockExpr : public Expr {
protected:
BlockDecl *TheBlock;
bool HasBlockDeclRefExprs;
public:
BlockExpr(BlockDecl *BD, QualType ty, bool hasBlockDeclRefExprs)
BlockExpr(BlockDecl *BD, QualType ty)
: Expr(BlockExprClass, ty, VK_RValue, OK_Ordinary,
ty->isDependentType(), false, false),
TheBlock(BD), HasBlockDeclRefExprs(hasBlockDeclRefExprs) {}
TheBlock(BD) {}
/// \brief Build an empty block expression.
explicit BlockExpr(EmptyShell Empty) : Expr(BlockExprClass, Empty) { }
@ -3554,11 +3553,6 @@ public:
/// getFunctionType - Return the underlying function type for this block.
const FunctionType *getFunctionType() const;
/// hasBlockDeclRefExprs - Return true iff the block has BlockDeclRefExpr
/// inside of the block that reference values outside the block.
bool hasBlockDeclRefExprs() const { return HasBlockDeclRefExprs; }
void setHasBlockDeclRefExprs(bool BDRE) { HasBlockDeclRefExprs = BDRE; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == BlockExprClass;
}

View File

@ -17,6 +17,7 @@
#include "clang/AST/Type.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SetVector.h"
namespace clang {
@ -101,8 +102,6 @@ public:
/// \brief Retains information about a block that is currently being parsed.
class BlockScopeInfo : public FunctionScopeInfo {
public:
bool hasBlockDeclRefExprs;
BlockDecl *TheDecl;
/// TheScope - This is the scope for the block itself, which contains
@ -117,9 +116,15 @@ public:
/// Its return type may be BuiltinType::Dependent.
QualType FunctionType;
/// Captures - The set of variables captured by this block.
llvm::SmallSetVector<VarDecl*, 4> Captures;
/// CapturesCXXThis - Whether this block captures 'this'.
bool CapturesCXXThis;
BlockScopeInfo(Diagnostic &Diag, Scope *BlockScope, BlockDecl *Block)
: FunctionScopeInfo(Diag), hasBlockDeclRefExprs(false),
TheDecl(Block), TheScope(BlockScope)
: FunctionScopeInfo(Diag), TheDecl(Block), TheScope(BlockScope),
CapturesCXXThis(false)
{
IsBlockInfo = true;
}

View File

@ -2104,8 +2104,21 @@ void BlockDecl::setParams(ParmVarDecl **NewParamInfo,
}
}
unsigned BlockDecl::getNumParams() const {
return NumParams;
void BlockDecl::setCapturedDecls(ASTContext &Context,
VarDecl * const *begin,
VarDecl * const *end,
bool capturesCXXThis) {
CapturesCXXThis = capturesCXXThis;
if (begin == end) {
NumCapturedDecls = 0;
CapturedDecls = 0;
return;
}
NumCapturedDecls = end - begin;
CapturedDecls = new (Context) VarDecl*[NumCapturedDecls];
memcpy(CapturedDecls, begin, NumCapturedDecls * sizeof(VarDecl*));
}
SourceRange BlockDecl::getSourceRange() const {

View File

@ -462,7 +462,7 @@ public:
{ return Success(E); }
bool VisitCallExpr(CallExpr *E);
bool VisitBlockExpr(BlockExpr *E) {
if (!E->hasBlockDeclRefExprs())
if (!E->getBlockDecl()->hasCaptures())
return Success(E);
return false;
}

View File

@ -1935,7 +1935,7 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) {
}
case Stmt::BlockExprClass:
if (cast<BlockExpr>(E)->hasBlockDeclRefExprs())
if (cast<BlockExpr>(E)->getBlockDecl()->hasCaptures())
return E; // local block.
return NULL;

View File

@ -733,58 +733,106 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks) {
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.
///
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;
/// \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;
// If this is an enum constant or function, it is constant, don't snapshot.
if (isa<EnumConstantDecl>(VD) || isa<FunctionDecl>(VD))
return false;
case Sema::PotentiallyEvaluated:
case Sema::PotentiallyEvaluatedIfUsed:
break;
// If this is a reference to an extern, static, or global variable, no need to
// snapshot it.
// FIXME: What about 'const' variables in C++?
if (const VarDecl *Var = dyn_cast<VarDecl>(VD))
if (!Var->hasLocalStorage())
return false;
// Blocks that have these can't be constant.
CurBlock->hasBlockDeclRefExprs = true;
// 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]);
if (!NextBlock)
continue;
// 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;
// Otherwise, the DeclRef from the inner block causes the outer one to need
// a snapshot as well.
NextBlock->hasBlockDeclRefExprs = true;
case Sema::PotentiallyPotentiallyEvaluated:
// FIXME: delay these!
break;
}
return true;
// 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.
///
/// 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;
// Fast path: variables from the current context never require capture.
DeclContext *DC = S.CurContext;
if (var->getDeclContext() == DC) return CR_NoCapture;
// Only variables with local storage require capture.
// FIXME: What about 'const' variables in C++?
if (!var->hasLocalStorage()) return CR_NoCapture;
// Otherwise, we need to capture.
unsigned functionScopesIndex = S.FunctionScopes.size() - 1;
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);
BlockScopeInfo *blockScope =
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC));
// 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;
functionScopesIndex--;
DC = cast<BlockDecl>(DC)->getDeclContext();
} while (var->getDeclContext() != DC);
return CR_Capture;
}
ExprResult
Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
@ -811,17 +859,6 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty,
// 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.
@ -2250,66 +2287,74 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
// 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;
}
// The BlocksAttr indicates the variable is bound by-reference.
bool byrefVar = (VD->getAttr<BlocksAttr>() != 0);
QualType T = VD->getType();
BlockDeclRefExpr *BDRE;
// If we got here, we need to capture.
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,
@ -8486,6 +8531,12 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
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>();
@ -8557,8 +8608,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
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 =

View File

@ -16,6 +16,7 @@
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
@ -563,14 +564,23 @@ ExprResult Sema::ActOnCXXThis(SourceLocation ThisLoc) {
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
DeclContext *DC = getFunctionLevelDeclContext();
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC))
if (MD->isInstance())
return Owned(new (Context) CXXThisExpr(ThisLoc,
MD->getThisType(Context),
/*isImplicit=*/false));
// Ignore block scopes (but nothing else).
DeclContext *DC = CurContext;
while (isa<BlockDecl>(DC)) DC = cast<BlockDecl>(DC)->getDeclContext();
return ExprError(Diag(ThisLoc, diag::err_invalid_this_use));
// If we're not an instance method, error out.
CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC);
if (!method || !method->isInstance())
return ExprError(Diag(ThisLoc, diag::err_invalid_this_use));
// Mark that we're closing on 'this' in all the block scopes, if applicable.
for (unsigned idx = FunctionScopes.size() - 1;
isa<BlockScopeInfo>(FunctionScopes[idx]);
--idx)
cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
return Owned(new (Context) CXXThisExpr(ThisLoc, method->getThisType(Context),
/*isImplicit=*/false));
}
ExprResult

View File

@ -696,6 +696,15 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) {
for (unsigned I = 0; I != NumParams; ++I)
Params.push_back(cast<ParmVarDecl>(Reader.GetDecl(Record[Idx++])));
BD->setParams(Params.data(), NumParams);
bool capturesCXXThis = Record[Idx++];
unsigned numCapturedDecls = Record[Idx++];
llvm::SmallVector<VarDecl*, 16> capturedDecls;
capturedDecls.reserve(numCapturedDecls);
for (unsigned i = 0; i != numCapturedDecls; ++i)
capturedDecls.push_back(cast<VarDecl>(Reader.GetDecl(Record[Idx++])));
BD->setCapturedDecls(*Reader.getContext(), capturedDecls.begin(),
capturedDecls.end(), capturesCXXThis);
}
void ASTDeclReader::VisitLinkageSpecDecl(LinkageSpecDecl *D) {

View File

@ -791,7 +791,6 @@ void ASTStmtReader::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
void ASTStmtReader::VisitBlockExpr(BlockExpr *E) {
VisitExpr(E);
E->setBlockDecl(cast_or_null<BlockDecl>(Reader.GetDecl(Record[Idx++])));
E->setHasBlockDeclRefExprs(Record[Idx++]);
}
void ASTStmtReader::VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {

View File

@ -623,6 +623,12 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) {
for (FunctionDecl::param_iterator P = D->param_begin(), PEnd = D->param_end();
P != PEnd; ++P)
Writer.AddDeclRef(*P, Record);
Record.push_back(D->capturesCXXThis());
Record.push_back(D->getNumCapturedDecls());
for (BlockDecl::capture_iterator
i = D->capture_begin(), e = D->capture_end(); i != e; ++i)
Writer.AddDeclRef(*i, Record);
Code = serialization::DECL_BLOCK;
}

View File

@ -761,7 +761,6 @@ void ASTStmtWriter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
void ASTStmtWriter::VisitBlockExpr(BlockExpr *E) {
VisitExpr(E);
Writer.AddDeclRef(E->getBlockDecl(), Record);
Record.push_back(E->hasBlockDeclRefExprs());
Code = serialization::EXPR_BLOCK;
}

View File

@ -3417,7 +3417,7 @@ void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C,
// Scan the BlockDecRefExprs for any object the retain/release checker
// may be tracking.
if (!BE->hasBlockDeclRefExprs())
if (!BE->getBlockDecl()->hasCaptures())
return;
const GRState *state = C.getState();

View File

@ -56,7 +56,7 @@ static const BlockDeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
void
UndefCapturedBlockVarChecker::PostVisitBlockExpr(CheckerContext &C,
const BlockExpr *BE) {
if (!BE->hasBlockDeclRefExprs())
if (!BE->getBlockDecl()->hasCaptures())
return;
const GRState *state = C.getState();