Iterable / iterator support and generic type parameter propagation#12
Closed
guybedford wants to merge 1 commit into
Closed
Iterable / iterator support and generic type parameter propagation#12guybedford wants to merge 1 commit into
guybedford wants to merge 1 commit into
Conversation
e9a73d3 to
414b1cf
Compare
Adds first-class handling for the TS iterability protocol and bare
generic type parameters in method signatures, so APIs like
`SyncKvStorage.put<T>(key: string, value: T)` and
`SyncKvStorage.list<T>(): Iterable<[string, T]>` round-trip into
typed wasm-bindgen externs without erasing the generics.
`Iterator<T>` and `IterableIterator<T>` map directly to
`js_sys::Iterator<T>`; `AsyncIterator<T>` and
`AsyncIterableIterator<T>` map to `js_sys::AsyncIterator<T>`. These
are added as new IR variants (`TypeRef::Iterator` and
`TypeRef::AsyncIterator`) and emit through the existing generic
container machinery alongside `Promise<T>`.
`Iterable<T>` describes the protocol — an object exposing
`[Symbol.iterator](): Iterator<T>` — distinct from `Iterator<T>`
itself. To preserve that at the binding layer, top-level
`Iterable<T>` returns now synthesize a wrapper extern type with a
single `[Symbol.iterator]()` method:
```ts
interface SyncKvStorage {
list<T>(): Iterable<[string, T]>;
}
```
becomes:
```rust
pub type SyncKvStorageList<T: ::wasm_bindgen::JsGeneric>;
pub fn iterator<T: ::wasm_bindgen::JsGeneric>(
this: &SyncKvStorageList<T>,
) -> Iterator<ArrayTuple<(JsString, T)>>;
pub fn list<T: ::wasm_bindgen::JsGeneric>(this: &SyncKvStorage) -> SyncKvStorageList<T>;
```
The wrapper's name follows the existing `<Parent><Member>`
convention with dedup, mirroring anonymous-interface parameter
synthesis. `AsyncIterable<T>` synthesizes the analogous wrapper
keyed on `[Symbol.asyncIterator]`. The bracketed `js_name`
matches wasm-bindgen's computed-property syntax for symbol-keyed
methods. Nested occurrences inside unions or arrays are not
synthesized — they erase to `JsValue`, matching the existing
parameter-synthesis limitation.
Bare TS generics on methods now survive codegen as
`<T: ::wasm_bindgen::JsGeneric>` declarations rather than being
erased to `JsValue`. Implementation:
* New `TypeRef::TypeParam(String)` IR variant for in-scope generic
type parameter references.
* `convert_ts_type_scoped` returns `TypeParam(name)` instead of
`Any` for names in the method's type-parameter scope.
* `convert_formal_params_with_synthesis` now threads scope through
to parameter type conversion (previously called `convert_ts_type`,
which ignored scope).
* Method codegen collects every `TypeParam` reachable from the
signature and emits a generic decl bounded by
`::wasm_bindgen::JsGeneric`. Synthesized iterable wrappers
propagate any `TypeParam` from their item type onto the wrapper
type itself, so `SyncKvStorageList` is `SyncKvStorageList<T>`.
* `pub type X<T, …>` declarations carry their generics so type
aliases that mention generic parameters compile.
`GenericInstantiation` codegen now consults the codegen context's
recorded type-parameter arity per local type. References like
`ReadableStream<Uint8Array>` whose target accepts zero generics
(non-generic web-sys / external types) drop the args and emit the
bare base type, preventing `E0107`.
Symbol JS names like `[Symbol.iterator]` previously snake_cased to
`symboliterator`. `base_rust_name` now strips the `[Symbol.`
prefix and trailing `]` when computing the Rust name, so the
synthesized iterator methods read as `iterator` /
`async_iterator` while `js_name` keeps the bracketed
`[Symbol.iterator]` for wasm-bindgen.
Generated files now begin with `// Generated by ts-gen. Do not
edit.` (prepended after rustfmt — `quote!` doesn't preserve line
comments through token streams).
* New `tests/fixtures/iterables.d.ts` exercises every iterability
variant plus generic propagation through `put<T>` / `get<T>`.
* New `tests/snapshots/iterables.rs` blesses the expected output.
* Four unit tests in `parse::members::tests` cover happy path
(sync), async, non-iterable rejection, and dedup.
* Existing snapshots updated: workers-types now produces typed
`Iterator<...>`, `AsyncIterator<...>`, and per-method generics
on `put` / `get` style methods that previously aliased `T` to
`JsValue`.
Two new sections (slotted next to `Promise<T>` since all three
share the generic-container shape):
* `Iterator<T>` / `IterableIterator<T>` map to `js_sys::Iterator<T>`
* `Iterable<T>` returns synthesize a wrapper with `[Symbol.iterator]()`
`AGENTS.md` updated to clarify that CONVENTIONS sections are not
numbered and are ordered from simplest to most obscure.
414b1cf to
bbda773
Compare
Contributor
Author
|
Replaced by #24 on the new IR model. |
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.
Adds first-class handling for the TS iterability protocol and bare generic type parameters in method signatures, so APIs like
SyncKvStorage.put<T>(key: string, value: T)andSyncKvStorage.list<T>(): Iterable<[string, T]>round-trip into typed wasm-bindgen externs without erasing the generics.Iterators
Iterator<T>andIterableIterator<T>map directly tojs_sys::Iterator<T>;AsyncIterator<T>andAsyncIterableIterator<T>map tojs_sys::AsyncIterator<T>. New IR variantsTypeRef::IteratorandTypeRef::AsyncIteratoremit through the existing generic container machinery alongsidePromise<T>.Iterable wrapper synthesis
Iterable<T>describes the protocol — an object exposing[Symbol.iterator](): Iterator<T>— distinct fromIterator<T>itself. To preserve that at the binding layer, top-levelIterable<T>returns now synthesize a wrapper extern type:becomes:
Naming follows the existing
<Parent><Member>convention with dedup, mirroring anonymous-interface parameter synthesis.AsyncIterable<T>synthesizes the analogous wrapper keyed on[Symbol.asyncIterator]. The bracketedjs_namematches wasm-bindgen's computed-property syntax for symbol-keyed methods. Nested occurrences inside unions or arrays are not synthesized — they erase toJsValue, matching the existing parameter-synthesis limitation.Generic type parameter propagation
Bare TS generics on methods now survive codegen as
<T: ::wasm_bindgen::JsGeneric>declarations rather than being erased toJsValue:TypeRef::TypeParam(String)IR variant for in-scope generic type parameter references.convert_ts_type_scopedreturnsTypeParam(name)instead ofAnyfor names in the method's type-parameter scope.convert_formal_params_with_synthesisthreads scope through to parameter type conversion (previously calledconvert_ts_type, which ignored scope — a pre-existing bug that erased every generic argument type toJsValueand emitted dummyuse JsValue as T;aliases).TypeParamreachable from the signature and emits the bound declaration. Synthesized iterable wrappers propagate anyTypeParamfrom their item type onto the wrapper type itself.pub type X<T, …>declarations carry their generics so type aliases that mention generic parameters compile.Before:
After:
External non-generic type protection
GenericInstantiationcodegen now consults the codegen context's recorded type-parameter arity per local type. References likeReadableStream<Uint8Array>whose target accepts zero generics (non-generic web-sys / external types) drop the args and emit the bare base type, preventingE0107at the use site.Symbol-keyed methods
Symbol JS names like
[Symbol.iterator]previously snake_cased tosymboliterator.base_rust_namenow strips the[Symbol.prefix and trailing]when computing the Rust name, so the synthesized iterator methods read asiterator/async_iteratorwhilejs_namekeeps the bracketed[Symbol.iterator]for wasm-bindgen.Dedup numbering
dedupe_namenow starts collision suffixes at_2instead of_1— the unsuffixed candidate is the implicit_1, so the first collision becomes_2. Matches the convention already used byparse::members::unique_type_name(Foo,Foo2,Foo3, …). User-visible:Response::json_1(instance method colliding with the staticjsonfactory) becomesjson_2, etc.Header comment
Generated files now begin with
// Generated by ts-gen. Do not edit.— prepended after rustfmt sincequote!strips line comments from token streams.Tests
tests/fixtures/iterables.d.tsexercises every iterability variant plus generic propagation throughput<T>/get<T>.tests/snapshots/iterables.rsblesses the expected output.parse::members::testscover happy path (sync), async, non-iterable rejection, and dedup.Iterator<...>,AsyncIterator<...>, and per-method generics onput/getstyle methods that previously aliasedTtoJsValue.Conventions
Two new sections, slotted next to
Promise<T>since all three share the generic-container shape:Iterator<T>/IterableIterator<T>map tojs_sys::Iterator<T>Iterable<T>returns synthesize a wrapper with[Symbol.iterator]()AGENTS.mdupdated to clarify that CONVENTIONS sections are not numbered and are ordered from simplest to most obscure.Verification
just test— 123 unit + 1 snapshot passjust clippy— clean with-D warningsjust fmt-check— clean