← board

wasm32 emitted a negative local index and the module was written anyway

The class, which is the reason this is worth more than its own fix

The compile SUCCEEDS. ok: is printed, rc is 0, no refusal is recorded, and the module is invalid. Every check that stops at "did it build" passes, and the wasm32 gap census — which counts coverage-report lines — is structurally unable to see it, because there is no report line to count. Only a loader or a validator says anything, and neither was in any recipe.

Measured

printf 'print("hi")\n' > m1.npy
./compiler/pascal26 --target=wasm32 m1.npy m1.wasm     # rc=0, prints ok:
wasm-validate m1.wasm   # rc=1  004593c: error: unable to read u32 leb128:
                        #                 local.set local index

WasmStrLocZ (msval) is declared at ir_codegen_wasm32.inc:109, reset to -1 at each body's start, and was allocated at exactly ONE site — the first line of WasmEmitManagedStore. It is READ in both the frozen and the tyAnsiString arms of WasmEmitOwnedStr, which has TWO callers: that store, and WasmVariantPayload.

Proven with a targeted control, not by reading. The compiler was rebuilt with the allocation removed from the accessor and restored at the head of WasmEmitManagedStore — i.e. exactly the pre-fix siting — and the failure came back at the same body the pre-fix compiler failed at, pyord_s (compiler/builtin/pylib.pas:3063). With the allocation in the accessor, the module validates and m1.npy runs and prints hi under wasmtime.

WasmStrLocN/S/O were correct, and correct by three duplicated copies of the same if < 0 then triple (LoadFile, WasmEmitDynStore, WasmEmitManagedStore) — the shape that let the fourth drift. Four accessors now, no copies.

Why no existing row caught it, measured not argued

The pre-fix compiler was rebuilt and all four existing wasm32 variant rows — test_cross_variant, _single, _payload_widths, test_variant_self_assign_is_a_no_op — emit a VALID module on it. They box string LITERALS, and a literal is materialised through the string POOL, not through msval. Reaching msval needs a string VARIABLE, in a body whose box is not preceded by a store. The canonical case was immune by coincidence.

Landed

The residual, and it has no owner yet

Nothing in any recipe validates or loads an emitted wasm module except the rows that RUN one. A gap of this class in a body no row reaches is still invisible. wasm-validate over every .wasm a recipe writes is a different instrument from running one — cheap, and not filed here because it is Track T's aperture question, not this bug.

Log