Files
swift-mirror/validation-test/SILGen/rdar119732084.swift
Nate Chandler 9f5a7c87c3 [SILGen] Fix leak in objc async thunk.
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
2024-01-04 19:10:17 -08:00

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()
}
}