Sendable loophole with `where Self: Sendable` extension?

I stumbled over something in my code that compiles just fine in Swift 6, but I think it really shouldn't.

Here is a stripped down version:

protocol P {
    func doIt()
}

protocol F: AnyObject {
    var x: Int { get set }
}

class NonSendable: F {
    var x = 0
}

extension NonSendable: P {} // is this a bug?

extension F where Self: Sendable {
    func doIt() {
        Task { print(self.x) }
        x += 1
    }
}

func test() {
    let f: P = NonSendable()
    f.doIt()
}

I think the where Self: Sendable combined with the separate protocol conformance extension is tripping up Swift.

Am I missing something super clever that the compiler knows, or is this a bug?

Could you state what part you think is wrong?

Edit: Ah, the ability to conform without providing doIt()? What happens when you run the code?

well, in doIt I can pass around self as sendable, but NonSendable clearly isn't. and I can call NonSendable.doIt just fine.

this can't be right.. right?

Definitely doesn't seem like it. You could try testing on a recent development toolchain and see if it's a bug that's been fixed, otherwise you may want to report it, if one of the Swift devs doesn't chime in in the meantime.

1 Like
1 Like

It looks like a bug in availability checking. Here is a minimal test case (note that it doesn't involve Sendable):

public protocol M {}

public protocol P {
    func doIt()
}

public class NonSendable: P {
}

@available(*, unavailable)
extension NonSendable: M {}

extension P where Self: M {
    public func doIt() {}
}

The choice of the doIt() witness depends on the unavailable conformance to M, but we just miss this case in availability checking. If you comment out the extension to M entirely, we diagnose:

14 |     public func doIt() {}
   |                 `- note: candidate would match if 'NonSendable' conformed to 'M'

Your code doesn't declare a Sendable conformance though, but with Sendable specifically, even if your type doesn't conform to it explicitly, the compiler will essentially synthesize the equivalent of that unavailable conformance on the extension. (That's what allows the -swift-version 5 mode to emit warnings about Sendable.)

Do you mind filing an issue?

4 Likes