Why does Swift favor protocol extension implementation over witness implementation?

Hello,

Does anyone know the rationale behind Swift favoring static dispatch in this context (attached screenshot), instead of considering witnesses within the class hierarchy?

Thanks in advance for your answer.

This is one of the oldest bugs in the language: [SR-103] Protocol Extension: function's implementation cannot be overridden by a subclass

6 Likes

Good to see it is on the community's radar.
I'm puzzled as to why this bug has not yet been addressed since 2015.

It’s not really a bug but rather a consequence of how protocol conformances work, but we should probably emit a warning in this situation.

4 Likes
  1. What should the emitted warning say?
  2. Back to the original question - what is the rationale behind Swift favoring static dispatch in this context (see attached screenshot)?

If a superclass A conforms to P but uses a default implementation for one of the requirements (echo), then there is no ‘slot’ in the class vtable for a A corresponding to the echo requirement. As a subclass, B doesn’t get its own unique conformance to P—rather it has to reuse A’s conformance, including the ‘use default echo implementation’ part of that conformance. And since A has no actual echo method, B cannot override the default implementation that A used. So even if there is an echo method on B, it’s not registered as being part of the conformance.

2 Likes

It is not (notionally) statically dispatched. There is only one witness to the protocol requirement and it is the extension method, for the reasons outlined above by @Jumhyn.

The emitted warning should say that the concrete method echo shadows but does not override the default implementation.

4 Likes

I think you mean there's no slot in the class vtable, not in the protocol conformance. A protocol conformance always records an implementation for each protocol requirement. If the implementation is declared in a protocol extension, the corresponding entry points at the protocol extension method. If the implementation is declared inside a class, the entry points at a thunk which loads the entry from the class's vtable and calls it. Method overrides in a subclass are implemented by the subclass replacing the vtable entry with its own implementation.

4 Likes

This behavior falls out from the table-driven dispatch used for protocol conformances and class methods. To allow a subclass to override a default implementation would require a more general dispatch mechanism, like objc_msgSend() or similar.

1 Like

Ah, yeah, you're right—I misremembered the details here. Edited to clarify, thank you!

Technically, it would only require that when the conformance is declared via an extension to the class.

If the conformance is declared at the class definition site, then it would theoretically be possible for the compiler to include these methods within the class vtable “as if” they had been written out manually.

1 Like

Yes, you're absolutely right, but the extension thing means it's still a leaky abstraction and the limitations of table dispatch cannot be completely papered over.

1 Like

I wonder if there might someday be a way to let people make it explicit. Like “@allowDefaultOverrides(Protocol)” within the main body of a class or something.

1 Like

If we introduced a way to refer to the default implementation directly rather than only via witness (e.g. via some fully-qualified name syntax), classes could make this explicit by providing the echo() function and having the implementation be, say:

self.P::echo()
5 Likes