|
| 1 | +# Default actor isolation typealias |
| 2 | + |
| 3 | +* Proposal: [SE-NNNN](NNNN-default-isolation-typealias.md) |
| 4 | +* Authors: [Holly Borla](https://github.com/hborla) |
| 5 | +* Review Manager: TBD |
| 6 | +* Status: **Awaiting review** |
| 7 | +* Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) |
| 8 | +* Implementation: [swiftlang/swift#80572](https://github.com/swiftlang/swift/pull/80572) |
| 9 | +* Experimental Feature Flag: `DefaultIsolationTypealias` |
| 10 | +* Previous Proposal: [SE-0466: Control default actor isolation inference][SE-0466] |
| 11 | +* Review: ([pitch](https://forums.swift.org/t/pitch-a-typealias-for-per-file-default-actor-isolation/79150)) |
| 12 | + |
| 13 | +## Introduction |
| 14 | + |
| 15 | +[SE-0466: Control default actor isolation inference][SE-0466] introduced the ability to specify default actor isolation on a per-module basis. This proposal introduces a new typealias for specifying default actor isolation in individual source files within a module. This allows specific files to opt out of main actor isolation within a main-actor-by-default module, and opt into main actor isolation within a nonisolated-by-default module. |
| 16 | + |
| 17 | +## Motivation |
| 18 | + |
| 19 | +SE-0466 allows code to opt in to being “single-threaded” by default by isolating everything in the module to the main actor. When the programmer really wants concurrency, they can request it explicitly by marking a function or type as `nonisolated`, or they can define it in a module that does not default to main-actor isolation. However, it's very common to group multiple declarations used in concurrent code into one source file or a small set of source files. Instead of choosing between writing `nonisolated` on each individual declaration or splitting those files into a separate module, it's desirable to state that all declarations in those files default to `nonisolated`. |
| 20 | + |
| 21 | +## Proposed solution |
| 22 | + |
| 23 | +This proposal allows writing a private typealias named `DefaultIsolation` to specify the default actor isolation for a file. |
| 24 | + |
| 25 | +An underlying type of `MainActor` specifies that all declarations in the file default to main actor isolated: |
| 26 | + |
| 27 | +```swift |
| 28 | +// main.swift |
| 29 | + |
| 30 | +private typealias DefaultIsolation = MainActor |
| 31 | + |
| 32 | +// Implicitly '@MainActor' |
| 33 | +var global = 0 |
| 34 | + |
| 35 | +// Implicitly '@MainActor' |
| 36 | +func main() { ... } |
| 37 | + |
| 38 | +main() |
| 39 | +``` |
| 40 | + |
| 41 | +An underlying type of `nonisolated` specifies that all declarations in the file default to `nonisolated`: |
| 42 | + |
| 43 | +```swift |
| 44 | +// Point.swift |
| 45 | + |
| 46 | +private typealias DefaultIsolation = nonisolated |
| 47 | + |
| 48 | +// Implicitly 'nonisolated' |
| 49 | +struct Point { |
| 50 | + var x: Int |
| 51 | + var y: Int |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +## Detailed design |
| 56 | + |
| 57 | + A typealias named `DefaultIsolation` can specify the actor isolation to use for the source file it's written in under the following conditions: |
| 58 | + |
| 59 | +* The typealias is written at the top-level. |
| 60 | +* The typealias is `private` or `fileprivate`; the `DefaultIsolation` typealias cannot be used to set the default isolation for the entire module, so its access level cannot be `internal` or above. |
| 61 | +* The underlying type is either `MainActor` or `nonisolated`. |
| 62 | + |
| 63 | + It is not invalid to write a typealias called `DefaultIsolation` that does not meet the above conditions. Any typealias named `DefaultIsolation` that does not meet the above conditions will be skipped when looking up the default isolation for the source file. The compiler will emit a warning for any `DefaultIsolation` typealias that is not considered for default actor isolation along with the reason why: |
| 64 | + |
| 65 | +```swift |
| 66 | +@globalActor |
| 67 | +actor CustomGlobalActor { |
| 68 | + static let shared = CustomGlobalActor() |
| 69 | +} |
| 70 | + |
| 71 | +private typealias DefaultIsolation = CustomGlobalActor // warning: not used for default actor isolation |
| 72 | +``` |
| 73 | + |
| 74 | +To allow writing `nonisolated` as the underlying type of a typealias, this proposal adds a typealias named `nonisolated` to the Concurrency library: |
| 75 | + |
| 76 | +```swift |
| 77 | +public typealias nonisolated = Never |
| 78 | +``` |
| 79 | + |
| 80 | +This typealias serves no purpose beyond specifying default actor isolation. |
| 81 | + |
| 82 | +## Source compatibility |
| 83 | + |
| 84 | +Technically source breaking if someone happens to have written a private `DefaultIsolation` typealias with an underlying type of `MainActor`, which will start to infer every declaration in that file as `@MainActor`-isolated after this change. This seems extremely unlikely. |
| 85 | + |
| 86 | +## ABI compatibility |
| 87 | + |
| 88 | +This proposal has no ABI impact on existing code. |
| 89 | + |
| 90 | +## Implications on adoption |
| 91 | + |
| 92 | +This proposal does not change the adoption implications of adding `@MainActor` to a declaration that was previously nonisolated and vice versa. The source and ABI compatibility implications of changing actor isolation are documented in the Swift migration guide's [Library Evolution](https://github.com/apple/swift-migration-guide/blob/29d6e889e3bd43c42fe38a5c3f612141c7cefdf7/Guide.docc/LibraryEvolution.md#main-actor-annotations) article. |
| 93 | + |
| 94 | +## Alternatives considered |
| 95 | + |
| 96 | +Adding a typealias named `nonisolated` to `Never` to the Concurrency library to enable writing it as the underlying type of a typealias is pretty strange; this approach leveraging the fact that `nonisolated` is a contextual keyword, so it's valid to use `nonisolated` as an identifier. This proposal uses a typealias instead of an empty struct or enum type to avoid the complications of having a new type be only available with the Swift 6.2 standard library. |
| 97 | + |
| 98 | +It's extremely valuable to have a consistent way to spell `nonisolated`. Introducing a type that follows standard naming conventions, such as `Nonisolated`, or using an existing type like `Never` is more consistent with recommended style, but overall complicates the concurrency model because it means you need to spell `nonisolated` differently when specifying it per file versus writing it on a declaration. And because the underlying type of this typealias is used to infer actor isolation, it's not used as a type in the same way that other typealiases are. |
| 99 | + |
| 100 | +Another alternative is to introduce a bespoke syntax such as `using MainActor` or `using nonisolated`. This approach preserves a consistent spelling for `nonisolated`, but at the cost of adding new language syntax that deviates from other defaulting rules such as the default literal types and the default actor system types. |
| 101 | + |
| 102 | +Having a `nonisolated` typealias may also allow us to improve the package manifest APIs for specifying default isolation, allowing us to move away from using `nil` to specify `nonisolated`: |
| 103 | + |
| 104 | +```swift |
| 105 | +SwiftSetting.defaultIsolation(nonisolated.self) |
| 106 | +``` |
| 107 | + |
| 108 | +We can also pursue allowing bare metatypes without `.self` to allow: |
| 109 | + |
| 110 | +```swift |
| 111 | +SwiftSetting.defaultIsolation(nonisolated) |
| 112 | +SwiftSetting.defaultIsolation(MainActor) |
| 113 | +``` |
| 114 | + |
| 115 | +[SE-0466]: /proposals/0466-control-default-actor-isolation.md |
0 commit comments