Generic parameters are not visible to qualified lookup (foo.bar); they’re not members, but type alias declarations are. Where does the type alias come from?
Here’s a bit of terminology. The concrete implementation of an associated type is called a type witness. The type witness can be an explicit type alias or nested type:
protocol P {
associatedtype A
func f() -> A
}
struct S1 : P {
typealias A = Int
func f() -> Int {…}
}
struct S2 : P {
struct A {}
func f() -> A {…}
}
There is a language feature called associated type inference which allows you to omit the type alias in some cases where it can be deduced. For example:
struct S3 : P {
func f() -> Int {…}
}
A generic parameter that has the same name as the associated type is then picked up as a special case:
struct S4<A> : P {
func f() -> A {…}
}
In all cases associated type inference synthesizes a type alias declaration, as if you had declared the type witness explicitly.
So S4
is equivalent to this:
struct S4<A> : P {
typealias A = A
func f() -> A {…}
}
Which is why you can refer to S4<String>.A
, which resolves to just String
.
Note that from inside the body of S4, A refers to the generic parameter, while Self.A is the type alias. This can cause the following slightly confusing situation:
struct S5<A> : P {
// synthesized: typealias A = Int
func f() -> Int {…}
}
Now S5<String>.A
resolves to Int
and not String
. Changing this would be a source break, but perhaps it could be banned under a future language mode.