gc
gc copied to clipboard
3-pronged subtyping hierarchy
This makes the necessary changes to the MVP doc for introducing the 3-pronged subtype hierarchy. For now, I haven’t done any renaming, so I went with types any and data and leave possible bikeshedding for later. Concretely:
- Reintroduce heap type
anyseparately fromextern(under the previous type code) - Add two new bottom types
nofuncandnoextern - Add shorthands
nullfuncref = (ref null nofunc)andnullexternref = (ref null noextern) - Split up subtyping rules for top and bottom types; they now need to inspect
t, unlike before (in other words, checking againstanybecomes more costly) - Extend typing rules for casts to check that they are hierarchy-consistent
- Add instructions
extern.internalizeandextern.externalize - Remove instructions
ref.is_func,ref.as_func,br_on_func,br_on_non_func
As discussed, there are no instructions for converting between extern and functions.
See #311 for implementation and tests.
See #311 for implementation and tests.
I still believe we do not need noextern. We can include (ref.null extern) in externref.
@manoskouk, I believe that wouldn't be forward-compatible. For example, if we later introduce type imports $t <: extern like @lukewagner mentioned, then (ref null extern) would no longer be a correct type for the null reference. Consequently, we would need to change nullexternref to mean something else later. But that would break existing programs that assume (ref null extern) is the same type (or a subtype).
A while back @rossberg suggested introducing a struct type for consistency with array. It was argued then (including by myself) that this does not add anything to the language.
However, it might be useful to include it because downcasts from struct are likely to be more efficient than from data, and even more so the sequence ref.as_struct (ref.cast $t) compared to ref.as_data (ref.cast $t).
Again, this might be a post-MVP consideration.
I'm all for adding struct, even in the MVP, to avoid an odd asymmetry. But I didn't want to mix it up with this PR.
@manoskouk, I believe that wouldn't be forward-compatible. For example, if we later introduce type imports
$t <: externlike @lukewagner mentioned, then(ref null extern)would no longer be a correct type for the null reference. Consequently, we would need to changenullexternrefto mean something else later. But that would break existing programs that assume(ref null extern)is the same type (or a subtype).
I am not following. Aren't we doing the same thing for func right now? (ref.null func) is typed (ref null func) in the MVP. Now that we introduced subtypes of func, we are retyping it as (ref null nofunc). Why can we not follow the same process for externref when and if we introduce subtypes of it?
Aren't we doing the same thing for
funcright now?(ref.null func)is typed(ref null func)in the MVP.
Only if you are assuming that we are removing nullexternref as well. Then we'd indeed be in a similar situation as with func today. But we're only in that because we had no nullref types in the first place. We have identified that as a mistake and are rectifying it now, so it seems preferable to do so consistently, and have explicit null types for every hierarchy. Otherwise we'd still provide no forward-compatible way to type-annotate an external nullref. Stupid example:
(module
(global (export "null") externref (ref.null extern))
)
This export will no longer be adequate in the presence of additional subtypes of extern. Maybe such a case is not very relevant in practice, but I see no benefit in not rectifying it along with the others. It's not like omitting it would simplify much.
Changed opcodes.
In the future, I would appreciate if we could get away from this mode of operation where implementations unilaterally deviate from what's proposed and then retroactively declare that "reality". ;)
In the future, I would appreciate if we could get away from this mode of operation where implementations unilaterally deviate from what's proposed and then retroactively declare that "reality". ;)
What other workflow would you want to see that still allows engines to have multiple arbitrary prototype/early-stage proposals in flight at once?
I think it's less a question of workflow than one of style and communication. When you discover conflicts between, or unresolved issues with, proposals, try to help the champions to get them resolved. If resolution is blocking you for too long, discuss what temporary choices you could move forward with. Consider them temporary.