[Swift] isolatedなasync functionからnon-isolatedなasync functionを呼び出した時は必ずActorから出る

# TL;DR

**Swift5.7以降では**

```swift
@MainActor
func isolatedFunc() async {
    await nonIsolatedFunc()
}

func nonIsolatedFunc() async {
    // ここはMainActorではない
}
```

**Swift5.6以前では**

```swift
@MainActor
func isolatedFunc() async {
    await nonIsolatedFunc()
}

func nonIsolatedFunc() async {
    // ここはMainActor
    ...
    
    try? await Task.sleep(nanoseconds: 100)
    
    // ここはMainActorではない
    ...
}
```

参考

https://github.com/apple/swift-evolution/blob/main/proposals/0338-clarify-execution-non-actor-async.md

# 事前知識

## isolatedなasync function

あるactor上での実行が保証されているasync function

- globalActorで明示されたasync function

```swift
@MainActor
func isolatedFunc() async {
    // 必ずここはMainActor
}
```

- globalActorによって明示された型またはactor含まれたasync function

```swift
@MainActor
class Hoge {
    func isolatedFunc() async {
        // 必ずここはMainActor
    }
}

@MainActor
struct Hoge {
    func isolatedFunc() async {
        // 必ずここはMainActor
    }
}

actor HogeActor {
    func isolatedFunc() async {
        // 必ずここはHogeActor
    }
}
```

## non-isolatedなasync function

どのactor上での実行も保証されていないasync function

- 何にも明示されていない型に含まれたasync function

```swift
class Hoge {
    func nonIsolatedFunc() async {
    }
}

struct Hoge {
    func nonIsolatedFunc() async {
    }
}
```

- 明示された型やactorに含まれているが、`nonisolated`キーワードがついているasync function

```swift
@MainActor
class Hoge {
    nonisolated func nonIsolatedFunc() async {
    }
}

@MainActor
struct Hoge {
    nonisolated func nonIsolatedFunc() async {
    }
}

actor HogeActor {
    nonisolated func nonIsolatedFunc() async {
    }
}
```

# 本題

「isolatedなasync function」から「non-isolatedなasync function」を呼び出した時、「non-isolatedなasync function」は、「isolatedなasync function」のActorを引き継ぐのか？

```swift
@MainActor
func isolatedFunc() async {
    await nonIsolatedFunc()
}

func nonIsolatedFunc() async {
    // ここはMainActorか？
}
```

## 実験

```swift
@MainActor
func isolatedFunc() async {
    await nonIsolatedFunc()
}

func nonIsolatedFunc() async {
    MainActor.shared.assertIsolated()
}

await isolatedFunc()
```

Swift 5.9

https://swiftfiddle.com/z6aog7gnqzen3bcbhzqzqlxxpq

クラッシュするのでnonIsolatedFuncはMainActorではないことがわかります。

## なぜ？

SE-0338に記載があります。

https://github.com/apple/swift-evolution/blob/main/proposals/0338-clarify-execution-non-actor-async.md

> actor-isolated async functions always formally run on the actor's executor
> non-actor-isolated async functions never formally run on any actor's executor


`never formally run on any actor's executor` なので **絶対にisolatedなasync functionのActor executorを引き継がない** ということになります。

これはSwift5.7からの仕様です。
SE-0338のStatusが **Implemented (Swift 5.7)** となっているからです。

Swfit5.6以前の仕様についても言及があります。

> In the current implementation, calls and returns from actor-isolated functions will continue running on that actor's executor.（中略）
> they will remain there until either the task suspends or it needs to run on a different actor.

current = 5.6以前では`will continue running on that actor's executor`なので**isolatedなasync functionのActor executorを引き継ぐ** ということになります。

また、 **suspendするかほかのActorで実行されるまで引き継ぐ** ので以下のような挙動になります。


```swift
@MainActor
func isolatedFunc() async {
    await nonIsolatedFunc()
}

func nonIsolatedFunc() async {
    // ここはMainActor
    ...
    
    try? await Task.sleep(nanoseconds: 100)
    
    // ここはMainActorではない
    ...
}
```

## `@_unsafeInheritExecutor`

上までの話は例外があります。例えば`withCheckedContinuation`です。

https://developer.apple.com/documentation/swift/withcheckedcontinuation(function:_:)

```swift
@MainActor
func isolatedFunc() async {
    await withCheckedContinuation { continuation in
        MainActor.shared.assertIsolated()
        print("OK")
        continuation.resume(returning: ())
    }
}

await isolatedFunc()
```

https://swiftfiddle.com/pxyibu2awjcadoln5cisxnxe3i

これは`@_unsafeInheritExecutor`というattributeによるものです。これによって呼び出し元（今回のケースではisolatedなasync functionのActor executor）を引き継ぐ挙動をとります。

https://github.com/apple/swift/blob/16cbb76fc6e18a08c886ced4d483c4322ac505d4/docs/ReferenceGuides/UnderscoredAttributes.md?plain=1#L1191

実際に`withCheckedContinuation`にはこのattributeがついています。

https://github.com/apple/swift/blob/16cbb76fc6e18a08c886ced4d483c4322ac505d4/stdlib/public/Concurrency/CheckedContinuation.swift#L288

SE-0338実装以前に`@_unsafeInheritExecutor`は存在しませんでした。SE-0338以後に`withCheckedContinuation`でexecutorを引き継ぐ挙動を再現するために`@_unsafeInheritExecutor`が実装されたという経緯です。

`@_unsafeInheritExecutor`の実装PR

https://github.com/apple/swift/pull/41376


## どうすればいい？

Swift５.6以前の挙動を確認してActorを引き継ぐ前提のコードを書いている場合は修正する必要がありそうです。

```swift
struct HogeView: View {

    ...
    
    var body: some View {
        VStack {
            Button {
                Task {
                    await hogeViewModel.onButtonTapped()
                }
            } label: {
                Text("load")
            }
        }
        .task {
            await hogeModel.needCallFromMainThread()
        }
    }
}

struct HogeModel {
    // @MainActor をつけるべき！！
    func needCallFromMainThread() async {
        await ...
    }
}

// @MainActorをつけるべき！！
final class HogeViewModel: ObservableObject {
    // PublishedはMainActorからしか更新してはいけない
    @Published private(set) var isLoading = false

    func onButtonTapped() async {
        isLoading = true
        ...
    }
}
```