Type checking inconsistency with generic metatypes

You can see what's happening this way:

protocol MyProto {}
struct S: MyProto {}

let instance: MyProto = S()
let cls: MyProto.Type = S.self
let proto = MyProto.self

func printType<T>(_ value: T) {
  print(value, T.self)
}

printType(instance) // S() MyProto
printType(cls)      // S MyProto.Type
printType(proto)    // MyProto MyProto.Protocol

That is, MyProto.Type and MyProto.Protocol are different types. One of them is the type of "types that conform to the protocol"; the other is the type of "the protocol itself". (In the language of Improving the UI of generics, the former is (any MyProto).Type.)

Okay, they're different, but why? (Since this is annoying.) The answer is because Objective-C has a function to dynamically check protocol conformances that looks something like this:

func objc_conformsToProtocol(_ cls: AnyClass, _ proto: AnyProtocol)

(Objective-C actually just calls this type "Protocol", but I've named it "AnyProtocol" to go with "AnyClass".)

The existence of this function means that protocols themselves have to have a type, and that type is distinct from "types that conform to the protocol". That's the design for any sort of reflection that acts on protocols themselves, as opposed to the types that conform to them.

1 Like