In ARC, don't try to reclaim the result of a call to performSelector
unless done in a context where the value is used retained.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136769 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 0061db9..ef4944e 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -4067,21 +4067,24 @@
// actual method. FIXME: we should infer retention by selector in
// cases where we don't have an actual method.
} else {
- Decl *D = 0;
+ ObjCMethodDecl *D = 0;
if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
D = Send->getMethodDecl();
} else {
CastExpr *CE = cast<CastExpr>(E);
- // FIXME. What other cast kinds to check for?
- if (CE->getCastKind() == CK_ObjCProduceObject ||
- CE->getCastKind() == CK_LValueToRValue)
- return MaybeBindToTemporary(CE->getSubExpr());
assert(CE->getCastKind() == CK_GetObjCProperty);
const ObjCPropertyRefExpr *PRE = CE->getSubExpr()->getObjCProperty();
D = (PRE->isImplicitProperty() ? PRE->getImplicitPropertyGetter() : 0);
}
ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
+
+ // Don't do reclaims on performSelector calls; despite their
+ // return type, the invoked method doesn't necessarily actually
+ // return an object.
+ if (!ReturnsRetained &&
+ D && D->getMethodFamily() == OMF_performSelector)
+ return Owned(E);
}
ExprNeedsCleanups = true;
diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m
index 109e360..4592710 100644
--- a/test/CodeGenObjC/arc.m
+++ b/test/CodeGenObjC/arc.m
@@ -1754,3 +1754,39 @@
// CHECK-NEXT: call void @objc_release(i8* [[T2]])
// CHECK-NEXT: ret void
}
+
+// Verify that we don't try to reclaim the result of performSelector.
+// rdar://problem/9887545
+@interface Test61
+- (id) performSelector: (SEL) selector;
+- (void) test61_void;
+- (id) test61_id;
+@end
+void test61(void) {
+ // CHECK: define void @test61()
+ // CHECK: [[Y:%.*]] = alloca i8*, align 8
+
+ extern id test61_make(void);
+
+ // CHECK-NEXT: [[T0:%.*]] = call i8* @test61_make()
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[T3:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[T4:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i8*)*)(i8* [[T1]], i8* [[T3]], i8* [[T2]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
+ [test61_make() performSelector: @selector(test61_void)];
+
+ // CHECK-NEXT: [[T0:%.*]] = call i8* @test61_make()
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[T3:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[T4:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i8*)*)(i8* [[T1]], i8* [[T3]], i8* [[T2]])
+ // CHECK-NEXT: [[T5:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T4]])
+ // CHECK-NEXT: store i8* [[T5]], i8** [[Y]]
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
+ id y = [test61_make() performSelector: @selector(test61_id)];
+
+ // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]]
+ // CHECK-NEXT: call void @objc_release(i8* [[T0]])
+ // CHECK-NEXT: ret void
+}