This compiles with no warnings… which seems to imply that AsyncStream<>.AsyncIterator.TypeisSendable… 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?
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 notSendable… but the type itself is Sendable? Is that correct?
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).
It looks like many concrete types adopting AsyncSequence conditionally adopt Sendable when the Element is Sendable:
The AsyncStream is Sendable when the Element is Sendable:
But the AsyncStream.Iterator explicitly tells us it is notSendable:
I see no place where AsyncStream.Iterator is then marked as Sendable when Element is Sendable. I also see no place where AsyncStream.Iterator is ever made SendableMetatype.
Does anyone understand how AsyncStream might be preventing this warning from displaying? Is there some kind of voodoo sorcery happening somewhere and I'm not understanding?
To put it another way… this code:
protocol P {
associatedtype S: AsyncSequence
where S.AsyncIterator: SendableMetatype
}
struct S<Element>: P {
typealias S = AsyncStream<Element>
}
Is not giving me errors… I don't understand why there are no errors… and I don't understand to what extent I can assume there would not be errors at some point in the future if this "implicit SendableMetatype" turns out to actually be a bug instead of a feature and I actually can't assume that AsyncStream.Iterator is a SendableMetatype.
Our project has 252 new warnings about non-Sendable metatypes when building in Xcode 26b2. Some of them are AsyncSequence+AsyncIterator-related, but far from all of them.
func sendableSequence<S: AsyncSequence & Sendable>(_ s: S) throws {
Task.detached {
for try await i in s {
print(i)
}
}
}
seems to build without warnings from Xcode_26_beta_6.
But the nonSendableSequence test:
func nonSendableSequence<S: AsyncSequence>(_ s: S) throws {
Task.detached {
for try await i in s { // expected-warning{{capture of non-Sendable type 'S.AsyncIterator.Type' in an isolated closure}}
// expected-warning@-1{{capture of non-Sendable type 'S.Type' in an isolated closure}}
print(i)
}
}
}
is now an build failure error from Xcode_26_beta_6:
Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
All base AsyncSequence (and algorithm AsyncSequences too) should all be fine to just ignore this warning for now - you can force that by using a nonisolated(unsafe) let seq = s and then passing the seq variable in instead.
@Douglas_Gregor what would it take to affix the SendableMetatype protocol to both AsyncSequence and AsyncIteratorProtocol? Is that something we could consider? As Swift Concurrency has changed from the initial implementation the meaning and role of AsyncSequence has changed slightly such that looking at it today may have actually required the type to be Sendable. (if not possible ~Copyable but that is a different story).