this is curious... your code looks like it should be okay to me, but i think that runtime error is produced based on a reference count check, so it's conceivable that there is something happening outside your direct control that results in this behavior. i would encourage you to file a bug since i don't think the runtime error is expected in this case.
i tested the following variant[1] a bit and it did not seem to hit the runtime error, though not understanding what the cause is means there's no guarantee it actually fixes anything.
import Dispatch
func run<R: Sendable>(_ block: @Sendable () -> R) async -> R {
typealias Closure = @Sendable () -> R
return await withoutActuallyEscaping(block) { (localBlock: @escaping Closure) in
// explicit consume so we can try to control the lifetime more precisely
nonisolated(unsafe) var box: Closure? = consume localBlock
return await withCheckedContinuation { continuation in
DispatchQueue.global().async { @Sendable in
// move the closure binding out of the wrapper & invoke it.
// hopefully there are no extra references to it now?
let value = box.take()!()
continuation.resume(returning: value)
}
}
}
}
await withTaskGroup { group in
for i in 0 ..< 500 {
group.addTask {
await run {
print(i)
}
}
}
await group.waitForAll()
}
edit: i also found this PR that seemed like it might be somewhat related. unfortunately the same issue appears to still occur in the 6.2 compiler, so that change presumably didn't fix whatever is causing this.
has some extra concurrency annotations to compile with
-swift-version 6↩︎