EXC_BAD_ACCESS when calling sending API from Objective C

I have following code in Swift:

@objc
public class SomeObject: NSObject {
    public override init() {
        super.init()
    }

    public func someMethod() {
        print("object invoked")
    }
}

@objc
public class ObjCWrapper: NSObject {
    @objc
    public func sendingMethod(_ val: sending Any) {
        // do nothing
    }
}

I am creating SomeObject and passing it to ObjCWrapper.sendingMethod in Objective C:

@implementation ObjCCode

- (void)startScenario {
    SomeObject *obj = [[SomeObject alloc] init];

    ObjCWrapper *wrapper = [[ObjCWrapper alloc] init];
    NSLog(@"SomeObject address: %p", obj);
    [wrapper sendingMethod:obj];

    [NSTimer scheduledTimerWithTimeInterval:5.0
                                    repeats:YES
                                      block:^(NSTimer * _Nonnull timer) {
        NSLog(@"SomeObject address: %p", obj);
    }];
}

@end

When I invoke ObjCCode().startScenario() from Swift I get:

Task 1: EXC_BAD_ACCESS (code=1, address=0x528c7a8e0)

It seems like in case sending in Swift, ARC counter is not updated when object is passed from Objective C. Similar logic in Swift doesn't cause any crash, e.g. Implementing startScenario in Swift doesn't cause any crash:

func startScenario() {
    let obj = SomeObject()

    let wrapper = ObjCWrapper()
    NSLog("SomeObject address: %p", obj)
    wrapper.sendingMethod(obj)

    Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in
        NSLog("SomeObject address: %p", obj)
    }
}

Is this a bug?

Can you share a complete backtrace for the crashing thread/task?

* thread #9, name = 'View.task @ ConcurrencyBug/ContentView.swift:16 (Task 2)', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x13ff2ee772f0)
    frame #0: 0x000000018288391c libobjc.A.dylib`objc_retain + 16
  * frame #1: 0x00000001047c40c0 ConcurrencyBug.debug.dylib`-[ObjCCode startScenario](self=0x000000088f1fda90, _cmd="startScenario") at ObjCCode.m:24:45
    frame #2: 0x00000001047c5214 ConcurrencyBug.debug.dylib`closure #1() in closure #1 in ContentView.body.getter at ContentView.swift:29:28
    frame #3: 0x00000001047cc404 ConcurrencyBug.debug.dylib`closure #1() in closure #1 in ContentView.body.getter at <compiler-generated>:0
    frame #4: 0x00000001da6ac82c SwiftUI`partial apply for closure #1 in closure #1 in closure #1 in AppDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:)
    frame #5: 0x00000001da6ac82c SwiftUI`partial apply for closure #1 in closure #1 in closure #1 in AppDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:)
    frame #6: 0x00000001da8c2774 SwiftUI`(1) await resume partial function for generic not re-abstracted specialization <Swift.Optional<()>> of reabstraction thunk helper <τ_0_0, τ_0_1 where τ_0_0: Swift.Sendable, τ_0_1 == Swift.Never> from @escaping @isolated(any) @callee_guaranteed @async () -> (@out τ_0_0) to @escaping @callee_guaranteed @async () -> (@out τ_0_0, @error @owned Swift.Error)
    frame #7: 0x00000001da6ac82c SwiftUI`partial apply for closure #1 in closure #1 in closure #1 in AppDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:)

@grynspan output of bt :backhand_index_pointing_up:

I realize that this isn't what you were asking about. However, I'm fairly sure there's another issue here.

The non-Sendable obj value is passed as a sending parameter to sendingMethod, but is then captured by a closure (and an @Sendable one, no less). This should be invalid. I think that Timer's preconcurrency status is messing things up here, but I'm not certain.

Created bug report: EXC_BAD_ACCESS when calling sending API from Objective C · Issue #90531 · swiftlang/swift · GitHub