Skip to content

Bake mixed-scalar JSON tables and destructure unions of arrays and tuples - #1455

Merged
ctate merged 5 commits into
mainfrom
opt/bc-mixed-tables
Oct 20, 2026
Merged

ctate merged 5 commits into
mainfrom
opt/bc-mixed-tables

Conversation

@cramforce

@cramforce cramforce commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Two shapes that keep common npm packages and service code from compiling.

JSON tables whose rows mix strings and numbers

// a character-set table: [["8140", 12288, 65292], ...]
const table = require("./table.json");
// a code-point range map: [[[0, 44], "disallowed"], [[65, 65], "mapped", [97]], ...]
const ranges = require("./ranges.json");

JSON documents a CommonJS package requires, and JSON modules a TypeScript file imports, bake into the binary as literals. The baker accepted numbers, strings, booleans, arrays and records, but not unions, so rows typed (string | number)[] or (string | number[])[] were refused, with a misleading "elements have no array representation" message. iconv-lite's encoding tables and tr46's mapping table have exactly this shape.

Literals now bake union-typed elements: each JSON value takes the first arm of the union it fits, by its JSON kind and, for arrays and records, its contents (records are exact, as before). null values and null-valued fields bake as null.

Array destructuring of unions of arrays and tuples

const [match, head, word] = /(\w*)-(\w+)/.exec(text) || ["", "", ""];
function pick(flag: boolean): [string, number] | string[] { ... }
const [p, q = 0] = pick(true);

re.exec(s) || ["", "", ""] has the type RegExpExecArray | [string, string, string], a union of an array and a tuple, which array destructuring refused (only unions of arrays compiled). Each position now reads the arm the value holds: a tuple arm answers its field, or undefined past its end; an array arm its element, or undefined past its end. A string arm (re.exec(s) || "") answers its code point at the position, as the string iterator does. Defaults fire exactly where JavaScript's do. Rest elements over such unions stay refused.

Tuple unions and discriminants

A union of tuple types ([string, boolean] | [string, boolean, number]) picked the checker's length literal as its discriminant, but tuple shapes keep only their positions, so the validator rejected the union with an internal error once a program materialized it (which the destructuring above now does). Tuple unions no longer get a discriminant.

Evidence

  • Corpus: 6410-json-mixed-scalar-tables (two packages modeled on a character-set table and a code-point range map, required at load and inside a function, plus an ES module JSON import) and 6411-union-array-tuple-destructuring.ts. Both match Node byte for byte in the plain and sanitized lanes.
  • Diagnostics: the json-shape fixture's document compiled once its null field baked, so it now uses a value no literal can spell (1e400, which parses to Infinity) and still reports at the import. diagnostics.test.ts: 199/199.
  • Zero-cost gate against main bf2afee on the compiler tip: PASS, with all 2501 bench, self and corpus programs identical (IR, LLVM and release executables). The bench geomean changes by -0.06% and self-compile time by +0.01%, both within tolerance. An earlier run on the first commit also passed. The self-hosted compiler IR build exits 0 with no diagnostics.
  • pnpm lint and pnpm format:check are clean, and pnpm test:ts7 --baselines-only passes, with 2 new baselines and no existing ones changed.
  • No compatibility rows change.

🤖 Generated with Claude Code

JSON documents whose rows mix strings and numbers (`(string | number)[][]`,
`(string | number[])[][]`, the character-set and code-point tables of
iconv-lite and tr46) now bake: comptime literals take union-typed elements
and pick each value's arm from its JSON kind and contents.

Array destructuring of a union of array and tuple types
(`const [m, a, b] = re.exec(s) || ["", "", ""]`) reads each position from
the arm the value holds: a tuple arm answers its field or undefined past
its end, an array arm its element or undefined past its end.

Unions of tuple types no longer pick the checker's `length` literal as a
discriminant: tuple shapes have no such slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@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 4:40pm UTC

`const [hit] = re.exec(s) || ""` destructures `RegExpExecArray | string`:
the string arm answers its code point at each position, or undefined past
its end, as the string iterator does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/compiler/src/frontend/lowering/lower-comptime.ts Outdated
Comment thread packages/compiler/src/frontend/lowering/lower-comptime.ts
…bake

A null-valued JSON field bakes as null, so the fixture's document now
compiles. It keeps its purpose with a value no literal spells (1e400 parses
to Infinity), and the mixed-table corpus program covers null elements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comptime record value whose union has a discriminant takes the arm its
discriminant field names, so a value that fits two arms with overlapping
shapes gets that arm's tag and layout instead of the first arm it fits.
comptimeBakeable reports a record type reachable from its own fields as
not bakeable instead of recursing without end, so such a comptime result
reports SC1090, and the message names unions and that limit.
The bakeability check tracks the unions it is inside as well as the
records, so a union reachable from itself through an array
(`type Tree = number | Tree[]`) reports SC1090 instead of recursing without
end. The message names both recursive records and recursive unions.
@ctate
ctate merged commit 9cfadc5 into main Oct 20, 2026
53 of 65 checks passed
@ctate
ctate deleted the opt/bc-mixed-tables branch October 20, 2026 16:42

This branch was successfully deployed

1 active deployment
Preview — 437dc116 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.

2 participants