Hello, this is a pitch for allowing of custom dynamic casting support. I'll first introduce the feature and the motivation for this pitch.
Introduction
The idea of this feature is to allow for classes to hook into dynamic cast failures and potentially provide the casting implementation themselves. This feature is for low level libraries, mostly for those interoping with other languages, where defining the full type enclosure in native Swift code is either impossible to do, or has performance issues. There are two main scenarios where this will be useful, which I'll highlight below. The APIs in this example are created by a code generator and are wrappers around a low level library which bridge between Swift and some other language.
class A {}
class B: A {}
protocol Scenarios {
func someTypeErasedValue() -> Any
func someBaseType() -> A
}
Let's say for both methods above, the type that is returned from the Scenarios
API is of type class B
. We don't know at code-gen time what these objects are, so the type casts (i.e. let b = someTypeErasedValue() as? B
or let b = someBaseType() as? B
) will fail.
The feature
protocol _DynamicCastFallback: AnyObject {
func castTo(_ type: (any Any.Type)) -> Any?
}
Motivation
We have a code generator (GitHub - thebrowsercompany/swift-winrt: Swift Language Projection for WinRT) which is used to generate code to interop with the Windows Runtime. The Windows runtime is built on top of COM, where type casting is done through IUnknown.QueryInterface
. There are currently a few problems that we have trouble solving:
- There is no guarantee we know the full enclosure of interfaces at compile time, resulting in us being unable to generate fully correct code
- In order to support the above scenario, we have to do expensive type introspection to return the appropriate object, potentially for no real reason if the user doesn't actually do an upcast.
- There are scenarios we simply can't support today (i.e. generic WinRT types)
Note: While COM is the main use case and motivation for this pitch, it is not tied to COM in any way
What this pitch is not
This pitch is not is for a way to escape or re-define Swift's casting abilities today. We simply provide a last chance effort to perform the cast when it would otherwise fail.
Prior Art
.NET has this functionality through the IDynamicInterfaceCastable interface. The same rules can apply here that only reference types (classes) can participate in this behavior.