Skip to content

interpreter: accept folded GC allocator global initializers in the WAT decoder - #148

Merged
mfornet merged 1 commit into
mainfrom
claude/issue-109-resolution-e0797e
Jul 15, 2026
Merged

interpreter: accept folded GC allocator global initializers in the WAT decoder#148
mfornet merged 1 commit into
mainfrom
claude/issue-109-resolution-e0797e

Conversation

@mfornet

@mfornet mfornet commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Fixes #109.

Problem

A global initialized with a plain folded GC allocation was rejected:

(global $g (ref $s) (struct.new $s (i32.const 100)))   ;; error: global init expression must be i32.const or i64.const
(global $g (ref $s) (struct.new $s (i32.add (i32.const 50) (i32.const 50))))  ;; accepted, evaluates to 100

The global-decl parser detected GC heap-allocating initializers (struct.new / array.new*) by scanning only the top-level atoms of the initializer sexpr — which matches the flat form emitted by wasm-tools print but not the folded leaf form, where the allocator atom sits inside a (...) list. The folded form fell through to the literal-folding shape match and errored. The arithmetic-wrapped variant was accepted only because the extended-const scan does recurse into folded operands and routed it to the const-expr path.

Fix

Generalize the existing recursive sexpr scan into initExprMentions (p : String → Bool) and use it for both:

  • initExprNeedsEval (extended-const / global.get) — unchanged behaviour, now defined via the shared scanner, and
  • a new initExprAllocates (struct.new, struct.new_default, array.new, array.new_default, array.new_fixed, array.new_data, array.new_elem) that replaces the top-level-only check, so GC allocators are detected in both the flat and the folded form and kept as a const-expr program for Module.runConstGlobals.

Tests

New regression theorems in Interpreter/Wasm/Examples/GlobalInitExpr.lean using the exact repro modules from the issue:

  • leaf_struct_new_keeps_initExpr — the leaf form decodes (no error) and keeps its initExpr,
  • leaf_struct_new_returns_100 / arith_struct_new_returns_100 — end-to-end, struct.get on the global returns 100 for both forms.

lake build of the interpreter package passes (3056 jobs, includes the native_decide checks above).

🤖 Generated with Claude Code

…T decoder

The global-decl parser detected GC heap-allocating initializers
(struct.new / array.new*) by scanning only the top-level atoms of the
initializer sexpr, so the folded leaf form
(struct.new $s (i32.const 100)) slipped past the check, fell through to
the literal-folding shape match, and was rejected with 'global init
expression must be i32.const or i64.const'. The same allocation wrapped
in extended-const arithmetic was accepted, because the extended-const
scan does recurse into folded operands.

Generalize the recursive sexpr scan into initExprMentions and use it for
both the extended-const detection and a new initExprAllocates predicate,
so GC allocators are detected in both the flat form emitted by
wasm-tools print and the folded form.

Adds regression theorems in Examples/GlobalInitExpr exercising the exact
repro from the issue: the leaf and arithmetic-wrapped struct.new
initializers both decode and evaluate to a struct whose field reads back
as 100.

Fixes #109

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Verifier report preview: https://talos-verifier-report-pr-148.vercel.app

(This URL is stable for this PR — it always points to the latest build of 182e808.)

@mfornet
mfornet merged commit c4e2b38 into main Jul 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Global init rejects a plain struct.new but accepts struct.new wrapped in extended-const arithmetic

1 participant