It's not dynamic dispatch. It's a much simpler* shadowing. Let's jump onto your new code. P
has only one definition of foo
, the protocol requirement, while A
has three:
- The unconstrained one (
foo
), - The one constrained to
T == Int
(bar
), and - The one used for protocol conformance.
When you do A<Int>.foo
, the compiler chooses the most specific one among the three, which is (bar
). In fact, if the compiler doesn't know that T == Int
, it can only choose among the first and the third ones, both of which are foo
.
func x<T>(_: A<T>.Type) {
A<T>.foo()
}
x(A<Int>.self) // foo
Swift needs to be able to generate unspecialized function correctly. It needs to be able to generate a binary for my x(_:)
function with unknown T
, i.e. unspecialized x
, which is usable by any x<T>
, including x<Int>
. That's why, inside x
, the compiler can't use the second definition of foo
.
As a rule of thumb, if you didn't write override
, they are different, unrelated functions.
Covariance plays very little role here (A<String>
and A<Int>
are unrelated regardless of variance support). There are a lot of constraints going into the design around this area (not to mention there are actual bugs riddled around). Unfortunately, it leads to a somewhat complex behaviour.
That's a different one.
* Well, I said simpler, but the precise shadowing rule is elusive to even the most veteran. We did explore a lot of corners a while back, though I think we ended up running around in circle.