I'm encountering an issue when working with metatype casting, associated types and would appreciate some clarification on how to resolve it.
Here's a simplified version of the code that demonstrates the issue:
protocol Container {
}
protocol SpecialisedContainer: Container {
associatedtype BaseContainer: Container
}
func repro<Root: Container>(root: Root.Type) {
if let specialised = root as? any SpecialisedContainer.Type {
let _ = specialised.BaseContainer.self # Type of expression is ambiguous without a type annotation
# let _: any Container.Type = specialised.BaseContainer.self # Type of expression is ambiguous without a type annotation
}
}
The error message Type of expression is ambiguous without a type annotation
occurs when attempting to access specialised.BaseContainer.self
.
I'm wondering how to properly access BaseContainer
metatype in this context. Is there a way to resolve this ambiguity?
The compiler is right because you cannot access a member type as a member of an existential metatype like that, but the diagnostic is not helpful because no type annotation will make this work, as you already observed. Do you mind filing a bug?
What you need to do is open the existential metatype. One way of doing that is to declare a local generic function with a named generic parameter, say T
, and then call the function with an existential as an argument. Inside the function, you can then refer to member types of T
directly:
protocol Container {
}
protocol SpecialisedContainer: Container {
associatedtype BaseContainer: Container
}
func repro<Root: Container>(root: Root.Type) {
if let specialised = root as? any SpecialisedContainer.Type {
func localFunc<T: SpecialisedContainer>(_: T.Type) {
let _ = T.BaseContainer.self
}
localFunc(specialised) // T is bound to the concrete type inside the existential
}
}
A possibly cleaner approach is to package up whatever it is you're doing with that member type into a protocol extension method; this is almost the same as the above, except here the generic parameter is the Self
parameter of the protocol:
extension SpecialisedContainer {
static func doStuff() {
print(Self.BaseContainer.self)
}
}
...
if let specialised = root as? any SpecialisedContainer.Type {
specialised.doStuff()
}
The complete behavior is described in SE-0352.
1 Like