mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Fix a leak when emitting the native to foreign thunk for an async
function which fulfills an Objective-C protocol requirement which can be
fulfilled with either a value or an error via a nullable completion.
Previously, the SIL in question used to look like this:
```sil
%maybe_completion = ...
try_apply %impl..., normal success, ...
success(%value):
switch_enum %maybe_completion...
case some!enumelt: invoke
case none!enumelt: ignore
ignore:
br join
invoke(%completion):
%some_value = enum Optional, some!enumelt, %value // consumes %value
%guaranteed_some_value = begin_borrow %some_value
%none_error = enum Optional, none!enumelt
apply %completion(%guaranteed_some_value, %none_error)
end_borrow %guaranteed_some_value
destroy_value %some_value
br join
join:
destroy_value %maybe_completion
...
```
which leaks %value on the codepath through `ignore`.
Note that `%value` is consumed by the `enum` instruction, but
`%completion` is invoked with `%guaranteed_some_value`, a guaranteed
value. So there is no need to consume %value in `invoke`.
Here, `%value` itself is borrowed and forwarded into an enum instruction
whose result is passed to `%completion`:
```sil
%maybe_completion = ...
try_apply %impl..., normal success, ...
success(%value):
switch_enum %maybe_completion...
case some!enumelt: invoke
case none!enumelt: ignore
ignore:
br join
invoke(%completion):
%guaranteed_value = begin_borrow %value
%guaranteed_some_value = enum Optional, some!enumelt, %guaranteed_value
%none_error = enum Optional, none!enumelt
apply %completion(%guaranteed_some_value, %none_error)
end_borrow %guaranteed_some_value
br join
join:
destroy_value %maybe_completion
destroy_value %value
...
```
Because an argument scope was already being created and a cleanup was
already being pushed for `%value`, nothing more is required to fix the
issue than to reorder the enum and the borrow.
rdar://119732084
68 lines
2.8 KiB
Swift
68 lines
2.8 KiB
Swift
// RUN: %empty-directory(%t)
|
|
// RUN: split-file %s %t
|
|
// RUN: %target-swift-emit-silgen -module-name Conformance -I %t %t/Conformance.swift -disable-availability-checking | %FileCheck %s
|
|
|
|
// REQUIRES: concurrency
|
|
// REQUIRES: objc_interop
|
|
|
|
//--- module.modulemap
|
|
module ObjCProto {
|
|
header "objc_proto.h"
|
|
export *
|
|
}
|
|
|
|
//--- objc_proto.h
|
|
#import <Foundation/Foundation.h>
|
|
|
|
@protocol Doable <NSObject>
|
|
|
|
- (void)doitWithCompletion:(void (^ __nullable)(NSObject * __nullable result, NSError * __nullable error))completion;
|
|
|
|
@end
|
|
|
|
//--- Conformance.swift
|
|
import ObjCProto
|
|
|
|
public final class ConformsToProto: NSObject {}
|
|
|
|
extension ConformsToProto: Doable {
|
|
// CHECK-LABEL: sil shared [thunk] [ossa] @$s11Conformance15ConformsToProtoC4doitSo8NSObjectCyYaKFyyYacfU_To : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[UNOWNED_MAYBE_COMPLETION:%[^,]+]] :
|
|
// CHECK: [[MAYBE_COMPLETION:%[^,]+]] = copy_block [[UNOWNED_MAYBE_COMPLETION]]
|
|
// CHECK: try_apply {{.*}} normal [[SUCCESS:bb[0-9]+]], error [[FAILURE:bb[0-9]+]]
|
|
// CHECK: [[SUCCESS]]([[RESULT:%[^,]+]] :
|
|
// CHECK: [[GUARANTEED_MAYBE_COMPLETION_SUCCESS:%[^,]+]] = begin_borrow [[MAYBE_COMPLETION]]
|
|
// CHECK: switch_enum [[GUARANTEED_MAYBE_COMPLETION_SUCCESS]]
|
|
// CHECK-SAME: case #Optional.some!enumelt: [[SUCCESS_AND_COMPLETION:bb[0-9]+]]
|
|
// CHECK-SAME: case #Optional.none!enumelt: [[SUCCESS_NO_COMPLETION:bb[0-9]+]]
|
|
// CHECK: [[SUCCESS_NO_COMPLETION]]:
|
|
// CHECK: br [[SUCCESS_JOIN:bb[0-9]+]]
|
|
// CHECK: [[SUCCESS_AND_COMPLETION]]
|
|
// CHECK: br [[SUCCESS_JOIN]]
|
|
// CHECK: [[SUCCESS_JOIN]]:
|
|
// CHECK: end_borrow [[GUARANTEED_MAYBE_COMPLETION_SUCCESS]]
|
|
// CHECK: destroy_value [[MAYBE_COMPLETION]]
|
|
// CHECK: destroy_value [[RESULT]]
|
|
// CHECK: br [[EXIT:bb[0-9]+]]
|
|
// CHECK: [[FAILURE]]([[ERROR:%[^,]+]] :
|
|
// CHECK: [[GUARANTEED_MAYBE_COMPLETION_FAILURE:%[^,]+]] = begin_borrow [[MAYBE_COMPLETION]]
|
|
// CHECK: switch_enum [[GUARANTEED_MAYBE_COMPLETION_FAILURE]]
|
|
// CHECK-SAME: case #Optional.some!enumelt: [[FAILURE_AND_COMPLETION:bb[0-9]+]]
|
|
// CHECK-SAME: case #Optional.none!enumelt: [[FAILURE_NO_COMPLETION:bb[0-9]+]]
|
|
// CHECK: [[FAILURE_NO_COMPLETION]]:
|
|
// CHECK: br [[FAILURE_JOIN:bb[0-9]+]]
|
|
// CHECK: [[FAILURE_AND_COMPLETION]]
|
|
// CHECK: br [[FAILURE_JOIN]]
|
|
// CHECK: [[FAILURE_JOIN]]:
|
|
// CHECK: end_borrow [[GUARANTEED_MAYBE_COMPLETION_FAILURE]]
|
|
// CHECK: destroy_value [[MAYBE_COMPLETION]]
|
|
// CHECK: destroy_value [[ERROR]]
|
|
// CHECK: br [[EXIT]]
|
|
// CHECK: [[EXIT]]:
|
|
// CHECK-LABEL: } // end sil function '$s11Conformance15ConformsToProtoC4doitSo8NSObjectCyYaKFyyYacfU_To'
|
|
public func doit() async throws -> NSObject {
|
|
fatalError()
|
|
}
|
|
}
|
|
|