mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This requires two major changes. The first is that we need to teach SILGen that the isolation of an initializer is essentially dynamic (as far as SILGen is concerned) --- that it needs to emit code in order to get the isolation reference. To make this work, I needed to refactor how we store the expected executor of a function so that it's not always a constant value; instead, we'll need to emit code that DI will lower properly. Fortunately, I can largely build on top of the work that Doug previously did to support #isolation in these functions. The SIL we emit here around delegating initializer calls is not ideal --- the breadcrumb hop ends up jumping to the generic executor, and then DI actually emits the hop to the actor. This is a little silly, but it's hard to eliminate without special-casing the self-rebinding, which honestly we should consider rather than the weirdly global handling of that in SILGen today. The optimizer should eliminate this hop pretty reliably, at least. The second is that we need to teach DI to handle the pattern of code we get in delegating initializers, where the builtin actually has to be passed the self var rather than a class reference. This is because we don't *have* a class reference that's consistently correct in these cases. This ended up being a fairly straightforward generalization. I also taught the hop_to_executor optimizer to skip over the initialization of the default-actor header; there are a lot of simple cases where we still do emit the prologue generic-executor hop, but at least the most trivial case is handled. To do this better, we'd need to teach this bit of the optimizer that the properties of self can be stored to in an initializer prior to the object having escaped, and we don't have that information easily at hand, I think. Fixes rdar://87485045.
246 lines
12 KiB
Swift
246 lines
12 KiB
Swift
// RUN: %target-swift-frontend -emit-silgen %s -module-name initializers -swift-version 5 -disable-availability-checking | %FileCheck %s --enable-var-scope --implicit-check-not=hop_to_executor
|
|
// REQUIRES: concurrency
|
|
|
|
// CHECK: protocol Person {
|
|
// CHECK-NEXT: init() async
|
|
// CHECK-NEXT: }
|
|
|
|
// CHECK: struct MyStruct {
|
|
// CHECK-NEXT: init() async
|
|
// CHECK-NEXT: }
|
|
|
|
// CHECK: enum MyEnum {
|
|
// CHECK-NEXT: init() async
|
|
// CHECK-NEXT: }
|
|
|
|
// CHECK: actor MyActor {
|
|
// CHECK-NEXT: init() async
|
|
|
|
|
|
// CHECK: class EarthPerson : Person {
|
|
// CHECK-NEXT: required init() async
|
|
// CHECK-NEXT: init(name: String) async
|
|
|
|
// CHECK: @_inheritsConvenienceInitializers class NorthAmericaPerson : EarthPerson {
|
|
// CHECK-NEXT: required init() async
|
|
// CHECK-NEXT: override init(name: String) async
|
|
|
|
// CHECK: class Cat {
|
|
// CHECK-NEXT: @MainActor init()
|
|
// CHECK-NEXT: @MainActor init(name: String)
|
|
|
|
// CHECK: struct Dog {
|
|
// CHECK-NEXT: @MainActor init() async
|
|
// CHECK-NEXT: @MainActor init(name: String)
|
|
// CHECK-NEXT: }
|
|
|
|
// CHECK: enum Birb {
|
|
// CHECK-NEXT: @MainActor init() async
|
|
// CHECK-NEXT: @MainActor init(name: String)
|
|
// CHECK-NEXT: }
|
|
|
|
|
|
|
|
protocol Person {
|
|
init() async
|
|
}
|
|
|
|
struct MyStruct {
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers8MyStructVACyYacfC : $@convention(method) @async (@thin MyStruct.Type) -> MyStruct
|
|
init() async {}
|
|
}
|
|
|
|
enum MyEnum {
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers6MyEnumOACyYacfC : $@convention(method) @async (@thin MyEnum.Type) -> MyEnum
|
|
init() async {}
|
|
}
|
|
|
|
actor MyActor {
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers7MyActorCACyYacfc : $@convention(method) @async (@sil_isolated @owned MyActor) -> @owned MyActor
|
|
// CHECK: bb0(%0 : @owned $MyActor):
|
|
// In the prologue, hop to the generic executor.
|
|
// CHECK-NEXT: [[NIL_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[NIL_EXECUTOR]] :
|
|
// Later, when we return from an async call, hop to the
|
|
// correct flow-sensitive value.
|
|
// CHECK: [[FN:%.*]] = function_ref @$s12initializers8MyStructVACyYacfC :
|
|
// CHECK-NEXT: apply [[FN]]
|
|
// CHECK-NEXT: [[ISOLATION:%.*]] = builtin "flowSensitiveSelfIsolation"<MyActor>({{%.*}})
|
|
// CHECK-NEXT: hop_to_executor [[ISOLATION]]
|
|
// CHECK-NEXT: destroy_value [[ISOLATION]]
|
|
init() async {
|
|
_ = await MyStruct()
|
|
}
|
|
}
|
|
|
|
class EarthPerson : Person {
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers11EarthPersonCACyYacfc : $@convention(method) @async (@owned EarthPerson) -> @owned EarthPerson
|
|
required init() async {}
|
|
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers11EarthPersonC4nameACSS_tYacfc : $@convention(method) @async (@owned String, @owned EarthPerson) -> @owned EarthPerson
|
|
init(name: String) async {}
|
|
|
|
// CHECK-DAG: sil private [transparent] [thunk] [ossa] @$s12initializers11EarthPersonCAA0C0A2aDPxyYacfCTW : $@convention(witness_method: Person) @async (@thick EarthPerson.Type) -> @out EarthPerson
|
|
}
|
|
|
|
class NorthAmericaPerson : EarthPerson {
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers18NorthAmericaPersonCACyYacfc : $@convention(method) @async (@owned NorthAmericaPerson) -> @owned NorthAmericaPerson
|
|
required init() async { await super.init() }
|
|
|
|
// CHECK-DAG: sil hidden [ossa] @$s12initializers18NorthAmericaPersonC4nameACSS_tYacfc : $@convention(method) @async (@owned String, @owned NorthAmericaPerson) -> @owned NorthAmericaPerson
|
|
override init(name: String) async { await super.init(name: name) }
|
|
}
|
|
|
|
////
|
|
// check that of global-actor isolation is preserved in async initializers.
|
|
// there's also coverage for sync global-actor-isolated initializers
|
|
|
|
func someAsyncFn() async {}
|
|
|
|
class Cat {
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers3CatCACyYacfc : $@convention(method) @async (@owned Cat) -> @owned Cat {
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: {{%[0-9]+}} = function_ref @$s12initializers11someAsyncFnyyYaF : $@convention(thin) @async () -> ()
|
|
// CHECK: {{%[0-9]+}} = apply {{%[0-9]+}}() : $@convention(thin) @async () -> ()
|
|
// CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: } // end sil function '$s12initializers3CatCACyYacfc'
|
|
@MainActor init() async {
|
|
await someAsyncFn()
|
|
}
|
|
|
|
@MainActor init(name: String) {}
|
|
}
|
|
|
|
struct Dog {
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers3DogVACyYacfC : $@convention(method) @async (@thin Dog.Type) -> Dog {
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: {{%[0-9]+}} = function_ref @$s12initializers11someAsyncFnyyYaF : $@convention(thin) @async () -> ()
|
|
// CHECK: {{%[0-9]+}} = apply {{%[0-9]+}}() : $@convention(thin) @async () -> ()
|
|
// CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: } // end sil function '$s12initializers3DogVACyYacfC'
|
|
@MainActor init() async {
|
|
await someAsyncFn()
|
|
}
|
|
|
|
@MainActor init(name: String) {}
|
|
}
|
|
|
|
enum Birb {
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers4BirbOACyYacfC : $@convention(method) @async (@thin Birb.Type) -> Birb {
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: {{%[0-9]+}} = function_ref @$s12initializers11someAsyncFnyyYaF : $@convention(thin) @async () -> ()
|
|
// CHECK: {{%[0-9]+}} = apply {{%[0-9]+}}() : $@convention(thin) @async () -> ()
|
|
// CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: } // end sil function '$s12initializers4BirbOACyYacfC'
|
|
@MainActor init() async {
|
|
await someAsyncFn()
|
|
}
|
|
|
|
@MainActor init(name: String) {}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers7makeCatyyYaF : $@convention(thin) @async () -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK-NEXT: {{%[0-9]+}} = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (@owned String, @thick Cat.Type) -> @owned Cat
|
|
// CHECK: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: } // end sil function '$s12initializers7makeCatyyYaF'
|
|
func makeCat() async {
|
|
_ = await Cat(name: "Socks")
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers7makeDogyyYaF : $@convention(thin) @async () -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK-NEXT: {{%[0-9]+}} = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (@owned String, @thin Dog.Type) -> Dog
|
|
// CHECK: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: } // end sil function '$s12initializers7makeDogyyYaF'
|
|
func makeDog() async {
|
|
_ = await Dog(name: "Lassie")
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers8makeBirbyyYaF : $@convention(thin) @async () -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK-NEXT: {{%[0-9]+}} = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (@owned String, @thin Birb.Type) -> Birb
|
|
// CHECK: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: } // end sil function '$s12initializers8makeBirbyyYaF'
|
|
func makeBirb() async {
|
|
_ = await Birb(name: "Chirpy")
|
|
}
|
|
|
|
actor SomeActor {
|
|
var x: Int = 0
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers9SomeActorCACyYacfc : $@convention(method) @async (@sil_isolated @owned SomeActor) -> @owned SomeActor {
|
|
// CHECK: bb0(%0 :
|
|
// CHECK-NEXT: [[NIL_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[NIL_EXECUTOR]]
|
|
init() async {}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers9SomeActorC10someMethodyyYaF : $@convention(method) @async (@sil_isolated @guaranteed SomeActor) -> () {
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $SomeActor
|
|
// CHECK: } // end sil function '$s12initializers9SomeActorC10someMethodyyYaF'
|
|
func someMethod() async {}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers9makeActorAA04SomeC0CyYaF : $@convention(thin) @async () -> @owned SomeActor {
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: [[INIT:%[0-9]+]] = function_ref @$s12initializers9SomeActorCACyYacfC : $@convention(method) @async (@thick SomeActor.Type) -> @owned SomeActor
|
|
// CHECK: {{%[0-9]+}} = apply [[INIT]]({{%[0-9]+}}) : $@convention(method) @async (@thick SomeActor.Type) -> @owned SomeActor
|
|
// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor
|
|
// CHECK: } // end sil function '$s12initializers9makeActorAA04SomeC0CyYaF'
|
|
@MainActor
|
|
func makeActor() async -> SomeActor {
|
|
return await SomeActor()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers20makeActorFromGenericAA04SomeC0CyYaF : $@convention(thin) @async () -> @owned SomeActor {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
func makeActorFromGeneric() async -> SomeActor {
|
|
return await SomeActor()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s12initializers26callActorMethodFromGeneric1ayAA04SomeC0C_tYaF : $@convention(thin) @async (@guaranteed SomeActor) -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
func callActorMethodFromGeneric(a: SomeActor) async {
|
|
await a.someMethod()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden {{.*}} @$s12initializers15makeActorInTaskyyYaF : $@convention(thin) @async () -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-LABEL: sil private [ossa] @$s12initializers15makeActorInTaskyyYaFAA04SomeC0CyYacfU_ : $@convention(thin) @async @substituted <τ_0_0> (@guaranteed Optional<any Actor>) -> @out τ_0_0 for <SomeActor> {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
@available(SwiftStdlib 5.1, *)
|
|
func makeActorInTask() async {
|
|
Task.detached { await SomeActor() }
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden {{.*}} @$s12initializers21callActorMethodInTask1ayAA04SomeC0C_tYaF : $@convention(thin) @async (@guaranteed SomeActor) -> () {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-LABEL: sil private [ossa] @$s12initializers21callActorMethodInTask1ayAA04SomeC0C_tYaFyyYacfU_ : $@convention(thin) @async @substituted <τ_0_0> (@guaranteed Optional<any Actor>, @guaranteed SomeActor) -> @out τ_0_0 for <()> {
|
|
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
// CHECK: apply
|
|
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
|
|
@available(SwiftStdlib 5.1, *)
|
|
func callActorMethodInTask(a: SomeActor) async {
|
|
Task.detached { await a.someMethod() }
|
|
}
|