Commit Graph
8 Commits
Author SHA1 Message Date
Chris Tate 924d5b96de Merge main 2026-10-20 08:39:29 -05:00
Chris Tate 8d4737dfdb Merge main 2026-10-19 20:27:03 -05:00
Chris Tate 53af3dc930 Compare strings in UTF-16 code-unit order
Relational string operators (`<`, `<=`, `>`, `>=`) compared the UTF-8
bytes, which is code-point order. Node compares UTF-16 code units, so the
results differed for characters above U+FFFF against U+E000..U+FFFF and
for lone surrogates against surrogate pairs. The default `sort()`
comparator, `util.inspect` with `sorted: true` and dynamic comparisons
already used UTF-16 order; the default comparator of a dynamic array's
`sort()` did not.

Typed comparisons and the dynamic array sort now use `scr_str_cmp_u16`,
and the IR's `strCmp` node no longer has a `utf16` flag. The two orders
agree unless both of the first differing bytes are at least 0xED, so:

- the inline wrapper decides differing first words, or last words of
  equal lengths up to 16 bytes, in byte order when neither word has a
  byte of at least 0xED (a SWAR test). Other differing words, and the
  first bytes of shorter strings, decide by their first differing byte:
  in byte order when one of the two is below 0xED, and by plane when both
  are lead bytes from 0xEE to 0xF4 (a supplementary character orders
  below U+E000..U+FFFF);
- otherwise a proven-ASCII string on either side compares with `memcmp`
  (`scr_str_cmp`), and other pairs call `scr_str_cmp_u16_bytes`. It finds
  the first differing byte with words up to byte 32, then with 32-byte
  SSE2 or NEON blocks where the target has them. The call is marked as
  only reading its arguments, so repeated comparisons of one pair merge.
- `scr_str_cmp_u16` itself takes the same `memcmp` path for proven-ASCII
  strings.

The native compiler sorts record fields with `<` and checks the order
with `sort()`, so it reported SC9001 for 5888-inspect-sorted.ts, whose
object has the keys "\u{1F600}" and "\uffff". 5888 runs in the
native-frontend self-host check. Adds corpus 6023.
2026-10-19 18:10:54 -05:00
Chris Tate 302f0f9fa9 Compile the remaining corpus programs with the native compiler
The native compiler failed on corpus programs that the Node-hosted
compiler compiles. Four compiler causes:

- A record that width-lifts into several members of a discriminated
  union had no conversion plan. As a field of a larger record, that
  removed the outer member it belonged to, and the outer record could
  then lift into the only member left: a `recordSet` statement built with
  a nested `recordGet` literal became a `break` statement, and a switch
  on `kind` read it with the `recordSet` layout. The native compiler
  stopped at the narrowing guard on 5585-record-results-inlined.ts and
  similar programs. Such a record now converts through the discriminant:
  each member it width-lifts into is a route, and the value's
  discriminant selects the member at run time, as same-layout members
  already do.
- Three compiler paths asserted a value that can be null, and the native
  compiler threw "null is not representable in the target union":
  stableCreateRequireBindingReason7 (`cache.get(decl)!` from a cache that
  stores null for stable createRequire bindings; 2631-create-require,
  2932-create-require-program), the Int32 contract check (`catchLocalId!`
  for a bindingless `catch {}`; 4970-int32-call-abi) and
  StringIndexBounds.binding (a memo that stores null). The caches are
  read without assertions and the catch test checks for null.
- scriptc adopts alwaysStrict, esModuleInterop and
  allowSyntheticDefaultImports from a project's tsconfig, but the TS 7
  SDK schema no longer declares them. A native compiler keeps only
  declared fields when it builds or parses a typed record, so these
  options were dropped, and the default-import check refused
  `import fs from "node:fs"` (SC1012, 2391-default-builtin-imports and the
  3055 to 3057 module-setting variants). The options a project reports
  are parsed with these fields declared, and Ts7CompilerOptions uses the
  same type.
- An object-literal method kept its inferred return type where an arrow
  function or function expression takes the return type of its
  contextual signature. For a method that returns an array of statement
  literals, a `varDecl` literal with `init: null` was converted to the
  member whose `init` holds only expressions, and the conversion threw
  (Array.fromAsync's array loop: 5854-array-from-async,
  5855-array-from-async-js). A synchronous object-literal method now
  takes its slot from the property of the same name on the literal's
  contextual type and adopts its return the same way.

Adds corpus 6022 and 6024. 2391, 2631, 3055, 4970, 5585 and 5854 run in
the native-frontend self-host check, which now also links the dyn-async
unit when the program uses it, as the production build does.
2026-10-19 17:02:31 -05:00
Malte UblandClaude Opus 5.5 60e0b246e7 Choose record key order from the orders values are built in
Each record shape's enumeration table now follows the key order its object
literals, spreads, rest bindings and record copies create, and appends
optional keys assigned after construction. Shapes whose values are built
in more than one order keep their declaration order and SC6002.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 05:34:30 -07:00
Malte UblandClaude Opus 5.5 f79ed58138 Link the runtime units of lowered IR as compile() does in the harnesses
The self-hosting harnesses link the IR they lower through compileC. Most
hand-picked a subset of runtime feature flags, which lacked dynTier: a
sanitized build compiles exactly the listed runtime sources, so programs
with jsval operations failed to link scr_js_value.c's entry points, and
harnesses passing no flags at all failed in both lanes.

Every such site now spreads moduleLinkFeatures (tests/harness/
link-features.ts), which is executableLinkFeatures, the selection
compile() uses, so a runtime unit added later cannot drift out of the
harness builds. moduleUsesDynTier joins the IR facade's unit gates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:12:42 -07:00
Chris Tate 201857eb6c Read the class base after the callable-base case in super calls
superCallStmt asserted the class base before checking for a callable
base. A class that extends an ordinary function constructor has no base
class info, so the assertion held null. Node ignores the unused value;
the native compiler throws the TypeError of a non-null assertion that
held null, and failed to compile programs with such classes (for example
the Effect stream fixtures). The base is now read after that case
returns.
2026-10-18 07:54:21 -05:00
Chris Tate 9acf71cfe3 Split the native frontend test across three files
self-hosting-native-frontend.test.ts was one test of 577 s (plain) and 607 s
(sanitized) in the cost table, and 748 s on san 1/20 in run 38835202089; once
the other long files were spread, it alone bounded the Linux test lane. It
builds the compiler frontend into a native executable, then checks 17 corpus
programs and four other cases with it. Locally the build took about 189 s and
the checks 297 s (14-20 s per program).

The build and the checks move to tests/harness/native-frontend.ts without
changes to what they check; useNativeFrontend builds the executable in a
beforeAll of the calling suite. self-hosting-native-frontend.test.ts,
-programs.test.ts and -checks.test.ts each build once and run programs 1-6,
7-12, and 13-17 plus the four other checks, each check as its own test. The
diagnostics, library contract and FFI checks use project directories of their
own instead of rewriting one shared main.ts.

scripts/ci-test-costs.json replaces the old file's entry with estimates for
the three files (the build share of its measured cost plus each file's share of
the checks); the next refresh measures them, since vitest's file time includes
beforeAll. The largest side-queue share per shard at 20 shards goes from 580 to
367 s (plain) and from 608 to 432 s (sanitized), for two more builds per flavor.
2026-10-15 23:46:14 -05:00