Skip to content

Keep holes when a holey array widens into union elements - #1478

Open
ctate wants to merge 2 commits into
mainfrom
ctate/keep-holes-when-3b7f37
Open

ctate wants to merge 2 commits into
mainfrom
ctate/keep-holes-when-3b7f37

Conversation

@ctate

@ctate ctate commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator
  • Converting a holey string[] into (string | number)[] storage (a spread, a field, an argument, a return value or an assignment) no longer aborts with "array index N is a hole"; the holes read as undefined, as they do in Node.
  • The conversion copy keeps holes and present undefined values at their indexes, and the element analysis records them under the target element type, so reads, iteration and the find family of the converted array handle them.
  • Adds corpus program 5687; dense arrays convert as before.

Converting a string[] with holes into (string | number)[] storage (a
spread, a field, an argument, a return value or an assignment of a
holey array) aborted with "array index N is a hole", where Node reads
the holes as undefined.

The width conversion copy now keeps each slot's state: present elements
convert, holes stay holes and a present undefined stays undefined at the
same index, as the same JS array holds them. The element analysis
records the copy's holes and undefined values under the target element
ABI wherever an array value is stored at a contextual array type with
another element ABI, so reads and iteration of the converted array
handle them.
@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scriptc Ready Ready Preview, v0 Oct 20, 2026 8:03am UTC

Comment on lines +5748 to +5751
const target = contextual ? this.mapTypeOf(contextual) : null;
if (target?.kind !== "array") return [];
const key = typeKey(target.elem);
return own.includes(key) ? [] : [key];

@vercel vercel Bot Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing a holey array to an optional or nullable widened-array parameter still drops its element-state facts because the contextual type is a union, leaving find-family reads trapping on the preserved holes.

Fix on Vercel

Comment on lines +120 to +129
if (ts.isVariableDeclaration(node) || ts.isPropertyAssignment(node)) convert(node.initializer);
else if (ts.isShorthandPropertyAssignment(node) && ts.isIdentifier(node.name))
convert(node.name);
else if (ts.isReturnStatement(node)) convert(node.expression);
else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken)
convert(node.right);
else if (ts.isCallExpression(node) || ts.isNewExpression(node))
for (const arg of node.arguments?.nodes ?? []) convert(arg);
return changed;
}

@vercel vercel Bot Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Width conversions in expression-bodied arrows, class field initializers, array elements, and type assertions preserve holes without recording them, so find-family reads still use trapping arrayGet operations.

Fix on Vercel

… copies

The width-conversion scan typed every stored value on its own and asked
for its contextual type, one checker query per site outside the batched
prefetch. It also carried facts into any contextual array type with
another element ABI, so the facts an element ABI shares with every
array of that ABI widened arrays the copy never reached (the compiler's
own IrParam arrays among them).

The values the scan compares, and the receivers of their property and
element reads, are now typed in the file's batch. The contextual type is
asked for only when the value can hold holes or undefined values; an
array a call or literal builds counts only its own facts. Facts are
carried only where the conversion copies the elements into an array of
union elements, whose reads otherwise trap on a hole.
Comment on lines +119 to +126
const built =
ts.isCallExpression(peel(value)) ||
ts.isNewExpression(peel(value)) ||
ts.isArrayLiteralExpression(peel(value));
this.ownFactsOnly = built;
let contents: Contents | null;
try {
contents = this.contents(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Widening a holey array returned directly by a function call now discards its hole facts because ownFactsOnly suppresses the source ABI facts, so subsequent find-family reads still trap.

Fix on Vercel

This branch was successfully deployed

1 active deployment
Preview — e40ffc39 Deployed Oct 20, 2026 by vercel[bot]
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