Skip to content

Pushing onto an array inside a constant tuple literal crashes (realloc of static data) #479

Closed

Description

@ASDAlexander77

Pushing onto an array that sits inside a constant tuple literal crashes, because the array's data is the literal's static, read-only data.

function main() {
    const st = [1, [6, 7]];
    const x = st[1];
    x.push(8);
    print(x.length);
    print("done.");
}

Expected: 3, then done.

Actual, tslang --emit=jit --no-default-lib (main f013674):

model output
gc access violation in GC_realloc (exit 139)
rc exits with 127 and prints nothing
none exits with 127 and prints nothing

The same happens with const st = [1, [6, 7]] at module level. These cases work:

  • const aa: number[][] = [[1], [2, 3]]; aa[1].push(4)
  • const flat: number[] = [6, 7]; flat.push(8)

Cause: an array inside a constant tuple is emitted through LLVMCodeHelper::getReadOnlyRTArray, which points the array at the literal's global data (getOrCreateGlobalArray, a constant global). push then calls realloc on that pointer. A top-level array literal is copied into a heap block when it becomes a T[] (CastLogicHelper::castToArrayType); an array nested in a tuple is not.

After #453 (arrays as references, branch array-reference-switch), the array also has a static header in a constant global. A push would then write into that global as well.

Found while reviewing #453.

🤖 Generated with Claude Code

Activity

  1. ASDAlexander77 commented on Oct 4, 2026

    @ASDAlexander77
    OwnerAuthor

    Update after #480 and #482: the static case got quieter, not fixed.

    An array inside an unannotated constant tuple literal is now a header in a constant global (ah_*) over the literal's constant data.

    • push still crashes.
    • With Arrays grow by doubling (#453 step 3) #482, pop, shift, splice and length = no longer reallocate. They store into that constant global and its data, which is undefined behaviour, and the optimiser folds the reads back to the initializer, so they give silently wrong results:
    function main() {
        const st = [1, [6, 7]];
        const x = st[1];
        const p = x.pop();
        print(p, x.length, x[0], x[1]);
        x.length = 0;
        print(x.length);
        print("done.");
    }

    With #482's head (c7ff7fc, --emit=jit --no-default-lib), gc, rc and none all print 7 2 6 7, then 2. Expected: 7 1 6, then 0.

    The fix is unchanged: the nested array must become a heap copy when the tuple element is read as a T[], as castToArrayType does for a top-level literal. ArrayLayout::ensureCapacity's comment now points here.

    🤖 Generated with Claude Code

  2. added a commit that references this issue on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions