]> granicus.if.org Git - clang/commitdiff
-Warc-retain-cycles: look through [^{...} copy] and Block_copy(^{...})
authorJordan Rose <jordan_rose@apple.com>
Mon, 17 Sep 2012 17:54:30 +0000 (17:54 +0000)
committerJordan Rose <jordan_rose@apple.com>
Mon, 17 Sep 2012 17:54:30 +0000 (17:54 +0000)
Retain cycles happen in the case where a block is persisted past its
life on the stack, and the way that occurs is by copying the block.
We should thus look through any explicit copies we see.

Note that Block_copy is actually a type-safe wrapper for _Block_copy,
which does all the real work.

<rdar://problem/12219663>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164039 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaChecking.cpp
test/SemaObjC/warn-retain-cycle.m

index 6d8d7f2c0446b0f3d38b934f3529c562a14f342d..4c8418a5335ce2aad020db9a52e042fb3a01799a 100644 (file)
@@ -5429,6 +5429,24 @@ static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) {
   assert(owner.Variable && owner.Loc.isValid());
 
   e = e->IgnoreParenCasts();
+
+  // Look through [^{...} copy] and Block_copy(^{...}).
+  if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(e)) {
+    Selector Cmd = ME->getSelector();
+    if (Cmd.isUnarySelector() && Cmd.getNameForSlot(0) == "copy") {
+      e = ME->getInstanceReceiver();
+      if (!e)
+        return 0;
+      e = e->IgnoreParenCasts();
+    }
+  } else if (CallExpr *CE = dyn_cast<CallExpr>(e)) {
+    if (CE->getNumArgs() == 1) {
+      FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
+      if (Fn && Fn->getIdentifier()->isStr("_Block_copy"))
+        e = CE->getArg(0)->IgnoreParenCasts();
+    }
+  }
+  
   BlockExpr *block = dyn_cast<BlockExpr>(e);
   if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable))
     return 0;
index 1a13fe37260ba0e81daef0770bb362ed5500b0ae..fb8f2b77f0676b3bbaf0c1c164aff573387a1ff8 100644 (file)
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -verify -Wno-objc-root-class %s
 
+void *_Block_copy(const void *block);
+
 @interface Test0
 - (void) setBlock: (void(^)(void)) block;
 - (void) addBlock: (void(^)(void)) block;
@@ -153,3 +155,32 @@ void testBlockVariable() {
   };
 }
 
+
+@interface NSObject
+- (id)copy;
+
+- (void (^)(void))someRandomMethodReturningABlock;
+@end
+
+
+void testCopying(Test0 *obj) {
+  typedef void (^block_t)(void);
+
+  [obj setBlock:[^{ // expected-note{{block will be retained by the captured object}}
+    [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}}
+  } copy]];
+
+  [obj addBlock:(__bridge_transfer block_t)_Block_copy((__bridge void *)^{ // expected-note{{block will be retained by the captured object}}
+    [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}}
+  })];
+  
+  [obj addBlock:[^{
+    [obj actNow]; // no-warning
+  } someRandomMethodReturningABlock]];
+  
+  extern block_t someRandomFunctionReturningABlock(block_t);
+  [obj setBlock:someRandomFunctionReturningABlock(^{
+    [obj actNow]; // no-warning
+  })];
+}
+