Skip to content

Widen an s32[] to number[] from what it holds, not from its literal - #354

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-array-widen-stale-literal
Sep 23, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-array-widen-stale-literal

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

Reading an s32[] as number[] silently dropped every change made since the declaration:

let a = [1, 2]; a.push(3); a[0] = 9;
const b: number[] = a;   // was [1, 2], now [9, 2, 3]

castConstArrayToArray (from #344) followed an array load back to the variable's initializer, or the global's initializer region, and rebuilt the array from that literal. Push, element writes, reassignment and sort were all lost. The module-level path had the same fault.

  • The trace-back is gone. The new castArrayElementwise handles an array whose elements are integers or floats (nested arrays included) when it is read as number[], a union holding a number, or any[]. It builds a new array of the same length at run time, and a synthesized loop does the element cast as each element is assigned. A real const_array literal still takes the constant path. An s32[] parameter read as number[] now compiles too; it used to be an error.
  • NewArray allocates zeroed storage. Under -mm=rc the first store into an element releases what the slot held, so malloc's leftovers were released as references. The conversion loop into any[], (number | string)[] or number[][] crashed on its first element. gc was unaffected because GC_malloc zeroes. This also covers new Array(n) under rc.

The widened array is a copy, so a later write to one is not seen through the other. Converting a literal already worked this way.

Verification

  • New test 00array_widen_elements_now.ts (compile, jit, and the rc/none corpus). It covers let, const, reassignment, a parameter, nested arrays, union and any targets, an empty array, copy semantics, and module-level arrays. On main it fails to compile, and the stale-value cases print the old literal.
  • Windows Release: 2853/2853 passed.
  • Linux (WSL): 2847/2847 passed.
  • DefaultLib release jit: 155/155.

Found while testing, not changed here: const nested = [[1, 2], [3]]; nested[1].push(4) crashes in GC_realloc, on main as well.

🤖 Generated with Claude Code

castConstArrayToArray traced an array load back to the variable's
initializer (or the global's initializer region) and rebuilt the array
from that literal. Every push, element write, reassignment or sort made
since the declaration was silently lost:

    let a = [1, 2]; a.push(3); a[0] = 9;
    const b: number[] = a;   // b was [1, 2]

The trace-back is gone. An array whose elements are integers or floats
(nested arrays too) read as number[], a union holding a number, or any[]
is now converted at run time by castArrayElementwise: a new array of the
same length, filled by a synthesized loop whose element assignment does
the cast. A real const_array literal still takes the constant path. This
also makes a plain s32[] parameter widen, which was a compile error.

NewArray now allocates zeroed storage. Under -mm=rc the first store into
an element releases what the slot held, and malloc's leftovers were
released as references - the conversion loop into any[] or
(number | string)[] crashed on the first element. gc was unaffected
because GC_malloc zeroes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 2874f72 into main Sep 23, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-array-widen-stale-literal branch September 23, 2026 22:58
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.

1 participant