Skip to content

Commit 9f304b5

Browse files
authored
[SE-0411] Minor editorial changes to the motivation and proposed solution. (#2198)
* [SE-0411] Minor editorial changes to the motivation and proposed solution. * [SE-0411] Correction to existing argument evaluation rules.
1 parent fc8b9c9 commit 9f304b5

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

proposals/0411-isolated-default-values.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ class C {
2929
}
3030
```
3131

32-
The above code allows any context to initialize an instance of `C()` through a synchronous, non-isolated `init` that synchronously calls both a `@MainActor`-isolated and a `@AnotherActor`-isolated function, violating actor isolation checking and enabling `requiresMainActor()` and `requiresAnotherActor()` to run concurrently with other code on those respective actors.
32+
The above code allows any context to initialize an instance of `C()` through a synchronous, nonisolated `init`. The initializer synchronously calls both `requiresMainActor()` and `requiresAnotherActor()`, which are `@MainActor`-isolated and `@AnotherActor`-isolated, respectively. This violates actor isolation checking because `requiresMainActor()` and `requiresAnotherActor()` may run concurrently with other code on their respective global actors.
3333

34-
The current actor isolation rules for default argument values do not admit data races, but default argument values are always `nonisolated` which is overly restrictive. This rule prohibits programmers from making `@MainActor`-isolated calls in default argument values of `@MainActor`-isolated functions that are only ever called from the main actor. For example, the following code is not valid even though it is perfectly safe:
34+
The current actor isolation rules for default argument values do not admit data races, but default argument values are always `nonisolated` which is overly restrictive. This rule prohibits programmers from making `@MainActor`-isolated calls in default argument values of `@MainActor`-isolated functions. For example, the following code is not valid even though it is perfectly safe:
3535

3636
```swift
37-
@MainActor class C { ... }
37+
@MainActor class C {}
3838

39-
@MainActor func f(c: C = C()) { ... } // error
39+
@MainActor func f(c: C = C()) {} // error: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
4040

4141
@MainActor func useFromMainActor() {
4242
f()
@@ -45,23 +45,37 @@ The current actor isolation rules for default argument values do not admit data
4545

4646
## Proposed solution
4747

48-
I propose allowing default value expressions to impose an isolation requirement at the call-site. The isolation requirement is inferred from the default value expression, and it must match the isolation of the enclosing function or the corresponding stored property. If the caller does not meet the isolation requirement, then the call must be made asynchronously and must be explicitly marked with `await`. For default stored property initializers that are implicitly invoked in the body of an `init`, the initialization must be written out explicitly if the default expression requires a different isolation from the `init` itself.
48+
I propose allowing default value expressions to have the same isolation as the enclosing function or the corresponding stored property. As usual, if the caller is not already in the isolation domain of the callee, then the call must be made asynchronously and must be explicitly marked with `await`. For isolated default values of stored properties, the implicit initialization only happens in the body of an `init` with the same isolation.
4949

50-
These rule makes the stored property example above invalid at the point of the `nonisolated` initializer, because the isolation requirement of the default values for the stored properties is not satisfied. Calling `requiresMainActor` explicitly with `await` resolves the issue:
50+
These rule makes the stored property example above invalid at the `nonisolated` initializer:
5151

5252
```swift
5353
@MainActor func requiresMainActor() -> Int { ... }
54+
@AnotherActor func requiresAnotherActor() -> Int { ... }
5455

5556
class C {
5657
@MainActor var x1 = requiresMainActor()
58+
@AnotherActor var x2 = requiresAnotherActor()
59+
60+
nonisolated init() {} // error: 'self.x2' and 'self.x2' are not initialized
61+
}
62+
```
63+
64+
Calling `requiresMainActor()` and `requiresAnotherActor()` explicitly with `await` resolves the issue:
65+
66+
```swift
67+
class C {
68+
@MainActor var x1 = requiresMainActor()
69+
@AnotherActor var x2 = requiresAnotherActor()
5770

5871
nonisolated init() async {
5972
self.x1 = await requiresMainActor()
73+
self.x2 = await requiresAnotherActor()
6074
}
6175
}
6276
```
6377

64-
This rule also makes the default argument example above valid, because the `@MainActor` isolation requirement for the default argument of `f` is satisfied by the caller.
78+
This rule also makes the default argument example above valid, because the default argument and the enclosing function are both `@MainActor`-isolated.
6579

6680
## Detailed design
6781

@@ -132,8 +146,7 @@ In the above example, `useDefault` has default arguments that are isolated to `@
132146
For a given call, argument evaluation happens in the following order:
133147

134148
1. Left-to-right evalution of explicit r-value arguments
135-
2. Left-to-right evaluation of default arguments
136-
3. Left-to-right evaluation of formal access arguments
149+
2. Left-to-right evaluation of default arguments and formal access arguments
137150

138151
For example:
139152

0 commit comments

Comments
 (0)