Support multiple subtype constraints
Type imports are going to be the primary mechanism by which a Wasm module interacts with objects in a host language. In particular, using that host language's APIs in the way they were intended means describing the relationship between host object types accurately enough to allow proper use of the API.
For host languages that have multiple inheritance, for example, Java or C#, which have interfaces, the type import mechanism must be able to describe all of the supertypes of an imported class or interface type. APIs in these languages make heavy use of interfaces.
For example, suppose we import the Java type List. This type is a subtype of Object of course, but it also implements the interface Iterable. Naturally, these three types would be type imports to our Wasm module, with List being a subtype of Object. Yet if we ever want to pass a List to another API method that accepts Iterable, then we either need to directly allow subsumption (via subtyping), i.e. directly pass the list object to that API, or we need another operation to "view" a List as an Iterable thing, likely by importing another view operation, a type coercion that is in the best case essentially a nop and in the worst case an actual wasm-level coercion. Also, importing all the type coercions that are not expressable in wasm will take a lot of space.
I argue it is most natural to be able to express the multiple subtyping relationship as multiple subtype constraints.
Clearly, this also means that subtype constraints can reference other type imports. I propose to disallow cycles by requiring topological sorting of type imports, i.e. one can only use previously-declared or imported types in subtype constraints.
I propose that the constraint mechanism have the following long-term definition:
- An imported type can specify 0 or more subtype constraints that refer to any earlier declared or imported value types.
We can then drop the need for the any type proposed here. Instead, we can impose the following short-term restrictions for now:
- An imported type must have at least 1 type constraint.
- Every value type in a type constraint must be a
reftype. - A type import whose constraints are all
reftypes is areftype.[1]
[1] Note that with multiple constraints, it is now possible to specific incompatible constraints such that there is no type that satisfies them all; e.g. both a subtype of i32 and f32. The engine need not check the solvability of constraints at compile time, since it will just never be possible to devise a type that meets all constraints that would be checked at instantiation time.