Skip to content

Crash in Swift 6.1 when using async let inside a do {} block (Xcode 16.3+) #81771

Open
@brightspread

Description

@brightspread

Description

In Swift 6.1 (Xcode 16.3 and 16.4), placing async 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 for async let when declared inside lexical scopes.

Thanks to @IrixVol for sharing the reproducible project: LetAsyncCrash

Reproduction

func crashingAsyncLetUsage() async {
  do {
    async let value1 = fetchValue1()
    async let value2 = fetchValue2()

    let result1 = try await value1()
    let result2 = try await value2()

    let model = buildModel(result1, result2)
    await consume(model)
  } catch {
    print("Caught error: \(error)")
  }
}

func safeAsyncLetUsage() async {
  async let value1 = fetchValue1()
  async let value2 = fetchValue2()

  do {
    let result1 = try await value1()
    let result2 = try await value2()

    let model = buildModel(result1, result2)
    await consume(model)
  } catch {
    print("Caught error: \(error)")
  }
}

Stack dump

Thread 12 Crashed:
0   libsystem_kernel.dylib        	       0x102628874 __pthread_kill + 8
1   libsystem_pthread.dylib       	       0x1015be2ec pthread_kill + 264
2   libsystem_c.dylib             	       0x180170568 __abort + 112
3   libsystem_c.dylib             	       0x1801704f8 abort + 116
4   libswift_Concurrency.dylib    	       0x2493a346c swift::swift_Concurrency_fatalErrorv(unsigned int, char const*, char*) + 28
5   libswift_Concurrency.dylib    	       0x2493a3488 swift::swift_Concurrency_fatalError(unsigned int, char const*, ...) + 28
6   libswift_Concurrency.dylib    	       0x2493a6718 swift_task_dealloc + 124
7   libswift_Concurrency.dylib    	       0x2493a27a4 asyncLet_finish_after_task_completion(swift::AsyncContext*, swift::AsyncLet*, void (swift::AsyncContext* swift_async_context) swiftasynccall*, swift::AsyncContext*, void*) + 72
8   Dev.debug.dylib               	       0x113f12529 implicit closure #6 in implicit closure #5 in implicit closure #4 in FooViewModel.sendAction<A>(_:) + 1
9   Dev.debug.dylib               	       0x113f1cddd partial apply for implicit closure #6 in implicit closure #5 in implicit closure #4 in FooViewModel.sendAction<A>(_:) + 1
10  Dev.debug.dylib               	       0x113ec8d6d thunk for @escaping @callee_guaranteed @async () -> () + 1
11  Dev.debug.dylib               	       0x113f1ccf9 partial apply for thunk for @escaping @callee_guaranteed @async () -> () + 1
12  Dev.debug.dylib               	       0x113f0f959 FooViewModel.sendAction<A>(_:) + 1 (FooViewModel.swift:)
13  Dev.debug.dylib               	       0x113f1b5c1 protocol witness for ActionCaster.sendAction<A>(_:) in conformance FooViewModel + 1
14  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(_:) + 1
16  Dev.debug.dylib               	       0x113e74cbd thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) + 1
17  Dev.debug.dylib               	       0x113e74e29 partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) + 1
18  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 {}.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.crashBug: A crash, i.e., an abnormal termination of softwaretriage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions