Skip to content

web: writes to module-level array elements inside functions are dropped (reads return initializer) #5016

Description

@proggeramlug

Summary

On the web target, a write to a module-level array element performed inside a function is silently dropped — subsequent reads return the array's initializer. The same code is correct on native. This makes any Perry program that keeps mutable state in module-level arrays (the idiom recommended in our own pitfalls doc to avoid module-let issues) non-functional on web: menus don't advance, game/UI state never updates, etc.

Discovered shipping Bloom Jump to web — the title menu renders but never responds: the nav handler runs (its playSound fires) and executes GS[GI_SEL] += 1, but the draw function still reads GS[GI_SEL] as its initial value, so the selection never moves and no screen can be entered.

Minimal repro

const A: number[] = [0.0];
function bump(n: number): void { A[0] = A[0] + n; }   // write module array inside a fn
function readA(): number { return A[0]; }
const step: number = Date.now() > 0.0 ? 5.0 : 1.0;    // runtime value → defeats const-folding
bump(step);
console.log("fnRead=" + readA().toString() + " topRead=" + A[0].toString());
Target Output
native (perry compile) fnRead=5 topRead=5
web (perry compile --target web) fnRead=0 topRead=0

The write inside bump() never lands; both the in-function read and the top-level read see the initializer 0.

Characterization

  • Non-foldable: the increment is derived from Date.now(), so this is not the optimizer constant-folding a trivially-known array — the runtime write is genuinely lost.
  • Top-level writes affected too: a variant doing A[0] = 99 then reading at top level also returns the initializer on web (native returns 99).
  • Write-through-parameter affected: fn(arr) { arr[0] = ... } called with the module array likewise no-ops on web.
  • Reads of module arrays that are not written (pure constants) work fine — it's specifically the mutation that doesn't persist.

Environment

  • Reproduced locally with perry 0.5.1125.
  • Symptom present in 0.5.1159 as well (the deployed Bloom Jump web build, compiled by 0.5.1159, exhibits the dead-menu behavior).

Impact

This is the documented-and-recommended state pattern (const S = [0.0, 0.0, ...]; mutate S[i]) — our own Perry-pitfalls guidance steers games toward it because module-let mutation is unreliable. With module-array writes also dropped on web, there's no working module-scope mutable-state pattern on the web target.

Repro files (native + web HTML) available; happy to attach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions