Skip to content

Commit 5c2cf66

Browse files
committed
Call out the nonisolated usability improvements in its own section of the proposal.
1 parent 2514d13 commit 5c2cf66

File tree

1 file changed

+66
-56
lines changed

1 file changed

+66
-56
lines changed

proposals/NNNN-nonisolated-for-global-actor-cutoff.md

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
## Introduction
1111

12-
This proposal allows annotating a set of declarations with `nonisolated` to prevent global actor inference.
12+
This proposal allows annotating a set of declarations with `nonisolated` to prevent global actor inference. Additionally, it extends the existing rules for when `nonisolated` can be written on a stored property, improving usability.
1313

1414
## Motivation
1515

@@ -78,7 +78,7 @@ protocol RemoveGlobalActor {}
7878
protocol RefinedProtocol: GloballyIsolated, RemoveGlobalActor {} // 'RefinedProtocol' is non-isolated
7979
```
8080

81-
In the above code, the programmer creates a new protocol that is isolated to an actor that nominally is isolated to the global actor. This means that the protocol declaration `RefinedProtocol` refining the `RemoveGlobalActor` protocol will result in a conflicting global actor isolation, one from `GloballyIsolated` that’s isolated to `@MainActor`, and another one from `RemoveGlobalActor` that’s isolated to the `@FakeGlobalActor`. This results in the overall declaration having no global actor isolation, while still refining the protocols it conformed to.
81+
In the above code, the programmer creates a new protocol that is isolated to an actor that nominally is isolated to the global actor. This means that the protocol declaration `RefinedProtocol` refining the `RemoveGlobalActor` protocol will result in a conflicting global actor isolation, one from `GloballyIsolated` that’s isolated to `@MainActor`, and another one from `RemoveGlobalActor` that’s isolated to the `@FakeGlobalActor`. This results in the overall declaration having no global actor isolation, while still refining the protocols it conformed to.
8282

8383

8484
## Proposed solution
@@ -97,18 +97,22 @@ nonisolated protocol P: GloballyIsolated {} // 'P' won't inherit isolation of 'G
9797

9898
And in the above code, the protocol `P` refines the `GloballyIsolated` protocol. Because `nonisolated` is applied to it, the global actor isolation coming from the `GloballyIsolated` protocol will not be inferred for protocol `P`.
9999

100+
In addition to the above, we propose extending existing rules for when `nonisolated` can be applied to stored properties to improve usability. More precisely, we propose `nonisolated` inference from within the module for mutable storage of `Sendable` value types, and annotating such storage with `nonisolated` to allow synchronous access from outside the module. Additionally, we propose explicit spelling of `nonisolated` for stored properties of non-`Sendable` types.
101+
100102
## Detailed design
101103

102104
Today, there are a number of places where `nonisolated` can be written, as proposed in [SE-0313: Improved control over actor isolation](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md#non-isolated-declarations):
103105

104106
* Functions
105107
* Stored properties of classes that are `let` and `Sendable`
106108

107-
Additionally, under [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md), `nonisolated` is allowed to be written on mutable `Sendable` storage of globally-isolated value types.
109+
Additionally, under [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md), `nonisolated` is allowed to be written on mutable `Sendable` storage of globally-isolated value types.
110+
111+
In this proposal, we expand the above rules by allowing annotating more declarations with `nonisolated`. The first batch of these rules is specifically targeting the global actor inference "cut-off", while the second focuses on usability improvements allowing `nonisolated` to be written on more kinds of storage.
108112

109-
In this proposal, we expand the above rules by allowing annotating the declarations listed below with `nonisolated`.
113+
### Allowing `nonisolated` to prevent global actor inference
110114

111-
### 1. Protocols
115+
#### Protocols
112116

113117
This proposal allows `nonisolated` attribute to be applied on protocol declarations:
114118

@@ -125,7 +129,7 @@ struct A: Refined {
125129

126130
In the above code, the protocol `Refined` is refining the `GloballyIsolated` protocol, but is declared non-isolated. This means that the `Refined` still has the same requirements as `GloballyIsolated`, but they are not isolated. Therefore, a struct `A` conforming to it is also non-isolated, which allows the programmer for more flexibility when implementing the requirements of a protocol.
127131

128-
### 2. Extensions
132+
#### Extensions
129133

130134
This proposal allows for `nonisolated` attribute to be applied on extension declarations:
131135

@@ -145,56 +149,7 @@ struct C: GloballyIsolated {
145149

146150
In the code above, the `nonisolated` attribute is applied to an extension declaration for a `GloballyIsolated` protocol. When applied to an extension, `nonisolated` applies to all of its members. In this case, `implicitlyNonisolated` method and the computed property `x` are both nonisolated, and therefore are able to be accessed from a nonisolated context in the body of `explicitlyNonisolated` method of a globally-isolated struct `C`.
147151

148-
### 3. Stored properties of non-`Sendable` types
149-
150-
Currently, any stored property of a non-`Sendable` type is implicitly treated as non-isolated. This proposal allows for spelling of this behavior:
151-
152-
```swift
153-
class MyClass {
154-
nonisolated var x: NonSendable = NonSendable() // okay
155-
}
156-
```
157-
158-
Because `MyClass` is does not conform to `Sendable`, the compiler guarantees mutually exclusive access to references of `MyClass` instance. `nonisolated` on methods and properties of non-`Sendable` types can be safely called from any isolation domain because the base instance can only be accessed by one isolation domain at a time.
159-
160-
### 4. Mutable `Sendable` storage of `Sendable` value types
161-
162-
For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types:
163-
164-
```swift
165-
struct S {
166-
var x: Int = 0 // okay ('nonisolated' is inferred within the module)
167-
}
168-
169-
actor MyActor {
170-
func test(s: S) {
171-
print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay.
172-
}
173-
}
174-
```
175-
176-
In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay.
177-
178-
Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types:
179-
180-
```swift
181-
// In Module A
182-
public struct S: Sendable {
183-
nonisolated public var x: Int = 0 // okay
184-
public init() {}
185-
}
186-
187-
// In Module B
188-
import A
189-
190-
actor MyActor {
191-
func test(s: S) {
192-
print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay.
193-
}
194-
}
195-
```
196-
197-
### 5. Classes, structs, and enums
152+
#### Classes, structs, and enums
198153

199154
Finally, we propose allowing writing `nonisolated` on class, struct and enum declarations:
200155

@@ -245,6 +200,61 @@ The above behavior is semantically consistent with the existing rules around glo
245200
}
246201
```
247202

203+
### Annotating more types of storage with `nonisolated`
204+
205+
This section extends the existing rules for when `nonisolated` can be written on a storage of a user-defined type.
206+
207+
#### Stored properties of non-`Sendable` types
208+
209+
Currently, any stored property of a non-`Sendable` type is implicitly treated as non-isolated. This proposal allows for spelling of this behavior:
210+
211+
```swift
212+
class MyClass {
213+
nonisolated var x: NonSendable = NonSendable() // okay
214+
}
215+
```
216+
217+
Because `MyClass` does not conform to `Sendable`, it cannot be accessed from multiple isolation domains at once. Therefore, the compiler guarantees mutually exclusive access to references of `MyClass` instance. The `nonisolated` on methods and properties of non-`Sendable` types can be safely called from any isolation domain because the base instance can only be accessed by one isolation domain at a time. Importantly, `nonisolated` does not impact the number of isolation domains that can reference the `self` value. As long as there is a reference to `self` value in one isolation domain, the `nonisolated` method/property can be safely called from that domain.
218+
219+
#### Mutable `Sendable` storage of `Sendable` value types
220+
221+
For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types:
222+
223+
```swift
224+
struct S {
225+
var x: Int = 0 // okay ('nonisolated' is inferred within the module)
226+
}
227+
228+
actor MyActor {
229+
func test(s: S) {
230+
print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay.
231+
}
232+
}
233+
```
234+
235+
In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay.
236+
237+
Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types:
238+
239+
```swift
240+
// In Module A
241+
public struct S: Sendable {
242+
nonisolated public var x: Int = 0 // okay
243+
public init() {}
244+
}
245+
```
246+
247+
```swift
248+
// In Module B
249+
import A
250+
251+
actor MyActor {
252+
func test(s: S) {
253+
print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay.
254+
}
255+
}
256+
```
257+
248258
### Restrictions
249259

250260
Additionally, we propose the following set of rules for when the `nonisolated` attribute **cannot** be applied:

0 commit comments

Comments
 (0)