Allow co/contra-variance for nullary `func.bind`?
This could be a post-MVP feature, just posting for discussion.
Currently, function types have declared subtyping like structs and arrays, and are limited to a single supertype. There isn't any implicit co-variance or contra-variance in parameter or return types (though those must be checked for a declared subtyping relation). The single-inheritance restriction is intended to make casts fast (i.e. constant time).
However, in the future, we may want richer subtyping for function references. This gets tricky because call_indirect does a dynamic signature-match, trapping on failure. Changing that may be hard and/or inefficient.
But I think we could allow func.bind to serve the role of a type coercion for function references beyond their declared subtyping relations and still make this work with call_indirect's signature-match semantics.
In particular:
- Suppose we have
B <: A - Suppose we declare
f1 = B -> Aandf2 = B -> B - We want to declare
f3 = A -> B; it is legally a subtype of either, but we can only declare it to be a subtype of one of the above, not both
We could:
- declare
f3to be a subtype off1 - allow
(func.bind f2 (expr: f3))
In this scenario, func.bind would have a more complex validation rule. In particular, for the "residual" type remaining after binding the arguments to the function (the arity difference between expr and f2, which could be 0), the allows co/contra variance params and returns. When the arity difference is 0, it is basically a type coercion.
For a non-zero arity difference, the assumption is that the engine allocates a closure, but for a 0 arity difference, we could as well have the engine generate a wrapper. The wrapper function needn't dynamically do anything, its metadata simply has a different signature. Thus when the wrapper is stored into a table, it is stored with the signature of the post-bind.