Skip to content

Swift 6.1 runtime crash when calling @objc async protocol method in target with mixed Swift 5 and Swift 6 dependencies #81846

Open
@yevgold

Description

@yevgold

Description

We are hitting an EXC_BAD_ACCESS runtime crash in Swift.CheckedContinuation.resume(returning: __owned τ_0_0) -> () () when calling an @objc async protocol method. This occurs when using the Swift 6.1 compiler and does not happen with Swift 6.0. It's reproducible only when there are both Swift 5 and Swift 6 language mode dependencies that include such protocols and the methods have the same return type.

Reproduction

Repro steps:
Build and run the attached sample app with Xcode 16.3

RESULT: app crashes

We narrowed down the repro to a small project with 3 targets:

Static library compiled with Swift 5 language mode:

@objc
public protocol P5 {
  // This method's return type must match the return type of doSomethingElse() in P6.
  // Otherwise, no crash.
  func doSomething() async
}

public class Impl5: P5 {
  public init() {}

  public func doSomething() async {}
}

public class Swift5Class {
  public init() {}

  public func foo() async throws {
    let p: P5 = Impl5()
    await p.doSomething()
  }
}

Similar static library but compiled with Swift 6 language mode:

@objc
public protocol P6 {
  // This method's return type must match the return type of doSomething() in P5.
  // Otherwise, no crash.
  func doSomethingElse() async
}

public class Impl6: P6 {
  public init() {}

  public func doSomethingElse() async {}
}

public class Swift6Class {
  public init() {}

  public func bar() async throws {
    let p: P6 = Impl6()
    await p.doSomethingElse()
  }
}

App target that links in the above 2 libraries:

import S5
import S6

@main
struct SampleApp {

  static func main() async throws {
    print("Starting call to Swift 5 class")
    let s5 = Swift5Class()
    try? await s5.foo()

    // Crash before reaching next line.
    print("Finished call to Swift 5 class")

    // No crash if the next line is commented out.
    let _ = Swift6Class()
  }
}

Observations:

  • no crash when the same code is built with Swift 6.0
  • annotating the protocols as Sendable and making the classes final did not eliminate the crash
  • the language mode of the app target does not matter. It crashes whether it's built with Swift 5 or Swift 6
  • no crash if the 2 libraries are both built with the same language mode
  • no crash if we use the implementation directly instead of calling through the protocol, ie: await Impl().doSomething() // no crash
  • the location of the protocol definitions and class implementations does not matter. The crash still happens even if there is a single protocol and impl defined in a shared library. What matters is that the calls to the protocol methods are in 2 different targets with different language modes.

Stack dump

Thread 3 Queue : com.apple.root.default-qos.cooperative (concurrent)

#0	0x00000001959b6c90 in swift_retain ()
#1	0x000000024a2d3074 in Swift.CheckedContinuation.resume(returning: __owned τ_0_0) -> () ()
#2	0x0000000104d5f958 in _resumeCheckedContinuation<()>(_:_:) ()
#3	0x0000000104d5f828 in @objc completion handler block implementation for @escaping @callee_unowned @convention(block) () -> () with result type () ()
#4	0x0000000104d60530 in @objc closure #1 in Impl5.doSomething() ()
#5	0x0000000104d60638 in partial apply for @objc closure #1 in Impl5.doSomething() ()
#6	0x0000000104d5fab4 in thunk for @escaping @callee_guaranteed @Sendable @async () -> () ()
#7	0x0000000104d5fc10 in partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> () ()
#8	0x0000000104d5fcec in thunk for @escaping @isolated(any) @callee_guaranteed @async () -> () ()
#9	0x0000000104d5fe50 in partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> () ()
#10	0x0000000104d601a0 in specialized thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
#11	0x0000000104d602e8 in partial apply for specialized thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()

Expected behavior

No crash should occur

Environment

swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3)
Target: arm64-apple-macosx15.0

Additional information

Swift forums post: https://forums.swift.org/t/xcode-16-3-unsafecontinuation-resume-exc-bad-access-crash/79821

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