SE-0353 introduced constrained existential types but I couldn't find or understand the reasons of the behavior that I have encountered.
Example
// MARK: - Definitions
protocol Base<T> {
associatedtype T
func make() -> T
}
protocol InheritanceExample: Base<Int> {
}
typealias TypeAliasExample = Base<Int>
struct IntBase: InheritanceExample {
func make() -> Int {
2
}
}
// MARK: - Input
func inheritanceAny() -> any InheritanceExample {
IntBase()
}
func inheritanceSome() -> some InheritanceExample {
IntBase()
}
func typeAliasAny() -> any TypeAliasExample {
IntBase()
}
func typeAliasSome() -> some TypeAliasExample {
IntBase()
}
func baseAny() -> any Base<Int> {
IntBase()
}
func baseSome() -> some Base<Int> {
IntBase()
}
// MARK: - Receivers
func acceptInstanceAny(_ base: any Base<Int>) -> Int {
base.make()
}
func acceptInstanceSome(_ base: some Base<Int>) -> Int {
base.make()
}
func acceptClosureAny(_ base: () -> any Base<Int>) -> Int {
base().make()
}
func acceptClosureSome(_ base: () -> some Base<Int>) -> Int {
base().make()
}
// MARK: - Usage
acceptInstanceAny(inheritanceAny()) // (1) fails with Type of expression is ambiguous without more context
acceptInstanceAny(inheritanceSome())
acceptInstanceAny(typeAliasAny())
acceptInstanceAny(typeAliasSome())
acceptInstanceAny(baseAny())
acceptInstanceAny(baseSome())
acceptInstanceSome(inheritanceAny())
acceptInstanceSome(inheritanceSome())
acceptInstanceSome(typeAliasAny())
acceptInstanceSome(typeAliasSome())
acceptInstanceSome(baseAny())
acceptInstanceSome(baseSome())
acceptClosureAny(inheritanceAny) // (2) fails with Cannot convert value of type '() -> any InheritanceExample' to expected argument type '() -> any Base<Int>'
acceptClosureAny(inheritanceSome)
acceptClosureAny(typeAliasAny)
acceptClosureAny(typeAliasSome)
acceptClosureAny(baseAny)
acceptClosureAny(baseSome)
acceptClosureSome(inheritanceAny) // (3) fails with Type 'any InheritanceExample' cannot conform to 'Base'
acceptClosureSome(inheritanceSome)
acceptClosureSome(typeAliasAny) // (4) fails with Type 'any TypeAliasExample' (aka 'any Base<Int>') cannot conform to 'Base'
acceptClosureSome(typeAliasSome)
acceptClosureSome(baseAny) // (5) fails with Type 'any Base<Int>' cannot conform to 'Base'
acceptClosureSome(baseSome)
Question #1
I guess (1)
and (2)
fail because any InheritanceExample
existential type is not the same existential type as any Base<Int>
.
Although, it seems like the compiler could infer that the Base
protocol is inherited by InheritanceExample
protocol and the constrained associated type is the same.
After all, that's the relevant part.
Could it be fixed, or is that nontrivial?
Question #2
I think (3)
, (4)
, and (5)
fail because
existential types do not conform to their protocols
Existentials and View - #14 by lukasa
As the error says: Type (...) cannot conform to 'Base'
And some
works like a generic parameter so it requires a concrete type conforming to a protocol, but if that's the case, why these work?
acceptInstanceSome(inheritanceAny())
acceptInstanceSome(typeAliasAny())
acceptInstanceSome(baseAny())
Is that a special case where existentials do conform? If so it should work for closures as the return type is covariant.
Is this expected?