Multi-Value Returns
Functions return whole structs through WebAssembly’s native multi-value feature — at any size, with no hidden pointer plumbing.
When a function returns a struct, the compiler emits a wasm signature
whose results are the struct’s fields, flattened in declaration order —
its leaves, in the sense of
Structs & Refs. A
Vec2-returning function is
(func (param ...) (result f32 f32)); a
Transform-returning function carries four results. The caller
receives the leaves directly on the operand stack — no allocation, no scratch
memory, no copy through linear memory.
struct Vec2 {
x: f32 = 0;
y: f32 = 0;
constructor(x: f32, y: f32) { this.x = x; this.y = y; }
}
function polar(r: f32, theta: f32): Vec2 {
// (result f32 f32) — leaves on the stack, no out-pointer
return Vec2(r * Mathf.cos(theta), r * Mathf.sin(theta));
}
export function demo(): f32 {
// Chained value expressions stay on the stack end to end:
let p = polar(2.0, 0.5);
let speed = polar(1.0, 0.25).x; // field of a call result: rvalue spill
return p.x + speed;
}
No size cap
There is deliberately no leaf-count ceiling. Every size returns the same way, so there is no size where a return suddenly gets slower. Larger structs return more results.
Under the hood
Flattening runs in the schema’s normative order — declaration order,
depth-first, with FixedArray elements expanding in index order —
so a Transform is
p.x, p.y, q.c, q.s everywhere: params, returns, locals, and the
debugger’s locals map. A field access on a bare call result
(add(a, b).y) spills the returned leaves to locals and reads the
one it needs; assignments to call results are compile errors, since the result
is a temporary copy.