vanvoorden
(Rick van Voorden)
June 10, 2025, 2:28am
1
I'm seeing a confusing concurrency warning from AsyncSequence
when building from Xcode 26 and Swift 6.2.
This builds with no warnings from 6.1:
func f1<Element: Sendable>(_ s: AsyncStream<Element>) throws {
Task {
for try await i in s {
print(i)
}
}
}
func f2<S: AsyncSequence & Sendable>(_ s: S) throws {
Task {
for try await i in s {
print(i)
}
}
}
But f2
is a warning from 6.2:
Capture of non-sendable type 'S.AsyncIterator.Type' in an isolated closure
This seems to be a workaround:
func f3<S: AsyncSequence & Sendable>(_ s: S) async throws {
for try await i in s {
print(i)
}
}
func f4<S: AsyncSequence & Sendable>(_ s: S) throws {
Task {
try await f3(s)
}
}
But I'm not sure I understand exactly why that workaround is needed and what could be wrong about the original code.
And then this makes this more confusing:
func f5<T: Sendable>(_: T.Type) {
}
func f6<Element: Sendable>(_: Element.Type) {
f5(AsyncStream<Element>.AsyncIterator.Type.self)
}
This compiles with no warnings … which seems to imply that AsyncStream<>.AsyncIterator.Type
is Sendable
… but when I actually check the source code of AsyncStream<>.AsyncIterator.Type
I can't actually see or confirm where that sendability is declared or enforced.
Any more ideas where else to look for clues? Could that Capture of non-sendable type
warning be a false positive that might be fixed in a new 6.2 beta?
3 Likes
vanvoorden
(Rick van Voorden)
June 10, 2025, 4:44am
2
And then there's this:
protocol P {
associatedtype S: AsyncSequence, Sendable where S.AsyncIterator: Sendable
}
struct S<Element: Sendable>: P {
typealias S = AsyncStream<Element>
// Type 'AsyncStream<Element>.AsyncIterator' (aka 'AsyncStream<Element>.Iterator') does not conform to the 'Sendable' protocol
}
Which seems to imply that instances of AsyncStream<>.AsyncIterator
are not Sendable
… but the type itself is Sendable
? Is that correct?
vanvoorden
(Rick van Voorden)
June 10, 2025, 5:02am
3
Ahh… it actually looks like SendableMetatype
might be what I needed:
# Global-actor isolated conformances
* Proposal: [SE-0470](0470-isolated-conformances.md)
* Authors: [Doug Gregor](https://github.com/DougGregor)
* Review Manager: [Xiaodi Wu](https://github.com/xwu)
* Status: **Implemented (Swift 6.2)**
* Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md)
* Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`.
* Upcoming Feature Flag: `InferIsolatedConformances`
* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189))
## Introduction
Types isolated to a global actor (such as `@MainActor`) are useful for representing data that can only ever be used from a single concurrency context. They occur both in single-threaded programs where all code is expected to run on the main actor as well as larger applications where interaction with the UI occurs through the main actor. Unfortunately, such types are unable to conform to most protocols due to isolation mismatches:
```swift
@MainActor
class MyModelType: Equatable {
var name: String
This file has been truncated. show original
This code all compiles from 6.2 without warnings:
func f1<Element: Sendable>(_ s: AsyncStream<Element>) throws {
Task {
for try await i in s {
print(i)
}
}
}
func f2<S: AsyncSequence & Sendable>(_ s: S) throws where S.AsyncIterator: SendableMetatype {
Task {
for try await i in s {
print(i)
}
}
}
func f3<S: AsyncSequence & Sendable>(_ s: S) async throws {
for try await i in s {
print(i)
}
}
func f4<S: AsyncSequence & Sendable>(_ s: S) throws {
Task {
try await f3(s)
}
}
func f5<T: Sendable>(_: T.Type) {
}
func f6<Element: Sendable>(_: Element.Type) {
f5(AsyncStream<Element>.AsyncIterator.Type.self)
}
func f7<S: AsyncSequence & Sendable>(_ t: S.Type) where S.AsyncIterator: SendableMetatype {
f5(S.AsyncIterator.Type.self)
}
protocol P {
associatedtype S: AsyncSequence, Sendable where S.AsyncIterator: SendableMetatype
}
struct S<Element: Sendable>: P {
typealias S = AsyncStream<Element>
}
1 Like
vanvoorden
(Rick van Voorden)
June 10, 2025, 5:47am
5
This seems to work:
protocol MyProtocol1 { init() }
struct MyStruct<T> {
var x: @Sendable () -> T.Type
}
func f<T: MyProtocol1 & SendableMetatype>(_ type: T.Type) {
MyStruct { T.self }
}
protocol MyProtocol2 {
associatedtype Value
}
func f<T: MyProtocol2 & SendableMetatype>(
_ type: T.Type,
closure: @escaping @Sendable (T.Value.Type) -> Void
) {
Task {
closure(T.Value.self)
}
}
AFAIK SendableMetatype
requires the 6.2 toolchain… so if you need that code to also compile for 6.1 then I guess you need some conditonal compilation.
1 Like
I'm not sure those warnings are to be expected until one opts in for isolated conformances with the experimental features IsolatedConformances
and/or StrictSendableMetatypes
from SE-0470 (which I did not).
3 Likes
vanvoorden
(Rick van Voorden)
June 10, 2025, 6:09am
7
Hmm… it looks like this is expected behavior in 6.2. I guess maybe the flags were for enabling the feature on main
before the proposal was accepted.
main
← DougGregor:se-0470-implemented
opened 04:59PM - 18 Apr 25 UTC