Skip to content

Commit ddffc2e

Browse files
Add a proposal for specifying default actor isolation per-file using a type alias. (#2777)
* Add a draft proposal for specifying default isolation via typealias. * Clarify that it is invalid to use `Never` as the underlying type of `DefaultIsolation`. * Update and rename NNNN-default-isolation-typealias.md to 0478-default-isolation-typealias.md Assigned SE-0478 --------- Co-authored-by: Stephen Canon <[email protected]>
1 parent 1d30063 commit ddffc2e

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Default actor isolation typealias
2+
3+
* Proposal: [SE-0478](0478-default-isolation-typealias.md)
4+
* Authors: [Holly Borla](https://github.com/hborla)
5+
* Review Manager: [Steve Canon](https://github.com/stephentyrone)
6+
* Status: **Active Review (April 21 ... May 5, 2025)**
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. To specify `nonisolated` using the `DefaultIsolation` typealias, the underlying type must be `nonisolated` exactly; it is invalid to write `private typealias DefaultIsolation = Never`.
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

Comments
 (0)