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
TagTypeis a thin wrapper aroundFuncTypeas 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!
cfallin commented on PR #11230:
A few other missing bits that I will do before marking as ready:
- I haven't implemented the GC-disabled stubs yet.
- We'll need to think about how to hide this appropriately -- at the very least, behind the GC feature, but perhaps behind another feature flag as well, until we have the full exceptions proposal implemented? We don't enable exception support in wasmparser so no one will be accidentally fooled by a failure deep in unimplemented bits; but the presence of a host API may be misleading still.
- No fuzzing yet; I'll build that out once I have the rest of the exceptions proposal done too.
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.
alexcrichton created PR review comment:
To avoid panicking during a coredump in the future by accident, could this return an error instead of panicking?
alexcrichton created PR review comment:
Could this be a
bail!instead ofunreachable!in case this ever becomes possible in the future?
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?
alexcrichton created PR review comment:
Could the docs above this referenceing 27 bits be updated to refer to 26?
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
gcfeature flag for now and leave it at that. This won't bring in any significantly new runtime dependencies thatgcdoesn'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_exceptionsknob will only be available withfeature = "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)
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.
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:
- fitzgen: wasmtime:ref-types
To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.
Learn more.
</details>
bjorn3 submitted PR review.
bjorn3 created PR review comment:
*the
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
ExnRefPreexplicitly 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.
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 theStoreor 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!
cfallin updated PR #11230.
cfallin updated PR #11230.
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.
cfallin updated PR #11230.
cfallin submitted PR review.
cfallin created PR review comment:
Good point!
cfallin submitted PR review.
cfallin created PR review comment:
Actually, updated to handle
Exnwith the same logic asAny(added to the match arm) -- the logic is generic and so if we ever need to support this it's already there.
cfallin submitted PR review.
cfallin created PR review comment:
Done!
cfallin submitted PR review.
cfallin created PR review comment:
One better, it's possible to create an exnref type manually (there's just no alias) -- done.
cfallin submitted PR review.
cfallin created PR review comment:
Done!
cfallin updated PR #11230.
cfallin submitted PR review.
cfallin created PR review comment:
Fixed, thanks!
cfallin updated PR #11230.
cfallin updated PR #11230.
cfallin updated PR #11230.
cfallin updated PR #11230.
cfallin updated PR #11230.
cfallin updated PR #11230.
cfallin updated PR #11230.
Last updated: Dec 06 2025 at 07:03 UTC