Skip to content
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

Concurrent top-level detection #41061

Merged
merged 4 commits into from
Feb 1, 2022

Conversation

etcwilde
Copy link
Contributor

This PR gets the semantics for detecting when to make top-level code a concurrent context. For compatibility reasons, we want scripts written in top-level code without concurrency today in Swift 5 to work exactly the same way when the async top-level lands. To ensure this, the top-level needs to detect when concurrency features are being used before behaving as an async context. The implementation of this is to detect when an await keyword has been used before switching the top-level code context to an asynchronous context. If there are no suspension points directly in the top-level, the context is not asynchronous.

The implications of making the top-level code an asynchronous context include overload resolution behavior and semantics around spinning up a runloop in the implicit async_Main function. These two changes to behavior could cause unintended script breakage. Thus, if there are no await calls made, the top-level will behave exactly as it did before.

This patch replaces the use of the experimental-async-top-level flag
with checking the decl context of the top-level variable to determine
whether the top-level contexts should be async.
I need to determine when top-level code contains an `await` to determine
whether to make the source file an async context. This logic is
perfectly encapsulated in the `FindInnerAsync` AST walker.
Unfortunately, that is pushed down in Sema/ConstraintSystem and isn't
available at the AST level. I've pulled it up into the brace statement
so that I can use that as part of determining whether the source file is
async in `DeclContext::isAsyncContext`.

Unfortunately, statements don't have an AST context or evaluator or I
would make this a request.
To help maintain source-compatibility, the presence of an `await` in
top-level code to kick the top-level code over to being a concurrent
context.

This, of course, means that in the test cases that exist today, they
will go back to behaving identically to how they did before I added all
of this because they don't have any awaits in the top-level. I'll be
adding new tests to verify the differences in behavior between swift 5,
swift 6, with and without async top level enabled in the next commit.
I've added four nearly identical top-level code tests to ensure the
correct diagnostic behaviours.

       | with await              | without await              |
-------+-------------------------+----------------------------+
Swift 5| async-5-top-level.swift | no-async-5-top-level.swift |
-------+-------------------------+----------------------------+
Swift 6| async-6-top-level.swift | no-async-6-top-level.swift |
-------+-------------------------+----------------------------+

Swift 5 and Swift 6 without without an await are identical to the
behaviour of the corresponding language version without concurrency
enabled at all. The differences between the two being whether a warning
is emitted about global variables not being safe for concurrency in
Swift 6, while no warnings are emitted in Swift 5.

Concurrency is where things get more interesting.
In Swift 5, top-level variables effectively have implicit
`@_predatesConcurrency @MainActor` attributes added. This is to help
alleviate some of the burden of switching things over and not break as
many scripts things. In this mode, top-level global variables can be
used directly in synchronous functions, regardless of which actor they
are on. Asynchronous functions have to use them appropriately.

In Swift 6, top-level global variables used from nonisolated contexts
will receive a proper error saying the function must be isolated to the
global actor to use the variables directly.
@etcwilde etcwilde added the concurrency Feature: umbrella label for concurrency language features label Jan 28, 2022
@etcwilde
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 9c02a3e

@etcwilde
Copy link
Contributor Author

@swift-ci please test macos

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 9c02a3e

@etcwilde
Copy link
Contributor Author

@swift-ci please test macOS

@etcwilde etcwilde merged commit 377ca35 into swiftlang:main Feb 1, 2022
@etcwilde etcwilde deleted the ewilde/async-top-level-detection branch February 4, 2022 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
concurrency Feature: umbrella label for concurrency language features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants