Skip to content

A const array can grow: push/pop/splice on an annotated const - #387

Merged
ASDAlexander77 merged 1 commit into
mainfrom
const-array-binding-storage
Sep 28, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
const-array-binding-storage

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

const a: number[] = [1, 2]; a.push(3) failed to compile:

error: Can't get reference of the array, ensure const array is not used

The same happened with a const holding an array returned by a call (const c = make(); c.push(9)). A const binding only forbids reassigning the name, not changing the array, so both should work.

Cause: #344 gave storage only to an unannotated array literal (!ts.const_array<T,N>). A const already typed !ts.array<T> stayed a bare SSA value, and push, pop, unshift, splice and length = change the array through a reference to it.

Fix: a const of array type now gets storage, the same as let (processConstRef, MLIRGenImpl.h). Two cases are unchanged:

  • A ConstRef (an internal flag used by for-of) already aliases the storage it was read from.
  • A narrowed view (addSafeCastStatement, whose initializer is a SafeCastOp) is the narrowed variable seen through another type, not a new array. My first version gave it storage too, and that broke typeof u === "array" and if (results) narrowing: the release landed at function scope, outside the branch where the value is defined.

Test: 00const_array_push.ts, registered for compile, jit and the corpus. It covers an annotated local, a call result, and a module-level const, using push, pop, unshift, splice and length =. It fails on main with the error above.

Results: the suite passes locally, 3017/3017. The new test also runs clean under -mm=rc --verify-ownership.

Not changed: arrays are value-typed here. After let b = a; b.push(2), a.length is still the old length, and the same holds for const items = holder.items. That was true before this change, and it's a separate issue.

🤖 Generated with Claude Code

`const a: number[] = [1, 2]; a.push(3)` failed with "Can't get reference
of the array, ensure const array is not used", and so did a const holding
an array returned by a call. Only an unannotated array literal
(!ts.const_array) was given storage; a const already typed !ts.array<T>
stayed a bare SSA value, and push/pop/unshift/splice/length= change the
array through a reference to it.

A const of array type now gets storage like `let`. Two cases keep what
they had: a ConstRef, which already aliases the storage it was read from,
and a narrowed view (addSafeCastStatement), which is the narrowed variable
seen through another type - giving it storage put a release at function
scope for a value defined inside the branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 0b06c56 into main Sep 28, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the const-array-binding-storage branch September 28, 2026 13:07
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