feat: export generic structs/impls to TypeScript via instantiate(...)#1
Open
rossng wants to merge 3 commits into
Open
feat: export generic structs/impls to TypeScript via instantiate(...)#1rossng wants to merge 3 commits into
rossng wants to merge 3 commits into
Conversation
Adds a `#[wasm_bindgen(instantiate(...))]` attribute that monomorphizes generic structs and their impls into concrete instantiations exported to JS. Tracks rust_generics on Struct/StructField/Export, threads generics/instantiate params through ClassMarker, and emits concrete type params (e.g. Tr::<f64>) in codegen instead of bare idents. Updates invalid-generics/invalid-items ui-test stderr expectations.
…ructs
#[wasm_bindgen(js_class = "FooDebug")] impl Foo<Evaluated> { ... } attaches
methods to one declared instantiation's exported class. Self is rewritten to
the concrete type and shims carry the instantiation's generic arguments.
This unblocks typestate generics (uniform impls that cannot type-check for
every instantiation) by letting per-state methods live on concrete impls.
Also restores a loud error for concrete-args impls without js_class (the
instantiate feature had made them silently strip their arguments).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
instantiate(Tr<JsValue> as Tr<P>) exports one erased runtime class whose generated .d.ts is generic: class Tr<P = any> with P in the signature positions whose original Rust type was the corresponding type parameter (or Self / Tr<P>). Statics redeclare used class params method-level (TS statics cannot reference class type params). A new free-form typescript_type_params = "Q" attribute declares method-level TS params, composing with unchecked_return_type/unchecked_param_type for cases like map<Q>(f: (p: P) => Q): Tr<Q>. Schema: Struct.ts_params + Function.ts_params; SCHEMA_VERSION bumped to 0.2.122-mnml.1 so a stock CLI fails loudly instead of misdecoding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
#[wasm_bindgen(instantiate(...))]— definition-site monomorphization that lets#[wasm_bindgen]be applied to generic structs and impls, which stock wasm-bindgen rejects outright. One generic definition stamps out one concrete exported JS class per listed instantiation.This is the upstream-side of removing a large class of hand-monomorphization / type-erasure boilerplate in our Rust→WASM crates (the
instantiate-genericswork).What's in it
Three composable capabilities, built as three commits on top of current upstream
main(0.2.123):instantiate(Foo<A> as FooA, Foo<B> as FooB)attribute — export a generic struct + generic impl as one concrete JS class per instantiation. Type-parameter-independent fields become shared auto-getters;#[wasm_bindgen(skip)]opts a field out.Per-instantiation concrete impl blocks —
#[wasm_bindgen(js_class = "FooB")] impl Foo<B> { ... }attaches methods to a single instantiation's exported class (Selfis rewritten to the concrete type). Unblocks typestate generics: methods that only type-check for one instantiation live on concrete impls while shared surface stays generic.TS-level type parameters —
instantiate(Foo<JsValue> as Foo<P>)exports one erased runtime class whose generated.d.tsis the genericclass Foo<P = any>: type-parameter positions are typedP,SelfpositionsFoo<P>.typescript_type_params = "Q"composes withunchecked_return_type/unchecked_param_typefor cases likemap<Q>(f: (p: P) => Q): Foo<Q>. Unblocks JS-side type erasure with a TS declaration generated from the Rust signatures instead of hand-maintained.Schema bump
This changes the bindgen schema, so the CLI must be built from this branch — the stock CLI rejects the
instantiateschema with a version mismatch. (crates/sharedschema hash updated accordingly.)Validation
wbg-generics-test/is a standalone end-to-end crate (detached from the workspace) exercising all three capabilities from a Node runtime:Holder<P>/Pair<A, B>— basic monomorphized exports (capability 1)Check<D>/Plan<D>with a GAT-basedEvalMode— typestate generics (capability 2)Frame<P>erased toJsValuewith a generic.d.ts— TS-level type params (capability 3)Verified locally: build the CLI from this branch, build the crate to
wasm32-unknown-unknown, runwasm-bindgen,node test.mjs→ all assertion groups pass.Base
Targets this fork's
main(now fast-forwarded to current upstream4f17390b5), not rustwasm upstream.🤖 Generated with Claude Code