Skip to content

Commit 9c02a3e

Browse files
committed
Add diagnostic tests for top-level concurrency modes
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.
1 parent 3b90b08 commit 9c02a3e

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -enable-experimental-async-top-level -swift-version 5 %s -verify
2+
3+
// enable-experimental-async-top-level is passed and an await is used in the
4+
// top-level, so the top-level code is a concurrent context. Variables are
5+
// declared with `@_predatesConcurrency @MainActor`, and the top-level is run on
6+
// the main actor.
7+
8+
var a = 10 // expected-note {{mutation of this var is only permitted within the actor}}
9+
10+
func nonIsolatedSync() {
11+
// Okay because `a` is '@_predatesConcurrency'
12+
print(a)
13+
a = a + 10
14+
}
15+
16+
@MainActor
17+
func isolatedSync() {
18+
print(a)
19+
a = a + 10
20+
}
21+
22+
func nonIsolatedAsync() async {
23+
await print(a)
24+
a = a + 10
25+
// expected-error@-1:5 {{var 'a' isolated to global actor 'MainActor' can not be mutated from this context}}
26+
// expected-error@-2:9 {{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
27+
// expected-note@-3:9 {{property access is 'async'}}
28+
}
29+
30+
@MainActor
31+
func isolatedAsync() async {
32+
print(a)
33+
a = a + 10
34+
}
35+
36+
nonIsolatedSync()
37+
isolatedSync()
38+
await nonIsolatedAsync()
39+
await isolatedAsync()
40+
41+
print(a)
42+
43+
if a > 10 {
44+
nonIsolatedSync()
45+
isolatedSync()
46+
await nonIsolatedAsync()
47+
await isolatedAsync()
48+
49+
print(a)
50+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -enable-experimental-async-top-level -swift-version 6 %s -verify
2+
3+
var a = 10
4+
// expected-note@-1 2 {{var declared here}}
5+
// expected-note@-2 2 {{mutation of this var is only permitted within the actor}}
6+
7+
func nonIsolatedSync() { //expected-note 3 {{add '@MainActor' to make global function 'nonIsolatedSync()' part of global actor 'MainActor'}}
8+
print(a) // expected-error {{var 'a' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
9+
a = a + 10
10+
// expected-error@-1:5 {{var 'a' isolated to global actor 'MainActor' can not be mutated from this context}}
11+
// expected-error@-2:9 {{var 'a' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
12+
13+
}
14+
15+
@MainActor
16+
func isolatedSync() {
17+
print(a)
18+
a = a + 10
19+
}
20+
21+
func nonIsolatedAsync() async {
22+
await print(a)
23+
a = a + 10
24+
// expected-error@-1:5 {{var 'a' isolated to global actor 'MainActor' can not be mutated from this context}}
25+
// expected-error@-2:9 {{expression is 'async' but is not marked with 'await'}}
26+
// expected-note@-3:9 {{property access is 'async'}}
27+
}
28+
29+
@MainActor
30+
func isolatedAsync() async {
31+
print(a)
32+
a = a + 10
33+
}
34+
35+
nonIsolatedSync()
36+
isolatedSync()
37+
await nonIsolatedAsync()
38+
await isolatedAsync()
39+
40+
print(a)
41+
42+
if a > 10 {
43+
nonIsolatedSync()
44+
isolatedSync()
45+
await nonIsolatedAsync()
46+
await isolatedAsync()
47+
48+
print(a)
49+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -enable-experimental-async-top-level -swift-version 5 %s -verify
2+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -swift-version 5 %s -verify
3+
4+
// Even though enable-experimental-async-top-level is enabled, there are no
5+
// `await`s made from the top-level, so it is not an async context. `a` is just
6+
// a normal top-level global variable with no actor isolation.
7+
8+
var a = 10
9+
10+
func nonIsolatedSync() {
11+
print(a)
12+
a = a + 10
13+
}
14+
15+
@MainActor
16+
func isolatedSync() { // expected-note 2 {{calls to global function 'isolatedSync()' from outside of its actor context are implicitly asynchronous}}
17+
print(a)
18+
a = a + 10
19+
}
20+
21+
func nonIsolatedAsync() async {
22+
await print(a) // expected-warning {{no 'async' operations occur within 'await' expression}}
23+
a = a + 10
24+
}
25+
26+
@MainActor
27+
func isolatedAsync() async { // expected-note 2 {{calls to global function 'isolatedAsync()' from outside of its actor context are implicitly asynchronous}}
28+
print(a)
29+
a = a + 10
30+
}
31+
32+
nonIsolatedSync()
33+
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
34+
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
35+
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
36+
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
37+
38+
print(a)
39+
40+
if a > 10 {
41+
nonIsolatedSync()
42+
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
43+
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
44+
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
45+
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
46+
47+
print(a)
48+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -enable-experimental-async-top-level -swift-version 6 %s -verify
2+
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -swift-version 6 %s -verify
3+
4+
// Even though enable-experimental-async-top-level is enabled, there are no
5+
// 'await's made from the top-level, thus the top-level is not an asynchronous
6+
// context. `a` is just a normal top-level global variable with no actor
7+
// isolation.
8+
9+
var a = 10 // expected-note 15 {{var declared here}}
10+
11+
func nonIsolatedSync() {
12+
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
13+
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
14+
}
15+
16+
@MainActor
17+
func isolatedSync() { // expected-note 2 {{calls to global function 'isolatedSync()' from outside of its actor context are implicitly asynchronous}}
18+
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
19+
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
20+
}
21+
22+
func nonIsolatedAsync() async {
23+
await print(a) // expected-warning {{no 'async' operations occur within 'await' expression}}
24+
// expected-warning@-1 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
25+
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
26+
}
27+
28+
@MainActor
29+
func isolatedAsync() async { // expected-note 2 {{calls to global function 'isolatedAsync()' from outside of its actor context are implicitly asynchronous}}
30+
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
31+
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
32+
}
33+
34+
nonIsolatedSync()
35+
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
36+
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
37+
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
38+
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
39+
40+
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
41+
42+
if a > 10 { // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
43+
nonIsolatedSync()
44+
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
45+
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
46+
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
47+
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
48+
49+
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
50+
}

0 commit comments

Comments
 (0)