Methods not declared in the protocol itself, but just in extensions, are called statically. The compiler chooses the most specific known overload.
Inside logByProtocol, all that can be known is that p conforms to MyProtocol, so the compiler dispatches to the method in the simple extension. In the global scope, the compiler knows that myGenericClassNumber is of type MyGenericClass<Int>, and since that satisfies the stricter constraints, it dispatches to the constrained extension method.
To get dynamic dispatch like you seem to want, declare func log() as a protocol requirement:
Then the method will be looked up at run‐time instead, and it will dispatch to whichever method the instance itself wants to use. The trade‐off is that this look‐up takes extra work during execution.
As a general rule:
if you want something to be customizable by conformers, declare it in the protocol itself.
The principle is the same though. When MyGenericClass is choosing (at compile time) what method to use for it’s conformance (the one it will report at run time), it only has the simple one to choose from. MyGenericClass, in general, does not satisfy the more specific constraints.
So if Swift query the real type information in runtime. p.log() can print the expected string. But due to the implementation of Swift LLVM, logByProtocol choose the static dispatch? So p.log only can choose implementation form its original protocol extension instead of the " extension MyProtocol where Self==MyGenericClass"? Would it be better if Swift Query the real type instead of MyProtocol?
This log() will always* be dynamically dispatched:
protocol MyProtocol {
func log()
}
Consider this use of log():
func logByProtocol(_ p: MyProtocol) {
p.log()
}
If log() is a static method, the unconstrained variant will always be called, because at compile time, all that is known is that the instance conforms to MyProtocol. Adding constrained overloads won’t make a difference. Even if the type declares such a method itself it won’t make a difference.
On the other hand, If log() is a dynamic method, the method of the real type will be called. If the type did not declare one of its own, the compiler will have given it a specialized version of the best default implementation to treat as its own. This is where you run into your surprise:
MyGenericClass<T> needs to be compiled with a single log() method which works for any T. It is only allowed to select a method which is valid for every possible T. It cannot choose the variant constrained to where Self==MyGenericClass<Int> because that is not valid for all possible T.
*The compiler is free to take shortcuts if it can prove the result would be the same.