I'm writing a library that wraps CoreData, and provides an async-friendly interface. One core building block I'm using to make my life easier is
extension NSManagedObjectContext {
func performAsync<T: Sendable, E: Error>(
_ body: sending @escaping () throws(E) -> T
) async throws(E) -> T {
do {
return try await withCheckedThrowingContinuation { continuation in
self.perform {
continuation.resume(with: Result { try body() })
}
}
} catch {
throw error as! E
}
}
}
In Xcode 16.4 (Swift 6.1) this compiles just fine. In Xcode 26.0.1 (Swift 6.2) I get the following error:
Reference to captured parameter 'body' in concurrently-executing code
Everywhere else I use sending is also giving me warnings about capturing non-sendable values. Is this supposed to work? What do I need to change?
(Assuming this can be made to work I have a follow-up question about priority inversion due to a runtime warning I got in Xcode 16.4. But one thing at a time.)
It's very hard to reason about these things, and the diagnostics don't help at all, but I think the problem here is that the compiler doesn't know what withCheckedThrowingContinuation or self.perform (or both) do with their closures — if they call them multiple times on multiple tasks, the constraint on sending would be broken.
The correct answer is that the language is missing a feature to designate a closure as once-callable, which would allow the compiler to reason about sending (and also non-Copyable) captures.
A practical (but non-performant) workaround until that SE appears would be to put the value into a Mutex as an optional, and take it out to call it.
I observed a similar behavior a while back but wasn't sure if it's by design or an omission. I have just filed #85231(Inconsistent behavior between sending parameter and captured variable). I think the issue I reported and the one in your code are probably the same issue, though my issue can be reproduced since Swift 6.0, the release in which sending was introduced, but you said that you ran into the issue only on 6.2.