- Notifications
You must be signed in to change notification settings - Fork10.5k
Description
Description
In Swift 6.1 (Xcode 16.3 and 16.4), placingasync let
bindings inside a do block causes a runtime crash—even when those values are properly awaited and the code is enclosed in do-catch. The crash does not occur in Xcode 16.2 or earlier.
This behavior seems to be related to how task deallocation is handled forasync let
when declared inside lexical scopes.
Thanks to@IrixVol for sharing the reproducible project:LetAsyncCrash
Reproduction
func crashingAsyncLetUsage()async{do{asyncletvalue1=fetchValue1()asyncletvalue2=fetchValue2()letresult1=tryawaitvalue1()letresult2=tryawaitvalue2()letmodel=buildModel(result1, result2)awaitconsume(model)}catch{print("Caught error:\(error)")}}func safeAsyncLetUsage()async{asyncletvalue1=fetchValue1()asyncletvalue2=fetchValue2()do{letresult1=tryawaitvalue1()letresult2=tryawaitvalue2()letmodel=buildModel(result1, result2)awaitconsume(model)}catch{print("Caught error:\(error)")}}
Stack dump
Thread 12 Crashed:0 libsystem_kernel.dylib 0x102628874 __pthread_kill + 81 libsystem_pthread.dylib 0x1015be2ec pthread_kill + 2642 libsystem_c.dylib 0x180170568 __abort + 1123 libsystem_c.dylib 0x1801704f8 abort + 1164 libswift_Concurrency.dylib 0x2493a346c swift::swift_Concurrency_fatalErrorv(unsigned int, char const*, char*) + 285 libswift_Concurrency.dylib 0x2493a3488 swift::swift_Concurrency_fatalError(unsigned int, char const*, ...) + 286 libswift_Concurrency.dylib 0x2493a6718 swift_task_dealloc + 1247 libswift_Concurrency.dylib 0x2493a27a4 asyncLet_finish_after_task_completion(swift::AsyncContext*, swift::AsyncLet*, void (swift::AsyncContext* swift_async_context) swiftasynccall*, swift::AsyncContext*, void*) + 728 Dev.debug.dylib 0x113f12529 implicit closure #6 in implicit closure #5 in implicit closure #4 in FooViewModel.sendAction<A>(_:) + 19 Dev.debug.dylib 0x113f1cddd partial apply for implicit closure #6 in implicit closure #5 in implicit closure #4 in FooViewModel.sendAction<A>(_:) + 110 Dev.debug.dylib 0x113ec8d6d thunk for @escaping @callee_guaranteed @async () -> () + 111 Dev.debug.dylib 0x113f1ccf9 partial apply for thunk for @escaping @callee_guaranteed @async () -> () + 112 Dev.debug.dylib 0x113f0f959 FooViewModel.sendAction<A>(_:) + 1 (FooViewModel.swift:)13 Dev.debug.dylib 0x113f1b5c1 protocol witness for ActionCaster.sendAction<A>(_:) in conformance FooViewModel + 114 Dev.debug.dylib 0x113f0b10d closure #1 in FooViewModel.viewWillAppear(_:) + 1 (FooViewModel.swift:)15 Dev.debug.dylib 0x113f0b2c5 partial apply for closure #1 in FooViewController.viewWillAppear(_:) + 116 Dev.debug.dylib 0x113e74cbd thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) + 117 Dev.debug.dylib 0x113e74e29 partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) + 118 libswift_Concurrency.dylib 0x2493a5829 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1
Expected behavior
async let
declared inside a do block should behave the same as when declared outside it, as long as all values are awaited. It should not result in a crash.
Environment
swift-driver version: 1.120.5 Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)
Target: arm64-apple-macosx15.0
Additional information
This may be a regression introduced by recent changes to task deallocation logic in Swift 6.1. The crash appears related to how async let scopes are finalized when declared in lexical blocks like do {}.