Skip to content

Commit db66a20

Browse files
authored
Merge pull request #74151 from gottesmm/pr-89c1f2ab73ad928c312b9bdddb9e2c5cb550439c
[concurrency] Update withUnsafe{,Throwing}Continuation to have a sending result.
2 parents 40e8b15 + 5db3b70 commit db66a20

File tree

2 files changed

+138
-10
lines changed

2 files changed

+138
-10
lines changed

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ public struct UnsafeContinuation<T, E: Error>: Sendable {
505505
/// The task continues executing
506506
/// when its executor schedules it.
507507
@_alwaysEmitIntoClient
508-
public func resume(returning value: __owned T) where E == Never {
508+
public func resume(returning value: sending T) where E == Never {
509509
#if compiler(>=5.5) && $BuiltinContinuation
510510
Builtin.resumeNonThrowingContinuationReturning(context, value)
511511
#else
@@ -527,7 +527,7 @@ public struct UnsafeContinuation<T, E: Error>: Sendable {
527527
/// The task continues executing
528528
/// when its executor schedules it.
529529
@_alwaysEmitIntoClient
530-
public func resume(returning value: __owned T) {
530+
public func resume(returning value: sending T) {
531531
#if compiler(>=5.5) && $BuiltinContinuation
532532
Builtin.resumeThrowingContinuationReturning(context, value)
533533
#else
@@ -549,7 +549,7 @@ public struct UnsafeContinuation<T, E: Error>: Sendable {
549549
/// The task continues executing
550550
/// when its executor schedules it.
551551
@_alwaysEmitIntoClient
552-
public func resume(throwing error: __owned E) {
552+
public func resume(throwing error: consuming E) {
553553
#if compiler(>=5.5) && $BuiltinContinuation
554554
Builtin.resumeThrowingContinuationThrowing(context, error)
555555
#else
@@ -577,7 +577,7 @@ extension UnsafeContinuation {
577577
/// The task continues executing
578578
/// when its executor schedules it.
579579
@_alwaysEmitIntoClient
580-
public func resume<Er: Error>(with result: Result<T, Er>) where E == Error {
580+
public func resume<Er: Error>(with result: __shared sending Result<T, Er>) where E == Error {
581581
switch result {
582582
case .success(let val):
583583
self.resume(returning: val)
@@ -603,7 +603,7 @@ extension UnsafeContinuation {
603603
/// The task continues executing
604604
/// when its executor schedules it.
605605
@_alwaysEmitIntoClient
606-
public func resume(with result: Result<T, E>) {
606+
public func resume(with result: __shared sending Result<T, E>) {
607607
switch result {
608608
case .success(let val):
609609
self.resume(returning: val)
@@ -635,7 +635,7 @@ extension UnsafeContinuation {
635635
@_alwaysEmitIntoClient
636636
internal func _resumeUnsafeContinuation<T>(
637637
_ continuation: UnsafeContinuation<T, Never>,
638-
_ value: __owned T
638+
_ value: sending T
639639
) {
640640
continuation.resume(returning: value)
641641
}
@@ -644,7 +644,7 @@ internal func _resumeUnsafeContinuation<T>(
644644
@_alwaysEmitIntoClient
645645
internal func _resumeUnsafeThrowingContinuation<T>(
646646
_ continuation: UnsafeContinuation<T, Error>,
647-
_ value: __owned T
647+
_ value: sending T
648648
) {
649649
continuation.resume(returning: value)
650650
}
@@ -653,7 +653,7 @@ internal func _resumeUnsafeThrowingContinuation<T>(
653653
@_alwaysEmitIntoClient
654654
internal func _resumeUnsafeThrowingContinuationWithError<T>(
655655
_ continuation: UnsafeContinuation<T, Error>,
656-
_ error: __owned Error
656+
_ error: consuming Error
657657
) {
658658
continuation.resume(throwing: error)
659659
}
@@ -689,7 +689,7 @@ internal func _resumeUnsafeThrowingContinuationWithError<T>(
689689
public func withUnsafeContinuation<T>(
690690
isolation: isolated (any Actor)? = #isolation,
691691
_ fn: (UnsafeContinuation<T, Never>) -> Void
692-
) async -> T {
692+
) async -> sending T {
693693
return await Builtin.withUnsafeContinuation {
694694
fn(UnsafeContinuation<T, Never>($0))
695695
}
@@ -725,7 +725,7 @@ public func withUnsafeContinuation<T>(
725725
public func withUnsafeThrowingContinuation<T>(
726726
isolation: isolated (any Actor)? = #isolation,
727727
_ fn: (UnsafeContinuation<T, Error>) -> Void
728-
) async throws -> T {
728+
) async throws -> sending T {
729729
return try await Builtin.withUnsafeThrowingContinuation {
730730
fn(UnsafeContinuation<T, Error>($0))
731731
}

test/Concurrency/sending_continuation.swift

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ func withCheckedContinuation_1() async -> NonSendableKlass {
2323
}
2424
}
2525

26+
func withCheckedContinuation_1a() async -> NonSendableKlass {
27+
await withCheckedContinuation { continuation in
28+
continuation.resume(returning: NonSendableKlass())
29+
}
30+
}
31+
2632
@MainActor
2733
func withCheckedContinuation_2() async -> NonSendableKlass {
2834
await withCheckedContinuation { continuation in
@@ -34,6 +40,16 @@ func withCheckedContinuation_2() async -> NonSendableKlass {
3440
}
3541
}
3642

43+
func withCheckedContinuation_2a() async -> NonSendableKlass {
44+
await withCheckedContinuation { continuation in
45+
let x = NonSendableKlass()
46+
continuation.resume(returning: x)
47+
// expected-error @-1 {{sending 'x' risks causing data races}}
48+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
49+
useValue(x) // expected-note {{access can happen concurrently}}
50+
}
51+
}
52+
3753
@MainActor
3854
func withCheckedContinuation_3() async {
3955
// x is main actor isolated since withCheckedContinuation is #isolated.
@@ -49,6 +65,19 @@ func withCheckedContinuation_3() async {
4965
// expected-note @-2 {{sending main actor-isolated 'x' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}}
5066
}
5167

68+
func withCheckedContinuation_3a() async {
69+
let x = await withCheckedContinuation { continuation in
70+
let x = NonSendableKlass()
71+
continuation.resume(returning: x)
72+
// expected-error @-1 {{sending 'x' risks causing data races}}
73+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
74+
useValue(x) // expected-note {{access can happen concurrently}}
75+
}
76+
77+
// This is ok since x is disconnected.
78+
await useValueAsync(x)
79+
}
80+
5281
@MainActor
5382
func withCheckedContinuation_4() async {
5483
// x is main actor isolated since withCheckedContinuation is #isolated.
@@ -64,6 +93,18 @@ func withCheckedContinuation_4() async {
6493
// expected-note @-2 {{sending main actor-isolated 'x' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}}
6594
}
6695

96+
func withCheckedContinuation_4a() async {
97+
// x is main actor isolated since withCheckedContinuation is #isolated.
98+
let y = NonSendableKlass()
99+
let x = await withCheckedContinuation { continuation in
100+
continuation.resume(returning: y)
101+
// expected-error @-1 {{sending 'y' risks causing data races}}
102+
// expected-note @-2 {{task-isolated 'y' is passed as a 'sending' parameter}}
103+
useValue(y)
104+
}
105+
await useValueAsync(x)
106+
}
107+
67108
@MainActor func testAsyncStream() {
68109
let (_, continuation) = AsyncStream.makeStream(of: NonSendableKlass.self)
69110

@@ -103,3 +144,90 @@ func withCheckedContinuation_4() async {
103144
useValue(x) // expected-note {{access can happen concurrently}}
104145
}
105146
}
147+
148+
@MainActor
149+
func withUnsafeContinuation_1() async -> NonSendableKlass {
150+
await withUnsafeContinuation { continuation in
151+
continuation.resume(returning: NonSendableKlass())
152+
}
153+
}
154+
155+
func withUnsafeContinuation_1a() async -> NonSendableKlass {
156+
await withUnsafeContinuation { continuation in
157+
continuation.resume(returning: NonSendableKlass())
158+
}
159+
}
160+
161+
@MainActor
162+
func withUnsafeContinuation_2() async -> NonSendableKlass {
163+
await withUnsafeContinuation { continuation in
164+
let x = NonSendableKlass()
165+
continuation.resume(returning: x)
166+
// expected-error @-1 {{sending 'x' risks causing data races}}
167+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
168+
useValue(x) // expected-note {{access can happen concurrently}}
169+
}
170+
}
171+
172+
func withUnsafeContinuation_2a() async -> NonSendableKlass {
173+
await withUnsafeContinuation { continuation in
174+
let x = NonSendableKlass()
175+
continuation.resume(returning: x)
176+
// expected-error @-1 {{sending 'x' risks causing data races}}
177+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
178+
useValue(x) // expected-note {{access can happen concurrently}}
179+
}
180+
}
181+
182+
@MainActor
183+
func withUnsafeContinuation_3() async {
184+
// x is main actor isolated since withUnsafeContinuation is #isolated.
185+
let x = await withUnsafeContinuation { continuation in
186+
let x = NonSendableKlass()
187+
continuation.resume(returning: x)
188+
// expected-error @-1 {{sending 'x' risks causing data races}}
189+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
190+
useValue(x) // expected-note {{access can happen concurrently}}
191+
}
192+
await useValueAsync(x)
193+
// expected-error @-1 {{sending 'x' risks causing data races}}
194+
// expected-note @-2 {{sending main actor-isolated 'x' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}}
195+
}
196+
197+
func withUnsafeContinuation_3a() async {
198+
let x = await withUnsafeContinuation { continuation in
199+
let x = NonSendableKlass()
200+
continuation.resume(returning: x)
201+
// expected-error @-1 {{sending 'x' risks causing data races}}
202+
// expected-note @-2 {{'x' used after being passed as a 'sending' parameter}}
203+
useValue(x) // expected-note {{access can happen concurrently}}
204+
}
205+
await useValueAsync(x)
206+
}
207+
208+
@MainActor
209+
func withUnsafeContinuation_4() async {
210+
// x is main actor isolated since withUnsafeContinuation is #isolated.
211+
let y = NonSendableKlass()
212+
let x = await withUnsafeContinuation { continuation in
213+
continuation.resume(returning: y)
214+
// expected-error @-1 {{sending 'y' risks causing data races}}
215+
// expected-note @-2 {{main actor-isolated 'y' is passed as a 'sending' parameter}}
216+
useValue(y)
217+
}
218+
await useValueAsync(x)
219+
// expected-error @-1 {{sending 'x' risks causing data races}}
220+
// expected-note @-2 {{sending main actor-isolated 'x' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}}
221+
}
222+
223+
func withUnsafeContinuation_4a() async {
224+
// x is main actor isolated since withUnsafeContinuation is #isolated.
225+
let y = NonSendableKlass()
226+
let x = await withUnsafeContinuation { continuation in
227+
continuation.resume(returning: y)
228+
// expected-error @-1 {{sending 'y' risks causing data races}}
229+
// expected-note @-2 {{task-isolated 'y' is passed as a 'sending' parameter}}
230+
useValue(y)
231+
}
232+
await useValueAsync(x)
233+
}

0 commit comments

Comments
 (0)