Skip to content

Commit 5c6d986

Browse files
committed
SILGen: When emitting a partial application thunk for a dynamic method, convert @autoreleased result to @owned
In IRGen, @autoreleased return values are always converted to +1 by calling objc_retainAutoreleasedReturnValue(), so a partial application thunk cannot have a result with @autoreleased convention. Just turn it into @owned instead, since that's what it is, using similar logic as the @unowned_inner_pointer => @unowned case. Fixes <rdar://problem/24805609>.
1 parent 3ce1ba3 commit 5c6d986

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

Diff for: lib/SIL/SILVerifier.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
862862
require(resultInfo->getNumAllResults() == substTy->getNumAllResults(),
863863
"applied results do not agree in count with function type");
864864
for (unsigned i = 0, size = resultInfo->getNumAllResults(); i < size; ++i) {
865+
auto originalResult = resultInfo->getAllResults()[i];
865866
auto expectedResult = substTy->getAllResults()[i];
866867

867868
// The "returns inner pointer" convention doesn't survive through a
@@ -871,12 +872,24 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
871872
== ResultConvention::UnownedInnerPointer) {
872873
expectedResult = SILResultInfo(expectedResult.getType(),
873874
ResultConvention::Unowned);
874-
require(resultInfo->getAllResults()[i] == expectedResult,
875+
require(originalResult == expectedResult,
875876
"result type of result function type for partially applied "
876877
"@unowned_inner_pointer function should have @unowned"
877878
"convention");
879+
880+
// The "autoreleased" convention doesn't survive through a
881+
// partial application, since the thunk takes responsibility for
882+
// retaining the return value.
883+
} else if (expectedResult.getConvention()
884+
== ResultConvention::Autoreleased) {
885+
expectedResult = SILResultInfo(expectedResult.getType(),
886+
ResultConvention::Owned);
887+
require(originalResult == expectedResult,
888+
"result type of result function type for partially applied "
889+
"@autoreleased function should have @owned convention");
890+
878891
} else {
879-
require(resultInfo->getAllResults()[i] == expectedResult,
892+
require(originalResult == expectedResult,
880893
"result type of result function type does not match original "
881894
"function");
882895
}

Diff for: lib/SILGen/SILGenApply.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -4580,12 +4580,18 @@ static SILValue emitDynamicPartialApply(SILGenFunction &gen,
45804580
auto fnTy = method->getType().castTo<SILFunctionType>();
45814581

45824582
// If the original method has an @unowned_inner_pointer return, the partial
4583-
// application thunk will lifetime-extend 'self' for us.
4583+
// application thunk will lifetime-extend 'self' for us, converting the
4584+
// return value to @unowned.
4585+
//
4586+
// If the original method has an @autoreleased return, the partial application
4587+
// thunk will retain it for us, converting the return value to @owned.
45844588
SmallVector<SILResultInfo, 4> results;
45854589
results.append(fnTy->getAllResults().begin(), fnTy->getAllResults().end());
45864590
for (auto &result : results) {
45874591
if (result.getConvention() == ResultConvention::UnownedInnerPointer)
45884592
result = SILResultInfo(result.getType(), ResultConvention::Unowned);
4593+
else if (result.getConvention() == ResultConvention::Autoreleased)
4594+
result = SILResultInfo(result.getType(), ResultConvention::Owned);
45894595
}
45904596

45914597
auto partialApplyTy = SILFunctionType::get(fnTy->getGenericSignature(),

Diff for: test/Interpreter/SDK/objc_protocol_lookup.swift

+41
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,44 @@ func check(x: AnyObject) {
3737
check(NSString()) // CHECK: true true
3838
check(C()) // CHECK: true true
3939
check(D()) // CHECK: true true
40+
41+
// Make sure partial application of methods with @autoreleased
42+
// return values works
43+
44+
var count = 0
45+
46+
class Juice : NSObject {
47+
override init() {
48+
count += 1
49+
}
50+
51+
deinit {
52+
count -= 1
53+
}
54+
}
55+
56+
@objc protocol Fruit {
57+
optional var juice: Juice { get }
58+
}
59+
60+
class Durian : NSObject, Fruit {
61+
init(juice: Juice) {
62+
self.juice = juice
63+
}
64+
65+
var juice: Juice
66+
}
67+
68+
func consume(fruit: Fruit) {
69+
_ = fruit.juice
70+
}
71+
72+
autoreleasepool {
73+
let tasty = Durian(juice: Juice())
74+
print(count) // CHECK: 1
75+
consume(tasty)
76+
}
77+
78+
do {
79+
print(count) // CHECK: 0
80+
}

Diff for: test/SILGen/objc_currying.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func curry_pod_AnyObject(x: AnyObject) -> Int -> Int {
8484
// CHECK: dynamic_method_br [[SELF:%.*]] : $@opened({{.*}}) AnyObject, #CurryTest.normalOwnership!1.foreign, [[HAS_METHOD:bb[0-9]+]]
8585
// CHECK: [[HAS_METHOD]]([[METHOD:%.*]] : $@convention(objc_method) (ImplicitlyUnwrappedOptional<CurryTest>, @opened({{.*}}) AnyObject) -> @autoreleased ImplicitlyUnwrappedOptional<CurryTest>):
8686
// CHECK: [[PA:%.*]] = partial_apply [[METHOD]]([[SELF]])
87-
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo_dGSQCSo9CurryTest__aGSQS___XFo_oGSQS___oGSQS___
87+
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo_dGSQCSo9CurryTest__oGSQS___XFo_oGSQS___oGSQS___
8888
// CHECK: partial_apply [[THUNK]]([[PA]])
8989
func curry_normalOwnership_AnyObject(x: AnyObject) -> CurryTest! -> CurryTest! {
9090
return x.normalOwnership!
@@ -105,7 +105,7 @@ func curry_weirdOwnership_AnyObject(x: AnyObject) -> CurryTest! -> CurryTest! {
105105
// CHECK: dynamic_method_br [[SELF:%.*]] : $@opened({{.*}}) AnyObject, #CurryTest.bridged!1.foreign, [[HAS_METHOD:bb[0-9]+]]
106106
// CHECK: [[HAS_METHOD]]([[METHOD:%.*]] : $@convention(objc_method) (ImplicitlyUnwrappedOptional<NSString>, @opened({{.*}}) AnyObject) -> @autoreleased ImplicitlyUnwrappedOptional<NSString>):
107107
// CHECK: [[PA:%.*]] = partial_apply [[METHOD]]([[SELF]])
108-
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo_dGSQCSo8NSString__aGSQS___XFo_oGSQSS__oGSQSS__
108+
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo_dGSQCSo8NSString__oGSQS___XFo_oGSQSS__oGSQSS__
109109
// CHECK: partial_apply [[THUNK]]([[PA]])
110110
func curry_bridged_AnyObject(x: AnyObject) -> String! -> String! {
111111
return x.bridged!
@@ -117,7 +117,7 @@ func curry_bridged_AnyObject(x: AnyObject) -> String! -> String! {
117117
// CHECK: dynamic_method_br [[SELF:%.*]] : $@opened({{.*}}) AnyObject, #CurryTest.returnsSelf!1.foreign, [[HAS_METHOD:bb[0-9]+]]
118118
// CHECK: [[HAS_METHOD]]([[METHOD:%.*]] : $@convention(objc_method) (@opened({{.*}}) AnyObject) -> @autoreleased ImplicitlyUnwrappedOptional<AnyObject>):
119119
// CHECK: [[PA:%.*]] = partial_apply [[METHOD]]([[SELF]])
120-
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo__aGSQPs9AnyObject___XFo__oGSQPS____
120+
// CHECK: [[THUNK:%.*]] = function_ref @_TTRXFo__oGSQPs9AnyObject___XFo_iT__iGSQPS____
121121
// CHECK: partial_apply [[THUNK]]([[PA]])
122122
func curry_returnsSelf_AnyObject(x: AnyObject) -> () -> AnyObject! {
123123
return x.returnsSelf!

0 commit comments

Comments
 (0)