This problem is further complicated by the fact that Swift does not support generic type parameters that conform to other type parameters (unless the latter is explicitly defined as a class type – it is impossible to define type parameters as protocol types).
Thus, it is impossible to fix the above problem by testing the concrete type:
protocol A {}
protocol B: A {}
class C: B {}
func f<P, I: P>(_ p: P.Type, _ i: @autoclosure () -> I) { // Type 'I' constrained to non-protocol, non-class type 'P'
print("1")
}
func f<P, I: P>(_ p: P.Type, _ i: @autoclosure () -> I) where I: A { // Type 'I' constrained to non-protocol, non-class type 'P'
print("2")
}
f(B.self, C())
f(C.self, C())
IMO Swift should allow this as well, but yield a compiler error on the call site if the user attempts to pass an incompatible concrete type to the parameter.
B.self is the protocol metatype value, also now spelt (any B).self.
The existential type any B does not conform to any protocol, not even B. You can see this clearly diagnosed if you create a similar function with a constraint where P: B that doesn’t have another overload of the same name.
This is not a bug: in the general case, an existential type cannot conform to its corresponding protocol. For example, any FixedWidthInteger does not, semantically, fulfill the semantics of a fixed-width integer type.
It would be nice if there was a way to declare P to be a protocol type; I was hoping the Swift compiler could infer it, but it appears that it cannot:
// cannot use 'Protocol' with non-protocol type 'P'
// Type 'I' constrained to non-protocol, non-class type 'P'
func f<P, I: P>(_ p: P.Protocol, _ i: @autoclosure () -> I) {