diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 12 | ||||
-rw-r--r-- | lib/Sema/JumpDiagnostics.cpp | 87 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 9 | ||||
-rw-r--r-- | test/SemaObjC/arc-jump-block.m | 84 |
4 files changed, 179 insertions, 13 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d7b16cf3f6..277ffcb54d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp index 8cabc92285..59bc2637c8 100644 --- a/lib/Sema/JumpDiagnostics.cpp +++ b/lib/Sema/JumpDiagnostics.cpp @@ -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); } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 09c68aef69..9464da84c1 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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); } diff --git a/test/SemaObjC/arc-jump-block.m b/test/SemaObjC/arc-jump-block.m new file mode 100644 index 0000000000..1c7b21e4e8 --- /dev/null +++ b/test/SemaObjC/arc-jump-block.m @@ -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 |