Skip to content

Commit fa20e52

Browse files
committed
[region-isolation] Add some tests around initializers and distributed actors.
1 parent 9d965f8 commit fa20e52

File tree

2 files changed

+238
-0
lines changed

2 files changed

+238
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -disable-availability-checking -swift-version 6 -verify %s -o /dev/null
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
6+
// This test validates the behavior of transfernonsendable around initializers.
7+
8+
////////////////////////
9+
// MARK: Declarations //
10+
////////////////////////
11+
12+
class NonSendableKlass {}
13+
@MainActor func transferToMain<T>(_ t: T) async {}
14+
15+
actor CustomActorInstance {}
16+
17+
@globalActor
18+
struct CustomActor {
19+
static let shared = CustomActorInstance()
20+
}
21+
22+
/////////////////
23+
// MARK: Tests //
24+
/////////////////
25+
26+
actor ActorWithSynchronousNonIsolatedInit {
27+
let k: NonSendableKlass
28+
29+
init(_ newK: NonSendableKlass) {
30+
k = NonSendableKlass()
31+
32+
helper(newK)
33+
34+
// TODO: This should say actor isolated.
35+
let _ = { @MainActor in
36+
print(newK) // expected-error {{transferring 'newK' may cause a data race}}
37+
// expected-note @-1 {{task-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
38+
}
39+
}
40+
41+
init(x newK: NonSendableKlass) {
42+
k = newK
43+
44+
helper(newK)
45+
46+
let _ = { @MainActor in
47+
// TODO: Second part should say later 'self'-isolated uses
48+
print(newK) // expected-error {{transferring 'newK' may cause a data race}}
49+
// expected-note @-1 {{'self'-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
50+
}
51+
}
52+
53+
nonisolated func helper(_ newK: NonSendableKlass) {}
54+
}
55+
56+
func initActorWithSyncNonIsolatedInit() {
57+
let k = NonSendableKlass()
58+
// TODO: This should say actor isolated.
59+
_ = ActorWithSynchronousNonIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
60+
// expected-note @-1 {{transferring disconnected 'k' to actor-isolated callee could cause races in between callee actor-isolated and local nonisolated uses}}
61+
let _ = { @MainActor in // expected-note {{use here could race}}
62+
print(k)
63+
}
64+
}
65+
66+
func initActorWithSyncNonIsolatedInit2(_ k: NonSendableKlass) {
67+
// TODO: This should say actor isolated.
68+
_ = ActorWithSynchronousNonIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
69+
// expected-note @-1 {{transferring task-isolated 'k' to actor-isolated callee could cause races between actor-isolated and task-isolated uses}}
70+
let _ = { @MainActor in
71+
print(k) // expected-error {{transferring 'k' may cause a data race}}
72+
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
73+
}
74+
}
75+
76+
actor ActorWithAsyncIsolatedInit {
77+
init(_ newK: NonSendableKlass) async {
78+
let _ = { @MainActor in
79+
print(newK) // expected-error {{transferring 'newK' may cause a data race}}
80+
// expected-note @-1 {{actor-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
81+
}
82+
}
83+
}
84+
85+
func initActorWithAsyncIsolatedInit() async {
86+
let k = NonSendableKlass()
87+
// TODO: This should say actor isolated.
88+
_ = await ActorWithAsyncIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
89+
// expected-note @-1 {{transferring disconnected 'k' to actor-isolated callee could cause races in between callee actor-isolated and local nonisolated uses}}
90+
let _ = { @MainActor in // expected-note {{use here could race}}
91+
print(k)
92+
}
93+
}
94+
95+
func initActorWithAsyncIsolatedInit2(_ k: NonSendableKlass) async {
96+
// TODO: This should say actor isolated.
97+
_ = await ActorWithAsyncIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
98+
// expected-note @-1 {{transferring task-isolated 'k' to actor-isolated callee could cause races between actor-isolated and task-isolated uses}}
99+
let _ = { @MainActor in
100+
print(k) // expected-error {{transferring 'k' may cause a data race}}
101+
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
102+
}
103+
}
104+
105+
////////////////////////////////
106+
// MARK: Actor Isolated Class //
107+
////////////////////////////////
108+
109+
@CustomActor
110+
class ClassWithSynchronousNonIsolatedInit {
111+
nonisolated init(_ newK: NonSendableKlass) {
112+
// We do not error on this since helper is nonisolated and newK is
113+
// considered task isolated.
114+
helper(newK)
115+
116+
let _ = { @MainActor in
117+
print(newK) // expected-error {{transferring 'newK' may cause a data race}}
118+
// expected-note @-1 {{task-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
119+
}
120+
}
121+
122+
nonisolated func helper(_ newK: NonSendableKlass) {}
123+
}
124+
125+
func initClassWithSyncNonIsolatedInit() {
126+
let k = NonSendableKlass()
127+
_ = ClassWithSynchronousNonIsolatedInit(k)
128+
let _ = { @MainActor in
129+
print(k)
130+
}
131+
}
132+
133+
func initClassWithSyncNonIsolatedInit2(_ k: NonSendableKlass) {
134+
_ = ClassWithSynchronousNonIsolatedInit(k)
135+
let _ = { @MainActor in
136+
print(k) // expected-error {{transferring 'k' may cause a data race}}
137+
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
138+
}
139+
}
140+
141+
@CustomActor
142+
class ClassWithAsyncIsolatedInit {
143+
init(_ newK: NonSendableKlass) async {
144+
let _ = { @MainActor in
145+
print(newK) // expected-error {{transferring 'newK' may cause a data race}}
146+
// expected-note @-1 {{global actor 'CustomActor'-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
147+
}
148+
}
149+
}
150+
151+
func initClassWithAsyncIsolatedInit() async {
152+
let k = NonSendableKlass()
153+
// TODO: Might make sense to emit a more specific error here since the closure
154+
// is MainActor isolated. The actual capture is initially not isolated to
155+
// MainActor.
156+
_ = await ClassWithAsyncIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
157+
// expected-note @-1 {{transferring disconnected 'k' to global actor 'CustomActor'-isolated callee could cause races in between callee global actor 'CustomActor'-isolated and local nonisolated uses}}
158+
let _ = { @MainActor in // expected-note {{use here could race}}
159+
print(k)
160+
}
161+
}
162+
163+
func initClassWithAsyncIsolatedInit2(_ k: NonSendableKlass) async {
164+
_ = await ClassWithAsyncIsolatedInit(k) // expected-error {{transferring 'k' may cause a data race}}
165+
// expected-note @-1 {{transferring task-isolated 'k' to global actor 'CustomActor'-isolated callee could cause races between global actor 'CustomActor'-isolated and task-isolated uses}}
166+
let _ = { @MainActor in
167+
print(k) // expected-error {{transferring 'k' may cause a data race}}
168+
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
169+
}
170+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-swift-frontend -swift-version 6 -verify -verify-ignore-unknown -disable-availability-checking -I %t %s -emit-sil
4+
5+
// REQUIRES: concurrency
6+
// REQUIRES: distributed
7+
8+
import Distributed
9+
import FakeDistributedActorSystems
10+
11+
////////////////////////
12+
// MARK: Declarations //
13+
////////////////////////
14+
15+
@available(SwiftStdlib 5.5, *)
16+
typealias DefaultDistributedActorSystem = FakeActorSystem
17+
18+
final class NonSendableKlass {}
19+
20+
extension NonSendableKlass : Codable {}
21+
22+
@MainActor func transferToMain<T>(_ t: T) async {}
23+
24+
/////////////////
25+
// MARK: Tests //
26+
/////////////////
27+
28+
distributed actor MyDistributedActor {
29+
let x: NonSendableKlass
30+
31+
init(system: FakeActorSystem, y: NonSendableKlass) {
32+
x = NonSendableKlass()
33+
actorSystem = system
34+
_ = { @MainActor in
35+
// TODO: This should error saying 'y' is actor isolated.
36+
print(y) // expected-error {{transferring 'y' may cause a data race}}
37+
// expected-note @-1 {{task-isolated 'y' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
38+
}
39+
}
40+
41+
init(system: FakeActorSystem, y2: NonSendableKlass) {
42+
x = y2
43+
actorSystem = system
44+
_ = { @MainActor in
45+
print(y2) // expected-error {{transferring 'y2' may cause a data race}}
46+
// expected-note @-1 {{'self'-isolated 'y2' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
47+
}
48+
}
49+
50+
distributed func transferActorField() async {
51+
await transferToMain(x) // expected-error {{transferring 'self.x' may cause a data race}}
52+
// expected-note @-1 {{transferring 'self'-isolated 'self.x' to main actor-isolated callee could cause races between main actor-isolated and 'self'-isolated uses}}
53+
}
54+
55+
distributed func transferActorIsolatedArg(_ x: NonSendableKlass) async {
56+
await transferToMain(x) // expected-error {{transferring 'x' may cause a data race}}
57+
// expected-note @-1 {{transferring actor-isolated 'x' to main actor-isolated callee could cause races between main actor-isolated and actor-isolated uses}}
58+
}
59+
60+
distributed func transferActorIsolatedArgIntoClosure(_ x: NonSendableKlass) async {
61+
_ = { @MainActor in
62+
// TODO: In 2nd part of message should say actor-isolated instead of later
63+
// nonisolated uses in the case of a closure.
64+
print(x) // expected-error {{transferring 'x' may cause a data race}}
65+
// expected-note @-1 {{actor-isolated 'x' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)