|
| 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/...)) |
| 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 an empty type named `nonisolated` to the Concurrency library: |
| 75 | + |
| 76 | +```swift |
| 77 | +public struct nonisolated {} |
| 78 | +``` |
| 79 | + |
| 80 | +This type 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 an empty type named `nonisolated` to the Concurrency library to enable writing it as the underlying type of a typealias is pretty weird, but I felt this was the simplest option that preserved a consistent way to spell `nonisolated`. There are a number of alternatives, including: |
| 97 | + |
| 98 | +* Using a bespoke syntax such as `using MainActor` and `using nonisolated`. |
| 99 | +* Capitalizing the `Nonisolated` type. |
| 100 | +* Using `Never` instead of a new empty type to represent `nonisolated`. |
| 101 | +* Allow writing a keyword such as `nonisolated` or `nil` as the underlying type of a typealias. |
| 102 | + |
| 103 | +The benefits of the typealias model include: |
| 104 | + |
| 105 | +* It's consistent with other similar defaulting rules such as the default literal types and default actor system types. |
| 106 | +* It does not require adding any new syntax. |
| 107 | +* The lookup rules for computing default isolation are very straightforward. |
| 108 | + |
| 109 | +Having a `nonisolated` type 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`: |
| 110 | + |
| 111 | +```swift |
| 112 | +SwiftSetting.defaultIsolation(nonisolated.self) |
| 113 | +``` |
| 114 | + |
| 115 | +We can also pursue allowing bare metatypes without `.self` to allow: |
| 116 | + |
| 117 | +```swift |
| 118 | +SwiftSetting.defaultIsolation(nonisolated) |
| 119 | +SwiftSetting.defaultIsolation(MainActor) |
| 120 | +``` |
| 121 | + |
| 122 | +[SE-0466]: /proposals/0466-control-default-actor-isolation.md |
0 commit comments