Files
swift-mirror/test/Concurrency/transfernonsendable.sil
John McCall a7d7970e29 Turn finishAsyncLet into a builtin.
This is necessary because we need to model its stack-allocation
behavior, although I'm not yet doing that in this patch because
StackNesting first needs to be taught to not try to move the
deallocation.

I'm not convinced that `async let` *should* be doing a stack allocation,
but it undoubtedly *is* doing a stack allocation, and until we have an
alternative to that, we will need to model it properly.
2025-11-03 16:33:40 -08:00

468 lines
26 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 : $()
}
// Dead block crasher
//
// We used to crash here since we attempted to process /all/ blocks for
// diagnostic even for non-dead blocks. This is a problem since the dataflow
// uses a reverse post order traversal to get quick convergence and that skips
// dead blocks.
sil [ossa] @dead_block_crasher : $@convention(thin) @async () -> () {
bb0:
%0 = function_ref @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
%1 = apply %0() : $@convention(thin) () -> @owned NonSendableKlass
br bb1
bbDeadBlock:
%transfer = function_ref @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %transfer(%1) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
br bb1
bb1:
destroy_value %1 : $NonSendableKlass
%9999 = tuple ()
return %9999 : $()
}
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 : $()
}