-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
closure isolation #2322
Open
sophiapoirier
wants to merge
17
commits into
swiftlang:main
Choose a base branch
from
sophiapoirier:closure-isolation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
closure isolation #2322
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
16e9bf0
closure isolation control
sophiapoirier 7e4d540
merge Matt Massicotte's "Parameter Actor Inheritance" proposal
sophiapoirier 5903e0a
add Konrad Malawski's discussion about distributed actors
sophiapoirier af48c4b
move distributed actor details into "detailed design" section
sophiapoirier 36388e1
rename file to match title
sophiapoirier 34e8ac4
address ambiguity of a closure parameter named nonisolated
sophiapoirier 1f0d6fc
add name of experimental feature flag
sophiapoirier 0fb068e
@nonisolated attribute syntax
sophiapoirier 0d6fef5
revert to non-attribute-@ syntax for nonisolated but require parenthe…
sophiapoirier d359d1e
move contents of "Implications on adoption" section into "Source comp…
sophiapoirier 4d81353
Matt Massicotte's corrections and clarifications
sophiapoirier 4429af8
Some typos an a few small wording changes
mattmassicotte 54edf1d
weak isolated in future directions
sophiapoirier 7f25fe9
isolated capture in sync closure
sophiapoirier 442fa7b
Merge pull request #2 from mattmassicotte/fix/typos
sophiapoirier a0c3109
non-@Sendable local functions should inherit their enclosing isolation
sophiapoirier 25d8171
add missing Void return type on example closure parameters
sophiapoirier File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Closure isolation control | ||
|
||
* Proposal: [SE-NNNN](nnnn-closure-isolation.md) | ||
* Authors: [Sophia Poirier](https://github.com/sophiapoirier), [John McCall](https://github.com/rjmccall) | ||
* Review Manager: TBD | ||
* Implementation: On `main` gated behind `-enable-experimental-feature TODO` | ||
* Previous Proposals: [SE-0313](0313-actor-isolation-control.md), [SE-0316](0316-global-actors.md) | ||
* Review: ([pitch](https://forums.swift.org/TODO)) | ||
|
||
## Introduction | ||
|
||
This proposal provides the ability to explicitly specify actor-isolation or non-isolation of a closure, as well as providing a parameter attribute to guarantee that a closure parameter inherits the isolation of the context. | ||
|
||
## Motivation | ||
|
||
The formal isolation of a closure can be explicitly specified as global actor isolation: | ||
|
||
```swift | ||
Task { @MainActor in | ||
print("global actor isolation") | ||
} | ||
``` | ||
|
||
Without a global actor isolation annotation, actor-isolation or non-isolation of a closure is inferred but cannot be explicitly specified. This proposal enables closures to be fully explicit about all three types of formal isolation: | ||
* `nonisolated` | ||
* global actor | ||
* specific actor value | ||
|
||
Explicit annotation has the benefit of disabling inference rules and the potential that they lead to a formal isolation that is not preferred. For example, there are circumstances where it is beneficial to guarantee that a closure is `nonisolated` therefore knowing that its execution will hop off the current actor. Explicit annotation also offers the ability to identify a mismatch of intention, such as a case where the developer expected `nonisolated` but inference landed on actor-isolated, and the closure is used in an isolated context. With explicit annotation, the developer would receive a diagnostic about a `nonisolated` closure being used in an actor-isolated context which helpfully identifies this mismatch of intention. | ||
|
||
## Proposed solution | ||
|
||
Enable explicit specification of non-isolation by allowing `nonisolated` to be a specifier on a closure: | ||
|
||
```swift | ||
Task { nonisolated in | ||
print("nonisolated") | ||
} | ||
``` | ||
|
||
Enable explicit specification of actor-isolation via an isolated parameter in a closure's capture list by using the `isolated` specifier: | ||
|
||
```swift | ||
actor A { | ||
func isolate() { | ||
Task { [isolated self] in | ||
print("isolated to 'self'") | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Providing a formal replacement of the experimental parameter attribute `@_inheritActorContext` is needed to resolve another area of ambiguity with closure isolation. Its replacement `@inheritsIsolation` changes the behavior so that it unconditionally and implicitly captures the isolation context (as opposed to currently in actor-isolated contexts it being conditional on whether you capture an isolated parameter or isolated capture or actor-isolated function, but guaranteed if the context is isolated to a global actor or `nonisolated`). | ||
|
||
```swift | ||
class Old { | ||
public init(@_inheritActorContext operation: () async) | ||
} | ||
|
||
class New { | ||
public init(@inheritsIsolation operation: () async) | ||
} | ||
|
||
class C { | ||
var value = 0 | ||
|
||
@MainActor | ||
func staticIsolation() { | ||
Old { | ||
value = 1 // closure is MainActor-isolated and therefore okay to access self | ||
} | ||
New { | ||
value = 2 // closure is MainActor-isolated and therefore okay to access self | ||
} | ||
} | ||
|
||
func dynamicIsolation(_ actor: isolated any Actor) { | ||
Old { | ||
// not isolated to actor without explicit capture | ||
} | ||
New { | ||
// isolated to actor through guaranteed implicit capture | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Detailed design | ||
|
||
An isolated parameter in a capture list must be of actor type, or conform to or imply an actor, potentially optional, and there can only be one isolated parameter captured, following the same rules described in [SE-0313](0313-actor-isolation-control.md#actor-isolated-parameters) for actor-isolated parameters. | ||
|
||
Opting out of `@inheritsIsolation` can be achieved by explicitly annotating the closure argument as `nonisolated`. | ||
|
||
`@_inheritActorContext` is currently used by the `Task` initializer in the standard library which should be updated to use `@inheritsIsolation` instead. | ||
|
||
## Source compatibility | ||
|
||
The language changes are additive and therefore have no implications on source compatibility. The change to `Task.init` in the standard library does have the potential to isolate some closures that previously were inferred to be `nonisolated`. Prior behavior in those cases could be restored, if desired, by explicitly declaring the closure as `nonisolated`. | ||
|
||
## ABI compatibility | ||
|
||
The language change does not add or affect ABI since formal isolation is already part of a closure's type regardless of whether it is explicitly specified. The `Task.init` cahnge does not impact ABI since the function is annotated with `@_alwaysEmitIntoClient` and therefore has no ABI. | ||
|
||
## Implications on adoption | ||
|
||
none | ||
|
||
## Alternatives considered | ||
|
||
TODO | ||
|
||
## Future directions | ||
|
||
TODO |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this imply the following scenarios?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Let me preface by saying that this proposal isn't fully baked yet, hence not having been pitched or added anyone to the PR yet.)
The first example, it is unclear to me what is "valid" given that the body of the closure does not do anything? But the syntax for the isolation capture is valid, and the capture will be isolated to
otherActor
.The second example correct will fail.
The third example yes correct as well.