Skip to content

Commit b0f0cb2

Browse files
authored
Audit and correct AsyncSequence Sendability (#41253)
* Audit and correct AsyncSequence Sendability * Annotate availability on Sendable conformances for AsyncSequences * Fix typo of Sendable.Iterator * Correct tests to use sendable builders * Add @preconcurrency annotations and fixup one remaining missing @sendable case * Move preconcurrency to correct syntactical position * Use unchecked Sendable conditional conformance instead of marking all internals as preconcurrency and sendable * Use unchecked Sendable conditional conformance instead of marking all internals as preconcurrency and sendable for dropWhile * claim ABI changes for marking of @preconcurrency as expected
1 parent 72c7284 commit b0f0cb2

16 files changed

+191
-15
lines changed

stdlib/public/Concurrency/AsyncCompactMapSequence.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ extension AsyncSequence {
4545
/// same or of a different type.
4646
/// - Returns: An asynchronous sequence that contains, in order, the
4747
/// non-`nil` elements produced by the `transform` closure.
48+
@preconcurrency
4849
@inlinable
4950
public __consuming func compactMap<ElementOfResult>(
50-
_ transform: @escaping (Element) async -> ElementOfResult?
51+
_ transform: @Sendable @escaping (Element) async -> ElementOfResult?
5152
) -> AsyncCompactMapSequence<Self, ElementOfResult> {
5253
return AsyncCompactMapSequence(self, transform: transform)
5354
}
@@ -129,3 +130,15 @@ extension AsyncCompactMapSequence: AsyncSequence {
129130
return Iterator(base.makeAsyncIterator(), transform: transform)
130131
}
131132
}
133+
134+
@available(SwiftStdlib 5.1, *)
135+
extension AsyncCompactMapSequence: @unchecked Sendable
136+
where Base: Sendable,
137+
Base.Element: Sendable,
138+
ElementOfResult: Sendable { }
139+
140+
@available(SwiftStdlib 5.1, *)
141+
extension AsyncCompactMapSequence.Iterator: @unchecked Sendable
142+
where Base.AsyncIterator: Sendable,
143+
Base.Element: Sendable,
144+
ElementOfResult: Sendable { }

stdlib/public/Concurrency/AsyncDropFirstSequence.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,13 @@ extension AsyncDropFirstSequence {
135135
return AsyncDropFirstSequence(base, dropping: self.count + count)
136136
}
137137
}
138+
139+
@available(SwiftStdlib 5.1, *)
140+
extension AsyncDropFirstSequence: Sendable
141+
where Base: Sendable,
142+
Base.Element: Sendable { }
143+
144+
@available(SwiftStdlib 5.1, *)
145+
extension AsyncDropFirstSequence.Iterator: Sendable
146+
where Base.AsyncIterator: Sendable,
147+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncDropWhileSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ extension AsyncSequence {
4141
/// modified sequence.
4242
/// - Returns: An asynchronous sequence that skips over values from the
4343
/// base sequence until the provided closure returns `false`.
44+
@preconcurrency
4445
@inlinable
4546
public __consuming func drop(
46-
while predicate: @escaping (Element) async -> Bool
47+
while predicate: @Sendable @escaping (Element) async -> Bool
4748
) -> AsyncDropWhileSequence<Self> {
4849
AsyncDropWhileSequence(self, predicate: predicate)
4950
}
@@ -127,3 +128,13 @@ extension AsyncDropWhileSequence: AsyncSequence {
127128
return Iterator(base.makeAsyncIterator(), predicate: predicate)
128129
}
129130
}
131+
132+
@available(SwiftStdlib 5.1, *)
133+
extension AsyncDropWhileSequence: @unchecked Sendable
134+
where Base: Sendable,
135+
Base.Element: Sendable { }
136+
137+
@available(SwiftStdlib 5.1, *)
138+
extension AsyncDropWhileSequence.Iterator: @unchecked Sendable
139+
where Base.AsyncIterator: Sendable,
140+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncFilterSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ extension AsyncSequence {
3333
/// that indicates whether to include the element in the filtered sequence.
3434
/// - Returns: An asynchronous sequence that contains, in order, the elements
3535
/// of the base sequence that satisfy the given predicate.
36+
@preconcurrency
3637
@inlinable
3738
public __consuming func filter(
38-
_ isIncluded: @escaping (Element) async -> Bool
39+
_ isIncluded: @Sendable @escaping (Element) async -> Bool
3940
) -> AsyncFilterSequence<Self> {
4041
return AsyncFilterSequence(self, isIncluded: isIncluded)
4142
}
@@ -113,3 +114,13 @@ extension AsyncFilterSequence: AsyncSequence {
113114
return Iterator(base.makeAsyncIterator(), isIncluded: isIncluded)
114115
}
115116
}
117+
118+
@available(SwiftStdlib 5.1, *)
119+
extension AsyncFilterSequence: @unchecked Sendable
120+
where Base: Sendable,
121+
Base.Element: Sendable { }
122+
123+
@available(SwiftStdlib 5.1, *)
124+
extension AsyncFilterSequence.Iterator: @unchecked Sendable
125+
where Base.AsyncIterator: Sendable,
126+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncFlatMapSequence.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ extension AsyncSequence {
3939
/// of this sequence as its parameter and returns an `AsyncSequence`.
4040
/// - Returns: A single, flattened asynchronous sequence that contains all
4141
/// elements in all the asychronous sequences produced by `transform`.
42-
@inlinable
42+
@preconcurrency
43+
@inlinable
4344
public __consuming func flatMap<SegmentOfResult: AsyncSequence>(
44-
_ transform: @escaping (Element) async -> SegmentOfResult
45+
_ transform: @Sendable @escaping (Element) async -> SegmentOfResult
4546
) -> AsyncFlatMapSequence<Self, SegmentOfResult> {
4647
return AsyncFlatMapSequence(self, transform: transform)
4748
}
@@ -154,3 +155,18 @@ extension AsyncFlatMapSequence: AsyncSequence {
154155
return Iterator(base.makeAsyncIterator(), transform: transform)
155156
}
156157
}
158+
159+
@available(SwiftStdlib 5.1, *)
160+
extension AsyncFlatMapSequence: @unchecked Sendable
161+
where Base: Sendable,
162+
Base.Element: Sendable,
163+
SegmentOfResult: Sendable,
164+
SegmentOfResult.Element: Sendable { }
165+
166+
@available(SwiftStdlib 5.1, *)
167+
extension AsyncFlatMapSequence.Iterator: @unchecked Sendable
168+
where Base.AsyncIterator: Sendable,
169+
Base.Element: Sendable,
170+
SegmentOfResult: Sendable,
171+
SegmentOfResult.Element: Sendable,
172+
SegmentOfResult.AsyncIterator: Sendable { }

stdlib/public/Concurrency/AsyncMapSequence.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ extension AsyncSequence {
4343
/// same or of a different type.
4444
/// - Returns: An asynchronous sequence that contains, in order, the elements
4545
/// produced by the `transform` closure.
46+
@preconcurrency
4647
@inlinable
4748
public __consuming func map<Transformed>(
48-
_ transform: @escaping (Element) async -> Transformed
49+
_ transform: @Sendable @escaping (Element) async -> Transformed
4950
) -> AsyncMapSequence<Self, Transformed> {
5051
return AsyncMapSequence(self, transform: transform)
5152
}
@@ -117,3 +118,15 @@ extension AsyncMapSequence: AsyncSequence {
117118
return Iterator(base.makeAsyncIterator(), transform: transform)
118119
}
119120
}
121+
122+
@available(SwiftStdlib 5.1, *)
123+
extension AsyncMapSequence: @unchecked Sendable
124+
where Base: Sendable,
125+
Base.Element: Sendable,
126+
Transformed: Sendable { }
127+
128+
@available(SwiftStdlib 5.1, *)
129+
extension AsyncMapSequence.Iterator: @unchecked Sendable
130+
where Base.AsyncIterator: Sendable,
131+
Base.Element: Sendable,
132+
Transformed: Sendable { }

stdlib/public/Concurrency/AsyncPrefixSequence.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,13 @@ extension AsyncPrefixSequence: AsyncSequence {
109109
return Iterator(base.makeAsyncIterator(), count: count)
110110
}
111111
}
112+
113+
@available(SwiftStdlib 5.1, *)
114+
extension AsyncPrefixSequence: Sendable
115+
where Base: Sendable,
116+
Base.Element: Sendable { }
117+
118+
@available(SwiftStdlib 5.1, *)
119+
extension AsyncPrefixSequence.Iterator: Sendable
120+
where Base.AsyncIterator: Sendable,
121+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ extension AsyncSequence {
3838
/// included in the modified sequence.
3939
/// - Returns: An asynchronous sequence of the initial, consecutive
4040
/// elements that satisfy `predicate`.
41+
@preconcurrency
4142
@inlinable
4243
public __consuming func prefix(
43-
while predicate: @escaping (Element) async -> Bool
44+
while predicate: @Sendable @escaping (Element) async -> Bool
4445
) rethrows -> AsyncPrefixWhileSequence<Self> {
4546
return AsyncPrefixWhileSequence(self, predicate: predicate)
4647
}
@@ -120,3 +121,13 @@ extension AsyncPrefixWhileSequence: AsyncSequence {
120121
return Iterator(base.makeAsyncIterator(), predicate: predicate)
121122
}
122123
}
124+
125+
@available(SwiftStdlib 5.1, *)
126+
extension AsyncPrefixWhileSequence: @unchecked Sendable
127+
where Base: Sendable,
128+
Base.Element: Sendable { }
129+
130+
@available(SwiftStdlib 5.1, *)
131+
extension AsyncPrefixWhileSequence.Iterator: @unchecked Sendable
132+
where Base.AsyncIterator: Sendable,
133+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncThrowingCompactMapSequence.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ extension AsyncSequence {
5757
/// non-`nil` elements produced by the `transform` closure. The sequence
5858
/// ends either when the base sequence ends or when `transform` throws an
5959
/// error.
60+
@preconcurrency
6061
@inlinable
6162
public __consuming func compactMap<ElementOfResult>(
62-
_ transform: @escaping (Element) async throws -> ElementOfResult?
63+
_ transform: @Sendable @escaping (Element) async throws -> ElementOfResult?
6364
) -> AsyncThrowingCompactMapSequence<Self, ElementOfResult> {
6465
return AsyncThrowingCompactMapSequence(self, transform: transform)
6566
}
@@ -151,3 +152,14 @@ extension AsyncThrowingCompactMapSequence: AsyncSequence {
151152
return Iterator(base.makeAsyncIterator(), transform: transform)
152153
}
153154
}
155+
156+
@available(SwiftStdlib 5.1, *)
157+
extension AsyncThrowingCompactMapSequence: @unchecked Sendable
158+
where Base: Sendable,
159+
Base.Element: Sendable { }
160+
161+
@available(SwiftStdlib 5.1, *)
162+
extension AsyncThrowingCompactMapSequence.Iterator: @unchecked Sendable
163+
where Base.AsyncIterator: Sendable,
164+
Base.Element: Sendable { }
165+

stdlib/public/Concurrency/AsyncThrowingDropWhileSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ extension AsyncSequence {
5454
/// element from the modified sequence.
5555
/// - Returns: An asynchronous sequence that skips over values until the
5656
/// provided closure returns `false` or throws an error.
57+
@preconcurrency
5758
@inlinable
5859
public __consuming func drop(
59-
while predicate: @escaping (Element) async throws -> Bool
60+
while predicate: @Sendable @escaping (Element) async throws -> Bool
6061
) -> AsyncThrowingDropWhileSequence<Self> {
6162
AsyncThrowingDropWhileSequence(self, predicate: predicate)
6263
}
@@ -154,3 +155,13 @@ extension AsyncThrowingDropWhileSequence: AsyncSequence {
154155
return Iterator(base.makeAsyncIterator(), predicate: predicate)
155156
}
156157
}
158+
159+
@available(SwiftStdlib 5.1, *)
160+
extension AsyncThrowingDropWhileSequence: @unchecked Sendable
161+
where Base: Sendable,
162+
Base.Element: Sendable { }
163+
164+
@available(SwiftStdlib 5.1, *)
165+
extension AsyncThrowingDropWhileSequence.Iterator: @unchecked Sendable
166+
where Base.AsyncIterator: Sendable,
167+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncThrowingFilterSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ extension AsyncSequence {
4545
/// of the base sequence that satisfy the given predicate. If the predicate
4646
/// throws an error, the sequence contains only values produced prior to
4747
/// the error.
48+
@preconcurrency
4849
@inlinable
4950
public __consuming func filter(
50-
_ isIncluded: @escaping (Element) async throws -> Bool
51+
_ isIncluded: @Sendable @escaping (Element) async throws -> Bool
5152
) -> AsyncThrowingFilterSequence<Self> {
5253
return AsyncThrowingFilterSequence(self, isIncluded: isIncluded)
5354
}
@@ -136,3 +137,13 @@ extension AsyncThrowingFilterSequence: AsyncSequence {
136137
return Iterator(base.makeAsyncIterator(), isIncluded: isIncluded)
137138
}
138139
}
140+
141+
@available(SwiftStdlib 5.1, *)
142+
extension AsyncThrowingFilterSequence: @unchecked Sendable
143+
where Base: Sendable,
144+
Base.Element: Sendable { }
145+
146+
@available(SwiftStdlib 5.1, *)
147+
extension AsyncThrowingFilterSequence.Iterator: @unchecked Sendable
148+
where Base.AsyncIterator: Sendable,
149+
Base.Element: Sendable { }

stdlib/public/Concurrency/AsyncThrowingFlatMapSequence.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ extension AsyncSequence {
5353
/// elements in all the asychronous sequences produced by `transform`. The
5454
/// sequence ends either when the the last sequence created from the last
5555
/// element from base sequence ends, or when `transform` throws an error.
56+
@preconcurrency
5657
@inlinable
5758
public __consuming func flatMap<SegmentOfResult: AsyncSequence>(
58-
_ transform: @escaping (Element) async throws -> SegmentOfResult
59+
_ transform: @Sendable @escaping (Element) async throws -> SegmentOfResult
5960
) -> AsyncThrowingFlatMapSequence<Self, SegmentOfResult> {
6061
return AsyncThrowingFlatMapSequence(self, transform: transform)
6162
}
@@ -169,3 +170,19 @@ extension AsyncThrowingFlatMapSequence: AsyncSequence {
169170
return Iterator(base.makeAsyncIterator(), transform: transform)
170171
}
171172
}
173+
174+
@available(SwiftStdlib 5.1, *)
175+
extension AsyncThrowingFlatMapSequence: @unchecked Sendable
176+
where Base: Sendable,
177+
Base.Element: Sendable,
178+
SegmentOfResult: Sendable,
179+
SegmentOfResult.Element: Sendable { }
180+
181+
@available(SwiftStdlib 5.1, *)
182+
extension AsyncThrowingFlatMapSequence.Iterator: @unchecked Sendable
183+
where Base.AsyncIterator: Sendable,
184+
Base.Element: Sendable,
185+
SegmentOfResult: Sendable,
186+
SegmentOfResult.Element: Sendable,
187+
SegmentOfResult.AsyncIterator: Sendable { }
188+

stdlib/public/Concurrency/AsyncThrowingMapSequence.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ extension AsyncSequence {
5656
/// ends the transformed sequence.
5757
/// - Returns: An asynchronous sequence that contains, in order, the elements
5858
/// produced by the `transform` closure.
59+
@preconcurrency
5960
@inlinable
6061
public __consuming func map<Transformed>(
61-
_ transform: @escaping (Element) async throws -> Transformed
62+
_ transform: @Sendable @escaping (Element) async throws -> Transformed
6263
) -> AsyncThrowingMapSequence<Self, Transformed> {
6364
return AsyncThrowingMapSequence(self, transform: transform)
6465
}
@@ -140,3 +141,15 @@ extension AsyncThrowingMapSequence: AsyncSequence {
140141
return Iterator(base.makeAsyncIterator(), transform: transform)
141142
}
142143
}
144+
145+
@available(SwiftStdlib 5.1, *)
146+
extension AsyncThrowingMapSequence: @unchecked Sendable
147+
where Base: Sendable,
148+
Base.Element: Sendable,
149+
Transformed: Sendable { }
150+
151+
@available(SwiftStdlib 5.1, *)
152+
extension AsyncThrowingMapSequence.Iterator: @unchecked Sendable
153+
where Base.AsyncIterator: Sendable,
154+
Base.Element: Sendable,
155+
Transformed: Sendable { }

stdlib/public/Concurrency/AsyncThrowingPrefixWhileSequence.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ extension AsyncSequence {
5050
/// of the base sequence that satisfy the given predicate. If the predicate
5151
/// throws an error, the sequence contains only values produced prior to
5252
/// the error.
53+
@preconcurrency
5354
@inlinable
5455
public __consuming func prefix(
55-
while predicate: @escaping (Element) async throws -> Bool
56+
while predicate: @Sendable @escaping (Element) async throws -> Bool
5657
) rethrows -> AsyncThrowingPrefixWhileSequence<Self> {
5758
return AsyncThrowingPrefixWhileSequence(self, predicate: predicate)
5859
}
@@ -139,3 +140,13 @@ extension AsyncThrowingPrefixWhileSequence: AsyncSequence {
139140
return Iterator(base.makeAsyncIterator(), predicate: predicate)
140141
}
141142
}
143+
144+
@available(SwiftStdlib 5.1, *)
145+
extension AsyncThrowingPrefixWhileSequence: @unchecked Sendable
146+
where Base: Sendable,
147+
Base.Element: Sendable { }
148+
149+
@available(SwiftStdlib 5.1, *)
150+
extension AsyncThrowingPrefixWhileSequence.Iterator: @unchecked Sendable
151+
where Base.AsyncIterator: Sendable,
152+
Base.Element: Sendable { }

test/Concurrency/Runtime/async_sequence.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ extension AsyncSequence where Element: Equatable {
986986
}
987987

988988
AsyncFlatMapSequenceTests.test("flat map throwing inner") {
989-
func buildSubSequence(
989+
@Sendable func buildSubSequence(
990990
_ start: Int
991991
) -> AsyncThrowingMapSequence<AsyncLazySequence<Range<Int>>, Int> {
992992
return (start..<4).async.map { (value: Int) throws -> Int in
@@ -1020,7 +1020,7 @@ extension AsyncSequence where Element: Equatable {
10201020
}
10211021

10221022
AsyncFlatMapSequenceTests.test("flat map throwing inner on throwing outer") {
1023-
func buildSubSequence(
1023+
@Sendable func buildSubSequence(
10241024
_ start: Int
10251025
) throws -> AsyncThrowingMapSequence<AsyncLazySequence<Range<Int>>, Int> {
10261026
return (start..<4).async.map { (value: Int) throws -> Int in

test/api-digester/stability-concurrency-abi.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@
5050
// UNSUPPORTED: swift_evolve
5151

5252
// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)
53+
Func AsyncSequence.compactMap(_:) is now with @preconcurrency
54+
Func AsyncSequence.drop(while:) is now with @preconcurrency
55+
Func AsyncSequence.filter(_:) is now with @preconcurrency
56+
Func AsyncSequence.flatMap(_:) is now with @preconcurrency
57+
Func AsyncSequence.map(_:) is now with @preconcurrency
58+
Func AsyncSequence.prefix(while:) is now with @preconcurrency
5359
Func MainActor.run(resultType:body:) has generic signature change from <T where T : Swift.Sendable> to <T>
5460
Func MainActor.run(resultType:body:) has mangled name changing from 'static Swift.MainActor.run<A where A: Swift.Sendable>(resultType: A.Type, body: @Swift.MainActor @Sendable () throws -> A) async throws -> A' to 'static Swift.MainActor.run<A>(resultType: A.Type, body: @Swift.MainActor @Sendable () throws -> A) async throws -> A'
5561
Struct CheckedContinuation has removed conformance to UnsafeSendable

0 commit comments

Comments
 (0)