objc-arc: Diagnose when captured variable in block literals

require destruction and there is possibility of that without
construction. Thanks Johnm for review and suggestions offline.
// rdar://9535237.

llvm-svn: 134906
This commit is contained in:
Fariborz Jahanian 2011-07-11 18:04:54 +00:00
parent 761feb85e1
commit 256d39d47d
4 changed files with 179 additions and 13 deletions

View File

@ -2511,6 +2511,12 @@ def note_protected_by___block : Note<
"jump bypasses setup of __block variable">;
def note_protected_by_objc_ownership : Note<
"jump bypasses initialization of retaining variable">;
def note_enters_block_captures_cxx_obj : Note<
"jump enters lifetime of block which captures a destructible c++ object">;
def note_enters_block_captures_strong : Note<
"jump enters lifetime of block which strongly captures a variable">;
def note_enters_block_captures_weak : Note<
"jump enters lifetime of block which weakly captures a variable">;
def note_exits_cleanup : Note<
"jump exits scope of variable with __attribute__((cleanup))">;
@ -2534,6 +2540,12 @@ def note_exits_objc_autoreleasepool : Note<
"jump exits autoreleasepool block">;
def note_exits_objc_ownership : Note<
"jump exits scope of retaining variable">;
def note_exits_block_captures_cxx_obj : Note<
"jump exits lifetime of block which captures a destructible c++ object">;
def note_exits_block_captures_strong : Note<
"jump exits lifetime of block which strongly captures a variable">;
def note_exits_block_captures_weak : Note<
"jump exits lifetime of block which weakly captures a variable">;
def err_func_returning_array_function : Error<
"function cannot return %select{array|function}0 type %1">;

View File

@ -68,7 +68,10 @@ public:
JumpScopeChecker(Stmt *Body, Sema &S);
private:
void BuildScopeInformation(Decl *D, unsigned &ParentScope);
void BuildScopeInformation(Stmt *S, unsigned ParentScope);
void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
unsigned &ParentScope);
void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
void VerifyJumps();
void VerifyIndirectJumps();
void DiagnoseIndirectJump(IndirectGotoStmt *IG, unsigned IGScope,
@ -87,7 +90,8 @@ JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s) : S(s) {
// Build information for the top level compound statement, so that we have a
// defined scope record for every "goto" and label.
BuildScopeInformation(Body, 0);
unsigned BodyParentScope = 0;
BuildScopeInformation(Body, BodyParentScope);
// Check that all jumps we saw are kosher.
VerifyJumps();
@ -228,11 +232,55 @@ void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
BuildScopeInformation(Init, ParentScope);
}
/// \brief Build scope information for a captured block literal variables.
void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
const BlockDecl *BDecl,
unsigned &ParentScope) {
// exclude captured __block variables; there's no destructor
// associated with the block literal for them.
if (D->hasAttr<BlocksAttr>())
return;
QualType T = D->getType();
QualType::DestructionKind destructKind = T.isDestructedType();
if (destructKind != QualType::DK_none) {
std::pair<unsigned,unsigned> Diags;
switch (destructKind) {
case QualType::DK_cxx_destructor:
Diags = ScopePair(diag::note_enters_block_captures_cxx_obj,
diag::note_exits_block_captures_cxx_obj);
break;
case QualType::DK_objc_strong_lifetime:
Diags = ScopePair(diag::note_enters_block_captures_strong,
diag::note_exits_block_captures_strong);
break;
case QualType::DK_objc_weak_lifetime:
Diags = ScopePair(diag::note_enters_block_captures_weak,
diag::note_exits_block_captures_weak);
break;
case QualType::DK_none:
llvm_unreachable("no-liftime captured variable");
}
SourceLocation Loc = D->getLocation();
if (Loc.isInvalid())
Loc = BDecl->getLocation();
Scopes.push_back(GotoScope(ParentScope,
Diags.first, Diags.second, Loc));
ParentScope = Scopes.size()-1;
}
}
/// BuildScopeInformation - The statements from CI to CE are known to form a
/// coherent VLA scope with a specified parent node. Walk through the
/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
/// walking the AST as needed.
void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope) {
// If this is a statement, rather than an expression, scopes within it don't
// propagate out into the enclosing scope. Otherwise we have to worry
// about block literals, which have the lifetime of their enclosing statement.
unsigned independentParentScope = origParentScope;
unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(S))
? origParentScope : independentParentScope);
bool SkipFirstSubStmt = false;
// If we found a label, remember that it is in ParentScope scope.
@ -314,17 +362,17 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
BuildScopeInformation(*I, ParentScope);
continue;
}
// Disallow jumps into any part of an @try statement by pushing a scope and
// walking all sub-stmts in that scope.
if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) {
unsigned newParentScope;
// Recursively walk the AST for the @try part.
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_objc_try,
diag::note_exits_objc_try,
AT->getAtTryLoc()));
if (Stmt *TryPart = AT->getTryBody())
BuildScopeInformation(TryPart, Scopes.size()-1);
BuildScopeInformation(TryPart, (newParentScope = Scopes.size()-1));
// Jump from the catch to the finally or try is not valid.
for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) {
@ -334,7 +382,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_exits_objc_catch,
AC->getAtCatchLoc()));
// @catches are nested and it isn't
BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1);
BuildScopeInformation(AC->getCatchBody(),
(newParentScope = Scopes.size()-1));
}
// Jump from the finally to the try or catch is not valid.
@ -343,12 +392,13 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_objc_finally,
diag::note_exits_objc_finally,
AF->getAtFinallyLoc()));
BuildScopeInformation(AF, Scopes.size()-1);
BuildScopeInformation(AF, (newParentScope = Scopes.size()-1));
}
continue;
}
unsigned newParentScope;
// Disallow jumps into the protected statement of an @synchronized, but
// allow jumps into the object expression it protects.
if (ObjCAtSynchronizedStmt *AS = dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)){
@ -362,7 +412,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_objc_synchronized,
diag::note_exits_objc_synchronized,
AS->getAtSynchronizedLoc()));
BuildScopeInformation(AS->getSynchBody(), Scopes.size()-1);
BuildScopeInformation(AS->getSynchBody(),
(newParentScope = Scopes.size()-1));
continue;
}
@ -374,7 +425,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_exits_cxx_try,
TS->getSourceRange().getBegin()));
if (Stmt *TryBlock = TS->getTryBlock())
BuildScopeInformation(TryBlock, Scopes.size()-1);
BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1));
// Jump from the catch into the try is not allowed either.
for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) {
@ -383,7 +434,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_cxx_catch,
diag::note_exits_cxx_catch,
CS->getSourceRange().getBegin()));
BuildScopeInformation(CS->getHandlerBlock(), Scopes.size()-1);
BuildScopeInformation(CS->getHandlerBlock(),
(newParentScope = Scopes.size()-1));
}
continue;
@ -397,10 +449,19 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_objc_autoreleasepool,
diag::note_exits_objc_autoreleasepool,
AS->getAtLoc()));
BuildScopeInformation(AS->getSubStmt(), Scopes.size()-1);
BuildScopeInformation(AS->getSubStmt(), (newParentScope = Scopes.size()-1));
continue;
}
if (const BlockExpr *BE = dyn_cast<BlockExpr>(SubStmt)) {
const BlockDecl *BDecl = BE->getBlockDecl();
for (BlockDecl::capture_const_iterator ci = BDecl->capture_begin(),
ce = BDecl->capture_end(); ci != ce; ++ci) {
VarDecl *variable = ci->getVariable();
BuildScopeInformation(variable, BDecl, ParentScope);
}
}
// Recursively walk the AST.
BuildScopeInformation(SubStmt, ParentScope);
}

View File

@ -8501,6 +8501,15 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
const AnalysisBasedWarnings::Policy &WP = AnalysisWarnings.getDefaultPolicy();
PopFunctionOrBlockScope(&WP, Result->getBlockDecl(), Result);
for (BlockDecl::capture_const_iterator ci = BSI->TheDecl->capture_begin(),
ce = BSI->TheDecl->capture_end(); ci != ce; ++ci) {
const VarDecl *variable = ci->getVariable();
QualType T = variable->getType();
QualType::DestructionKind destructKind = T.isDestructedType();
if (destructKind != QualType::DK_none)
getCurFunction()->setHasBranchProtectedScope();
}
return Owned(Result);
}

View File

@ -0,0 +1,84 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s
// rdar://9535237
typedef struct dispatch_queue_s *dispatch_queue_t;
typedef void (^dispatch_block_t)(void);
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
extern __attribute__((visibility("default"))) struct dispatch_queue_s _dispatch_main_q;
@interface SwitchBlockCrashAppDelegate
- (void)pageLeft;
- (void)pageRight;;
@end
@implementation SwitchBlockCrashAppDelegate
- (void)choose:(int)button {
switch (button) {
case 0:
dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; }); // expected-note 3 {{jump enters lifetime of block which strongly captures a variable}}
break;
case 2: // expected-error {{switch case is in protected scope}}
dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); // expected-note 2 {{jump enters lifetime of block which strongly captures a variable}}
break;
case 3: // expected-error {{switch case is in protected scope}}
{
dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; });
break;
}
case 4: // expected-error {{switch case is in protected scope}}
break;
}
__block SwitchBlockCrashAppDelegate *captured_block_obj;
switch (button) {
case 10:
{
dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; });
break;
}
case 12:
if (button)
dispatch_async((&_dispatch_main_q), ^{ [captured_block_obj pageRight]; });
break;
case 13:
while (button)
dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; });
break;
case 14:
break;
}
switch (button) {
case 10:
{
dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; });
break;
}
case 12:
if (button)
dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; });
switch (button) {
case 0:
{
dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; });
break;
}
case 4:
break;
}
break;
case 13:
while (button)
dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; });
break;
case 14:
break;
}
}
- (void)pageLeft {}
- (void)pageRight {}
@end