Skip to content

Commit 4fbf7a5

Browse files
authored
Merge pull request #40192 from DougGregor/unchecked-continuation-conditional-sendable
2 parents 0b5081d + 5a99ae4 commit 4fbf7a5

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

stdlib/public/Concurrency/CheckedContinuation.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal func logFailedCheck(_ message: UnsafeRawPointer)
1919
/// Implementation class that holds the `UnsafeContinuation` instance for
2020
/// a `CheckedContinuation`.
2121
@available(SwiftStdlib 5.1, *)
22-
internal final class CheckedContinuationCanary {
22+
internal final class CheckedContinuationCanary: @unchecked Sendable {
2323
// The instance state is stored in tail-allocated raw memory, so that
2424
// we can atomically check the continuation state.
2525

@@ -110,7 +110,7 @@ internal final class CheckedContinuationCanary {
110110
/// enough to obtain the extra checking without further source modification in
111111
/// most circumstances.
112112
@available(SwiftStdlib 5.1, *)
113-
public struct CheckedContinuation<T, E: Error>: @unchecked Sendable {
113+
public struct CheckedContinuation<T, E: Error> {
114114
private let canary: CheckedContinuationCanary
115115

116116
/// Initialize a `CheckedContinuation` wrapper around an
@@ -175,6 +175,9 @@ public struct CheckedContinuation<T, E: Error>: @unchecked Sendable {
175175
}
176176
}
177177

178+
@available(SwiftStdlib 5.1, *)
179+
extension CheckedContinuation: Sendable where T: Sendable { }
180+
178181
@available(SwiftStdlib 5.1, *)
179182
extension CheckedContinuation {
180183
/// Resume the task awaiting the continuation by having it either

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public struct UnownedJob: Sendable {
3434

3535
@available(SwiftStdlib 5.1, *)
3636
@frozen
37-
public struct UnsafeContinuation<T, E: Error>: Sendable {
37+
public struct UnsafeContinuation<T, E: Error> {
3838
@usableFromInline internal var context: Builtin.RawUnsafeContinuation
3939

4040
@_alwaysEmitIntoClient
@@ -106,6 +106,9 @@ public struct UnsafeContinuation<T, E: Error>: Sendable {
106106
}
107107
}
108108

109+
@available(SwiftStdlib 5.1, *)
110+
extension UnsafeContinuation: Sendable where T: Sendable { }
111+
109112
@available(SwiftStdlib 5.1, *)
110113
extension UnsafeContinuation {
111114
/// Resume the task awaiting the continuation by having it either

test/Concurrency/async_tasks.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ func test_unsafeThrowingContinuations() async throws {
8787
// TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure?
8888
}
8989

90+
// ==== Sendability ------------------------------------------------------------
91+
class NotSendable { }
92+
// expected-note@-1{{class 'NotSendable' does not conform to the 'Sendable' protocol}}
93+
94+
@available(SwiftStdlib 5.1, *)
95+
func test_nonsendableContinuation() async throws {
96+
let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
97+
continuation.resume(returning: NotSendable())
98+
}
99+
100+
let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
101+
Task {
102+
continuation.resume(returning: NotSendable()) // expected-warning{{cannot use parameter 'continuation' with a non-sendable type 'UnsafeContinuation<NotSendable, Error>' from concurrently-executed code}}
103+
}
104+
}
105+
}
106+
90107
// ==== Detached Tasks ---------------------------------------------------------
91108

92109
@available(SwiftStdlib 5.1, *)

0 commit comments

Comments
 (0)