[swift-evolution] Proposal: Universal dynamic dispatch for method calls

Brent Royal-Gordon brent at architechies.com
Fri Dec 11 00:35:30 CST 2015

>> Swift loves to dispatch things statically and does so wherever possible. In some cases—such as value types not having inheritance—language features are clearly designed the way they are specifically to allow them to be statically dispatched. But Swift never uses static dispatch where dynamic dispatch would have given a different result. This contrasts with, for instance, a non-virtual C++ method’s behavior of “ha ha ha, I’m just going to ignore your override for speed." You could write a Swift compiler which dispatched everything dynamically, and you would never see any difference in semantics.
> Yes it does. When message dispatch is done using the obj-c runtime, the compiler is free to omit the message send and use static dispatch if it believes it knows the concrete type of the receiver. This normally behaves the same, because dynamic dispatch with a known receiver type and static dispatch are the same, except if the method is dynamically replaced using the obj-c runtime methods, the static dispatch will not invoke the dynamically overridden method. The most common way you see this is with KVO. If you try and KVO an @objc property on a Swift object, it may work sometimes and may not work at other times, depending on when the compiler believes it can safely use static dispatch. This is why Swift has a whole keyword called `dynamic` whose job is to say "no really, use dynamic dispatch for every single access to this method/property, I don't care that you know for a fact it's a Foo and not a subclass, just trust me on this”.

Okay, time to introduce some precise definitions so we don’t talk past each other.

There are three types of dispatch:

* Static. The compiler determines the exact function to execute.
* Virtual (I was previously calling this “dynamic”). The compiler determines the function to execute’s position in the instance’s vtable. (I don’t know if Swift actually calls this data structure a “vtable”, but you get my meaning.)
* Dynamic. The compiler determines the selector to send to the instance, thereby causing a function to execute.

Swift prefers virtual dispatch. Unless you request dynamic dispatch with `dynamic`, or you’re using members written in Objective-C (which aren’t included in the vtables Swift uses for virtual dispatch), Swift always behaves as if you’re going to get at least virtual dispatch. In some cases it uses static dispatch, but only where the language’s semantics guarantee that virtual dispatch would give the same result. Examples of this include `final` and `static` members (which forbid overriding the members in question, and thus prevent a mismatch between the results of virtual dispatch and static) and value types (which don’t support inheritance, so there’s no way to introduce a mismatch).

Again, the sole exception to this is protocol extensions. Unlike any other construct in the language, protocol extension methods are dispatched statically in a situation where a virtual dispatch would cause different results. No compiler error prevents this mismatch.

> More generally, I don't see there being any real difference between
> extension Proto {
>    func someMethodNotDefinedInTheProto()
> }
> and saying
> func someFuncThatUsesTheProto<T: Proto>(x: T)
> except that one is invoked using method syntax and one is invoked using function syntax. Method invocation syntax does not inherently mean "dynamic dispatch" any more than function syntax does.

I’m sorry, I just don’t agree with you on this. In Swift, generics and overloads are statically resolved at compile time. Other than explicit branching constructs like `if` and `switch`, the *only* place in Swift where a dynamic, *runtime* type—as opposed to the static, *compile-time* type—chooses which code to run is in the self position of a member access. Swift may choose to use static dispatch in certain cases, but the language is designed to prevent any difference in semantics based on that decision.

Protocol extensions are the weird outlier here—the only case in which the static and virtual dispatch behaviors are different, and Swift chooses the static dispatch behavior.

Brent Royal-Gordon

More information about the swift-evolution mailing list