Stream: git-wasmtime

Topic: wasmtime / PR #11230 WIP: Implement exception objects.


view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 00:20):

cfallin opened PR #11230 from cfallin:wasm-exnref to bytecodealliance:main:

This PR implements exception objects: the data model required to support Wasm exceptions, integrated with Wasm GC.

It builds out support for defining exception types (based on function signatures), and a host API to allocate and examine exception objects. It does not yet tie this into any exception-throwing or -catching instructions (throw, throw_ref, try_table); that will come next.

The design is a little fiddly because the type system has a nominal aspect to it: tags are per-instance entities and exceptions are associated with tags. The stack-switching work already added support for tags analogously to other instance entities (memories, tables, etc.). But, notably, because they are a runtime concept, they are not types per-se. Their signatures are, though, and TagType is a thin wrapper around FuncType as a result. The Wasm proposal does not define any concrete type definitions for exception objects in the lattice, only providing top (exnref / (ref null exn)) and bottom (nullexnref / (ref null noexn)) types. In order to align with the way the rest of the GC system works, I've opted to define our own notion of structural types at the host API and type-interning layer built around the signatures; so all Wasm exception objects with the same func-type / signature are the same "type" as far as GC is concerned. The objects of that type are then associated with particular tag instances via runtime fields. In essence we are refining the Wasm types further to allow our implementation to precompute things (i.e., layouts); this is fine because this refinement is not exposed to Wasm.

I'm posting this as a draft to get any early feedback that might exist but I'll plan to add more tests next week before building on this!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 00:22):

cfallin commented on PR #11230:

A few other missing bits that I will do before marking as ready:

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton created PR review comment:

It's ok to skip this since there's no limiter being invoked as part of generate_tag_export, that's just for memories/tables.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton created PR review comment:

To avoid panicking during a coredump in the future by accident, could this return an error instead of panicking?

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton created PR review comment:

Could this be a bail! instead of unreachable! in case this ever becomes possible in the future?

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton created PR review comment:

Technically this should alwayas be Ok(()), right? Given that it's impossible for a wasm module to request a typed exnref?

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton created PR review comment:

Could the docs above this referenceing 27 bits be updated to refer to 26?

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:39):

alexcrichton submitted PR review:

Nice!

We'll need to think about how to hide this appropriately

Personally I think it's reasonable to keep this behind the gc feature flag for now and leave it at that. This won't bring in any significantly new runtime dependencies that gc doesn't already bring, so having it behind the same flag I think is reasonable. In that sense filling out similar-ish GC-disabled stubs for exnref I think is fine.

In the future this means that the hypothetical Config::wasm_exceptions knob will only be available with feature = "gc" and if the gc feature is disabled then this feature will always be disabled for wasmparser, effectively being where we're at today.

One hypothetical branch point we could reach in the future is to implement wasm exceptions entirely independently of wasm gc. In such a world we'd probably have a separate feature gate for exception-handling but in lieu of that I don't think it's necessary to add a new feature just for exceptions (assuming no new "substantial" runtime dependencies or such are needed, which AFAIK won't be the case)


As a small aside, it's a bit regrettable how much duplication in a sense there is necessary for this. Most of this PR looks like a copy/paste of the GC bits for structref but then edited to be for exnref instead. That's not a dealbreaker by any means and rustc more-or-less shoehorns these implementations to exactly what they are, but I also think it'd be reasonable if you see refactoring opportunities to apply them more aggressively too (e.g. what you did here with initialization/reading struct fields and refactoring that so exceptions can use the same path)

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 01:40):

alexcrichton commented on PR #11230:

The design is a little fiddly because the type system has a nominal aspect to it

Oh one thing I forgot, are you familiar enough with the JS API for exceptions to say whether or not the approach here is roughly analagous? I don't know myself what the JS API looks like so I'm not sure myself.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 03:59):

github-actions[bot] commented on PR #11230:

Subscribe to Label Action

cc @fitzgen

<details>
This issue or pull request has been labeled: "wasmtime:api", "wasmtime:ref-types"

Thus the following users have been cc'd because of the following labels:

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

Learn more.
</details>

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 12:41):

bjorn3 submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 12 2025 at 12:41):

bjorn3 created PR review comment:

*the

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 21:42):

cfallin commented on PR #11230:

Oh one thing I forgot, are you familiar enough with the JS API for exceptions to say whether or not the approach here is roughly analagous? I don't know myself what the JS API looks like so I'm not sure myself.

As far as I can tell, this is pretty close to both the Wasm JS API and the host API outlined in the spec, modulo requiring the ExnRefPre explicitly in our case whereas the above APIs allow one to allocate an exception object solely with a runtime tag reference and payloads. This is more-or-less in line with the way we support allocating e.g. struct objects too -- separating the layout lookup/computation is a reasonable performance optimization IMHO.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 21:49):

cfallin commented on PR #11230:

One hypothetical branch point we could reach in the future is to implement wasm exceptions entirely independently of wasm gc. In such a world we'd probably have a separate feature gate for exception-handling but in lieu of that I don't think it's necessary to add a new feature just for exceptions (assuming no new "substantial" runtime dependencies or such are needed, which AFAIK won't be the case)

Yeah, I thought about this quite a bit before building out this part of the proposal: I was wondering whether the two (exceptions and GC) could be kept orthogonal as much as possible, largely to avoid the effort in this PR. An exnref would then be more like an externref; maybe held as an Option<Box<Exception>> in a table on the Store or something like that. In the end I didn't like that for three main reasons: (i) exception objects can store GC refs as payload, so when both are enabled, one needs special handling anyway to trace edges in both directions; (ii) it's quite a lot of duplicated effort (computing how to lay out values in a struct-like container, handling layouts and interning them, etc.); (iii) if one addresses problem (i) by having a separate exnref implementation for with-GC and without-GC, then one has two code paths to test and that's awful. Plus in practice most users of exceptions will likely want GC too. (Perhaps with the exception of a setjmp/longjmp use-case in wasi-libc?) Or to summarize all the above: GC has enough overlap with exceptions that they just need to interact, so we might as well handle them with common bits.

As a small aside, it's a bit regrettable how much duplication in a sense there is necessary for this. Most of this PR looks like a copy/paste of the GC bits for structref but then edited to be for exnref instead. That's not a dealbreaker by any means and rustc more-or-less shoehorns these implementations to exactly what they are, but I also think it'd be reasonable if you see refactoring opportunities to apply them more aggressively too (e.g. what you did here with initialization/reading struct fields and refactoring that so exceptions can use the same path)

Yeah, I don't like it too much either; but several alternatives I considered didn't feel great either. In particular one could imagine either using more dynamism internally (having a "struct or exception layout" that knows its kind dynamically and has the appropriate dynamic checks) or using traits or macros to factor out method implementations. In the end the surface bits needed to be separate for clean typesafe API reasons and the rest kind of fell out of that. Happy to take any particulara suggestions for refactors if anyone has any though!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 22:35):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:02):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:05):

alexcrichton commented on PR #11230:

I was wondering whether the two (exceptions and GC) could be kept orthogonal as much as possible

FWIW I agree with your conclusions as well. I mostly brought this up to say that I think it would warrant a separate feature in such a hypothetical alternative world, but given the current implementation I don't think this warrants a separate feature or anything more than gating behind GC.

In the end the surface bits needed to be separate for clean typesafe API reasons and the rest kind of fell out of that

FWIW this is mostly my takeaway as well. It's wordy, but easy to read and verify it's right. The downside is that if it's got a bug (which would likely be preexisting if any) there's a lot of places to chase down. I think it's best to leave that for the hypothetical future in which we discover this though. Otherwise though I don't have any silver bullet suggestions myself either.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:13):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:13):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:13):

cfallin created PR review comment:

Good point!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin created PR review comment:

Actually, updated to handle Exn with the same logic as Any (added to the match arm) -- the logic is generic and so if we ever need to support this it's already there.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin created PR review comment:

Done!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin created PR review comment:

One better, it's possible to create an exnref type manually (there's just no alias) -- done.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:14):

cfallin created PR review comment:

Done!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:15):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:15):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:15):

cfallin created PR review comment:

Fixed, thanks!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:25):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:27):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:41):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:48):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 14 2025 at 23:56):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 15 2025 at 00:06):

cfallin updated PR #11230.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 15 2025 at 00:20):

cfallin updated PR #11230.


Last updated: Dec 06 2025 at 07:03 UTC