Summary
handle<T> (a typed opaque handle whose tag is a struct T) is rendered differently by each generator, and two of the renderings do not compile. The IDL reference says it is "still a uint64_t" at the C ABI, but the emitted header declares it as an opaque struct pointer (weaveffi_<module>_<T>*), and the language wrappers disagree about what a consumer receives.
Observed on main after #44, using the kitchen_sink and edge_cases snapshot fixtures:
| Generator |
handle<Token> surfaces as |
Status |
| C header |
weaveffi_kitchen_Token* (opaque pointer) |
compiles; contradicts the docs |
| Dart |
Token._(result) / token._handle on the Token record class |
does not compile: Token is a plain data class with no _ constructor or _handle getter |
Node .d.ts |
Token (the record interface itself) |
compiles but wrong: a pointer is typed as the decoded record |
| Swift |
UInt64 |
compiles |
| Python |
int (c_void_p) |
compiles |
Wasm .d.ts |
number |
compiles |
The Dart failure predates #44 and was never caught because no sample uses handle<T> and nothing compiled the generated Dart before the edge_cases audit.
Reproduction
weaveffi generate crates/weaveffi-cli/tests/fixtures/edge_cases.yml -o out
cd out/dart && dart analyze
- See
The getter '_handle' isn't defined for the type 'Leaf' and The method '_' isn't defined for the type 'Leaf' at the typedHandleParam wrapper. The same Token._(result) shape appears in the kitchen_sink Dart output.
Expected
One documented answer to "what does a consumer hold for handle<T>?" applied uniformly. Two reasonable designs:
- Opaque wrapper class per tag. Each language gets a tiny
TokenHandle-style class wrapping the pointer, giving the type safety the docs promise. This is what Dart appears to have been reaching for.
- Plain integer everywhere. Match Swift and Python, and fix the docs'
uint64_t claim to agree with the header (or change the header to uint64_t).
Either way, Dart must stop calling members that do not exist, Node must stop typing the handle as the record, and docs/src/reference/idl.md ("Typed handles") must match the header.
Environment
Additional context
Found during the coverage audit in #44. The edge_cases fixture keeps typed_handle_param (handle<Leaf> as both a parameter and a return) so the snapshot diff will show the fix landing in every generator.
Summary
handle<T>(a typed opaque handle whose tag is a structT) is rendered differently by each generator, and two of the renderings do not compile. The IDL reference says it is "still auint64_t" at the C ABI, but the emitted header declares it as an opaque struct pointer (weaveffi_<module>_<T>*), and the language wrappers disagree about what a consumer receives.Observed on
mainafter #44, using thekitchen_sinkandedge_casessnapshot fixtures:handle<Token>surfaces asweaveffi_kitchen_Token*(opaque pointer)Token._(result)/token._handleon theTokenrecord classTokenis a plain data class with no_constructor or_handlegetter.d.tsToken(the record interface itself)UInt64int(c_void_p).d.tsnumberThe Dart failure predates #44 and was never caught because no sample uses
handle<T>and nothing compiled the generated Dart before theedge_casesaudit.Reproduction
weaveffi generate crates/weaveffi-cli/tests/fixtures/edge_cases.yml -o outcd out/dart && dart analyzeThe getter '_handle' isn't defined for the type 'Leaf'andThe method '_' isn't defined for the type 'Leaf'at thetypedHandleParamwrapper. The sameToken._(result)shape appears in thekitchen_sinkDart output.Expected
One documented answer to "what does a consumer hold for
handle<T>?" applied uniformly. Two reasonable designs:TokenHandle-style class wrapping the pointer, giving the type safety the docs promise. This is what Dart appears to have been reaching for.uint64_tclaim to agree with the header (or change the header touint64_t).Either way, Dart must stop calling members that do not exist, Node must stop typing the handle as the record, and
docs/src/reference/idl.md("Typed handles") must match the header.Environment
mainafter feat!: resolve types to Ty, and move package config to weaveffi.toml #44Additional context
Found during the coverage audit in #44. The
edge_casesfixture keepstyped_handle_param(handle<Leaf>as both a parameter and a return) so the snapshot diff will show the fix landing in every generator.