From 4fa054b16e12a64617d77ea43411a8e0bc762633 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 11:34:13 +0200 Subject: [PATCH 01/18] 0.16.3 step 1: lazy CallerFnCollector + drop globals/start (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foundation refactor for the caller_fn i32-table redesign — codegen becomes the single source of truth for which fn names land in the exported table, no more AST walker. Changes: - `body::CallerFnCollector` (new): RefCell-friendly registry that `emit_caller_fn_idx` lazily registers fn names into. Each call site that wants a caller_fn slot gets one assigned in encounter order (first call to `register("main")` → idx 0, etc.). Idempotent — the per-fn probe pass in `module.rs` registers the same names twice (probe + real emit), `register` returns the already-stored idx on the second hit. - `EmitCtx` carries `&RefCell`; `emit_fn_body` takes the collector through its signature. - `body/emit.rs::emit_caller_fn_idx` rewritten — instead of looking up `registry.caller_fn_idx[self_fn_name]` (the 0.16.2 walker-built map), it calls `collector.register(self_fn_name)` and emits `i32.const `. Single source of truth. - `effects.rs::params` trailing arg switched from `any_ref_ty()` to `ValType::I32`. Host gets an idx instead of a String ref. - AST walker `fn_body_emits_effect_call` removed from `types.rs`, along with `caller_fn_idx` / `caller_fn_order` fields and the pre-walker fn-name segment registration. The collector replaces all three. - `module.rs`: 0.16.2's GlobalSection + StartSection + init body for `__init_globals` are gone. Their replacement (`__caller_fn_count` + `__caller_fn_name` exports plus the matching passive segments appended to the data section) is the next step — see TODO comments inline. Status — partial: - Compile: green (debug + release). - 10/10 wasm-gc record-replay smokes pass — but only because the host's `lm_string_to_host` falls through `Val::I32 → None → "main"` for the new ABI shape, and every smoke asserts `caller_fn: "main"` (which was 0.16.2's "main" too). The actual feature (per-fn caller_fn labels) is broken until step 2 lands the exports + body + segments + host wiring. TODO step 2 (next session): - 2 new wasm types: `__caller_fn_count: () -> i32` and `__caller_fn_name: (i32) -> (ref null $string)`. - 2 new fn entries in the function section, allocated last so their wasm fn idxs come after every helper. - 2 new exports. - Post-emit body emit for both, reading `collector.names`. - Append `collector.names` as fresh passive segments after the pre-walked literal segments; bump the data count section. - Host (Rust + JS): drop the LM round-trip in `dispatch_aver_import`, read `params.last().i32()` directly, look up `caller_fn_table[idx as usize]` from a `Vec` materialised once at instantiation by walking `__caller_fn_name(0..count)`. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body.rs | 37 ++++++++++ src/codegen/wasm_gc/body/builtins.rs | 6 +- src/codegen/wasm_gc/body/emit.rs | 33 ++++----- src/codegen/wasm_gc/effects.rs | 8 +- src/codegen/wasm_gc/module.rs | 105 ++++++--------------------- src/codegen/wasm_gc/types.rs | 84 ++------------------- 6 files changed, 91 insertions(+), 182 deletions(-) diff --git a/src/codegen/wasm_gc/body.rs b/src/codegen/wasm_gc/body.rs index 714c4f38..3a84ecb5 100644 --- a/src/codegen/wasm_gc/body.rs +++ b/src/codegen/wasm_gc/body.rs @@ -158,6 +158,7 @@ pub(super) fn emit_fn_body( self_wasm_idx: u32, registry: &TypeRegistry, effect_idx_lookup: &HashMap, + caller_fn_collector: &std::cell::RefCell, ) -> Result, WasmGcError> { let slots = SlotTable::build_for_fn(fd, registry, fn_map)?; let FnBody::Block(stmts) = fd.body.as_ref(); @@ -187,6 +188,7 @@ pub(super) fn emit_fn_body( params: &fd.params, binding_names: &binding_names, effect_idx_lookup, + caller_fn_collector, }; for (i, stmt) in stmts.iter().enumerate() { @@ -247,6 +249,35 @@ pub(super) fn emit_fn_body( Ok(slots.extra_locals(count_value_params(&fd.params))) } +/// Lazy-populated registry of caller_fn names actually emitted by the +/// codegen. Replaces the AST walker `fn_body_emits_effect_call` from +/// 0.16.2 — every site that calls `emit_caller_fn_idx` registers the +/// fn name here on demand, so the wasm output's caller-fn name table +/// contains exactly the fns whose bodies emit a caller_fn slot. Zero +/// false positives, zero rozjazdy walker↔codegen. +#[derive(Default)] +pub(super) struct CallerFnCollector { + /// Aver fn name → idx in the exported caller-fn table (0..N). + pub(super) idx_by_name: HashMap, + /// Insertion order — the host walks `__caller_fn_name(0..N)` and + /// the i-th entry must match `names[i]`. + pub(super) names: Vec, +} + +impl CallerFnCollector { + /// Get-or-insert a name, returning the i32 idx the codegen emits + /// at the call site. New names land at the end of `names`. + pub(super) fn register(&mut self, name: &str) -> u32 { + if let Some(&i) = self.idx_by_name.get(name) { + return i; + } + let i = self.names.len() as u32; + self.names.push(name.to_string()); + self.idx_by_name.insert(name.to_string(), i); + i + } +} + /// Per-fn lowering context — read-only state every emit fn needs. pub(super) struct EmitCtx<'a> { pub(super) fn_map: &'a FnMap, @@ -276,6 +307,12 @@ pub(super) struct EmitCtx<'a> { /// independent product anywhere — discovery only registers the /// three host imports if it sees one. pub(super) effect_idx_lookup: &'a HashMap, + /// Lazy collector for caller_fn names. `emit_caller_fn_idx` + /// registers the current fn name here on each effect call site; + /// the eventual `__caller_fn_name(idx)` body and data segments + /// are emitted from `names` after every fn body has been + /// produced. + pub(super) caller_fn_collector: &'a std::cell::RefCell, } impl<'a> EmitCtx<'a> { diff --git a/src/codegen/wasm_gc/body/builtins.rs b/src/codegen/wasm_gc/body/builtins.rs index 3079b7dd..9bea0b4a 100644 --- a/src/codegen/wasm_gc/body/builtins.rs +++ b/src/codegen/wasm_gc/body/builtins.rs @@ -60,7 +60,7 @@ pub(super) fn emit_dotted_builtin( for arg in args { emit_expr(func, arg, slots, ctx)?; } - super::emit::emit_caller_fn_global(func, ctx)?; + super::emit::emit_caller_fn_idx(func, ctx)?; func.instruction(&Instruction::Call(wasm_idx)); return Ok(()); } @@ -492,7 +492,7 @@ pub(super) fn emit_args_get_inline( ))?; // len = args_len() - super::emit::emit_caller_fn_global(func, ctx)?; + super::emit::emit_caller_fn_idx(func, ctx)?; func.instruction(&Instruction::Call(args_len_idx)); func.instruction(&Instruction::LocalSet(len_slot)); // i = len - 1 @@ -515,7 +515,7 @@ pub(super) fn emit_args_get_inline( func.instruction(&Instruction::BrIf(1)); // s = args_get(i) func.instruction(&Instruction::LocalGet(i_slot)); - super::emit::emit_caller_fn_global(func, ctx)?; + super::emit::emit_caller_fn_idx(func, ctx)?; func.instruction(&Instruction::Call(args_get_idx)); func.instruction(&Instruction::LocalSet(s_slot)); // acc = struct.new List { head: s, tail: acc } diff --git a/src/codegen/wasm_gc/body/emit.rs b/src/codegen/wasm_gc/body/emit.rs index 0b5f31bd..924b6f87 100644 --- a/src/codegen/wasm_gc/body/emit.rs +++ b/src/codegen/wasm_gc/body/emit.rs @@ -1322,7 +1322,7 @@ pub(super) fn emit_independent_product_unwrap( /// to match. fn emit_group_call(func: &mut Function, ctx: &EmitCtx<'_>, op: &str) { if let Some(idx) = ctx.effect_idx_lookup.get(op) { - if emit_caller_fn_global(func, ctx).is_err() { + if emit_caller_fn_idx(func, ctx).is_err() { // Should never trip — every fn def has a global allocated // in `TypeRegistry::build`. return; @@ -1339,7 +1339,7 @@ fn emit_group_call(func: &mut Function, ctx: &EmitCtx<'_>, op: &str) { fn emit_branch_marker(func: &mut Function, ctx: &EmitCtx<'_>, branch_idx: u32) { if let Some(idx) = ctx.effect_idx_lookup.get("__record_set_branch") { func.instruction(&Instruction::I64Const(branch_idx as i64)); - if emit_caller_fn_global(func, ctx).is_err() { + if emit_caller_fn_idx(func, ctx).is_err() { return; } func.instruction(&Instruction::Call(*idx)); @@ -2352,27 +2352,22 @@ pub(super) fn emit_string_match( Ok(()) } -/// Push the caller-fn String ref via `global.get`. Each fn def in -/// the program owns one immutable `(ref null $string)` global, -/// `array.new_data`-init from the fn-name passive segment at module -/// instantiation. The hot path is a single `global.get` per effect -/// call — zero alloc, ~3 bytes per call site. -pub(super) fn emit_caller_fn_global( +/// Push the caller-fn idx as an `i32` immediate. Lazy-registers the +/// current fn name with the collector so the post-emit phase knows +/// exactly which fn names to put in the exported caller-fn table. +/// Single source of truth: any code path that wants to label its +/// effect call site with the originating fn just calls this — no +/// AST walker, no rozjazdy walker↔codegen. Hot path: 2-3 bytes +/// (`i32.const `), zero allocation. +pub(super) fn emit_caller_fn_idx( func: &mut Function, ctx: &EmitCtx<'_>, ) -> Result<(), WasmGcError> { let idx = ctx - .registry - .caller_fn_globals - .get(ctx.self_fn_name) - .copied() - .ok_or_else(|| { - WasmGcError::Validation(format!( - "no caller_fn global registered for fn `{}`", - ctx.self_fn_name - )) - })?; - func.instruction(&Instruction::GlobalGet(idx)); + .caller_fn_collector + .borrow_mut() + .register(ctx.self_fn_name); + func.instruction(&Instruction::I32Const(idx as i32)); Ok(()) } diff --git a/src/codegen/wasm_gc/effects.rs b/src/codegen/wasm_gc/effects.rs index 1bd98a82..cd901cf4 100644 --- a/src/codegen/wasm_gc/effects.rs +++ b/src/codegen/wasm_gc/effects.rs @@ -411,7 +411,13 @@ impl EffectName { /// their interpreter stack frames, wasm-gc has to be told. pub(super) fn params(self, registry: &TypeRegistry) -> Result, WasmGcError> { let mut p = self.params_without_caller(registry)?; - p.push(any_ref_ty()); + // Trailing caller_fn arg is now an `i32` index into the + // exported caller-fn name table (built by the host at + // instantiation via `__caller_fn_count` + `__caller_fn_name`). + // 0.16.2 used `(ref null $string)` per-call — replaced to + // drop N globals + N start-fn allocs and to give the host + // a vector-index lookup instead of a per-call LM round-trip. + p.push(ValType::I32); Ok(p) } diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index b40b80e4..00062fe4 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -42,6 +42,15 @@ pub(super) fn emit_module( ) -> Result, WasmGcError> { let registry = TypeRegistry::build_with_handler(items, handler_name.is_some()); + // Lazy caller_fn name registry — populated during user-fn body + // emit by `emit_caller_fn_idx` call sites. Threaded into every + // `emit_fn_body` call via `EmitCtx::caller_fn_collector`. The + // post-emit phase reads `collector.names` to materialise the + // exported caller-fn name table (`__caller_fn_count` + + // `__caller_fn_name`) and the matching passive data segments. + let caller_fn_collector = + std::cell::RefCell::new(super::body::CallerFnCollector::default()); + let fn_defs: Vec<&FnDef> = items .iter() .filter_map(|it| match it { @@ -354,20 +363,6 @@ pub(super) fn emit_module( funcs.function(b.grow_type); } factory_exports.emit_function_entries(&mut funcs); - // `__init_globals`: wasm-level start fn that lazy-fills the - // caller_fn globals via `array.new_data`. Has to be a separate - // fn (not part of `_start`) because the host invokes `main` - // directly when both are exported, bypassing `_start` — - // wasm-level start fns are auto-run at instantiation regardless - // of which export the host calls. Allocated last so its idx is - // import_count + funcs.len() once the entry is appended. - let init_globals_fn_idx: Option = if !registry.caller_fn_global_order.is_empty() { - let idx = import_count + funcs.len(); - funcs.function(start_type_idx); - Some(idx) - } else { - None - }; module.section(&funcs); // ── Memory section (bridge LM only) ──────────────────────────── @@ -391,40 +386,10 @@ pub(super) fn emit_module( module.section(&memories); } - // ── Global section ───────────────────────────────────────────── - // One mutable `(ref null $string)` global per fn def, declared as - // `ref.null` and lazy-initialised at the top of `_start` via - // `array.new_data $string $segment 0 N`. The wasm-gc spec doesn't - // permit `array.new_data` in const-expr position, so the init has - // to land in a code body — `_start` runs once per instantiation - // and dominates every effect call site, which is what we need. - // Body emit pushes the caller-fn ref through - // `global.get $caller_fn_` at every effect call site — the - // alloc happens once at startup, the hot path costs one global - // load. - if !registry.caller_fn_global_order.is_empty() { - let string_idx = registry - .string_array_type_idx - .expect("caller_fn globals require the $string slot — TypeRegistry forces it on"); - let mut globals = wasm_encoder::GlobalSection::new(); - for _ in ®istry.caller_fn_global_order { - let init = wasm_encoder::ConstExpr::extended([Instruction::RefNull( - wasm_encoder::HeapType::Concrete(string_idx), - )]); - globals.global( - wasm_encoder::GlobalType { - val_type: wasm_encoder::ValType::Ref(wasm_encoder::RefType { - nullable: true, - heap_type: wasm_encoder::HeapType::Concrete(string_idx), - }), - mutable: true, - shared: false, - }, - &init, - ); - } - module.section(&globals); - } + // (caller_fn delivery moved from per-fn globals to an exported + // name table; segment append + `__caller_fn_*` exports are + // wired in the post-emit phase further down. Globals + their + // start-fn init are gone.) // Build the fn-name → wasm-fn-idx map. With K imports: // imports at idx 0..K @@ -536,14 +501,9 @@ pub(super) fn emit_module( } module.section(&exports); - // ── Start section ────────────────────────────────────────────── - // Wasm-level start fn — auto-runs once at instantiation, ahead of - // any host-invoked export. Used for caller_fn global init. - if let Some(idx) = init_globals_fn_idx { - module.section(&wasm_encoder::StartSection { - function_index: idx, - }); - } + // (No StartSection — 0.16.2's caller_fn globals init is gone; + // host reads the caller_fn name table via `__caller_fn_count` + // + `__caller_fn_name(i)` exports at instantiation instead.) // ── Data count section (must precede code when using passive // segments via array.new_data / data.drop). @@ -585,6 +545,7 @@ pub(super) fn emit_module( self_wasm_idx, ®istry, &effect_idx_lookup, + &caller_fn_collector, )?; let local_groups: Vec<(u32, ValType)> = extra_locals_dry.iter().map(|v| (1, *v)).collect(); @@ -596,6 +557,7 @@ pub(super) fn emit_module( self_wasm_idx, ®istry, &effect_idx_lookup, + &caller_fn_collector, )?; codes.function(&func); } @@ -658,32 +620,11 @@ pub(super) fn emit_module( factory_exports.emit_bodies(&mut codes, ®istry)?; - // `__init_globals` body — one `array.new_data → global.set` per - // registered caller_fn name. Walks `caller_fn_global_order` so - // global idx i in the section matches the `i`-th name in the - // walker output. Runs at instantiation via the StartSection - // emitted below. - if init_globals_fn_idx.is_some() { - let string_idx = registry - .string_array_type_idx - .expect("caller_fn globals require the $string slot"); - let mut init = Function::new([]); - for (idx, fn_name) in registry.caller_fn_global_order.iter().enumerate() { - let bytes = fn_name.as_bytes(); - let segment_idx = registry - .string_literal_segment(bytes) - .expect("fn-name passive segment registered alongside the global"); - init.instruction(&Instruction::I32Const(0)); - init.instruction(&Instruction::I32Const(bytes.len() as i32)); - init.instruction(&Instruction::ArrayNewData { - array_type_index: string_idx, - array_data_index: segment_idx, - }); - init.instruction(&Instruction::GlobalSet(idx as u32)); - } - init.instruction(&Instruction::End); - codes.function(&init); - } + // (caller_fn name-table body emit + segment append happen + // post-emit once the lazy collector has all names — TODO in + // step 2 of the 0.16.3 refactor; currently the wasm binary + // is missing `__caller_fn_count` / `__caller_fn_name` exports + // and the host falls back to "main" for every caller_fn slot.) module.section(&codes); diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index 8b0582a4..2f216988 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -115,16 +115,6 @@ pub(super) struct TypeRegistry { /// $string $segment_idx` with offset=0, size=len. pub(super) string_literals: Vec>, pub(super) string_literal_idx: HashMap, u32>, - /// Per-fn `(ref null $string)` global, init by `array.new_data` - /// from the fn-name passive segment. Codegen emits `global.get` - /// at every effect call site instead of allocating a fresh - /// `(array i8)` per call — module-instantiation pays the alloc - /// once, the runtime hot path costs one global load. - pub(super) caller_fn_globals: HashMap, - /// Emit order for the caller-fn globals, matched to the - /// idx values stored above. The `GlobalSection` walks this - /// vector so wasm idx 0..N matches the map values. - pub(super) caller_fn_global_order: Vec, /// Type names that must NOT be erased to their underlying /// primitive by the newtype optimisation. Populated with every /// record/variant used as a `Map` key — Map's open- @@ -699,33 +689,13 @@ impl TypeRegistry { collect_string_literals_in_fn(fd, &mut string_literals, &mut string_literal_idx); } } - // Pre-register fn names as passive String literals AND - // allocate `(ref null $string)` wasm globals — but only for - // fns whose body actually emits caller_fn at a call site (a - // dotted call `Foo.bar(...)` or an `?!`/`!` independent - // product, which lowers to group/branch markers). Pure fns - // and plain forwarders that only call other user fns don't - // need a slot. - let mut caller_fn_globals: HashMap = HashMap::new(); - let mut caller_fn_global_order: Vec = Vec::new(); - for item in items { - if let TopLevel::FnDef(fd) = item { - if !fn_body_emits_effect_call(fd) { - continue; - } - let bytes = fd.name.as_bytes().to_vec(); - string_literal_idx.entry(bytes.clone()).or_insert_with(|| { - let idx = string_literals.len() as u32; - string_literals.push(bytes); - idx - }); - caller_fn_globals.entry(fd.name.clone()).or_insert_with(|| { - let idx = caller_fn_global_order.len() as u32; - caller_fn_global_order.push(fd.name.clone()); - idx - }); - } - } + // Note: caller_fn name registration moved out of `TypeRegistry` + // — `body::CallerFnCollector` lazy-registers names during + // codegen (every site that calls `emit_caller_fn_idx` registers + // its self_fn_name on demand), and the post-emit phase appends + // the resulting names as fresh passive segments after the + // pre-walked literal segments above. Single source of truth, + // zero AST-walker false positives. // `Int.mod` lowers to a boxed `Result` whose Err // arm carries a fixed "Division by zero" message — register // its bytes as a passive segment so the emitter has an idx @@ -789,8 +759,6 @@ impl TypeRegistry { string_array_type_idx, string_literals, string_literal_idx, - caller_fn_globals, - caller_fn_global_order, non_newtypable_keys, } } @@ -2035,44 +2003,6 @@ fn fn_body_calls_int_mod(fd: &crate::ast::FnDef) -> bool { }) } -/// Returns true when the fn body contains anything that emits a -/// caller_fn slot at codegen — a dotted call (any `Attr(_, _)` -/// callee, including nested-module shape `Module.Sub.fn(args)`) or -/// an independent product (`?!` / `!`, lowers to group/branch -/// markers). Used to skip allocating a global for fns that never -/// need one. Conservative on dotted: builtin namespace calls like -/// `List.length` are also flagged; false positives cost one segment -/// + one global per fn, false negatives crash wasm validation. -fn fn_body_emits_effect_call(fd: &crate::ast::FnDef) -> bool { - use crate::ast::{Expr, FnBody, Stmt}; - fn walk(e: &Expr) -> bool { - match e { - Expr::FnCall(callee, args) => { - let dotted = matches!(&callee.node, Expr::Attr(_, _)); - dotted || walk(&callee.node) || args.iter().any(|a| walk(&a.node)) - } - Expr::IndependentProduct(_, _) => true, - Expr::Match { subject, arms } => { - walk(&subject.node) || arms.iter().any(|a| walk(&a.body.node)) - } - Expr::BinOp(_, l, r) => walk(&l.node) || walk(&r.node), - Expr::Attr(o, _) => walk(&o.node), - Expr::ErrorProp(i) => walk(&i.node), - Expr::TailCall(b) => b.args.iter().any(|a| walk(&a.node)), - Expr::List(xs) | Expr::Tuple(xs) => xs.iter().any(|x| walk(&x.node)), - Expr::RecordCreate { fields, .. } => fields.iter().any(|(_, e)| walk(&e.node)), - Expr::RecordUpdate { base, updates, .. } => { - walk(&base.node) || updates.iter().any(|(_, e)| walk(&e.node)) - } - Expr::Constructor(_, p) => p.as_deref().is_some_and(|x| walk(&x.node)), - _ => false, - } - } - let FnBody::Block(stmts) = fd.body.as_ref(); - stmts.iter().any(|stmt| match stmt { - Stmt::Binding(_, _, e) | Stmt::Expr(e) => walk(&e.node), - }) -} fn collect_string_literals_in_fn( fd: &crate::ast::FnDef, From df911fd90c22be721657e8e84cad213553adda2b Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 12:12:32 +0200 Subject: [PATCH 02/18] 0.16.3 step 2: __caller_fn_count + __caller_fn_name exports + host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires up the compiler-side and host-side ends of the caller_fn name table that step 1's lazy collector was already feeding. wasm-gc binaries now self-host their fn name list — no external metadata, no LM round-trip per call. Compiler: - Two new wasm types in `module.rs::compile_to_wasm_gc`: `__caller_fn_count: () -> i32` and `__caller_fn_name: (i32) -> (ref null $string)`. Allocated only when the program has the `$string` slot (i.e. any user fn def). - Two matching fn entries appended to the function section after factory exports; their idxs are exported as `__caller_fn_count` / `__caller_fn_name`. - Pre-pass over user fn bodies populates the lazy collector with every fn name that ever pushes caller_fn at a call site. Needed before the data count section emit because passive segment count is `string_literals.len() + collector.names.len()`. - `__caller_fn_count` body: `i32.const N; end`. - `__caller_fn_name(idx)` body: per-name `block { brif ne; emit; br outer; end }` chain. One arm per registered fn name, each materialising the matching String ref via `array.new_data` from a freshly-appended passive segment. Default arm returns `ref.null` for out-of-range idxs. - Caller_fn name segments appended to the data section after the pre-walked literal segments; data count bumped accordingly. Rust host (`run_wasm_gc.rs`): - New `RunWasmGcHost::caller_fn_table: Vec` field, populated at instance creation by `build_caller_fn_table` — calls `__caller_fn_count`, then walks `__caller_fn_name(0..count)`, decoding each ref via the existing `__rt_string_to_lm` bridge. Empty vector when the module doesn't export the table (programs with no fn defs). - `imports.rs::dispatch_aver_import` swaps the per-call LM round- trip for a vector index lookup: `params.last().i32() → caller_fn_ table[idx]`. Unused `lm_string_to_host` import removed. JS host (`tools/website/playground/wasm_host.js`): - `setInstance` calls `materialiseCallerFnTable(instance)` — same shape as the Rust side, walks `__caller_fn_count` + `__caller_fn_ name(0..count)`, decodes via `averToJs`, caches in `this.callerFnTable`. - Per effect callback's trailing arg renamed `callerRef → callerIdx` (Number now, not anyref). Helper `dec(...) → averToJs(ref)` swept to `callerFnFromIdx(idx) → callerFnTable[idx] || "main"`. Sizes (rebuild_playground.py with the fresh compiler): - snake: 4506 → 4421 B (-85 B, -1.9%) - tetris: 8723 → 8440 B (-283 B, -3.2%) - life: 7097 → 6897 B (-200 B, -2.8%) - wumpus: 5590 → 5312 B (-278 B, -5.0%) - doom: 23197 → 20890 B (-2307 B, -9.9%) - checkers: 23969 → 21617 B (-2352 B, -9.8%) - rogue: 28666 → 26173 B (-2493 B, -8.7%) Bigger games shed ~10% — the per-fn caller_fn globals (8 B decl + 9 B init body each) dropped, replaced by one `i32.const` per call site and a single name-table dispatch shared across all fns. Verified end-to-end: $ aver run --wasm-gc --record /tmp/cf.json examples/core/effects_explicit.av Hello, Ada! Goodbye, Ada! $ python3 -c '...' Console.print -> greet Console.print -> farewell 10/10 wasm-gc record-replay smokes pass; 7/7 prebuilt games rebuild + validate. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/module.rs | 154 +++++++++++++++++++-- src/main/run_wasm_gc.rs | 90 ++++++++++++ src/main/run_wasm_gc/imports.rs | 19 ++- tools/website/index.html | 16 +-- tools/website/playground/checkers.wasm | Bin 23969 -> 21617 bytes tools/website/playground/doom.wasm | Bin 23197 -> 20890 bytes tools/website/playground/index.html | 16 +-- tools/website/playground/life.wasm | Bin 7097 -> 6897 bytes tools/website/playground/rogue.wasm | Bin 28666 -> 26173 bytes tools/website/playground/snake.wasm | Bin 4506 -> 4421 bytes tools/website/playground/tetris.wasm | Bin 8723 -> 8440 bytes tools/website/playground/wasm/aver.js | 2 +- tools/website/playground/wasm/aver_bg.wasm | Bin 4665508 -> 4664936 bytes tools/website/playground/wasm_host.js | 143 +++++++++++-------- tools/website/playground/wumpus.wasm | Bin 5590 -> 5312 bytes 15 files changed, 355 insertions(+), 85 deletions(-) diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 00062fe4..4d186478 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -316,6 +316,37 @@ pub(super) fn emit_module( &effect_registry, )?; + // 10) Caller-fn name table exports. `__caller_fn_count() -> i32` + // and `__caller_fn_name(i32) -> ref null $string`. Host walks + // `0..count` once at instantiation, decodes each ref via the + // LM bridge, caches in a `Vec`. Per effect call: `i32` + // idx flows through `params.last()` → vector index lookup, + // no LM round-trip on the hot path. + // + // Allocated only when the program has the String slot (i.e. + // any fn def, since `needs_string` forces the slot whenever + // `has_fn_defs`). Programs without fns never emit caller_fn + // anywhere so the exports would be unused. + let caller_fn_table_types: Option<(u32, u32)> = + if let Some(string_type_idx) = registry.string_array_type_idx { + // count: () -> i32 + types.ty().function([], [ValType::I32]); + let count_type_idx = next_type_idx; + next_type_idx += 1; + // name: (i32) -> (ref null $string) + let string_ref_ty = ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(string_type_idx), + }); + types.ty().function([ValType::I32], [string_ref_ty]); + let name_type_idx = next_type_idx; + // Last type allocation in this fn — `next_type_idx` + // increment dropped to silence `unused_assignments`. + Some((count_type_idx, name_type_idx)) + } else { + None + }; + module.section(&types); // ── Import section ───────────────────────────────────────────── @@ -363,6 +394,18 @@ pub(super) fn emit_module( funcs.function(b.grow_type); } factory_exports.emit_function_entries(&mut funcs); + // Caller-fn name table fns — fixed-shape entries (count + name), + // their bodies land at the very end of the code section once + // `caller_fn_collector` has all names. Idxs are recorded so + // `module.section(&exports)` can wire them up without re-deriving + // the position. + let caller_fn_table_fns: Option<(u32, u32)> = caller_fn_table_types.map(|(c_ty, n_ty)| { + let count_fn_idx = import_count + funcs.len(); + funcs.function(c_ty); + let name_fn_idx = import_count + funcs.len(); + funcs.function(n_ty); + (count_fn_idx, name_fn_idx) + }); module.section(&funcs); // ── Memory section (bridge LM only) ──────────────────────────── @@ -499,17 +542,45 @@ pub(super) fn emit_module( ); } } + if let Some((count_fn_idx, name_fn_idx)) = caller_fn_table_fns { + exports.export("__caller_fn_count", ExportKind::Func, count_fn_idx); + exports.export("__caller_fn_name", ExportKind::Func, name_fn_idx); + } module.section(&exports); // (No StartSection — 0.16.2's caller_fn globals init is gone; // host reads the caller_fn name table via `__caller_fn_count` // + `__caller_fn_name(i)` exports at instantiation instead.) + // Pre-pass over user fn bodies — populates `caller_fn_collector` + // with every fn name that emits caller_fn at a call site. Needed + // before data count + data section emit because the count of + // passive segments is `string_literals + collector.names`, and + // data count section must precede the code section. Real body + // emit later in the code section calls `register` again with the + // same names; the collector is idempotent so the idx assignment + // matches what the call sites observed during this probe. + for (i, fd) in fn_defs.iter().enumerate() { + let self_wasm_idx = import_count + 1 + (i as u32); + let mut probe = Function::new([]); + let _ = emit_fn_body( + &mut probe, + fd, + &fn_map, + self_wasm_idx, + ®istry, + &effect_idx_lookup, + &caller_fn_collector, + )?; + } + let caller_fn_segment_count = caller_fn_collector.borrow().names.len() as u32; + // ── Data count section (must precede code when using passive // segments via array.new_data / data.drop). - if !registry.string_literals.is_empty() { + let total_segment_count = registry.string_literals.len() as u32 + caller_fn_segment_count; + if total_segment_count > 0 { let count = DataCountSection { - count: registry.string_literals.len() as u32, + count: total_segment_count, }; module.section(&count); } @@ -620,22 +691,89 @@ pub(super) fn emit_module( factory_exports.emit_bodies(&mut codes, ®istry)?; - // (caller_fn name-table body emit + segment append happen - // post-emit once the lazy collector has all names — TODO in - // step 2 of the 0.16.3 refactor; currently the wasm binary - // is missing `__caller_fn_count` / `__caller_fn_name` exports - // and the host falls back to "main" for every caller_fn slot.) + // `__caller_fn_count` + `__caller_fn_name` bodies. Emitted after + // every helper so their fn idxs land last in the code section, + // matching the function section allocation order. The collector + // is fully populated at this point — every user-fn body ran + // through the pre-pass and the real-emit pass. + if let Some((_count_fn_idx, _name_fn_idx)) = caller_fn_table_fns { + let names = caller_fn_collector.borrow(); + let string_idx = registry + .string_array_type_idx + .expect("caller_fn name table requires the $string slot"); + // Caller-fn name segments occupy the data section slot range + // [string_literals.len()..string_literals.len()+names.len()]; + // `array.new_data` in `__caller_fn_name(i)` reads from those + // idxs. + let segment_base = registry.string_literals.len() as u32; + + // __caller_fn_count: pure constant. + let mut count_fn = Function::new([]); + count_fn.instruction(&Instruction::I32Const(names.names.len() as i32)); + count_fn.instruction(&Instruction::End); + codes.function(&count_fn); + + // __caller_fn_name(idx) -> ref null $string. Switch on idx + // via `br_table`; each arm materialises the matching String + // ref via `array.new_data`. A trailing default arm returns + // ref.null for out-of-range idxs (host shouldn't pass them, + // but the wasm validator wants a fallthrough). + let string_ref_ty = ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(string_idx), + }); + let mut name_fn = Function::new([]); + let block_ty = wasm_encoder::BlockType::Result(string_ref_ty); + name_fn.instruction(&Instruction::Block(block_ty)); + for (i, fn_name) in names.names.iter().enumerate() { + let bytes = fn_name.as_bytes(); + // Inner block: if idx == i, this arm emits the ref and + // breaks out of the outer block. Otherwise falls through + // to the next arm. + name_fn.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty)); + // if local 0 != i { br 0 } — skip to next arm. + name_fn.instruction(&Instruction::LocalGet(0)); + name_fn.instruction(&Instruction::I32Const(i as i32)); + name_fn.instruction(&Instruction::I32Ne); + name_fn.instruction(&Instruction::BrIf(0)); + // Match: emit ref + break to outer. + name_fn.instruction(&Instruction::I32Const(0)); + name_fn.instruction(&Instruction::I32Const(bytes.len() as i32)); + name_fn.instruction(&Instruction::ArrayNewData { + array_type_index: string_idx, + array_data_index: segment_base + i as u32, + }); + name_fn.instruction(&Instruction::Br(1)); + name_fn.instruction(&Instruction::End); + } + // Default arm — out-of-range idx returns ref.null. + name_fn.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Concrete( + string_idx, + ))); + name_fn.instruction(&Instruction::End); + name_fn.instruction(&Instruction::End); + codes.function(&name_fn); + } module.section(&codes); // ── Data section ─────────────────────────────────────────────── // Passive segments holding String literal byte sequences. Emitted // last; `array.new_data $string $segment_idx` reads from these. - if !registry.string_literals.is_empty() { + // Order: pre-walked program literals first, caller_fn names + // second. `__caller_fn_name`'s body uses + // `segment_base = registry.string_literals.len()` so its arms + // hit the right slots regardless of how many literals the + // program has. + if total_segment_count > 0 { let mut data = DataSection::new(); for bytes in ®istry.string_literals { data.passive(bytes.iter().copied()); } + let names = caller_fn_collector.borrow(); + for fn_name in &names.names { + data.passive(fn_name.as_bytes().iter().copied()); + } module.section(&data); } diff --git a/src/main/run_wasm_gc.rs b/src/main/run_wasm_gc.rs index b7acece4..2da72f9b 100644 --- a/src/main/run_wasm_gc.rs +++ b/src/main/run_wasm_gc.rs @@ -193,6 +193,84 @@ pub(super) struct RunWasmGcHost { /// output. `None` is the production path — zero overhead beyond the /// `Option::is_some` check per effect call. pub(super) recorder: Option, + /// Caller-fn name table, materialised at instantiation by walking + /// `__caller_fn_count` + `__caller_fn_name(0..count)`. Per effect + /// call, `imports.rs::dispatch_aver_import` looks up + /// `caller_fn_table[idx]` to stamp the recorded effect's + /// `caller_fn` field. Cleared (kept empty) for modules that don't + /// export the table. + pub(super) caller_fn_table: Vec, +} + +/// Walk `__caller_fn_count` + `__caller_fn_name(0..count)` once at +/// instance creation, decode each name via the LM bridge, return the +/// resulting `Vec` so per-call dispatch can index into it +/// without per-call LM round-trips. Empty when the module doesn't +/// export the table (programs without effect-emitting fns). +#[cfg(feature = "wasm")] +fn build_caller_fn_table( + store: &mut wasmtime::Store, + instance: &wasmtime::Instance, +) -> Result, String> { + use wasmtime::Val; + let count_fn = match instance.get_func(&mut *store, "__caller_fn_count") { + Some(f) => f, + None => return Ok(Vec::new()), + }; + let name_fn = match instance.get_func(&mut *store, "__caller_fn_name") { + Some(f) => f, + None => return Ok(Vec::new()), + }; + let to_lm = match instance.get_func(&mut *store, "__rt_string_to_lm") { + Some(f) => f, + None => return Ok(Vec::new()), + }; + let memory = match instance.get_memory(&mut *store, "memory") { + Some(m) => m, + None => return Ok(Vec::new()), + }; + + let mut count_out = [Val::I32(0)]; + count_fn + .call(&mut *store, &[], &mut count_out) + .map_err(|e| format!("__caller_fn_count: {e:#}"))?; + let count = match count_out[0] { + Val::I32(n) => n.max(0) as usize, + _ => 0, + }; + + let mut out = Vec::with_capacity(count); + let mut name_out = [Val::AnyRef(None)]; + let mut len_out = [Val::I32(0)]; + for i in 0..count { + name_fn + .call(&mut *store, &[Val::I32(i as i32)], &mut name_out) + .map_err(|e| format!("__caller_fn_name({i}): {e:#}"))?; + // Decode via __rt_string_to_lm: writes bytes into LM at + // offset 0, returns the byte length on the i32 return. + let any_ref = match &name_out[0] { + Val::AnyRef(Some(r)) => Val::AnyRef(Some(*r)), + _ => { + out.push("main".to_string()); + continue; + } + }; + to_lm + .call(&mut *store, &[any_ref], &mut len_out) + .map_err(|e| format!("__rt_string_to_lm: {e:#}"))?; + let len = match len_out[0] { + Val::I32(n) => n.max(0) as usize, + _ => 0, + }; + let mut buf = vec![0u8; len]; + if len > 0 { + memory + .read(&*store, 0, &mut buf) + .map_err(|e| format!("read caller_fn name {i}: {e:#}"))?; + } + out.push(String::from_utf8_lossy(&buf).into_owned()); + } + Ok(out) } /// Walk the parsed AST for a `fn (...) -> T` definition and @@ -257,6 +335,7 @@ fn run_wasm_gc_with_host( RunWasmGcHost { program_args: program_args.to_vec(), recorder: recorder.take(), + caller_fn_table: Vec::new(), }, ); let mut linker: Linker = Linker::new(&engine); @@ -317,6 +396,17 @@ fn run_wasm_gc_with_host( .instantiate(&mut store, &module) .map_err(|e| format!("instantiate: {e:#}"))?; + // Materialise the caller-fn name table. The compiler exports + // `__caller_fn_count() -> i32` and `__caller_fn_name(i32) -> ref + // null $string` whenever any user fn might emit caller_fn (i.e. + // the program has fn defs); we walk `0..count` once, decode each + // ref via the LM bridge, and cache the strings in + // `RunWasmGcHost::caller_fn_table`. Per effect call, + // `imports.rs::dispatch_aver_import` reads the trailing `i32` + // arg as an idx into this vector — no LM round-trip per call. + let caller_fn_table = build_caller_fn_table(&mut store, &instance)?; + store.data_mut().caller_fn_table = caller_fn_table; + // Two entry shapes: // // - `entry_info = Some((fn_name, args))` — `aver run --wasm-gc -e diff --git a/src/main/run_wasm_gc/imports.rs b/src/main/run_wasm_gc/imports.rs index a76a1fb9..3d29bacd 100644 --- a/src/main/run_wasm_gc/imports.rs +++ b/src/main/run_wasm_gc/imports.rs @@ -60,7 +60,6 @@ pub(super) use factories::{ pub(super) use lm::lm_string_from_host; use http::{HttpVerb, http_body_dispatch, http_simple_dispatch}; -use lm::lm_string_to_host; pub(super) fn dispatch_aver_import( name: &str, @@ -78,8 +77,22 @@ pub(super) fn dispatch_aver_import( // dispatch chain. Falls back to "main" when the trailing arg // is missing or not decodable — keeps replay of older recorded // traces / hand-rolled wasm-gc modules working. - let caller_fn = lm_string_to_host(caller, params.last())? - .filter(|s| !s.is_empty()) + // Trailing arg is now an `i32` idx into the caller-fn name table + // the host materialised at instantiation (see + // `run_wasm_gc::build_caller_fn_table`). Vector index lookup + // replaces the per-call LM round-trip from 0.16.2. + let caller_fn_idx = params + .last() + .and_then(|v| match v { + wasmtime::Val::I32(n) => Some(*n), + _ => None, + }) + .unwrap_or(-1); + let caller_fn: String = caller + .data() + .caller_fn_table + .get(caller_fn_idx as usize) + .cloned() .unwrap_or_else(|| "main".to_string()); let caller_fn_ref: &str = &caller_fn; let real_params: &[wasmtime::Val] = if params.is_empty() { diff --git a/tools/website/index.html b/tools/website/index.html index b7f909e4..bfceb1dd 100644 --- a/tools/website/index.html +++ b/tools/website/index.html @@ -220,42 +220,42 @@

What Aver deliberately omits

Native WASM, shared runtime

-

Seven games compiled directly from Aver to WebAssembly GC. Engine handles GC and tail calls — no NaN-boxing, no custom heap. Snake ships at 4.4 KiB; a full roguelike with procedural generation is 28.0 KiB. Modern browsers only (Chrome 119+ / Firefox 120+ / Safari 18.2+).

+

Seven games compiled directly from Aver to WebAssembly GC. Engine handles GC and tail calls — no NaN-boxing, no custom heap. Snake ships at 4.3 KiB; a full roguelike with procedural generation is 25.6 KiB. Modern browsers only (Chrome 119+ / Firefox 120+ / Safari 18.2+).

diff --git a/tools/website/playground/checkers.wasm b/tools/website/playground/checkers.wasm index 4896977218e9e977d089b125e93f778dca496313..f3382aee386adc0ac7a553ca0d6dfdde6c30aaaf 100644 GIT binary patch delta 2278 zcmZwHdsGxv90&0Got*`CXMq70VR^`KGy{$rpkN8&-jRn22%;h=xVz$H6l}O4WGP!& zX_;Bw-q}NYnWSZ#`RrlOKRR{D=yf=q`psT#$L#ipC(3=MiCM}CHZn{)X=CENgJ^ugZ<&qKwv1+A0A;f zqA+=3)8=^0J<>mD1%?MKYltn6izhDI!~M4f28UT~+)_f3t;2xrMSP-!d>*5x%veu5M^sWaY}{=B2}RBNY`D+gn<$ zi*KV###$HB=J&VWwqsS>?RV_lH6FbaccH54ZnU@W#;Uf})tpz~gQ|N`X>VqS*|Fw6 z?7?2#j|Z?1=Gt}Z);@@3%Q`#vWBvMvqFC0orhCJN?gMxjkD%*O2vj|WYF_;~Hg2r# z>3ITA;wh}IuYVf7^@|pjFDhR^`!jeJ2k{)9#|wB7hj17#;bpvnS8@IIui*$98ed0a zLu1oXG&D7B+H?bLOBOF)O8W$5yn$mljyLfZPSoLT#PAMI;$57=X`DgtS)9XpyodMk z0Y1b9_yZpy_%S}grw9fBxirSbqeTmSBq}v0MU~6ZB#NqBd|$M{8IOt_ql${P8qFEpVHeXXshHs~)ty|t zZY;>-PTg_2s*BE5gH0(erqAb2QBj;{*hI~#RWr&`sJfdcP0JGH#rfc|mf{g5l_$q< z&`C)tTzolemMtnZr^(E8F8-R8mu8ETDlMd>Tpqh-c9;Fc<0bRyc&wzGj>)CvS=>$i z+&ZYG6vJS;!NtnbdAVGRkKm@xbLd>926K{1A1H0f<{sT;VplP9_zh;VZk2wx__@?) zM>J|4JzHN)zp2#HxR_a1NW;~Z`RM*T%f>SBhxn={nDYm;OlB|xhGIAj_2Lm^{t0cB zI9*#Q#%jyFSH~wPE>6}?M0uL7n~aVbS9XJ>D<< z>V65Z`_UUtfYUCNo+xD365lUzuHbsSmVR}u1YE9_0K1myppI8=JIO8{n^$*1N#+^4 zVkkWJtG^mFS98tWIXsgV!)$vo$fcI0J7~7C)1XJ)X(;*`=%*<4W^+e~d;d4)r2^O5 z1>{1>o}NT%ukT8`MmFUYGb(u&e8%*oe@BVcFPo2io})XRa>_HmfTopRsa?*+{`!)9 z?n}hK{BJxqSziqw&yTMv(7+0v@hJLt^b|#n+h+0tnggbP3p$}k)xWXBu)_tk z0LAgfoD%y?>!LMhrt-pRA8FygpO&ATl7DKfps6itI+PSu8K0>gRvG>E8>~U~)01m- z1N9!zHAiDyJR}WL6e+1BjZz#^bdn}1s+3HU<*8#h$Z`QgWr(gY`)9 zC31SDcUoTPntD3WeD*>e%Pn96R3;RKySK zcFlH2tnd~qvfLUN0-cApw1ur^YX`KUfzS>s(tcBD*b-lLUL$sIoST-~5l;?miG;@Z ch*;f|En0dEvA3rUUV&b(_`7GT80#(j2iw~K!vFvP delta 4526 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 B-cbMm delta 4309 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 is Aver? About WASM GitHub @@ -225,7 +225,7 @@

Native WASM

Tiny binaries - Snake ships at 4.4 KiB. Tetris is 8.5 KiB. A full roguelike with procedural generation is 28.0 KiB. Built with --target wasm-gc --optimize size — engine GC + native tail-calls; per-program binary, no shared runtime to fetch. + Snake ships at 4.3 KiB. Tetris is 8.2 KiB. A full roguelike with procedural generation is 25.6 KiB. Built with --target wasm-gc --optimize size — engine GC + native tail-calls; per-program binary, no shared runtime to fetch.
Capability-based imports diff --git a/tools/website/playground/life.wasm b/tools/website/playground/life.wasm index 8f169d522018e51076970ac68d9192cdf4ed26e1..627c2c1bb874e1dc64b7cd83cf103da2d2075e58 100644 GIT binary patch delta 768 zcmYk3yKfUg5XN`*p0>}<8pnwfllWqc^NwTZ#Y-e>iAYEkSV**I>o_9|`x25EA&N5< zHP#kTQ9uI~LY5E+i9QYDFQBJF(4iDC=S3pLXy^OQH?yygTR%6R-WvgMXb)T>mt_A@ zF5e?{)m4HZ09UC~AEhyrLExI;nsq|@pK^J?E?OthtD_Y?p^nt)zcfJI|Mh{oRJ@1V za8|_Wo!Ke1@xVKX9<@52bJ+I0eKI{H){u1l)Ri;I%k+T?_Sj+xHI^`rxQ@Fsao z<6LJZMg$|uMQ^l>=YBW(EW!CmHm_R4H5Muli})Qwfj6 zUIW1ow8=Gf2c0H5=PZcXgv0KTM3Kx(LL~{OEl8qB!jLRVqWsdOs%GnKh-al?N{~n^Y9DIdL;PJ!Rg-kG-+Cw-3re*9XIG Odkw$44{E2;^8W(f%%~^; delta 1002 zcmb7=J8u&~6ou!`zU{TMiE%LI;U%-=K@z}8oDg1-Edh!EAqY?**{jVaTE)9I_68vY z4mB0gh*yCq=x8V`5Qtv@6+)tcUqKxtxMLEDmSSeUd+wPtkDsLvbC1^g`k_iUz$1Sb z*t}a9ibzfNq)w*;o>Zd|mW0%_p3>=fI(Yi#$3hMb*ntQZ&t*loq6`&?S22_4Ruu#nhtiriACtc7Hn*u(~#+ERrKEOf7`1$CYg zizqv?Ef26{vINkn5L>Rmy&;8x1-&+wHZ7Af*)k}#4l@4Q95ooVXoXY=<3G$dS*(yN zfGeO=MyJX6%ha$UI*fl#by7_Fw|6}~ltG0kXqV95FN!4N57XxqCR;M=eoJ4WeN3@r zn`}`~kyOS@{Z(My$NisZim8^4!@!YXd~H_|7=N+rslhbM#F1kBbapt;bSo{+(C~~A zk)+WrU8dFydv!lcH@*YDtE^yiG>@Poj0q6RVF6;_xBwMlVh1L7T1Nz!*g7gey!)5{ zkyCU;bOLG05uYNGrXBH-2GWeMSvQeNj22HO@FXAJ0uo&(wvko13MV6tC`vP0&Mk%|G7?mO*LOUC#$?J^Bl3 z+*n<$`=EM$y? zQ<`QAO}N6H>u%2Dp4ut2#=xo?RI-a#Dc1MdhGONg-t)PB-@v)@lM-^@z$_s=-> z9ZoF3sh|OzV`B)m^CQ}6_VN={G``vl8`)rngPjppR&1XISlQ#O6A3+%1wK29v4}jw5YiL74Ut%kkltQ0Hg-Fe|D%^gx+C3pATmCFCx-6AI_}0j=kM!Ero`fr01nJYK+ycnL4#6}*bqa00L64Y+s{qi-SpHsbMK(`Gt>4VW@j^}(KYuGKE@~b6uI1I_#9v0OC%DBUAw-*+}HR9^Yae;TQAH1 zhQjaIz*!XXVUGvEentATWy3w_|5j;lOsBJ2K5tm*88e+NE}CWT0Iyt#rdBYxRX%`> z|Ce=~b92px55)cc>T7G$U)P5PI&Ev;hf9c{aLZQ-Ayj!ESLFe9Srv(U=7yt?iKuWb zqH3#japlG%bdS^6(J2#;!pqkHdK+<)9m`aI=xDu?comTjj|%nl>NfhtDO;3bI&Y#w zBF^QVS2Po!;`I=YNL633HAIiQl&^X(=9RPbfOw}VZCT^xi?|RMf*Mf0!Sj`(1}>7K>i6FeWoEUEN-V) zk?B>21CT=;L8LALDoG|hlLjgyOoa1mQcOf93SBeG#KWZ4d5p;>CPwYQ#~DeCYON=j zc$o-PCYkt{h*YMS_?c{`GR;J0(nKXq<;-r`k@ZpGus`F@>SggdIbSR;B#hF6RxuIC z&4lwMgOtl!b{-9^QjIxnDOa?d=J}SCY#F9*SP_dpAV`*>={?3CG*q-)nV9;ZRm>Wt k5(I0}gjmpWCN?MP8`8R^Rd&p27MlJ8)+n~9_vTFFUqm}C>;M1& delta 3350 zcmbu9TW}lY6~}cXIl9_jE4CH8oz_7b2+c4A+LG&4lQ;>9L|@M;qxE3=9qqcF}PkbnoAPKRf^r!hxZO;53|pg9m$UICSV?NKH?t z@R&IZk3hF5J_?=Xk%TK~n@HAY2XW&_Q4xWdhp^NYW z*cSp@x~EDo@&=5Iya{i?+wcy&3-1AA@5Aiu2QW7`a|ycUe+HM~=kOsY$}ixT@DY3r zqobpT5C010e+{3&!osKU8QA|0?3no+{sLdXUtv$Gt_}cB2%C0?@nQb%Na+8+`mSvK zywn~1jZp7*pLB=ammv7nY520#`3)9;Sn0+1b#(jv4Ji-aqBk9f)|f|dEnnm5fHil( zwdMv$E+@UQ(n~j=hfvHbxVCzeRuY|uzjqE}?7w|~S148|xI@WSHhJDuLUAw?Ffws;{*|e34#WMNe89~no!wG5JcEVK;iod>Jbh& z%0WjtL_p5djxs|apkj$cn~^dElv*NaMvw`pJR_j^SpuptM?mrO1k|)bKusSe@FOe` z_zyEoYKt)Uf>S&RGQZB^-i82)&+tIkYA{8C4 zTjL&-DCe=b7m2!BCS%A#Iv%g9Xe;qeNYuRLWIF+IA8H=dZiBcV(TjLH!~=*__au-j z(JCjZat9~s_|B?!7pDMfcXOgD_W-X$PxM|c(%w#SBJIAac7IiSfD>)~K~7ZUp(>rO z(iu+FjLnJiJT{eZ&EuS?|0g(6!tn&3BR#{Z1?gE%7U6SM@p(>^@M2Yafm1tbKjcI+`Xf#>ZZC4824CVtO}|{V zzQT#d`_(G_m=iVl8nj#Z?x@`BT%@W02`AEi%4rKyi4zrmgA)~b6XHP(rpj+|k#_Pn zr#7T_I8l{%IZ^8Os`Ng@Lm1q$Ru_nT0PXgbnJMqt=p`s!Cz@U>oLN+|@`Pe^>xvAG z8&?xdf(Xmy&Eg2lv0R4fOioA=G&tIbR^aQLs4uL$KvV6S(gY1=qDLpoW+7Z*EsMeX zpuV~sHCf)VR#I1txZ=j3D9ioKFvK~Aae1+zrc_fg6=r}?v!u4{e5l!irW&1RY5p&X zT5RCzpvrj4BDPHVcbhC{RBCai%;j3nl7btn&b!qSP0K-~ELa#`R>;e*sEXMw<{0=) zz1XiUF$jyMDM|~KnxU+whr}$? z%>gFrnIbfPcf*PYG;m?vJ*>>8vIa}=E+D8bX0!HdEE)<@CqtsCYgzkawso6NW`@L6 z8A3W%v~(UC%12oW{qdr?5=9gQ^7n zw6;`ky@5#5R8v$`W5|RktqWo{o7S})kMCkE&vbimbDIT?WHlrar>ZcCP9SU$xHkky zk(s=R+pZ#i*-?Cf^VEZ)&aIc!45%?w2Fe^lL+i!}#jw9__S^oJ4*NSTTcgmj(J1Eg zI0y+GmlG|~9kn)77bQl{wS{zMn1i#!i)@kEzio-$2<;maW@e3{@-Po*GJ~d0Ete6^ z>3Jq*hq7W3AILvi@p~33x`LlYg7@55QFNBWFDhX|L64E z#lSzVlF2e?sVL>oD&_pn+Wx0JfHMlMH707%Kpjjw(bjqcG}l;r7Ue!gy}C-}7i#V zfNZCrV*e;KXK(ahT2T@~THum71*1iuCE=Wq_^V!vKUtO`L4ZqTd~Ce~8BgqcHAw~I zTf~K3wj+tu=N2`jCNv!bhJh*L-}W9R-{BP;d%T*KOt2PFXUO;!uj(jR^do&l#&utN%GRlD94XE=il*P8Hp&~aBk$yhxobL!{G?D@qP$w(CvkP E0m9&tO8@`> delta 753 zcmb7=zi-n(6vywK&vEQ?+)L7=ZTc(q87TB8bjSch6A1*OMO_dBLbc^sYSrR~)~aA2 zL1JK})h!ha3``XaNL`tbSP??3EX?p1bm3ip`~g^Z?>_H)-}ml(U;Q_7FI!1T2r=LY zjc&I~LYksN&O{c{7-Fh-AvSTGczZe$?N6b5vYkFI8t?p#ec1Aq+$Tu3) z$-@m7>dueK+|euZuThmmkQUU*F$JqdpX>CP5cMhNtB>g+NkbSWCJd9jRKJ`@Xui&eFy7}A1<3?!k<&AzddVkDRIuqGe}|4c z?g7T%?%f4x2^*&)X2dkE9H)!Zd#VG!ievqFpZ-U7ip zaJ+-4ok3B9i%Lm@jZoI$A@pl-5GsK#%OO<*K+^VsfFc!;1_R@4cPL;;x<@TI|Jh;P zB#+Wat?8fAXplqAKy56rko2332YJpv(7H-Ruf?PGZj{E!?IdagZ*OmPmZF`k8OPwB wC-;)48QAe!5^p?+HaEeGI-MkKuWYTS+Nl1Bn|&->k2fH%Da+pHG{CjNBAs=n`_Uwl94J-bpX#{DXY5Aq^Xv@ignj6)tBv3 zAy>5W=6WetETc|$Q;OhDw^`1__tj>oC9Al+RxtVKfRJ*oV4E9x+g?Ze-RJUSr&C^9 zj1R`(iYE>v5?x&f4-F-gNnO_u52sS8BO~#n-N%lfNHb$}Y<%Km&#B3t-qXF^XF9}n z_T2fY3l}e4hWYyRj6SPhnY(&zexYx%f62`B_gUAoJ|D*TKCSyoX8qoikVd2<13%C) zLlo%}b6=|uF>@zL4Eh2|L;r%bK>vo6p&!wr(9dgy7*i->h}CTdRar-yQc$4_n*k!x zc^JXQZbM=2H!TwMB0qCOp@`!}s<%1_tNor4&or<)&$Mk8@GRt680P)YUhi25jOXU> z4|^5>+G7a52~bVS>rMZtX}f$?%PbKi#gFsuFwId~@v&;Sg#HzY|C z8BMg`Kn8rtqj1L{Q)v*IO1o$Z?gW|!O(8>&MWfyCJD?~UVt7JH^Y>{`w=dyckoiye zlZ;p04~@g)tE8b;e@H7q!-mw5BlVnkz{8s&ghx&g))si992;`gnWjX*Q4rI}XN=(I z*3r06`jUzYkTw`Vqe&v delta 1309 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 j%8JW?Lbn#KO={!w%3Gmgs~O+kRX|iGWv|E`eX delta 475519 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

w0ufo4B_ig{nH<5Z%K^Tb5 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�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 i32` and + /// `__caller_fn_name(i32) -> ref null $string`; we walk + /// `0..count`, decode each ref via `averToJs` (which uses the + /// existing LM bridge), cache the JS strings in an array. + /// Per effect call the trailing `i32` arg indexes into this + /// array — no LM round-trip on the hot path. + materialiseCallerFnTable(instance) { + const exports = instance.exports; + if (typeof exports.__caller_fn_count !== "function" || + typeof exports.__caller_fn_name !== "function") { + return []; + } + const count = exports.__caller_fn_count(); + const out = []; + for (let i = 0; i < count; i++) { + const ref = exports.__caller_fn_name(i); + out.push(ref == null ? "main" : this.averToJs(ref)); + } + return out; + } + + callerFnFromIdx(idx) { + if (typeof idx !== "number") return "main"; + const name = this.callerFnTable && this.callerFnTable[idx]; + return name || "main"; } setTerminalSize(cols, rows) { @@ -273,25 +302,25 @@ export class AverBrowserHost { createImports() { // Every effect import declares a trailing `caller_fn: // any_ref` param now (see `effects.rs::params`). Each - // callback below picks it up as `callerRef`, decodes via + // callback below picks it up as `callerIdx`, decodes via // LM transport, and pipes the resulting JS string through // `recordOrDispatch` as the recorder's caller_fn stamp. // Pure imports (`float_*`) and the group markers ignore // the trailing arg — JS just lets the extra value drop on // the floor. - const dec = (callerRef) => this.averToJs(callerRef); + const dec = (callerIdx) => this.averToJs(callerIdx); return { aver: { - args_len: (callerRef) => + args_len: (callerIdx) => this.recordOrDispatch( "Args.len", [], () => BigInt(this.programArgs.length), (json) => BigInt(json ?? 0), (v) => Number(v), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - args_get: (index, callerRef) => { + args_get: (index, callerIdx) => { const idx = Number(index); return this.recordOrDispatch( "Args.get", @@ -308,10 +337,10 @@ export class AverBrowserHost { idx >= 0 && idx < this.programArgs.length ? this.programArgs[idx] : "", - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - console_print: (sref, callerRef) => { + console_print: (sref, callerIdx) => { const text = this.averToJs(sref); this.recordOrDispatch( "Console.print", @@ -319,10 +348,10 @@ export class AverBrowserHost { () => this.postConsole("stdout", text), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - console_error: (sref, callerRef) => { + console_error: (sref, callerIdx) => { const text = this.averToJs(sref); this.recordOrDispatch( "Console.error", @@ -330,10 +359,10 @@ export class AverBrowserHost { () => this.postConsole("stderr", text), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - console_warn: (sref, callerRef) => { + console_warn: (sref, callerIdx) => { const text = this.averToJs(sref); this.recordOrDispatch( "Console.warn", @@ -341,10 +370,10 @@ export class AverBrowserHost { () => this.postConsole("stderr", text), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - console_read_line: (callerRef) => { + console_read_line: (callerIdx) => { const exports = this.instance.exports; return this.recordOrDispatch( "Console.readLine", @@ -370,63 +399,63 @@ export class AverBrowserHost { : ""; return { $ok: peek }; }, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - random_int: (min, max, callerRef) => + random_int: (min, max, callerIdx) => this.recordOrDispatch( "Random.int", [Number(min), Number(max)], () => chooseRandomInt(min, max), (json) => BigInt(json ?? 0), (v) => Number(v), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - random_float: (callerRef) => + random_float: (callerIdx) => this.recordOrDispatch( "Random.float", [], () => Math.random(), (json) => Number(json ?? 0), (v) => Number(v), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - time_unix_ms: (callerRef) => + time_unix_ms: (callerIdx) => this.recordOrDispatch( "Time.unixMs", [], () => BigInt(Date.now()), (json) => BigInt(json ?? 0), (v) => Number(v), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - time_now: (callerRef) => + time_now: (callerIdx) => this.recordOrDispatch( "Time.now", [], () => this.jsToAver(new Date().toISOString()), (json) => this.jsToAver(json ?? ""), () => new Date().toISOString(), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - time_sleep: (millis, callerRef) => + time_sleep: (millis, callerIdx) => this.recordOrDispatch( "Time.sleep", [Number(millis)], () => sleepMillis(millis), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), // Float math is pure — no recording, no replay. The // wasm-gc imports list these because the engine // doesn't expose `f64.sin` directly. The trailing - // `callerRef` arg is ignored. - float_sin: (x, _callerRef) => Math.sin(x), - float_cos: (x, _callerRef) => Math.cos(x), - float_atan2: (y, x, _callerRef) => Math.atan2(y, x), - float_pow: (b, e, _callerRef) => Math.pow(b, e), - terminal_enable_raw_mode: (callerRef) => + // `callerIdx` arg is ignored. + float_sin: (x, _callerIdx) => Math.sin(x), + float_cos: (x, _callerIdx) => Math.cos(x), + float_atan2: (y, x, _callerIdx) => Math.atan2(y, x), + float_pow: (b, e, _callerIdx) => Math.pow(b, e), + terminal_enable_raw_mode: (callerIdx) => this.recordOrDispatch( "Terminal.enableRawMode", [], @@ -436,9 +465,9 @@ export class AverBrowserHost { }, () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_disable_raw_mode: (callerRef) => + terminal_disable_raw_mode: (callerIdx) => this.recordOrDispatch( "Terminal.disableRawMode", [], @@ -448,18 +477,18 @@ export class AverBrowserHost { }, () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_clear: (callerRef) => + terminal_clear: (callerIdx) => this.recordOrDispatch( "Terminal.clear", [], () => this.terminal.clear(), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_move_to: (x, y, callerRef) => { + terminal_move_to: (x, y, callerIdx) => { const xi = Number(x); const yi = Number(y); this.recordOrDispatch( @@ -468,10 +497,10 @@ export class AverBrowserHost { () => this.terminal.moveTo(xi, yi), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - terminal_print: (sref, callerRef) => { + terminal_print: (sref, callerIdx) => { const text = this.averToJs(sref); this.recordOrDispatch( "Terminal.print", @@ -479,10 +508,10 @@ export class AverBrowserHost { () => this.terminal.print(text), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - terminal_set_color: (sref, callerRef) => { + terminal_set_color: (sref, callerIdx) => { const color = this.averToJs(sref); this.recordOrDispatch( "Terminal.setColor", @@ -493,19 +522,19 @@ export class AverBrowserHost { ), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - terminal_reset_color: (callerRef) => + terminal_reset_color: (callerIdx) => this.recordOrDispatch( "Terminal.resetColor", [], () => this.terminal.resetColor(), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_read_key: (callerRef) => { + terminal_read_key: (callerIdx) => { const exports = this.instance.exports; return this.recordOrDispatch( "Terminal.readKey", @@ -529,10 +558,10 @@ export class AverBrowserHost { ? { $some: head } : { $none: true }; }, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - terminal_size: (callerRef) => { + terminal_size: (callerIdx) => { const exports = this.instance.exports; const cols = this.terminal.cols; const rows = this.terminal.rows; @@ -557,47 +586,47 @@ export class AverBrowserHost { fields: { width: cols, height: rows }, }, }), - dec(callerRef), + this.callerFnFromIdx(callerIdx), ); }, - terminal_hide_cursor: (callerRef) => + terminal_hide_cursor: (callerIdx) => this.recordOrDispatch( "Terminal.hideCursor", [], () => this.terminal.hideCursor(), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_show_cursor: (callerRef) => + terminal_show_cursor: (callerIdx) => this.recordOrDispatch( "Terminal.showCursor", [], () => this.terminal.showCursor(), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), - terminal_flush: (callerRef) => + terminal_flush: (callerIdx) => this.recordOrDispatch( "Terminal.flush", [], () => this.postTerminalSnapshot(), () => undefined, () => null, - dec(callerRef), + this.callerFnFromIdx(callerIdx), ), // Independent-product structural-scope markers — same // contract the wasm-gc CLI host enforces. Trailing - // `callerRef` ignored (group state lives in the + // `callerIdx` ignored (group state lives in the // recorder, not in trace records). - record_enter_group: (_callerRef) => { + record_enter_group: (_callerIdx) => { this.recorder.enterGroup(); }, - record_set_branch: (i, _callerRef) => { + record_set_branch: (i, _callerIdx) => { this.recorder.setBranch(Number(i)); }, - record_exit_group: (_callerRef) => { + record_exit_group: (_callerIdx) => { this.recorder.exitGroup(); }, }, diff --git a/tools/website/playground/wumpus.wasm b/tools/website/playground/wumpus.wasm index aeba3498da21f70595e3bd3c6070194c62833a87..b982609aee6eff0eac094a2174c218aaa8e2460a 100644 GIT binary patch delta 604 zcmYk(yN=U96b9fq<4e3_2WA7&+Hux#ZoqEBZn9jKJDFmUsE{ZS5`}F!Mv9c!!d%Lj z2cTf_3-AtZQczG(BC$w_H=v-TqDaGd?SjVh>l^t$$MVm?=biVfL+}y!#6`%(OJ4(D zb77GKB;d;~g2d%#4#IMoz|zHGg81d}uP+vNew#ponIL*^f|$f!IE@z0EPRFR^gEn? z+CKG7KMG$)^T3G%Z|clvVSqZqc+}|j`WG%<+A+=Dfj+#vx3BB^Xlxu@xq7YHG_D(s z!y7km-M({o`<`{b)v~rF2>{;G%3pCe^YJ6Rx9K>(H=hTw^E`C?=rtcZrvF>$Edq3) zL658&qwJIT1Ov;?Rh|jJ(ulPA^O%@j1{7>WxAK!ArmQ#Qvo=~|B-7qnsbJb?={I6l z7^Z+HEd4{;Ql9gqXZvau3rwKtM`ak)ELB4`Ffnhe8LEPKF{PF|1DFzCzQxN8CY=uI z^V}$Ni)XXU1D>0Cre|p9M~81h5Q7KeBwvc~u_slXf5-3Y2O$v=lhqhuU$)VaI4Re+ zG*Ip0ol;tbVBS)SU5HQ!)y9%c_~@9>6_|M$mH Mv`C|RJzdqUe`f!LGXMYp delta 873 zcmaKqJ8#oa6o7r~#CC2Ra8M=Eq|M7tn>J0;2k%GQ2!vFrRH#TS42|ntHKwth+D@x3 zF&kqk*^&4SSr8Kgj3AH@e}SUjU&s=#Ro?vB(}lsCOG4JDue6(Nxc)oufXAE3GB#c5ePzofr3zEz?LrQtca;HQxby4fVJri zSX*TbGYpbwW*M-qDg#zLH?EmyfTOp>nm+zD3p$T7g=KY?RS^~$q!Aipv!n~C;;5GO z7*Y&rMUN+wXVU8EZ~BxE6ArcZJA1C@1F`&O`;}o*CjiBBU3<&&gUx{2AhS2Mg9enI zWga|6Z#iZFk`r_yoiIDpJcy9k?h$>~HBP7p#P7In(DXd_7?K7HPc7f4ZBUN;cCYVm zQ`7MFP9V{ Date: Wed, 6 May 2026 12:17:22 +0200 Subject: [PATCH 03/18] 0.16.3 step 2: rustfmt fix CI Format step rejected the multi-line `caller_fn_collector` let- binding and a stray blank line in `types.rs`. Auto-applied `cargo fmt --all`. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/module.rs | 3 +-- src/codegen/wasm_gc/types.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 4d186478..8a31920e 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -48,8 +48,7 @@ pub(super) fn emit_module( // post-emit phase reads `collector.names` to materialise the // exported caller-fn name table (`__caller_fn_count` + // `__caller_fn_name`) and the matching passive data segments. - let caller_fn_collector = - std::cell::RefCell::new(super::body::CallerFnCollector::default()); + let caller_fn_collector = std::cell::RefCell::new(super::body::CallerFnCollector::default()); let fn_defs: Vec<&FnDef> = items .iter() diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index 2f216988..560ea4e2 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -2003,7 +2003,6 @@ fn fn_body_calls_int_mod(fd: &crate::ast::FnDef) -> bool { }) } - fn collect_string_literals_in_fn( fd: &crate::ast::FnDef, out: &mut Vec>, From 87a13387750040dcb89abb53f139d2cfe63a2a17 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 13:26:02 +0200 Subject: [PATCH 04/18] =?UTF-8?q?0.16.3:=20backend=20bugs=201-3=20?= =?UTF-8?q?=E2=80=94=20record/sum=20list-hash=20+=20recursive=20sum=20eq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three related fixes that lift the wasm-gc backend's "Int/Float/Bool/String only" cap on record + sum field types in hash and equality dispatch. ## Bug 1: list-hash on records (lists.rs:2073) `emit_list_hash` was matching on the surface element string (`elem.trim()`), so `List` where `Box(n: Int)` is newtype- optimised to i64 reached the `other` arm and panicked with a "file at github" comment that named the right culprit but didn't fix it. The companion `let _ = kind;` line was a TODO marker — the kind enum was supposed to be the dispatch axis. Fix: dispatch by `kind` (`ListEqKind`) instead of the elem string. Newtype-erased records resolve to their underlying primitive arm; nominal record/sum elements get a new `RecordEq(name)` arm that calls `emit_record_inline_hash` over the field list, and sum elements get a `SumEq(parent)` arm that calls a new `emit_sum_inline_hash` walking variants in `emit_sum_eq_inline`'s sorted order (so eq + hash agree on which variant to inspect first). The same shape was duplicated in `emit_vec_hash` (`lists.rs: 2443`) — fixed identically: dispatch by kind, route record/sum to the new inline emitters. ## Bug 2: list / vector hash for sum types `emit_sum_inline_hash` materialises an i32 hash by mixing the matched variant's `type_idx` (as a stable tag — empty variants of the same parent get distinct hashes) and DJB2-folding each primitive field. Variants are disjoint subtypes of the parent struct, so at most one `ref.test` succeeds per call; non-matched arms test to false and skip silently. Restrictions inherited from `list_eq_kind`: variant fields must all be `{Int, Bool, Float, String}`. Recursive ref / nested record fields surface as `Validation` errors; bug 3 lifts that on the eq side. ## Bug 3: recursive sum eq (lists.rs:1748, maps.rs:2300) `emit_record_eq_inline` and `emit_sum_eq_inline` rejected any field type outside `{Int, Float, Bool, String}` with `WasmGcError::Unimplemented("phase 4 — …")`. That blocked every recursive nominal type — `Tree.Leaf | Tree.Node(Int, Tree, Tree)`, `List`-cell shapes, mutually-recursive sums. Fix: thread two extra params into both inline emitters — `eq_helper_fn_idx: &HashMap` and `self_fn_idx: Option`. The dispatch grows two new arms: - `field_ty == self_name && self_fn_idx.is_some()` → `Call(self_fn_idx)`. Recursive ref to the same type, e.g. `Tree.Node` carrying `Tree` fields. - `eq_helper_fn_idx.contains_key(field_ty)` → `Call(idx)`. Nested nominal type with its own `__eq_` helper. Field refs are subtypes of `eqref` so the implicit upcast at the call site is fine. The body emit in `body/eq_helpers.rs::EqHelperRegistry::emit_ helper_bodies` snapshots `self.slots → HashMap<&str, fn_idx>` once and threads it + the per-helper `self_fn_idx` into the inline emitters. List.contains call sites (lists.rs:1546/1559) pass empty maps + `None` — `__eq_` field dispatch only fires inside helper bodies, not inline `List.contains`. (Lifting that covers fewer use cases and is a follow-up.) ## Verified - Bug 1 repro (`List` with `Box(n: Int)` record): wasm-gc matches VM (`found: true`). - Bug 2 repro (`List` with empty-variant sum): wasm-gc matches VM (`found: true`). - Bug 3 repro (`Tree.Node(Int, Tree, Tree); t1 == t2`): wasm-gc matches VM (`eq: true`). Recursive call into the same `__eq_Tree` helper resolves through `self_fn_idx`. - 10/10 wasm-gc record-replay smokes pass. ## Followup Bugs 4 (generic E) and 5 (ref/eqref) — TBD in next commits. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/eq_helpers.rs | 39 ++- src/codegen/wasm_gc/lists.rs | 445 ++++++++++++++++++++++--- 2 files changed, 426 insertions(+), 58 deletions(-) diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index 09b28f8a..9b47a446 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -116,17 +116,42 @@ impl EqHelperRegistry { registry: &TypeRegistry, string_eq_fn_idx: Option, ) -> Result<(), WasmGcError> { + // Snapshot type_name → fn_idx so the inline emitters can + // dispatch nested record/sum fields by `Call(idx)` instead + // of erroring on `Unimplemented`. Self-recursive fields + // (parent==field) get their own `self_fn_idx` argument so + // recursive sum/record types (Tree.Node holding Tree, …) + // resolve to a recursive call into the same helper. + let helper_idx_map: HashMap = self + .slots + .iter() + .map(|(n, (fn_idx, _))| (n.clone(), *fn_idx)) + .collect(); for name in &self.order { let kind = self.kinds[name]; + let self_fn_idx = self.slots.get(name).map(|(f, _)| *f); let mut f = Function::new(Vec::new()); - // Local 0 = lhs, local 1 = rhs (function params). Reuse the - // inline emitters from `lists.rs` which expect both operands - // already stashed in named slots — no extra prologue needed. match kind { - EqKind::Sum => emit_sum_eq_inline(&mut f, name, registry, 0, 1, string_eq_fn_idx)?, - EqKind::Record => { - emit_record_eq_inline(&mut f, name, registry, 0, 1, string_eq_fn_idx)? - } + EqKind::Sum => emit_sum_eq_inline( + &mut f, + name, + registry, + 0, + 1, + string_eq_fn_idx, + &helper_idx_map, + self_fn_idx, + )?, + EqKind::Record => emit_record_eq_inline( + &mut f, + name, + registry, + 0, + 1, + string_eq_fn_idx, + &helper_idx_map, + self_fn_idx, + )?, } f.instruction(&Instruction::End); codes.function(&f); diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 122bcf6d..94b1df93 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -1543,7 +1543,16 @@ fn emit_list_contains( f.instruction(&Instruction::LocalSet(3)); f.instruction(&Instruction::LocalGet(1)); f.instruction(&Instruction::LocalSet(4)); - emit_record_eq_inline(&mut f, record_name, registry, 3, 4, string_eq_fn_idx)?; + emit_record_eq_inline( + &mut f, + record_name, + registry, + 3, + 4, + string_eq_fn_idx, + &std::collections::HashMap::new(), + None, + )?; } ListEqKind::SumEq(parent_name) => { // Same scratch dance as RecordEq — both ref.test and @@ -1556,7 +1565,16 @@ fn emit_list_contains( f.instruction(&Instruction::LocalSet(3)); f.instruction(&Instruction::LocalGet(1)); f.instruction(&Instruction::LocalSet(4)); - emit_sum_eq_inline(&mut f, parent_name, registry, 3, 4, string_eq_fn_idx)?; + emit_sum_eq_inline( + &mut f, + parent_name, + registry, + 3, + 4, + string_eq_fn_idx, + &std::collections::HashMap::new(), + None, + )?; } _ => { f.instruction(&Instruction::LocalGet(2)); @@ -1617,6 +1635,8 @@ pub(super) fn emit_record_eq_inline( head_local: u32, needle_local: u32, string_eq_fn_idx: Option, + eq_helper_fn_idx: &std::collections::HashMap, + self_fn_idx: Option, ) -> Result<(), WasmGcError> { let record_idx = registry .record_type_idx(record_name) @@ -1649,24 +1669,43 @@ pub(super) fn emit_record_eq_inline( }); // emit per-field eq → i32 match field_ty.trim() { - "Int" => f.instruction(&Instruction::I64Eq), - "Bool" => f.instruction(&Instruction::I32Eq), - "Float" => f.instruction(&Instruction::F64Eq), + "Int" => { + f.instruction(&Instruction::I64Eq); + } + "Bool" => { + f.instruction(&Instruction::I32Eq); + } + "Float" => { + f.instruction(&Instruction::F64Eq); + } "String" => { let eq_fn = string_eq_fn_idx.ok_or(WasmGcError::Validation( "List.contains record field of String type needs \ __wasmgc_string_eq registered" .into(), ))?; - f.instruction(&Instruction::Call(eq_fn)) + f.instruction(&Instruction::Call(eq_fn)); + } + other if other == record_name && self_fn_idx.is_some() => { + // Recursive ref to the same record — call self. + f.instruction(&Instruction::Call(self_fn_idx.unwrap())); } - _ => { - return Err(WasmGcError::Unimplemented( - "phase 4 — record field type in List.contains \ - not in {Int, Float, Bool, String}", - )); + other if eq_helper_fn_idx.contains_key(other) => { + // Nested nominal type with its own __eq_ helper + // — call by fn idx. Field refs are subtypes of + // eqref so the implicit upcast at the call site + // is fine. + let idx = eq_helper_fn_idx[other]; + f.instruction(&Instruction::Call(idx)); } - }; + other => { + return Err(WasmGcError::Validation(format!( + "record `{record_name}` field type `{other}` has no eq dispatch \ + (not in {{Int, Float, Bool, String}}, no `__eq_{other}` helper, \ + not self-recursive)" + ))); + } + } if i > 0 { f.instruction(&Instruction::I32And); } @@ -1686,6 +1725,8 @@ pub(super) fn emit_sum_eq_inline( head_local: u32, needle_local: u32, string_eq_fn_idx: Option, + eq_helper_fn_idx: &std::collections::HashMap, + self_fn_idx: Option, ) -> Result<(), WasmGcError> { // Collect all variants of this sum (use a stable order — names // sorted ascending — so two compiler runs produce identical wasm). @@ -1733,24 +1774,44 @@ pub(super) fn emit_sum_eq_inline( field_index: i as u32, }); match field_ty.trim() { - "Int" => f.instruction(&Instruction::I64Eq), - "Bool" => f.instruction(&Instruction::I32Eq), - "Float" => f.instruction(&Instruction::F64Eq), + "Int" => { + f.instruction(&Instruction::I64Eq); + } + "Bool" => { + f.instruction(&Instruction::I32Eq); + } + "Float" => { + f.instruction(&Instruction::F64Eq); + } "String" => { let eq_fn = string_eq_fn_idx.ok_or(WasmGcError::Validation( "List.contains sum field of String type needs \ __wasmgc_string_eq registered" .into(), ))?; - f.instruction(&Instruction::Call(eq_fn)) + f.instruction(&Instruction::Call(eq_fn)); + } + other if other == parent_name && self_fn_idx.is_some() => { + // Recursive ref to the same sum (Tree.Node carrying + // Tree fields, ditto Cons-cell shapes) — call self. + f.instruction(&Instruction::Call(self_fn_idx.unwrap())); } - _ => { - return Err(WasmGcError::Unimplemented( - "phase 4 — sum-variant field type in List.contains \ - not in {Int, Float, Bool, String}", - )); + other if eq_helper_fn_idx.contains_key(other) => { + // Nested record/sum field with its own __eq_ + // helper — dispatch by fn idx. Field refs are + // subtypes of eqref, the call's typed args + // accept implicit upcast. + let idx = eq_helper_fn_idx[other]; + f.instruction(&Instruction::Call(idx)); } - }; + other => { + return Err(WasmGcError::Validation(format!( + "sum `{parent_name}` variant field type `{other}` has no eq \ + dispatch (not primitive, no `__eq_{other}` helper, not \ + self-recursive)" + ))); + } + } if i > 0 { f.instruction(&Instruction::I32And); } @@ -2023,12 +2084,47 @@ fn emit_list_hash( let list_idx = list_idx_of(canonical, registry)?; let _ = string_eq_fn_idx; let elem = TypeRegistry::list_element_type(canonical).unwrap(); - // params: 0=l. locals: 1=cur, 2=h. + // params: 0=l. locals: 1=cur, 2=h. Plus per-kind extras for + // record / sum element hash dispatch (3=elem_ref, 4=elem_hash + // accumulator). let list_ref = ValType::Ref(RefType { nullable: true, heap_type: HeapType::Concrete(list_idx), }); - let mut f = Function::new([(1, list_ref), (1, ValType::I32)]); + let mut locals: Vec<(u32, ValType)> = vec![(1, list_ref), (1, ValType::I32)]; + match &kind { + ListEqKind::RecordEq(record_name) => { + let r_idx = registry.record_type_idx(record_name).ok_or( + WasmGcError::Validation(format!( + "list hash for `List<{record_name}>`: record not registered" + )), + )?; + let r_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(r_idx), + }); + locals.push((1, r_ref)); // 3 = elem_ref + locals.push((1, ValType::I32)); // 4 = elem_hash + } + ListEqKind::SumEq(_) => { + // Sum types lower to `(ref null eq)` (per + // `types.rs::aver_to_wasm` for sum-parent surface + // names). Hold the head ref as eqref; per-variant + // `ref.cast` narrows it to the concrete variant idx + // before reading its fields. + let eq_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Eq, + }, + }); + locals.push((1, eq_ref)); // 3 = elem_ref (eqref carrier) + locals.push((1, ValType::I32)); // 4 = elem_hash + } + _ => {} + } + let mut f = Function::new(locals); // h = 5381 f.instruction(&Instruction::I32Const(5381)); f.instruction(&Instruction::LocalSet(2)); @@ -2051,17 +2147,24 @@ fn emit_list_hash( struct_type_index: list_idx, field_index: 0, }); - // Element hash → i32 - match elem.trim() { - "Int" => { + // Element hash → i32. Dispatch by `kind` rather than the raw + // `elem` string — newtype optimisation erases single-field + // records to their underlying primitive (`Box(n: Int)` → I64), + // so the surface name can be `"Box"` while the actual wasm + // representation is `i64`. `list_eq_kind` resolves the newtype + // before returning, so `kind` is the source of truth for + // representation. + let _ = elem; + match &kind { + ListEqKind::I64 => { f.instruction(&Instruction::I32WrapI64); } - "Bool" => {} // already i32 - "Float" => { + ListEqKind::I32 => {} // bool — already i32 + ListEqKind::F64 => { f.instruction(&Instruction::I64ReinterpretF64); f.instruction(&Instruction::I32WrapI64); } - "String" => { + ListEqKind::StringEq => { // Inline DJB2 over the (array i8) — short version // that reuses the per-fn locals would need extra // scratch. Cheap fallback: take array length as the @@ -2070,14 +2173,36 @@ fn emit_list_hash( // for non-cryptographic mix. f.instruction(&Instruction::ArrayLen); } - other => panic!( - "internal compiler error: list hash emit reached unsupported \ - element type `{other}`; upstream `list_eq_kind` must restrict \ - list-hash emission to {{Int, Bool, Float, String}}. \ - Please file at https://github.com/jasisz/aver/issues" - ), + ListEqKind::RecordEq(record_name) => { + let r_idx = + registry + .record_type_idx(record_name) + .ok_or(WasmGcError::Validation(format!( + "list hash dispatch: record `{record_name}` not registered" + )))?; + let fields = registry.record_fields.get(record_name).ok_or( + WasmGcError::Validation(format!( + "list hash dispatch: record `{record_name}` has no field info" + )), + )?; + emit_record_inline_hash( + &mut f, + r_idx, + fields, + /* elem_local */ 3, + /* elem_hash_local */ 4, + )?; + } + ListEqKind::SumEq(parent_name) => { + emit_sum_inline_hash( + &mut f, + parent_name, + registry, + /* elem_local */ 3, + /* elem_hash_local */ 4, + )?; + } } - let _ = kind; f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); // cur = cur.tail @@ -2095,6 +2220,169 @@ fn emit_list_hash( Ok(f) } +/// Inline DJB2-style hash for a sum element. Stack on entry has the +/// element eqref; on exit has an `i32` hash. Walks variants in the +/// same sorted order as `emit_sum_eq_inline` (so two compiler runs +/// produce identical bytecode + so `eq` and `hash` agree on which +/// variant to inspect first), and per matched variant mixes its +/// `type_idx` (as a stable tag) plus DJB2-folds each primitive +/// field into `elem_hash`. Variants are disjoint subtypes of the +/// parent, so at most one `ref.test` succeeds per call — non- +/// matched arms `ref.test` to false and skip silently. +fn emit_sum_inline_hash( + f: &mut Function, + parent_name: &str, + registry: &TypeRegistry, + elem_local: u32, + elem_hash_local: u32, +) -> Result<(), WasmGcError> { + // Collect variants of this sum. Same ordering as + // `emit_sum_eq_inline` for parity. + let mut variants: Vec<(String, super::types::VariantInfo)> = registry + .variants + .iter() + .flat_map(|(n, vs)| vs.iter().map(move |v| (n.clone(), v.clone()))) + .filter(|(_, v)| v.parent == parent_name) + .collect(); + variants.sort_by(|a, b| a.0.cmp(&b.0)); + if variants.is_empty() { + return Err(WasmGcError::Validation(format!( + "list hash dispatch: sum type `{parent_name}` has no variants" + ))); + } + + // Save eqref → elem_local; init elem_hash = 5381. + f.instruction(&Instruction::LocalSet(elem_local)); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(elem_hash_local)); + + for (_v_name, info) in &variants { + let v_idx = info.type_idx; + let v_heap = wasm_encoder::HeapType::Concrete(v_idx); + // if ref.test V elem_ref: + f.instruction(&Instruction::LocalGet(elem_local)); + f.instruction(&Instruction::RefTestNonNull(v_heap)); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + // Fold variant tag (type_idx as i32) into elem_hash — + // ensures empty variants of different shape still get + // distinct hashes. + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::I32Const(v_idx as i32)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(elem_hash_local)); + // Per field, downcast then fold. + for (i, field_ty) in info.fields.iter().enumerate() { + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Add); + + f.instruction(&Instruction::LocalGet(elem_local)); + f.instruction(&Instruction::RefCastNonNull(v_heap)); + f.instruction(&Instruction::StructGet { + struct_type_index: v_idx, + field_index: i as u32, + }); + match field_ty.trim() { + "Int" => { + f.instruction(&Instruction::I32WrapI64); + } + "Bool" => {} // already i32 + "Float" => { + f.instruction(&Instruction::I64ReinterpretF64); + f.instruction(&Instruction::I32WrapI64); + } + "String" => { + f.instruction(&Instruction::ArrayLen); + } + other => { + return Err(WasmGcError::Validation(format!( + "sum hash dispatch: variant field type `{other}` \ + unsupported (only Int/Bool/Float/String); upstream \ + `list_eq_kind` all_simple gate should have rejected this." + ))); + } + } + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(elem_hash_local)); + } + f.instruction(&Instruction::End); // end if + } + + f.instruction(&Instruction::LocalGet(elem_hash_local)); + Ok(()) +} + +/// Inline DJB2-style hash for a record element. Stack on entry has +/// the element ref; on exit has an `i32` hash. `elem_local` and +/// `elem_hash_local` are pre-declared scratch slots in the calling +/// fn (typed `(ref null $record_idx)` and `i32` respectively). +/// +/// Per-field hash trick mirrors the primitive arms in +/// `emit_list_hash`: Int → wrap, Float → reinterpret+wrap, Bool → +/// already i32, String → array.len. Field shapes are restricted to +/// {Int, Bool, Float, String} by `list_eq_kind`'s `all_simple` gate; +/// nested records / lists trip `WasmGcError::Validation` here. +fn emit_record_inline_hash( + f: &mut Function, + record_idx: u32, + fields: &[(String, String)], + elem_local: u32, + elem_hash_local: u32, +) -> Result<(), WasmGcError> { + // Save record ref → elem_local for repeated struct.get. + f.instruction(&Instruction::LocalSet(elem_local)); + // elem_hash = 5381 (DJB2 init). + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(elem_hash_local)); + for (i, (_field_name, field_type)) in fields.iter().enumerate() { + // elem_hash = elem_hash * 33 + field_hash + // (= (elem_hash << 5) + elem_hash + field_hash, DJB2.) + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(elem_hash_local)); + f.instruction(&Instruction::I32Add); + // Push field value, then mix to i32. + f.instruction(&Instruction::LocalGet(elem_local)); + f.instruction(&Instruction::StructGet { + struct_type_index: record_idx, + field_index: i as u32, + }); + match field_type.trim() { + "Int" => { + f.instruction(&Instruction::I32WrapI64); + } + "Bool" => {} // already i32 + "Float" => { + f.instruction(&Instruction::I64ReinterpretF64); + f.instruction(&Instruction::I32WrapI64); + } + "String" => { + f.instruction(&Instruction::ArrayLen); + } + other => { + return Err(WasmGcError::Validation(format!( + "record hash dispatch: field type `{other}` unsupported \ + (only Int/Bool/Float/String); upstream `list_eq_kind` \ + all_simple gate should have rejected this list." + ))); + } + } + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(elem_hash_local)); + } + // Push final elem_hash so the caller's mix can fold it into the + // total list hash. + f.instruction(&Instruction::LocalGet(elem_hash_local)); + Ok(()) +} + /// `eq : (Vector, Vector) -> i32`. Length check + element- /// wise eq via per-T instruction. Same `T must be eq-able` rule as /// list_eq. @@ -2175,10 +2463,42 @@ fn emit_vec_hash( _string_eq_fn_idx: Option, ) -> Result { let (vec_idx, _) = vec_idx_of_pair(canonical, registry)?; - let _ = kind; let elem = TypeRegistry::list_element_type(canonical).unwrap(); - // params: 0=v. locals: 1=h, 2=len, 3=i. - let mut f = Function::new([(1, ValType::I32), (1, ValType::I32), (1, ValType::I32)]); + // params: 0=v. locals: 1=h, 2=len, 3=i, plus per-kind extras + // (4=elem_ref, 5=elem_hash) for record/sum element dispatch — + // newtype optimisation may erase a record name down to its + // underlying primitive (kind == I64 even though `elem == "Box"`), + // so dispatch by `kind`, not the surface element string. + let mut locals: Vec<(u32, ValType)> = + vec![(1, ValType::I32), (1, ValType::I32), (1, ValType::I32)]; + match &kind { + ListEqKind::RecordEq(record_name) => { + let r_idx = registry.record_type_idx(record_name).ok_or( + WasmGcError::Validation(format!( + "vector hash for `Vector<{record_name}>`: record not registered" + )), + )?; + let r_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(r_idx), + }); + locals.push((1, r_ref)); // 4 = elem_ref + locals.push((1, ValType::I32)); // 5 = elem_hash + } + ListEqKind::SumEq(_) => { + let eq_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Eq, + }, + }); + locals.push((1, eq_ref)); // 4 = elem_ref (eqref) + locals.push((1, ValType::I32)); // 5 = elem_hash + } + _ => {} + } + let mut f = Function::new(locals); f.instruction(&Instruction::I32Const(5381)); f.instruction(&Instruction::LocalSet(1)); f.instruction(&Instruction::LocalGet(0)); @@ -2201,24 +2521,47 @@ fn emit_vec_hash( f.instruction(&Instruction::LocalGet(0)); f.instruction(&Instruction::LocalGet(3)); f.instruction(&Instruction::ArrayGet(vec_idx)); - match elem.trim() { - "Int" => { + let _ = elem; + match &kind { + ListEqKind::I64 => { f.instruction(&Instruction::I32WrapI64); } - "Bool" => {} - "Float" => { + ListEqKind::I32 => {} // bool — already i32 + ListEqKind::F64 => { f.instruction(&Instruction::I64ReinterpretF64); f.instruction(&Instruction::I32WrapI64); } - "String" => { + ListEqKind::StringEq => { f.instruction(&Instruction::ArrayLen); } - other => panic!( - "internal compiler error: Vector hash emit reached unsupported \ - element type `{other}`; upstream `list_eq_kind` must restrict \ - Vector-hash emission to {{Int, Bool, Float, String}}. \ - Please file at https://github.com/jasisz/aver/issues" - ), + ListEqKind::RecordEq(record_name) => { + let r_idx = registry.record_type_idx(record_name).ok_or( + WasmGcError::Validation(format!( + "vector hash dispatch: record `{record_name}` not registered" + )), + )?; + let fields = registry.record_fields.get(record_name).ok_or( + WasmGcError::Validation(format!( + "vector hash dispatch: record `{record_name}` has no field info" + )), + )?; + emit_record_inline_hash( + &mut f, + r_idx, + fields, + /* elem_local */ 4, + /* elem_hash_local */ 5, + )?; + } + ListEqKind::SumEq(parent_name) => { + emit_sum_inline_hash( + &mut f, + parent_name, + registry, + /* elem_local */ 4, + /* elem_hash_local */ 5, + )?; + } } f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(1)); From 609ae431c923fe30c1253868c50a9fcc9f3e63dc Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 13:29:37 +0200 Subject: [PATCH 05/18] 0.16.3: rustfmt + clippy too_many_arguments allow CI Format + Clippy steps flagged: - One indentation diff in `lists.rs::emit_list_hash` from the new RecordEq locals match arm. - `clippy::too_many_arguments` on `emit_record_eq_inline` / `emit_sum_eq_inline` after threading `eq_helper_fn_idx` and `self_fn_idx` for recursive eq dispatch (now 8 args each). Splitting these into a context struct is cleaner long-term but not the right cut while we're still settling the dispatch shape; an `#[allow]` attribute is fine for the moment. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/lists.rs | 67 +++++++++++++++++------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 94b1df93..8fcde40d 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -1628,6 +1628,7 @@ fn emit_list_contains( /// surface as Unimplemented (same constraint as `emit_eq_record` in /// maps.rs — extending requires nested-record / list / vector eq /// dispatch). +#[allow(clippy::too_many_arguments)] pub(super) fn emit_record_eq_inline( f: &mut Function, record_name: &str, @@ -1718,6 +1719,7 @@ pub(super) fn emit_record_eq_inline( /// and needle have that concrete type. If both: cast + field-by- /// field eq, push result. If only one: push 0 (different variants). /// Final i32 on stack: 1 = equal, 0 = different. +#[allow(clippy::too_many_arguments)] pub(super) fn emit_sum_eq_inline( f: &mut Function, parent_name: &str, @@ -2094,11 +2096,11 @@ fn emit_list_hash( let mut locals: Vec<(u32, ValType)> = vec![(1, list_ref), (1, ValType::I32)]; match &kind { ListEqKind::RecordEq(record_name) => { - let r_idx = registry.record_type_idx(record_name).ok_or( - WasmGcError::Validation(format!( + let r_idx = registry + .record_type_idx(record_name) + .ok_or(WasmGcError::Validation(format!( "list hash for `List<{record_name}>`: record not registered" - )), - )?; + )))?; let r_ref = ValType::Ref(RefType { nullable: true, heap_type: HeapType::Concrete(r_idx), @@ -2174,23 +2176,19 @@ fn emit_list_hash( f.instruction(&Instruction::ArrayLen); } ListEqKind::RecordEq(record_name) => { - let r_idx = - registry - .record_type_idx(record_name) - .ok_or(WasmGcError::Validation(format!( - "list hash dispatch: record `{record_name}` not registered" - )))?; - let fields = registry.record_fields.get(record_name).ok_or( - WasmGcError::Validation(format!( + let r_idx = registry + .record_type_idx(record_name) + .ok_or(WasmGcError::Validation(format!( + "list hash dispatch: record `{record_name}` not registered" + )))?; + let fields = registry + .record_fields + .get(record_name) + .ok_or(WasmGcError::Validation(format!( "list hash dispatch: record `{record_name}` has no field info" - )), - )?; + )))?; emit_record_inline_hash( - &mut f, - r_idx, - fields, - /* elem_local */ 3, - /* elem_hash_local */ 4, + &mut f, r_idx, fields, /* elem_local */ 3, /* elem_hash_local */ 4, )?; } ListEqKind::SumEq(parent_name) => { @@ -2473,11 +2471,11 @@ fn emit_vec_hash( vec![(1, ValType::I32), (1, ValType::I32), (1, ValType::I32)]; match &kind { ListEqKind::RecordEq(record_name) => { - let r_idx = registry.record_type_idx(record_name).ok_or( - WasmGcError::Validation(format!( + let r_idx = registry + .record_type_idx(record_name) + .ok_or(WasmGcError::Validation(format!( "vector hash for `Vector<{record_name}>`: record not registered" - )), - )?; + )))?; let r_ref = ValType::Ref(RefType { nullable: true, heap_type: HeapType::Concrete(r_idx), @@ -2535,22 +2533,19 @@ fn emit_vec_hash( f.instruction(&Instruction::ArrayLen); } ListEqKind::RecordEq(record_name) => { - let r_idx = registry.record_type_idx(record_name).ok_or( - WasmGcError::Validation(format!( + let r_idx = registry + .record_type_idx(record_name) + .ok_or(WasmGcError::Validation(format!( "vector hash dispatch: record `{record_name}` not registered" - )), - )?; - let fields = registry.record_fields.get(record_name).ok_or( - WasmGcError::Validation(format!( + )))?; + let fields = registry + .record_fields + .get(record_name) + .ok_or(WasmGcError::Validation(format!( "vector hash dispatch: record `{record_name}` has no field info" - )), - )?; + )))?; emit_record_inline_hash( - &mut f, - r_idx, - fields, - /* elem_local */ 4, - /* elem_hash_local */ 5, + &mut f, r_idx, fields, /* elem_local */ 4, /* elem_hash_local */ 5, )?; } ListEqKind::SumEq(parent_name) => { From 06b832f0ae34092a9ec0ace8f5aa8542bf6479da Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 14:11:04 +0200 Subject: [PATCH 06/18] =?UTF-8?q?0.16.3:=20bug=204=20+=205=20=E2=80=94=20t?= =?UTF-8?q?ransitive=20eq=20helpers=20+=20eqref=20ref.cast=20prologue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User stress-tested the bug 1-3 fix with three exotic shapes; two of them surfaced fresh regressions both rooted in the `__eq_` helper machinery. ## Repros that triggered the fixes - **A: record with sum field.** `record Item { name: String, color: Color }`, `Item == Item`. Failed with `record Item field type Color has no eq dispatch`. - **B: sum with record field.** `type Cell = Empty | Filled(Pos)` where `record Pos { x: Int, y: Int }`. Failed with `expected (ref null $type), found eqref`. - **C: mutually recursive sums.** `type A = A0 | A1(B); type B = B0 | B1(A)`. Worked already after bug 3, kept as a regression smoke. ## Fix 1: transitive `__eq_` helper registration `EqHelperRegistry::register` only ran on the types that the user wrote `==` / `!=` on directly. Field types reached only transitively — e.g. `Color` showing up as a field of `Item` — never got a slot, so the bug 3 dispatch `Call(eq_helper_fn_idx[field_type])` resolved to None and bailed with the new validation error. New `register_transitive(name, kind, registry)` walks the record fields / sum variants and recurses on any nominal field type. Idempotent on cycles (Tree → Tree.Node → Tree's `register_transitive` is a no-op after the first hit), so recursive shapes don't loop. Discovery walker in `module.rs` swapped to call this instead of plain `register`. ## Fix 2: ref.cast prologue in `__eq_` body The helper signature is `(eqref, eqref) -> i32` (uniform for record + sum so call sites don't need per-type fn type idxs). That was fine for sums — `emit_sum_eq_inline` runs `ref.test` + `ref.cast` per variant before reading fields. Records read fields straight off the param via `struct.get $record_idx ` which the validator rejects: `struct.get` wants a typed `(ref null $record)`, not `eqref`. `emit_helper_bodies::EqKind::Record` now declares 2 typed locals on the helper, casts both params at the top of the body, and drives `emit_record_eq_inline` against the typed locals. Sum path is unchanged. ## Verified - All three exotic repros (A/B/C) match VM output. - Bug 1/2/3 repros still match. - 10/10 wasm-gc record-replay smokes pass. - All ~92 examples (core/data/games/projects) compile + validate under wasm-gc. `exo_b` was the shape memory's "bug 5: ref/eqref" referred to — the validator message and structure match. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/eq_helpers.rs | 143 +++++++++++++++++++++---- src/codegen/wasm_gc/module.rs | 4 +- 2 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index 9b47a446..84aaa200 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -68,6 +68,70 @@ impl EqHelperRegistry { } } + /// Register `type_name` and recursively register every nominal + /// field type it transitively reaches. Needed because the + /// emitted `__eq_` body dispatches nested record/sum fields + /// by `Call(__eq_)` — those helper slots have to + /// exist even if the user never wrote `field == field` directly. + /// `register` is idempotent on cycle (Tree → Tree.Node → Tree) + /// so the recursion terminates. + pub(crate) fn register_transitive( + &mut self, + type_name: &str, + kind: EqKind, + registry: &TypeRegistry, + ) { + if self.kinds.contains_key(type_name) { + return; + } + self.register(type_name, kind); + // Walk fields and recurse on nominal types. + match kind { + EqKind::Record => { + if let Some(fields) = registry.record_fields.get(type_name) { + for (_, field_ty) in fields { + self.register_field_type(field_ty.trim(), registry); + } + } + } + EqKind::Sum => { + let variants: Vec<_> = registry + .variants + .values() + .flat_map(|vs| vs.iter()) + .filter(|v| v.parent == type_name) + .cloned() + .collect(); + for v in &variants { + for field_ty in &v.fields { + self.register_field_type(field_ty.trim(), registry); + } + } + } + } + } + + fn register_field_type(&mut self, field_ty: &str, registry: &TypeRegistry) { + // Skip primitives and compound carriers (List/Map/Vector/ + // Option/Result handle their own dispatch). + if matches!(field_ty, "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char") { + return; + } + if registry.record_fields.contains_key(field_ty) { + self.register_transitive(field_ty, EqKind::Record, registry); + } else if registry + .variants + .values() + .flat_map(|v| v.iter()) + .any(|v| v.parent == field_ty) + { + self.register_transitive(field_ty, EqKind::Sum, registry); + } + // Other shapes (List, Map, generics) — out of + // scope for nominal eq dispatch today; leave them to fail + // loudly in the inline emitter so we know to extend. + } + pub(crate) fn iter(&self) -> impl Iterator + '_ { self.order.iter().map(|n| (n.as_str(), self.kinds[n])) } @@ -130,31 +194,64 @@ impl EqHelperRegistry { for name in &self.order { let kind = self.kinds[name]; let self_fn_idx = self.slots.get(name).map(|(f, _)| *f); - let mut f = Function::new(Vec::new()); match kind { - EqKind::Sum => emit_sum_eq_inline( - &mut f, - name, - registry, - 0, - 1, - string_eq_fn_idx, - &helper_idx_map, - self_fn_idx, - )?, - EqKind::Record => emit_record_eq_inline( - &mut f, - name, - registry, - 0, - 1, - string_eq_fn_idx, - &helper_idx_map, - self_fn_idx, - )?, + EqKind::Sum => { + // Sum's emit_sum_eq_inline does its own + // ref.test/ref.cast cascade per variant — the + // raw eqref params are fine to feed in directly. + let mut f = Function::new(Vec::new()); + emit_sum_eq_inline( + &mut f, + name, + registry, + 0, + 1, + string_eq_fn_idx, + &helper_idx_map, + self_fn_idx, + )?; + f.instruction(&Instruction::End); + codes.function(&f); + } + EqKind::Record => { + // Record's emit_record_eq_inline reads fields via + // `struct.get $record_idx ` straight off the + // local — that needs a typed `(ref null $record)`, + // not the eqref the helper signature carries. So + // declare two typed locals (idxs 2, 3), `ref.cast` + // each param into its slot, then drive the inline + // emitter against the typed locals. + let r_idx = registry.record_type_idx(name).ok_or( + WasmGcError::Validation(format!( + "eq helper for record `{name}`: record not registered" + )), + )?; + let r_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(r_idx), + }); + let mut f = Function::new(vec![(2, r_ref)]); + let r_heap = wasm_encoder::HeapType::Concrete(r_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(r_heap)); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::RefCastNonNull(r_heap)); + f.instruction(&Instruction::LocalSet(3)); + emit_record_eq_inline( + &mut f, + name, + registry, + 2, + 3, + string_eq_fn_idx, + &helper_idx_map, + self_fn_idx, + )?; + f.instruction(&Instruction::End); + codes.function(&f); + } } - f.instruction(&Instruction::End); - codes.function(&f); } Ok(()) } diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 8a31920e..b6f5aed1 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -1261,14 +1261,14 @@ fn discover_builtins_in_expr( && let AverType::Named(name) = t { if type_registry.record_fields.contains_key(name) { - eq_helpers.register(name, EqKind::Record); + eq_helpers.register_transitive(name, EqKind::Record, type_registry); } else if type_registry .variants .values() .flat_map(|v| v.iter()) .any(|v| &v.parent == name) { - eq_helpers.register(name, EqKind::Sum); + eq_helpers.register_transitive(name, EqKind::Sum, type_registry); } } discover_builtins_in_expr(&l.node, builtins, effects, eq_helpers, type_registry); From 6d80dd74e7e160225aa56a3d070cecf95158630c Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 14:44:46 +0200 Subject: [PATCH 07/18] 0.16.3: lift nominal eq cap for collections + transitive helper register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User stress-test ("spróbuj egzotyków") surfaced that the bug-1-3 fix covered nominal `==` directly on a record/sum (`Item == Item`) and inside `__eq_` field dispatch, but the surrounding ListEqKind / ListHelperRegistry wiring still bailed out for collections holding nominal elements: - `List` failed `list_eq_kind` because Tree.Node's recursive `Tree` field tripped the old `all_simple` gate — primitive-only. - `emit_list_eq` / `emit_list_contains` / `emit_vec_eq` then panic-d or returned Unimplemented on the RecordEq/SumEq arm. - The helper-body emit threaded `&HashMap::new()` for the eq helper lookup map and `None` for self_fn_idx, so even when a kind landed the inner `emit_record_eq_inline` / `emit_sum_eq_inline` couldn't resolve nested-nominal field eq (`Item.color: Color`). Lifts: - `list_eq_kind` now accepts nominal elements whose fields are themselves resolvable. `record_fields_resolvable` and `sum_fields_resolvable` recurse with a `seen` set so cycles (Tree → Tree.Node → Tree) terminate. Fields can be primitive, another resolvable record, or another resolvable sum. - `emit_list_eq` / `emit_vec_eq` no longer panic on RecordEq / SumEq. They dispatch to the per-type `__eq_` helper via `Call(eq_helper_fn_idx[name])`. Both refs on the stack are subtypes of eqref so the implicit upcast at the helper signature is fine. - `emit_list_contains`'s nested `emit_record_eq_inline` / `emit_sum_eq_inline` calls now thread the actual eq-helper map in (used to be empty), so a `List.contains(needle)` with Item carrying a Color field resolves Color through `Call(__eq_Color)` instead of bailing. - `ListHelperRegistry::emit_helper_bodies` takes `eq_helper_fn_idx: &HashMap` and passes it down. `module.rs` snapshots `eq_helpers_registry.iter()` once and hands it to both list_helpers' emit and the existing eq_helpers' own bodies (where it was already wired in step 3). - Discovery walker (`module.rs::discover_builtins_in_expr`) gains `register_nominal_in_type(t)` that walks `t` recursively and registers every Named record/sum it reaches. Needed because `==` on `List` (or `Map`, `Option`, …) used to register nothing — `t` matched neither `AverType::Named` nor a record/sum walker; the helper bodies later tried `Call(__eq_)` on an unregistered slot. ## Verified by stress repros - A: `record Item { name: String, color: Color }`, `==`. Color is now auto-registered transitively, dispatch via `Call(__eq_Color)` resolves cleanly. - B: `type Cell = Empty | Filled(Pos)` with `record Pos { x, y }`. Pos registered transitively, helper signature ref.cast prologue (step 3 fix) gives the typed ref `struct.get` needs. - C: mutually recursive `type A = A0 | A1(B); type B = B0 | B1(A)`. Both helpers register; recursive Call(self_fn_idx) closes the loop. - bugs 1, 2, 3 still match VM. - 10/10 wasm-gc record-replay smokes pass. - All 92 examples (core/data/games/projects) compile + validate. ## Known remaining shapes (out of 5-bug scope) - `==` on `List` directly (BinOp::Eq with operand type `Type::List(_)`): `body/emit.rs::sum_or_record_eq_fn`'s lookup is keyed by `Type::Named(name)` only, so `List<…>` falls through to the default `i64.eq` arm. Fix would extend the dispatch to call `fn_map.list_ops[canonical].eq` when the operand type is `List<…>` / `Vector<…>`. Separate refactor — flagged for 0.16.4. - Records with `Option<…>` / `Result<…, …>` / `Tuple<…>` fields: `emit_record_eq_inline`'s field type match needs a similar generic-carrier dispatch path. Same followup. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/lists.rs | 172 ++++++++++++++++++++++++---------- src/codegen/wasm_gc/module.rs | 64 ++++++++++++- 2 files changed, 185 insertions(+), 51 deletions(-) diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 8fcde40d..91cfba3e 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -569,6 +569,7 @@ impl ListHelperRegistry { codes: &mut CodeSection, registry: &TypeRegistry, string_eq_fn_idx: Option, + eq_helper_fn_idx: &std::collections::HashMap, ) -> Result<(), WasmGcError> { for canonical in &self.list_order { // Order MUST match `assign_slots` and @@ -589,6 +590,7 @@ impl ListHelperRegistry { registry, kind.clone(), string_eq_fn_idx, + eq_helper_fn_idx, )?); if let (Some(eq_fn), Some(_hash_fn)) = (ops.eq, ops.hash) { codes.function(&emit_list_eq( @@ -597,6 +599,7 @@ impl ListHelperRegistry { kind.clone(), string_eq_fn_idx, eq_fn, + eq_helper_fn_idx, )?); codes.function(&emit_list_hash( canonical, @@ -620,6 +623,7 @@ impl ListHelperRegistry { registry, kind.clone(), string_eq_fn_idx, + eq_helper_fn_idx, )?); codes.function(&emit_vec_hash(canonical, registry, kind, string_eq_fn_idx)?); } @@ -697,20 +701,18 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { "Bool" => Some(ListEqKind::I32), "String" => Some(ListEqKind::StringEq), other => { + // Record / sum element gets a contains/eq/hash slot + // whenever its fields are themselves resolvable: primitives + // or nominal types we've already accepted. Recursive refs + // (Tree.Node holding Tree) and nominal cross-references + // (Item holding Color) both flow through `field_resolvable` + // which recurses with a `seen` set so cycles terminate. + // The inline emitters (emit_record_eq_inline / + // emit_sum_eq_inline) handle these via `eq_helper_fn_idx` + // dispatch + `self_fn_idx` for self-recursion since 0.16.3. + let mut seen: std::collections::HashSet = std::collections::HashSet::new(); if registry.record_type_idx(other).is_some() { - // Record element only gets a `contains` slot when - // every field is comparable inline. Nested records, - // lists, vectors, sums in fields would need a - // recursive eq dispatch the inline emit doesn't - // support — those records still work as Map keys - // (where nested record helpers are force-registered - // and called by fn idx); they just don't get a - // List.contains slot. - let fields = registry.record_fields.get(other)?; - let all_simple = fields - .iter() - .all(|(_, t)| matches!(t.trim(), "Int" | "Float" | "Bool" | "String")); - if all_simple { + if record_fields_resolvable(other, registry, &mut seen) { Some(ListEqKind::RecordEq(other.to_string())) } else { None @@ -721,17 +723,7 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { .flat_map(|v| v.iter()) .any(|v| v.parent == other) { - let all_simple = registry - .variants - .values() - .flat_map(|vs| vs.iter()) - .filter(|v| v.parent == other) - .all(|v| { - v.fields - .iter() - .all(|t| matches!(t.trim(), "Int" | "Float" | "Bool" | "String")) - }); - if all_simple { + if sum_fields_resolvable(other, registry, &mut seen) { Some(ListEqKind::SumEq(other.to_string())) } else { None @@ -743,6 +735,62 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { } } +/// True when every field of `record` is something the inline eq +/// emitters can dispatch: primitive, a registered record/sum +/// (recursively resolvable), or self-recursion. Cycles terminate +/// via the `seen` set. +fn record_fields_resolvable( + record: &str, + registry: &TypeRegistry, + seen: &mut std::collections::HashSet, +) -> bool { + if !seen.insert(record.to_string()) { + return true; // already visiting — break the cycle + } + let Some(fields) = registry.record_fields.get(record) else { + return false; + }; + fields + .iter() + .all(|(_, t)| field_type_resolvable(t.trim(), registry, seen)) +} + +fn sum_fields_resolvable( + parent: &str, + registry: &TypeRegistry, + seen: &mut std::collections::HashSet, +) -> bool { + if !seen.insert(parent.to_string()) { + return true; + } + registry + .variants + .values() + .flat_map(|vs| vs.iter()) + .filter(|v| v.parent == parent) + .all(|v| { + v.fields + .iter() + .all(|t| field_type_resolvable(t.trim(), registry, seen)) + }) +} + +fn field_type_resolvable( + field: &str, + registry: &TypeRegistry, + seen: &mut std::collections::HashSet, +) -> bool { + matches!(field, "Int" | "Float" | "Bool" | "String") + || (registry.record_type_idx(field).is_some() + && record_fields_resolvable(field, registry, seen)) + || (registry + .variants + .values() + .flat_map(|vs| vs.iter()) + .any(|v| v.parent == field) + && sum_fields_resolvable(field, registry, seen)) +} + fn list_idx_of(canonical: &str, registry: &TypeRegistry) -> Result { registry .list_type_idx(canonical) @@ -1498,6 +1546,7 @@ fn emit_list_contains( registry: &TypeRegistry, kind: ListEqKind, string_eq_fn_idx: Option, + eq_helper_fn_idx: &std::collections::HashMap, ) -> Result { let list_idx = list_idx_of(canonical, registry)?; let list_ref = ValType::Ref(RefType { @@ -1550,7 +1599,7 @@ fn emit_list_contains( 3, 4, string_eq_fn_idx, - &std::collections::HashMap::new(), + eq_helper_fn_idx, None, )?; } @@ -1572,7 +1621,7 @@ fn emit_list_contains( 3, 4, string_eq_fn_idx, - &std::collections::HashMap::new(), + eq_helper_fn_idx, None, )?; } @@ -1995,12 +2044,14 @@ fn emit_list_zip( /// when both lists end at the same step with all heads equal; 0 /// otherwise. Same `T` constraint as contains — only emitted when T /// has a `list_eq_kind`. +#[allow(clippy::too_many_arguments)] fn emit_list_eq( canonical: &str, registry: &TypeRegistry, kind: ListEqKind, string_eq_fn_idx: Option, self_fn_idx: u32, + eq_helper_fn_idx: &std::collections::HashMap, ) -> Result { let list_idx = list_idx_of(canonical, registry)?; // params: 0 = la, 1 = lb. No locals — short body. @@ -2032,25 +2083,35 @@ fn emit_list_eq( field_index: 0, }); match &kind { - ListEqKind::I64 => f.instruction(&Instruction::I64Eq), - ListEqKind::F64 => f.instruction(&Instruction::F64Eq), - ListEqKind::I32 => f.instruction(&Instruction::I32Eq), + ListEqKind::I64 => { + f.instruction(&Instruction::I64Eq); + } + ListEqKind::F64 => { + f.instruction(&Instruction::F64Eq); + } + ListEqKind::I32 => { + f.instruction(&Instruction::I32Eq); + } ListEqKind::StringEq => { let eq_fn = string_eq_fn_idx.ok_or(WasmGcError::Validation( "List eq over String needs __wasmgc_string_eq".into(), ))?; - f.instruction(&Instruction::Call(eq_fn)) + f.instruction(&Instruction::Call(eq_fn)); } - ListEqKind::RecordEq(_) | ListEqKind::SumEq(_) => { - // Same `all_simple` constraint as contains — these - // variants don't reach this emit path. - panic!( - "internal compiler error: emit_list_eq reached RecordEq/SumEq; \ - list_eq_kind must return None for record/sum elements. \ - Please file at https://github.com/jasisz/aver/issues" - ) + ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) => { + // Nominal element — dispatch to the per-type + // `__eq_` helper. Its signature is + // `(eqref, eqref) -> i32`; both refs on the stack are + // subtypes of eqref so the implicit upcast is fine. + let idx = eq_helper_fn_idx.get(name).copied().ok_or( + WasmGcError::Validation(format!( + "List eq over `{name}`: __eq_{name} helper not registered \ + (discovery walker should have transitively flagged it)" + )), + )?; + f.instruction(&Instruction::Call(idx)); } - }; + } // if heads differ → 0 f.instruction(&Instruction::I32Eqz); f.instruction(&Instruction::If(BlockType::Empty)); @@ -2389,6 +2450,7 @@ fn emit_vec_eq( registry: &TypeRegistry, kind: ListEqKind, string_eq_fn_idx: Option, + eq_helper_fn_idx: &std::collections::HashMap, ) -> Result { let (vec_idx, _) = vec_idx_of_pair(canonical, registry)?; // params: 0=va, 1=vb. locals: 2=len, 3=i. @@ -2420,22 +2482,32 @@ fn emit_vec_eq( f.instruction(&Instruction::LocalGet(3)); f.instruction(&Instruction::ArrayGet(vec_idx)); match &kind { - ListEqKind::I64 => f.instruction(&Instruction::I64Eq), - ListEqKind::F64 => f.instruction(&Instruction::F64Eq), - ListEqKind::I32 => f.instruction(&Instruction::I32Eq), + ListEqKind::I64 => { + f.instruction(&Instruction::I64Eq); + } + ListEqKind::F64 => { + f.instruction(&Instruction::F64Eq); + } + ListEqKind::I32 => { + f.instruction(&Instruction::I32Eq); + } ListEqKind::StringEq => { let eq_fn = string_eq_fn_idx.ok_or(WasmGcError::Validation( "Vector eq over String needs __wasmgc_string_eq".into(), ))?; - f.instruction(&Instruction::Call(eq_fn)) + f.instruction(&Instruction::Call(eq_fn)); } - kind => panic!( - "internal compiler error: Vector eq emit reached unsupported \ - kind {kind:?}; upstream `list_eq_kind` must filter Vector eq \ - to scalar / String elements. \ - Please file at https://github.com/jasisz/aver/issues" - ), - }; + ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) => { + // Nominal element — `Call(__eq_)`. Same eqref upcast + // shape as in `emit_list_eq`. + let idx = eq_helper_fn_idx.get(name).copied().ok_or( + WasmGcError::Validation(format!( + "Vector eq over `{name}`: __eq_{name} helper not registered" + )), + )?; + f.instruction(&Instruction::Call(idx)); + } + } f.instruction(&Instruction::I32Eqz); f.instruction(&Instruction::If(BlockType::Empty)); f.instruction(&Instruction::I32Const(0)); diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index b6f5aed1..42496054 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -665,8 +665,19 @@ pub(super) fn emit_module( map_helpers.emit_helper_bodies(&mut codes, ®istry, &compound_eq_hash_lookup)?; // List / Vector.fromList / String.split-join helper bodies. + // Snapshot eq-helper fn idxs so list/vec eq+hash bodies can + // dispatch nominal-element `==`/hash through `Call(__eq_)`. let string_eq_fn_idx = builtin_registry.lookup_wasm_fn_idx(BuiltinName::StringEq); - list_helpers.emit_helper_bodies(&mut codes, ®istry, string_eq_fn_idx)?; + let eq_helper_fn_idx_map: HashMap = eq_helpers_registry + .iter() + .filter_map(|(n, _k)| eq_helpers_registry.lookup_fn_idx(n).map(|i| (n.to_string(), i))) + .collect(); + list_helpers.emit_helper_bodies( + &mut codes, + ®istry, + string_eq_fn_idx, + &eq_helper_fn_idx_map, + )?; // Per-(record/sum) `__eq_` helper bodies — emit after // list helpers so any String fields can call `__wasmgc_string_eq` @@ -1199,6 +1210,47 @@ fn discover_builtins_in_stmt( } } +/// Recursively walks `t` and registers every nominal record/sum it +/// reaches in `eq_helpers`. Needed for `==` on collection types +/// whose element/key/value type is nominal — `List`, +/// `Map`, `Option`, etc. Without this, the +/// helper-body emit (`emit_list_eq`, `emit_record_eq_inline`, +/// `emit_eq_record`) would dispatch by `Call(__eq_)` against +/// an unregistered slot. +fn register_nominal_in_type( + t: &AverType, + eq_helpers: &mut EqHelperRegistry, + type_registry: &super::types::TypeRegistry, +) { + match t { + AverType::Named(name) => { + if type_registry.record_fields.contains_key(name) { + eq_helpers.register_transitive(name, EqKind::Record, type_registry); + } else if type_registry + .variants + .values() + .flat_map(|v| v.iter()) + .any(|v| &v.parent == name) + { + eq_helpers.register_transitive(name, EqKind::Sum, type_registry); + } + } + AverType::List(inner) | AverType::Vector(inner) | AverType::Option(inner) => { + register_nominal_in_type(inner, eq_helpers, type_registry); + } + AverType::Map(k, v) | AverType::Result(k, v) => { + register_nominal_in_type(k, eq_helpers, type_registry); + register_nominal_in_type(v, eq_helpers, type_registry); + } + AverType::Tuple(items) => { + for item in items { + register_nominal_in_type(item, eq_helpers, type_registry); + } + } + _ => {} + } +} + fn discover_builtins_in_expr( expr: &Expr, builtins: &mut BuiltinRegistry, @@ -1271,6 +1323,16 @@ fn discover_builtins_in_expr( eq_helpers.register_transitive(name, EqKind::Sum, type_registry); } } + // List / Vector / Map / Option / Result / Tuple `==` — + // dispatch reaches the per-element/key __eq_ through + // the helper bodies, so any nominal element type also + // needs an __eq slot. Walk the operand type recursively + // and register every nominal we hit. + if matches!(op, Op::Eq | Op::Neq) + && let Some(t) = l.ty() + { + register_nominal_in_type(t, eq_helpers, type_registry); + } discover_builtins_in_expr(&l.node, builtins, effects, eq_helpers, type_registry); discover_builtins_in_expr(&r.node, builtins, effects, eq_helpers, type_registry); } From 40eba21241da370849bc6a6b85df432d2acb0e34 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 15:19:36 +0200 Subject: [PATCH 08/18] 0.16.3: register_transitive guard + collection seed sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous "lift nominal eq cap" change tripped the rogue playground test because: - `record Entity { something: Option, ... }`. My new `record_fields_resolvable` accepted Entity (Point itself is primitive-fielded so it resolved), so `list_eq_kind("Entity")` returned `Some(RecordEq("Entity"))` and a `List` got contains/eq/hash slots. - Discovery's nominal seed sweep (also new) walked the registered list element types and registered `__eq_Entity` transitively. `register_field_type` for "Option" silently skipped (it's not a primitive, record, or sum), so Entity got registered but Option didn't — and `emit_record_eq_inline`'s field dispatch later panicked. Two complementary fixes: - `register_transitive` now precomputes whether the type's fields are resolvable via `lists::record_fields_resolvable` / `sum_fields_resolvable` before registering. If any field type is a generic carrier (Option / Result / List / etc.) the registration is skipped — same gate `list_eq_kind` uses, so the two stay in sync. - `field_type_resolvable` rejects any field name containing `<` (i.e. any generic carrier) outright. The inline eq emitters don't have a dispatch for these yet — that's followup F. Net: rogue compile_multi_file_rogue_from_virtual_fs passes again, and List-style shapes simply don't get a list contains/eq/ hash slot (programs holding such lists still compile, they just can't compare lists or call .contains on them — same conservative fallback as before this PR). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/eq_helpers.rs | 18 +++++++++++++ src/codegen/wasm_gc/lists.rs | 18 ++++++++++--- src/codegen/wasm_gc/module.rs | 37 ++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index 84aaa200..8bf51881 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -84,6 +84,24 @@ impl EqHelperRegistry { if self.kinds.contains_key(type_name) { return; } + // Skip nominal types whose fields contain generic carriers + // (Option<…>, List<…>, etc.) the inline eq emitter doesn't + // handle yet. Without this guard, `register_field_type` + // silently dropped the unsupported field and the helper body + // emit later panicked with "no eq dispatch" — same error the + // surrounding sweep is trying to prevent. + let mut seen = std::collections::HashSet::new(); + let resolvable = match kind { + EqKind::Record => super::super::lists::record_fields_resolvable( + type_name, registry, &mut seen, + ), + EqKind::Sum => { + super::super::lists::sum_fields_resolvable(type_name, registry, &mut seen) + } + }; + if !resolvable { + return; + } self.register(type_name, kind); // Walk fields and recurse on nominal types. match kind { diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 91cfba3e..3de139fb 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -739,7 +739,7 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { /// emitters can dispatch: primitive, a registered record/sum /// (recursively resolvable), or self-recursion. Cycles terminate /// via the `seen` set. -fn record_fields_resolvable( +pub(super) fn record_fields_resolvable( record: &str, registry: &TypeRegistry, seen: &mut std::collections::HashSet, @@ -755,7 +755,7 @@ fn record_fields_resolvable( .all(|(_, t)| field_type_resolvable(t.trim(), registry, seen)) } -fn sum_fields_resolvable( +pub(super) fn sum_fields_resolvable( parent: &str, registry: &TypeRegistry, seen: &mut std::collections::HashSet, @@ -775,11 +775,23 @@ fn sum_fields_resolvable( }) } -fn field_type_resolvable( +pub(super) fn field_type_resolvable( field: &str, registry: &TypeRegistry, seen: &mut std::collections::HashSet, ) -> bool { + // Conservative: reject any generic carrier (`Option<…>`, + // `Result<…,…>`, `List<…>`, `Vector<…>`, `Map<…,…>`, + // `Tuple<…>`) as a field type. The inline eq emitters don't + // know how to dispatch eq for these — that's its own ABI + // path (Option tag-and-compare, Result variant tags, etc.). + // Programs that hold a record with such a field still work, + // they just don't get list-eq / map-key / contains-on-list + // slots emitted for the outer type. Lifting this is its own + // followup. + if field.contains('<') { + return false; + } matches!(field, "Int" | "Float" | "Bool" | "String") || (registry.record_type_idx(field).is_some() && record_fields_resolvable(field, registry, seen)) diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 42496054..7583d3d8 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -75,6 +75,43 @@ pub(super) fn emit_module( ®istry, ); } + // Sweep nominal element types of every registered List / Vector + // and key types of every registered Map. The list/vec helper + // bodies dispatch nominal element eq/hash via `Call(__eq_)` + // (since 0.16.3); without auto-registering those types here, a + // program that holds `List` without ever writing + // `list == list` directly would still get a list helper body + // that calls into an unregistered `__eq_Item`. Keys of `Map` + // need the same: maps.rs `emit_eq_for(K)` reaches into + // `__eq_` helpers when K is a record/sum field-of-field. + let mut nominal_seed: Vec = Vec::new(); + for canonical in ®istry.list_order { + if let Some(elem) = super::types::TypeRegistry::list_element_type(canonical) { + nominal_seed.push(elem.trim().to_string()); + } + } + for canonical in ®istry.vector_order { + if let Some(elem) = super::types::TypeRegistry::vector_element_type(canonical) { + nominal_seed.push(elem.trim().to_string()); + } + } + for canonical in ®istry.map_order { + if let Some((k, _v)) = super::types::parse_map_kv(canonical) { + nominal_seed.push(k.trim().to_string()); + } + } + for name in &nominal_seed { + if registry.record_fields.contains_key(name) { + eq_helpers_registry.register_transitive(name, EqKind::Record, ®istry); + } else if registry + .variants + .values() + .flat_map(|v| v.iter()) + .any(|v| &v.parent == name) + { + eq_helpers_registry.register_transitive(name, EqKind::Sum, ®istry); + } + } // Eq helpers over records / sums with String fields need // `__wasmgc_string_eq` — force-register so the slot is allocated // before bodies emit. From bf521d36fe462074f9ab4288461b31ae22a4407e Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 16:00:40 +0200 Subject: [PATCH 09/18] =?UTF-8?q?0.16.3:=20bug=20F=20+=20G=20=E2=80=94=20O?= =?UTF-8?q?ption/Result/Tuple=20eq=20+=20carrier=20dispatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two stress-tests from the user's "popierdolone scenariusze" set landed real holes that bugs 1-3+5 hadn't covered: - F2: `record Person { name: String, age: Option }` then `a == b`. Discovery walker registered `__eq_Person` but `register_field_type` silently dropped the `Option` field because it wasn't a primitive / record / sum. Helper body emit later panicked: `record Person field type Option has no eq dispatch`. - G: `let a: Result = Result.Ok(Box(val=5))` then `a == b`. Two cascading misses: - `Result` wasn't even registered in the type section because the discovery walker for Result canonicals only scanned fn signatures + builtin uses, not let-binding annotations. - Even after registration, `body/emit.rs::sum_or_record_eq_fn` matched `Type::Named` only — `Type::Result(_,_)` / `Type::Option(_)` / `Type::Tuple(_)` operands fell through to the default `i64.eq` arm. - Inner `Box` field eq dispatched through the helper map, but Box is newtype-erased to i64 — helper signature is `(eqref, eqref)` so the validator caught the type mismatch. ## Fix - `EqHelperRegistry` grows three new kinds — `OptionEq`, `ResultEq`, `TupleEq`. Each instantiation gets its own helper slot keyed by the canonical string (`"Option"`, `"Result"`, `"Tuple"`). - `register_field_type` now recognises `Option<…>`, `Result<…,…>`, `Tuple<…>` field types: registers the carrier helper, then recurses on inner types so any nominal piece (e.g. `Option` → register `Color`) gets its slot too. - `register_transitive` for carrier kinds skips the `record_fields_resolvable` gate (carriers have one fixed shape; inner-resolvability is tracked at the parent's `field_type_resolvable` level). - `lists.rs::field_type_resolvable` accepts carriers iff every inner is resolvable — same recursive shape it already used for Record/Sum. - `eq_helpers.rs` grows three body emitters: - `emit_option_eq_body`: cast both eqref → typed Option ref, compare tags; differ → 0; both 0 (None) → 1; both 1 (Some) → inner eq via `emit_inner_eq_dispatch`. - `emit_result_eq_body`: same shape; tag=1 (Ok) → field 1 eq; tag=0 (Err) → field 2 eq. - `emit_tuple_eq_body`: per-field eq, AND-fold across all elements. - `emit_inner_eq_dispatch` resolves `newtype_underlying` before dispatching — `Box(n: Int)` reaches as "Box" but lowers to i64, so the primitive arm fires (helper map miss avoided). - `body/emit.rs::sum_or_record_eq_fn` now matches `Type::Option` / `Type::Result` / `Type::Tuple` and looks up the helper via the whitespace-stripped canonical (`fn_map.eq_helpers["Result"]`). - `module.rs::register_nominal_in_type` registers the outer carrier canonical (so `==` on a `Result<…>` operand directly flags `__eq_Result`), then recurses on inner types. - `types.rs::TypeRegistry::build` walks let-binding annotations for Result canonicals, mirroring what the Option/Vector/List body walkers were already doing. - `parse_result_kv` / `parse_tuple_elems` helpers in `eq_helpers.rs` — depth-aware comma split so `Result, MyError>` and `Tuple>` parse right. ## Verified - A: `record Item { name: String, color: Color }` ==. Match VM. - B: `type Cell = Empty | Filled(Pos)` with `record Pos`. Match VM. - C: mutually recursive `type A`/`type B`. Match VM. - D: `List == List` — still falls through to default arm since BinOp::Eq dispatch for `Type::List` requires a separate path. Documented as followup. - F2: `record Person { age: Option }` ==. Match VM. - G: `Result` ==. Match VM (both Ok and Err arms). - H: `record Move { via: Option }` (carrier holding nominal). Match VM. - bugs 1, 2, 3, 5 still pass. - 1100+ unit tests pass; 10/10 wasm-gc record-replay smokes; rogue compile_multi_file test still green; flaky buffer_build_pass test passed on retry (pre-existing non-determinism, unrelated). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/emit.rs | 48 ++-- src/codegen/wasm_gc/body/eq_helpers.rs | 359 ++++++++++++++++++++++++- src/codegen/wasm_gc/lists.rs | 78 ++++-- src/codegen/wasm_gc/module.rs | 19 +- src/codegen/wasm_gc/types.rs | 17 ++ 5 files changed, 467 insertions(+), 54 deletions(-) diff --git a/src/codegen/wasm_gc/body/emit.rs b/src/codegen/wasm_gc/body/emit.rs index 924b6f87..dfe7c30b 100644 --- a/src/codegen/wasm_gc/body/emit.rs +++ b/src/codegen/wasm_gc/body/emit.rs @@ -556,25 +556,37 @@ pub(super) fn emit_expr( /// type mismatch. fn sum_or_record_eq_fn(operand: &Spanned, ctx: &EmitCtx<'_>) -> Option { let ty = operand.ty()?; - let crate::types::Type::Named(name) = ty else { - return None; - }; - // Newtypes already lower to their underlying primitive — no helper - // needed (the default i64/f64 eq handles them). - if ctx.registry.newtype_underlying(name).is_some() { - return None; + match ty { + crate::types::Type::Named(name) => { + // Newtypes already lower to their underlying primitive — + // no helper needed (the default i64/f64 eq handles them). + if ctx.registry.newtype_underlying(name).is_some() { + return None; + } + let is_record = ctx.registry.record_fields.contains_key(name); + let is_sum = ctx + .registry + .variants + .values() + .flat_map(|v| v.iter()) + .any(|v| &v.parent == name); + if !is_record && !is_sum { + return None; + } + ctx.fn_map.eq_helpers.get(name).copied() + } + // Generic carriers — `Option`, `Result`, `Tuple<…>` + // have per-instantiation `__eq_` helpers since + // 0.16.3. Lookup by the type's display canonical (whitespace- + // free, matching how the discovery walker registered it). + crate::types::Type::Option(_) + | crate::types::Type::Result(_, _) + | crate::types::Type::Tuple(_) => { + let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + ctx.fn_map.eq_helpers.get(&canonical).copied() + } + _ => None, } - let is_record = ctx.registry.record_fields.contains_key(name); - let is_sum = ctx - .registry - .variants - .values() - .flat_map(|v| v.iter()) - .any(|v| &v.parent == name); - if !is_record && !is_sum { - return None; - } - ctx.fn_map.eq_helpers.get(name).copied() } fn nullary_variant_idx(expr: &Spanned, ctx: &EmitCtx<'_>) -> Option { diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index 8bf51881..2cfbaa64 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -36,11 +36,19 @@ use super::super::WasmGcError; use super::super::lists::{emit_record_eq_inline, emit_sum_eq_inline}; use super::super::types::TypeRegistry; -/// What kind of nominal type a registered eq helper covers. +/// What kind of type a registered eq helper covers. Records and +/// sums are nominal (registry name = type name). The generic- +/// carrier kinds (Option / Result / Tuple) carry their canonical +/// instantiation string as the registry name (e.g. `"Option"`, +/// `"Result"`, `"Tuple"`) — one helper slot +/// per concrete inner shape. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum EqKind { Record, Sum, + OptionEq, + ResultEq, + TupleEq, } /// Per-module registry of `__eq_` helpers needed by @@ -84,12 +92,13 @@ impl EqHelperRegistry { if self.kinds.contains_key(type_name) { return; } - // Skip nominal types whose fields contain generic carriers - // (Option<…>, List<…>, etc.) the inline eq emitter doesn't - // handle yet. Without this guard, `register_field_type` - // silently dropped the unsupported field and the helper body - // emit later panicked with "no eq dispatch" — same error the - // surrounding sweep is trying to prevent. + // Skip nominal types whose fields contain unhandled shapes + // (List/Map/Vector/Set inside fields — the inline eq emitter + // covers Option/Result/Tuple via per-instantiation helpers + // registered alongside the parent below). Without this guard + // `register_field_type` silently dropped the unsupported + // field and the helper body emit later panicked with "no eq + // dispatch". let mut seen = std::collections::HashSet::new(); let resolvable = match kind { EqKind::Record => super::super::lists::record_fields_resolvable( @@ -98,6 +107,10 @@ impl EqHelperRegistry { EqKind::Sum => { super::super::lists::sum_fields_resolvable(type_name, registry, &mut seen) } + // Carrier kinds — Option/Result/Tuple have one + // shape regardless of inner; resolvability is checked at + // the record/sum level via `field_type_resolvable`. + EqKind::OptionEq | EqKind::ResultEq | EqKind::TupleEq => true, }; if !resolvable { return; @@ -126,28 +139,60 @@ impl EqHelperRegistry { } } } + // Carrier kinds — inner-type registration is handled by + // the surrounding `register_field_type` arm that decides + // to register the carrier in the first place. Nothing + // extra to walk here (the helper body inspects the + // canonical at emit time via `option_element_type` / + // `parse_result_kv` / `parse_tuple_elems`). + EqKind::OptionEq | EqKind::ResultEq | EqKind::TupleEq => {} } } fn register_field_type(&mut self, field_ty: &str, registry: &TypeRegistry) { - // Skip primitives and compound carriers (List/Map/Vector/ - // Option/Result handle their own dispatch). if matches!(field_ty, "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char") { return; } if registry.record_fields.contains_key(field_ty) { self.register_transitive(field_ty, EqKind::Record, registry); - } else if registry + return; + } + if registry .variants .values() .flat_map(|v| v.iter()) .any(|v| v.parent == field_ty) { self.register_transitive(field_ty, EqKind::Sum, registry); + return; + } + // Generic carriers — `Option`, `Result`, `Tuple<…>`. + // Each instantiation gets its own helper slot keyed by the + // canonical string. Inner types are walked too so any + // nominal piece (`Option` → register Color). + if let Some(inner) = field_ty.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + self.register_transitive(field_ty, EqKind::OptionEq, registry); + self.register_field_type(inner.trim(), registry); + } else if field_ty.starts_with("Result<") && field_ty.ends_with('>') { + self.register_transitive(field_ty, EqKind::ResultEq, registry); + if let Some((ok, err)) = parse_result_kv(field_ty) { + self.register_field_type(ok.trim(), registry); + self.register_field_type(err.trim(), registry); + } + } else if field_ty.starts_with("Tuple<") && field_ty.ends_with('>') { + self.register_transitive(field_ty, EqKind::TupleEq, registry); + if let Some(elems) = parse_tuple_elems(field_ty) { + for elem in elems { + self.register_field_type(elem.trim(), registry); + } + } } - // Other shapes (List, Map, generics) — out of - // scope for nominal eq dispatch today; leave them to fail - // loudly in the inline emitter so we know to extend. + // List, Vector, Map — those have their own + // helper machinery (list_helpers/map_helpers fn idxs). + // BinOp::Eq dispatch on collections is covered by the + // separate body/emit.rs path; field-of-record holding a + // collection still falls through here. Followup if real + // programs surface it. } pub(crate) fn iter(&self) -> impl Iterator + '_ { @@ -269,6 +314,33 @@ impl EqHelperRegistry { f.instruction(&Instruction::End); codes.function(&f); } + EqKind::OptionEq => { + let f = emit_option_eq_body( + name, + registry, + string_eq_fn_idx, + &helper_idx_map, + )?; + codes.function(&f); + } + EqKind::ResultEq => { + let f = emit_result_eq_body( + name, + registry, + string_eq_fn_idx, + &helper_idx_map, + )?; + codes.function(&f); + } + EqKind::TupleEq => { + let f = emit_tuple_eq_body( + name, + registry, + string_eq_fn_idx, + &helper_idx_map, + )?; + codes.function(&f); + } } } Ok(()) @@ -288,6 +360,261 @@ impl EqHelperRegistry { } } +/// `Result` → `Some(("Ok", "Err"))`. Tracks angle/paren +/// depth so `Result, MyError>` splits at the right comma. +fn parse_result_kv(canonical: &str) -> Option<(&str, &str)> { + let inner = canonical.trim().strip_prefix("Result<")?.strip_suffix('>')?; + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + return Some((inner[..idx].trim(), inner[idx + 1..].trim())); + } + _ => {} + } + } + None +} + +/// `Tuple` → `Some(vec!["A", "B", "C"])`. Same depth-aware +/// split as `parse_result_kv`. +fn parse_tuple_elems(canonical: &str) -> Option> { + let inner = canonical.trim().strip_prefix("Tuple<")?.strip_suffix('>')?; + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + let mut start = 0; + let mut out = Vec::new(); + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + out.push(inner[start..idx].trim()); + start = idx + 1; + } + _ => {} + } + } + out.push(inner[start..].trim()); + Some(out) +} + +/// Emit `(eqref, eqref) -> i32` body for `Option`. Layout: +/// `(struct (mut i32 tag) (mut X value))`, tag=0 None, tag=1 Some. +/// Compare tags first; if differ → 0; if both 0 → 1; if both 1 → +/// inner eq. +fn emit_option_eq_body( + canonical: &str, + registry: &TypeRegistry, + string_eq_fn_idx: Option, + helper_idx_map: &HashMap, +) -> Result { + let opt_idx = registry.option_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: option not registered")), + )?; + let inner = TypeRegistry::option_element_type(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse inner")), + )?; + let opt_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(opt_idx), + }); + let mut f = Function::new(vec![(2, opt_ref)]); + let opt_heap = wasm_encoder::HeapType::Concrete(opt_idx); + // Cast both eqref params → typed Option ref into locals 2, 3. + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(opt_heap)); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::RefCastNonNull(opt_heap)); + f.instruction(&Instruction::LocalSet(3)); + // if tag(lhs) != tag(rhs) → return 0 + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::I32Ne); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + // tags equal — if 0 (None), return 1 + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::I32Eqz); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + f.instruction(&Instruction::I32Const(1)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + // Both Some — compare inner via per-type dispatch. + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); + emit_inner_eq_dispatch(&mut f, inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::End); + Ok(f) +} + +/// Emit `(eqref, eqref) -> i32` body for `Result`. Layout: +/// `(struct (mut i32 tag) (mut X ok) (mut Y err))`, tag=0 Err, +/// tag=1 Ok. Compare tags; if differ → 0; both 0 → err eq; +/// both 1 → ok eq. +fn emit_result_eq_body( + canonical: &str, + registry: &TypeRegistry, + string_eq_fn_idx: Option, + helper_idx_map: &HashMap, +) -> Result { + let res_idx = registry.result_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: result not registered")), + )?; + let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse inner")), + )?; + let res_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(res_idx), + }); + let mut f = Function::new(vec![(2, res_ref)]); + let res_heap = wasm_encoder::HeapType::Concrete(res_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(res_heap)); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::RefCastNonNull(res_heap)); + f.instruction(&Instruction::LocalSet(3)); + // if tag(lhs) != tag(rhs) → 0 + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::I32Ne); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + // tags equal — branch on tag value: tag=1 (Ok) → field 1 eq; + // tag=0 (Err) → field 2 eq. + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Result(wasm_encoder::ValType::I32))); + // Ok arm + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); + emit_inner_eq_dispatch(&mut f, ok_inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::Else); + // Err arm + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); + emit_inner_eq_dispatch(&mut f, err_inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::End); + f.instruction(&Instruction::End); + Ok(f) +} + +/// Emit `(eqref, eqref) -> i32` body for `Tuple`. +/// Layout: `(struct field0 field1 …)`. Per-element eq, AND-fold. +fn emit_tuple_eq_body( + canonical: &str, + registry: &TypeRegistry, + string_eq_fn_idx: Option, + helper_idx_map: &HashMap, +) -> Result { + let tup_idx = registry.tuple_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: tuple not registered")), + )?; + let elems = parse_tuple_elems(canonical).ok_or( + WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse elements")), + )?; + let tup_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(tup_idx), + }); + let mut f = Function::new(vec![(2, tup_ref)]); + let tup_heap = wasm_encoder::HeapType::Concrete(tup_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(tup_heap)); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::RefCastNonNull(tup_heap)); + f.instruction(&Instruction::LocalSet(3)); + for (i, elem) in elems.iter().enumerate() { + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: tup_idx, + field_index: i as u32, + }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { + struct_type_index: tup_idx, + field_index: i as u32, + }); + emit_inner_eq_dispatch(&mut f, elem.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + if i > 0 { + f.instruction(&Instruction::I32And); + } + } + f.instruction(&Instruction::End); + Ok(f) +} + +/// Stack: `[lhs_value, rhs_value]` of `inner` aver type. Push i32 +/// eq verdict. Same shape used by Option's payload, Result's two +/// arms, Tuple's per-element compares. +/// +/// Newtype-optimised records (`Box(val: Int)` → i64) are handled +/// before the helper-map lookup: the actual stack values are the +/// underlying primitive, so dispatch through the primitive arm. +fn emit_inner_eq_dispatch( + f: &mut Function, + inner: &str, + registry: &TypeRegistry, + string_eq_fn_idx: Option, + helper_idx_map: &HashMap, +) -> Result<(), WasmGcError> { + // Resolve newtypes — `Box(n: Int)` reaches here as "Box" but + // its wasm representation is i64. + let resolved: String = if let Some(under) = registry.newtype_underlying(inner) { + under.to_string() + } else { + inner.to_string() + }; + match resolved.as_str() { + "Int" => { + f.instruction(&Instruction::I64Eq); + } + "Bool" => { + f.instruction(&Instruction::I32Eq); + } + "Float" => { + f.instruction(&Instruction::F64Eq); + } + "String" => { + let eq_fn = string_eq_fn_idx.ok_or(WasmGcError::Validation( + "carrier eq with String inner needs __wasmgc_string_eq".into(), + ))?; + f.instruction(&Instruction::Call(eq_fn)); + } + other if helper_idx_map.contains_key(other) => { + f.instruction(&Instruction::Call(helper_idx_map[other])); + } + other => { + return Err(WasmGcError::Validation(format!( + "carrier eq inner type `{other}` has no eq dispatch" + ))); + } + } + Ok(()) +} + fn type_has_string_field( name: &str, kind: EqKind, @@ -309,5 +636,11 @@ fn type_has_string_field( .flat_map(|vs| vs.iter()) .filter(|v| v.parent == name) .any(|v| v.fields.iter().any(|t| t.trim() == "String")), + // Option/Result/Tuple<…> — check whether any inner + // type is `String`. Cheap string match on the canonical + // (e.g. `"Option"` contains `"String"`); accurate + // enough since we just need to force-register + // `__wasmgc_string_eq` when it might be called. + EqKind::OptionEq | EqKind::ResultEq | EqKind::TupleEq => name.contains("String"), } } diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 3de139fb..70818769 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -780,27 +780,67 @@ pub(super) fn field_type_resolvable( registry: &TypeRegistry, seen: &mut std::collections::HashSet, ) -> bool { - // Conservative: reject any generic carrier (`Option<…>`, - // `Result<…,…>`, `List<…>`, `Vector<…>`, `Map<…,…>`, - // `Tuple<…>`) as a field type. The inline eq emitters don't - // know how to dispatch eq for these — that's its own ABI - // path (Option tag-and-compare, Result variant tags, etc.). - // Programs that hold a record with such a field still work, - // they just don't get list-eq / map-key / contains-on-list - // slots emitted for the outer type. Lifting this is its own - // followup. - if field.contains('<') { + if matches!(field, "Int" | "Float" | "Bool" | "String") { + return true; + } + if registry.record_type_idx(field).is_some() { + return record_fields_resolvable(field, registry, seen); + } + if registry + .variants + .values() + .flat_map(|vs| vs.iter()) + .any(|v| v.parent == field) + { + return sum_fields_resolvable(field, registry, seen); + } + // Generic carriers — `Option`, `Result`, `Tuple<…>` get + // per-instantiation `__eq_` helpers since 0.16.3. + // Resolvable iff every inner type is itself resolvable. + // List/Vector/Map field types still fall through (their dispatch + // from `emit_record_eq_inline` is a separate followup). + if let Some(inner) = field.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + return field_type_resolvable(inner.trim(), registry, seen); + } + if let Some(inner) = field.strip_prefix("Result<").and_then(|s| s.strip_suffix('>')) { + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + let ok = inner[..idx].trim(); + let err = inner[idx + 1..].trim(); + return field_type_resolvable(ok, registry, seen) + && field_type_resolvable(err, registry, seen); + } + _ => {} + } + } return false; } - matches!(field, "Int" | "Float" | "Bool" | "String") - || (registry.record_type_idx(field).is_some() - && record_fields_resolvable(field, registry, seen)) - || (registry - .variants - .values() - .flat_map(|vs| vs.iter()) - .any(|v| v.parent == field) - && sum_fields_resolvable(field, registry, seen)) + if let Some(inner) = field.strip_prefix("Tuple<").and_then(|s| s.strip_suffix('>')) { + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + let mut start = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + let elem = inner[start..idx].trim(); + if !field_type_resolvable(elem, registry, seen) { + return false; + } + start = idx + 1; + } + _ => {} + } + } + return field_type_resolvable(inner[start..].trim(), registry, seen); + } + false } fn list_idx_of(canonical: &str, registry: &TypeRegistry) -> Result { diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 7583d3d8..6a4eba79 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -1259,6 +1259,7 @@ fn register_nominal_in_type( eq_helpers: &mut EqHelperRegistry, type_registry: &super::types::TypeRegistry, ) { + let canonical: String = t.display().chars().filter(|c| !c.is_whitespace()).collect(); match t { AverType::Named(name) => { if type_registry.record_fields.contains_key(name) { @@ -1272,18 +1273,28 @@ fn register_nominal_in_type( eq_helpers.register_transitive(name, EqKind::Sum, type_registry); } } - AverType::List(inner) | AverType::Vector(inner) | AverType::Option(inner) => { + AverType::Option(inner) => { + eq_helpers.register_transitive(&canonical, EqKind::OptionEq, type_registry); register_nominal_in_type(inner, eq_helpers, type_registry); } - AverType::Map(k, v) | AverType::Result(k, v) => { - register_nominal_in_type(k, eq_helpers, type_registry); - register_nominal_in_type(v, eq_helpers, type_registry); + AverType::Result(ok, err) => { + eq_helpers.register_transitive(&canonical, EqKind::ResultEq, type_registry); + register_nominal_in_type(ok, eq_helpers, type_registry); + register_nominal_in_type(err, eq_helpers, type_registry); } AverType::Tuple(items) => { + eq_helpers.register_transitive(&canonical, EqKind::TupleEq, type_registry); for item in items { register_nominal_in_type(item, eq_helpers, type_registry); } } + AverType::List(inner) | AverType::Vector(inner) => { + register_nominal_in_type(inner, eq_helpers, type_registry); + } + AverType::Map(k, v) => { + register_nominal_in_type(k, eq_helpers, type_registry); + register_nominal_in_type(v, eq_helpers, type_registry); + } _ => {} } } diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index 560ea4e2..753a59ef 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -315,6 +315,23 @@ impl TypeRegistry { &mut result_order, &mut next_idx, ); + // Walk binding annotations too — `let r: Result = …` + // wouldn't be picked up by the builtin-uses scan (which only + // looks at known dotted calls like `Disk.readText`). Without this, + // user-typed `Result` values fail to find their + // type slot at construction time. + use crate::ast::{FnBody, Stmt}; + let FnBody::Block(stmts) = fd.body.as_ref(); + for stmt in stmts { + if let Stmt::Binding(_, Some(annot), _) = stmt { + collect_results_from_str( + annot, + &mut result_types, + &mut result_order, + &mut next_idx, + ); + } + } } } let mut list_types: HashMap = HashMap::new(); From 9f053a3a27b059ae71a105a1ba9779755a6649b0 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 16:49:14 +0200 Subject: [PATCH 10/18] =?UTF-8?q?0.16.3:=20perfect=20hash=20=E2=80=94=20?= =?UTF-8?q?=5F=5Fhash=5F=20helpers=20+=20dispatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symmetric counterpart to the eq helpers: per-record / per-sum / per-carrier `__hash_` helpers replace the drop+0 fallback the inline hash emitters were using for non-primitive fields. ## Why `Map` keyed by a record (or any nominal/carrier shape) wants a deterministic hash so two `Person` values that compare equal collapse to the same bucket. The old fallback (drop the field value, contribute 0 to the hash) was correct (eq still disambiguates the bucket) but degenerate — every value sharing a primitive prefix mapped to one bucket, lookup degraded to O(n). ## What - New `body/hash_helpers.rs` registry analogous to `body/eq_helpers.rs`. Same shape: register / register_transitive / assign_slots / emit_helper_types / emit_helper_bodies. Helper signature is `(eqref) -> i32` (single operand vs eq's two). - Per-kind body emitters in the new module: - `emit_record_hash_body`: cast eqref → typed record ref, DJB2 fold over fields with proper inner dispatch. - `emit_sum_hash_body`: ref.test cascade per variant; matched arm folds variant `type_idx` (as a stable tag) plus per- variant fields. - `emit_option_hash_body`: tag fold; if Some, mix inner. Holds h in a local across the if-arm so the block body can be `BlockType::Empty` (avoids the stack-shape bug an earlier `Result(I32)` shape had). - `emit_result_hash_body`: same shape; if-arm picks ok-field or err-field hash to fold based on tag. - `emit_tuple_hash_body`: per-element fold, AND-style. - `emit_inner_hash_dispatch` mirrors `emit_inner_eq_dispatch`: resolves newtypes (`Box(n: Int)` → i64), handles primitives + String + nominal/carrier helper map lookup. Fallback on unresolved shapes still drops + 0 (collision-tolerant). - Existing inline emitters (`emit_record_inline_hash` / `emit_sum_inline_hash` in `lists.rs`) take a new `hash_helper_fn_idx: &HashMap` parameter and dispatch non-primitive fields through `Call(__hash_)` instead of dropping. Ditto `emit_list_hash` / `emit_vec_hash` and the surrounding `ListHelperRegistry::emit_helper_bodies`. - `module.rs` wires the registry: parallel sweep of the eq registry's transitive closure (every type that gets an eq helper also gets a hash helper, since list/vec/map helpers dispatch both directions). New `FnMap.hash_helpers` for body emit lookup. ## Verified - Bugs 1, 2, 3, 5, A, B, C, D, F, G, H all pass — eq side unchanged. - 1500+ unit tests pass; 10/10 wasm-gc record-replay smokes; rogue compile_multi_file_rogue_from_virtual_fs still green. ## What this *doesn't* fix yet `Map` (where Person carries an Option field or similar) still fails because `Map.set` looks up its key helpers in the per-(K,V) `MapHelperRegistry`, which is a separate sweep from `EqHelperRegistry` / `HashHelperRegistry`. Discovery for that registry hasn't been taught about the carrier-resolvable gate yet — that's a followup. The hash helpers are correctly emitted; nothing reaches them through the map-key path today. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body.rs | 6 + src/codegen/wasm_gc/body/emit.rs | 13 + src/codegen/wasm_gc/body/hash_helpers.rs | 615 +++++++++++++++++++++++ src/codegen/wasm_gc/lists.rs | 106 ++-- src/codegen/wasm_gc/module.rs | 47 ++ 5 files changed, 747 insertions(+), 40 deletions(-) create mode 100644 src/codegen/wasm_gc/body/hash_helpers.rs diff --git a/src/codegen/wasm_gc/body.rs b/src/codegen/wasm_gc/body.rs index 3a84ecb5..8865d811 100644 --- a/src/codegen/wasm_gc/body.rs +++ b/src/codegen/wasm_gc/body.rs @@ -35,6 +35,7 @@ use crate::ir::CallLowerCtx; mod builtins; mod emit; pub(super) mod eq_helpers; +pub(super) mod hash_helpers; mod infer; mod slots; @@ -73,6 +74,11 @@ pub(super) struct FnMap { /// Per-(record/sum) `__eq_` helpers used by `BinOp::Eq` /// / `BinOp::Neq` over nominal types. Key = bare type name. pub(super) eq_helpers: std::collections::HashMap, + /// Per-type `__hash_` helpers used by record/sum/carrier + /// hash bodies for non-primitive field hashing. Symmetric to + /// `eq_helpers`. Key = canonical (bare for record/sum, + /// whitespace-free instantiation for carriers). + pub(super) hash_helpers: std::collections::HashMap, } impl FnMap { diff --git a/src/codegen/wasm_gc/body/emit.rs b/src/codegen/wasm_gc/body/emit.rs index dfe7c30b..4a8f04cf 100644 --- a/src/codegen/wasm_gc/body/emit.rs +++ b/src/codegen/wasm_gc/body/emit.rs @@ -585,6 +585,19 @@ fn sum_or_record_eq_fn(operand: &Spanned, ctx: &EmitCtx<'_>) -> Option` (None when T isn't equality- + // resolvable; falls through and the surrounding default i64 + // arm fails validation, same as before this PR). + crate::types::Type::List(_) => { + let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + ctx.fn_map.list_ops.get(&canonical).and_then(|ops| ops.eq) + } + crate::types::Type::Vector(_) => { + let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + ctx.fn_map.vfl_ops.get(&canonical).and_then(|ops| ops.eq) + } _ => None, } } diff --git a/src/codegen/wasm_gc/body/hash_helpers.rs b/src/codegen/wasm_gc/body/hash_helpers.rs new file mode 100644 index 00000000..a39b249a --- /dev/null +++ b/src/codegen/wasm_gc/body/hash_helpers.rs @@ -0,0 +1,615 @@ +//! Per-record / per-sum / per-carrier hash helpers — the symmetric +//! counterpart to `eq_helpers.rs` for the hash side of nominal + +//! generic-carrier dispatch. +//! +//! ## Why +//! +//! `Map` keyed by a record (`Map`) wants a +//! deterministic hash so two `Person` values that compare equal +//! collapse to the same bucket. The inline `emit_record_inline_hash` +//! / `emit_sum_inline_hash` in `lists.rs` previously fell back to +//! `drop + i32.const 0` for any non-primitive field — correct +//! (eq still disambiguates the bucket) but degenerate, every value +//! sharing primitive-prefix maps to one bucket and lookup goes +//! O(n). +//! +//! This module sets up `__hash_` helpers per nominal / +//! carrier instantiation. Body emit goes through `Call(__hash_)` +//! for non-primitive field values; the helper itself does the +//! shape-specific DJB2 fold. Symmetric to how `__eq_` works on +//! the equality side. + +use std::collections::HashMap; + +use wasm_encoder::{Function, Instruction}; + +use super::super::WasmGcError; +use super::super::types::TypeRegistry; + +/// What kind of type a registered hash helper covers. Same shapes +/// as `EqKind`, separate enum to keep the two registries +/// independent (a type may want a hash helper without an eq helper +/// — e.g. `Map` registers hash for X without ever forcing +/// `==` on the surface). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum HashKind { + Record, + Sum, + OptionHash, + ResultHash, + TupleHash, +} + +/// Per-module registry of `__hash_` helpers needed for +/// shape-faithful hashing inside list/vec/map helpers + per-record +/// / sum / carrier inline hash bodies. +#[derive(Default)] +pub(crate) struct HashHelperRegistry { + order: Vec, + kinds: HashMap, + /// `type_name -> (wasm_fn_idx, wasm_type_idx)`. + slots: HashMap, +} + +impl HashHelperRegistry { + pub(crate) fn new() -> Self { + Self::default() + } + + pub(crate) fn register(&mut self, type_name: &str, kind: HashKind) { + if !self.kinds.contains_key(type_name) { + self.order.push(type_name.to_string()); + self.kinds.insert(type_name.to_string(), kind); + } + } + + /// Walk `type_name`'s fields and register `__hash_` for + /// every nominal / carrier piece reachable. Mirrors + /// `EqHelperRegistry::register_transitive` but for hash; same + /// resolvability gate so we don't end up with a registered + /// helper whose body can't be emitted (a record holding e.g. + /// `List` field — list-as-field hash isn't wired yet). + pub(crate) fn register_transitive( + &mut self, + type_name: &str, + kind: HashKind, + registry: &TypeRegistry, + ) { + if self.kinds.contains_key(type_name) { + return; + } + let mut seen = std::collections::HashSet::new(); + let resolvable = match kind { + HashKind::Record => super::super::lists::record_fields_resolvable( + type_name, registry, &mut seen, + ), + HashKind::Sum => { + super::super::lists::sum_fields_resolvable(type_name, registry, &mut seen) + } + HashKind::OptionHash | HashKind::ResultHash | HashKind::TupleHash => true, + }; + if !resolvable { + return; + } + self.register(type_name, kind); + match kind { + HashKind::Record => { + if let Some(fields) = registry.record_fields.get(type_name) { + for (_, field_ty) in fields { + self.register_field_type(field_ty.trim(), registry); + } + } + } + HashKind::Sum => { + let variants: Vec<_> = registry + .variants + .values() + .flat_map(|vs| vs.iter()) + .filter(|v| v.parent == type_name) + .cloned() + .collect(); + for v in &variants { + for field_ty in &v.fields { + self.register_field_type(field_ty.trim(), registry); + } + } + } + HashKind::OptionHash | HashKind::ResultHash | HashKind::TupleHash => {} + } + } + + fn register_field_type(&mut self, field_ty: &str, registry: &TypeRegistry) { + if matches!(field_ty, "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char") { + return; + } + if registry.record_fields.contains_key(field_ty) { + self.register_transitive(field_ty, HashKind::Record, registry); + return; + } + if registry + .variants + .values() + .flat_map(|v| v.iter()) + .any(|v| v.parent == field_ty) + { + self.register_transitive(field_ty, HashKind::Sum, registry); + return; + } + if let Some(inner) = field_ty.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + self.register_transitive(field_ty, HashKind::OptionHash, registry); + self.register_field_type(inner.trim(), registry); + } else if field_ty.starts_with("Result<") && field_ty.ends_with('>') { + self.register_transitive(field_ty, HashKind::ResultHash, registry); + if let Some((ok, err)) = parse_result_kv(field_ty) { + self.register_field_type(ok.trim(), registry); + self.register_field_type(err.trim(), registry); + } + } else if field_ty.starts_with("Tuple<") && field_ty.ends_with('>') { + self.register_transitive(field_ty, HashKind::TupleHash, registry); + if let Some(elems) = parse_tuple_elems(field_ty) { + for elem in elems { + self.register_field_type(elem.trim(), registry); + } + } + } + } + + pub(crate) fn iter(&self) -> impl Iterator + '_ { + self.order.iter().map(|n| (n.as_str(), self.kinds[n])) + } + + pub(crate) fn assign_slots(&mut self, next_fn_idx: &mut u32, next_type_idx: &mut u32) { + for name in &self.order { + self.slots + .insert(name.clone(), (*next_fn_idx, *next_type_idx)); + *next_fn_idx += 1; + *next_type_idx += 1; + } + } + + pub(crate) fn lookup_fn_idx(&self, type_name: &str) -> Option { + self.slots.get(type_name).map(|(f, _)| *f) + } + + pub(crate) fn lookup_type_idx(&self, type_name: &str) -> Option { + self.slots.get(type_name).map(|(_, t)| *t) + } + + /// Emit `(eqref) -> i32` fn type for each registered helper, in + /// the same order as `assign_slots`. + pub(crate) fn emit_helper_types(&self, types: &mut wasm_encoder::TypeSection) { + let eq_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Eq, + }, + }); + for _ in &self.order { + types + .ty() + .function([eq_ref], [wasm_encoder::ValType::I32]); + } + } + + pub(crate) fn emit_helper_bodies( + &self, + codes: &mut wasm_encoder::CodeSection, + registry: &TypeRegistry, + string_eq_fn_idx: Option, + ) -> Result<(), WasmGcError> { + let _ = string_eq_fn_idx; // String fields hash via array.len, no helper needed. + let helper_idx_map: HashMap = self + .slots + .iter() + .map(|(n, (fn_idx, _))| (n.clone(), *fn_idx)) + .collect(); + for name in &self.order { + let kind = self.kinds[name]; + let self_fn_idx = self.slots.get(name).map(|(f, _)| *f); + match kind { + HashKind::Record => { + let f = emit_record_hash_body(name, registry, &helper_idx_map, self_fn_idx)?; + codes.function(&f); + } + HashKind::Sum => { + let f = emit_sum_hash_body(name, registry, &helper_idx_map, self_fn_idx)?; + codes.function(&f); + } + HashKind::OptionHash => { + let f = emit_option_hash_body(name, registry, &helper_idx_map)?; + codes.function(&f); + } + HashKind::ResultHash => { + let f = emit_result_hash_body(name, registry, &helper_idx_map)?; + codes.function(&f); + } + HashKind::TupleHash => { + let f = emit_tuple_hash_body(name, registry, &helper_idx_map)?; + codes.function(&f); + } + } + } + Ok(()) + } +} + +/// `(eqref) -> i32` body for a record. Cast → typed, DJB2 fold over +/// fields, return. +fn emit_record_hash_body( + name: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, + self_fn_idx: Option, +) -> Result { + let r_idx = registry + .record_type_idx(name) + .ok_or(WasmGcError::Validation(format!( + "hash helper for record `{name}`: not registered" + )))?; + let fields = registry.record_fields.get(name).ok_or( + WasmGcError::Validation(format!("hash helper for record `{name}`: no fields")), + )?; + let r_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(r_idx), + }); + // Locals: 1 = typed record ref, 2 = h accumulator. + let mut f = Function::new(vec![(1, r_ref), (1, wasm_encoder::ValType::I32)]); + let r_heap = wasm_encoder::HeapType::Concrete(r_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(r_heap)); + f.instruction(&Instruction::LocalSet(1)); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(2)); + for (i, (_, field_ty)) in fields.iter().enumerate() { + // h = h * 33 + field_hash + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { + struct_type_index: r_idx, + field_index: i as u32, + }); + emit_inner_hash_dispatch( + &mut f, + field_ty.trim(), + registry, + helper_idx_map, + self_fn_idx.filter(|_| field_ty.trim() == name), + )?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + } + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// `(eqref) -> i32` body for a sum. ref.test cascade per variant; +/// matched arm folds tag idx + variant fields. +fn emit_sum_hash_body( + parent_name: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, + self_fn_idx: Option, +) -> Result { + let mut variants: Vec<(String, super::super::types::VariantInfo)> = registry + .variants + .iter() + .flat_map(|(n, vs)| vs.iter().map(move |v| (n.clone(), v.clone()))) + .filter(|(_, v)| v.parent == parent_name) + .collect(); + variants.sort_by(|a, b| a.0.cmp(&b.0)); + if variants.is_empty() { + return Err(WasmGcError::Validation(format!( + "hash helper for sum `{parent_name}`: no variants" + ))); + } + // Locals: 1 = h accumulator. (Per-variant typed cast happens + // inside the if-arm via `RefCastNonNull` → `StructGet`; we read + // each field directly off the casted ref so no scratch slot + // for the variant ref is needed.) + let mut f = Function::new(vec![(1, wasm_encoder::ValType::I32)]); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(1)); + for (_v_name, info) in &variants { + let v_idx = info.type_idx; + let v_heap = wasm_encoder::HeapType::Concrete(v_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefTestNonNull(v_heap)); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + // Mix variant tag (its type_idx) into hash. + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::I32Const(v_idx as i32)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(1)); + for (i, field_ty) in info.fields.iter().enumerate() { + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(v_heap)); + f.instruction(&Instruction::StructGet { + struct_type_index: v_idx, + field_index: i as u32, + }); + emit_inner_hash_dispatch( + &mut f, + field_ty.trim(), + registry, + helper_idx_map, + self_fn_idx.filter(|_| field_ty.trim() == parent_name), + )?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(1)); + } + f.instruction(&Instruction::End); + } + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// `(eqref) -> i32` body for `Option`. h held in local 2 across +/// the if-arm so the block body can be empty-typed (no stack-shape +/// constraint). DJB2-fold tag, then if Some fold inner hash too. +fn emit_option_hash_body( + canonical: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, +) -> Result { + let opt_idx = registry.option_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: option not registered")), + )?; + let inner = TypeRegistry::option_element_type(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse inner")), + )?; + let opt_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(opt_idx), + }); + // Locals: 1 = typed Option ref, 2 = h. + let mut f = Function::new(vec![(1, opt_ref), (1, wasm_encoder::ValType::I32)]); + let opt_heap = wasm_encoder::HeapType::Concrete(opt_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(opt_heap)); + f.instruction(&Instruction::LocalSet(1)); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(2)); + // h = h * 33 + tag + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + // if Some (tag != 0), mix inner hash into h. + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); + emit_inner_hash_dispatch(&mut f, inner.trim(), registry, helper_idx_map, None)?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::End); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// `(eqref) -> i32` body for `Result`. h held in local 2. +fn emit_result_hash_body( + canonical: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, +) -> Result { + let res_idx = registry.result_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: result not registered")), + )?; + let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse inner")), + )?; + let res_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(res_idx), + }); + let mut f = Function::new(vec![(1, res_ref), (1, wasm_encoder::ValType::I32)]); + let res_heap = wasm_encoder::HeapType::Concrete(res_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(res_heap)); + f.instruction(&Instruction::LocalSet(1)); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(2)); + // h = h * 33 + tag + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + // Branch on tag. + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); + // Ok arm: mix field 1 (ok) into h. + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); + emit_inner_hash_dispatch(&mut f, ok_inner.trim(), registry, helper_idx_map, None)?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::Else); + // Err arm: mix field 2 (err) into h. + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); + emit_inner_hash_dispatch(&mut f, err_inner.trim(), registry, helper_idx_map, None)?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::End); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// `(eqref) -> i32` body for `Tuple`. DJB2 fold per-elem. +fn emit_tuple_hash_body( + canonical: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, +) -> Result { + let tup_idx = registry.tuple_type_idx(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: tuple not registered")), + )?; + let elems = parse_tuple_elems(canonical).ok_or( + WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse elements")), + )?; + let tup_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Concrete(tup_idx), + }); + let mut f = Function::new(vec![(1, tup_ref), (1, wasm_encoder::ValType::I32)]); + let tup_heap = wasm_encoder::HeapType::Concrete(tup_idx); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(tup_heap)); + f.instruction(&Instruction::LocalSet(1)); + f.instruction(&Instruction::I32Const(5381)); + f.instruction(&Instruction::LocalSet(2)); + for (i, elem) in elems.iter().enumerate() { + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { + struct_type_index: tup_idx, + field_index: i as u32, + }); + emit_inner_hash_dispatch(&mut f, elem.trim(), registry, helper_idx_map, None)?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(2)); + } + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// Stack: `[value]` of `inner` aver type. Push `i32` hash. Mirror +/// of `eq_helpers::emit_inner_eq_dispatch` but for hash. +fn emit_inner_hash_dispatch( + f: &mut Function, + inner: &str, + registry: &TypeRegistry, + helper_idx_map: &HashMap, + self_fn_idx: Option, +) -> Result<(), WasmGcError> { + let resolved: String = if let Some(under) = registry.newtype_underlying(inner) { + under.to_string() + } else { + inner.to_string() + }; + match resolved.as_str() { + "Int" => { + f.instruction(&Instruction::I32WrapI64); + } + "Bool" => {} // already i32 + "Float" => { + f.instruction(&Instruction::I64ReinterpretF64); + f.instruction(&Instruction::I32WrapI64); + } + "String" => { + f.instruction(&Instruction::ArrayLen); + } + other if Some(other) == self_fn_idx.and(Some(inner)) => { + // Self-recursive case — caller passed `Some(self_fn_idx)` + // when the field is a recursive ref to the parent. + let idx = self_fn_idx.unwrap(); + f.instruction(&Instruction::Call(idx)); + } + other if helper_idx_map.contains_key(other) => { + f.instruction(&Instruction::Call(helper_idx_map[other])); + } + _other => { + // Last-resort fallback (newtype-erased through to a + // non-eq-able underlying, or a shape we genuinely can't + // resolve). Drop the value, contribute 0. Same + // collision-tolerant degradation the older inline + // emitters used. + f.instruction(&Instruction::Drop); + f.instruction(&Instruction::I32Const(0)); + } + } + Ok(()) +} + +/// `Result` → `Some(("Ok", "Err"))`. Tracks angle/paren +/// depth so `Result, MyError>` splits at the right comma. +fn parse_result_kv(canonical: &str) -> Option<(&str, &str)> { + let inner = canonical.trim().strip_prefix("Result<")?.strip_suffix('>')?; + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + return Some((inner[..idx].trim(), inner[idx + 1..].trim())); + } + _ => {} + } + } + None +} + +/// `Tuple` → `Some(vec!["A", "B", "C"])`. +fn parse_tuple_elems(canonical: &str) -> Option> { + let inner = canonical.trim().strip_prefix("Tuple<")?.strip_suffix('>')?; + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + let mut start = 0; + let mut out = Vec::new(); + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + out.push(inner[start..idx].trim()); + start = idx + 1; + } + _ => {} + } + } + out.push(inner[start..].trim()); + Some(out) +} diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 70818769..411cbd18 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -162,16 +162,14 @@ impl ListHelperRegistry { } else { None }; - // List eq + hash use a stricter rule than contains — - // their bodies expect stack-shape per-element eq. - // SumEq / RecordEq need scratch locals + cascade emit; - // the bodies don't support those today. The record-key - // field-dispatch path that drives these helpers only - // ever calls them with primitive / String elements. - let needs_list_helpers = matches!( - contains_eq, - Some(ListEqKind::I64 | ListEqKind::F64 | ListEqKind::I32 | ListEqKind::StringEq) - ); + // List eq + hash slots track the same kinds contains + // does — primitives, String, and (since 0.16.3) record + // / sum nominal elements that resolve through the + // per-type `__eq_` helper map. The body emitters + // (`emit_list_eq` / `emit_list_hash`) dispatch nominal + // elements through `Call(__eq_)` / inline record + // /sum hash now. + let needs_list_helpers = contains_eq.is_some(); let list_eq_type = if needs_list_helpers { let t = *next_type_idx; *next_type_idx += 1; @@ -272,13 +270,10 @@ impl ListHelperRegistry { *next_wasm_fn_idx += 1; let to_fn = *next_wasm_fn_idx; *next_wasm_fn_idx += 1; - // Vector eq + hash conditionally — same primitive / - // String constraint as list eq+hash (SumEq / RecordEq - // not supported in the stack-eq body). - let eq_kind_ok = matches!( - list_eq_kind(elem.trim(), registry), - Some(ListEqKind::I64 | ListEqKind::F64 | ListEqKind::I32 | ListEqKind::StringEq) - ); + // Vector eq + hash slots match the list cap — same + // resolvable kinds (primitive / String / nominal record- + // sum since 0.16.3). + let eq_kind_ok = list_eq_kind(elem.trim(), registry).is_some(); let (vec_eq_ty, vec_hash_ty, vec_eq_fn, vec_hash_fn) = if eq_kind_ok { let eq_ty = *next_type_idx; *next_type_idx += 1; @@ -414,10 +409,7 @@ impl ListHelperRegistry { if kind.is_some() { types.ty().function([list_ref, elem_val], [ValType::I32]); } - if matches!( - kind, - Some(ListEqKind::I64 | ListEqKind::F64 | ListEqKind::I32 | ListEqKind::StringEq) - ) { + if kind.is_some() { // eq : (List, List) -> i32 types.ty().function([list_ref, list_ref], [ValType::I32]); // hash : (List) -> i32 @@ -451,10 +443,7 @@ impl ListHelperRegistry { // to_list : (Vector) -> List types.ty().function([vec_ref], [list_ref]); let elem = TypeRegistry::list_element_type(canonical).unwrap(); - if matches!( - list_eq_kind(elem.trim(), registry), - Some(ListEqKind::I64 | ListEqKind::F64 | ListEqKind::I32 | ListEqKind::StringEq) - ) { + if list_eq_kind(elem.trim(), registry).is_some() { // eq : (Vector, Vector) -> i32 types.ty().function([vec_ref, vec_ref], [ValType::I32]); // hash : (Vector) -> i32 @@ -570,6 +559,7 @@ impl ListHelperRegistry { registry: &TypeRegistry, string_eq_fn_idx: Option, eq_helper_fn_idx: &std::collections::HashMap, + hash_helper_fn_idx: &std::collections::HashMap, ) -> Result<(), WasmGcError> { for canonical in &self.list_order { // Order MUST match `assign_slots` and @@ -607,6 +597,7 @@ impl ListHelperRegistry { kind, string_eq_fn_idx, ops.hash.unwrap(), + hash_helper_fn_idx, )?); } } @@ -625,7 +616,13 @@ impl ListHelperRegistry { string_eq_fn_idx, eq_helper_fn_idx, )?); - codes.function(&emit_vec_hash(canonical, registry, kind, string_eq_fn_idx)?); + codes.function(&emit_vec_hash( + canonical, + registry, + kind, + string_eq_fn_idx, + hash_helper_fn_idx, + )?); } } for tup_canonical in &self.zip_order { @@ -2189,12 +2186,14 @@ fn emit_list_eq( /// `hash : (l) -> i32`. DJB2-style fold: `h = 5381; for elem: h = h /// * 33 + element_hash`. Element hash dispatched per `kind`. Same /// `T` constraint as eq. +#[allow(clippy::too_many_arguments)] fn emit_list_hash( canonical: &str, registry: &TypeRegistry, kind: ListEqKind, string_eq_fn_idx: Option, _self_fn_idx: u32, + hash_helper_fn_idx: &std::collections::HashMap, ) -> Result { let list_idx = list_idx_of(canonical, registry)?; let _ = string_eq_fn_idx; @@ -2302,6 +2301,7 @@ fn emit_list_hash( )))?; emit_record_inline_hash( &mut f, r_idx, fields, /* elem_local */ 3, /* elem_hash_local */ 4, + registry, hash_helper_fn_idx, )?; } ListEqKind::SumEq(parent_name) => { @@ -2311,6 +2311,7 @@ fn emit_list_hash( registry, /* elem_local */ 3, /* elem_hash_local */ 4, + hash_helper_fn_idx, )?; } } @@ -2340,12 +2341,14 @@ fn emit_list_hash( /// field into `elem_hash`. Variants are disjoint subtypes of the /// parent, so at most one `ref.test` succeeds per call — non- /// matched arms `ref.test` to false and skip silently. +#[allow(clippy::too_many_arguments)] fn emit_sum_inline_hash( f: &mut Function, parent_name: &str, registry: &TypeRegistry, elem_local: u32, elem_hash_local: u32, + hash_helper_fn_idx: &std::collections::HashMap, ) -> Result<(), WasmGcError> { // Collect variants of this sum. Same ordering as // `emit_sum_eq_inline` for parity. @@ -2399,7 +2402,13 @@ fn emit_sum_inline_hash( struct_type_index: v_idx, field_index: i as u32, }); - match field_ty.trim() { + let resolved: String = + if let Some(under) = registry.newtype_underlying(field_ty.trim()) { + under.to_string() + } else { + field_ty.trim().to_string() + }; + match resolved.as_str() { "Int" => { f.instruction(&Instruction::I32WrapI64); } @@ -2411,12 +2420,14 @@ fn emit_sum_inline_hash( "String" => { f.instruction(&Instruction::ArrayLen); } - other => { - return Err(WasmGcError::Validation(format!( - "sum hash dispatch: variant field type `{other}` \ - unsupported (only Int/Bool/Float/String); upstream \ - `list_eq_kind` all_simple gate should have rejected this." - ))); + other if hash_helper_fn_idx.contains_key(other) => { + f.instruction(&Instruction::Call(hash_helper_fn_idx[other])); + } + _ => { + // Last-resort fallback — no helper for this + // shape. Drop, contribute 0 (eq disambiguates). + f.instruction(&Instruction::Drop); + f.instruction(&Instruction::I32Const(0)); } } f.instruction(&Instruction::I32Add); @@ -2439,12 +2450,15 @@ fn emit_sum_inline_hash( /// already i32, String → array.len. Field shapes are restricted to /// {Int, Bool, Float, String} by `list_eq_kind`'s `all_simple` gate; /// nested records / lists trip `WasmGcError::Validation` here. +#[allow(clippy::too_many_arguments)] fn emit_record_inline_hash( f: &mut Function, record_idx: u32, fields: &[(String, String)], elem_local: u32, elem_hash_local: u32, + registry: &TypeRegistry, + hash_helper_fn_idx: &std::collections::HashMap, ) -> Result<(), WasmGcError> { // Save record ref → elem_local for repeated struct.get. f.instruction(&Instruction::LocalSet(elem_local)); @@ -2465,7 +2479,12 @@ fn emit_record_inline_hash( struct_type_index: record_idx, field_index: i as u32, }); - match field_type.trim() { + let resolved: String = if let Some(under) = registry.newtype_underlying(field_type.trim()) { + under.to_string() + } else { + field_type.trim().to_string() + }; + match resolved.as_str() { "Int" => { f.instruction(&Instruction::I32WrapI64); } @@ -2477,12 +2496,16 @@ fn emit_record_inline_hash( "String" => { f.instruction(&Instruction::ArrayLen); } - other => { - return Err(WasmGcError::Validation(format!( - "record hash dispatch: field type `{other}` unsupported \ - (only Int/Bool/Float/String); upstream `list_eq_kind` \ - all_simple gate should have rejected this list." - ))); + other if hash_helper_fn_idx.contains_key(other) => { + f.instruction(&Instruction::Call(hash_helper_fn_idx[other])); + } + _ => { + // No helper available — drop + 0. Collision OK; eq + // disambiguates the bucket. Hits when the field type + // is a generic carrier we haven't covered yet (e.g. + // `List` field in a record). + f.instruction(&Instruction::Drop); + f.instruction(&Instruction::I32Const(0)); } } f.instruction(&Instruction::I32Add); @@ -2583,6 +2606,7 @@ fn emit_vec_hash( registry: &TypeRegistry, kind: ListEqKind, _string_eq_fn_idx: Option, + hash_helper_fn_idx: &std::collections::HashMap, ) -> Result { let (vec_idx, _) = vec_idx_of_pair(canonical, registry)?; let elem = TypeRegistry::list_element_type(canonical).unwrap(); @@ -2670,6 +2694,7 @@ fn emit_vec_hash( )))?; emit_record_inline_hash( &mut f, r_idx, fields, /* elem_local */ 4, /* elem_hash_local */ 5, + registry, hash_helper_fn_idx, )?; } ListEqKind::SumEq(parent_name) => { @@ -2679,6 +2704,7 @@ fn emit_vec_hash( registry, /* elem_local */ 4, /* elem_hash_local */ 5, + hash_helper_fn_idx, )?; } } diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 6a4eba79..4a416efd 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -26,6 +26,7 @@ use wasm_encoder::{ use super::WasmGcError; use super::body::eq_helpers::{EqHelperRegistry, EqKind}; +use super::body::hash_helpers::{HashHelperRegistry, HashKind}; use super::body::{FnEntry, FnMap, emit_fn_body}; use super::builtins::{BuiltinName, BuiltinRegistry}; use super::effects::{EffectName, EffectRegistry}; @@ -66,6 +67,7 @@ pub(super) fn emit_module( let mut builtin_registry = BuiltinRegistry::new(); let mut effect_registry = EffectRegistry::new(); let mut eq_helpers_registry = EqHelperRegistry::new(); + let mut hash_helpers_registry = HashHelperRegistry::new(); for fd in &fn_defs { discover_builtins_in_fn( fd, @@ -103,6 +105,7 @@ pub(super) fn emit_module( for name in &nominal_seed { if registry.record_fields.contains_key(name) { eq_helpers_registry.register_transitive(name, EqKind::Record, ®istry); + hash_helpers_registry.register_transitive(name, HashKind::Record, ®istry); } else if registry .variants .values() @@ -110,8 +113,28 @@ pub(super) fn emit_module( .any(|v| &v.parent == name) { eq_helpers_registry.register_transitive(name, EqKind::Sum, ®istry); + hash_helpers_registry.register_transitive(name, HashKind::Sum, ®istry); } } + // Mirror eq registry's transitive shape — every type registered + // for eq dispatch also needs a hash helper, since list/vec/map + // helpers and per-record/sum hash bodies dispatch through + // `Call(__hash_)` for non-primitive fields. Walk the eq + // registry post-seed and register matching hash slots. + let eq_snapshot: Vec<(String, EqKind)> = eq_helpers_registry + .iter() + .map(|(n, k)| (n.to_string(), k)) + .collect(); + for (name, kind) in &eq_snapshot { + let hk = match kind { + EqKind::Record => HashKind::Record, + EqKind::Sum => HashKind::Sum, + EqKind::OptionEq => HashKind::OptionHash, + EqKind::ResultEq => HashKind::ResultHash, + EqKind::TupleEq => HashKind::TupleHash, + }; + hash_helpers_registry.register_transitive(name, hk, ®istry); + } // Eq helpers over records / sums with String fields need // `__wasmgc_string_eq` — force-register so the slot is allocated // before bodies emit. @@ -252,6 +275,8 @@ pub(super) fn emit_module( // `__wasmgc_string_eq` registered above). eq_helpers_registry.assign_slots(&mut next_builtin_fn_idx, &mut next_type_idx); eq_helpers_registry.emit_helper_types(&mut types); + hash_helpers_registry.assign_slots(&mut next_builtin_fn_idx, &mut next_type_idx); + hash_helpers_registry.emit_helper_types(&mut types); // 8a) `aver_http_handle` wrapper — `--handler X` synthesises a // no-arg fn that reads request fields via the `Request.*` @@ -419,6 +444,13 @@ pub(super) fn emit_module( .expect("registered eq helper has type idx after assign_slots"); funcs.function(t_idx); } + // Hash helpers — same shape (one entry per registered slot). + for (name, _kind) in hash_helpers_registry.iter() { + let t_idx = hash_helpers_registry + .lookup_type_idx(name) + .expect("registered hash helper has type idx after assign_slots"); + funcs.function(t_idx); + } if let Some(hw) = &handler_wrapper { funcs.function(hw.wrapper_type); funcs.function(hw.list_cons_type); @@ -531,6 +563,12 @@ pub(super) fn emit_module( eq_helpers_lookup.insert(name.to_string(), fn_idx); } } + let mut hash_helpers_lookup: HashMap = HashMap::new(); + for (name, _kind) in hash_helpers_registry.iter() { + if let Some(fn_idx) = hash_helpers_registry.lookup_fn_idx(name) { + hash_helpers_lookup.insert(name.to_string(), fn_idx); + } + } let fn_map = FnMap { by_name, builtins: builtin_idx_lookup, @@ -541,6 +579,7 @@ pub(super) fn emit_module( zip_ops: zip_ops_lookup, string_split_ops, eq_helpers: eq_helpers_lookup, + hash_helpers: hash_helpers_lookup, }; // ── Export section ───────────────────────────────────────────── @@ -709,17 +748,25 @@ pub(super) fn emit_module( .iter() .filter_map(|(n, _k)| eq_helpers_registry.lookup_fn_idx(n).map(|i| (n.to_string(), i))) .collect(); + let hash_helper_fn_idx_map: HashMap = hash_helpers_registry + .iter() + .filter_map(|(n, _k)| hash_helpers_registry.lookup_fn_idx(n).map(|i| (n.to_string(), i))) + .collect(); list_helpers.emit_helper_bodies( &mut codes, ®istry, string_eq_fn_idx, &eq_helper_fn_idx_map, + &hash_helper_fn_idx_map, )?; // Per-(record/sum) `__eq_` helper bodies — emit after // list helpers so any String fields can call `__wasmgc_string_eq` // by the index recorded above. eq_helpers_registry.emit_helper_bodies(&mut codes, ®istry, string_eq_fn_idx)?; + // `__hash_` helper bodies — emitted right after eq helpers so + // every nominal/carrier hash dispatch finds its target fn_idx. + hash_helpers_registry.emit_helper_bodies(&mut codes, ®istry, string_eq_fn_idx)?; if let Some(hw) = &handler_wrapper { let user_handler_wasm_idx = import_count + 1 + (hw.user_handler_idx as u32); From 1e7726c049feb3165f6b897cbd02018b6f283b1c Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 17:14:58 +0200 Subject: [PATCH 11/18] =?UTF-8?q?0.16.3:=20Map=20with=20carrier=20K?= =?UTF-8?q?=20=E2=80=94=20Option/Result/Tuple=20as=20map=20keys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MapHelperRegistry now accepts carrier types as K (or carriers in K record field positions). Carrier helpers don't allocate their own slot — they proxy via Call to the per-instantiation `__eq_` / `__hash_` helpers in eq_helpers / hash_helpers, threaded through emit_helper_bodies as `carrier_eq_hash`. types.rs: Map walker now scans let-binding annotations in fn bodies so `let m: Map = …` is registered (mirrors the Result walker). Drops the unused `FnMap.hash_helpers` lookup field — record/sum/carrier hash bodies dispatch through the registry directly, and map-key proxy goes through the dedicated `carrier_eq_hash` parameter. Map ✓ Person.age: Option Map,V> ✓ Map,V> ✓ Map,V> ✓ Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body.rs | 5 -- src/codegen/wasm_gc/maps.rs | 105 ++++++++++++++++++++++++++++++---- src/codegen/wasm_gc/module.rs | 28 ++++++--- src/codegen/wasm_gc/types.rs | 11 ++++ 4 files changed, 125 insertions(+), 24 deletions(-) diff --git a/src/codegen/wasm_gc/body.rs b/src/codegen/wasm_gc/body.rs index 8865d811..4f2b558f 100644 --- a/src/codegen/wasm_gc/body.rs +++ b/src/codegen/wasm_gc/body.rs @@ -74,11 +74,6 @@ pub(super) struct FnMap { /// Per-(record/sum) `__eq_` helpers used by `BinOp::Eq` /// / `BinOp::Neq` over nominal types. Key = bare type name. pub(super) eq_helpers: std::collections::HashMap, - /// Per-type `__hash_` helpers used by record/sum/carrier - /// hash bodies for non-primitive field hashing. Symmetric to - /// `eq_helpers`. Key = canonical (bare for record/sum, - /// whitespace-free instantiation for carriers). - pub(super) hash_helpers: std::collections::HashMap, } impl FnMap { diff --git a/src/codegen/wasm_gc/maps.rs b/src/codegen/wasm_gc/maps.rs index a9902f1d..194b97b2 100644 --- a/src/codegen/wasm_gc/maps.rs +++ b/src/codegen/wasm_gc/maps.rs @@ -204,11 +204,23 @@ impl MapHelperRegistry { .values() .flat_map(|v| v.iter()) .any(|v| v.parent == ft); - if (is_record || is_sum) && k_seen.insert(ft.clone()) { + let is_carrier = ft.starts_with("Option<") + || ft.starts_with("Result<") + || ft.starts_with("Tuple<"); + if (is_record || is_sum || is_carrier) && k_seen.insert(ft.clone()) { k_names.push(ft.clone()); - to_visit.push(ft.clone()); + if !is_carrier { + // Records / sums recurse into their own + // fields. Carriers don't (their helper bodies + // delegate via Call to eq_helpers/hash_ + // helpers, which handle inner types + // themselves). + to_visit.push(ft.clone()); + } // String inside the nested type's fields → - // force-register String. + // force-register String. Carriers' name + // contains "String" only when an inner type is + // literally `String`; cheap heuristic. let mut nested_needs_string = false; if let Some(fs) = registry.record_fields.get(&ft) { nested_needs_string |= fs.iter().any(|(_, t)| t.trim() == "String"); @@ -221,6 +233,9 @@ impl MapHelperRegistry { .filter(|v| v.parent == ft) .any(|v| v.fields.iter().any(|t| t.trim() == "String")); } + if is_carrier { + nested_needs_string |= ft.contains("String"); + } if nested_needs_string && k_seen.insert("String".into()) { k_names.push("String".into()); } @@ -318,13 +333,18 @@ impl MapHelperRegistry { .values() .flat_map(|v| v.iter()) .any(|v| v.parent == k_aver); + let is_carrier_k = k_aver.starts_with("Option<") + || k_aver.starts_with("Result<") + || k_aver.starts_with("Tuple<"); if k_aver != "String" && registry.record_type_idx(k_aver).is_none() && !is_primitive_k && !is_sum_k + && !is_carrier_k { return Err(WasmGcError::Unimplemented( - "phase 3c — Map with K not String / user-record / sum / primitive", + "phase 3c — Map with K not String / user-record / sum / \ + primitive / generic-carrier (Option/Result/Tuple)", )); } @@ -508,15 +528,26 @@ impl MapHelperRegistry { codes: &mut CodeSection, registry: &TypeRegistry, list_eq_hash: &HashMap, + carrier_eq_hash: &HashMap, ) -> Result<(), WasmGcError> { let string_key_helpers = self.key.get("String").copied(); // Snapshot every K's helpers — record hash/eq dispatch // needs to call helpers for nested record fields. Plus // virtual entries for `List` field types so hash/eq // dispatch can call into list_helpers without a - // separate cross-module lookup. + // separate cross-module lookup. Carriers (Option / Result + // / Tuple) come in through `carrier_eq_hash` from the + // `__eq_` / `__hash_` registries (eq_helpers / + // hash_helpers); their map-key body is a thin proxy that + // Calls into those. let mut all_key_helpers: HashMap = self.key.iter().map(|(k, h)| (k.clone(), *h)).collect(); + for (carrier, &(eq_fn, hash_fn)) in carrier_eq_hash { + all_key_helpers.insert( + carrier.clone(), + KeyHelpers { hash: hash_fn, eq: eq_fn }, + ); + } for (list_canonical, &(eq_fn, hash_fn)) in list_eq_hash { all_key_helpers.insert( list_canonical.clone(), @@ -592,6 +623,21 @@ fn emit_hash_for( { return emit_hash_sum(k_aver, registry, string_key_helpers); } + if k_aver.starts_with("Option<") + || k_aver.starts_with("Result<") + || k_aver.starts_with("Tuple<") + { + // Same shape as carrier eq: proxy to the per-instantiation + // `__hash_` helper from `hash_helpers`. + let helpers = all_key_helpers.get(k_aver).ok_or(WasmGcError::Validation( + format!("hash_for: carrier `{k_aver}` has no registered hash helper"), + ))?; + let mut f = Function::new([]); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::Call(helpers.hash)); + f.instruction(&Instruction::End); + return Ok(f); + } Err(WasmGcError::Unimplemented( "phase 3c — hash for unsupported K kind", )) @@ -620,6 +666,24 @@ fn emit_eq_for( { return emit_eq_sum(k_aver, registry, string_key_helpers); } + if k_aver.starts_with("Option<") + || k_aver.starts_with("Result<") + || k_aver.starts_with("Tuple<") + { + // Map-key eq for carriers proxies to the per-instantiation + // `__eq_` helper registered in `eq_helpers` (looked up + // via `all_key_helpers` which includes the carrier eq fn + // idx forwarded from the registry). + let helpers = all_key_helpers.get(k_aver).ok_or(WasmGcError::Validation( + format!("eq_for: carrier `{k_aver}` has no registered eq helper"), + ))?; + let mut f = Function::new([]); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::Call(helpers.eq)); + f.instruction(&Instruction::End); + return Ok(f); + } Err(WasmGcError::Unimplemented( "phase 3c — eq for unsupported K kind", )) @@ -1438,30 +1502,41 @@ fn emit_hash_record( // Nested record / List field. Both dispatch via // `all_key_helpers` — records were force-registered // as pseudo-K in `assign_slots`; list canonicals were - // injected by `emit_helper_bodies` from list_helpers. + // injected by `emit_helper_bodies` from list_helpers; + // carriers (Option/Result/Tuple) come from + // `carrier_eq_hash`. let lookup_key = if other.starts_with("List<") || other.starts_with("Vector<") { super::types::normalize_compound(other).to_string() } else { other.to_string() }; let is_compound = other.starts_with("List<") || other.starts_with("Vector<"); + let is_carrier = other.starts_with("Option<") + || other.starts_with("Result<") + || other.starts_with("Tuple<"); let is_sum = registry .variants .values() .flat_map(|v| v.iter()) .any(|v| v.parent == other); - if registry.record_type_idx(other).is_some() || is_compound || is_sum { + if registry.record_type_idx(other).is_some() + || is_compound + || is_sum + || is_carrier + { let inner = all_key_helpers .get(&lookup_key) .ok_or(WasmGcError::Validation(format!( "hash_record: field `{other}` has no key helpers \ - (record / list / vector / sum T may need force-registration)" + (record / list / vector / sum / Option / Result / Tuple T \ + may need force-registration)" )))?; f.instruction(&Instruction::Call(inner.hash)); } else { return Err(WasmGcError::Unimplemented( "phase 3c — record-key field type not in \ - {Int, Float, Bool, String, nested record, List, Vector, sum}", + {Int, Float, Bool, String, nested record, List, Vector, sum, \ + Option/Result/Tuple}", )); } } @@ -1523,12 +1598,19 @@ fn emit_eq_record( other.to_string() }; let is_compound = other.starts_with("List<") || other.starts_with("Vector<"); + let is_carrier = other.starts_with("Option<") + || other.starts_with("Result<") + || other.starts_with("Tuple<"); let is_sum = registry .variants .values() .flat_map(|v| v.iter()) .any(|v| v.parent == other); - if registry.record_type_idx(other).is_some() || is_compound || is_sum { + if registry.record_type_idx(other).is_some() + || is_compound + || is_sum + || is_carrier + { let inner = all_key_helpers .get(&lookup_key) .ok_or(WasmGcError::Validation(format!( @@ -1538,7 +1620,8 @@ fn emit_eq_record( } else { return Err(WasmGcError::Unimplemented( "phase 3c — record-key field type not in \ - {Int, Float, Bool, String, nested record, List, Vector, sum}", + {Int, Float, Bool, String, nested record, List, Vector, sum, \ + Option/Result/Tuple}", )); } } diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 4a416efd..1797d5ce 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -563,12 +563,6 @@ pub(super) fn emit_module( eq_helpers_lookup.insert(name.to_string(), fn_idx); } } - let mut hash_helpers_lookup: HashMap = HashMap::new(); - for (name, _kind) in hash_helpers_registry.iter() { - if let Some(fn_idx) = hash_helpers_registry.lookup_fn_idx(name) { - hash_helpers_lookup.insert(name.to_string(), fn_idx); - } - } let fn_map = FnMap { by_name, builtins: builtin_idx_lookup, @@ -579,7 +573,6 @@ pub(super) fn emit_module( zip_ops: zip_ops_lookup, string_split_ops, eq_helpers: eq_helpers_lookup, - hash_helpers: hash_helpers_lookup, }; // ── Export section ───────────────────────────────────────────── @@ -738,7 +731,26 @@ pub(super) fn emit_module( compound_eq_hash_lookup.insert(format!("Vector<{}>", elem.trim()), (eq_fn, hash_fn)); } } - map_helpers.emit_helper_bodies(&mut codes, ®istry, &compound_eq_hash_lookup)?; + // Carrier eq+hash lookup — Option/Result/Tuple instantiations + // get their helpers from eq_helpers / hash_helpers; map keys + // proxy through these. Build the pair map by zipping the two + // registries' fn idxs by canonical. + let mut carrier_eq_hash_lookup: HashMap = HashMap::new(); + for (name, kind) in eq_helpers_registry.iter() { + use super::body::eq_helpers::EqKind as EK; + if matches!(kind, EK::OptionEq | EK::ResultEq | EK::TupleEq) + && let Some(eq_fn) = eq_helpers_registry.lookup_fn_idx(name) + && let Some(hash_fn) = hash_helpers_registry.lookup_fn_idx(name) + { + carrier_eq_hash_lookup.insert(name.to_string(), (eq_fn, hash_fn)); + } + } + map_helpers.emit_helper_bodies( + &mut codes, + ®istry, + &compound_eq_hash_lookup, + &carrier_eq_hash_lookup, + )?; // List / Vector.fromList / String.split-join helper bodies. // Snapshot eq-helper fn idxs so list/vec eq+hash bodies can diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index 753a59ef..ee8a0a41 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -528,6 +528,17 @@ impl TypeRegistry { for (_, ty) in &fd.params { collect_maps_from_str(ty, &mut pending_maps); } + // Walk let-binding annotations too — `let m: Map = + // Map.set(…)` won't show up in fn signatures and the discovery + // walker would otherwise miss the canonical entirely. Mirrors + // what the Result walker added a few commits back. + use crate::ast::{FnBody, Stmt}; + let FnBody::Block(stmts) = fd.body.as_ref(); + for stmt in stmts { + if let Stmt::Binding(_, Some(annot), _) = stmt { + collect_maps_from_str(annot, &mut pending_maps); + } + } } } // Dedup in encounter order. From cf661471e1f65d4bc8c3d2c206916aae5f680669 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 17:22:20 +0200 Subject: [PATCH 12/18] 0.16.3: rustfmt fix Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/emit.rs | 18 ++- src/codegen/wasm_gc/body/eq_helpers.rs | 188 +++++++++++++++-------- src/codegen/wasm_gc/body/hash_helpers.rs | 111 ++++++++----- src/codegen/wasm_gc/lists.rs | 63 +++++--- src/codegen/wasm_gc/maps.rs | 31 ++-- src/codegen/wasm_gc/module.rs | 12 +- 6 files changed, 282 insertions(+), 141 deletions(-) diff --git a/src/codegen/wasm_gc/body/emit.rs b/src/codegen/wasm_gc/body/emit.rs index 4a8f04cf..9a4a08f7 100644 --- a/src/codegen/wasm_gc/body/emit.rs +++ b/src/codegen/wasm_gc/body/emit.rs @@ -582,7 +582,11 @@ fn sum_or_record_eq_fn(operand: &Spanned, ctx: &EmitCtx<'_>) -> Option { - let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + let canonical: String = ty + .display() + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); ctx.fn_map.eq_helpers.get(&canonical).copied() } // List / Vector — list_helpers / vfl_helpers slot the per-T @@ -591,11 +595,19 @@ fn sum_or_record_eq_fn(operand: &Spanned, ctx: &EmitCtx<'_>) -> Option { - let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + let canonical: String = ty + .display() + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); ctx.fn_map.list_ops.get(&canonical).and_then(|ops| ops.eq) } crate::types::Type::Vector(_) => { - let canonical: String = ty.display().chars().filter(|c| !c.is_whitespace()).collect(); + let canonical: String = ty + .display() + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); ctx.fn_map.vfl_ops.get(&canonical).and_then(|ops| ops.eq) } _ => None, diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index 2cfbaa64..f999155c 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -101,9 +101,9 @@ impl EqHelperRegistry { // dispatch". let mut seen = std::collections::HashSet::new(); let resolvable = match kind { - EqKind::Record => super::super::lists::record_fields_resolvable( - type_name, registry, &mut seen, - ), + EqKind::Record => { + super::super::lists::record_fields_resolvable(type_name, registry, &mut seen) + } EqKind::Sum => { super::super::lists::sum_fields_resolvable(type_name, registry, &mut seen) } @@ -150,7 +150,10 @@ impl EqHelperRegistry { } fn register_field_type(&mut self, field_ty: &str, registry: &TypeRegistry) { - if matches!(field_ty, "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char") { + if matches!( + field_ty, + "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char" + ) { return; } if registry.record_fields.contains_key(field_ty) { @@ -170,7 +173,10 @@ impl EqHelperRegistry { // Each instantiation gets its own helper slot keyed by the // canonical string. Inner types are walked too so any // nominal piece (`Option` → register Color). - if let Some(inner) = field_ty.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + if let Some(inner) = field_ty + .strip_prefix("Option<") + .and_then(|s| s.strip_suffix('>')) + { self.register_transitive(field_ty, EqKind::OptionEq, registry); self.register_field_type(inner.trim(), registry); } else if field_ty.starts_with("Result<") && field_ty.ends_with('>') { @@ -284,11 +290,11 @@ impl EqHelperRegistry { // declare two typed locals (idxs 2, 3), `ref.cast` // each param into its slot, then drive the inline // emitter against the typed locals. - let r_idx = registry.record_type_idx(name).ok_or( - WasmGcError::Validation(format!( + let r_idx = registry + .record_type_idx(name) + .ok_or(WasmGcError::Validation(format!( "eq helper for record `{name}`: record not registered" - )), - )?; + )))?; let r_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(r_idx), @@ -315,30 +321,15 @@ impl EqHelperRegistry { codes.function(&f); } EqKind::OptionEq => { - let f = emit_option_eq_body( - name, - registry, - string_eq_fn_idx, - &helper_idx_map, - )?; + let f = emit_option_eq_body(name, registry, string_eq_fn_idx, &helper_idx_map)?; codes.function(&f); } EqKind::ResultEq => { - let f = emit_result_eq_body( - name, - registry, - string_eq_fn_idx, - &helper_idx_map, - )?; + let f = emit_result_eq_body(name, registry, string_eq_fn_idx, &helper_idx_map)?; codes.function(&f); } EqKind::TupleEq => { - let f = emit_tuple_eq_body( - name, - registry, - string_eq_fn_idx, - &helper_idx_map, - )?; + let f = emit_tuple_eq_body(name, registry, string_eq_fn_idx, &helper_idx_map)?; codes.function(&f); } } @@ -363,7 +354,10 @@ impl EqHelperRegistry { /// `Result` → `Some(("Ok", "Err"))`. Tracks angle/paren /// depth so `Result, MyError>` splits at the right comma. fn parse_result_kv(canonical: &str) -> Option<(&str, &str)> { - let inner = canonical.trim().strip_prefix("Result<")?.strip_suffix('>')?; + let inner = canonical + .trim() + .strip_prefix("Result<")? + .strip_suffix('>')?; let bytes = inner.as_bytes(); let mut depth: i32 = 0; for (idx, b) in bytes.iter().enumerate() { @@ -412,12 +406,14 @@ fn emit_option_eq_body( string_eq_fn_idx: Option, helper_idx_map: &HashMap, ) -> Result { - let opt_idx = registry.option_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: option not registered")), - )?; - let inner = TypeRegistry::option_element_type(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse inner")), - )?; + let opt_idx = registry + .option_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "eq helper for `{canonical}`: option not registered" + )))?; + let inner = TypeRegistry::option_element_type(canonical).ok_or(WasmGcError::Validation( + format!("eq helper for `{canonical}`: can't parse inner"), + ))?; let opt_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(opt_idx), @@ -433,9 +429,15 @@ fn emit_option_eq_body( f.instruction(&Instruction::LocalSet(3)); // if tag(lhs) != tag(rhs) → return 0 f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); f.instruction(&Instruction::LocalGet(3)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); f.instruction(&Instruction::I32Ne); f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); f.instruction(&Instruction::I32Const(0)); @@ -443,7 +445,10 @@ fn emit_option_eq_body( f.instruction(&Instruction::End); // tags equal — if 0 (None), return 1 f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); f.instruction(&Instruction::I32Eqz); f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); f.instruction(&Instruction::I32Const(1)); @@ -451,10 +456,22 @@ fn emit_option_eq_body( f.instruction(&Instruction::End); // Both Some — compare inner via per-type dispatch. f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 1, + }); f.instruction(&Instruction::LocalGet(3)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); - emit_inner_eq_dispatch(&mut f, inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 1, + }); + emit_inner_eq_dispatch( + &mut f, + inner.trim(), + registry, + string_eq_fn_idx, + helper_idx_map, + )?; f.instruction(&Instruction::End); Ok(f) } @@ -469,12 +486,14 @@ fn emit_result_eq_body( string_eq_fn_idx: Option, helper_idx_map: &HashMap, ) -> Result { - let res_idx = registry.result_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: result not registered")), - )?; - let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse inner")), - )?; + let res_idx = registry + .result_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "eq helper for `{canonical}`: result not registered" + )))?; + let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or(WasmGcError::Validation( + format!("eq helper for `{canonical}`: can't parse inner"), + ))?; let res_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(res_idx), @@ -489,9 +508,15 @@ fn emit_result_eq_body( f.instruction(&Instruction::LocalSet(3)); // if tag(lhs) != tag(rhs) → 0 f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 0, + }); f.instruction(&Instruction::LocalGet(3)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 0, + }); f.instruction(&Instruction::I32Ne); f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); f.instruction(&Instruction::I32Const(0)); @@ -500,21 +525,50 @@ fn emit_result_eq_body( // tags equal — branch on tag value: tag=1 (Ok) → field 1 eq; // tag=0 (Err) → field 2 eq. f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); - f.instruction(&Instruction::If(wasm_encoder::BlockType::Result(wasm_encoder::ValType::I32))); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 0, + }); + f.instruction(&Instruction::If(wasm_encoder::BlockType::Result( + wasm_encoder::ValType::I32, + ))); // Ok arm f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 1, + }); f.instruction(&Instruction::LocalGet(3)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); - emit_inner_eq_dispatch(&mut f, ok_inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 1, + }); + emit_inner_eq_dispatch( + &mut f, + ok_inner.trim(), + registry, + string_eq_fn_idx, + helper_idx_map, + )?; f.instruction(&Instruction::Else); // Err arm f.instruction(&Instruction::LocalGet(2)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 2, + }); f.instruction(&Instruction::LocalGet(3)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); - emit_inner_eq_dispatch(&mut f, err_inner.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 2, + }); + emit_inner_eq_dispatch( + &mut f, + err_inner.trim(), + registry, + string_eq_fn_idx, + helper_idx_map, + )?; f.instruction(&Instruction::End); f.instruction(&Instruction::End); Ok(f) @@ -528,12 +582,14 @@ fn emit_tuple_eq_body( string_eq_fn_idx: Option, helper_idx_map: &HashMap, ) -> Result { - let tup_idx = registry.tuple_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: tuple not registered")), - )?; - let elems = parse_tuple_elems(canonical).ok_or( - WasmGcError::Validation(format!("eq helper for `{canonical}`: can't parse elements")), - )?; + let tup_idx = registry + .tuple_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "eq helper for `{canonical}`: tuple not registered" + )))?; + let elems = parse_tuple_elems(canonical).ok_or(WasmGcError::Validation(format!( + "eq helper for `{canonical}`: can't parse elements" + )))?; let tup_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(tup_idx), @@ -557,7 +613,13 @@ fn emit_tuple_eq_body( struct_type_index: tup_idx, field_index: i as u32, }); - emit_inner_eq_dispatch(&mut f, elem.trim(), registry, string_eq_fn_idx, helper_idx_map)?; + emit_inner_eq_dispatch( + &mut f, + elem.trim(), + registry, + string_eq_fn_idx, + helper_idx_map, + )?; if i > 0 { f.instruction(&Instruction::I32And); } diff --git a/src/codegen/wasm_gc/body/hash_helpers.rs b/src/codegen/wasm_gc/body/hash_helpers.rs index a39b249a..0ee468a5 100644 --- a/src/codegen/wasm_gc/body/hash_helpers.rs +++ b/src/codegen/wasm_gc/body/hash_helpers.rs @@ -80,9 +80,9 @@ impl HashHelperRegistry { } let mut seen = std::collections::HashSet::new(); let resolvable = match kind { - HashKind::Record => super::super::lists::record_fields_resolvable( - type_name, registry, &mut seen, - ), + HashKind::Record => { + super::super::lists::record_fields_resolvable(type_name, registry, &mut seen) + } HashKind::Sum => { super::super::lists::sum_fields_resolvable(type_name, registry, &mut seen) } @@ -119,7 +119,10 @@ impl HashHelperRegistry { } fn register_field_type(&mut self, field_ty: &str, registry: &TypeRegistry) { - if matches!(field_ty, "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char") { + if matches!( + field_ty, + "Int" | "Float" | "Bool" | "String" | "Unit" | "Byte" | "Char" + ) { return; } if registry.record_fields.contains_key(field_ty) { @@ -135,7 +138,10 @@ impl HashHelperRegistry { self.register_transitive(field_ty, HashKind::Sum, registry); return; } - if let Some(inner) = field_ty.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + if let Some(inner) = field_ty + .strip_prefix("Option<") + .and_then(|s| s.strip_suffix('>')) + { self.register_transitive(field_ty, HashKind::OptionHash, registry); self.register_field_type(inner.trim(), registry); } else if field_ty.starts_with("Result<") && field_ty.ends_with('>') { @@ -186,9 +192,7 @@ impl HashHelperRegistry { }, }); for _ in &self.order { - types - .ty() - .function([eq_ref], [wasm_encoder::ValType::I32]); + types.ty().function([eq_ref], [wasm_encoder::ValType::I32]); } } @@ -247,9 +251,12 @@ fn emit_record_hash_body( .ok_or(WasmGcError::Validation(format!( "hash helper for record `{name}`: not registered" )))?; - let fields = registry.record_fields.get(name).ok_or( - WasmGcError::Validation(format!("hash helper for record `{name}`: no fields")), - )?; + let fields = registry + .record_fields + .get(name) + .ok_or(WasmGcError::Validation(format!( + "hash helper for record `{name}`: no fields" + )))?; let r_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(r_idx), @@ -368,12 +375,14 @@ fn emit_option_hash_body( registry: &TypeRegistry, helper_idx_map: &HashMap, ) -> Result { - let opt_idx = registry.option_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: option not registered")), - )?; - let inner = TypeRegistry::option_element_type(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse inner")), - )?; + let opt_idx = registry + .option_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "hash helper for `{canonical}`: option not registered" + )))?; + let inner = TypeRegistry::option_element_type(canonical).ok_or(WasmGcError::Validation( + format!("hash helper for `{canonical}`: can't parse inner"), + ))?; let opt_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(opt_idx), @@ -393,12 +402,18 @@ fn emit_option_hash_body( f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); // if Some (tag != 0), mix inner hash into h. f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Const(5)); @@ -406,7 +421,10 @@ fn emit_option_hash_body( f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: opt_idx, field_index: 1 }); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 1, + }); emit_inner_hash_dispatch(&mut f, inner.trim(), registry, helper_idx_map, None)?; f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); @@ -422,12 +440,14 @@ fn emit_result_hash_body( registry: &TypeRegistry, helper_idx_map: &HashMap, ) -> Result { - let res_idx = registry.result_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: result not registered")), - )?; - let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse inner")), - )?; + let res_idx = registry + .result_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "hash helper for `{canonical}`: result not registered" + )))?; + let (ok_inner, err_inner) = parse_result_kv(canonical).ok_or(WasmGcError::Validation( + format!("hash helper for `{canonical}`: can't parse inner"), + ))?; let res_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(res_idx), @@ -446,12 +466,18 @@ fn emit_result_hash_body( f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 0, + }); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); // Branch on tag. f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 0 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 0, + }); f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty)); // Ok arm: mix field 1 (ok) into h. f.instruction(&Instruction::LocalGet(2)); @@ -460,7 +486,10 @@ fn emit_result_hash_body( f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 1 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 1, + }); emit_inner_hash_dispatch(&mut f, ok_inner.trim(), registry, helper_idx_map, None)?; f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); @@ -472,7 +501,10 @@ fn emit_result_hash_body( f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalGet(1)); - f.instruction(&Instruction::StructGet { struct_type_index: res_idx, field_index: 2 }); + f.instruction(&Instruction::StructGet { + struct_type_index: res_idx, + field_index: 2, + }); emit_inner_hash_dispatch(&mut f, err_inner.trim(), registry, helper_idx_map, None)?; f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); @@ -488,12 +520,14 @@ fn emit_tuple_hash_body( registry: &TypeRegistry, helper_idx_map: &HashMap, ) -> Result { - let tup_idx = registry.tuple_type_idx(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: tuple not registered")), - )?; - let elems = parse_tuple_elems(canonical).ok_or( - WasmGcError::Validation(format!("hash helper for `{canonical}`: can't parse elements")), - )?; + let tup_idx = registry + .tuple_type_idx(canonical) + .ok_or(WasmGcError::Validation(format!( + "hash helper for `{canonical}`: tuple not registered" + )))?; + let elems = parse_tuple_elems(canonical).ok_or(WasmGcError::Validation(format!( + "hash helper for `{canonical}`: can't parse elements" + )))?; let tup_ref = wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: true, heap_type: wasm_encoder::HeapType::Concrete(tup_idx), @@ -576,7 +610,10 @@ fn emit_inner_hash_dispatch( /// `Result` → `Some(("Ok", "Err"))`. Tracks angle/paren /// depth so `Result, MyError>` splits at the right comma. fn parse_result_kv(canonical: &str) -> Option<(&str, &str)> { - let inner = canonical.trim().strip_prefix("Result<")?.strip_suffix('>')?; + let inner = canonical + .trim() + .strip_prefix("Result<")? + .strip_suffix('>')?; let bytes = inner.as_bytes(); let mut depth: i32 = 0; for (idx, b) in bytes.iter().enumerate() { diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 411cbd18..aea2c38e 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -796,10 +796,16 @@ pub(super) fn field_type_resolvable( // Resolvable iff every inner type is itself resolvable. // List/Vector/Map field types still fall through (their dispatch // from `emit_record_eq_inline` is a separate followup). - if let Some(inner) = field.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) { + if let Some(inner) = field + .strip_prefix("Option<") + .and_then(|s| s.strip_suffix('>')) + { return field_type_resolvable(inner.trim(), registry, seen); } - if let Some(inner) = field.strip_prefix("Result<").and_then(|s| s.strip_suffix('>')) { + if let Some(inner) = field + .strip_prefix("Result<") + .and_then(|s| s.strip_suffix('>')) + { let bytes = inner.as_bytes(); let mut depth: i32 = 0; for (idx, b) in bytes.iter().enumerate() { @@ -817,7 +823,10 @@ pub(super) fn field_type_resolvable( } return false; } - if let Some(inner) = field.strip_prefix("Tuple<").and_then(|s| s.strip_suffix('>')) { + if let Some(inner) = field + .strip_prefix("Tuple<") + .and_then(|s| s.strip_suffix('>')) + { let bytes = inner.as_bytes(); let mut depth: i32 = 0; let mut start = 0; @@ -2152,12 +2161,13 @@ fn emit_list_eq( // `__eq_` helper. Its signature is // `(eqref, eqref) -> i32`; both refs on the stack are // subtypes of eqref so the implicit upcast is fine. - let idx = eq_helper_fn_idx.get(name).copied().ok_or( - WasmGcError::Validation(format!( + let idx = eq_helper_fn_idx + .get(name) + .copied() + .ok_or(WasmGcError::Validation(format!( "List eq over `{name}`: __eq_{name} helper not registered \ (discovery walker should have transitively flagged it)" - )), - )?; + )))?; f.instruction(&Instruction::Call(idx)); } } @@ -2300,8 +2310,13 @@ fn emit_list_hash( "list hash dispatch: record `{record_name}` has no field info" )))?; emit_record_inline_hash( - &mut f, r_idx, fields, /* elem_local */ 3, /* elem_hash_local */ 4, - registry, hash_helper_fn_idx, + &mut f, + r_idx, + fields, + /* elem_local */ 3, + /* elem_hash_local */ 4, + registry, + hash_helper_fn_idx, )?; } ListEqKind::SumEq(parent_name) => { @@ -2402,12 +2417,12 @@ fn emit_sum_inline_hash( struct_type_index: v_idx, field_index: i as u32, }); - let resolved: String = - if let Some(under) = registry.newtype_underlying(field_ty.trim()) { - under.to_string() - } else { - field_ty.trim().to_string() - }; + let resolved: String = if let Some(under) = registry.newtype_underlying(field_ty.trim()) + { + under.to_string() + } else { + field_ty.trim().to_string() + }; match resolved.as_str() { "Int" => { f.instruction(&Instruction::I32WrapI64); @@ -2575,11 +2590,12 @@ fn emit_vec_eq( ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) => { // Nominal element — `Call(__eq_)`. Same eqref upcast // shape as in `emit_list_eq`. - let idx = eq_helper_fn_idx.get(name).copied().ok_or( - WasmGcError::Validation(format!( + let idx = eq_helper_fn_idx + .get(name) + .copied() + .ok_or(WasmGcError::Validation(format!( "Vector eq over `{name}`: __eq_{name} helper not registered" - )), - )?; + )))?; f.instruction(&Instruction::Call(idx)); } } @@ -2693,8 +2709,13 @@ fn emit_vec_hash( "vector hash dispatch: record `{record_name}` has no field info" )))?; emit_record_inline_hash( - &mut f, r_idx, fields, /* elem_local */ 4, /* elem_hash_local */ 5, - registry, hash_helper_fn_idx, + &mut f, + r_idx, + fields, + /* elem_local */ 4, + /* elem_hash_local */ 5, + registry, + hash_helper_fn_idx, )?; } ListEqKind::SumEq(parent_name) => { diff --git a/src/codegen/wasm_gc/maps.rs b/src/codegen/wasm_gc/maps.rs index 194b97b2..cf5b4f1c 100644 --- a/src/codegen/wasm_gc/maps.rs +++ b/src/codegen/wasm_gc/maps.rs @@ -545,7 +545,10 @@ impl MapHelperRegistry { for (carrier, &(eq_fn, hash_fn)) in carrier_eq_hash { all_key_helpers.insert( carrier.clone(), - KeyHelpers { hash: hash_fn, eq: eq_fn }, + KeyHelpers { + hash: hash_fn, + eq: eq_fn, + }, ); } for (list_canonical, &(eq_fn, hash_fn)) in list_eq_hash { @@ -629,9 +632,11 @@ fn emit_hash_for( { // Same shape as carrier eq: proxy to the per-instantiation // `__hash_` helper from `hash_helpers`. - let helpers = all_key_helpers.get(k_aver).ok_or(WasmGcError::Validation( - format!("hash_for: carrier `{k_aver}` has no registered hash helper"), - ))?; + let helpers = all_key_helpers + .get(k_aver) + .ok_or(WasmGcError::Validation(format!( + "hash_for: carrier `{k_aver}` has no registered hash helper" + )))?; let mut f = Function::new([]); f.instruction(&Instruction::LocalGet(0)); f.instruction(&Instruction::Call(helpers.hash)); @@ -674,9 +679,11 @@ fn emit_eq_for( // `__eq_` helper registered in `eq_helpers` (looked up // via `all_key_helpers` which includes the carrier eq fn // idx forwarded from the registry). - let helpers = all_key_helpers.get(k_aver).ok_or(WasmGcError::Validation( - format!("eq_for: carrier `{k_aver}` has no registered eq helper"), - ))?; + let helpers = all_key_helpers + .get(k_aver) + .ok_or(WasmGcError::Validation(format!( + "eq_for: carrier `{k_aver}` has no registered eq helper" + )))?; let mut f = Function::new([]); f.instruction(&Instruction::LocalGet(0)); f.instruction(&Instruction::LocalGet(1)); @@ -1519,10 +1526,7 @@ fn emit_hash_record( .values() .flat_map(|v| v.iter()) .any(|v| v.parent == other); - if registry.record_type_idx(other).is_some() - || is_compound - || is_sum - || is_carrier + if registry.record_type_idx(other).is_some() || is_compound || is_sum || is_carrier { let inner = all_key_helpers .get(&lookup_key) @@ -1606,10 +1610,7 @@ fn emit_eq_record( .values() .flat_map(|v| v.iter()) .any(|v| v.parent == other); - if registry.record_type_idx(other).is_some() - || is_compound - || is_sum - || is_carrier + if registry.record_type_idx(other).is_some() || is_compound || is_sum || is_carrier { let inner = all_key_helpers .get(&lookup_key) diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 1797d5ce..80346bed 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -758,11 +758,19 @@ pub(super) fn emit_module( let string_eq_fn_idx = builtin_registry.lookup_wasm_fn_idx(BuiltinName::StringEq); let eq_helper_fn_idx_map: HashMap = eq_helpers_registry .iter() - .filter_map(|(n, _k)| eq_helpers_registry.lookup_fn_idx(n).map(|i| (n.to_string(), i))) + .filter_map(|(n, _k)| { + eq_helpers_registry + .lookup_fn_idx(n) + .map(|i| (n.to_string(), i)) + }) .collect(); let hash_helper_fn_idx_map: HashMap = hash_helpers_registry .iter() - .filter_map(|(n, _k)| hash_helpers_registry.lookup_fn_idx(n).map(|i| (n.to_string(), i))) + .filter_map(|(n, _k)| { + hash_helpers_registry + .lookup_fn_idx(n) + .map(|i| (n.to_string(), i)) + }) .collect(); list_helpers.emit_helper_bodies( &mut codes, From d9dec4f9c1c73c5b81ed29db779c205642dbb211 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 18:35:40 +0200 Subject: [PATCH 13/18] 0.16.3: List/Vector elements + Map keys close the eq/hash matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds carrier (Option/Result/Tuple) and List/Vector handling at three layers that were previously gated to primitives + flat record/sum: - list_eq_kind extended with `CarrierEq(canonical)` for Option / Result / Tuple<…> / List / Vector elements. Element dispatch is `Call(__eq_)` — same calling convention as record/sum, so the same kind variant covers all of them. - field_type_resolvable now accepts List/Vector when the inner type is itself resolvable, so a record with a `List>` field gets a registered eq+hash helper instead of falling through to the i64-default validation error. - register_field_type recurses into List/Vector inner types in both eq_helpers and hash_helpers; carrier register_transitive now walks inner types too so direct top-level seeding of a carrier still picks up its inner shape (fixes tetris: `Option` seeded via List element walker without PieceKind itself getting a sum helper otherwise). - emit_helper_bodies (eq + hash) takes a `compound_lookup` arg threading List/Vector fn idxs into the helper map so a record field of compound type can `Call(__eq_List<…>)`. - MapHelperRegistry whitelists List/Vector as K, with proxy bodies in emit_eq_for / emit_hash_for. Map.remove now picks the null heap type via `key_storage_null_heap` (concrete idx for primitive-box / String / record / carrier / List / Vector, abstract Eq for sum K) instead of the old primitive-only fallback to type idx 0. - Discovery seed walker registers carriers in eq_helpers / hash_helpers when they appear as List/Vector elements or Map K, so list-helper bodies dispatch carrier elements correctly. Coverage: shape_a Map, String> ✓ shape_d record { List> } ✓ shape_h List> == ✓ shape_k record { Vector> } ✓ shape_l Map, String> ✓ exo_hash Map w/ Option field ✓ + 7 carrier exotics, 3 bug repros, 7/7 games 621/621 lib tests, no regressions. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/eq_helpers.rs | 71 +++++++++++--- src/codegen/wasm_gc/body/hash_helpers.rs | 49 +++++++++- src/codegen/wasm_gc/lists.rs | 115 ++++++++++++++++++++--- src/codegen/wasm_gc/maps.rs | 81 ++++++++++++---- src/codegen/wasm_gc/module.rs | 51 +++++++++- 5 files changed, 314 insertions(+), 53 deletions(-) diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index f999155c..fcacfd51 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -139,13 +139,34 @@ impl EqHelperRegistry { } } } - // Carrier kinds — inner-type registration is handled by - // the surrounding `register_field_type` arm that decides - // to register the carrier in the first place. Nothing - // extra to walk here (the helper body inspects the - // canonical at emit time via `option_element_type` / - // `parse_result_kv` / `parse_tuple_elems`). - EqKind::OptionEq | EqKind::ResultEq | EqKind::TupleEq => {} + // Carrier kinds — recurse into inner types so a direct + // top-level register (e.g. discovery seed walker hitting + // `Option` from a `List>`) + // still registers PieceKind. When the carrier is reached + // via `register_field_type` the recursion happens there + // too; this duplicate is idempotent (slots dedup by + // canonical). + EqKind::OptionEq => { + if let Some(inner) = type_name + .strip_prefix("Option<") + .and_then(|s| s.strip_suffix('>')) + { + self.register_field_type(inner.trim(), registry); + } + } + EqKind::ResultEq => { + if let Some((ok, err)) = parse_result_kv(type_name) { + self.register_field_type(ok.trim(), registry); + self.register_field_type(err.trim(), registry); + } + } + EqKind::TupleEq => { + if let Some(elems) = parse_tuple_elems(type_name) { + for e in elems { + self.register_field_type(e.trim(), registry); + } + } + } } } @@ -192,13 +213,25 @@ impl EqHelperRegistry { self.register_field_type(elem.trim(), registry); } } + } else if let Some(inner) = field_ty + .strip_prefix("List<") + .and_then(|s| s.strip_suffix('>')) + { + // List / Vector — element kind needs its own + // helper too. The list_helpers slot itself is owned by + // ListHelperRegistry (separate registry); this walk + // ensures the per-element `__eq_` exists in + // eq_helpers when inner is a carrier or nominal type. + self.register_field_type(inner.trim(), registry); + } else if let Some(inner) = field_ty + .strip_prefix("Vector<") + .and_then(|s| s.strip_suffix('>')) + { + self.register_field_type(inner.trim(), registry); } - // List, Vector, Map — those have their own - // helper machinery (list_helpers/map_helpers fn idxs). - // BinOp::Eq dispatch on collections is covered by the - // separate body/emit.rs path; field-of-record holding a - // collection still falls through here. Followup if real - // programs surface it. + // Map still falls through — record-of-Map field eq is a + // separate followup (the map's own equality semantics aren't + // canonical: insertion-order vs structural). } pub(crate) fn iter(&self) -> impl Iterator + '_ { @@ -248,18 +281,26 @@ impl EqHelperRegistry { codes: &mut wasm_encoder::CodeSection, registry: &TypeRegistry, string_eq_fn_idx: Option, + compound_lookup: &HashMap, ) -> Result<(), WasmGcError> { // Snapshot type_name → fn_idx so the inline emitters can // dispatch nested record/sum fields by `Call(idx)` instead // of erroring on `Unimplemented`. Self-recursive fields // (parent==field) get their own `self_fn_idx` argument so // recursive sum/record types (Tree.Node holding Tree, …) - // resolve to a recursive call into the same helper. - let helper_idx_map: HashMap = self + // resolve to a recursive call into the same helper. Compound + // lookups (`List`, `Vector`) are merged in so a record + // field of type `List>` can `Call(__eq_List<…>)` + // — list_helpers owns those fn idxs and threads them in via + // the `compound_lookup` arg. + let mut helper_idx_map: HashMap = self .slots .iter() .map(|(n, (fn_idx, _))| (n.clone(), *fn_idx)) .collect(); + for (canonical, fn_idx) in compound_lookup { + helper_idx_map.insert(canonical.clone(), *fn_idx); + } for name in &self.order { let kind = self.kinds[name]; let self_fn_idx = self.slots.get(name).map(|(f, _)| *f); diff --git a/src/codegen/wasm_gc/body/hash_helpers.rs b/src/codegen/wasm_gc/body/hash_helpers.rs index 0ee468a5..2e662d19 100644 --- a/src/codegen/wasm_gc/body/hash_helpers.rs +++ b/src/codegen/wasm_gc/body/hash_helpers.rs @@ -114,7 +114,30 @@ impl HashHelperRegistry { } } } - HashKind::OptionHash | HashKind::ResultHash | HashKind::TupleHash => {} + // Mirror eq_helpers — recurse so direct top-level + // registration of a carrier (seed walker etc.) still + // discovers inner types. + HashKind::OptionHash => { + if let Some(inner) = type_name + .strip_prefix("Option<") + .and_then(|s| s.strip_suffix('>')) + { + self.register_field_type(inner.trim(), registry); + } + } + HashKind::ResultHash => { + if let Some((ok, err)) = parse_result_kv(type_name) { + self.register_field_type(ok.trim(), registry); + self.register_field_type(err.trim(), registry); + } + } + HashKind::TupleHash => { + if let Some(elems) = parse_tuple_elems(type_name) { + for e in elems { + self.register_field_type(e.trim(), registry); + } + } + } } } @@ -157,6 +180,21 @@ impl HashHelperRegistry { self.register_field_type(elem.trim(), registry); } } + } else if let Some(inner) = field_ty + .strip_prefix("List<") + .and_then(|s| s.strip_suffix('>')) + { + // Symmetric to eq_helpers — recurse into the element so + // `List>` registers `__hash_Option`. The + // list_helpers slot itself is owned by the separate + // ListHelperRegistry; this walk only covers the carrier + // / nominal hash dispatch the inline body needs. + self.register_field_type(inner.trim(), registry); + } else if let Some(inner) = field_ty + .strip_prefix("Vector<") + .and_then(|s| s.strip_suffix('>')) + { + self.register_field_type(inner.trim(), registry); } } @@ -201,13 +239,20 @@ impl HashHelperRegistry { codes: &mut wasm_encoder::CodeSection, registry: &TypeRegistry, string_eq_fn_idx: Option, + compound_lookup: &HashMap, ) -> Result<(), WasmGcError> { let _ = string_eq_fn_idx; // String fields hash via array.len, no helper needed. - let helper_idx_map: HashMap = self + // Same shape as eq_helpers — merge `List` / `Vector` + // hash fn idxs from list_helpers so a record field of a + // compound type can `Call(__hash_)`. + let mut helper_idx_map: HashMap = self .slots .iter() .map(|(n, (fn_idx, _))| (n.clone(), *fn_idx)) .collect(); + for (canonical, fn_idx) in compound_lookup { + helper_idx_map.insert(canonical.clone(), *fn_idx); + } for name in &self.order { let kind = self.kinds[name]; let self_fn_idx = self.slots.get(name).map(|(f, _)| *f); diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index aea2c38e..6a2678c5 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -683,6 +683,13 @@ enum ListEqKind { /// if both head and needle are V_i, compare fields; if only one /// is V_i, return false. Carries the parent type name. SumEq(String), + /// Generic carrier element — `Option`, `Result`, or + /// `Tuple<…>`. List eq dispatches per element via + /// `Call(__eq_)` from `eq_helpers`; signature is + /// `(eqref, eqref) -> i32`, same shape as record/sum eq. Carries + /// the canonical (whitespace-free) so the body emitter can look + /// it up in the helper idx map at emit time. + CarrierEq(String), } fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { @@ -692,6 +699,27 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { if let Some(under) = registry.newtype_underlying(trimmed) { return list_eq_kind(under, registry); } + // Generic carriers — `Option` / `Result` / `Tuple<…>` + // get a per-instantiation `__eq_` helper from + // eq_helpers. Their list-element dispatch is a thin Call into + // that helper, same shape as record/sum dispatch. Same path + // covers `List` / `Vector` elements (helpers live in + // list_helpers but the calling convention is identical: + // `(eqref, eqref) -> i32` after implicit upcast). + if (trimmed.starts_with("Option<") + || trimmed.starts_with("Result<") + || trimmed.starts_with("Tuple<") + || trimmed.starts_with("List<") + || trimmed.starts_with("Vector<")) + && trimmed.ends_with('>') + { + let canonical: String = trimmed.chars().filter(|c| !c.is_whitespace()).collect(); + let mut seen = std::collections::HashSet::new(); + if field_type_resolvable(trimmed, registry, &mut seen) { + return Some(ListEqKind::CarrierEq(canonical)); + } + return None; + } match trimmed { "Int" => Some(ListEqKind::I64), "Float" => Some(ListEqKind::F64), @@ -846,6 +874,24 @@ pub(super) fn field_type_resolvable( } return field_type_resolvable(inner[start..].trim(), registry, seen); } + // List / Vector — list/vec helpers slot a per-instantiation + // eq+hash fn in `list_helpers` whenever X is itself + // `list_eq_kind`-able (recursively: primitive, record, sum, or a + // resolvable carrier). Record/sum field dispatch then calls + // `__eq_List` / `__eq_Vector` via the compound lookup + // map threaded into `emit_record_eq_inline`. + if let Some(inner) = field + .strip_prefix("List<") + .and_then(|s| s.strip_suffix('>')) + { + return field_type_resolvable(inner.trim(), registry, seen); + } + if let Some(inner) = field + .strip_prefix("Vector<") + .and_then(|s| s.strip_suffix('>')) + { + return field_type_resolvable(inner.trim(), registry, seen); + } false } @@ -1619,7 +1665,8 @@ fn emit_list_contains( // params: 0=in, 1=needle. local 2 = cur. RecordEq adds two // extra scratch locals (3 = head, 4 = needle copy) since field- // by-field eq needs `struct.get` against both refs multiple - // times. + // times. CarrierEq needs no scratch — the helper takes eqref + // params directly so head + needle are forwarded as-is. let mut locals: Vec<(u32, ValType)> = vec![(1, list_ref)]; if matches!(&kind, ListEqKind::RecordEq(_) | ListEqKind::SumEq(_)) { // Record / sum eq does multiple struct.get reads against @@ -1683,6 +1730,24 @@ fn emit_list_contains( None, )?; } + ListEqKind::CarrierEq(canonical) => { + // `Call(__eq_)` — both head and needle pushed + // as eqref args (head from struct.get, needle from + // param 1). + let eq_fn = eq_helper_fn_idx.get(canonical).copied().ok_or_else(|| { + WasmGcError::Validation(format!( + "List.contains over carrier `{canonical}`: \ + __eq_{canonical} not registered in eq_helpers" + )) + })?; + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: list_idx, + field_index: 0, + }); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::Call(eq_fn)); + } _ => { f.instruction(&Instruction::LocalGet(2)); f.instruction(&Instruction::StructGet { @@ -1700,12 +1765,12 @@ fn emit_list_contains( ))?; f.instruction(&Instruction::Call(eq_fn)) } - ListEqKind::RecordEq(_) | ListEqKind::SumEq(_) => panic!( - "internal compiler error: List.contains emit reached \ - RecordEq/SumEq path; should be filtered upstream by \ - `list_eq_kind` returning None for record/sum elements. \ - Please file at https://github.com/jasisz/aver/issues" - ), + ListEqKind::RecordEq(_) | ListEqKind::SumEq(_) | ListEqKind::CarrierEq(_) => { + unreachable!( + "filtered by outer match arms — RecordEq/SumEq/CarrierEq \ + handled above before this fallthrough" + ) + } }; } } @@ -2156,8 +2221,8 @@ fn emit_list_eq( ))?; f.instruction(&Instruction::Call(eq_fn)); } - ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) => { - // Nominal element — dispatch to the per-type + ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) | ListEqKind::CarrierEq(name) => { + // Nominal/carrier element — dispatch to the per-type // `__eq_` helper. Its signature is // `(eqref, eqref) -> i32`; both refs on the stack are // subtypes of eqref so the implicit upcast is fine. @@ -2329,6 +2394,19 @@ fn emit_list_hash( hash_helper_fn_idx, )?; } + ListEqKind::CarrierEq(canonical) => { + // Element on stack is eqref; `Call(__hash_)` + // returns i32. The carrier's `__hash_` is registered + // alongside `__eq_` in hash_helpers. + let idx = hash_helper_fn_idx + .get(canonical) + .copied() + .ok_or(WasmGcError::Validation(format!( + "list hash over carrier `{canonical}`: __hash_{canonical} \ + not registered in hash_helpers" + )))?; + f.instruction(&Instruction::Call(idx)); + } } f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(2)); @@ -2587,9 +2665,9 @@ fn emit_vec_eq( ))?; f.instruction(&Instruction::Call(eq_fn)); } - ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) => { - // Nominal element — `Call(__eq_)`. Same eqref upcast - // shape as in `emit_list_eq`. + ListEqKind::RecordEq(name) | ListEqKind::SumEq(name) | ListEqKind::CarrierEq(name) => { + // Nominal/carrier element — `Call(__eq_)`. Same eqref + // upcast shape as in `emit_list_eq`. let idx = eq_helper_fn_idx .get(name) .copied() @@ -2728,6 +2806,19 @@ fn emit_vec_hash( hash_helper_fn_idx, )?; } + ListEqKind::CarrierEq(canonical) => { + // Element on stack is eqref; `Call(__hash_)` + // returns i32. Same shape as `emit_list_hash`'s carrier + // arm. + let idx = hash_helper_fn_idx + .get(canonical) + .copied() + .ok_or(WasmGcError::Validation(format!( + "vector hash over carrier `{canonical}`: \ + __hash_{canonical} not registered in hash_helpers" + )))?; + f.instruction(&Instruction::Call(idx)); + } } f.instruction(&Instruction::I32Add); f.instruction(&Instruction::LocalSet(1)); diff --git a/src/codegen/wasm_gc/maps.rs b/src/codegen/wasm_gc/maps.rs index cf5b4f1c..1d42d0b6 100644 --- a/src/codegen/wasm_gc/maps.rs +++ b/src/codegen/wasm_gc/maps.rs @@ -336,15 +336,17 @@ impl MapHelperRegistry { let is_carrier_k = k_aver.starts_with("Option<") || k_aver.starts_with("Result<") || k_aver.starts_with("Tuple<"); + let is_list_or_vec_k = k_aver.starts_with("List<") || k_aver.starts_with("Vector<"); if k_aver != "String" && registry.record_type_idx(k_aver).is_none() && !is_primitive_k && !is_sum_k && !is_carrier_k + && !is_list_or_vec_k { return Err(WasmGcError::Unimplemented( "phase 3c — Map with K not String / user-record / sum / \ - primitive / generic-carrier (Option/Result/Tuple)", + primitive / generic-carrier (Option/Result/Tuple) / List / Vector", )); } @@ -629,13 +631,18 @@ fn emit_hash_for( if k_aver.starts_with("Option<") || k_aver.starts_with("Result<") || k_aver.starts_with("Tuple<") + || k_aver.starts_with("List<") + || k_aver.starts_with("Vector<") { // Same shape as carrier eq: proxy to the per-instantiation - // `__hash_` helper from `hash_helpers`. + // `__hash_` helper. Carriers come from hash_helpers, + // List/Vector from list_helpers; both are merged into + // `all_key_helpers` via the compound lookup at module + // assembly. let helpers = all_key_helpers .get(k_aver) .ok_or(WasmGcError::Validation(format!( - "hash_for: carrier `{k_aver}` has no registered hash helper" + "hash_for: compound `{k_aver}` has no registered hash helper" )))?; let mut f = Function::new([]); f.instruction(&Instruction::LocalGet(0)); @@ -674,15 +681,16 @@ fn emit_eq_for( if k_aver.starts_with("Option<") || k_aver.starts_with("Result<") || k_aver.starts_with("Tuple<") + || k_aver.starts_with("List<") + || k_aver.starts_with("Vector<") { - // Map-key eq for carriers proxies to the per-instantiation - // `__eq_` helper registered in `eq_helpers` (looked up - // via `all_key_helpers` which includes the carrier eq fn - // idx forwarded from the registry). + // Map-key eq for compounds proxies to `__eq_` (carriers + // from eq_helpers, List/Vector from list_helpers). Both are + // merged into `all_key_helpers` at module assembly time. let helpers = all_key_helpers .get(k_aver) .ok_or(WasmGcError::Validation(format!( - "eq_for: carrier `{k_aver}` has no registered eq helper" + "eq_for: compound `{k_aver}` has no registered eq helper" )))?; let mut f = Function::new([]); f.instruction(&Instruction::LocalGet(0)); @@ -787,6 +795,46 @@ fn emit_box_key(f: &mut Function, k_aver: &str, registry: &TypeRegistry) { } } +/// Heap type used by `Map.remove` for `ref.null` in the keys-array +/// store-back. Concrete idx for K kinds with their own struct type +/// (primitive K boxes, String, record, carrier, List, Vector); abstract +/// Eq for sum K (whose wasm rep is eqref since variants share no +/// concrete struct type). +fn key_storage_null_heap(k_aver: &str, registry: &TypeRegistry) -> HeapType { + if let Some(box_idx) = registry.primitive_key_box_idx(k_aver) { + return HeapType::Concrete(box_idx); + } + if k_aver == "String" + && let Some(s) = registry.string_array_type_idx + { + return HeapType::Concrete(s); + } + if let Some(r) = registry.record_type_idx(k_aver) { + return HeapType::Concrete(r); + } + if let Some(o) = registry.option_type_idx(k_aver) { + return HeapType::Concrete(o); + } + if let Some(r) = registry.result_type_idx(k_aver) { + return HeapType::Concrete(r); + } + if let Some(t) = registry.tuple_type_idx(k_aver) { + return HeapType::Concrete(t); + } + if let Some(l) = registry.list_type_idx(k_aver) { + return HeapType::Concrete(l); + } + if let Some(v) = registry.vector_type_idx(k_aver) { + return HeapType::Concrete(v); + } + // Sum K — eqref. ref.null of abstract Eq is a subtype of every + // concrete variant ref, so the array.set typechecks. + HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Eq, + } +} + fn string_idx(registry: &TypeRegistry) -> Result { registry .string_array_type_idx @@ -2006,19 +2054,14 @@ fn emit_map_remove( f.instruction(&Instruction::End); f.instruction(&Instruction::End); - // keys[i] = null. Heap type matches the keys array element ref: - // primitive-key box for primitive K, String slot for K=String, - // record slot for K=record. - let null_heap_idx = registry - .primitive_key_box_idx(k_aver) - .or(registry - .string_array_type_idx - .filter(|_| k_aver == "String")) - .or_else(|| registry.record_type_idx(k_aver)) - .unwrap_or(0); + // keys[i] = null. Heap type matches the keys array element ref — + // see `key_storage_null_heap` for the per-K-kind table (primitive + // box / String / record / carrier / List / Vector concrete idx, + // abstract Eq for sum K). + let null_heap = key_storage_null_heap(k_aver, registry); f.instruction(&Instruction::LocalGet(4)); f.instruction(&Instruction::LocalGet(7)); - f.instruction(&Instruction::RefNull(HeapType::Concrete(null_heap_idx))); + f.instruction(&Instruction::RefNull(null_heap)); f.instruction(&Instruction::ArraySet(slots.keys_array)); // map.size = map.size - 1 diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 80346bed..513254bd 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -114,6 +114,20 @@ pub(super) fn emit_module( { eq_helpers_registry.register_transitive(name, EqKind::Sum, ®istry); hash_helpers_registry.register_transitive(name, HashKind::Sum, ®istry); + } else if name.starts_with("Option<") && name.ends_with('>') { + // Carrier element of List> / Vector> + // / Map, _> — list/vec eq+hash bodies dispatch + // each element via `Call(__eq_Option)` which means + // eq_helpers must hold the slot. Same logic for the hash + // side. Inner type registration happens transitively. + eq_helpers_registry.register_transitive(name, EqKind::OptionEq, ®istry); + hash_helpers_registry.register_transitive(name, HashKind::OptionHash, ®istry); + } else if name.starts_with("Result<") && name.ends_with('>') { + eq_helpers_registry.register_transitive(name, EqKind::ResultEq, ®istry); + hash_helpers_registry.register_transitive(name, HashKind::ResultHash, ®istry); + } else if name.starts_with("Tuple<") && name.ends_with('>') { + eq_helpers_registry.register_transitive(name, EqKind::TupleEq, ®istry); + hash_helpers_registry.register_transitive(name, HashKind::TupleHash, ®istry); } } // Mirror eq registry's transitive shape — every type registered @@ -755,8 +769,10 @@ pub(super) fn emit_module( // List / Vector.fromList / String.split-join helper bodies. // Snapshot eq-helper fn idxs so list/vec eq+hash bodies can // dispatch nominal-element `==`/hash through `Call(__eq_)`. + // Merge in list_helpers' own list/vec canonicals so `List>` + // / `List>` element dispatch finds the inner helper. let string_eq_fn_idx = builtin_registry.lookup_wasm_fn_idx(BuiltinName::StringEq); - let eq_helper_fn_idx_map: HashMap = eq_helpers_registry + let mut eq_helper_fn_idx_map: HashMap = eq_helpers_registry .iter() .filter_map(|(n, _k)| { eq_helpers_registry @@ -764,7 +780,7 @@ pub(super) fn emit_module( .map(|i| (n.to_string(), i)) }) .collect(); - let hash_helper_fn_idx_map: HashMap = hash_helpers_registry + let mut hash_helper_fn_idx_map: HashMap = hash_helpers_registry .iter() .filter_map(|(n, _k)| { hash_helpers_registry @@ -772,6 +788,10 @@ pub(super) fn emit_module( .map(|i| (n.to_string(), i)) }) .collect(); + for (canonical, (eq_fn, hash_fn)) in &compound_eq_hash_lookup { + eq_helper_fn_idx_map.insert(canonical.clone(), *eq_fn); + hash_helper_fn_idx_map.insert(canonical.clone(), *hash_fn); + } list_helpers.emit_helper_bodies( &mut codes, ®istry, @@ -782,11 +802,32 @@ pub(super) fn emit_module( // Per-(record/sum) `__eq_` helper bodies — emit after // list helpers so any String fields can call `__wasmgc_string_eq` - // by the index recorded above. - eq_helpers_registry.emit_helper_bodies(&mut codes, ®istry, string_eq_fn_idx)?; + // by the index recorded above. The compound eq lookup forwards + // `List` / `Vector` fn idxs so a record field of type + // `List>` (etc.) can dispatch via + // `Call(__eq_List<…>)`. Same shape on the hash side. + let compound_eq_lookup: HashMap = compound_eq_hash_lookup + .iter() + .map(|(n, (eq, _))| (n.clone(), *eq)) + .collect(); + let compound_hash_lookup: HashMap = compound_eq_hash_lookup + .iter() + .map(|(n, (_, h))| (n.clone(), *h)) + .collect(); + eq_helpers_registry.emit_helper_bodies( + &mut codes, + ®istry, + string_eq_fn_idx, + &compound_eq_lookup, + )?; // `__hash_` helper bodies — emitted right after eq helpers so // every nominal/carrier hash dispatch finds its target fn_idx. - hash_helpers_registry.emit_helper_bodies(&mut codes, ®istry, string_eq_fn_idx)?; + hash_helpers_registry.emit_helper_bodies( + &mut codes, + ®istry, + string_eq_fn_idx, + &compound_hash_lookup, + )?; if let Some(hw) = &handler_wrapper { let user_handler_wasm_idx = import_count + 1 + (hw.user_handler_idx as u32); From f97b558c6da34ee46f207c849748d77121dd8fa1 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 19:30:49 +0200 Subject: [PATCH 14/18] 0.16.3: Map structural eq + commutative hash + literal discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Map joins the eq/hash matrix as a first-class compound: - MapHelperRegistry gets two new slots per instantiation: `eq` (eqref, eqref) -> i32 and `hash` (eqref) -> i32. Bodies live alongside the existing 11 kv helpers; type-section, function-section, and code- section emit grow accordingly. - `__eq_Map(a, b)`: ref.cast both args, fast-fail on size mismatch, then for each occupied bucket of a probe `b` via the per-(K,V) get fn — `None` or value mismatch returns 0. Insertion order is intentionally ignored (matches VM's `Value::Map(HashMap)` PartialEq + Python/Java/Rust/Haskell mainstream). - `__hash_Map(m)`: XOR-fold per occupied entry of `(djb2(k) << 5) + djb2(k) + djb2(v)`. XOR is commutative + associative → hash invariant under bucket / insertion order. Pairs with structural eq for the standard hash-eq contract. - V's helper resolution mirrors K — primitive V (Int/Float/Bool) inlines the cmp + hash, ref V (String/record/sum/carrier/list/vec/map) flows through `__eq_` / `__hash_`. assign_slots force-registers V as pseudo-K so the helpers exist regardless of `Map` reachability. - Map as a record/sum/list/vec field type now resolves: the compound_eq_hash_lookup threaded into eq_helpers / hash_helpers / list_helpers / map_helpers carries Map fn idxs alongside List/ Vector. Lookups normalize whitespace so source-side spacing (`Map` from record_fields) finds the registry's whitespace-free canonical. - Map as Map K is whitelisted in assign_slots; emit_eq_for / emit_hash_for / key_storage_null_heap dispatch through the same compound proxy pattern. Discovery walker: - types.rs adds `collect_maps_from_expr` — walks every Spanned in fn bodies and harvests Map from `expr.ty()`. Catches map literals (`{a => 3}` without annotation) where the canonical never appears in any signature. Recurses through FnCall / List / Tuple / IndependentProduct / MapLiteral / RecordCreate / RecordUpdate / Constructor / Match / BinOp / Attr / ErrorProp / TailCall. - list_eq_kind extended with `Map<…>` element kind; field_type_resolvable recursively accepts Map when both K and V resolve. - eq_helpers + hash_helpers' register_field_type recurses into Map's K and V so transitive helpers exist for the structural fold. Coverage: Map == ✓ structural, order-invariant record { Map } ✓ List> / Vector> ✓ Map, V> / Map, V> ✓ Map, V> ✓ m = {a => 3} (no annotation) ✓ via expr.ty() walker 621/621 lib tests, 7/7 games, no regressions. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/wasm_gc/body/emit.rs | 12 + src/codegen/wasm_gc/body/eq_helpers.rs | 28 +- src/codegen/wasm_gc/body/hash_helpers.rs | 23 ++ src/codegen/wasm_gc/lists.rs | 60 ++- src/codegen/wasm_gc/maps.rs | 461 ++++++++++++++++++++++- src/codegen/wasm_gc/module.rs | 19 + src/codegen/wasm_gc/types.rs | 67 ++++ 7 files changed, 646 insertions(+), 24 deletions(-) diff --git a/src/codegen/wasm_gc/body/emit.rs b/src/codegen/wasm_gc/body/emit.rs index 9a4a08f7..f8e4bd98 100644 --- a/src/codegen/wasm_gc/body/emit.rs +++ b/src/codegen/wasm_gc/body/emit.rs @@ -610,6 +610,18 @@ fn sum_or_record_eq_fn(operand: &Spanned, ctx: &EmitCtx<'_>) -> Option structural eq — `__eq_Map` slot lives in + // MapHelperRegistry but we mirror the fn idx into + // `eq_helpers` so the lookup shape stays uniform with + // record/sum/carrier/list/vec dispatch. + crate::types::Type::Map(_, _) => { + let canonical: String = ty + .display() + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); + ctx.fn_map.eq_helpers.get(&canonical).copied() + } _ => None, } } diff --git a/src/codegen/wasm_gc/body/eq_helpers.rs b/src/codegen/wasm_gc/body/eq_helpers.rs index fcacfd51..18044b88 100644 --- a/src/codegen/wasm_gc/body/eq_helpers.rs +++ b/src/codegen/wasm_gc/body/eq_helpers.rs @@ -228,10 +228,32 @@ impl EqHelperRegistry { .and_then(|s| s.strip_suffix('>')) { self.register_field_type(inner.trim(), registry); + } else if let Some(inner) = field_ty + .strip_prefix("Map<") + .and_then(|s| s.strip_suffix('>')) + { + // Map — eq+hash slots live in MapHelperRegistry, + // not eq_helpers. This walk only ensures K and V's own + // helpers exist (K may be a record/sum/carrier; V same). + // Structural eq on the map dispatches K and V via Call + // into those helpers. + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + let k = inner[..idx].trim(); + let v = inner[idx + 1..].trim(); + self.register_field_type(k, registry); + self.register_field_type(v, registry); + return; + } + _ => {} + } + } } - // Map still falls through — record-of-Map field eq is a - // separate followup (the map's own equality semantics aren't - // canonical: insertion-order vs structural). } pub(crate) fn iter(&self) -> impl Iterator + '_ { diff --git a/src/codegen/wasm_gc/body/hash_helpers.rs b/src/codegen/wasm_gc/body/hash_helpers.rs index 2e662d19..735c0d00 100644 --- a/src/codegen/wasm_gc/body/hash_helpers.rs +++ b/src/codegen/wasm_gc/body/hash_helpers.rs @@ -195,6 +195,29 @@ impl HashHelperRegistry { .and_then(|s| s.strip_suffix('>')) { self.register_field_type(inner.trim(), registry); + } else if let Some(inner) = field_ty + .strip_prefix("Map<") + .and_then(|s| s.strip_suffix('>')) + { + // Mirror eq_helpers — Map hash slot lives in + // MapHelperRegistry; recurse so K and V's hash helpers + // exist for the structural fold. + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + let k = inner[..idx].trim(); + let v = inner[idx + 1..].trim(); + self.register_field_type(k, registry); + self.register_field_type(v, registry); + return; + } + _ => {} + } + } } } diff --git a/src/codegen/wasm_gc/lists.rs b/src/codegen/wasm_gc/lists.rs index 6a2678c5..4a267e14 100644 --- a/src/codegen/wasm_gc/lists.rs +++ b/src/codegen/wasm_gc/lists.rs @@ -710,7 +710,8 @@ fn list_eq_kind(elem: &str, registry: &TypeRegistry) -> Option { || trimmed.starts_with("Result<") || trimmed.starts_with("Tuple<") || trimmed.starts_with("List<") - || trimmed.starts_with("Vector<")) + || trimmed.starts_with("Vector<") + || trimmed.starts_with("Map<")) && trimmed.ends_with('>') { let canonical: String = trimmed.chars().filter(|c| !c.is_whitespace()).collect(); @@ -892,6 +893,27 @@ pub(super) fn field_type_resolvable( { return field_type_resolvable(inner.trim(), registry, seen); } + // Map — `__eq_Map` / `__hash_Map` live in + // MapHelperRegistry::kv. Resolvable iff both K and V are + // themselves resolvable. + if let Some(inner) = field.strip_prefix("Map<").and_then(|s| s.strip_suffix('>')) { + let bytes = inner.as_bytes(); + let mut depth: i32 = 0; + for (idx, b) in bytes.iter().enumerate() { + match b { + b'<' | b'(' => depth += 1, + b'>' | b')' => depth -= 1, + b',' if depth == 0 => { + let k = inner[..idx].trim(); + let v = inner[idx + 1..].trim(); + return field_type_resolvable(k, registry, seen) + && field_type_resolvable(v, registry, seen); + } + _ => {} + } + } + return false; + } false } @@ -1863,20 +1885,30 @@ pub(super) fn emit_record_eq_inline( // Recursive ref to the same record — call self. f.instruction(&Instruction::Call(self_fn_idx.unwrap())); } - other if eq_helper_fn_idx.contains_key(other) => { - // Nested nominal type with its own __eq_ helper - // — call by fn idx. Field refs are subtypes of - // eqref so the implicit upcast at the call site - // is fine. - let idx = eq_helper_fn_idx[other]; - f.instruction(&Instruction::Call(idx)); - } other => { - return Err(WasmGcError::Validation(format!( - "record `{record_name}` field type `{other}` has no eq dispatch \ - (not in {{Int, Float, Bool, String}}, no `__eq_{other}` helper, \ - not self-recursive)" - ))); + // Look up `__eq_` — for compound types (List/ + // Vector/Map/Option/Result/Tuple) the registry keys + // are whitespace-stripped, but a field type from + // `record_fields` can carry source-side spacing. + // Try both forms before erroring. + let normalized = super::types::normalize_compound(other); + let key = if eq_helper_fn_idx.contains_key(other) { + Some(other.to_string()) + } else if eq_helper_fn_idx.contains_key(normalized.as_str()) { + Some(normalized.clone()) + } else { + None + }; + if let Some(k) = key { + let idx = eq_helper_fn_idx[k.as_str()]; + f.instruction(&Instruction::Call(idx)); + } else { + return Err(WasmGcError::Validation(format!( + "record `{record_name}` field type `{other}` has no eq dispatch \ + (not in {{Int, Float, Bool, String}}, no `__eq_{other}` helper, \ + not self-recursive)" + ))); + } } } if i > 0 { diff --git a/src/codegen/wasm_gc/maps.rs b/src/codegen/wasm_gc/maps.rs index 1d42d0b6..7e1061d0 100644 --- a/src/codegen/wasm_gc/maps.rs +++ b/src/codegen/wasm_gc/maps.rs @@ -86,6 +86,16 @@ pub(super) struct MapKVHelpers { /// `from_list(l) -> Map`. Walks `l`, struct.get's the /// (K, V) from each tuple, calls the per-(K, V) `set` helper. pub(super) from_list: u32, + /// `__eq_Map(a, b) -> i32`. Structural eq — `a.size == + /// b.size && ∀ k ∈ a: get(b, k) == Some(a[k])`. Insertion order + /// is intentionally ignored (matches VM's `HashMap` PartialEq + + /// the Python/Java/Rust/Haskell mainstream). + pub(super) eq: u32, + /// `__hash_Map(m) -> i32`. Order-independent commutative + /// fold — `h = 0; for (k, v) in m: h ^= djb2(k) * 33 + djb2(v)`. + /// XOR is commutative + associative so the result is invariant + /// to bucket ordering. + pub(super) hash: u32, } #[derive(Default)] @@ -105,7 +115,24 @@ pub(super) struct MapHelperRegistry { /// matches `assign_slots` / `emit_function_section` / /// `emit_helper_bodies` exactly. #[allow(clippy::type_complexity)] - kv_type_indices: HashMap, + kv_type_indices: HashMap< + String, + ( + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + ), + >, } impl MapHelperRegistry { @@ -129,7 +156,7 @@ impl MapHelperRegistry { let mut k_names: Vec = Vec::new(); let mut k_seen: std::collections::HashSet = std::collections::HashSet::new(); for canonical in map_canonicals { - let (k_aver, _) = + let (k_aver, v_aver) = super::types::parse_map_kv(canonical).ok_or(WasmGcError::Validation(format!( "MapHelperRegistry: cannot parse K, V from `{canonical}`" )))?; @@ -152,12 +179,26 @@ impl MapHelperRegistry { .filter(|v| v.parent == k_aver) .any(|v| v.fields.iter().any(|t| t.trim() == "String")); } + // Map's structural eq + hash dispatches V via the + // same `__hash_` / `__eq_` helper map K uses, so V + // is force-registered as pseudo-K too. Skips primitive V + // (the body emitter falls back to inline cmp for those). + let v_aver_trim = v_aver.trim(); + if v_aver_trim == "String" { + needs_string = true; + } if needs_string && k_seen.insert("String".into()) { k_names.push("String".into()); } if k_seen.insert(k_aver.to_string()) { k_names.push(k_aver.to_string()); } + if !super::types::TypeRegistry::is_primitive_map_key(v_aver_trim) + && v_aver_trim != "String" + && k_seen.insert(v_aver_trim.to_string()) + { + k_names.push(v_aver_trim.to_string()); + } } // For every record / sum K, recursively collect all @@ -297,6 +338,10 @@ impl MapHelperRegistry { *next_type_idx += 1; let from_list_type_idx = *next_type_idx; *next_type_idx += 1; + let eq_type_idx = *next_type_idx; + *next_type_idx += 1; + let hash_type_idx = *next_type_idx; + *next_type_idx += 1; let empty_fn = *next_wasm_fn_idx; *next_wasm_fn_idx += 1; let set_fn = *next_wasm_fn_idx; @@ -319,6 +364,10 @@ impl MapHelperRegistry { *next_wasm_fn_idx += 1; let from_list_fn = *next_wasm_fn_idx; *next_wasm_fn_idx += 1; + let eq_fn = *next_wasm_fn_idx; + *next_wasm_fn_idx += 1; + let hash_fn = *next_wasm_fn_idx; + *next_wasm_fn_idx += 1; // K can be String, a user-defined record (field-by-field // hash + eq), or a primitive (Int / Float / Bool). Primitive @@ -337,16 +386,19 @@ impl MapHelperRegistry { || k_aver.starts_with("Result<") || k_aver.starts_with("Tuple<"); let is_list_or_vec_k = k_aver.starts_with("List<") || k_aver.starts_with("Vector<"); + let is_map_k = k_aver.starts_with("Map<"); if k_aver != "String" && registry.record_type_idx(k_aver).is_none() && !is_primitive_k && !is_sum_k && !is_carrier_k && !is_list_or_vec_k + && !is_map_k { return Err(WasmGcError::Unimplemented( "phase 3c — Map with K not String / user-record / sum / \ - primitive / generic-carrier (Option/Result/Tuple) / List / Vector", + primitive / generic-carrier (Option/Result/Tuple) / List / \ + Vector / Map", )); } @@ -364,6 +416,8 @@ impl MapHelperRegistry { remove: remove_fn, entries: entries_fn, from_list: from_list_fn, + eq: eq_fn, + hash: hash_fn, }, ); self.kv_type_indices.insert( @@ -380,6 +434,8 @@ impl MapHelperRegistry { remove_type_idx, entries_type_idx, from_list_type_idx, + eq_type_idx, + hash_type_idx, ), ); self.kv_order.push(canonical.clone()); @@ -493,6 +549,20 @@ impl MapHelperRegistry { types.ty().function([map_ref], [lt_ref]); // from_list : (List>) -> Map types.ty().function([lt_ref], [map_ref]); + // __eq_Map : (eqref, eqref) -> i32. Eqref params so + // record/sum/list/vec field dispatch can call uniformly + // (the body ref.casts both args to the typed map ref). + let eq_ref = ValType::Ref(RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Eq, + }, + }); + types.ty().function([eq_ref, eq_ref], [ValType::I32]); + // __hash_Map : (eqref) -> i32. Same eqref-shape + // calling convention as the carrier hash helpers. + types.ty().function([eq_ref], [ValType::I32]); let _ = opt_ref; let _ = tup_idx; } @@ -508,7 +578,8 @@ impl MapHelperRegistry { funcs.function(e); } for canonical in &self.kv_order { - let (em, st, gt, ln, god, pair, ks, vs, rm, en, fl) = self.kv_type_indices[canonical]; + let (em, st, gt, ln, god, pair, ks, vs, rm, en, fl, eq, hs) = + self.kv_type_indices[canonical]; funcs.function(em); funcs.function(st); funcs.function(gt); @@ -520,6 +591,8 @@ impl MapHelperRegistry { funcs.function(rm); funcs.function(en); funcs.function(fl); + funcs.function(eq); + funcs.function(hs); } } @@ -597,11 +670,51 @@ impl MapHelperRegistry { let helpers = self.kv[canonical]; codes.function(&emit_map_entries(canonical, registry)?); codes.function(&emit_map_from_list(canonical, registry, helpers.set)?); + // Structural eq + commutative hash for `Map`. V's + // hash + eq fn idxs come from `all_key_helpers` (same + // table that drives K dispatch — V is just another + // shape that needs the same family of helpers when V is + // not a primitive). + let v_helpers = v_helper_for(canonical, &all_key_helpers, registry)?; + codes.function(&emit_map_eq( + canonical, + registry, + key_h, + v_helpers, + helpers.get, + )?); + codes.function(&emit_map_hash(canonical, registry, key_h, v_helpers)?); } Ok(()) } } +/// Resolve hash + eq fn idx for V — looks the same shape up as K. V +/// can be a primitive (hash/eq are inline instructions, not fn calls +/// — return None and let the body emitter pick the inline path), or +/// a ref-shaped K kind we already registered (records / sums / +/// carriers / List / Vector / Map). Returns the proxy fn idxs. +fn v_helper_for( + canonical: &str, + all_key_helpers: &HashMap, + _registry: &TypeRegistry, +) -> Result, WasmGcError> { + let (_, v_aver) = super::types::parse_map_kv(canonical).ok_or(WasmGcError::Validation( + format!("v_helper_for: bad canonical `{canonical}`"), + ))?; + let v_aver = v_aver.trim(); + if super::types::TypeRegistry::is_primitive_map_key(v_aver) { + // Primitive V (Int / Float / Bool) — body emitter inlines + // the comparison + hash, no helper dispatch. + return Ok(None); + } + // String + every other ref V flows through `all_key_helpers`, + // which assign_slots force-registered as pseudo-K (so the + // helpers exist regardless of whether the program actually + // holds `Map`). + Ok(all_key_helpers.get(v_aver).copied()) +} + /// `hash : (K) -> i32`. K can be `String` (DJB2 over the bytes) or /// any user-defined record (field-by-field combine, delegating to /// the per-K helper for String fields). @@ -633,10 +746,11 @@ fn emit_hash_for( || k_aver.starts_with("Tuple<") || k_aver.starts_with("List<") || k_aver.starts_with("Vector<") + || k_aver.starts_with("Map<") { // Same shape as carrier eq: proxy to the per-instantiation // `__hash_` helper. Carriers come from hash_helpers, - // List/Vector from list_helpers; both are merged into + // List/Vector/Map from their own registries; all merged into // `all_key_helpers` via the compound lookup at module // assembly. let helpers = all_key_helpers @@ -683,10 +797,12 @@ fn emit_eq_for( || k_aver.starts_with("Tuple<") || k_aver.starts_with("List<") || k_aver.starts_with("Vector<") + || k_aver.starts_with("Map<") { // Map-key eq for compounds proxies to `__eq_` (carriers - // from eq_helpers, List/Vector from list_helpers). Both are - // merged into `all_key_helpers` at module assembly time. + // from eq_helpers, List/Vector from list_helpers, Map from + // MapHelperRegistry::kv). All merged into `all_key_helpers` + // at module assembly time. let helpers = all_key_helpers .get(k_aver) .ok_or(WasmGcError::Validation(format!( @@ -827,6 +943,9 @@ fn key_storage_null_heap(k_aver: &str, registry: &TypeRegistry) -> HeapType { if let Some(v) = registry.vector_type_idx(k_aver) { return HeapType::Concrete(v); } + if let Some(slots) = registry.map_slots(k_aver) { + return HeapType::Concrete(slots.map); + } // Sum K — eqref. ref.null of abstract Eq is a subtype of every // concrete variant ref, so the array.set typechecks. HeapType::Abstract { @@ -1870,6 +1989,334 @@ fn emit_map_walk_values_to_list( Ok(f) } +/// `__eq_Map(a: eqref, b: eqref) -> i32`. Structural eq — +/// `a.size == b.size && ∀ k ∈ a: get(b, k) == Some(a[k])`. Order- +/// independent (matches VM's Rust HashMap PartialEq). +fn emit_map_eq( + canonical: &str, + registry: &TypeRegistry, + keyh: KeyHelpers, + v_helpers: Option, + get_fn_idx: u32, +) -> Result { + let slots = slots_for(canonical, registry)?; + let (k_aver, v_aver) = super::types::parse_map_kv(canonical).unwrap(); + let _ = keyh; + let map_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.map), + }); + let keys_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.keys_array), + }); + let values_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.values_array), + }); + let v_val = + super::types::aver_to_wasm(v_aver, Some(registry))?.ok_or(WasmGcError::Validation( + format!("Map<{k_aver},{v_aver}>.eq: V `{v_aver}` has no wasm rep"), + ))?; + let opt_idx = registry + .option_type_idx(&format!("Option<{v_aver}>")) + .ok_or(WasmGcError::Validation(format!( + "Map.eq: `Option<{v_aver}>` not registered" + )))?; + let opt_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(opt_idx), + }); + // Locals: 2=typed map_a, 3=typed map_b, 4=cap, 5=i, 6=keys_a, + // 7=values_a, 8=cur_key (boxed), 9=opt result, 10=v_a, 11=v_b + let mut f = Function::new(vec![ + (1, map_ref), + (1, map_ref), + (1, ValType::I32), + (1, ValType::I32), + (1, keys_ref), + (1, values_ref), + (1, key_storage_val_type(k_aver, registry)?), + (1, opt_ref), + (1, v_val), + (1, v_val), + ]); + let map_heap = HeapType::Concrete(slots.map); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(map_heap)); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::RefCastNonNull(map_heap)); + f.instruction(&Instruction::LocalSet(3)); + // if a.size != b.size return 0 + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 0, + }); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 0, + }); + f.instruction(&Instruction::I32Ne); + f.instruction(&Instruction::If(BlockType::Empty)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + // cap = a.cap; keys_a = a.keys; values_a = a.values; i = 0 + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 1, + }); + f.instruction(&Instruction::LocalSet(4)); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 2, + }); + f.instruction(&Instruction::LocalSet(6)); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 3, + }); + f.instruction(&Instruction::LocalSet(7)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::LocalSet(5)); + // for i in 0..cap + f.instruction(&Instruction::Block(BlockType::Empty)); + f.instruction(&Instruction::Loop(BlockType::Empty)); + f.instruction(&Instruction::LocalGet(5)); + f.instruction(&Instruction::LocalGet(4)); + f.instruction(&Instruction::I32GeS); + f.instruction(&Instruction::BrIf(1)); + // cur_key = keys_a[i] + f.instruction(&Instruction::LocalGet(6)); + f.instruction(&Instruction::LocalGet(5)); + f.instruction(&Instruction::ArrayGet(slots.keys_array)); + f.instruction(&Instruction::LocalSet(8)); + // if cur_key != null: probe b + f.instruction(&Instruction::LocalGet(8)); + f.instruction(&Instruction::RefIsNull); + f.instruction(&Instruction::I32Eqz); + f.instruction(&Instruction::If(BlockType::Empty)); + // opt = get_fn(b, unbox(cur_key)) + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::LocalGet(8)); + emit_unbox_key(&mut f, k_aver, registry); + f.instruction(&Instruction::Call(get_fn_idx)); + f.instruction(&Instruction::LocalSet(9)); + // if opt.tag == 0 → return 0 + f.instruction(&Instruction::LocalGet(9)); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 0, + }); + f.instruction(&Instruction::I32Eqz); + f.instruction(&Instruction::If(BlockType::Empty)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + // v_a = values_a[i]; v_b = opt.value + f.instruction(&Instruction::LocalGet(7)); + f.instruction(&Instruction::LocalGet(5)); + f.instruction(&Instruction::ArrayGet(slots.values_array)); + f.instruction(&Instruction::LocalSet(10)); + f.instruction(&Instruction::LocalGet(9)); + f.instruction(&Instruction::StructGet { + struct_type_index: opt_idx, + field_index: 1, + }); + f.instruction(&Instruction::LocalSet(11)); + // if v_a != v_b → return 0 + f.instruction(&Instruction::LocalGet(10)); + f.instruction(&Instruction::LocalGet(11)); + emit_v_eq(&mut f, v_aver, v_helpers)?; + f.instruction(&Instruction::I32Eqz); + f.instruction(&Instruction::If(BlockType::Empty)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Return); + f.instruction(&Instruction::End); + f.instruction(&Instruction::End); + // i++ + f.instruction(&Instruction::LocalGet(5)); + f.instruction(&Instruction::I32Const(1)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(5)); + f.instruction(&Instruction::Br(0)); + f.instruction(&Instruction::End); + f.instruction(&Instruction::End); + f.instruction(&Instruction::I32Const(1)); + f.instruction(&Instruction::End); + Ok(f) +} + +/// Stack: `[v_a, v_b]` of Aver type `v_aver`. Push i32 (1=eq, 0=ne). +/// Primitive V → inline cmp; ref V → `Call(v_helpers.eq)`. +fn emit_v_eq( + f: &mut Function, + v_aver: &str, + v_helpers: Option, +) -> Result<(), WasmGcError> { + match v_aver.trim() { + "Int" => { + f.instruction(&Instruction::I64Eq); + } + "Bool" => { + f.instruction(&Instruction::I32Eq); + } + "Float" => { + f.instruction(&Instruction::F64Eq); + } + _ => { + let h = v_helpers.ok_or(WasmGcError::Validation(format!( + "emit_v_eq: V `{v_aver}` needs ref helpers (record/sum/carrier/list/vec/map)" + )))?; + f.instruction(&Instruction::Call(h.eq)); + } + } + Ok(()) +} + +/// Stack: `[v]` of Aver type `v_aver`. Push i32 hash. Primitive V → +/// inline DJB2-style mix; ref V → `Call(v_helpers.hash)`. +fn emit_v_hash( + f: &mut Function, + v_aver: &str, + v_helpers: Option, +) -> Result<(), WasmGcError> { + match v_aver.trim() { + "Int" => { + f.instruction(&Instruction::I32WrapI64); + } + "Bool" => {} // already i32 + "Float" => { + f.instruction(&Instruction::I64ReinterpretF64); + f.instruction(&Instruction::I32WrapI64); + } + _ => { + let h = v_helpers.ok_or(WasmGcError::Validation(format!( + "emit_v_hash: V `{v_aver}` needs ref helpers" + )))?; + f.instruction(&Instruction::Call(h.hash)); + } + } + Ok(()) +} + +/// `__hash_Map(m: eqref) -> i32`. XOR-fold per occupied entry of +/// `djb2(k) * 33 + djb2(v)`. XOR is commutative + associative → the +/// result is invariant to bucket / insertion order. +fn emit_map_hash( + canonical: &str, + registry: &TypeRegistry, + keyh: KeyHelpers, + v_helpers: Option, +) -> Result { + let slots = slots_for(canonical, registry)?; + let (k_aver, v_aver) = super::types::parse_map_kv(canonical).unwrap(); + let map_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.map), + }); + let keys_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.keys_array), + }); + let values_ref = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(slots.values_array), + }); + // Locals: 1=typed map, 2=cap, 3=i, 4=keys, 5=values, 6=cur_key + // (boxed), 7=h (i32 accumulator), 8=entry_h (i32 per-entry mix). + let mut f = Function::new(vec![ + (1, map_ref), + (1, ValType::I32), + (1, ValType::I32), + (1, keys_ref), + (1, values_ref), + (1, key_storage_val_type(k_aver, registry)?), + (1, ValType::I32), + (1, ValType::I32), + ]); + let map_heap = HeapType::Concrete(slots.map); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::RefCastNonNull(map_heap)); + f.instruction(&Instruction::LocalSet(1)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::LocalSet(7)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 1, + }); + f.instruction(&Instruction::LocalSet(2)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 2, + }); + f.instruction(&Instruction::LocalSet(4)); + f.instruction(&Instruction::LocalGet(1)); + f.instruction(&Instruction::StructGet { + struct_type_index: slots.map, + field_index: 3, + }); + f.instruction(&Instruction::LocalSet(5)); + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::LocalSet(3)); + f.instruction(&Instruction::Block(BlockType::Empty)); + f.instruction(&Instruction::Loop(BlockType::Empty)); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::LocalGet(2)); + f.instruction(&Instruction::I32GeS); + f.instruction(&Instruction::BrIf(1)); + f.instruction(&Instruction::LocalGet(4)); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::ArrayGet(slots.keys_array)); + f.instruction(&Instruction::LocalSet(6)); + f.instruction(&Instruction::LocalGet(6)); + f.instruction(&Instruction::RefIsNull); + f.instruction(&Instruction::I32Eqz); + f.instruction(&Instruction::If(BlockType::Empty)); + // entry_h = hash_K(unbox(cur_key)) * 33 + hash_V(values[i]) + f.instruction(&Instruction::LocalGet(6)); + emit_unbox_key(&mut f, k_aver, registry); + f.instruction(&Instruction::Call(keyh.hash)); + f.instruction(&Instruction::I32Const(5)); + f.instruction(&Instruction::I32Shl); + // h_k * 32 (will add h_k below to become *33; OR more accurate: + // shift-add for *33). Cheaper: do `(kh<<5) + kh + vh`. + f.instruction(&Instruction::LocalGet(6)); + emit_unbox_key(&mut f, k_aver, registry); + f.instruction(&Instruction::Call(keyh.hash)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalGet(5)); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::ArrayGet(slots.values_array)); + emit_v_hash(&mut f, v_aver, v_helpers)?; + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(8)); + // h ^= entry_h + f.instruction(&Instruction::LocalGet(7)); + f.instruction(&Instruction::LocalGet(8)); + f.instruction(&Instruction::I32Xor); + f.instruction(&Instruction::LocalSet(7)); + f.instruction(&Instruction::End); + f.instruction(&Instruction::LocalGet(3)); + f.instruction(&Instruction::I32Const(1)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(3)); + f.instruction(&Instruction::Br(0)); + f.instruction(&Instruction::End); + f.instruction(&Instruction::End); + f.instruction(&Instruction::LocalGet(7)); + f.instruction(&Instruction::End); + Ok(f) +} + /// `remove(map, k) -> map`. Linear-probe locate the entry; if not /// found, return the map unchanged. If found, do a backwards-shift /// over the contiguous probe chain so subsequent `get` calls still diff --git a/src/codegen/wasm_gc/module.rs b/src/codegen/wasm_gc/module.rs index 513254bd..68a22729 100644 --- a/src/codegen/wasm_gc/module.rs +++ b/src/codegen/wasm_gc/module.rs @@ -577,6 +577,16 @@ pub(super) fn emit_module( eq_helpers_lookup.insert(name.to_string(), fn_idx); } } + // Map structural-eq fn idxs flow through the same lookup so + // BinOp::Eq on a Map dispatches via `Call(__eq_Map)` (sum_ + // or_record_eq_fn → ctx.fn_map.eq_helpers). Whitespace-free + // canonical matches what the operand's `.ty().display()` produces + // at the call site. + for canonical in ®istry.map_order { + if let Some(h) = map_helpers.kv_helpers(canonical) { + eq_helpers_lookup.insert(canonical.clone(), h.eq); + } + } let fn_map = FnMap { by_name, builtins: builtin_idx_lookup, @@ -745,6 +755,15 @@ pub(super) fn emit_module( compound_eq_hash_lookup.insert(format!("Vector<{}>", elem.trim()), (eq_fn, hash_fn)); } } + // Map structural eq + commutative hash — per-instantiation + // helpers live in MapHelperRegistry::kv. Threading them into the + // compound lookup lets record/sum/list/vec field dispatch call + // `__eq_Map` / `__hash_Map` uniformly with carriers. + for canonical in ®istry.map_order { + if let Some(h) = map_helpers.kv_helpers(canonical) { + compound_eq_hash_lookup.insert(canonical.clone(), (h.eq, h.hash)); + } + } // Carrier eq+hash lookup — Option/Result/Tuple instantiations // get their helpers from eq_helpers / hash_helpers; map keys // proxy through these. Build the pair map by zipping the two diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index ee8a0a41..96f0dbfd 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -538,6 +538,10 @@ impl TypeRegistry { if let Stmt::Binding(_, Some(annot), _) = stmt { collect_maps_from_str(annot, &mut pending_maps); } + let expr = match stmt { + Stmt::Binding(_, _, e) | Stmt::Expr(e) => e, + }; + collect_maps_from_expr(expr, &mut pending_maps); } } } @@ -1850,6 +1854,69 @@ fn collect_maps_from_str(type_str: &str, out: &mut Vec) { } } +/// Walk every sub-expression and harvest `Map` canonicals from +/// each `Spanned::ty()` stamp. Catches map literals (`{a => 3}` +/// without annotation) and expressions whose type is a Map but the +/// canonical never shows up in any signature — symmetrical with +/// `collect_tuples_from_expr`. Recurses into sub-expressions so +/// nested literals / fn args / match arms all get their stamps +/// harvested. +fn collect_maps_from_expr(expr: &crate::ast::Spanned, out: &mut Vec) { + use crate::ast::Expr; + if let Some(ty) = expr.ty() { + let display = ty.display(); + collect_maps_from_str(&display, out); + } + match &expr.node { + Expr::FnCall(callee, args) => { + collect_maps_from_expr(callee, out); + for a in args { + collect_maps_from_expr(a, out); + } + } + Expr::List(items) | Expr::Tuple(items) | Expr::IndependentProduct(items, _) => { + for it in items { + collect_maps_from_expr(it, out); + } + } + Expr::MapLiteral(entries) => { + for (k, v) in entries { + collect_maps_from_expr(k, out); + collect_maps_from_expr(v, out); + } + } + Expr::RecordCreate { fields, .. } => { + for (_, v) in fields { + collect_maps_from_expr(v, out); + } + } + Expr::RecordUpdate { base, updates, .. } => { + collect_maps_from_expr(base, out); + for (_, v) in updates { + collect_maps_from_expr(v, out); + } + } + Expr::Constructor(_, Some(arg)) => collect_maps_from_expr(arg, out), + Expr::Match { subject, arms } => { + collect_maps_from_expr(subject, out); + for arm in arms { + collect_maps_from_expr(&arm.body, out); + } + } + Expr::BinOp(_, l, r) => { + collect_maps_from_expr(l, out); + collect_maps_from_expr(r, out); + } + Expr::Attr(e, _) | Expr::ErrorProp(e) => collect_maps_from_expr(e, out), + Expr::TailCall(tc) => { + for a in &tc.args { + collect_maps_from_expr(a, out); + } + } + _ => {} + } +} + /// Split a canonical `Map` into its `K` and `V` parts (both /// borrowed slices of the input). Returns `None` if the string /// doesn't match the expected shape. From 40fbb5534e6797812d813f6f9175fbef6cef6de9 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 20:18:07 +0200 Subject: [PATCH 15/18] =?UTF-8?q?0.17=20sweep=20[1/4]:=20drop=20Map.empty(?= =?UTF-8?q?)=20=E2=80=94=20use=20{}=20literal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Map.empty()` was redundant against the `{}` literal — same way `[]` covers `List.empty` (which never existed). Bidirectional inference already propagates the expected `Map` from the binding annotation into `{}`, and the discovery walker introduced in 0.16.3 picks up map canonicals from `expr.ty()` so unannotated literals like `m = {a => 3}` also work end-to-end. Removed: - typecheck signature + bidirectional case + map_calls arm - VM dispatch (Value::Map and NanValue paths) - 4 backends: codegen/builtins enum, rust/wasm/wasm-gc/lean/dafny dispatch (lean and dafny had stale match arms that silently turned into wildcard bindings after the enum drop — purged) - types/map.rs `empty` / `empty_nv` fns + member registration Migration: - examples: data/map.av (5×), data/json.av (1×), formal/law_auto.av, games/rogue (false positive — that's `Map.emptyMap`, a user fn, untouched), tools/website/llms.txt - lib tests: src/types/checker/tests.rs (2×) - docs: language.md (set example), services.md (Map fn table), vm.md (Immediate-tag note); CHANGELOG kept historical wording. Vera-bench solutions/aver: 0 occurrences, no companion PR needed. 621/621 lib tests, no regressions. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/language.md | 2 +- docs/services.md | 2 +- docs/vm.md | 2 +- examples/data/json.av | 2 +- examples/data/map.av | 10 +++--- examples/formal/law_auto.av | 2 +- src/codegen/builtins.rs | 2 -- src/codegen/dafny/expr.rs | 1 - src/codegen/lean/builtins.rs | 1 - src/codegen/lean/expr.rs | 10 ++---- src/codegen/rust/builtins.rs | 1 - src/codegen/wasm/expr/builtins.rs | 4 --- src/codegen/wasm_gc/body/builtins.rs | 49 ++-------------------------- src/types/checker/builtins.rs | 1 - src/types/checker/infer/expr.rs | 3 -- src/types/checker/infer/map_calls.rs | 8 ----- src/types/checker/tests.rs | 4 +-- src/types/map.rs | 29 +++------------- src/vm/builtin.rs | 5 +-- tools/website/llms.txt | 2 +- 20 files changed, 23 insertions(+), 117 deletions(-) diff --git a/docs/language.md b/docs/language.md index 380fee17..a3b7e7e1 100644 --- a/docs/language.md +++ b/docs/language.md @@ -245,7 +245,7 @@ Most application code in Aver stays first-order and explicit. Use function param Aver has no dedicated `Set` type. The idiomatic way to express a set is `Map` — a map whose values carry no information. All `Map.*` operations work on sets: ```aver -seen: Map = Map.empty() +seen: Map = {} seen2 = Map.set(seen, "alice", Unit) Map.has(seen2, "alice") // true Map.len(seen2) // 1 diff --git a/docs/services.md b/docs/services.md index 3c8dd502..76914afd 100644 --- a/docs/services.md +++ b/docs/services.md @@ -134,7 +134,7 @@ Source: `src/types/map.rs` | Function | Signature | Notes | |---|---|---| -| `Map.empty` | `() -> Map` | | +| `{}` (literal) | — | The empty map; type from context (annotation or expected type). No `Map.empty()` builtin since 0.17 — symmetric with `[]` for List. | | `Map.fromList` | `List<(K, V)> -> Map` | Keys must be hashable (Int, Float, String, Bool) | | `Map.set` | `(Map, K, V) -> Map` | Returns new map with key set | | `Map.get` | `(Map, K) -> Option` | | diff --git a/docs/vm.md b/docs/vm.md index 852beba3..139ad469 100644 --- a/docs/vm.md +++ b/docs/vm.md @@ -117,7 +117,7 @@ The point of `v2` is not only compactness. It is to keep the common Aver shapes - `Bool`, `Unit`, and `None` are pure inline singletons - `Some(true)`, `Ok(Unit)`, `Err(None)` stay inline - `Some(42)`, `Ok(-7)`, `Err(0)` stay inline as long as the int fits the wrapper-inline range -- `[]` and `Map.empty()` are real values under their normal collection tags, not exceptions hidden in `Immediate` +- `[]` and `{}` are real values under their normal collection tags, not exceptions hidden in `Immediate` - strings up to 5 UTF-8 bytes stay inline under `TAG_STRING` - nullary variants such as `Status.Todo` or `Color.Red` travel as `Symbol` handles instead of arena entries diff --git a/examples/data/json.av b/examples/data/json.av index 12c24795..fa81385e 100644 --- a/examples/data/json.av +++ b/examples/data/json.av @@ -1081,7 +1081,7 @@ verify parseArrayNext fn emptyJsonMap() -> Map ? "JSON helper: emptyJsonMap." - Map.empty() + {} fn parseObject(s: String, pos: Int) -> ParseResult ? "JSON helper: parseObject." diff --git a/examples/data/map.av b/examples/data/map.av index 97cca983..b7d0e2dc 100644 --- a/examples/data/map.av +++ b/examples/data/map.av @@ -6,7 +6,7 @@ module MapExample fn emptyCounts() -> Map ? "Creates an empty word-count map." - Map.empty() + {} fn incCount(counts: Map, word: String) -> Map ? "Increments the counter for one word." @@ -16,16 +16,16 @@ fn incCount(counts: Map, word: String) -> Map Option.None -> Map.set(counts, word, 1) verify incCount - incCount(Map.empty(), "a") => {"a" => 1} + incCount({}, "a") => {"a" => 1} incCount({"a" => 1}, "a") => {"a" => 2} verify incCount law keyPresent - given counts: Map = [Map.empty(), {"a" => 1}, {"x" => 2, "y" => 4}] + given counts: Map = [{}, {"a" => 1}, {"x" => 2, "y" => 4}] given word: String = ["a", "z", "new"] Map.has(incCount(counts, word), word) => true verify incCount law trackedCountStepsByOne - given counts: Map = [Map.empty(), {"a" => 1}, {"a" => 5, "x" => 2}] + given counts: Map = [{}, {"a" => 1}, {"a" => 5, "x" => 2}] given word: String = ["a", "new"] Option.withDefault(Map.get(incCount(counts, word), word), 0) => Option.withDefault(Map.get(counts, word), 0) + 1 @@ -36,7 +36,7 @@ fn countWords(words: List) -> Map [word, ..rest] -> incCount(countWords(rest), word) verify countWords - countWords([]) => Map.empty() + countWords([]) => {} countWords(["a", "b", "a"]) => {"a" => 2, "b" => 1} verify countWords law presenceMatchesContains diff --git a/examples/formal/law_auto.av b/examples/formal/law_auto.av index c99b076a..3bfd2b1b 100644 --- a/examples/formal/law_auto.av +++ b/examples/formal/law_auto.av @@ -84,7 +84,7 @@ fn incCount(counts: Map, word: String) -> Map Option.None -> Map.set(counts, word, 1) verify incCount law keyPresent - given counts: Map = [Map.empty(), {"a" => 1}, {"x" => 2, "y" => 4}] + given counts: Map = [{}, {"a" => 1}, {"x" => 2, "y" => 4}] given word: String = ["a", "z", "new"] Map.has(incCount(counts, word), word) => true diff --git a/src/codegen/builtins.rs b/src/codegen/builtins.rs index db1db69a..b3636a1b 100644 --- a/src/codegen/builtins.rs +++ b/src/codegen/builtins.rs @@ -102,7 +102,6 @@ pub(crate) enum Builtin { VectorToList, // --- Map --- - MapEmpty, MapGet, MapSet, MapHas, @@ -217,7 +216,6 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { "Vector.toList" => Builtin::VectorToList, // Map - "Map.empty" => Builtin::MapEmpty, "Map.get" => Builtin::MapGet, "Map.set" => Builtin::MapSet, "Map.has" => Builtin::MapHas, diff --git a/src/codegen/dafny/expr.rs b/src/codegen/dafny/expr.rs index 752b434e..c125c31b 100644 --- a/src/codegen/dafny/expr.rs +++ b/src/codegen/dafny/expr.rs @@ -468,7 +468,6 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str VectorToList => a[0].clone(), // Map - MapEmpty => "map[]".to_string(), MapGet => format!("MapGet({}, {})", a[0], a[1]), MapSet => format!("{}[{} := {}]", a[0], a[1], a[2]), MapHas => format!("({} in {})", a[1], a[0]), diff --git a/src/codegen/lean/builtins.rs b/src/codegen/lean/builtins.rs index 251b545a..e0d83719 100644 --- a/src/codegen/lean/builtins.rs +++ b/src/codegen/lean/builtins.rs @@ -135,7 +135,6 @@ pub fn emit_builtin_call( VectorToList => format!("{}.toList", p(&a[0])), // ---- Map ---- - MapEmpty => "AverMap.empty".to_string(), MapGet => format!("AverMap.get {} {}", p(&a[0]), p(&a[1])), MapSet => format!("AverMap.set {} {} {}", p(&a[0]), p(&a[1]), p(&a[2])), MapHas => format!("AverMap.has {} {}", p(&a[0]), p(&a[1])), diff --git a/src/codegen/lean/expr.rs b/src/codegen/lean/expr.rs index b683fa9b..4d90f760 100644 --- a/src/codegen/lean/expr.rs +++ b/src/codegen/lean/expr.rs @@ -405,14 +405,8 @@ fn extract_bool_arms(arms: &[MatchArm]) -> Option<(&Spanned, &Spanned String { match stmt { Stmt::Binding(name, type_ann, expr) => { - let mut val = emit_expr(expr, ctx); - // Map binding initialized with Map.empty → set empty - if let Some(ann) = type_ann - && crate::codegen::common::is_set_annotation(ann) - && val == "AverMap.empty" - { - val = "AverSet.empty".to_string(); - } + let val = emit_expr(expr, ctx); + let _ = type_ann; format!("let {} := {}", aver_name_to_lean(name), val) } Stmt::Expr(expr) => emit_expr(expr, ctx), diff --git a/src/codegen/rust/builtins.rs b/src/codegen/rust/builtins.rs index 7c31d5b4..4ad93ff3 100644 --- a/src/codegen/rust/builtins.rs +++ b/src/codegen/rust/builtins.rs @@ -662,7 +662,6 @@ fn emit_builtin_call_inner( )) } // ---- Map ---- - "Map.empty" => Some("HashMap::new()".to_string()), "Map.fromList" => { let list = clone_arg(&args[0], ctx, ectx); Some(format!( diff --git a/src/codegen/wasm/expr/builtins.rs b/src/codegen/wasm/expr/builtins.rs index b07fa00a..1270b932 100644 --- a/src/codegen/wasm/expr/builtins.rs +++ b/src/codegen/wasm/expr/builtins.rs @@ -127,10 +127,6 @@ impl<'a> ExprEmitter<'a> { self.instructions .push(Instruction::Call(self.rt.f64_to_str_obj)); } - "Map.empty" if args.is_empty() => { - // Empty map = empty association list = null ptr - self.instructions.push(Instruction::I32Const(0)); - } "Map.get" if args.len() == 2 => { // args on stack: [map(i32), key(?)] // ABI: rt_map_get(map: i32, key: i64, kind: i32) -> i32 diff --git a/src/codegen/wasm_gc/body/builtins.rs b/src/codegen/wasm_gc/body/builtins.rs index 9bea0b4a..ecba77b3 100644 --- a/src/codegen/wasm_gc/body/builtins.rs +++ b/src/codegen/wasm_gc/body/builtins.rs @@ -387,9 +387,9 @@ pub(super) fn emit_dotted_builtin( emit_option_to_result(func, &args[0], &args[1], slots, ctx) } // Map — dispatch to the per-instantiation helper. The - // canonical comes from inferring the type of the map argument - // (or the surrounding context for Map.empty). - "Map.empty" => emit_map_empty_call(func, args, slots, ctx), + // canonical comes from inferring the type of the map argument. + // Empty map literals (`{}`) flow through `emit_map_literal` + // — there is no `Map.empty()` builtin. "Map.set" | "Map.get" | "Map.len" | "Map.has" | "Map.keys" | "Map.values" | "Map.remove" | "Map.entries" => emit_map_kv_call(func, method, args, slots, ctx), "Map.fromList" if args.len() == 1 => emit_map_from_list_call(func, &args[0], slots, ctx), @@ -444,11 +444,6 @@ pub(super) fn emit_dotted_builtin( } } -/// `Map.empty()` → `call $map_empty_KV`. With a single registered -/// instantiation the canonical is unambiguous; with several, the type -/// must be deducible from the surrounding context (which today only -/// works when one instantiation exists — generalising would mean -/// threading expected-type through expression emission). /// Inline lowering of `Args.get()` (no args, returns `List`). /// Host imports are `args_len(): i64` and `args_get(i: i64): String`; /// no `args_get_all`. Walks `i = len-1 .. 0` cons-building the list so @@ -537,44 +532,6 @@ pub(super) fn emit_args_get_inline( Ok(()) } -pub(super) fn emit_map_empty_call( - func: &mut Function, - args: &[Spanned], - _slots: &SlotTable, - ctx: &EmitCtx<'_>, -) -> Result<(), WasmGcError> { - if !args.is_empty() { - return Err(WasmGcError::Validation(format!( - "Map.empty expects 0 args, got {}", - args.len() - ))); - } - let canonical = if ctx.registry.map_order.len() == 1 { - ctx.registry.map_order[0].clone() - } else { - // Multi-Map module — disambiguate by checking the enclosing - // fn's return type. Common case: `fn build() -> Map` - // wraps a `Map.set(Map.empty(), ...)` chain; the empty call - // inherits its slot from the declared return type. - let ret_canonical = super::super::types::normalize_compound(ctx.return_type); - if ctx.registry.map_slots(&ret_canonical).is_some() { - ret_canonical - } else { - return Err(WasmGcError::Unimplemented( - "Map.empty across multiple Map instantiations needs context-driven type inference", - )); - } - }; - let helpers = ctx - .fn_map - .map_helpers_lookup(&canonical) - .ok_or(WasmGcError::Validation(format!( - "Map.empty: helpers missing for `{canonical}`" - )))?; - func.instruction(&Instruction::Call(helpers.empty)); - Ok(()) -} - /// Map.set / Map.get / Map.len dispatch — the canonical is recovered /// from the map argument's inferred type, helper indices come from /// `fn_map.map_helpers`. diff --git a/src/types/checker/builtins.rs b/src/types/checker/builtins.rs index 0a869886..d8edb34f 100644 --- a/src/types/checker/builtins.rs +++ b/src/types/checker/builtins.rs @@ -555,7 +555,6 @@ impl TypeChecker { let v_var = || Type::Var("V".to_string()); let map_kv = || Type::Map(Box::new(k_var()), Box::new(v_var())); let map_sigs: &[(&str, &[Type], Type, &[&str])] = &[ - ("Map.empty", &[], map_kv(), &[]), ("Map.set", &[map_kv(), k_var(), v_var()], map_kv(), &[]), ( "Map.get", diff --git a/src/types/checker/infer/expr.rs b/src/types/checker/infer/expr.rs index 1ddafd4c..6e904089 100644 --- a/src/types/checker/infer/expr.rs +++ b/src/types/checker/infer/expr.rs @@ -201,9 +201,6 @@ impl TypeChecker { Expr::FnCall(callee, args) => { let key = Self::callee_key(&callee.node)?; match (key.as_str(), args.len(), expected) { - // Map.empty() — no args, expected must be Map. - ("Map.empty", 0, Type::Map(_, _)) => Some(expected.clone()), - // Map.fromList(xs) — expected Map gives xs the // concrete List<(K, V)> element type. ("Map.fromList", 1, Type::Map(k, v)) => { diff --git a/src/types/checker/infer/map_calls.rs b/src/types/checker/infer/map_calls.rs index 74e8a0e9..44e320b1 100644 --- a/src/types/checker/infer/map_calls.rs +++ b/src/types/checker/infer/map_calls.rs @@ -10,8 +10,6 @@ impl TypeChecker { let option_ty = |v: Type| Type::Option(Box::new(v)); let list_ty = |v: Type| Type::List(Box::new(v)); let tuple2 = |k: Type, v: Type| Type::Tuple(vec![k, v]); - let k_var = || Type::Var("K".to_string()); - let v_var = || Type::Var("V".to_string()); let is_hashable_key_type = |ty: &Type| { // The Map runtime hashes any heap value through rt_deep_hash, // so user-defined types (variants/records/tuples/lists) are @@ -58,12 +56,6 @@ impl TypeChecker { }; match name { - "Map.empty" => { - if let Err(fallback) = expect_arity(self, 0, map_ty(Type::Invalid, Type::Invalid)) { - return Some(fallback); - } - Some(map_ty(k_var(), v_var())) - } "Map.len" => { if let Err(fallback) = expect_arity(self, 1, Type::Int) { return Some(fallback); diff --git a/src/types/checker/tests.rs b/src/types/checker/tests.rs index 43e41770..4b9ec421 100644 --- a/src/types/checker/tests.rs +++ b/src/types/checker/tests.rs @@ -309,7 +309,7 @@ record AppCtx config: String fn handler(ctx: AppCtx, req: HttpRequest) -> HttpResponse - HttpResponse(status = 200, body = "ok", headers = Map.empty()) + HttpResponse(status = 200, body = "ok", headers = {}) fn main() -> Unit ! [HttpServer.listenWith] @@ -333,7 +333,7 @@ record AppCtx config: String fn handler(ctx: AppCtx, req: HttpRequest) -> HttpResponse - HttpResponse(status = 200, body = ctx.config, headers = Map.empty()) + HttpResponse(status = 200, body = ctx.config, headers = {}) fn main(ctx: AppCtx) -> Unit ! [HttpServer.listenWith] diff --git a/src/types/map.rs b/src/types/map.rs index 215748ce..72e3aede 100644 --- a/src/types/map.rs +++ b/src/types/map.rs @@ -1,7 +1,6 @@ /// Map namespace — immutable key/value map helpers. /// /// Methods: -/// Map.empty() → Map /// Map.set(map, key, value) → Map /// Map.get(map, key) → Option /// Map.remove(map, key) → Map @@ -12,6 +11,9 @@ /// Map.len(map) → Int /// Map.fromList(pairs) → Map where each pair is (key, value) /// +/// The empty map is the literal `{}` (with type from context); there is +/// no `Map.empty()` builtin since 0.17 — symmetric with `[]` for List. +/// /// Key constraint: only scalar keys are allowed (Int, Float, String, Bool). /// /// No effects required. @@ -25,7 +27,7 @@ use crate::value::{RuntimeError, Value, aver_repr, list_from_vec, list_view}; pub fn register(global: &mut HashMap) { let mut members = HashMap::new(); for method in &[ - "empty", "set", "get", "remove", "has", "keys", "values", "entries", "len", "fromList", + "set", "get", "remove", "has", "keys", "values", "entries", "len", "fromList", ] { members.insert( method.to_string(), @@ -48,7 +50,6 @@ pub fn effects(_name: &str) -> &'static [&'static str] { /// Returns `Some(result)` when `name` is owned by this namespace, `None` otherwise. pub fn call(name: &str, args: &[Value]) -> Option> { match name { - "Map.empty" => Some(empty(args)), "Map.set" => Some(set(args)), "Map.get" => Some(get(args)), "Map.remove" => Some(remove(args)), @@ -62,16 +63,6 @@ pub fn call(name: &str, args: &[Value]) -> Option> { } } -fn empty(args: &[Value]) -> Result { - if !args.is_empty() { - return Err(RuntimeError::Error(format!( - "Map.empty() takes 0 arguments, got {}", - args.len() - ))); - } - Ok(Value::Map(HashMap::new())) -} - fn set(args: &[Value]) -> Result { let [map_val, key, value] = three_args("Map.set", args)?; let Value::Map(map) = map_val else { @@ -294,7 +285,6 @@ pub fn call_nv( arena: &mut Arena, ) -> Option> { match name { - "Map.empty" => Some(empty_nv(args, arena)), "Map.set" => Some(set_nv(args, arena)), "Map.get" => Some(get_nv(args, arena)), "Map.remove" => Some(remove_nv(args, arena)), @@ -331,17 +321,6 @@ fn nv_key_bits(v: NanValue, arena: &Arena) -> u64 { v.map_key_hash_deep(arena) } -fn empty_nv(args: &[NanValue], arena: &mut Arena) -> Result { - if !args.is_empty() { - return Err(RuntimeError::Error(format!( - "Map.empty() takes 0 arguments, got {}", - args.len() - ))); - } - let _ = arena; - Ok(NanValue::EMPTY_MAP) -} - /// Map.set with sole-owned first argument — takes instead of cloning. pub fn set_nv_owned(args: &[NanValue], arena: &mut Arena) -> Result { if args.len() != 3 { diff --git a/src/vm/builtin.rs b/src/vm/builtin.rs index a0a35122..5d178659 100644 --- a/src/vm/builtin.rs +++ b/src/vm/builtin.rs @@ -146,7 +146,6 @@ vm_builtins! { ListContains => "List.contains", ListZip => "List.zip", - MapEmpty => "Map.empty", MapSet => "Map.set", MapGet => "Map.get", MapRemove => "Map.remove", @@ -224,7 +223,6 @@ impl VmBuiltin { | Self::StringEndsWith | Self::StringContains | Self::ListContains - | Self::MapEmpty | Self::MapLen | Self::MapHas | Self::VectorLen @@ -472,8 +470,7 @@ impl VmBuiltin { | Self::ListContains | Self::ListZip => list::call_nv(self.name(), args, arena), - Self::MapEmpty - | Self::MapSet + Self::MapSet | Self::MapGet | Self::MapRemove | Self::MapHas diff --git a/tools/website/llms.txt b/tools/website/llms.txt index 0ead6c76..e002ddf2 100644 --- a/tools/website/llms.txt +++ b/tools/website/llms.txt @@ -321,7 +321,7 @@ Key `Vector` API (O(1) indexed access): - `Vector.new(n, default)`, `Vector.get(v, i) -> Option`, `Vector.set(v, i, val) -> Option>` Key `Map` API: -- `Map.empty()`, `Map.fromList(pairs)`, `Map.get(m, k) -> Option`, `Map.set(m, k, v)`, `Map.has(m, k)`, `Map.remove(m, k)`, `Map.keys(m)`, `Map.len(m)` +- `{}`, `Map.fromList(pairs)`, `Map.get(m, k) -> Option`, `Map.set(m, k, v)`, `Map.has(m, k)`, `Map.remove(m, k)`, `Map.keys(m)`, `Map.len(m)` Effectful namespaces: - `Console`: print, error, warn, readLine — **`print`/`error`/`warn` take `String`**, not arbitrary values. Stringify at the call site: interpolation `"{x}"` for primitives, a per-type render fn (`fn show(r: Result) -> String`) for compound shapes. From 9066b09ff76f30ffb86b90792086f0be1510536d Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 20:27:22 +0200 Subject: [PATCH 16/18] 0.17 sweep [2/4]: drop unreachable builtin aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Int.parse`, `Float.parse`, `Int.rem` were registered in 4 backend dispatch tables (codegen/builtins enum + name table, lean, dafny, rust) but never reachable from Aver source — typecheck didn't know them, VM didn't dispatch them. Lean and dafny treated them as outright aliases for `Int.fromString` / `Float.fromString` / `Int.mod` (`IntFromString | IntParse =>`, `IntRem | IntMod =>`). Half-cooked: enum variants existed without the language ever exposing them. Drop the 3 surface names and their `Builtin::Int{Parse,Rem}` / `Builtin::FloatParse` enum variants from every backend they leaked into. No migration needed — they were unreachable. 621/621 lib tests, build green. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/builtins.rs | 6 ------ src/codegen/dafny/expr.rs | 6 +++--- src/codegen/lean/builtins.rs | 5 ++--- src/codegen/rust/builtins.rs | 15 --------------- src/codegen/rust/expr.rs | 1 - 5 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/codegen/builtins.rs b/src/codegen/builtins.rs index b3636a1b..eb872df7 100644 --- a/src/codegen/builtins.rs +++ b/src/codegen/builtins.rs @@ -20,10 +20,8 @@ pub(crate) enum Builtin { IntToFloat, IntToString, IntFromString, - IntParse, IntMin, IntMax, - IntRem, IntMod, // --- Float --- @@ -36,7 +34,6 @@ pub(crate) enum Builtin { FloatToInt, FloatToString, FloatFromString, - FloatParse, FloatPi, FloatMin, FloatMax, @@ -134,10 +131,8 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { "Int.toFloat" | "Float.fromInt" => Builtin::IntToFloat, "Int.toString" => Builtin::IntToString, "Int.fromString" => Builtin::IntFromString, - "Int.parse" => Builtin::IntParse, "Int.min" => Builtin::IntMin, "Int.max" => Builtin::IntMax, - "Int.rem" => Builtin::IntRem, "Int.mod" => Builtin::IntMod, // Float @@ -150,7 +145,6 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { "Float.toInt" => Builtin::FloatToInt, "Float.toString" => Builtin::FloatToString, "Float.fromString" => Builtin::FloatFromString, - "Float.parse" => Builtin::FloatParse, "Float.pi" => Builtin::FloatPi, "Float.min" => Builtin::FloatMin, "Float.max" => Builtin::FloatMax, diff --git a/src/codegen/dafny/expr.rs b/src/codegen/dafny/expr.rs index c125c31b..04c0cd1a 100644 --- a/src/codegen/dafny/expr.rs +++ b/src/codegen/dafny/expr.rs @@ -379,10 +379,10 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str IntAbs => format!("(if {} >= 0 then {} else -{})", a[0], a[0], a[0]), IntToFloat => format!("({} as real)", a[0]), IntToString | StringFromInt => format!("IntToString({})", a[0]), - IntFromString | IntParse => format!("IntFromString({})", a[0]), + IntFromString => format!("IntFromString({})", a[0]), IntMin => format!("(if {} <= {} then {} else {})", a[0], a[1], a[0], a[1]), IntMax => format!("(if {} >= {} then {} else {})", a[0], a[1], a[0], a[1]), - IntRem | IntMod => format!("Result.Ok(({} % {}))", a[0], a[1]), + IntMod => format!("Result.Ok(({} % {}))", a[0], a[1]), // Float FloatAbs => format!("(if {} >= 0.0 then {} else -{})", a[0], a[0], a[0]), @@ -390,7 +390,7 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str FloatPow => format!("FloatPow({}, {})", a[0], a[1]), FloatRound | FloatFloor | FloatCeil | FloatToInt => format!("FloatToInt({})", a[0]), FloatToString | StringFromFloat => format!("FloatToString({})", a[0]), - FloatFromString | FloatParse => format!("FloatFromString({})", a[0]), + FloatFromString => format!("FloatFromString({})", a[0]), FloatPi => "FloatPi()".to_string(), FloatMin => format!("(if {} <= {} then {} else {})", a[0], a[1], a[0], a[1]), FloatMax => format!("(if {} >= {} then {} else {})", a[0], a[1], a[0], a[1]), diff --git a/src/codegen/lean/builtins.rs b/src/codegen/lean/builtins.rs index e0d83719..31254946 100644 --- a/src/codegen/lean/builtins.rs +++ b/src/codegen/lean/builtins.rs @@ -46,15 +46,14 @@ pub fn emit_builtin_call( IntToString => format!("toString {}", p(&a[0])), IntMin => format!("min {} {}", p(&a[0]), p(&a[1])), IntMax => format!("max {} {}", p(&a[0]), p(&a[1])), - IntRem | IntMod => format!("(Except.ok ({} % {}) : Except String Int)", a[0], a[1]), + IntMod => format!("(Except.ok ({} % {}) : Except String Int)", a[0], a[1]), IntFromString => format!("Int.fromString {}", p(&a[0])), - IntParse => format!("Int.fromString {}", p(&a[0])), // ---- Float ---- FloatAbs => format!("Float.abs {}", p(&a[0])), FloatSqrt => format!("Float.sqrt {}", p(&a[0])), FloatToString => format!("toString {}", p(&a[0])), - FloatFromString | FloatParse => format!("Float.fromString {}", p(&a[0])), + FloatFromString => format!("Float.fromString {}", p(&a[0])), FloatPow => format!("AverFloat.pow {} {}", p(&a[0]), p(&a[1])), FloatRound => format!("AverFloat.round {}", p(&a[0])), FloatFloor => format!("AverFloat.floor {}", p(&a[0])), diff --git a/src/codegen/rust/builtins.rs b/src/codegen/rust/builtins.rs index 4ad93ff3..2ac66182 100644 --- a/src/codegen/rust/builtins.rs +++ b/src/codegen/rust/builtins.rs @@ -14,8 +14,6 @@ fn builtin_needs_str_conversion(name: &str) -> bool { | "Time.now" | "Int.toString" | "Float.toString" - | "Int.parse" - | "Float.parse" | "Int.fromString" | "Float.fromString" | "String.slice" @@ -418,10 +416,6 @@ fn emit_builtin_call_inner( let arg = emit_arg(0); Some(format!("{}.to_string()", arg)) } - "Int.parse" => { - let arg = emit_arg(0); - Some(format!("{}.parse::().map_err(|e| e.to_string())", arg)) - } "Int.fromString" => { let arg = emit_arg(0); Some(format!("{}.parse::().map_err(|e| e.to_string())", arg)) @@ -436,11 +430,6 @@ fn emit_builtin_call_inner( let b = emit_arg(1); Some(format!("{}.max({})", a, b)) } - "Int.rem" => { - let a = emit_arg(0); - let b = emit_arg(1); - Some(format!("({} % {})", a, b)) - } "Int.mod" => { let a = emit_arg(0); let b = emit_arg(1); @@ -478,10 +467,6 @@ fn emit_builtin_call_inner( let arg = emit_arg(0); Some(format!("{}.to_string()", arg)) } - "Float.parse" => { - let arg = emit_arg(0); - Some(format!("{}.parse::().map_err(|e| e.to_string())", arg)) - } "Float.sqrt" => { let arg = emit_arg(0); Some(format!("{}.sqrt()", arg)) diff --git a/src/codegen/rust/expr.rs b/src/codegen/rust/expr.rs index 783e1b01..566e2017 100644 --- a/src/codegen/rust/expr.rs +++ b/src/codegen/rust/expr.rs @@ -1074,7 +1074,6 @@ fn expr_is_numeric(expr: &Expr, ectx: &EmitCtx) -> bool { "Int.abs" | "Int.min" | "Int.max" - | "Int.rem" | "Float.abs" | "Float.floor" | "Float.ceil" From 7ef288bf23effbb21ca28c19036d740076de37fc Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 21:06:04 +0200 Subject: [PATCH 17/18] 0.17.0 "Purge" [3+4/4]: stdlib convention + version bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops six redundant builtins, renames one for symmetry, bumps to 0.17.0. The convention `Target.fromSource` for type conversions wins; operators (`+` on strings, arithmetic, comparisons) and literals (`{}`, `[]`) cover composition; named functions only when they add semantics that can't be expressed otherwise. Removed: - `Int.toString` → `String.fromInt(n)` or `"{n}"` interpolation - `Float.toString` → `String.fromFloat(f)` or `"{f}"` interpolation - `Float.toInt` → `Int.fromFloat(f)` - `Int.toFloat` → `Float.fromInt(n)` - `String.concat(a, b)` → `a + b` Renamed: - `Vector.toList(v)` → `List.fromVector(v)` (paired with the existing `Vector.fromList(l)` under the same convention) Migration: - examples / docs / lib tests / website playground sources sed-migrated in one batch; the `Float.fromInt` / `Int.fromFloat` confusion in docs/language.md (sed double-replacement artifact) hand-fixed. - VM body fns + dispatch tables in `types/{int,float,vector}.rs` purged of the dropped names; `String.fromX` body fns in `types/string.rs` unchanged (they're the canonical replacements). - Builtin enum variants in `codegen/builtins.rs` dropped/renamed: `IntToString` / `FloatToString` / `IntToFloat` / `FloatToInt` / `StringConcat` / `VectorToList` → `IntFromFloat` / `FloatFromInt` / `ListFromVector` (or removed for the duplicates). - Backend dispatchers (rust, wasm legacy, wasm-gc, lean, dafny) re-pointed to new names; lean/dafny opaque function declarations (`IntToString`, `FloatToString`, `FloatToInt` as Lean/Dafny names) kept — they're external-language conventions, not Aver surface. - `wasm_gc::BuiltinName::IntToString` / `FloatToString` enum variants kept as internal Rust labels (now mapped from `String.fromInt` / `String.fromFloat`); renaming the labels was unnecessary churn. - Self-host `aver_generated/` left as-is (will regenerate on next self-host bootstrap; stale arms are dead since typecheck rejects the old names). Vera-bench solutions/aver: zero usage of any removed/renamed builtin (checked again before bumping). No companion PR needed. Cargo.toml: 0.16.2 → 0.17.0; aver-lsp's `aver-lang` path-dep version bumped in lockstep. 621/621 lib tests, examples typecheck, runtime VM == wasm-gc on data/map.av and friends. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 40 +++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- aver-lsp/Cargo.toml | 2 +- docs/language.md | 2 +- docs/services.md | 8 +-- examples/data/date.av | 12 ++-- examples/data/fibonacci.av | 2 +- examples/games/checkers/main.av | 16 ++--- examples/games/checkers/render.av | 6 +- examples/games/doom/enemy.av | 8 +-- examples/games/doom/level.av | 2 +- examples/games/doom/main.av | 6 +- examples/games/doom/math.av | 2 +- examples/games/doom/render.av | 22 +++--- examples/games/doom/rng.av | 2 +- examples/games/life.av | 10 +-- examples/games/rogue/main.av | 22 +++--- examples/games/snake.av | 6 +- examples/games/tetris/main.av | 8 +-- examples/games/wumpus.av | 8 +-- examples/services/redis.av | 4 +- src/checker/perf.rs | 28 ++++---- src/codegen/builtins.rs | 18 ++--- src/codegen/dafny/expr.rs | 12 ++-- src/codegen/lean/builtins.rs | 9 +-- src/codegen/rust/builtins.rs | 20 +----- src/codegen/rust/expr.rs | 2 +- src/codegen/wasm/alloc_policy.rs | 4 +- src/codegen/wasm/expr/builtins.rs | 10 +-- src/codegen/wasm_gc/body/builtins.rs | 12 ++-- src/codegen/wasm_gc/builtins/mod.rs | 7 +- src/codegen/wasm_gc/types.rs | 5 +- src/ir/alloc_info.rs | 8 +-- src/ir/analyze.rs | 2 +- src/types/checker/builtins.rs | 5 +- src/types/checker/infer/vector_calls.rs | 2 +- src/types/float.rs | 26 ------- src/types/int.rs | 71 ++----------------- src/types/list.rs | 20 +++++- src/types/vector.rs | 18 ++--- src/vm/alloc_policy.rs | 2 +- src/vm/builtin.rs | 13 +--- tools/edge/bench.av | 2 +- tools/edge/fractal.av | 42 +++++------ tools/website/llms.txt | 8 +-- .../sources/examples/games/checkers/main.av | 16 ++--- .../sources/examples/games/checkers/render.av | 6 +- .../sources/examples/games/doom/enemy.av | 8 +-- .../sources/examples/games/doom/level.av | 2 +- .../sources/examples/games/doom/main.av | 6 +- .../sources/examples/games/doom/math.av | 2 +- .../sources/examples/games/doom/render.av | 22 +++--- .../sources/examples/games/doom/rng.av | 2 +- .../playground/sources/examples/games/life.av | 10 +-- .../sources/examples/games/rogue/main.av | 22 +++--- .../sources/examples/games/snake.av | 6 +- .../sources/examples/games/tetris/main.av | 8 +-- .../sources/examples/games/wumpus.av | 8 +-- .../playground/sources/examples/shapes.av | 4 +- 60 files changed, 297 insertions(+), 363 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 297bcd91..43333ce2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,46 @@ All notable changes to Aver are documented here. Starting with 0.10.0, minor rel ## Unreleased +## 0.17.0 "Purge" — 2026-05-06 + +> _What was never used is no longer in the way._ + +A stdlib sweep before the language gets harder to break. Eight redundant builtins removed, one renamed for symmetry, one rule for what stays: each operation has one obvious spelling. Literals for literals (`{}`, `[]`), operators for primitive composition (`+` on strings, arithmetic), `Target.fromSource` for conversions, interpolation for rendering, named functions only when they add semantics. + +### Removed + +- **`Map.empty()`** — redundant against the `{}` literal. Bidirectional inference propagates `Map` from binding annotations into `{}`, and the discovery walker introduced in 0.16.3 picks up unannotated literals like `m = {a => 3}` from `expr.ty()`. Symmetric with `[]` for List (which never had a `List.empty` builtin). +- **`Int.parse`, `Float.parse`, `Int.rem`** — unreachable builtin aliases. Registered in 4 backend dispatch tables but never wired through typecheck or VM. Lean and dafny treated them as outright aliases for `Int.fromString` / `Float.fromString` / `Int.mod`. +- **`Int.toString`, `Float.toString`** — duplicates of `String.fromInt` / `String.fromFloat`. The convention `Target.fromSource` wins: result type leads (matters for code review and AI-generated code), and it scales (`Int.fromFloat`, `Float.fromInt`, `String.fromBool`, …). Interpolation `"{x}"` remains the primary idiom for rendering values into human-readable strings; `String.fromX` is for explicit data conversion (e.g. `"user:" + String.fromInt(id)`). +- **`Float.toInt`, `Int.toFloat`** — duplicates of `Int.fromFloat` / `Float.fromInt`. Same convention. +- **`String.concat(a, b)`** — literally `a + b`. Zero usage in examples (only in docs); not even dispatched in the wasm-gc backend. The `+` operator is the primary idiom for tail-recursive accumulators (`acc + sep + String.fromInt(head)`) and inline string composition; `String.join(parts, sep)` covers list-join (a different operation). + +### Renamed + +- **`Vector.toList(v)` → `List.fromVector(v)`** — paired with `Vector.fromList(l)` under the same `Target.fromSource` rule. + +### Kept (deliberately) + +- **`+` on strings** — primary idiom for tail-recursive string accumulators and inline composition. Forcing only interpolation `"{a}{b}"` would be awkward in `acc + " " + String.fromInt(head)` patterns. +- **`Bool.and` / `Bool.or` / `Bool.not`** — *intentionally not added* as `&&` / `||` / `!` operators. Aver tracks effects deterministically (record/replay/proof export); short-circuit on `a && f!()` would require deciding whether the effect of `f` may or may not fire depending on `a`, which complicates trace recording and Lean export. Strict-FP eager evaluation matches the rest of the language. Pattern-matching `(x > 0, y > 0)` covers most idiomatic AND/OR cases. Not a "later language decision" — a settled non-feature. +- **`Char.toCode` / `Char.fromCode`, `Byte.toHex` / `Byte.fromHex`, `Option.toResult`, `String.toLower` / `String.toUpper`** — encoding semantics or semantic conversions, not type-to-type. Not duplicates. + +### Migration + +Programs using removed builtins: + +```aver +-- before +m = Map.empty() -- → {} +s = Int.toString(42) -- → String.fromInt(42) or "{42}" +n = Float.toInt(3.7) -- → Int.fromFloat(3.7) +f = Int.toFloat(5) -- → Float.fromInt(5) +xs = Vector.toList(v) -- → List.fromVector(v) +greeting = String.concat("hi, ", name) -- → "hi, " + name +``` + +Examples in `examples/` and `tools/website/` migrated; vera-bench solutions checked (zero usage of any removed builtin, no companion PR needed). All 621 lib tests pass. The full sweep landed in four self-contained commits on the 0.17 branch. + ## 0.16.2 — 2026-05-06 > _Record/replay correctness across all three backends — and a tidier wasm-gc imports tree along the way._ diff --git a/Cargo.lock b/Cargo.lock index 07fe9d91..e47181d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,7 +119,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aver-lang" -version = "0.16.2" +version = "0.17.0" dependencies = [ "aver-memory", "aver-rt", diff --git a/Cargo.toml b/Cargo.toml index 53c6dc96..911fba17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["tools/wasm-runner"] [package] name = "aver-lang" -version = "0.16.2" +version = "0.17.0" edition = "2024" default-run = "aver" description = "VM and transpiler for Aver, a statically-typed language designed for AI-assisted development" diff --git a/aver-lsp/Cargo.toml b/aver-lsp/Cargo.toml index 11f928f8..fdb0335f 100644 --- a/aver-lsp/Cargo.toml +++ b/aver-lsp/Cargo.toml @@ -16,7 +16,7 @@ name = "aver-lsp" path = "src/main.rs" [dependencies] -aver = { path = "..", version = "=0.16.2", package = "aver-lang" } +aver = { path = "..", version = "=0.17.0", package = "aver-lang" } tower-lsp-server = "0.23" tokio = { version = "1", features = ["full"] } serde_json = "1" diff --git a/docs/language.md b/docs/language.md index a3b7e7e1..f8f0049b 100644 --- a/docs/language.md +++ b/docs/language.md @@ -35,7 +35,7 @@ Duplicate binding of the same name in the same scope is a type error. ## Operators -Arithmetic: `+`, `-`, `*`, `/` — operands must match (`Int+Int`, `Float+Float`, `String+String`). No implicit promotion; use `Int.toFloat` / `Float.fromInt` to convert. +Arithmetic: `+`, `-`, `*`, `/` — operands must match (`Int+Int`, `Float+Float`, `String+String`). No implicit promotion; use `Float.fromInt` / `Int.fromFloat` to convert. Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=`. Error propagation: `expr?` — unwraps `Result.Ok`, propagates `Result.Err` as a `RuntimeError`. Independent products: `(a, b)!` — product of independent computations. `(a, b)?!` — same, with Result unwrapping (all must succeed or first error propagates). Elements cannot reference each other; independence is structural. Composes recursively for fan-out parallelism. See [independence.md](independence.md). diff --git a/docs/services.md b/docs/services.md index 76914afd..f2fee056 100644 --- a/docs/services.md +++ b/docs/services.md @@ -44,7 +44,7 @@ Vector is a persistent indexed sequence — use it for grids, buffers, lookup ta | `Vector.set` | `(Vector, Int, T) -> Option>` | O(1) COW update; `None` if out of bounds | | `Vector.len` | `Vector -> Int` | | | `Vector.fromList` | `List -> Vector` | Convert list to vector | -| `Vector.toList` | `Vector -> List` | Convert vector to list | +| `List.fromVector` | `Vector -> List` | Convert vector to list | ### `Result` namespace @@ -75,8 +75,8 @@ Source: `src/types/int.rs` |---|---| | `Int.fromString` | `String -> Result` | | `Int.fromFloat` | `Float -> Int` | -| `Int.toString` | `Int -> String` | -| `Int.toFloat` | `Int -> Float` | +| `String.fromInt` | `Int -> String` | +| `Float.fromInt` | `Int -> Float` | | `Int.abs` | `Int -> Int` | | `Int.min` | `(Int, Int) -> Int` | | `Int.max` | `(Int, Int) -> Int` | @@ -90,7 +90,7 @@ Source: `src/types/float.rs` |---|---| | `Float.fromString` | `String -> Result` | | `Float.fromInt` | `Int -> Float` | -| `Float.toString` | `Float -> String` | +| `String.fromFloat` | `Float -> String` | | `Float.abs` | `Float -> Float` | | `Float.floor` | `Float -> Int` | | `Float.ceil` | `Float -> Int` | diff --git a/examples/data/date.av b/examples/data/date.av index 1cdbe1bd..b38066c8 100644 --- a/examples/data/date.av +++ b/examples/data/date.av @@ -44,8 +44,8 @@ verify parse fn padTwo(n: Int) -> String ? "Pad a single-digit number with a leading zero." match n < 10 - true -> "0{Int.toString(n)}" - false -> Int.toString(n) + true -> "0{String.fromInt(n)}" + false -> String.fromInt(n) verify padTwo padTwo(0) => "00" @@ -54,7 +54,7 @@ verify padTwo fn format(d: Date) -> String ? "Format a Date as YYYY-MM-DD HH:MM:SS." - y = Int.toString(d.year) + y = String.fromInt(d.year) m = padTwo(d.month) da = padTwo(d.day) h = padTwo(d.hour) @@ -82,6 +82,6 @@ fn main() d = parse(now) Console.print("Raw: {now}") Console.print("Parsed: {format(d)}") - Console.print("Year: {Int.toString(d.year)}") - Console.print("Month: {Int.toString(d.month)}") - Console.print("Day: {Int.toString(d.day)}") + Console.print("Year: {String.fromInt(d.year)}") + Console.print("Month: {String.fromInt(d.month)}") + Console.print("Day: {String.fromInt(d.day)}") diff --git a/examples/data/fibonacci.av b/examples/data/fibonacci.av index 8d90a62b..406508cb 100644 --- a/examples/data/fibonacci.av +++ b/examples/data/fibonacci.av @@ -234,7 +234,7 @@ fn goldenApprox(n: Int) -> Result ? "Approximates golden ratio as F(n+1)/F(n). Requires n >= 1." match n < 1 true -> Result.Err("Need n >= 1") - false -> Result.Ok(Int.toFloat(fib(n + 1)) / Int.toFloat(fib(n))) + false -> Result.Ok(Float.fromInt(fib(n + 1)) / Float.fromInt(fib(n))) verify goldenApprox goldenApprox(0) => Result.Err("Need n >= 1") diff --git a/examples/games/checkers/main.av b/examples/games/checkers/main.av index d36b5e0d..73794513 100644 --- a/examples/games/checkers/main.av +++ b/examples/games/checkers/main.av @@ -210,11 +210,11 @@ fn renderAiPanel(result: AiResult) -> Unit ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] Terminal.moveTo(22, 1) Terminal.setColor("cyan") - Terminal.print("AI depth={Int.toString(result.depth)} {Int.toString(result.thinkMs)}ms") + Terminal.print("AI depth={String.fromInt(result.depth)} {String.fromInt(result.thinkMs)}ms") Terminal.resetColor() Terminal.moveTo(22, 2) Terminal.setColor("yellow") - Terminal.print("root-parallel x{Int.toString(Ai.rootParallelWidth())} | {Int.toString(List.len(result.candidates))} moves") + Terminal.print("root-parallel x{String.fromInt(Ai.rootParallelWidth())} | {String.fromInt(List.len(result.candidates))} moves") Terminal.resetColor() renderCandidates(result.candidates, result.move, 0) @@ -242,7 +242,7 @@ fn renderChosenCandidate(sm: ScoredMove, i: Int) -> Unit Terminal.setColor("green") Terminal.print("★ ") Terminal.print(moveLabel(sm.move)) - Terminal.print(" {Int.toString(sm.score)}") + Terminal.print(" {String.fromInt(sm.score)}") Terminal.resetColor() fn renderOtherCandidate(sm: ScoredMove, i: Int) -> Unit @@ -250,7 +250,7 @@ fn renderOtherCandidate(sm: ScoredMove, i: Int) -> Unit ! [Terminal.print] Terminal.print(" ") Terminal.print(moveLabel(sm.move)) - Terminal.print(" {Int.toString(sm.score)}") + Terminal.print(" {String.fromInt(sm.score)}") fn isChosenMove(a: List, b: List) -> Bool ? "True if two moves are the same (compare first and last points)" @@ -295,7 +295,7 @@ fn moveLabel(move: List) -> String ? "Human-readable label for a move path" match firstPoint(move) Option.Some(from) -> match lastPoint(move) - Option.Some(to) -> "{colChar(from.x)}{Int.toString(8 - from.y)}→{colChar(to.x)}{Int.toString(8 - to.y)}" + Option.Some(to) -> "{colChar(from.x)}{String.fromInt(8 - from.y)}→{colChar(to.x)}{String.fromInt(8 - to.y)}" Option.None -> "?" Option.None -> "?" @@ -307,7 +307,7 @@ fn renderHelp(state: GameState) -> Unit ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] Terminal.moveTo(0, 14) Terminal.setColor("green") - Terminal.print(" You (black) vs AI (white) · depth {Int.toString(state.searchDepth)} · +/- to change") + Terminal.print(" You (black) vs AI (white) · depth {String.fromInt(state.searchDepth)} · +/- to change") Terminal.resetColor() Terminal.moveTo(0, 15) match state.selected @@ -415,7 +415,7 @@ fn aiPhase(state: GameState) -> Result ] renderFrame(state) Terminal.moveTo(0, 14) - Terminal.print("AI thinking (depth {Int.toString(state.searchDepth)}, root-parallel x{Int.toString(Ai.rootParallelWidth())})...") + Terminal.print("AI thinking (depth {String.fromInt(state.searchDepth)}, root-parallel x{String.fromInt(Ai.rootParallelWidth())})...") Terminal.flush() t0 = Time.unixMs() aiState = handleAiTurn(state) @@ -443,7 +443,7 @@ fn gameOverResult(state: GameState) -> Result Terminal.print("Game over!") Terminal.flush() Time.sleep(2000) - Result.Ok("Game over! Move {Int.toString(state.moveNum)}") + Result.Ok("Game over! Move {String.fromInt(state.moveNum)}") fn gameTurn(state: GameState) -> Result ? "Dispatch to player or AI" diff --git a/examples/games/checkers/render.av b/examples/games/checkers/render.av index 5e3e472f..f0145779 100644 --- a/examples/games/checkers/render.av +++ b/examples/games/checkers/render.av @@ -240,7 +240,7 @@ fn renderRowLabelsNext(r: Int) -> Unit fn rowLabel(r: Int) -> String ? "Row label: row 0 = 8, row 7 = 1" - Int.toString(8 - r) + String.fromInt(8 - r) verify rowLabel rowLabel(0) => "8" @@ -281,7 +281,7 @@ fn renderStatus(currentPlayer: Color, moveNum: Int) -> Unit Terminal.setColor(playerColor(currentPlayer)) Terminal.print("{colorName(currentPlayer)}") Terminal.resetColor() - Terminal.print(" to move | Move #{Int.toString(moveNum)}") + Terminal.print(" to move | Move #{String.fromInt(moveNum)}") fn renderAiTrace(move: List, score: Int, alternatives: Int, depth: Int) -> Unit ? "Show AI decision trace" @@ -290,4 +290,4 @@ fn renderAiTrace(move: List, score: Int, alternatives: Int, depth: Int) - Terminal.setColor("cyan") Terminal.print("AI: ") Terminal.resetColor() - Terminal.print("score={Int.toString(score)} alts={Int.toString(alternatives)} depth={Int.toString(depth)}") + Terminal.print("score={String.fromInt(score)} alts={String.fromInt(alternatives)} depth={String.fromInt(depth)}") diff --git a/examples/games/doom/enemy.av b/examples/games/doom/enemy.av index 670ab64e..e99d3f5e 100644 --- a/examples/games/doom/enemy.av +++ b/examples/games/doom/enemy.av @@ -49,8 +49,8 @@ fn ghostTeleport(enemy: Enemy, seed: Int, levelMap: List) -> Enemy ? "Teleport ghost to a random nearby floor tile" s1 = Rng.nextSeed(seed) s2 = Rng.nextSeed(s1) - dx = Int.toFloat(Rng.seedToRange(s1, 0 - 4, 4)) - dy = Int.toFloat(Rng.seedToRange(s2, 0 - 4, 4)) + dx = Float.fromInt(Rng.seedToRange(s1, 0 - 4, 4)) + dy = Float.fromInt(Rng.seedToRange(s2, 0 - 4, 4)) nx = enemy.x + dx ny = enemy.y + dy match Level.isWall(levelMap, Float.floor(nx), Float.floor(ny)) @@ -251,7 +251,7 @@ fn applyShotVec(enemies: Vector, idx: Int) -> (List, String) ? "Apply 5 damage to enemy at index using indexed vector access" match Vector.get(enemies, idx) Option.Some(e) -> applyDamageToEnemyVec(enemies, idx, e) - Option.None -> (Vector.toList(enemies), "Shot missed!") + Option.None -> (List.fromVector(enemies), "Shot missed!") verify applyShotVec applyShotVec(Vector.fromList([]), 0) => ([], "Shot missed!") @@ -268,7 +268,7 @@ fn applyDamageToEnemyVec(enemies: Vector, idx: Int, e: Enemy) -> (List (removeEnemyVec(enemies, idx, 0, []), "Killed {Types.enemyGlyph(e.kind)}!") - false -> (replaceEnemyVec(enemies, idx, Enemy.update(e, hp = newHp), 0, []), "Hit {Types.enemyGlyph(e.kind)}! ({Int.toString(newHp)} HP)") + false -> (replaceEnemyVec(enemies, idx, Enemy.update(e, hp = newHp), 0, []), "Hit {Types.enemyGlyph(e.kind)}! ({String.fromInt(newHp)} HP)") verify applyDamageToEnemyVec applyDamageToEnemyVec(Vector.fromList([Types.mkEnemy(1.0, 1.0, EnemyKind.Imp, 3)]), 0, Types.mkEnemy(1.0, 1.0, EnemyKind.Imp, 3)) => ([], "Killed !!") diff --git a/examples/games/doom/level.av b/examples/games/doom/level.av index e83b0e4b..7c72e3d1 100644 --- a/examples/games/doom/level.av +++ b/examples/games/doom/level.av @@ -280,7 +280,7 @@ verify roomFromSeed fn roomCenter(room: Room) -> (Float, Float) ? "Center of a room as float coordinates" - (Int.toFloat(room.x + room.w / 2) + 0.5, Int.toFloat(room.y + room.h / 2) + 0.5) + (Float.fromInt(room.x + room.w / 2) + 0.5, Float.fromInt(room.y + room.h / 2) + 0.5) verify roomCenter roomCenter(Room(x = 4, y = 4, w = 6, h = 6)) => (7.5, 7.5) diff --git a/examples/games/doom/main.av b/examples/games/doom/main.av index 089bcf05..836b8aba 100644 --- a/examples/games/doom/main.av +++ b/examples/games/doom/main.av @@ -33,8 +33,8 @@ verify turnSpeed fn spawnInRoom(room: Level.Room, seed: Int) -> Enemy ? "Spawn an enemy at the center of a room" kind = pickKind(seed) - cx = Int.toFloat(room.x + room.w / 2) + 0.5 - cy = Int.toFloat(room.y + room.h / 2) + 0.5 + cx = Float.fromInt(room.x + room.w / 2) + 0.5 + cy = Float.fromInt(room.y + room.h / 2) + 0.5 Types.mkEnemy(cx, cy, kind, Types.enemyMaxHp(kind)) verify spawnInRoom @@ -214,7 +214,7 @@ fn updateTurn(state: GameState) -> GameState msgs = match isOver true -> ["You died!"] false -> match damaged.hp < state.player.hp - true -> List.concat(state.messages, ["Ouch! HP: {Int.toString(damaged.hp)}"]) + true -> List.concat(state.messages, ["Ouch! HP: {String.fromInt(damaged.hp)}"]) false -> state.messages trimmedMsgs = trimMessages(msgs) GameState.update(state, enemies = newEnemies, player = damaged, turnCount = state.turnCount + 1, gameOver = isOver, messages = trimmedMsgs) diff --git a/examples/games/doom/math.av b/examples/games/doom/math.av index 9b89add2..15f90514 100644 --- a/examples/games/doom/math.av +++ b/examples/games/doom/math.av @@ -29,7 +29,7 @@ verify radToDeg fn normalizeAngle(a: Float) -> Float ? "Wrap angle to [0, 2*pi)" twoPi = Float.pi() * 2.0 - rem = a - Int.toFloat(Float.floor(a / twoPi)) * twoPi + rem = a - Float.fromInt(Float.floor(a / twoPi)) * twoPi match rem < 0.0 true -> rem + twoPi false -> rem diff --git a/examples/games/doom/render.av b/examples/games/doom/render.av index 823002fc..1123b5ea 100644 --- a/examples/games/doom/render.av +++ b/examples/games/doom/render.av @@ -134,7 +134,7 @@ fn wallHeight(distance: Float) -> Int ? "Pixel height of the wall strip for a given corrected distance" match distance < 0.1 true -> pixelH() - false -> Math.clampInt(Float.floor(Int.toFloat(pixelH()) / distance), 0, pixelH()) + false -> Math.clampInt(Float.floor(Float.fromInt(pixelH()) / distance), 0, pixelH()) verify wallHeight wallHeight(1.0) => 80 @@ -282,7 +282,7 @@ verify buildZBuffer fn buildZBufferCol(levelMap: List, wallMap: List, px: Float, py: Float, angle: Float, col: Int, acc: List) -> List ? "Cast one ray and append to buffer" - rayAngle = angle - fov() / 2.0 + Int.toFloat(col) * fov() / Int.toFloat(screenW()) + rayAngle = angle - fov() / 2.0 + Float.fromInt(col) * fov() / Float.fromInt(screenW()) hit = castRay(levelMap, wallMap, px, py, rayAngle) corrected = RayHit.update(hit, dist = hit.dist * Float.cos(rayAngle - angle)) buildZBuffer(levelMap, wallMap, px, py, angle, col + 1, List.concat(acc, [corrected])) @@ -375,7 +375,7 @@ fn renderEnemySprite(enemies: Vector, zbuf: Vector, px: Float, py eDist = Math.dist(px, py, e.x, e.y) eAngle = Float.atan2(dy, dx) relAngle = Math.normalizeAngle(eAngle - pAngle + Float.pi()) - Float.pi() - screenCol = Float.floor(Int.toFloat(screenW()) / 2.0 + relAngle * Int.toFloat(screenW()) / fov()) + screenCol = Float.floor(Float.fromInt(screenW()) / 2.0 + relAngle * Float.fromInt(screenW()) / fov()) match isEnemyVisibleVec(screenCol, eDist, zbuf) true -> drawEnemyChar(e, screenCol, eDist, enemies, zbuf, px, py, pAngle, i) false -> renderEnemiesVec(enemies, zbuf, px, py, pAngle, i + 1) @@ -409,8 +409,8 @@ verify isEnemyVisibleVec fn drawEnemyChar(e: Enemy, screenCol: Int, eDist: Float, enemies: Vector, zbuf: Vector, px: Float, py: Float, pAngle: Float, i: Int) -> Unit ? "Draw enemy using solid Unicode block characters" ! [Terminal.flush, Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] - spriteH = Math.clampInt(Float.floor(2.5 / eDist * Int.toFloat(screenH())), 1, 10) - spriteW = Math.clampInt(Float.floor(5.0 / eDist * Int.toFloat(screenW()) / 10.0), 3, 13) + spriteH = Math.clampInt(Float.floor(2.5 / eDist * Float.fromInt(screenH())), 1, 10) + spriteW = Math.clampInt(Float.floor(5.0 / eDist * Float.fromInt(screenW()) / 10.0), 3, 13) topRow = screenH() / 2 - spriteH / 2 leftCol = screenCol - spriteW / 2 drawBlockGrid(e, leftCol, topRow, spriteH, spriteW, 0, 0) @@ -463,9 +463,9 @@ verify fullBlock fn isInsideShape(kind: EnemyKind, col: Int, row: Int, spriteW: Int, spriteH: Int) -> Bool ? "Check if (col, row) is inside the enemy silhouette" - halfW = Int.toFloat(spriteW) / 2.0 - cx = Float.abs(Int.toFloat(col) - halfW + 0.5) / halfW - cy = Int.toFloat(row) / Int.toFloat(spriteH) + halfW = Float.fromInt(spriteW) / 2.0 + cx = Float.abs(Float.fromInt(col) - halfW + 0.5) / halfW + cy = Float.fromInt(row) / Float.fromInt(spriteH) maxW = shapeWidth(kind, cy) cx < maxW @@ -557,13 +557,13 @@ fn renderHud(state: GameState) -> Unit Terminal.print("BRAILLE DOOM") Terminal.setColor("red") Terminal.moveTo(hudX, 3) - Terminal.print("HP: {Int.toString(state.player.hp)}/100") + Terminal.print("HP: {String.fromInt(state.player.hp)}/100") Terminal.setColor("white") Terminal.moveTo(hudX, 4) deg = Float.floor(Math.radToDeg(state.player.angle)) - Terminal.print("angle: {Int.toString(deg)}") + Terminal.print("angle: {String.fromInt(deg)}") Terminal.moveTo(hudX, 5) - Terminal.print("enemies: {Int.toString(List.len(state.enemies))}") + Terminal.print("enemies: {String.fromInt(List.len(state.enemies))}") Terminal.setColor("cyan") Terminal.moveTo(hudX, 7) Terminal.print("W/S forward/back") diff --git a/examples/games/doom/rng.av b/examples/games/doom/rng.av index ca76be06..52312e63 100644 --- a/examples/games/doom/rng.av +++ b/examples/games/doom/rng.av @@ -38,7 +38,7 @@ verify advanceSeed fn randomFloat(s: Int) -> Float ? "Map seed to [0.0, 1.0)" - Int.toFloat(Result.withDefault(Int.mod(Int.abs(s), 10000), 0)) / 10000.0 + Float.fromInt(Result.withDefault(Int.mod(Int.abs(s), 10000), 0)) / 10000.0 verify randomFloat randomFloat(0) => 0.0 diff --git a/examples/games/life.av b/examples/games/life.av index b5488dec..cf25411e 100644 --- a/examples/games/life.av +++ b/examples/games/life.av @@ -301,7 +301,7 @@ fn drawOneRow(grid: Vector, s: Int, w: Int, h: Int, row: Int) -> Unit fn drawHud(st: SimState, mode: String) -> Unit ? "Draw two-line HUD: status on line 0, controls on line 1." ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] - genStr = Int.toString(st.gen) + genStr = String.fromInt(st.gen) Terminal.setColor("green") Terminal.moveTo(0, 0) line1 = match mode == "edit" @@ -310,7 +310,7 @@ fn drawHud(st: SimState, mode: String) -> Unit true -> " PAUSED Gen:{genStr} " false -> match st.delay == 0 true -> " {st.fps.display}fps Gen:{genStr} BENCHMARK " - false -> " {st.fps.display}fps Gen:{genStr} {Int.toString(st.delay)}ms " + false -> " {st.fps.display}fps Gen:{genStr} {String.fromInt(st.delay)}ms " Terminal.print(line1) Terminal.moveTo(0, 1) Terminal.setColor("cyan") @@ -327,7 +327,7 @@ fn formatFps(frames: Int, elapsed: Int) -> String tenths = frames * 10000 / elapsed whole = tenths / 10 frac = Result.withDefault(Int.mod(tenths, 10), 0) - Int.toString(whole) + "." + Int.toString(frac) + String.fromInt(whole) + "." + String.fromInt(frac) verify formatFps formatFps(2, 1000) => "2.0" @@ -452,7 +452,7 @@ fn simAction(k: String, st: SimState) -> Result true -> 0 false -> 1 match k - "q" -> Result.Ok("Stopped at generation {Int.toString(st.gen)}.") + "q" -> Result.Ok("Stopped at generation {String.fromInt(st.gen)}.") " " -> gameLoop(SimState.update(st, paused = newPaused)) "e" -> simToEditor(st) "+" -> gameLoop(SimState.update(st, delay = Int.max(st.delay - 10, 0))) @@ -470,7 +470,7 @@ fn simToEditor(st: SimState) -> Result result = editorLoop(st.grid, st.stride, st.width, st.height, st.width / 2, st.height / 2) match result Result.Ok(newGrid) -> gameLoop(SimState.update(st, grid = newGrid, gen = 0)) - Result.Err(_) -> Result.Ok("Stopped at generation {Int.toString(st.gen)}.") + Result.Err(_) -> Result.Ok("Stopped at generation {String.fromInt(st.gen)}.") // ─── Entry ────────────────────────────────────────────────── diff --git a/examples/games/rogue/main.av b/examples/games/rogue/main.av index 565b39dd..07b7cbed 100644 --- a/examples/games/rogue/main.av +++ b/examples/games/rogue/main.av @@ -354,7 +354,7 @@ fn resolveAttack(state: GameState, target: Entity, idx: Int) -> GameState hit = Combat.applyDamageToEntity(target, dmg) match Combat.isEntityDead(hit) true -> killEntity(state, target, idx, dmg) - false -> alertNearby(addMessage(GameState.update(state, entities = Combat.replaceEntity(state.entities, idx, hit, 0)), "You hit {target.name} for {Int.toString(dmg)}!"), state.player.pos, 12, 0) + false -> alertNearby(addMessage(GameState.update(state, entities = Combat.replaceEntity(state.entities, idx, hit, 0)), "You hit {target.name} for {String.fromInt(dmg)}!"), state.player.pos, 12, 0) fn killEntity(state: GameState, target: Entity, idx: Int, dmg: Int) -> GameState ? "Kill entity — for-loop gets one respawn (off-by-one error)" @@ -373,7 +373,7 @@ fn loopRespawn(state: GameState, target: Entity, idx: Int) -> GameState fn actualKill(state: GameState, target: Entity, idx: Int) -> GameState ? "Actually remove entity and gain exp" newPlayer = Combat.gainExp(state.player, Combat.expForKill(target.kind)) - addMessage(GameState.update(state, entities = Combat.removeAt(state.entities, idx, 0), player = newPlayer), "You defeated {target.name}! (+{Int.toString(Combat.expForKill(target.kind))} exp)") + addMessage(GameState.update(state, entities = Combat.removeAt(state.entities, idx, 0), player = newPlayer), "You defeated {target.name}! (+{String.fromInt(Combat.expForKill(target.kind))} exp)") verify actualKill List.len(actualKill(GameState(gameMap = [], player = Types.makePlayer(0, 0), entities = [Types.makeIfElse(1, 1)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeIfElse(1, 1), 0).entities) => 0 @@ -400,9 +400,9 @@ verify pickUpItem fn applyItem(state: GameState, item: Item, idx: Int) -> GameState ? "Apply item effect" match item.kind - ItemKind.PotionOfPurity -> addMessage(GameState.update(state, player = Combat.healPlayer(state.player, item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You drink {item.name}. Purity restored! (+{Int.toString(item.value)} HP)") - ItemKind.ScrollOfPatternMatch -> addMessage(GameState.update(state, player = Player.update(state.player, attack = state.player.attack + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You read {item.name}. Your pattern matching grows stronger! (+{Int.toString(item.value)} ATK)") - ItemKind.ShieldOfImmutability -> addMessage(GameState.update(state, player = Player.update(state.player, defense = state.player.defense + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You equip {item.name}. Nothing can change you! (+{Int.toString(item.value)} DEF)") + ItemKind.PotionOfPurity -> addMessage(GameState.update(state, player = Combat.healPlayer(state.player, item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You drink {item.name}. Purity restored! (+{String.fromInt(item.value)} HP)") + ItemKind.ScrollOfPatternMatch -> addMessage(GameState.update(state, player = Player.update(state.player, attack = state.player.attack + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You read {item.name}. Your pattern matching grows stronger! (+{String.fromInt(item.value)} ATK)") + ItemKind.ShieldOfImmutability -> addMessage(GameState.update(state, player = Player.update(state.player, defense = state.player.defense + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You equip {item.name}. Nothing can change you! (+{String.fromInt(item.value)} DEF)") verify applyItem applyItem(GameState(gameMap = [], player = Player.update(Types.makePlayer(0, 0), hp = 10), entities = [], items = [Types.makePurity(0, 0)], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makePurity(0, 0), 0).player.hp => 18 @@ -533,8 +533,8 @@ fn ifElseAttack(state: GameState, entity: Entity, i: Int) -> GameState match Combat.isPlayerDead(newPlayer) true -> addMessage(GameState.update(state, player = newPlayer, gameOver = true), "{entity.name} branch-predicts your death!") false -> match doubled == 1 - true -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} takes BOTH branches! {Int.toString(totalDmg)} dmg!") - false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} hits you for {Int.toString(dmg)}.") + true -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} takes BOTH branches! {String.fromInt(totalDmg)} dmg!") + false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} hits you for {String.fromInt(dmg)}.") fn loopAttack(state: GameState, entity: Entity, i: Int) -> GameState ? "for-loop: normal attack but respawns once when killed (off-by-one)" @@ -542,7 +542,7 @@ fn loopAttack(state: GameState, entity: Entity, i: Int) -> GameState newPlayer = Combat.applyDamageToPlayer(state.player, dmg) match Combat.isPlayerDead(newPlayer) true -> addMessage(GameState.update(state, player = newPlayer, gameOver = true), "{entity.name} iterates you to death!") - false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} iterates on you for {Int.toString(dmg)}!") + false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} iterates on you for {String.fromInt(dmg)}!") verify loopAttack loopAttack(GameState(gameMap = [], player = Types.makePlayer(0, 0), entities = [Types.makeLoop(1, 0)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeLoop(1, 0), 0).player.hp < 20 => true @@ -551,7 +551,7 @@ fn nullAttack(state: GameState, entity: Entity, i: Int) -> GameState ? "null: steals EXP instead of dealing HP damage (null dereference — wipes memory)" stolen = Int.max(1, state.player.exp / 4) newPlayer = Player.update(state.player, exp = Int.max(0, state.player.exp - stolen)) - addMessage(GameState.update(state, player = newPlayer), "{entity.name} dereferences your memory! -{Int.toString(stolen)} EXP!") + addMessage(GameState.update(state, player = newPlayer), "{entity.name} dereferences your memory! -{String.fromInt(stolen)} EXP!") verify nullAttack nullAttack(GameState(gameMap = [], player = Player.update(Types.makePlayer(0, 0), exp = 40), entities = [Types.makeNull(1, 0)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeNull(1, 0), 0).player.exp => 30 @@ -655,7 +655,7 @@ fn descend(state: GameState) -> GameState itms = spawnItems(newSeed, rooms, 0) vis = Fov.computeFov(newMap, Types.pt(px, py), 8) rem = markRemembered(emptyRemembered(0), vis, 0) - addMessage(GameState(gameMap = newMap, player = newPlayer, entities = ents, items = itms, visible = vis, visGrid = buildVisGrid(vis, emptyRemembered(0), 0), remembered = rem, messages = state.messages, seed = Map.nextSeed(newSeed), gameOver = false), "You descend to floor {Int.toString(newFloor)} of Non-Aver!") + addMessage(GameState(gameMap = newMap, player = newPlayer, entities = ents, items = itms, visible = vis, visGrid = buildVisGrid(vis, emptyRemembered(0), 0), remembered = rem, messages = state.messages, seed = Map.nextSeed(newSeed), gameOver = false), "You descend to floor {String.fromInt(newFloor)} of Non-Aver!") verify descend descend(initGame(42)).player.floor => 2 @@ -715,7 +715,7 @@ fn gameLoop(state: GameState) -> Result ] renderFrame(state) match state.gameOver - true -> Result.Ok("Game over on floor {Int.toString(state.player.floor)}. EXP: {Int.toString(state.player.exp)}. The Aver way prevails!") + true -> Result.Ok("Game over on floor {String.fromInt(state.player.floor)}. EXP: {String.fromInt(state.player.exp)}. The Aver way prevails!") false -> waitAndProcess(state) fn waitAndProcess(state: GameState) -> Result diff --git a/examples/games/snake.av b/examples/games/snake.av index e10a086b..c169a9b0 100644 --- a/examples/games/snake.av +++ b/examples/games/snake.av @@ -274,7 +274,7 @@ fn render(state: GameState) -> Unit drawAt(state.food.x, state.food.y, "██") Terminal.resetColor() Terminal.moveTo(0, state.height) - Terminal.print("Score: {Int.toString(state.score)} | Q / ESC to quit") + Terminal.print("Score: {String.fromInt(state.score)} | Q / ESC to quit") Terminal.flush() fn handleKey(state: GameState, k: String) -> GameState @@ -304,7 +304,7 @@ fn tick(state: GameState) -> Result Time.sleep, ] match state.gameOver - true -> Result.Ok("Game over! Score: {Int.toString(state.score)}") + true -> Result.Ok("Game over! Score: {String.fromInt(state.score)}") false -> tickMove(moveSnake(state)) fn tickMove(moved: GameState) -> Result @@ -315,7 +315,7 @@ fn tickMove(moved: GameState) -> Result Time.sleep, ] match checkCollision(moved) - true -> Result.Ok("Game over! Score: {Int.toString(moved.score)}") + true -> Result.Ok("Game over! Score: {String.fromInt(moved.score)}") false -> match didEatFood(moved) true -> gameLoop(growSnake(randomFood(moved))) false -> gameLoop(moved) diff --git a/examples/games/tetris/main.av b/examples/games/tetris/main.av index 41172ebb..db30b14f 100644 --- a/examples/games/tetris/main.av +++ b/examples/games/tetris/main.av @@ -127,11 +127,11 @@ fn renderInfo(state: GameState) -> Unit ? "Draw score, level, lines info" ! [Terminal.moveTo, Terminal.print] Terminal.moveTo(26, 2) - Terminal.print("Score: {Int.toString(state.score)}") + Terminal.print("Score: {String.fromInt(state.score)}") Terminal.moveTo(26, 4) - Terminal.print("Level: {Int.toString(state.level)}") + Terminal.print("Level: {String.fromInt(state.level)}") Terminal.moveTo(26, 6) - Terminal.print("Lines: {Int.toString(state.lines)}") + Terminal.print("Lines: {String.fromInt(state.lines)}") Terminal.moveTo(26, 10) Terminal.print("Controls:") Terminal.moveTo(26, 11) @@ -204,7 +204,7 @@ fn tick(state: GameState) -> Result Time.sleep, ] match state.gameOver - true -> Result.Ok("Game over! Score: {Int.toString(state.score)} Lines: {Int.toString(state.lines)}") + true -> Result.Ok("Game over! Score: {String.fromInt(state.score)} Lines: {String.fromInt(state.lines)}") false -> tickGravity(state) fn gameLoop(state: GameState) -> Result diff --git a/examples/games/wumpus.av b/examples/games/wumpus.av index a1847be4..721c4638 100644 --- a/examples/games/wumpus.av +++ b/examples/games/wumpus.av @@ -48,7 +48,7 @@ verify validRoom fn roomListItem(r: Int, rest: List) -> String ? "Format a single room entry and recurse on remainder." - rs = Int.toString(r) + rs = String.fromInt(r) match rest [] -> rs _ -> "{rs}, {roomList(rest)}" @@ -246,10 +246,10 @@ fn showRoom(game: Game) -> Unit ? "Display current room, tunnels, and hazard warnings." ! [Console.print] Console.print("") - Console.print("You are in room {Int.toString(game.player)}.") + Console.print("You are in room {String.fromInt(game.player)}.") Console.print("Tunnels lead to: {roomList(neighbors(game.player))}") Console.print(warningText(game)) - Console.print("Arrows: {Int.toString(game.arrows)}") + Console.print("Arrows: {String.fromInt(game.arrows)}") fn gameLoop(game: Game) -> Result ? "Main game loop — one turn per recursive call." @@ -311,7 +311,7 @@ fn teleportAndCheck(game: Game) -> Result ? "Bats carry you to a random room; check hazards there." ! [Console.print, Console.readLine, Random.int] dest = randRoom() - Console.print("Super bats carry you to room {Int.toString(dest)}!") + Console.print("Super bats carry you to room {String.fromInt(dest)}!") resolveRoomCheck(checkRoom(Game.update(game, player = dest))) fn retry(game: Game, msg: String) -> Result diff --git a/examples/services/redis.av b/examples/services/redis.av index be070750..948d9fd8 100644 --- a/examples/services/redis.av +++ b/examples/services/redis.av @@ -40,7 +40,7 @@ record RedisConfig fn respBulkLen(s: String) -> String ? "Returns the byte-length of s as a string, used to build RESP bulk-string headers." - Int.toString(String.byteLength(s)) + String.fromInt(String.byteLength(s)) verify respBulkLen respBulkLen("PING") => "4" @@ -77,7 +77,7 @@ fn sendArgs(conn: Tcp.Connection, args: List) -> Result) -> Result ? "Sends a full RESP command: writes the '*N' array header then each arg via sendArgs." ! [Tcp.writeLine] - _ = Tcp.writeLine(conn, "*{Int.toString(List.len(args))}")? + _ = Tcp.writeLine(conn, "*{String.fromInt(List.len(args))}")? sendArgs(conn, args) // Public API diff --git a/src/checker/perf.rs b/src/checker/perf.rs index 95ec8246..e9a17f23 100644 --- a/src/checker/perf.rs +++ b/src/checker/perf.rs @@ -99,7 +99,7 @@ fn expr_to_short_str(expr: &Expr) -> String { } /// Check whether an expression mentions only identifiers from `allowed` and literals. -/// Namespace prefixes (e.g. `Int` in `Int.toFloat(...)`) are treated as constants, +/// Namespace prefixes (e.g. `Int` in `Float.fromInt(...)`) are treated as constants, /// not as variable references that need to be in `allowed`. fn expr_uses_only(expr: &Expr, allowed: &HashSet) -> bool { match expr { @@ -110,7 +110,7 @@ fn expr_uses_only(expr: &Expr, allowed: &HashSet) -> bool { } Expr::FnCall(callee, args) => { let callee_ok = match &callee.node { - // Namespace call like `Int.toFloat(...)` — the namespace is a constant + // Namespace call like `Float.fromInt(...)` — the namespace is a constant Expr::Attr(obj, _) => { if let Expr::Ident(ns) = &obj.node { // Check if this is a known pure namespace prefix @@ -905,15 +905,15 @@ mod tests { #[test] fn b4_loop_invariant_warns() { // fn draw(x: Int, y: Int, width: Int) -> Int - // w = Int.toFloat(width) + // w = Float.fromInt(width) // ... // TailCall("draw", [x + 1, y, width]) // - // `width` is forwarded unchanged → `Int.toFloat(width)` is loop-invariant + // `width` is forwarded unchanged → `Float.fromInt(width)` is loop-invariant let int_to_float_call = Expr::FnCall( Box::new(spanned(Expr::Attr( - Box::new(spanned(ident("Int"))), - "toFloat".to_string(), + Box::new(spanned(ident("Float"))), + "fromInt".to_string(), ))), vec![spanned(ident("width"))], ); @@ -939,7 +939,7 @@ mod tests { assert!( warnings .iter() - .any(|w| w.message.contains("Int.toFloat(width)") + .any(|w| w.message.contains("Float.fromInt(width)") && w.message.contains("doesn't change")), "expected loop-invariant warning, got {:?}", warnings @@ -949,12 +949,12 @@ mod tests { #[test] fn b4_changed_param_no_warning() { // fn draw(x: Int, width: Int) -> Int - // w = Int.toFloat(width) + // w = Float.fromInt(width) // TailCall("draw", [x + 1, width - 1]) ← width is NOT forwarded unchanged let int_to_float_call = Expr::FnCall( Box::new(spanned(Expr::Attr( - Box::new(spanned(ident("Int"))), - "toFloat".to_string(), + Box::new(spanned(ident("Float"))), + "fromInt".to_string(), ))), vec![spanned(ident("width"))], ); @@ -977,7 +977,7 @@ mod tests { let items = vec![TopLevel::FnDef(fd)]; let warnings = collect_perf_warnings(&items); assert!( - !warnings.iter().any(|w| w.message.contains("Int.toFloat")), + !warnings.iter().any(|w| w.message.contains("Float.fromInt")), "expected no warning when width changes, got {:?}", warnings ); @@ -1048,12 +1048,12 @@ mod tests { #[test] fn b4_no_tailcall_no_warning() { // fn f(x: Int, y: Int) -> Int - // w = Int.toFloat(y) + // w = Float.fromInt(y) // w (no TailCall → not a recursive fn in TCO sense) let int_to_float_call = Expr::FnCall( Box::new(spanned(Expr::Attr( - Box::new(spanned(ident("Int"))), - "toFloat".to_string(), + Box::new(spanned(ident("Float"))), + "fromInt".to_string(), ))), vec![spanned(ident("y"))], ); diff --git a/src/codegen/builtins.rs b/src/codegen/builtins.rs index eb872df7..643356e9 100644 --- a/src/codegen/builtins.rs +++ b/src/codegen/builtins.rs @@ -17,8 +17,7 @@ pub(crate) enum Builtin { // --- Int --- IntAbs, - IntToFloat, - IntToString, + IntFromFloat, IntFromString, IntMin, IntMax, @@ -31,8 +30,7 @@ pub(crate) enum Builtin { FloatRound, FloatFloor, FloatCeil, - FloatToInt, - FloatToString, + FloatFromInt, FloatFromString, FloatPi, FloatMin, @@ -43,7 +41,6 @@ pub(crate) enum Builtin { // --- String --- StringLen, - StringConcat, StringCharAt, StringChars, StringSlice, @@ -96,7 +93,7 @@ pub(crate) enum Builtin { VectorSet, VectorLen, VectorFromList, - VectorToList, + ListFromVector, // --- Map --- MapGet, @@ -128,8 +125,7 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { // Int "Int.abs" => Builtin::IntAbs, - "Int.toFloat" | "Float.fromInt" => Builtin::IntToFloat, - "Int.toString" => Builtin::IntToString, + "Int.fromFloat" => Builtin::IntFromFloat, "Int.fromString" => Builtin::IntFromString, "Int.min" => Builtin::IntMin, "Int.max" => Builtin::IntMax, @@ -142,8 +138,7 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { "Float.round" => Builtin::FloatRound, "Float.floor" => Builtin::FloatFloor, "Float.ceil" => Builtin::FloatCeil, - "Float.toInt" => Builtin::FloatToInt, - "Float.toString" => Builtin::FloatToString, + "Float.fromInt" => Builtin::FloatFromInt, "Float.fromString" => Builtin::FloatFromString, "Float.pi" => Builtin::FloatPi, "Float.min" => Builtin::FloatMin, @@ -154,7 +149,6 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { // String "String.len" => Builtin::StringLen, - "String.concat" => Builtin::StringConcat, "String.charAt" => Builtin::StringCharAt, "String.chars" => Builtin::StringChars, "String.slice" => Builtin::StringSlice, @@ -207,7 +201,7 @@ pub(crate) fn recognize_builtin(name: &str) -> Option { "Vector.set" => Builtin::VectorSet, "Vector.len" => Builtin::VectorLen, "Vector.fromList" => Builtin::VectorFromList, - "Vector.toList" => Builtin::VectorToList, + "List.fromVector" => Builtin::ListFromVector, // Map "Map.get" => Builtin::MapGet, diff --git a/src/codegen/dafny/expr.rs b/src/codegen/dafny/expr.rs index 04c0cd1a..c72c64c4 100644 --- a/src/codegen/dafny/expr.rs +++ b/src/codegen/dafny/expr.rs @@ -377,8 +377,8 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str // Int IntAbs => format!("(if {} >= 0 then {} else -{})", a[0], a[0], a[0]), - IntToFloat => format!("({} as real)", a[0]), - IntToString | StringFromInt => format!("IntToString({})", a[0]), + IntFromFloat => format!("({} as int)", a[0]), + StringFromInt => format!("IntToString({})", a[0]), IntFromString => format!("IntFromString({})", a[0]), IntMin => format!("(if {} <= {} then {} else {})", a[0], a[1], a[0], a[1]), IntMax => format!("(if {} >= {} then {} else {})", a[0], a[1], a[0], a[1]), @@ -388,8 +388,9 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str FloatAbs => format!("(if {} >= 0.0 then {} else -{})", a[0], a[0], a[0]), FloatSqrt => format!("FloatSqrt({})", a[0]), FloatPow => format!("FloatPow({}, {})", a[0], a[1]), - FloatRound | FloatFloor | FloatCeil | FloatToInt => format!("FloatToInt({})", a[0]), - FloatToString | StringFromFloat => format!("FloatToString({})", a[0]), + FloatRound | FloatFloor | FloatCeil => format!("FloatToInt({})", a[0]), + FloatFromInt => format!("({} as real)", a[0]), + StringFromFloat => format!("FloatToString({})", a[0]), FloatFromString => format!("FloatFromString({})", a[0]), FloatPi => "FloatPi()".to_string(), FloatMin => format!("(if {} <= {} then {} else {})", a[0], a[1], a[0], a[1]), @@ -400,7 +401,6 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str // String StringLen => format!("|{}|", a[0]), - StringConcat => format!("({} + {})", a[0], a[1]), StringCharAt => format!("StringCharAt({}, {})", a[0], a[1]), StringChars => format!("StringChars({})", a[0]), StringSlice => format!("{}[{}..{}]", a[0], a[1], a[2]), @@ -465,7 +465,7 @@ fn emit_dafny_builtin(b: crate::codegen::builtins::Builtin, a: &[String]) -> Str ), VectorLen => format!("|{}|", a[0]), VectorFromList => a[0].clone(), - VectorToList => a[0].clone(), + ListFromVector => a[0].clone(), // Map MapGet => format!("MapGet({}, {})", a[0], a[1]), diff --git a/src/codegen/lean/builtins.rs b/src/codegen/lean/builtins.rs index 31254946..d5484cdd 100644 --- a/src/codegen/lean/builtins.rs +++ b/src/codegen/lean/builtins.rs @@ -42,8 +42,7 @@ pub fn emit_builtin_call( // ---- Int ---- IntAbs => format!("{}.natAbs", p(&a[0])), - IntToFloat => format!("Float.ofInt {}", p(&a[0])), - IntToString => format!("toString {}", p(&a[0])), + IntFromFloat => format!("AverFloat.toInt {}", p(&a[0])), IntMin => format!("min {} {}", p(&a[0]), p(&a[1])), IntMax => format!("max {} {}", p(&a[0]), p(&a[1])), IntMod => format!("(Except.ok ({} % {}) : Except String Int)", a[0], a[1]), @@ -52,13 +51,12 @@ pub fn emit_builtin_call( // ---- Float ---- FloatAbs => format!("Float.abs {}", p(&a[0])), FloatSqrt => format!("Float.sqrt {}", p(&a[0])), - FloatToString => format!("toString {}", p(&a[0])), FloatFromString => format!("Float.fromString {}", p(&a[0])), + FloatFromInt => format!("Float.ofInt {}", p(&a[0])), FloatPow => format!("AverFloat.pow {} {}", p(&a[0]), p(&a[1])), FloatRound => format!("AverFloat.round {}", p(&a[0])), FloatFloor => format!("AverFloat.floor {}", p(&a[0])), FloatCeil => format!("AverFloat.ceil {}", p(&a[0])), - FloatToInt => format!("AverFloat.toInt {}", p(&a[0])), FloatPi => "(3.141592653589793 : Float)".to_string(), FloatMin => format!("min {} {}", p(&a[0]), p(&a[1])), FloatMax => format!("max {} {}", p(&a[0]), p(&a[1])), @@ -81,7 +79,6 @@ pub fn emit_builtin_call( // ---- String ---- StringLen => format!("{}.length", p(&a[0])), - StringConcat => format!("({} ++ {})", p(&a[0]), p(&a[1])), StringCharAt => format!("String.charAt {} {}", p(&a[0]), p(&a[1])), StringChars => format!("String.chars {}", p(&a[0])), StringSlice => format!("String.slice {} {} {}", p(&a[0]), p(&a[1]), p(&a[2])), @@ -131,7 +128,7 @@ pub fn emit_builtin_call( ), VectorLen => format!("{}.size", p(&a[0])), VectorFromList => format!("{}.toArray", p(&a[0])), - VectorToList => format!("{}.toList", p(&a[0])), + ListFromVector => format!("{}.toList", p(&a[0])), // ---- Map ---- MapGet => format!("AverMap.get {} {}", p(&a[0]), p(&a[1])), diff --git a/src/codegen/rust/builtins.rs b/src/codegen/rust/builtins.rs index 2ac66182..55156ed1 100644 --- a/src/codegen/rust/builtins.rs +++ b/src/codegen/rust/builtins.rs @@ -12,8 +12,6 @@ fn builtin_needs_str_conversion(name: &str) -> bool { name, "Console.readLine" | "Time.now" - | "Int.toString" - | "Float.toString" | "Int.fromString" | "Float.fromString" | "String.slice" @@ -408,13 +406,9 @@ fn emit_builtin_call_inner( let arg = emit_arg(0); Some(format!("{}.abs()", arg)) } - "Int.toFloat" => { + "Int.fromFloat" => { let arg = emit_arg(0); - Some(format!("({} as f64)", arg)) - } - "Int.toString" => { - let arg = emit_arg(0); - Some(format!("{}.to_string()", arg)) + Some(format!("({} as i64)", arg)) } "Int.fromString" => { let arg = emit_arg(0); @@ -459,14 +453,6 @@ fn emit_builtin_call_inner( let arg = emit_arg(0); Some(format!("{}.parse::().map_err(|e| e.to_string())", arg)) } - "Float.toInt" => { - let arg = emit_arg(0); - Some(format!("({} as i64)", arg)) - } - "Float.toString" => { - let arg = emit_arg(0); - Some(format!("{}.to_string()", arg)) - } "Float.sqrt" => { let arg = emit_arg(0); Some(format!("{}.sqrt()", arg)) @@ -783,7 +769,7 @@ fn emit_builtin_call_inner( let list = emit_arg(0); Some(format!("aver_rt::AverVector::from_vec({}.to_vec())", list)) } - "Vector.toList" => { + "List.fromVector" => { let vec = emit_arg(0); Some(format!("{}.to_list()", vec)) } diff --git a/src/codegen/rust/expr.rs b/src/codegen/rust/expr.rs index 566e2017..dd6db347 100644 --- a/src/codegen/rust/expr.rs +++ b/src/codegen/rust/expr.rs @@ -1086,7 +1086,7 @@ fn expr_is_numeric(expr: &Expr, ectx: &EmitCtx) -> bool { | "Float.cos" | "Float.atan2" | "Float.fromInt" - | "Int.toFloat" + | "Float.fromInt" | "List.len" | "Vector.len" | "Map.len" diff --git a/src/codegen/wasm/alloc_policy.rs b/src/codegen/wasm/alloc_policy.rs index d5fdf376..d92540b2 100644 --- a/src/codegen/wasm/alloc_policy.rs +++ b/src/codegen/wasm/alloc_policy.rs @@ -46,7 +46,7 @@ fn is_pure_non_alloc_builtin(name: &str) -> bool { "Int.abs" | "Int.min" | "Int.max" - | "Int.toFloat" + | "Float.fromInt" // Float arithmetic, transcendentals, constants, predicates. | "Float.abs" | "Float.floor" @@ -96,7 +96,7 @@ mod tests { assert!(!p.builtin_allocates("Float.sin")); assert!(!p.builtin_allocates("Float.sqrt")); assert!(!p.builtin_allocates("Int.abs")); - assert!(!p.builtin_allocates("Int.toFloat")); + assert!(!p.builtin_allocates("Float.fromInt")); } #[test] diff --git a/src/codegen/wasm/expr/builtins.rs b/src/codegen/wasm/expr/builtins.rs index 1270b932..cd97d811 100644 --- a/src/codegen/wasm/expr/builtins.rs +++ b/src/codegen/wasm/expr/builtins.rs @@ -116,14 +116,14 @@ impl<'a> ExprEmitter<'a> { "Float.fromInt" if args.len() == 1 => { self.instructions.push(Instruction::F64ConvertI64S); } - "Int.toFloat" if args.len() == 1 => { + "Float.fromInt" if args.len() == 1 => { self.instructions.push(Instruction::F64ConvertI64S); } - "Int.toString" if args.len() == 1 => { + "String.fromInt" if args.len() == 1 => { self.instructions .push(Instruction::Call(self.rt.i64_to_str_obj)); } - "Float.toString" if args.len() == 1 => { + "String.fromFloat" if args.len() == 1 => { self.instructions .push(Instruction::Call(self.rt.f64_to_str_obj)); } @@ -344,7 +344,7 @@ impl<'a> ExprEmitter<'a> { )); self.instructions.push(Instruction::Call(self.rt.vec_new)); } - "Vector.toList" if args.len() == 1 => { + "List.fromVector" if args.len() == 1 => { self.instructions .push(Instruction::Call(self.rt.vec_to_list)); } @@ -626,7 +626,7 @@ impl<'a> ExprEmitter<'a> { self.instructions .push(Instruction::F64Const(std::f64::consts::PI)); } - "Float.toInt" if args.len() == 1 => { + "Int.fromFloat" if args.len() == 1 => { self.instructions.push(Instruction::I64TruncF64S); } "Float.fromString" if args.len() == 1 => { diff --git a/src/codegen/wasm_gc/body/builtins.rs b/src/codegen/wasm_gc/body/builtins.rs index ecba77b3..fca0e507 100644 --- a/src/codegen/wasm_gc/body/builtins.rs +++ b/src/codegen/wasm_gc/body/builtins.rs @@ -157,7 +157,7 @@ pub(super) fn emit_dotted_builtin( } // `Int.toFloat` is the same op as `Float.fromInt` — Aver has // both spellings; map both to the same instruction. - "Int.toFloat" if args.len() == 1 => { + "Float.fromInt" if args.len() == 1 => { emit_expr(func, &args[0], slots, ctx)?; func.instruction(&Instruction::F64ConvertI64S); Ok(()) @@ -298,7 +298,7 @@ pub(super) fn emit_dotted_builtin( let to_string_idx = ctx.fn_map .builtins - .get("Int.toString") + .get("String.fromInt") .copied() .ok_or(WasmGcError::Validation( "String.fromInt requires Int.toString builtin".into(), @@ -308,7 +308,7 @@ pub(super) fn emit_dotted_builtin( Ok(()) } "String.fromFloat" if args.len() == 1 => { - let to_string_idx = ctx.fn_map.builtins.get("Float.toString").copied().ok_or( + let to_string_idx = ctx.fn_map.builtins.get("String.fromFloat").copied().ok_or( WasmGcError::Validation("String.fromFloat requires Float.toString builtin".into()), )?; emit_expr(func, &args[0], slots, ctx)?; @@ -415,7 +415,7 @@ pub(super) fn emit_dotted_builtin( "List.zip" if args.len() == 2 => emit_list_zip_call(func, &args[0], &args[1], slots, ctx), // Vector.fromList(list: List) -> Vector "Vector.fromList" if args.len() == 1 => emit_vec_from_list_call(func, &args[0], slots, ctx), - "Vector.toList" if args.len() == 1 => emit_vec_to_list_call(func, &args[0], slots, ctx), + "List.fromVector" if args.len() == 1 => emit_vec_to_list_call(func, &args[0], slots, ctx), // String.split / String.join — singleton (T=String). "String.split" if args.len() == 2 => { let ops = ctx.fn_map.string_split_ops.ok_or(WasmGcError::Validation( @@ -1119,7 +1119,7 @@ pub(super) fn emit_interpolated_str( "String" => { /* identity */ } "Int" => { let to_string_idx = - ctx.fn_map.builtins.get("Int.toString").copied().ok_or( + ctx.fn_map.builtins.get("String.fromInt").copied().ok_or( WasmGcError::Validation( "interpolation of Int requires Int.toString builtin".into(), ), @@ -1128,7 +1128,7 @@ pub(super) fn emit_interpolated_str( } "Float" => { let to_string_idx = - ctx.fn_map.builtins.get("Float.toString").copied().ok_or( + ctx.fn_map.builtins.get("String.fromFloat").copied().ok_or( WasmGcError::Validation( "interpolation of Float requires Float.toString builtin".into(), ), diff --git a/src/codegen/wasm_gc/builtins/mod.rs b/src/codegen/wasm_gc/builtins/mod.rs index 76a44203..79f0583b 100644 --- a/src/codegen/wasm_gc/builtins/mod.rs +++ b/src/codegen/wasm_gc/builtins/mod.rs @@ -127,7 +127,7 @@ pub(super) enum BuiltinName { impl BuiltinName { pub(super) fn from_dotted(s: &str) -> Option { match s { - "Int.toString" | "String.fromInt" => Some(Self::IntToString), + "String.fromInt" => Some(Self::IntToString), "String.fromFloat" => Some(Self::FloatToString), "String.len" | "String.length" | "String.byteLength" => Some(Self::StringLength), "String.startsWith" => Some(Self::StringStartsWith), @@ -138,7 +138,6 @@ impl BuiltinName { "String.trim" => Some(Self::StringTrim), "Int.fromString" => Some(Self::IntFromString), "Float.fromString" => Some(Self::FloatFromString), - "Float.toString" => Some(Self::FloatToString), "String.endsWith" => Some(Self::StringEndsWith), "String.fromBool" => Some(Self::StringFromBool), "String.charAt" => Some(Self::StringCharAt), @@ -153,7 +152,7 @@ impl BuiltinName { pub(super) fn canonical(self) -> &'static str { match self { - Self::IntToString => "Int.toString", + Self::IntToString => "String.fromInt", Self::StringLength => "String.len", Self::StringConcatN => "__wasmgc_concat_n", Self::StringStartsWith => "String.startsWith", @@ -164,7 +163,7 @@ impl BuiltinName { Self::StringTrim => "String.trim", Self::IntFromString => "Int.fromString", Self::FloatFromString => "Float.fromString", - Self::FloatToString => "Float.toString", + Self::FloatToString => "String.fromFloat", Self::StringEq => "__wasmgc_string_eq", Self::StringCompare => "__wasmgc_string_compare", Self::StringEndsWith => "String.endsWith", diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index 96f0dbfd..a9dec7bb 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -1965,11 +1965,10 @@ fn expr_uses_string(expr: &crate::ast::Expr) -> bool { let dotted = format!("{p}.{member}"); if matches!( dotted.as_str(), - "Int.toString" - | "Float.toString" + "String.fromInt" + | "String.fromFloat" | "String.len" | "String.length" - | "String.concat" | "String.startsWith" | "String.contains" | "String.slice" diff --git a/src/ir/alloc_info.rs b/src/ir/alloc_info.rs index 1548908d..942ea8d5 100644 --- a/src/ir/alloc_info.rs +++ b/src/ir/alloc_info.rs @@ -316,7 +316,7 @@ mod tests { impl AllocPolicy for TestPolicy { fn builtin_allocates(&self, name: &str) -> bool { - name.starts_with("Map.") || name == "Int.toString" + name.starts_with("Map.") || name == "String.fromInt" } fn constructor_allocates(&self, _name: &str, _has_payload: bool) -> bool { false @@ -363,11 +363,11 @@ mod tests { #[test] fn allocating_builtin_call_allocates() { - // Int.toString(42) + // String.fromInt(42) let call = Expr::FnCall( Box::new(sp(Expr::Attr( - Box::new(sp(Expr::Ident("Int".into()))), - "toString".into(), + Box::new(sp(Expr::Ident("String".into()))), + "fromInt".into(), ))), vec![lit_int(42)], ); diff --git a/src/ir/analyze.rs b/src/ir/analyze.rs index 991a1ec8..0d0d79ac 100644 --- a/src/ir/analyze.rs +++ b/src/ir/analyze.rs @@ -73,7 +73,7 @@ impl AllocPolicy for NeutralAllocPolicy { "Int.abs" | "Int.min" | "Int.max" - | "Int.toFloat" + | "Float.fromInt" | "Float.abs" | "Float.floor" | "Float.ceil" diff --git a/src/types/checker/builtins.rs b/src/types/checker/builtins.rs index d8edb34f..c3b99ad9 100644 --- a/src/types/checker/builtins.rs +++ b/src/types/checker/builtins.rs @@ -439,12 +439,10 @@ impl TypeChecker { let int_sigs: &[(&str, &[Type], Type, &[&str])] = &[ ("Int.fromString", &[Type::Str], int_result(), &[]), ("Int.fromFloat", &[Type::Float], Type::Int, &[]), - ("Int.toString", &[Type::Int], Type::Str, &[]), ("Int.abs", &[Type::Int], Type::Int, &[]), ("Int.min", &[Type::Int, Type::Int], Type::Int, &[]), ("Int.max", &[Type::Int, Type::Int], Type::Int, &[]), ("Int.mod", &[Type::Int, Type::Int], int_result(), &[]), - ("Int.toFloat", &[Type::Int], Type::Float, &[]), ]; for (name, params, ret, effects) in int_sigs { self.insert_sig(name, params, ret.clone(), effects); @@ -455,7 +453,6 @@ impl TypeChecker { let float_sigs: &[(&str, &[Type], Type, &[&str])] = &[ ("Float.fromString", &[Type::Str], float_result(), &[]), ("Float.fromInt", &[Type::Int], Type::Float, &[]), - ("Float.toString", &[Type::Float], Type::Str, &[]), ("Float.abs", &[Type::Float], Type::Float, &[]), ("Float.floor", &[Type::Float], Type::Int, &[]), ("Float.ceil", &[Type::Float], Type::Int, &[]), @@ -613,7 +610,7 @@ impl TypeChecker { &[], ), ( - "Vector.toList", + "List.fromVector", &[vec_t()], Type::List(Box::new(t_var())), &[], diff --git a/src/types/checker/infer/vector_calls.rs b/src/types/checker/infer/vector_calls.rs index 2cf6c136..ef5a8254 100644 --- a/src/types/checker/infer/vector_calls.rs +++ b/src/types/checker/infer/vector_calls.rs @@ -117,7 +117,7 @@ impl TypeChecker { }; Some(vector_ty(elem)) } - "Vector.toList" => { + "List.fromVector" => { if let Err(fallback) = expect_arity(self, 1, list_ty(Type::Invalid)) { return Some(fallback); } diff --git a/src/types/float.rs b/src/types/float.rs index 1445f995..93656a31 100644 --- a/src/types/float.rs +++ b/src/types/float.rs @@ -3,7 +3,6 @@ /// Methods: /// Float.fromString(s) → Result — parse string to float /// Float.fromInt(n) → Float — widen int to float -/// Float.toString(f) → String — format float as string /// Float.abs(f) → Float — absolute value /// Float.floor(f) → Int — floor to int /// Float.ceil(f) → Int — ceil to int @@ -29,7 +28,6 @@ pub fn register(global: &mut HashMap) { for method in &[ "fromString", "fromInt", - "toString", "abs", "floor", "ceil", @@ -66,7 +64,6 @@ pub fn call(name: &str, args: &[Value]) -> Option> { match name { "Float.fromString" => Some(from_string(args)), "Float.fromInt" => Some(from_int(args)), - "Float.toString" => Some(to_string(args)), "Float.abs" => Some(abs(args)), "Float.floor" => Some(floor(args)), "Float.ceil" => Some(ceil(args)), @@ -111,16 +108,6 @@ fn from_int(args: &[Value]) -> Result { Ok(Value::Float(*n as f64)) } -fn to_string(args: &[Value]) -> Result { - let [val] = one_arg("Float.toString", args)?; - let Value::Float(f) = val else { - return Err(RuntimeError::Error( - "Float.toString: argument must be a Float".to_string(), - )); - }; - Ok(Value::Str(format!("{}", f))) -} - fn abs(args: &[Value]) -> Result { let [val] = one_arg("Float.abs", args)?; let Value::Float(f) = val else { @@ -271,7 +258,6 @@ pub fn register_nv(global: &mut HashMap, arena: &mut Arena) { let methods = &[ "fromString", "fromInt", - "toString", "abs", "floor", "ceil", @@ -305,7 +291,6 @@ pub fn call_nv( match name { "Float.fromString" => Some(from_string_nv(args, arena)), "Float.fromInt" => Some(from_int_nv(args, arena)), - "Float.toString" => Some(to_string_nv(args, arena)), "Float.abs" => Some(abs_nv(args, arena)), "Float.floor" => Some(floor_nv(args, arena)), "Float.ceil" => Some(ceil_nv(args, arena)), @@ -375,17 +360,6 @@ fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result Result { - let v = nv_check1("Float.toString", args)?; - if !v.is_float() { - return Err(RuntimeError::Error( - "Float.toString: argument must be a Float".to_string(), - )); - } - let s = format!("{}", v.as_float()); - Ok(NanValue::new_string_value(&s, arena)) -} - fn abs_nv(args: &[NanValue], _arena: &mut Arena) -> Result { let v = nv_check1("Float.abs", args)?; if !v.is_float() { diff --git a/src/types/int.rs b/src/types/int.rs index 60a8b132..e8140bc8 100644 --- a/src/types/int.rs +++ b/src/types/int.rs @@ -3,12 +3,13 @@ /// Methods: /// Int.fromString(s) → Result — parse string to int /// Int.fromFloat(f) → Int — truncate float to int -/// Int.toString(n) → String — format int as string /// Int.abs(n) → Int — absolute value /// Int.min(a, b) → Int — minimum of two ints /// Int.max(a, b) → Int — maximum of two ints /// Int.mod(a, b) → Result — modulo (error on b=0) -/// Int.toFloat(n) → Float — widen int to float +/// +/// Stringification goes through `String.fromInt` (or `"{n}"` interpolation); +/// widening to Float goes through `Float.fromInt`. /// /// No effects required. use std::collections::HashMap; @@ -19,16 +20,7 @@ use crate::value::{RuntimeError, Value}; pub fn register(global: &mut HashMap) { let mut members = HashMap::new(); - for method in &[ - "fromString", - "fromFloat", - "toString", - "abs", - "min", - "max", - "mod", - "toFloat", - ] { + for method in &["fromString", "fromFloat", "abs", "min", "max", "mod"] { members.insert( method.to_string(), Value::Builtin(format!("Int.{}", method)), @@ -52,12 +44,10 @@ pub fn call(name: &str, args: &[Value]) -> Option> { match name { "Int.fromString" => Some(from_string(args)), "Int.fromFloat" => Some(from_float(args)), - "Int.toString" => Some(to_string(args)), "Int.abs" => Some(abs(args)), "Int.min" => Some(min(args)), "Int.max" => Some(max(args)), "Int.mod" => Some(modulo(args)), - "Int.toFloat" => Some(to_float(args)), _ => None, } } @@ -90,16 +80,6 @@ fn from_float(args: &[Value]) -> Result { Ok(Value::Int(*f as i64)) } -fn to_string(args: &[Value]) -> Result { - let [val] = one_arg("Int.toString", args)?; - let Value::Int(n) = val else { - return Err(RuntimeError::Error( - "Int.toString: argument must be an Int".to_string(), - )); - }; - Ok(Value::Str(format!("{}", n))) -} - fn abs(args: &[Value]) -> Result { let [val] = one_arg("Int.abs", args)?; let Value::Int(n) = val else { @@ -146,16 +126,6 @@ fn modulo(args: &[Value]) -> Result { } } -fn to_float(args: &[Value]) -> Result { - let [val] = one_arg("Int.toFloat", args)?; - let Value::Int(n) = val else { - return Err(RuntimeError::Error( - "Int.toFloat: argument must be an Int".to_string(), - )); - }; - Ok(Value::Float(*n as f64)) -} - // ─── Helpers ──────────────────────────────────────────────────────────────── fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> { @@ -183,16 +153,7 @@ fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], Runtime // ─── NanValue-native API ───────────────────────────────────────────────────── pub fn register_nv(global: &mut HashMap, arena: &mut Arena) { - let methods = &[ - "fromString", - "fromFloat", - "toString", - "abs", - "min", - "max", - "mod", - "toFloat", - ]; + let methods = &["fromString", "fromFloat", "abs", "min", "max", "mod"]; let mut members: Vec<(Rc, NanValue)> = Vec::with_capacity(methods.len()); for method in methods { let idx = arena.push_builtin(&format!("Int.{}", method)); @@ -213,12 +174,10 @@ pub fn call_nv( match name { "Int.fromString" => Some(from_string_nv(args, arena)), "Int.fromFloat" => Some(from_float_nv(args, arena)), - "Int.toString" => Some(to_string_nv(args, arena)), "Int.abs" => Some(abs_nv(args, arena)), "Int.min" => Some(min_nv(args, arena)), "Int.max" => Some(max_nv(args, arena)), "Int.mod" => Some(modulo_nv(args, arena)), - "Int.toFloat" => Some(to_float_nv(args, arena)), _ => None, } } @@ -276,17 +235,6 @@ fn from_float_nv(args: &[NanValue], arena: &mut Arena) -> Result Result { - let v = nv_check1("Int.toString", args)?; - if !v.is_int() { - return Err(RuntimeError::Error( - "Int.toString: argument must be an Int".to_string(), - )); - } - let s = format!("{}", v.as_int(arena)); - Ok(NanValue::new_string_value(&s, arena)) -} - fn abs_nv(args: &[NanValue], arena: &mut Arena) -> Result { let v = nv_check1("Int.abs", args)?; if !v.is_int() { @@ -341,12 +289,3 @@ fn modulo_nv(args: &[NanValue], arena: &mut Arena) -> Result Result { - let v = nv_check1("Int.toFloat", args)?; - if !v.is_int() { - return Err(RuntimeError::Error( - "Int.toFloat: argument must be an Int".to_string(), - )); - } - Ok(NanValue::new_float(v.as_int(arena) as f64)) -} diff --git a/src/types/list.rs b/src/types/list.rs index 526b7c36..5bc3116d 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -22,7 +22,15 @@ use crate::value::{ pub fn register(global: &mut HashMap) { let mut members = HashMap::new(); for method in &[ - "len", "prepend", "take", "drop", "concat", "reverse", "contains", "zip", + "len", + "prepend", + "take", + "drop", + "concat", + "reverse", + "contains", + "zip", + "fromVector", ] { members.insert( method.to_string(), @@ -197,7 +205,15 @@ fn zip(args: &[Value]) -> Result { pub fn register_nv(global: &mut HashMap, arena: &mut Arena) { let methods = &[ - "len", "prepend", "take", "drop", "concat", "reverse", "contains", "zip", + "len", + "prepend", + "take", + "drop", + "concat", + "reverse", + "contains", + "zip", + "fromVector", ]; let mut members: Vec<(Rc, NanValue)> = Vec::with_capacity(methods.len()); for method in methods { diff --git a/src/types/vector.rs b/src/types/vector.rs index bab413f5..3ce46569 100644 --- a/src/types/vector.rs +++ b/src/types/vector.rs @@ -6,7 +6,7 @@ /// Vector.set(vec, idx, val) → Option> /// Vector.len(vec) → Int /// Vector.fromList(xs) → Vector -/// Vector.toList(vec) → List +/// List.fromVector(vec) → List /// /// No effects required. use std::collections::HashMap; @@ -19,7 +19,7 @@ use crate::value::{RuntimeError, Value, list_from_vec, list_view}; pub fn register(global: &mut HashMap) { let mut members = HashMap::new(); - for method in &["new", "get", "set", "len", "fromList", "toList"] { + for method in &["new", "get", "set", "len", "fromList"] { members.insert( method.to_string(), Value::Builtin(format!("Vector.{}", method)), @@ -45,7 +45,7 @@ pub fn call(name: &str, args: &[Value]) -> Option> { "Vector.set" => Some(vec_set(args)), "Vector.len" => Some(vec_len(args)), "Vector.fromList" => Some(vec_from_list(args)), - "Vector.toList" => Some(vec_to_list(args)), + "List.fromVector" => Some(vec_to_list(args)), _ => None, } } @@ -156,13 +156,13 @@ fn vec_from_list(args: &[Value]) -> Result { fn vec_to_list(args: &[Value]) -> Result { if args.len() != 1 { return Err(RuntimeError::Error(format!( - "Vector.toList() takes 1 argument, got {}", + "List.fromVector() takes 1 argument, got {}", args.len() ))); } let Value::Vector(vec) = &args[0] else { return Err(RuntimeError::Error( - "Vector.toList: argument must be a Vector".to_string(), + "List.fromVector: argument must be a Vector".to_string(), )); }; Ok(list_from_vec(vec.to_vec())) @@ -171,7 +171,7 @@ fn vec_to_list(args: &[Value]) -> Result { // ─── NanValue-native API ───────────────────────────────────────────────────── pub fn register_nv(global: &mut HashMap, arena: &mut Arena) { - let methods = &["new", "get", "set", "len", "fromList", "toList"]; + let methods = &["new", "get", "set", "len", "fromList"]; let mut members: Vec<(Rc, NanValue)> = Vec::with_capacity(methods.len()); for method in methods { let idx = arena.push_builtin(&format!("Vector.{}", method)); @@ -195,7 +195,7 @@ pub fn call_nv( "Vector.set" => Some(vec_set_nv(args, arena)), "Vector.len" => Some(vec_len_nv(args, arena)), "Vector.fromList" => Some(vec_from_list_nv(args, arena)), - "Vector.toList" => Some(vec_to_list_nv(args, arena)), + "List.fromVector" => Some(vec_to_list_nv(args, arena)), _ => None, } } @@ -364,13 +364,13 @@ fn vec_from_list_nv(args: &[NanValue], arena: &mut Arena) -> Result Result { if args.len() != 1 { return Err(RuntimeError::Error(format!( - "Vector.toList() takes 1 argument, got {}", + "List.fromVector() takes 1 argument, got {}", args.len() ))); } if !args[0].is_vector() { return Err(RuntimeError::Error( - "Vector.toList: argument must be a Vector".to_string(), + "List.fromVector: argument must be a Vector".to_string(), )); } let items = arena.clone_vector_value(args[0]); diff --git a/src/vm/alloc_policy.rs b/src/vm/alloc_policy.rs index 4a36c96d..d8436552 100644 --- a/src/vm/alloc_policy.rs +++ b/src/vm/alloc_policy.rs @@ -32,7 +32,7 @@ fn is_pure_non_alloc_builtin(name: &str) -> bool { "Int.abs" | "Int.min" | "Int.max" - | "Int.toFloat" + | "Float.fromInt" | "Float.abs" | "Float.floor" | "Float.ceil" diff --git a/src/vm/builtin.rs b/src/vm/builtin.rs index 5d178659..317bc873 100644 --- a/src/vm/builtin.rs +++ b/src/vm/builtin.rs @@ -96,16 +96,13 @@ vm_builtins! { IntFromString => "Int.fromString", IntFromFloat => "Int.fromFloat", - IntToString => "Int.toString", IntAbs => "Int.abs", IntMin => "Int.min", IntMax => "Int.max", IntMod => "Int.mod", - IntToFloat => "Int.toFloat", FloatFromString => "Float.fromString", FloatFromInt => "Float.fromInt", - FloatToString => "Float.toString", FloatAbs => "Float.abs", FloatFloor => "Float.floor", FloatCeil => "Float.ceil", @@ -161,7 +158,7 @@ vm_builtins! { VectorSet => "Vector.set", VectorLen => "Vector.len", VectorFromList => "Vector.fromList", - VectorToList => "Vector.toList", + ListFromVector => "List.fromVector", OptionWithDefault => "Option.withDefault", OptionToResult => "Option.toResult", @@ -203,7 +200,6 @@ impl VmBuiltin { | Self::IntAbs | Self::IntMin | Self::IntMax - | Self::IntToFloat | Self::FloatFromInt | Self::FloatAbs | Self::FloatFloor @@ -420,16 +416,13 @@ impl VmBuiltin { Self::IntFromString | Self::IntFromFloat - | Self::IntToString | Self::IntAbs | Self::IntMin | Self::IntMax - | Self::IntMod - | Self::IntToFloat => int::call_nv(self.name(), args, arena), + | Self::IntMod => int::call_nv(self.name(), args, arena), Self::FloatFromString | Self::FloatFromInt - | Self::FloatToString | Self::FloatAbs | Self::FloatFloor | Self::FloatCeil @@ -485,7 +478,7 @@ impl VmBuiltin { | Self::VectorSet | Self::VectorLen | Self::VectorFromList - | Self::VectorToList => crate::types::vector::call_nv(self.name(), args, arena), + | Self::ListFromVector => crate::types::vector::call_nv(self.name(), args, arena), Self::OptionWithDefault | Self::OptionToResult => { option::call_nv(self.name(), args, arena) diff --git a/tools/edge/bench.av b/tools/edge/bench.av index 3ad8ecc8..d0a3950a 100644 --- a/tools/edge/bench.av +++ b/tools/edge/bench.av @@ -12,4 +12,4 @@ fn main() -> Unit seahorse = Fractal.viewFromQuery("cx=-0.7463&cy=0.1102&w=0.012") zoom = Fractal.render(200, 120, 220, seahorse) t2 = Time.unixMs() - Console.print("full(200x120,100)={Int.toString(t1 - t0)}ms[{Int.toString(String.len(full))}B] zoom(200x120,220)={Int.toString(t2 - t1)}ms[{Int.toString(String.len(zoom))}B] total={Int.toString(t2 - t0)}ms") + Console.print("full(200x120,100)={String.fromInt(t1 - t0)}ms[{String.fromInt(String.len(full))}B] zoom(200x120,220)={String.fromInt(t2 - t1)}ms[{String.fromInt(String.len(zoom))}B] total={String.fromInt(t2 - t0)}ms") diff --git a/tools/edge/fractal.av b/tools/edge/fractal.av index 0b6cccde..ee8abf18 100644 --- a/tools/edge/fractal.av +++ b/tools/edge/fractal.av @@ -141,9 +141,9 @@ verify gridHFor fn navLink(cx: Float, cy: Float, w: Float, label: String, hint: String) -> String ? "One nav anchor — encodes (cx, cy, w) into a /fractal query string. Browsers fire a fresh request per click; the new view is parsed back out by `viewFromQuery` on the server." String.join([ - "", label, "" ], "") @@ -189,22 +189,22 @@ fn mandelStep(n: Int, x: Float, y: Float, prevZ2: Float, cx: Float, cy: Float, m ? "One iteration of z = z² + c. On escape (|z|² > 4) returns a *continuous* escape time using linear interpolation between the previous and current |z|²: nu = (n - 1) + (R² - prevZ²) / (curZ² - prevZ²). This sub-iter precision drives the Bayer-dither palette lookup so iso-bands transition smoothly across pixels instead of stepping at integer iter boundaries." curZ2 = x * x + y * y match curZ2 > 4.0 - true -> Int.toFloat(n - 1) + (4.0 - prevZ2) / (curZ2 - prevZ2) + true -> Float.fromInt(n - 1) + (4.0 - prevZ2) / (curZ2 - prevZ2) false -> mandelIter(n + 1, x * x - y * y + cx, 2.0 * x * y + cy, curZ2, cx, cy, maxIter) verify mandelStep - mandelStep(7, 3.0, 0.0, 1.0, 0.0, 0.0, 50) => Int.toFloat(7 - 1) + (4.0 - 1.0) / (9.0 - 1.0) - mandelStep(0, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Int.toFloat(50) + mandelStep(7, 3.0, 0.0, 1.0, 0.0, 0.0, 50) => Float.fromInt(7 - 1) + (4.0 - 1.0) / (9.0 - 1.0) + mandelStep(0, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Float.fromInt(50) fn mandelIter(n: Int, x: Float, y: Float, prevZ2: Float, cx: Float, cy: Float, maxIter: Int) -> Float - ? "Iterates z = z² + c until |z|² > 4 or maxIter; returns continuous escape time as Float. In-set points (n hits maxIter) return Int.toFloat(maxIter). TCO-eligible mutual recursion via mandelStep." + ? "Iterates z = z² + c until |z|² > 4 or maxIter; returns continuous escape time as Float. In-set points (n hits maxIter) return Float.fromInt(maxIter). TCO-eligible mutual recursion via mandelStep." match n >= maxIter - true -> Int.toFloat(maxIter) + true -> Float.fromInt(maxIter) false -> mandelStep(n, x, y, prevZ2, cx, cy, maxIter) verify mandelIter - mandelIter(0, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Int.toFloat(50) - mandelIter(50, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Int.toFloat(50) + mandelIter(0, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Float.fromInt(50) + mandelIter(50, 0.0, 0.0, 0.0, 0.0, 0.0, 50) => Float.fromInt(50) fn insideCardioid(cx: Float, cy: Float) -> Bool ? "True if (cx, cy) lies inside the main cardioid (the largest connected component of the set). Roughly 50% of inside-set points fall here; this test costs ~5 ops and avoids running mandelIter to maxIter on every one of them." @@ -232,21 +232,21 @@ fn escapeIterSlow(cx: Float, cy: Float, maxIter: Int) -> Float fn escapeIterBulb(cx: Float, cy: Float, maxIter: Int) -> Float ? "Cardioid missed — try the period-2 bulb, else fall through." match insideBulb(cx, cy) - true -> Int.toFloat(maxIter) + true -> Float.fromInt(maxIter) false -> escapeIterSlow(cx, cy, maxIter) verify escapeIterBulb - escapeIterBulb(0.0 - 1.0, 0.0, 50) => Int.toFloat(50) + escapeIterBulb(0.0 - 1.0, 0.0, 50) => Float.fromInt(50) fn escapeIter(cx: Float, cy: Float, maxIter: Int) -> Float - ? "Returns the continuous escape time for (cx, cy) under the given iteration cap. Returns Int.toFloat(maxIter) for inside-set. Uses the cardioid + period-2 bulb pre-tests to short-circuit ~70% of inside-set points before any iteration runs. Sub-iter precision (Float vs Int) drives Bayer-dither palette lookup downstream." + ? "Returns the continuous escape time for (cx, cy) under the given iteration cap. Returns Float.fromInt(maxIter) for inside-set. Uses the cardioid + period-2 bulb pre-tests to short-circuit ~70% of inside-set points before any iteration runs. Sub-iter precision (Float vs Int) drives Bayer-dither palette lookup downstream." match insideCardioid(cx, cy) - true -> Int.toFloat(maxIter) + true -> Float.fromInt(maxIter) false -> escapeIterBulb(cx, cy, maxIter) verify escapeIter - escapeIter(0.0, 0.0, 50) => Int.toFloat(50) - escapeIter(0.0 - 1.0, 0.0, 50) => Int.toFloat(50) + escapeIter(0.0, 0.0, 50) => Float.fromInt(50) + escapeIter(0.0 - 1.0, 0.0, 50) => Float.fromInt(50) fn ditherThreshold(col: Int, row: Int) -> Float ? "4x4 ordered Bayer dither threshold for pixel (col, row). Standard recursive 4x4 Bayer matrix divided by 16 (centred so frac=0 never upgrades and frac=1 always upgrades). 16 distinct thresholds across a 4x4 block give 16-level fractional palette interpolation — fine enough that the dither pattern is barely visible at normal viewing distance while still smoothly bridging adjacent palette entries." @@ -279,28 +279,28 @@ verify ditherThreshold fn paletteIdx(nu: Float, col: Int, row: Int, maxIter: Int) -> Int ? "Maps a continuous escape-time `nu` to a palette index 0..15 with 2x2 ordered-Bayer dither. Inside-set (nu >= maxIter) → 0 (deep navy). Escaped pixels: `n = floor(nu)`, `frac = nu - n`. Compare `frac` against the position-dependent dither threshold; if greater, show the next palette entry, else the current one. The result is that fractional `nu` translates into per-cell choice between two adjacent palette entries, breaking up integer iso-band stepping into smooth 2x2 transitions." - match nu >= Int.toFloat(maxIter) + match nu >= Float.fromInt(maxIter) true -> 0 - false -> match (nu - Int.toFloat(Float.floor(nu))) > ditherThreshold(col, row) + false -> match (nu - Float.fromInt(Float.floor(nu))) > ditherThreshold(col, row) true -> Result.withDefault(Int.mod(Float.floor(nu) + 1, 30), 0) + 1 false -> Result.withDefault(Int.mod(Float.floor(nu), 30), 0) + 1 verify paletteIdx - paletteIdx(Int.toFloat(50), 0, 0, 50) => 0 + paletteIdx(Float.fromInt(50), 0, 0, 50) => 0 paletteIdx(0.0, 0, 0, 50) => 1 paletteIdx(29.0, 0, 0, 50) => 30 paletteIdx(30.0, 0, 0, 50) => 1 fn pixelCx(pixCol: Int, pixelsW: Int, view: View) -> Float ? "Pixel column → Mandelbrot cx coordinate." - Int.toFloat(pixCol) / Int.toFloat(pixelsW) * view.cxRange + view.cxMin + Float.fromInt(pixCol) / Float.fromInt(pixelsW) * view.cxRange + view.cxMin verify pixelCx pixelCx(0, 1, fullView()) => 0.0 - 2.0 fn pixelCy(pixRow: Int, pixelsH: Int, view: View) -> Float ? "Pixel row → Mandelbrot cy coordinate." - Int.toFloat(pixRow) / Int.toFloat(pixelsH) * view.cyRange + view.cyMin + Float.fromInt(pixRow) / Float.fromInt(pixelsH) * view.cyRange + view.cyMin verify pixelCy pixelCy(0, 1, fullView()) => 0.0 - 1.5 diff --git a/tools/website/llms.txt b/tools/website/llms.txt index e002ddf2..3db11b4e 100644 --- a/tools/website/llms.txt +++ b/tools/website/llms.txt @@ -104,7 +104,7 @@ Compound: Notes: - top-level named functions can be passed where `Fn(...)` is expected - there are no lambdas and no closures -- no implicit type promotion; use `Int.toFloat` / `Float.fromInt` +- no implicit type promotion; use `Float.fromInt` / `Float.fromInt` ### User-defined types @@ -136,7 +136,7 @@ Rules: ```aver match value - Result.Ok(v) -> Int.toString(v) + Result.Ok(v) -> String.fromInt(v) Result.Err(e) -> e ``` @@ -309,8 +309,8 @@ Common pure namespaces: Key `String` API: - `String.len`, `String.contains`, `String.startsWith`, `String.endsWith` - `String.toUpper`, `String.toLower`, `String.trim` -- `String.concat`, `String.join`, `String.split`, `String.chars` -- `Int.fromString : String -> Result`, `Int.toString : Int -> String` +- `String.join`, `String.split`, `String.chars` — concat is the `+` operator +- `Int.fromString : String -> Result`, `String.fromInt : Int -> String` - string interpolation: `"Hello, {name}!"` is the idiomatic concat Key `List` API (small, recursion-first): diff --git a/tools/website/playground/sources/examples/games/checkers/main.av b/tools/website/playground/sources/examples/games/checkers/main.av index d36b5e0d..73794513 100644 --- a/tools/website/playground/sources/examples/games/checkers/main.av +++ b/tools/website/playground/sources/examples/games/checkers/main.av @@ -210,11 +210,11 @@ fn renderAiPanel(result: AiResult) -> Unit ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] Terminal.moveTo(22, 1) Terminal.setColor("cyan") - Terminal.print("AI depth={Int.toString(result.depth)} {Int.toString(result.thinkMs)}ms") + Terminal.print("AI depth={String.fromInt(result.depth)} {String.fromInt(result.thinkMs)}ms") Terminal.resetColor() Terminal.moveTo(22, 2) Terminal.setColor("yellow") - Terminal.print("root-parallel x{Int.toString(Ai.rootParallelWidth())} | {Int.toString(List.len(result.candidates))} moves") + Terminal.print("root-parallel x{String.fromInt(Ai.rootParallelWidth())} | {String.fromInt(List.len(result.candidates))} moves") Terminal.resetColor() renderCandidates(result.candidates, result.move, 0) @@ -242,7 +242,7 @@ fn renderChosenCandidate(sm: ScoredMove, i: Int) -> Unit Terminal.setColor("green") Terminal.print("★ ") Terminal.print(moveLabel(sm.move)) - Terminal.print(" {Int.toString(sm.score)}") + Terminal.print(" {String.fromInt(sm.score)}") Terminal.resetColor() fn renderOtherCandidate(sm: ScoredMove, i: Int) -> Unit @@ -250,7 +250,7 @@ fn renderOtherCandidate(sm: ScoredMove, i: Int) -> Unit ! [Terminal.print] Terminal.print(" ") Terminal.print(moveLabel(sm.move)) - Terminal.print(" {Int.toString(sm.score)}") + Terminal.print(" {String.fromInt(sm.score)}") fn isChosenMove(a: List, b: List) -> Bool ? "True if two moves are the same (compare first and last points)" @@ -295,7 +295,7 @@ fn moveLabel(move: List) -> String ? "Human-readable label for a move path" match firstPoint(move) Option.Some(from) -> match lastPoint(move) - Option.Some(to) -> "{colChar(from.x)}{Int.toString(8 - from.y)}→{colChar(to.x)}{Int.toString(8 - to.y)}" + Option.Some(to) -> "{colChar(from.x)}{String.fromInt(8 - from.y)}→{colChar(to.x)}{String.fromInt(8 - to.y)}" Option.None -> "?" Option.None -> "?" @@ -307,7 +307,7 @@ fn renderHelp(state: GameState) -> Unit ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] Terminal.moveTo(0, 14) Terminal.setColor("green") - Terminal.print(" You (black) vs AI (white) · depth {Int.toString(state.searchDepth)} · +/- to change") + Terminal.print(" You (black) vs AI (white) · depth {String.fromInt(state.searchDepth)} · +/- to change") Terminal.resetColor() Terminal.moveTo(0, 15) match state.selected @@ -415,7 +415,7 @@ fn aiPhase(state: GameState) -> Result ] renderFrame(state) Terminal.moveTo(0, 14) - Terminal.print("AI thinking (depth {Int.toString(state.searchDepth)}, root-parallel x{Int.toString(Ai.rootParallelWidth())})...") + Terminal.print("AI thinking (depth {String.fromInt(state.searchDepth)}, root-parallel x{String.fromInt(Ai.rootParallelWidth())})...") Terminal.flush() t0 = Time.unixMs() aiState = handleAiTurn(state) @@ -443,7 +443,7 @@ fn gameOverResult(state: GameState) -> Result Terminal.print("Game over!") Terminal.flush() Time.sleep(2000) - Result.Ok("Game over! Move {Int.toString(state.moveNum)}") + Result.Ok("Game over! Move {String.fromInt(state.moveNum)}") fn gameTurn(state: GameState) -> Result ? "Dispatch to player or AI" diff --git a/tools/website/playground/sources/examples/games/checkers/render.av b/tools/website/playground/sources/examples/games/checkers/render.av index 5e3e472f..f0145779 100644 --- a/tools/website/playground/sources/examples/games/checkers/render.av +++ b/tools/website/playground/sources/examples/games/checkers/render.av @@ -240,7 +240,7 @@ fn renderRowLabelsNext(r: Int) -> Unit fn rowLabel(r: Int) -> String ? "Row label: row 0 = 8, row 7 = 1" - Int.toString(8 - r) + String.fromInt(8 - r) verify rowLabel rowLabel(0) => "8" @@ -281,7 +281,7 @@ fn renderStatus(currentPlayer: Color, moveNum: Int) -> Unit Terminal.setColor(playerColor(currentPlayer)) Terminal.print("{colorName(currentPlayer)}") Terminal.resetColor() - Terminal.print(" to move | Move #{Int.toString(moveNum)}") + Terminal.print(" to move | Move #{String.fromInt(moveNum)}") fn renderAiTrace(move: List, score: Int, alternatives: Int, depth: Int) -> Unit ? "Show AI decision trace" @@ -290,4 +290,4 @@ fn renderAiTrace(move: List, score: Int, alternatives: Int, depth: Int) - Terminal.setColor("cyan") Terminal.print("AI: ") Terminal.resetColor() - Terminal.print("score={Int.toString(score)} alts={Int.toString(alternatives)} depth={Int.toString(depth)}") + Terminal.print("score={String.fromInt(score)} alts={String.fromInt(alternatives)} depth={String.fromInt(depth)}") diff --git a/tools/website/playground/sources/examples/games/doom/enemy.av b/tools/website/playground/sources/examples/games/doom/enemy.av index 670ab64e..e99d3f5e 100644 --- a/tools/website/playground/sources/examples/games/doom/enemy.av +++ b/tools/website/playground/sources/examples/games/doom/enemy.av @@ -49,8 +49,8 @@ fn ghostTeleport(enemy: Enemy, seed: Int, levelMap: List) -> Enemy ? "Teleport ghost to a random nearby floor tile" s1 = Rng.nextSeed(seed) s2 = Rng.nextSeed(s1) - dx = Int.toFloat(Rng.seedToRange(s1, 0 - 4, 4)) - dy = Int.toFloat(Rng.seedToRange(s2, 0 - 4, 4)) + dx = Float.fromInt(Rng.seedToRange(s1, 0 - 4, 4)) + dy = Float.fromInt(Rng.seedToRange(s2, 0 - 4, 4)) nx = enemy.x + dx ny = enemy.y + dy match Level.isWall(levelMap, Float.floor(nx), Float.floor(ny)) @@ -251,7 +251,7 @@ fn applyShotVec(enemies: Vector, idx: Int) -> (List, String) ? "Apply 5 damage to enemy at index using indexed vector access" match Vector.get(enemies, idx) Option.Some(e) -> applyDamageToEnemyVec(enemies, idx, e) - Option.None -> (Vector.toList(enemies), "Shot missed!") + Option.None -> (List.fromVector(enemies), "Shot missed!") verify applyShotVec applyShotVec(Vector.fromList([]), 0) => ([], "Shot missed!") @@ -268,7 +268,7 @@ fn applyDamageToEnemyVec(enemies: Vector, idx: Int, e: Enemy) -> (List (removeEnemyVec(enemies, idx, 0, []), "Killed {Types.enemyGlyph(e.kind)}!") - false -> (replaceEnemyVec(enemies, idx, Enemy.update(e, hp = newHp), 0, []), "Hit {Types.enemyGlyph(e.kind)}! ({Int.toString(newHp)} HP)") + false -> (replaceEnemyVec(enemies, idx, Enemy.update(e, hp = newHp), 0, []), "Hit {Types.enemyGlyph(e.kind)}! ({String.fromInt(newHp)} HP)") verify applyDamageToEnemyVec applyDamageToEnemyVec(Vector.fromList([Types.mkEnemy(1.0, 1.0, EnemyKind.Imp, 3)]), 0, Types.mkEnemy(1.0, 1.0, EnemyKind.Imp, 3)) => ([], "Killed !!") diff --git a/tools/website/playground/sources/examples/games/doom/level.av b/tools/website/playground/sources/examples/games/doom/level.av index e83b0e4b..7c72e3d1 100644 --- a/tools/website/playground/sources/examples/games/doom/level.av +++ b/tools/website/playground/sources/examples/games/doom/level.av @@ -280,7 +280,7 @@ verify roomFromSeed fn roomCenter(room: Room) -> (Float, Float) ? "Center of a room as float coordinates" - (Int.toFloat(room.x + room.w / 2) + 0.5, Int.toFloat(room.y + room.h / 2) + 0.5) + (Float.fromInt(room.x + room.w / 2) + 0.5, Float.fromInt(room.y + room.h / 2) + 0.5) verify roomCenter roomCenter(Room(x = 4, y = 4, w = 6, h = 6)) => (7.5, 7.5) diff --git a/tools/website/playground/sources/examples/games/doom/main.av b/tools/website/playground/sources/examples/games/doom/main.av index 089bcf05..836b8aba 100644 --- a/tools/website/playground/sources/examples/games/doom/main.av +++ b/tools/website/playground/sources/examples/games/doom/main.av @@ -33,8 +33,8 @@ verify turnSpeed fn spawnInRoom(room: Level.Room, seed: Int) -> Enemy ? "Spawn an enemy at the center of a room" kind = pickKind(seed) - cx = Int.toFloat(room.x + room.w / 2) + 0.5 - cy = Int.toFloat(room.y + room.h / 2) + 0.5 + cx = Float.fromInt(room.x + room.w / 2) + 0.5 + cy = Float.fromInt(room.y + room.h / 2) + 0.5 Types.mkEnemy(cx, cy, kind, Types.enemyMaxHp(kind)) verify spawnInRoom @@ -214,7 +214,7 @@ fn updateTurn(state: GameState) -> GameState msgs = match isOver true -> ["You died!"] false -> match damaged.hp < state.player.hp - true -> List.concat(state.messages, ["Ouch! HP: {Int.toString(damaged.hp)}"]) + true -> List.concat(state.messages, ["Ouch! HP: {String.fromInt(damaged.hp)}"]) false -> state.messages trimmedMsgs = trimMessages(msgs) GameState.update(state, enemies = newEnemies, player = damaged, turnCount = state.turnCount + 1, gameOver = isOver, messages = trimmedMsgs) diff --git a/tools/website/playground/sources/examples/games/doom/math.av b/tools/website/playground/sources/examples/games/doom/math.av index 9b89add2..15f90514 100644 --- a/tools/website/playground/sources/examples/games/doom/math.av +++ b/tools/website/playground/sources/examples/games/doom/math.av @@ -29,7 +29,7 @@ verify radToDeg fn normalizeAngle(a: Float) -> Float ? "Wrap angle to [0, 2*pi)" twoPi = Float.pi() * 2.0 - rem = a - Int.toFloat(Float.floor(a / twoPi)) * twoPi + rem = a - Float.fromInt(Float.floor(a / twoPi)) * twoPi match rem < 0.0 true -> rem + twoPi false -> rem diff --git a/tools/website/playground/sources/examples/games/doom/render.av b/tools/website/playground/sources/examples/games/doom/render.av index 823002fc..1123b5ea 100644 --- a/tools/website/playground/sources/examples/games/doom/render.av +++ b/tools/website/playground/sources/examples/games/doom/render.av @@ -134,7 +134,7 @@ fn wallHeight(distance: Float) -> Int ? "Pixel height of the wall strip for a given corrected distance" match distance < 0.1 true -> pixelH() - false -> Math.clampInt(Float.floor(Int.toFloat(pixelH()) / distance), 0, pixelH()) + false -> Math.clampInt(Float.floor(Float.fromInt(pixelH()) / distance), 0, pixelH()) verify wallHeight wallHeight(1.0) => 80 @@ -282,7 +282,7 @@ verify buildZBuffer fn buildZBufferCol(levelMap: List, wallMap: List, px: Float, py: Float, angle: Float, col: Int, acc: List) -> List ? "Cast one ray and append to buffer" - rayAngle = angle - fov() / 2.0 + Int.toFloat(col) * fov() / Int.toFloat(screenW()) + rayAngle = angle - fov() / 2.0 + Float.fromInt(col) * fov() / Float.fromInt(screenW()) hit = castRay(levelMap, wallMap, px, py, rayAngle) corrected = RayHit.update(hit, dist = hit.dist * Float.cos(rayAngle - angle)) buildZBuffer(levelMap, wallMap, px, py, angle, col + 1, List.concat(acc, [corrected])) @@ -375,7 +375,7 @@ fn renderEnemySprite(enemies: Vector, zbuf: Vector, px: Float, py eDist = Math.dist(px, py, e.x, e.y) eAngle = Float.atan2(dy, dx) relAngle = Math.normalizeAngle(eAngle - pAngle + Float.pi()) - Float.pi() - screenCol = Float.floor(Int.toFloat(screenW()) / 2.0 + relAngle * Int.toFloat(screenW()) / fov()) + screenCol = Float.floor(Float.fromInt(screenW()) / 2.0 + relAngle * Float.fromInt(screenW()) / fov()) match isEnemyVisibleVec(screenCol, eDist, zbuf) true -> drawEnemyChar(e, screenCol, eDist, enemies, zbuf, px, py, pAngle, i) false -> renderEnemiesVec(enemies, zbuf, px, py, pAngle, i + 1) @@ -409,8 +409,8 @@ verify isEnemyVisibleVec fn drawEnemyChar(e: Enemy, screenCol: Int, eDist: Float, enemies: Vector, zbuf: Vector, px: Float, py: Float, pAngle: Float, i: Int) -> Unit ? "Draw enemy using solid Unicode block characters" ! [Terminal.flush, Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] - spriteH = Math.clampInt(Float.floor(2.5 / eDist * Int.toFloat(screenH())), 1, 10) - spriteW = Math.clampInt(Float.floor(5.0 / eDist * Int.toFloat(screenW()) / 10.0), 3, 13) + spriteH = Math.clampInt(Float.floor(2.5 / eDist * Float.fromInt(screenH())), 1, 10) + spriteW = Math.clampInt(Float.floor(5.0 / eDist * Float.fromInt(screenW()) / 10.0), 3, 13) topRow = screenH() / 2 - spriteH / 2 leftCol = screenCol - spriteW / 2 drawBlockGrid(e, leftCol, topRow, spriteH, spriteW, 0, 0) @@ -463,9 +463,9 @@ verify fullBlock fn isInsideShape(kind: EnemyKind, col: Int, row: Int, spriteW: Int, spriteH: Int) -> Bool ? "Check if (col, row) is inside the enemy silhouette" - halfW = Int.toFloat(spriteW) / 2.0 - cx = Float.abs(Int.toFloat(col) - halfW + 0.5) / halfW - cy = Int.toFloat(row) / Int.toFloat(spriteH) + halfW = Float.fromInt(spriteW) / 2.0 + cx = Float.abs(Float.fromInt(col) - halfW + 0.5) / halfW + cy = Float.fromInt(row) / Float.fromInt(spriteH) maxW = shapeWidth(kind, cy) cx < maxW @@ -557,13 +557,13 @@ fn renderHud(state: GameState) -> Unit Terminal.print("BRAILLE DOOM") Terminal.setColor("red") Terminal.moveTo(hudX, 3) - Terminal.print("HP: {Int.toString(state.player.hp)}/100") + Terminal.print("HP: {String.fromInt(state.player.hp)}/100") Terminal.setColor("white") Terminal.moveTo(hudX, 4) deg = Float.floor(Math.radToDeg(state.player.angle)) - Terminal.print("angle: {Int.toString(deg)}") + Terminal.print("angle: {String.fromInt(deg)}") Terminal.moveTo(hudX, 5) - Terminal.print("enemies: {Int.toString(List.len(state.enemies))}") + Terminal.print("enemies: {String.fromInt(List.len(state.enemies))}") Terminal.setColor("cyan") Terminal.moveTo(hudX, 7) Terminal.print("W/S forward/back") diff --git a/tools/website/playground/sources/examples/games/doom/rng.av b/tools/website/playground/sources/examples/games/doom/rng.av index ca76be06..52312e63 100644 --- a/tools/website/playground/sources/examples/games/doom/rng.av +++ b/tools/website/playground/sources/examples/games/doom/rng.av @@ -38,7 +38,7 @@ verify advanceSeed fn randomFloat(s: Int) -> Float ? "Map seed to [0.0, 1.0)" - Int.toFloat(Result.withDefault(Int.mod(Int.abs(s), 10000), 0)) / 10000.0 + Float.fromInt(Result.withDefault(Int.mod(Int.abs(s), 10000), 0)) / 10000.0 verify randomFloat randomFloat(0) => 0.0 diff --git a/tools/website/playground/sources/examples/games/life.av b/tools/website/playground/sources/examples/games/life.av index b5488dec..cf25411e 100644 --- a/tools/website/playground/sources/examples/games/life.av +++ b/tools/website/playground/sources/examples/games/life.av @@ -301,7 +301,7 @@ fn drawOneRow(grid: Vector, s: Int, w: Int, h: Int, row: Int) -> Unit fn drawHud(st: SimState, mode: String) -> Unit ? "Draw two-line HUD: status on line 0, controls on line 1." ! [Terminal.moveTo, Terminal.print, Terminal.resetColor, Terminal.setColor] - genStr = Int.toString(st.gen) + genStr = String.fromInt(st.gen) Terminal.setColor("green") Terminal.moveTo(0, 0) line1 = match mode == "edit" @@ -310,7 +310,7 @@ fn drawHud(st: SimState, mode: String) -> Unit true -> " PAUSED Gen:{genStr} " false -> match st.delay == 0 true -> " {st.fps.display}fps Gen:{genStr} BENCHMARK " - false -> " {st.fps.display}fps Gen:{genStr} {Int.toString(st.delay)}ms " + false -> " {st.fps.display}fps Gen:{genStr} {String.fromInt(st.delay)}ms " Terminal.print(line1) Terminal.moveTo(0, 1) Terminal.setColor("cyan") @@ -327,7 +327,7 @@ fn formatFps(frames: Int, elapsed: Int) -> String tenths = frames * 10000 / elapsed whole = tenths / 10 frac = Result.withDefault(Int.mod(tenths, 10), 0) - Int.toString(whole) + "." + Int.toString(frac) + String.fromInt(whole) + "." + String.fromInt(frac) verify formatFps formatFps(2, 1000) => "2.0" @@ -452,7 +452,7 @@ fn simAction(k: String, st: SimState) -> Result true -> 0 false -> 1 match k - "q" -> Result.Ok("Stopped at generation {Int.toString(st.gen)}.") + "q" -> Result.Ok("Stopped at generation {String.fromInt(st.gen)}.") " " -> gameLoop(SimState.update(st, paused = newPaused)) "e" -> simToEditor(st) "+" -> gameLoop(SimState.update(st, delay = Int.max(st.delay - 10, 0))) @@ -470,7 +470,7 @@ fn simToEditor(st: SimState) -> Result result = editorLoop(st.grid, st.stride, st.width, st.height, st.width / 2, st.height / 2) match result Result.Ok(newGrid) -> gameLoop(SimState.update(st, grid = newGrid, gen = 0)) - Result.Err(_) -> Result.Ok("Stopped at generation {Int.toString(st.gen)}.") + Result.Err(_) -> Result.Ok("Stopped at generation {String.fromInt(st.gen)}.") // ─── Entry ────────────────────────────────────────────────── diff --git a/tools/website/playground/sources/examples/games/rogue/main.av b/tools/website/playground/sources/examples/games/rogue/main.av index 565b39dd..07b7cbed 100644 --- a/tools/website/playground/sources/examples/games/rogue/main.av +++ b/tools/website/playground/sources/examples/games/rogue/main.av @@ -354,7 +354,7 @@ fn resolveAttack(state: GameState, target: Entity, idx: Int) -> GameState hit = Combat.applyDamageToEntity(target, dmg) match Combat.isEntityDead(hit) true -> killEntity(state, target, idx, dmg) - false -> alertNearby(addMessage(GameState.update(state, entities = Combat.replaceEntity(state.entities, idx, hit, 0)), "You hit {target.name} for {Int.toString(dmg)}!"), state.player.pos, 12, 0) + false -> alertNearby(addMessage(GameState.update(state, entities = Combat.replaceEntity(state.entities, idx, hit, 0)), "You hit {target.name} for {String.fromInt(dmg)}!"), state.player.pos, 12, 0) fn killEntity(state: GameState, target: Entity, idx: Int, dmg: Int) -> GameState ? "Kill entity — for-loop gets one respawn (off-by-one error)" @@ -373,7 +373,7 @@ fn loopRespawn(state: GameState, target: Entity, idx: Int) -> GameState fn actualKill(state: GameState, target: Entity, idx: Int) -> GameState ? "Actually remove entity and gain exp" newPlayer = Combat.gainExp(state.player, Combat.expForKill(target.kind)) - addMessage(GameState.update(state, entities = Combat.removeAt(state.entities, idx, 0), player = newPlayer), "You defeated {target.name}! (+{Int.toString(Combat.expForKill(target.kind))} exp)") + addMessage(GameState.update(state, entities = Combat.removeAt(state.entities, idx, 0), player = newPlayer), "You defeated {target.name}! (+{String.fromInt(Combat.expForKill(target.kind))} exp)") verify actualKill List.len(actualKill(GameState(gameMap = [], player = Types.makePlayer(0, 0), entities = [Types.makeIfElse(1, 1)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeIfElse(1, 1), 0).entities) => 0 @@ -400,9 +400,9 @@ verify pickUpItem fn applyItem(state: GameState, item: Item, idx: Int) -> GameState ? "Apply item effect" match item.kind - ItemKind.PotionOfPurity -> addMessage(GameState.update(state, player = Combat.healPlayer(state.player, item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You drink {item.name}. Purity restored! (+{Int.toString(item.value)} HP)") - ItemKind.ScrollOfPatternMatch -> addMessage(GameState.update(state, player = Player.update(state.player, attack = state.player.attack + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You read {item.name}. Your pattern matching grows stronger! (+{Int.toString(item.value)} ATK)") - ItemKind.ShieldOfImmutability -> addMessage(GameState.update(state, player = Player.update(state.player, defense = state.player.defense + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You equip {item.name}. Nothing can change you! (+{Int.toString(item.value)} DEF)") + ItemKind.PotionOfPurity -> addMessage(GameState.update(state, player = Combat.healPlayer(state.player, item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You drink {item.name}. Purity restored! (+{String.fromInt(item.value)} HP)") + ItemKind.ScrollOfPatternMatch -> addMessage(GameState.update(state, player = Player.update(state.player, attack = state.player.attack + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You read {item.name}. Your pattern matching grows stronger! (+{String.fromInt(item.value)} ATK)") + ItemKind.ShieldOfImmutability -> addMessage(GameState.update(state, player = Player.update(state.player, defense = state.player.defense + item.value), items = Combat.removeItemAt(state.items, idx, 0)), "You equip {item.name}. Nothing can change you! (+{String.fromInt(item.value)} DEF)") verify applyItem applyItem(GameState(gameMap = [], player = Player.update(Types.makePlayer(0, 0), hp = 10), entities = [], items = [Types.makePurity(0, 0)], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makePurity(0, 0), 0).player.hp => 18 @@ -533,8 +533,8 @@ fn ifElseAttack(state: GameState, entity: Entity, i: Int) -> GameState match Combat.isPlayerDead(newPlayer) true -> addMessage(GameState.update(state, player = newPlayer, gameOver = true), "{entity.name} branch-predicts your death!") false -> match doubled == 1 - true -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} takes BOTH branches! {Int.toString(totalDmg)} dmg!") - false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} hits you for {Int.toString(dmg)}.") + true -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} takes BOTH branches! {String.fromInt(totalDmg)} dmg!") + false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} hits you for {String.fromInt(dmg)}.") fn loopAttack(state: GameState, entity: Entity, i: Int) -> GameState ? "for-loop: normal attack but respawns once when killed (off-by-one)" @@ -542,7 +542,7 @@ fn loopAttack(state: GameState, entity: Entity, i: Int) -> GameState newPlayer = Combat.applyDamageToPlayer(state.player, dmg) match Combat.isPlayerDead(newPlayer) true -> addMessage(GameState.update(state, player = newPlayer, gameOver = true), "{entity.name} iterates you to death!") - false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} iterates on you for {Int.toString(dmg)}!") + false -> addMessage(GameState.update(state, player = newPlayer), "{entity.name} iterates on you for {String.fromInt(dmg)}!") verify loopAttack loopAttack(GameState(gameMap = [], player = Types.makePlayer(0, 0), entities = [Types.makeLoop(1, 0)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeLoop(1, 0), 0).player.hp < 20 => true @@ -551,7 +551,7 @@ fn nullAttack(state: GameState, entity: Entity, i: Int) -> GameState ? "null: steals EXP instead of dealing HP damage (null dereference — wipes memory)" stolen = Int.max(1, state.player.exp / 4) newPlayer = Player.update(state.player, exp = Int.max(0, state.player.exp - stolen)) - addMessage(GameState.update(state, player = newPlayer), "{entity.name} dereferences your memory! -{Int.toString(stolen)} EXP!") + addMessage(GameState.update(state, player = newPlayer), "{entity.name} dereferences your memory! -{String.fromInt(stolen)} EXP!") verify nullAttack nullAttack(GameState(gameMap = [], player = Player.update(Types.makePlayer(0, 0), exp = 40), entities = [Types.makeNull(1, 0)], items = [], visible = [], visGrid = [], remembered = [], messages = [], seed = 0, gameOver = false), Types.makeNull(1, 0), 0).player.exp => 30 @@ -655,7 +655,7 @@ fn descend(state: GameState) -> GameState itms = spawnItems(newSeed, rooms, 0) vis = Fov.computeFov(newMap, Types.pt(px, py), 8) rem = markRemembered(emptyRemembered(0), vis, 0) - addMessage(GameState(gameMap = newMap, player = newPlayer, entities = ents, items = itms, visible = vis, visGrid = buildVisGrid(vis, emptyRemembered(0), 0), remembered = rem, messages = state.messages, seed = Map.nextSeed(newSeed), gameOver = false), "You descend to floor {Int.toString(newFloor)} of Non-Aver!") + addMessage(GameState(gameMap = newMap, player = newPlayer, entities = ents, items = itms, visible = vis, visGrid = buildVisGrid(vis, emptyRemembered(0), 0), remembered = rem, messages = state.messages, seed = Map.nextSeed(newSeed), gameOver = false), "You descend to floor {String.fromInt(newFloor)} of Non-Aver!") verify descend descend(initGame(42)).player.floor => 2 @@ -715,7 +715,7 @@ fn gameLoop(state: GameState) -> Result ] renderFrame(state) match state.gameOver - true -> Result.Ok("Game over on floor {Int.toString(state.player.floor)}. EXP: {Int.toString(state.player.exp)}. The Aver way prevails!") + true -> Result.Ok("Game over on floor {String.fromInt(state.player.floor)}. EXP: {String.fromInt(state.player.exp)}. The Aver way prevails!") false -> waitAndProcess(state) fn waitAndProcess(state: GameState) -> Result diff --git a/tools/website/playground/sources/examples/games/snake.av b/tools/website/playground/sources/examples/games/snake.av index e10a086b..c169a9b0 100644 --- a/tools/website/playground/sources/examples/games/snake.av +++ b/tools/website/playground/sources/examples/games/snake.av @@ -274,7 +274,7 @@ fn render(state: GameState) -> Unit drawAt(state.food.x, state.food.y, "██") Terminal.resetColor() Terminal.moveTo(0, state.height) - Terminal.print("Score: {Int.toString(state.score)} | Q / ESC to quit") + Terminal.print("Score: {String.fromInt(state.score)} | Q / ESC to quit") Terminal.flush() fn handleKey(state: GameState, k: String) -> GameState @@ -304,7 +304,7 @@ fn tick(state: GameState) -> Result Time.sleep, ] match state.gameOver - true -> Result.Ok("Game over! Score: {Int.toString(state.score)}") + true -> Result.Ok("Game over! Score: {String.fromInt(state.score)}") false -> tickMove(moveSnake(state)) fn tickMove(moved: GameState) -> Result @@ -315,7 +315,7 @@ fn tickMove(moved: GameState) -> Result Time.sleep, ] match checkCollision(moved) - true -> Result.Ok("Game over! Score: {Int.toString(moved.score)}") + true -> Result.Ok("Game over! Score: {String.fromInt(moved.score)}") false -> match didEatFood(moved) true -> gameLoop(growSnake(randomFood(moved))) false -> gameLoop(moved) diff --git a/tools/website/playground/sources/examples/games/tetris/main.av b/tools/website/playground/sources/examples/games/tetris/main.av index 41172ebb..db30b14f 100644 --- a/tools/website/playground/sources/examples/games/tetris/main.av +++ b/tools/website/playground/sources/examples/games/tetris/main.av @@ -127,11 +127,11 @@ fn renderInfo(state: GameState) -> Unit ? "Draw score, level, lines info" ! [Terminal.moveTo, Terminal.print] Terminal.moveTo(26, 2) - Terminal.print("Score: {Int.toString(state.score)}") + Terminal.print("Score: {String.fromInt(state.score)}") Terminal.moveTo(26, 4) - Terminal.print("Level: {Int.toString(state.level)}") + Terminal.print("Level: {String.fromInt(state.level)}") Terminal.moveTo(26, 6) - Terminal.print("Lines: {Int.toString(state.lines)}") + Terminal.print("Lines: {String.fromInt(state.lines)}") Terminal.moveTo(26, 10) Terminal.print("Controls:") Terminal.moveTo(26, 11) @@ -204,7 +204,7 @@ fn tick(state: GameState) -> Result Time.sleep, ] match state.gameOver - true -> Result.Ok("Game over! Score: {Int.toString(state.score)} Lines: {Int.toString(state.lines)}") + true -> Result.Ok("Game over! Score: {String.fromInt(state.score)} Lines: {String.fromInt(state.lines)}") false -> tickGravity(state) fn gameLoop(state: GameState) -> Result diff --git a/tools/website/playground/sources/examples/games/wumpus.av b/tools/website/playground/sources/examples/games/wumpus.av index a1847be4..721c4638 100644 --- a/tools/website/playground/sources/examples/games/wumpus.av +++ b/tools/website/playground/sources/examples/games/wumpus.av @@ -48,7 +48,7 @@ verify validRoom fn roomListItem(r: Int, rest: List) -> String ? "Format a single room entry and recurse on remainder." - rs = Int.toString(r) + rs = String.fromInt(r) match rest [] -> rs _ -> "{rs}, {roomList(rest)}" @@ -246,10 +246,10 @@ fn showRoom(game: Game) -> Unit ? "Display current room, tunnels, and hazard warnings." ! [Console.print] Console.print("") - Console.print("You are in room {Int.toString(game.player)}.") + Console.print("You are in room {String.fromInt(game.player)}.") Console.print("Tunnels lead to: {roomList(neighbors(game.player))}") Console.print(warningText(game)) - Console.print("Arrows: {Int.toString(game.arrows)}") + Console.print("Arrows: {String.fromInt(game.arrows)}") fn gameLoop(game: Game) -> Result ? "Main game loop — one turn per recursive call." @@ -311,7 +311,7 @@ fn teleportAndCheck(game: Game) -> Result ? "Bats carry you to a random room; check hazards there." ! [Console.print, Console.readLine, Random.int] dest = randRoom() - Console.print("Super bats carry you to room {Int.toString(dest)}!") + Console.print("Super bats carry you to room {String.fromInt(dest)}!") resolveRoomCheck(checkRoom(Game.update(game, player = dest))) fn retry(game: Game, msg: String) -> Result diff --git a/tools/website/playground/sources/examples/shapes.av b/tools/website/playground/sources/examples/shapes.av index f3ad7b23..197efc5b 100644 --- a/tools/website/playground/sources/examples/shapes.av +++ b/tools/website/playground/sources/examples/shapes.av @@ -21,5 +21,5 @@ fn main() -> Unit ! [Console.print] c = Shape.Circle(5.0) r = Shape.Rectangle(3.0, 4.0) - Console.print("circle area = {Float.toString(area(c))}") - Console.print("rect area = {Float.toString(area(r))}") + Console.print("circle area = {String.fromFloat(area(c))}") + Console.print("rect area = {String.fromFloat(area(r))}") From 5e3d21457a552785579088c7319a6dcea2501ef3 Mon Sep 17 00:00:00 2001 From: jasisz Date: Wed, 6 May 2026 21:14:45 +0200 Subject: [PATCH 18/18] 0.17.0 "Purge" fix: dedup match arms after sed migration The bulk sed-replace in [3+4/4] turned aliased arms like `"Int.toFloat" | "Float.fromInt"` into duplicate `"Float.fromInt" | "Float.fromInt"`, which clippy `-D warnings` flags as unreachable_patterns. Same for `"Int.toString"` aliases collapsing into duplicate `"String.fromInt"` entries. Plus an alias arm in `wasm_gc/body/builtins.rs` that became identical to the main `"Float.fromInt"` arm above it. Hand-removed 7 duplicates across rust/wasm/wasm-gc/ir/vm match ladders + 1 alias arm in body/builtins.rs. 621/621 lib tests, fmt clean, clippy `-D warnings` clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/codegen/rust/expr.rs | 1 - src/codegen/wasm/alloc_policy.rs | 1 - src/codegen/wasm_gc/body/builtins.rs | 18 +++--------------- src/codegen/wasm_gc/types.rs | 2 -- src/ir/analyze.rs | 1 - src/types/int.rs | 1 - src/vm/alloc_policy.rs | 1 - 7 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/codegen/rust/expr.rs b/src/codegen/rust/expr.rs index dd6db347..6630c532 100644 --- a/src/codegen/rust/expr.rs +++ b/src/codegen/rust/expr.rs @@ -1086,7 +1086,6 @@ fn expr_is_numeric(expr: &Expr, ectx: &EmitCtx) -> bool { | "Float.cos" | "Float.atan2" | "Float.fromInt" - | "Float.fromInt" | "List.len" | "Vector.len" | "Map.len" diff --git a/src/codegen/wasm/alloc_policy.rs b/src/codegen/wasm/alloc_policy.rs index d92540b2..866e31cf 100644 --- a/src/codegen/wasm/alloc_policy.rs +++ b/src/codegen/wasm/alloc_policy.rs @@ -60,7 +60,6 @@ fn is_pure_non_alloc_builtin(name: &str) -> bool { | "Float.pow" | "Float.atan2" | "Float.pi" - | "Float.fromInt" // Char ↔ code-point. `Char.fromCode` returns Option // (allocates the Some wrapper around a heap String); only the // reverse direction is pure. diff --git a/src/codegen/wasm_gc/body/builtins.rs b/src/codegen/wasm_gc/body/builtins.rs index fca0e507..c97932e6 100644 --- a/src/codegen/wasm_gc/body/builtins.rs +++ b/src/codegen/wasm_gc/body/builtins.rs @@ -155,13 +155,6 @@ pub(super) fn emit_dotted_builtin( func.instruction(&Instruction::F64Const(std::f64::consts::PI)); Ok(()) } - // `Int.toFloat` is the same op as `Float.fromInt` — Aver has - // both spellings; map both to the same instruction. - "Float.fromInt" if args.len() == 1 => { - emit_expr(func, &args[0], slots, ctx)?; - func.instruction(&Instruction::F64ConvertI64S); - Ok(()) - } "Int.abs" if args.len() == 1 => { // Branched: if (x < 0) 0 - x else x. Two evaluations of x; // cheap when x is a Resolved local. @@ -295,14 +288,9 @@ pub(super) fn emit_dotted_builtin( // Int.toString / Float.toString — Aver source allows both, // backend points each at the same helper. "String.fromInt" if args.len() == 1 => { - let to_string_idx = - ctx.fn_map - .builtins - .get("String.fromInt") - .copied() - .ok_or(WasmGcError::Validation( - "String.fromInt requires Int.toString builtin".into(), - ))?; + let to_string_idx = ctx.fn_map.builtins.get("String.fromInt").copied().ok_or( + WasmGcError::Validation("String.fromInt requires Int.toString builtin".into()), + )?; emit_expr(func, &args[0], slots, ctx)?; func.instruction(&Instruction::Call(to_string_idx)); Ok(()) diff --git a/src/codegen/wasm_gc/types.rs b/src/codegen/wasm_gc/types.rs index a9dec7bb..685ff5db 100644 --- a/src/codegen/wasm_gc/types.rs +++ b/src/codegen/wasm_gc/types.rs @@ -1978,8 +1978,6 @@ fn expr_uses_string(expr: &crate::ast::Expr) -> bool { | "String.replace" | "String.split" | "String.join" - | "String.fromInt" - | "String.fromFloat" | "String.fromBool" | "String.endsWith" | "String.charAt" diff --git a/src/ir/analyze.rs b/src/ir/analyze.rs index 0d0d79ac..25979e6a 100644 --- a/src/ir/analyze.rs +++ b/src/ir/analyze.rs @@ -86,7 +86,6 @@ impl AllocPolicy for NeutralAllocPolicy { | "Float.pow" | "Float.atan2" | "Float.pi" - | "Float.fromInt" | "Char.toCode" | "String.len" | "String.byteLength" diff --git a/src/types/int.rs b/src/types/int.rs index e8140bc8..43fe379e 100644 --- a/src/types/int.rs +++ b/src/types/int.rs @@ -288,4 +288,3 @@ fn modulo_nv(args: &[NanValue], arena: &mut Arena) -> Result bool { | "Float.pow" | "Float.atan2" | "Float.pi" - | "Float.fromInt" | "Char.toCode" | "String.len" | "String.byteLength"