Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

3-pronged subtyping hierarchy #310

Merged
merged 9 commits into from
Aug 26, 2022
Merged

3-pronged subtyping hierarchy #310

merged 9 commits into from
Aug 26, 2022

Conversation

rossberg
Copy link
Member

@rossberg rossberg commented Jul 14, 2022

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 any separately from extern (under the previous type code)
  • Add two new bottom types nofunc and noextern
  • Add shorthands nullfuncref = (ref null nofunc) and nullexternref = (ref null noextern)
  • Split up subtyping rules for top and bottom types; they now need to inspect t, unlike before
    (in other words, checking against any becomes more costly)
  • Extend typing rules for casts to check that they are hierarchy-consistent
  • Add instructions extern.internalize and extern.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.

@rossberg
Copy link
Member Author

See #311 for implementation and tests.

@manoskouk
Copy link
Contributor

I still believe we do not need noextern. We can include (ref.null extern) in externref.

@rossberg
Copy link
Member Author

@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).

#### Classification
#### External conversion

* `extern.internalize` converts an external value into the internal representation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the discussions, in was mentioned that it would be useful for performance to include direct transformations to more specific abstract types. This might be best as a post-MVP feature though.

@manoskouk
Copy link
Contributor

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.

@rossberg
Copy link
Member Author

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
Copy link
Contributor

@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).

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?

@rossberg
Copy link
Member Author

Aren't we doing the same thing for func right 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.

@tlively tlively mentioned this pull request Jul 21, 2022
| -0x13 | `eqref` | | shorthand |
| -0x14 | `(ref null ht)` | `ht : heaptype (s33)` | from funcref proposal |
| -0x15 | `(ref ht)` | `ht : heaptype (s33)` | from funcref proposal |
| -0x16 | `i31ref` | | shorthand |
| -0x19 | `dataref` | | shorthand |
| -0x1a | `arrayref` | | shorthand |
| -0x1b | `nullref` | | shorthand |
| -0x1c | `nullfuncref` | | shorthand |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that -0x1c through -0x1f are taken by the stringrefs proposal, see https://github.com/WebAssembly/stringref/blob/main/proposals/stringref/Overview.md#binary-encoding.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out. I opened WebAssembly/stringref#41.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, we've implemented these as:

| -0x17 | `nullexternref` |
| -0x18 | `nullfuncref`   |

to resolve the conflict with the stringref proposal. (Of course this applies to lines 749/750 below as well.)

Co-authored-by: Matthias Liedtke <[email protected]>
@rossberg rossberg mentioned this pull request Aug 26, 2022
Copy link
Contributor

@jakobkummerow jakobkummerow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM.

I'd recommend to make the binary encoding listed here reflect reality (see note below) before landing this PR, but that's up to you.

| -0x13 | `eqref` | | shorthand |
| -0x14 | `(ref null ht)` | `ht : heaptype (s33)` | from funcref proposal |
| -0x15 | `(ref ht)` | `ht : heaptype (s33)` | from funcref proposal |
| -0x16 | `i31ref` | | shorthand |
| -0x19 | `dataref` | | shorthand |
| -0x1a | `arrayref` | | shorthand |
| -0x1b | `nullref` | | shorthand |
| -0x1c | `nullfuncref` | | shorthand |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, we've implemented these as:

| -0x17 | `nullexternref` |
| -0x18 | `nullfuncref`   |

to resolve the conflict with the stringref proposal. (Of course this applies to lines 749/750 below as well.)

@rossberg
Copy link
Member Author

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". ;)

@rossberg rossberg merged commit cdccaa1 into main Aug 26, 2022
@rossberg rossberg deleted the 3pronged branch August 26, 2022 10:17
@tlively
Copy link
Member

tlively commented Aug 26, 2022

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?

@rossberg
Copy link
Member Author

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants