my interpretation is that the right to perform this optimization remains 'reserved', and hasn't yet been implemented. the Task initializer currently extracts the isolation of its operation
parameter to determine on which executor the Task should begin.
in the SIL for this example, the calls to Task.init look like this:
%9 = init_existential_ref %8 : $MainActor : $MainActor, $any Actor, loc "/app/example.swift":11:10, scope 21 // user: %10
%10 = enum $Optional<any Actor>, #Optional.some!enumelt, %9 : $any Actor, loc "/app/example.swift":11:10, scope 21 // user: %11
%11 = partial_apply [callee_guaranteed] [isolated_any] %7(%10) : $@convention(thin) @Sendable @async @substituted <Ī_0_0> (@guaranteed Optional<any Actor>) -> @out Ī_0_0 for <()>, loc "/app/example.swift":11:10, scope 21 // user: %12
%12 = convert_function %11 : $@isolated(any) @Sendable @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()> to $@isolated(any) @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()>, loc "/app/example.swift":11:10, scope 21 // user: %14
%13 = function_ref @$sScTss5NeverORs_rlE8priority9operationScTyxABGScPSg_xyYaYAcntcfCyt_Tt1g5 : $@convention(thin) (@in Optional<TaskPriority>, @sil_sending @owned @isolated(any) @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()>) -> @owned Task<(), Never>, loc "/app/example.swift":11:5, scope 21 // users: %25, %14
%14 = apply %13(%5, %12) : $@convention(thin) (@in Optional<TaskPriority>, @sil_sending @owned @isolated(any) @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()>) -> @owned Task<(), Never>, loc "/app/example.swift":11:5, scope 21 // user: %15
release_value %14 : $Task<(), Never>, loc * "<compiler-generated>":0:0, scope 21 // id: %15
dealloc_stack %5 : $*Optional<TaskPriority>, loc "/app/example.swift":14:5, scope 21 // id: %16
%17 = alloc_stack $Optional<TaskPriority>, loc "/app/example.swift":16:10, scope 21 // users: %25, %27, %18
inject_enum_addr %17 : $*Optional<TaskPriority>, #Optional.none!enumelt, loc "/app/example.swift":16:10, scope 21 // id: %18
%19 = function_ref @$s6output3fooyyYaFyyYacfU0_ : $@convention(thin) @Sendable @async @substituted <Ī_0_0> (@guaranteed Optional<any Actor>) -> @out Ī_0_0 for <()>, loc "/app/example.swift":16:10, scope 21 // user: %23
%20 = apply %1(%0) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor, loc "/app/example.swift":16:10, scope 21 // user: %21
%21 = init_existential_ref %20 : $MainActor : $MainActor, $any Actor, loc "/app/example.swift":16:10, scope 21 // user: %22
%22 = enum $Optional<any Actor>, #Optional.some!enumelt, %21 : $any Actor, loc "/app/example.swift":16:10, scope 21 // user: %23
%23 = partial_apply [callee_guaranteed] [isolated_any] %19(%22) : $@convention(thin) @Sendable @async @substituted <Ī_0_0> (@guaranteed Optional<any Actor>) -> @out Ī_0_0 for <()>, loc "/app/example.swift":16:10, scope 21 // user: %24
%24 = convert_function %23 : $@isolated(any) @Sendable @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()> to $@isolated(any) @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()>, loc "/app/example.swift":16:10, scope 21 // user: %25
%25 = apply %13(%17, %24) : $@convention(thin) (@in Optional<TaskPriority>, @sil_sending @owned @isolated(any) @async @callee_guaranteed @substituted <Ī_0_0> () -> @out Ī_0_0 for <()>) -> @owned Task<(), Never>, loc "/app/example.swift":16:5, scope 21 // user: %26
the initializer method is called by instructions %14
and %25
, and in both cases if you backtrack slightly, you can see that the main actor is passed through as the function's isolation (instructions %10
and %22
i think).
personally i wonder if such an optimization wouldn't lead to some potentially confusing behaviors... the current implementation may be less efficient than it theoretically could be, but it is perhaps more straightforward to reason about.
at any rate, this is my best guess based on inspecting the source and current compiler output â @John_McCall or @ktoso would presumably have more definitive answers.