Existentials of AnyObject are not class types

Would love to understand better why, in:

protocol P: AnyObject {}
class C: P {}

struct S<O: AnyObject>: Identifiable {
    let o: O
    var id: ObjectIdentifier { ObjectIdentifier(self.o) }

let c: C = C()
let p: P = c

S(o: c).id // ObjectIdentifier(0x00006000035140a0)
S(o: p).id // Generic struct 'S' requires that 'any P' be a class type
S(o: p as AnyObject).id // ObjectIdentifier(0x00006000035140a0)

any P can not be considered a class type, even though P explicitly declares a requirement that its instance must be a reference type. Furthermore, clearly this is supported as we can override the limitation with a simple as AnyObject.

Is there any reason as to why an existential should /not/ be considered a reference type when its protocol declares a class type requirement?

This obstacle leads me to make the wrapper type unsafe, since I need to drop the O: AnyObject requirement in S, which is there to ensure that the wrapper is only used by reference types for which ObjectIdentifier is stable.

Does anyone have a suggestion for an alternative approach to wrapping existentials for reference types?

What's even weirder to me is that this works just fine if the protocol is marked @objc. Perhaps it's because objects conforming to @objc protocols are guaranteed to be either NSObject or NSProxy, but the asymmetry still seems weird to me.

Part of the AnyObject constraint's is that the type be representable as a single refcounted pointer. Swift protocol existentials don't satisfy this constraint because they also carry around an extra word for the dispatch table for every protocol the underlying type conforms to. In Swift 5.7, I think this should work:

(S(o: p) as any Identifiable).id`

This will create an S<O> using the dynamic type inside of p, and then pass it off as a value of type any Identifiable.


Thanks, @Joe_Groff.

That is an interesting glimpse into the technicals.

From a usage perspective, though, I'm primarily interested in expressing a requirement that my S wrapper's o should be a reference type value (or at least one with a stable object identifier, so my implementation of id is reliable).

I'm slightly at a loss for how casting S as any Identifiable before accessing its id contributes to the solution, since the problem happens before S is constructed. Could you help indicate what problem is solved by passing S off as an any Identifiable? The compiler will still not let me require that p should be a class type, your line of code has the same compiler error as the one in the OP.

Is the conclusion that expressing this requirement is not currently technically possible?