Files
swift-mirror/test/Concurrency/transfernonsendable.sil
Erik Eckstein 0f0aa0c17b Optimizer: require that there are no unreachable blocks and infinite loops in OSSA
These two new invariants eliminate corner cases which caused bugs if optimization didn't handle them.
Also, it will significantly simplify lifetime completion.

The implementation basically consists of these changes:
* add a flag in SILFunction which tells optimization if they need to take care of infinite loops
* add a utility to break infinite loops
* let all optimizations remove unreachable blocks and break infinite loops if necessary
* add verification to check the new SIL invariants

The new `breakIfniniteLoops` utility breaks infinite loops in the control flow by inserting an "artificial" loop exit to a new dead-end block with an `unreachable`.
It inserts a `cond_br` with a `builtin "infinite_loop_true_condition"`:
```
bb0:
  br bb1
bb1:
  br bb1              // back-end branch
```
->
```
bb0:
  br bb1
bb1:
  %1 = builtin "infinite_loop_true_condition"() // always true, but the compiler doesn't know
  cond_br %1, bb2, bb3
bb2:                  // new back-end block
  br bb1
bb3:                  // new dead-end block
  unreachable
```
2026-01-22 17:41:23 +01:00

445 lines
25 KiB
Plaintext

// RUN: %target-sil-opt -send-non-sendable -strict-concurrency=complete %s -o /dev/null -verify
// REQUIRES: concurrency
// REQUIRES: asserts
// PLEASE READ THIS!
//
// This test is specifically meant for small test cases that come from bugs that
// do not categorize by a specific category. If this gets too big, please split
// sections of tests out if possible.
sil_stage raw
import Swift
import Builtin
import _Concurrency
////////////////////////
// MARK: Declarations //
////////////////////////
class Klass {}
class NonSendableKlass { // expected-note 4{{}}
var klass: Klass
func asyncCall() async
@MainActor static func getValue() -> sending NonSendableKlass
}
sil @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
sil @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
sil @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
sil @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
sil @useUnmanagedNonSendableKlass : $@convention(thin) (@guaranteed @sil_unmanaged NonSendableKlass) -> ()
sil @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
sil @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
final class SendableKlass : Sendable {}
sil @transferSendableKlass : $@convention(thin) @async (@guaranteed SendableKlass) -> ()
sil @constructSendableKlass : $@convention(thin) () -> @owned SendableKlass
final class KlassContainingKlasses {
let nsImmutable : NonSendableKlass
var nsMutable : NonSendableKlass
let sImmutable : SendableKlass
var sMutable : SendableKlass
}
sil @transferKlassContainingKlasses : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> ()
sil @useKlassContainingKlasses : $@convention(thin) (@guaranteed KlassContainingKlasses) -> ()
sil @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
struct NonSendableMoveOnlyStruct: ~Copyable {
var ns: NonSendableKlass
deinit
}
sil @constructMoveOnlyStruct : $@convention(thin) () -> @owned NonSendableMoveOnlyStruct
sil @transferMoveOnlyStruct : $@convention(thin) @async (@guaranteed NonSendableMoveOnlyStruct) -> ()
struct NonSendableStruct {
var ns: NonSendableKlass
}
sil @constructStruct : $@convention(thin) () -> @owned NonSendableStruct
sil @transferStruct : $@convention(thin) @async (@guaranteed NonSendableStruct) -> ()
sil @transferRawPointer : $@convention(thin) @async (Builtin.RawPointer) -> ()
sil @useRawPointer : $@convention(thin) (Builtin.RawPointer) -> ()
sil @initRawPointer : $@convention(thin) () -> Builtin.RawPointer
sil @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
sil @transferIndirectWithOutResult : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
sil @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
sil @initIndirect : $@convention(thin) <T> () -> @out T
sil @initIndirectTransferring : $@convention(thin) @async <T> () -> @out T
sil @initIndirectTransferringError : $@convention(thin) @async <T> () -> (@out Optional<T>, @error any Error)
enum FakeOptional<T> {
case none
case some(T)
}
sil @swift_asyncLet_get : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
@MainActor
struct MainActorIsolatedStruct {
let ns: NonSendableKlass
}
@MainActor
enum MainActorIsolatedEnum {
case first
case second(NonSendableKlass)
}
actor MyActor {
var klass: NonSendableKlass { get set }
}
sil @beginApplyMultipleResultCallee : $@yield_once @convention(thin) () -> (@yields @guaranteed NonSendableKlass, @yields @guaranteed NonSendableKlass)
/////////////////
// MARK: Tests //
/////////////////
// This goes through the access projection code differently from normal
// project_box since we do not see the alloc_box.
sil [ossa] @project_box_loadable_test_case : $@convention(thin) @async (@in { var NonSendableKlass }) -> () {
bb0(%0 : $*{ var NonSendableKlass }):
%1 = load [take] %0 : $*{ var NonSendableKlass }
%3 = project_box %1 : ${ var NonSendableKlass }, 0
%f = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%3) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// expected-warning @-1 {{}}
// expected-note @-2 {{}}
destroy_value %1 : ${ var NonSendableKlass }
%9999 = tuple ()
return %9999 : $()
}
sil [ossa] @transfer_does_not_transfer_out_parameters_1 : $@convention(thin) @async () -> () {
bb0:
%0 = alloc_stack $NonSendableKlass
%init = function_ref @initIndirect : $@convention(thin) <T> () -> @out T
apply %init<NonSendableKlass>(%0) : $@convention(thin) <T> () -> @out T
%1 = alloc_stack $NonSendableKlass
%f = function_ref @transferIndirectWithOutResult : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 // expected-warning {{}}
%useIndirect = function_ref @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
apply %useIndirect<NonSendableKlass>(%1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
destroy_addr %1 : $*NonSendableKlass
dealloc_stack %1 : $*NonSendableKlass
destroy_addr %0 : $*NonSendableKlass
dealloc_stack %0 : $*NonSendableKlass
%9999 = tuple ()
return %9999 : $()
}
sil [ossa] @transfer_does_not_transfer_out_parameters_2 : $@convention(thin) @async () -> () {
bb0:
%0 = alloc_stack $NonSendableKlass
%init = function_ref @initIndirect : $@convention(thin) <T> () -> @out T
apply %init<NonSendableKlass>(%0) : $@convention(thin) <T> () -> @out T
%1 = alloc_stack $NonSendableKlass
%f = function_ref @transferIndirectWithOutResult : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 // expected-warning {{}}
// We do not error here on use after send since %1 is already isolated to the
// same global actor as transferIndirect which is not considered to be a
// send. We leave the actual error to be the error about returning an isolated
// value into a non-isolated context.
%f2 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f2<NonSendableKlass>(%1) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
%useIndirect = function_ref @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
apply %useIndirect<NonSendableKlass>(%1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
destroy_addr %1 : $*NonSendableKlass
dealloc_stack %1 : $*NonSendableKlass
destroy_addr %0 : $*NonSendableKlass
dealloc_stack %0 : $*NonSendableKlass
%9999 = tuple ()
return %9999 : $()
}
sil @implicitClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
sil [ossa] @asyncLetWithThinToThickFunction : $@convention(thin) @async () -> () {
bb0:
%0 = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
hop_to_executor %0 : $Optional<Builtin.Executor>
%2 = alloc_stack $NonSendableKlass
%3 = address_to_pointer %2 : $*NonSendableKlass to $Builtin.RawPointer
%4 = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
%5 = function_ref @implicitClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
%6 = convert_function %5 : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass> to $@convention(thin) @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
%7 = thin_to_thick_function %6 : $@convention(thin) @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass> to $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
%8 = builtin "startAsyncLetWithLocalBuffer"<NonSendableKlass>(%4 : $Optional<Builtin.RawPointer>, %7 : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>, %3 : $Builtin.RawPointer) : $Builtin.RawPointer
%9 = function_ref @swift_asyncLet_get : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
%10 = apply %9(%8, %3) : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
hop_to_executor %0 : $Optional<Builtin.Executor>
%12 = load [copy] %2 : $*NonSendableKlass
destroy_value %12 : $NonSendableKlass
%15 = builtin "finishAsyncLet"(%8, %3) : $()
hop_to_executor %0 : $Optional<Builtin.Executor>
%17 = builtin "endAsyncLetLifetime"(%8 : $Builtin.RawPointer) : $()
dealloc_stack %2 : $*NonSendableKlass
%19 = tuple ()
return %19 : $()
}
sil [ossa] @synchronous_returns_transferring : $@convention(method) (@guaranteed NonSendableStruct) -> @sil_sending @owned NonSendableKlass {
bb0(%0 : @guaranteed $NonSendableStruct):
debug_value %0 : $NonSendableStruct, var, name "myname"
%2 = struct_extract %0 : $NonSendableStruct, #NonSendableStruct.ns
%3 = copy_value %2 : $NonSendableKlass
return %3 : $NonSendableKlass // expected-warning {{sending 'myname.ns' risks causing data races}}
// expected-note @-1 {{task-isolated 'myname.ns' cannot be a 'sending' result. task-isolated uses may race with caller uses}}
}
sil [ossa] @synchronous_returns_transferring_globalactor_struct_structextract : $@convention(method) (@guaranteed MainActorIsolatedStruct) -> @sil_sending @owned NonSendableKlass {
bb0(%0 : @guaranteed $MainActorIsolatedStruct):
debug_value %0 : $MainActorIsolatedStruct, var, name "myname"
%2 = struct_extract %0 : $MainActorIsolatedStruct, #MainActorIsolatedStruct.ns
%3 = copy_value %2 : $NonSendableKlass
return %3 : $NonSendableKlass // expected-warning {{sending 'myname.ns' risks causing data races}}
// expected-note @-1 {{main actor-isolated 'myname.ns' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}
sil [ossa] @synchronous_returns_transferring_globalactor_struct_structelementaddr : $@convention(method) (@in_guaranteed MainActorIsolatedStruct) -> @sil_sending @owned NonSendableKlass {
bb0(%0 : $*MainActorIsolatedStruct):
debug_value %0 : $*MainActorIsolatedStruct, var, name "myname"
%2 = struct_element_addr %0 : $*MainActorIsolatedStruct, #MainActorIsolatedStruct.ns
%3 = load [copy] %2 : $*NonSendableKlass
return %3 : $NonSendableKlass // expected-warning {{sending 'myname.ns' risks causing data races}}
// expected-note @-1 {{main actor-isolated 'myname.ns' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}
sil [ossa] @synchronous_returns_transferring_globalactor_enum_uncheckedenumdata : $@convention(method) (@guaranteed MainActorIsolatedEnum) -> @sil_sending @owned NonSendableKlass {
bb0(%0 : @guaranteed $MainActorIsolatedEnum):
debug_value %0 : $MainActorIsolatedEnum, var, name "myname"
%2 = unchecked_enum_data %0 : $MainActorIsolatedEnum, #MainActorIsolatedEnum.second!enumelt
%3 = copy_value %2 : $NonSendableKlass
return %3 : $NonSendableKlass // expected-warning {{sending 'myname.second' risks causing data races}}
// expected-note @-1 {{main actor-isolated 'myname.second' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}
sil [ossa] @synchronous_returns_transferring_globalactor_enum_uncheckedtakeenumdataaddr : $@convention(method) (@in MainActorIsolatedEnum) -> @sil_sending @owned NonSendableKlass {
bb0(%0 : $*MainActorIsolatedEnum):
debug_value %0 : $*MainActorIsolatedEnum, var, name "myname"
%2 = unchecked_take_enum_data_addr %0 : $*MainActorIsolatedEnum, #MainActorIsolatedEnum.second!enumelt
%3 = load [take] %2 : $*NonSendableKlass
return %3 : $NonSendableKlass // expected-warning {{sending 'myname.second' risks causing data races}}
// expected-note @-1 {{main actor-isolated 'myname.second' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}
sil [ossa] @synchronous_returns_transferring_globalactor_enum_switchenum : $@convention(method) (@guaranteed MainActorIsolatedEnum) -> @sil_sending @owned FakeOptional<NonSendableKlass> {
bb0(%0 : @guaranteed $MainActorIsolatedEnum):
debug_value %0 : $MainActorIsolatedEnum, var, name "myname"
switch_enum %0 : $MainActorIsolatedEnum, case #MainActorIsolatedEnum.first!enumelt: bb1, case #MainActorIsolatedEnum.second!enumelt: bb2
bb1:
%nil = enum $FakeOptional<NonSendableKlass>, #FakeOptional.none!enumelt
br bb3(%nil : $FakeOptional<NonSendableKlass>)
bb2(%1 : @guaranteed $NonSendableKlass):
%2 = copy_value %1 : $NonSendableKlass
%3 = enum $FakeOptional<NonSendableKlass>, #FakeOptional.some!enumelt, %2 : $NonSendableKlass
br bb3(%3 : $FakeOptional<NonSendableKlass>)
bb3(%4 : @owned $FakeOptional<NonSendableKlass>):
return %4 : $FakeOptional<NonSendableKlass> // expected-warning {{sending 'myname.some' risks causing data races}}
// expected-note @-1 {{main actor-isolated 'myname.some' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}
sil [ossa] @warningIfCallingGetter : $@convention(method) @async (@sil_isolated @guaranteed MyActor) -> () {
bb0(%0 : @guaranteed $MyActor):
debug_value %0 : $MyActor, let, name "self", argno 1
hop_to_executor %0 : $MyActor
%3 = class_method %0 : $MyActor, #MyActor.klass!getter : (isolated MyActor) -> () -> NonSendableKlass, $@convention(method) (@sil_isolated @guaranteed MyActor) -> @owned NonSendableKlass
%4 = apply %3(%0) : $@convention(method) (@sil_isolated @guaranteed MyActor) -> @owned NonSendableKlass
%5 = class_method %4 : $NonSendableKlass, #NonSendableKlass.asyncCall : (NonSendableKlass) -> () async -> (), $@convention(method) @async (@guaranteed NonSendableKlass) -> ()
%6 = apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %5(%4) : $@convention(method) @async (@guaranteed NonSendableKlass) -> () // expected-warning {{}}
// expected-note @-1 {{}}
destroy_value %4 : $NonSendableKlass
hop_to_executor %0 : $MyActor
%9 = tuple ()
return %9 : $()
}
sil [ossa] @assignIntoSetter : $@convention(method) @async (@sil_isolated @guaranteed MyActor) -> () {
bb0(%0 : @guaranteed $MyActor):
debug_value %0 : $MyActor, let, name "self", argno 1
hop_to_executor %0 : $MyActor
%4 = function_ref @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
%5 = apply %4() : $@convention(thin) () -> @owned NonSendableKlass
%6 = move_value [lexical] [var_decl] %5 : $NonSendableKlass
debug_value %6 : $NonSendableKlass, let, name "x"
%8 = copy_value %6 : $NonSendableKlass
%9 = class_method %0 : $MyActor, #MyActor.klass!setter : (isolated MyActor) -> (NonSendableKlass) -> (), $@convention(method) (@owned NonSendableKlass, @sil_isolated @guaranteed MyActor) -> ()
%10 = apply %9(%8, %0) : $@convention(method) (@owned NonSendableKlass, @sil_isolated @guaranteed MyActor) -> ()
%11 = alloc_stack $NonSendableKlass
%12 = store_borrow %6 to %11 : $*NonSendableKlass
%13 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
%14 = apply [caller_isolation=actor_instance] [callee_isolation=global_actor] %13<NonSendableKlass>(%12) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () // expected-warning {{}}
// expected-note @-1 {{}}
end_borrow %12 : $*NonSendableKlass
dealloc_stack %11 : $*NonSendableKlass
hop_to_executor %0 : $MyActor
destroy_value %6 : $NonSendableKlass
%19 = tuple ()
return %19 : $()
}
sil @sendableAsyncLetClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> (@guaranteed NonSendableKlass) -> (@out τ_0_0, @error any Error) for <Int>
sil [ossa] @sendableAsyncLetClosureTest : $@convention(thin) @async () -> () {
bb0:
%3 = function_ref @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
%4 = apply %3() : $@convention(thin) () -> @owned NonSendableKlass
%5 = move_value [lexical] [var_decl] %4 : $NonSendableKlass
debug_value %5 : $NonSendableKlass, let, name "x"
%7 = alloc_stack $Int
%8 = address_to_pointer %7 : $*Int to $Builtin.RawPointer
%9 = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
%10 = function_ref @sendableAsyncLetClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> (@guaranteed NonSendableKlass) -> (@out τ_0_0, @error any Error) for <Int>
%11 = copy_value %5 : $NonSendableKlass
%12 = partial_apply [callee_guaranteed] %10(%11) : $@convention(thin) @Sendable @async @substituted <τ_0_0> (@guaranteed NonSendableKlass) -> (@out τ_0_0, @error any Error) for <Int>
%13 = convert_function %12 : $@Sendable @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int> to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int>
%14 = convert_escape_to_noescape [not_guaranteed] %13 : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int> to $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int>
%15 = builtin "startAsyncLetWithLocalBuffer"<Int>(%9 : $Optional<Builtin.RawPointer>, %14 : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int>, %8 : $Builtin.RawPointer) : $Builtin.RawPointer
%useValue = function_ref @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
apply %useValue(%5) : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
%26 = function_ref @swift_asyncLet_get : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
%27 = apply %26(%15, %8) : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
%29 = load [trivial] %7 : $*Int
%31 = builtin "finishAsyncLet"(%15, %8) : $()
%33 = builtin "endAsyncLetLifetime"(%15 : $Builtin.RawPointer) : $()
destroy_value %14 : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int>
destroy_value %13 : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <Int>
dealloc_stack %7 : $*Int
destroy_value %5 : $NonSendableKlass
%38 = tuple ()
return %38 : $()
}
// Make sure that when we process the following sil we do not crash.
//
// We previously crashed since we used the base of the storage instead of the
// operand of the root (the ref_element_addr)
sil [ossa] @test_ref_element_addr_with_different_storage_no_crash : $@convention(method) <Self where Self : NonSendableKlass> (@guaranteed Self) -> () {
bb0(%0 : @guaranteed $Self):
%1 = copy_value %0 : $Self
%2 = upcast %1 : $Self to $NonSendableKlass
%3 = begin_borrow %2 : $NonSendableKlass
%4 = ref_element_addr [immutable] %3 : $NonSendableKlass, #NonSendableKlass.klass
%5 = load [copy] %4 : $*Klass
destroy_value %5 : $Klass
end_borrow %3 : $NonSendableKlass
destroy_value %2 : $NonSendableKlass
%9999 = tuple ()
return %9999 : $()
}
sil [ossa] @sending_direct_result_from_callee : $@convention(thin) @async () -> () {
bb0:
%0 = function_ref @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
%1 = apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %0() : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
%useValue = function_ref @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %useValue(%1) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
destroy_value %1
// Double check we actually error without @sil_sending
%2 = function_ref @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
%3 = apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %2() : $@convention(thin) @async () -> @owned NonSendableKlass
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from global actor '<null>'-isolated function to nonisolated context}}
apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %useValue(%3) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
// expected-warning @-1 {{sending value of non-Sendable type 'NonSendableKlass' risks causing data races}}
// expected-note @-2 {{}}
destroy_value %3
%9999 = tuple ()
return %9999 : $()
}
sil [ossa] @sending_direct_result_from_callee_2 : $@convention(thin) @async () -> () {
bb0:
%0 = function_ref @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
%1 = apply [caller_isolation=global_actor] [callee_isolation=global_actor] %0() : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
%useValue = function_ref @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
apply [caller_isolation=global_actor] [callee_isolation=actor_instance] %useValue(%1) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
destroy_value %1
// Double check we actually error without @sil_sending
%2 = function_ref @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
%3 = apply [caller_isolation=global_actor] [callee_isolation=global_actor] %2() : $@convention(thin) @async () -> @owned NonSendableKlass
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from global actor '<null>'-isolated function to global actor '<null>'-isolated context}}
apply [caller_isolation=global_actor] [callee_isolation=actor_instance] %useValue(%3) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
// expected-warning @-1 {{sending value of non-Sendable type 'NonSendableKlass' risks causing data races}}
// expected-note @-2 {{}}
destroy_value %3
%9999 = tuple ()
return %9999 : $()
}
sil @closureForCheckedContinuation : $@convention(thin) <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()
sil @withCheckedContinuation : $@convention(thin) @async <τ_0_0> (@sil_isolated @guaranteed Optional<any Actor>, @guaranteed @noescape @callee_guaranteed <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()) -> @sil_sending @out τ_0_0
// We shouldn't emit any error here due to sil_sending.
sil [ossa] @sending_indirect_result_from_callee : $@convention(thin) @async () -> () {
bb0:
%0 = alloc_stack $NonSendableKlass
%1 = function_ref @closureForCheckedContinuation : $@convention(thin) <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()
%1a = thin_to_thick_function %1 : $@convention(thin) <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> () to $@noescape @callee_guaranteed <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()
%2 = enum $Optional<any Actor>, #Optional.none!enumelt
%3 = function_ref @withCheckedContinuation : $@convention(thin) @async <τ_0_0> (@sil_isolated @guaranteed Optional<any Actor>, @guaranteed @noescape @callee_guaranteed <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()) -> @sil_sending @out τ_0_0
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %3<NonSendableKlass>(%0, %2, %1a) : $@convention(thin) @async <τ_0_0> (@sil_isolated @guaranteed Optional<any Actor>, @guaranteed @noescape @callee_guaranteed <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()) -> @sil_sending @out τ_0_0
%f = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %f<NonSendableKlass>(%0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
destroy_addr %0 : $*NonSendableKlass
dealloc_stack %0 : $*NonSendableKlass
%9999 = tuple ()
return %9999 : $()
}
// Make sure that we do not crash on this.
//
// We used to crash on this since we would want to assign the region of an
// operand to the results... but we do not have one and have multiple
// results. This doesn't normally happen with most applies since applies do not
// have multiple results, so in such a case, we would just assign fresh and not
// try to do the assignment for the rest of the values.
sil [ossa] @handleNoOperandToAssignToResults : $@convention(thin) () -> () {
bb0:
%0 = function_ref @beginApplyMultipleResultCallee : $@yield_once @convention(thin) () -> (@yields @guaranteed NonSendableKlass, @yields @guaranteed NonSendableKlass)
(%1, %2, %3) = begin_apply %0() : $@yield_once @convention(thin) () -> (@yields @guaranteed NonSendableKlass, @yields @guaranteed NonSendableKlass)
end_apply %3 as $()
%9999 = tuple ()
return %9999 : $()
}