Commit Graph
5285 Commits
Author SHA1 Message Date
Chris Tate 828ea84dcc Relocate the prebuilt lib diagnostics path when installing the native command (#2300) 2026-10-25 22:12:41 -05:00
Chris Tate 7741f24cc8 Reserve the rc word's high half for layout ids (#2299) 2026-10-25 22:10:20 -05:00
Chris Tate 00b58e5c7a Map UTF-16 indexes of long non-ASCII strings in constant time, and quick-check normalize (#2294) 2026-10-25 21:59:27 -05:00
Chris Tate 161d15538e Map wide shape-union records to their arm through one table per union (#2298) 2026-10-25 21:53:56 -05:00
Chris Tate 23deb595ac Relocate the prebuilt lib diagnostics path when installing the native command
The native toolchain manifest's lib_diagnostics path is relative to the
platform package's dist/bin. installNativeCli copies the manifest into the
scriptc package's bin and relocates each asset path, but lib_diagnostics
was not in the list, so it pointed at a missing file under the scriptc
package and npm installs did not use the prebuilt lib diagnostics. It is
now relocated with the other assets.
2026-10-25 21:41:36 -05:00
Malte UblandClaude Opus 5.5 19f51465f3 Format the class id bound check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:37:58 -07:00
Chris Tate cc2266ab1d Add --cpu-dispatch for x86-64 executables (#2288) 2026-10-25 21:36:00 -05:00
Chris Tate 0abe5063ff Wait for every released package to resolve on npm before the GitHub release (#2281) 2026-10-25 21:28:51 -05:00
Chris Tate 018dbb766c Compute Math functions as V8 does and link libm only when a program needs it (#2287) 2026-10-25 21:26:20 -05:00
Chris Tate a67f7d13ea Map wide shape-union records to their arm through one table per union
A shape union's tag helper switched on the record's shape number. Inlined
into a caller's branch on the arm, LLVM folded the two into one jump table
per site, spanning the arms' shape numbers. In the native compiler, whose
IR unions span about 112 shape numbers, that came to about 27,600 tables
and 9 MB of read-only data, most of which a cold build pages in.

A union whose arms span more than 32 shape numbers now maps the shape
number to its arm through one constant table, indexed by the shape number
minus the arms' lowest. An entry is a byte, or a 16-bit word for a union
of 255 arms or more; the all-ones entry names no arm. Callers branch on
the small arm tag. Narrower unions keep the switch, whose folded jump
tables stay small and dispatch in one jump.
2026-10-25 21:23:47 -05:00
Chris Tate 59e78f298a Name the package declaration and its patch when a declared-type check fails (#2268) 2026-10-25 21:20:34 -05:00
Chris Tate 91d22a5c8f Cut repeated work in the lowering prelude's whole-program passes (#2291) 2026-10-25 21:14:27 -05:00
Chris Tate 43a8c6d8cc Link the Linux release helpers against LLVM built with ThinLTO and a helper profile (#2282) 2026-10-25 21:09:17 -05:00
Chris Tate 0b483e8650 Map UTF-16 indexes of long non-ASCII strings in constant time and quick-check normalize
- Index walks (slice, substring, substr, startsWith/endsWith with a
  position, lastIndexOf results) on a non-ASCII receiver of 64 KiB or
  more: once they would have paid for one pass over it, the receiver's
  index entry keeps every unit's byte offset (a 32-bit base per 64 units
  and one byte per unit), and indexes map with two loads. Before that, a
  walk within 64 units of the hot cursor starts from it, without the index
  search. The index entry stays 128 bytes on 64-bit targets.
- lastIndexOf on non-ASCII receivers tests the needle's first byte before
  comparing, as the ASCII path does.
- normalize: a quick check (UAX #15) returns the receiver when every code
  point is inert for the form: unchanged when normalized alone, combining
  class 0, and, in the ranges checked, never the second element of a
  composition. Other text normalizes segment by segment, each starting at
  a starter, so runs of inert code points copy as they are and a lone
  precomposed letter's result is kept per form. The per-code-point facts
  come from unicode_normalize, 256 code points at a time.
2026-10-25 21:04:29 -05:00
Chris Tate d21f0d7c8b Pin the identity of native values seen as untyped values (#2290) 2026-10-25 21:04:04 -05:00
Chris Tate 794a8cf8a8 Compute products of typed-array reads in 32 bits under bitwise operators (#2289) 2026-10-25 20:58:49 -05:00
Chris Tate 10a7e1a553 Cut repeated work in the lowering prelude's whole-program passes
collectProgram and the runtime-optional array analysis walk every file of
the program before any function body is lowered. This change removes work
they repeated and moves single checker requests into existing batches. The
emitted IR is unchanged.

- classReferences reads a per-file index of identifiers by text, built in
  one walk and kept on ProgramAnalysis, instead of walking every file once
  per class.
- widenedType reads its memo before the syntactic position test, and
  memoizes nodes without a wide position.
- isJsSourceFileName tests the extensions with endsWith.
- NanCodedReads groups the identifiers of a name by resolved symbol once,
  instead of resolving every same-named identifier for every binding.
- Each function's return scan in the optional-array fixpoint is a
  SiteDependencies site, so incremental passes rescan only the returns
  whose facts changed.
- The fixpoint's site batch also types the receivers widenedType reads and
  for-of iterables, and collectDynamicViews prefetches the values it types
  in one batch before its walk.
2026-10-25 20:42:25 -05:00
Malte UblandClaude Opus 5.5 1493088594 Pin the identity of native values seen as untyped values
A native record, array or class instance boxed as unknown or any is the same
object: every box of it is one value, untyped writes, optional fields,
deletes and expandos show through typed reads and other boxes, nested
records and arrays are the typed program's own, and the value keys Map, Set
and WeakMap entries as itself. Main already behaves this way since records
and arrays box as live references; this corpus program pins it for the
single object model's later stages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:33:21 -07:00
Chris Tate 8981b5c0a2 Resolve computed createRequire specifiers at run time (#2079) 2026-10-25 20:29:51 -05:00
Chris Tate c820a6c57f Compute products of typed-array reads in 32 bits under bitwise operators
A sum of products of integer typed-array reads that only reaches a
bitwise operator, such as `(p[j] * 77 + p[j + 1] * 150) >> 8`, now adds
in 32-bit integers like a sum of the reads themselves: a product whose
every result is a safe integer, and never -0, is an exact integer or NaN.
A NaN-coded read passes its validity bit up through those sums, so the
integer result selects 0 for a missed read without a floating compare,
and a loop over such sums vectorizes without converting to doubles.
2026-10-25 20:29:18 -05:00
Chris Tate 26159b264e Ship the lib diagnostics answers with the native distribution (#2285) 2026-10-25 20:23:14 -05:00
Chris Tate 8e4837f94a Run untyped pipeline function stages and later iterable stages (#2276) 2026-10-25 20:17:26 -05:00
Chris Tate fe48f8e69e Type the typed-arguments parameter list as IrParam[] (#2283) 2026-10-25 20:05:13 -05:00
Chris Tate 07c5e79ffd Add --cpu-dispatch: x86-64-v3 copies of the program's code chosen at startup
With --cpu-dispatch, an x86-64 executable carries two copies of each
program function: the baseline code and a copy compiled for x86-64-v3.
main reads CPUID and XGETBV once. Calls between the program's functions
go to the caller's copy. A function that is also named outside the
copies (a function value, a call from main, a runtime callback or a
coroutine body) keeps a dispatcher under its own name, which tests the
level and tail-calls one copy. Functions without such a use get no
dispatcher, so their copies have the callers the original had and LLVM
inlines them the same way.

The executable still runs on any x86-64 CPU. SCRIPTC_CPU_LEVEL caps the
level at run time. The option is off by default, applies to executables
without a --target-cpu level, and is part of the executable cache key.
bench:runtime takes --baseline-cpu-dispatch and --candidate-cpu-dispatch,
and the LLVM differential suite runs each program at levels 0 and 3 with
SCRIPTC_CPU_DISPATCH=1.
2026-10-25 20:00:16 -05:00
Chris Tate b6a1058f24 Fuse multiply-adds in the Math port on macOS arm64, as Node's build does
Node for macOS on arm64 is compiled by clang, which contracts a multiply
and an add within one expression into a fused multiply-add. Its sin, cos,
tan, asin, acos, atan, sinh, asinh, acosh, atanh, exp, expm1, log, log1p
and log10 differ from the unfused results in the last bit for some inputs,
so corpus 7717 and 7718 failed on macOS. scr_ieee754.c now sets
FP_CONTRACT ON for Apple arm64 targets. Other targets keep it off and
compile to the same code as before.
2026-10-25 19:55:14 -05:00
Chris Tate dd418b067a Compute Math functions as V8 does and link libm only when a program needs it 2026-10-25 19:48:38 -05:00
Chris Tate 78f9d0c719 Address review feedback 2026-10-25 19:42:02 -05:00
Chris Tate c022c08dd1 Add scriptc types patch and scriptc types list (#2267) 2026-10-25 19:28:13 -05:00
Chris Tate d9bf6c59d2 Ship the lib diagnostics answers with the native distribution
A build with an empty cache type-checked scriptc's own declaration files,
the TypeScript libraries and scriptc's declarations, before reusing the
answer in later builds. That check was 18 ms of a 105 ms first build of
hello world.

- build-native-cli.mts has the built compiler build a module without
  imports and a module with a type error, each in an empty cache, and
  records the content keys of the declaration file sets the checker
  reported no diagnostics in: the checker release, the compiler options,
  and each file's name and sha256. They ship in
  lib/declarations/lib-diagnostics.json, named by the toolchain manifest's
  lib_diagnostics.
- A build whose cache has no answer computes the files' content key and,
  when the shipped answers hold it, uses no diagnostics for those files
  and stores that answer in the cache. The shipped answers apply only
  while the checker is the distribution's own tsc-ts and its package
  version is the release they were recorded with; a changed file has
  another key.
- A compiler the build host cannot run ships without them.
2026-10-25 19:24:27 -05:00
Chris Tate ebf36f94a1 Type the typed-arguments parameter list as IrParam[]
declareTypedArguments appends the hidden positional slots of a
typed-arguments function expression to the parameter list that
declareParams returns. Its parameter was typed
`{ localId: string; name: string; type: IrType }[]`, while the list is an
`IrParam[]` (which also declares `owned?: true`). The native compiler
passes an array whose element type has another layout as a converted copy,
so the slots were appended to the copy and the lifted function kept no
parameters. Building tests/corpus/6380 to 6384 (variadic wrappers that
forward `arguments`) with the native compiler then stopped with SC9001
"closure ...: func type does not match target signature" in 6380, 6381,
6382 and 6384.

The parameter is now typed `IrParam[]`, the type of the list the caller
passes, and the native compiler builds all five programs with output that
matches Node.
2026-10-25 19:12:43 -05:00
Chris Tate 58a64ffb36 Discard the result of void generic methods called through class-backed interfaces (#2275) 2026-10-25 18:56:01 -05:00
Chris Tate 5dfa62b9fc Merge main 2026-10-25 18:54:10 -05:00
Chris Tate 681ac6ee15 Quote declaration-derived arguments in suggested type-patch commands 2026-10-25 18:53:50 -05:00
Chris Tate 4c3a1d9c98 Link the Linux release helpers against LLVM built with ThinLTO and a helper profile
The Linux helpers linked LLVM 22.1.8's release archive. The helper's code
generation is LLVM's code, so building that LLVM from its release sources
with ThinLTO and with a profile of the helper's own work makes code
generation 5-9% faster than the helper as released, with identical objects.

- native/llvm-codegen/llvm.profdata.gz is the profile, collected by
  scripts/refresh-llvm-profile.mjs: it builds LLVM with IR instrumentation
  and an instrumented helper, and runs that helper on tsc-ts, five Effect
  fixtures, the runtime benchmark workloads and every 50th corpus program
  (scripts/llvm-helper-inputs.mjs). It follows LLVM's sources, so it needs
  a refresh when LLVM is bumped.
- scripts/build-llvm-dist.mjs builds LLVM with that profile and ThinLTO,
  and packs it with a checksum. The llvm-dist workflow, run by hand, builds
  it for x64 and arm64, requires a helper linked against it to give the
  same objects as one linked against the release archive
  (scripts/check-llvm-dist.mjs), prints both helpers' times and the
  profile's mismatch count, and attaches the archive to the LLVM_DIST_TAG
  release of this repository.
- The release workflow's Linux jobs, and the wasm32-wasi job, which also
  uploads the x64 GNU helper, download that archive and build the helper
  with SCRIPTC_LLVM_THINLTO=1: clang, lld and ThinLTO, and the profile for
  the helper's own sources. Without the archive, or if that build fails,
  the helper links the release archive as before.
- The four Linux helper packages share their build in
  native/llvm-codegen/linux-helper.mjs. Without SCRIPTC_LLVM_THINLTO it
  builds the same executable as before.
2026-10-25 18:53:25 -05:00
Chris Tate 68e148add9 Wait for every released package to resolve on npm before the GitHub release
The release workflow publishes each package and moves on once npm accepts
the upload. A package whose processing on npm stalls was visible only on
npmjs.com.

A new npm-available job runs after "Publish to npm". It checks each package
of the release with `npm view <name>@<version> version --prefer-online`
every 2 minutes (scripts/wait-npm-available.mjs) until every package
resolves, and reports each package's time from upload to available in the
log and the job summary. When a package still does not resolve after 2
hours, the job fails and names each missing package and version. It only
reads from the registry.

The publish job lists every package of the release and records the time
npm accepted each upload, keeps those times with its upload receipts, and
passes both to the new job. Create GitHub Release now waits for the new
job, so the release is created and marked complete only once every
package resolves; a later release run checks again and skips uploads npm
already accepted.
2026-10-25 18:51:56 -05:00
Chris Tate 5f9fd5d49e Clone union records whose override is an empty array literal (#2278) 2026-10-25 18:49:23 -05:00
Chris Tate a848a7b349 Resolve the pipeline promise to undefined for an untyped iterable destination
When an untyped destination function returned an async iterable, the
promise form settled its result without a payload, and code that read the
awaited value as an untyped value dereferenced NULL. The promise form for
an untyped destination now resolves to undefined when the destination
returned no promise, or its promise carries no value, as Node does.
2026-10-25 18:44:18 -05:00
Chris Tate 14bb8d4156 Convert asserted values that no ordinary conversion reaches (#2260) 2026-10-25 18:27:26 -05:00
Malte Ubl c7ac4d7001 Expect the types patch command in the same-name origin test 2026-10-25 16:25:10 -07:00
Chris Tate 0063214e42 Keep unchanged partitions cached across edits (#2110) 2026-10-25 18:21:52 -05:00
Chris Tate 417e3a6d08 Call function tables without boxing or reference counting (#2173) 2026-10-25 18:17:54 -05:00
Chris Tate 693b038b15 Keep the result of void generic methods called through class-backed interfaces
A class may implement a void interface method with a method that returns a
value, and JavaScript passes that value on (`console.log(c.publish(m))`).
The call now keeps the dispatch's untyped result, as a call through a
non-generic void member already does, instead of replacing it with
undefined: printing or interpolating it shows the value, and binding it to
a void-typed name checks it.
2026-10-25 18:17:32 -05:00
Chris Tate 81fc7bfa89 Map any parameters of interface method and call signatures as checked values (#2273) 2026-10-25 18:14:23 -05:00
Chris Tate 30cf205e90 Run untyped pipeline function stages and later iterable stages
A pipeline stage that arrives as an untyped value now follows Node's
stage ladder for more shapes. As the first stage, a function is a source
function: the pipeline calls it with { signal } while it is set up and
pumps its result, which must be iterable (ERR_INVALID_RETURN_VALUE
otherwise), and a { readable, writable } pair pumps its readable side.

After the first stage, the pipeline call is lowered once per combination
of its first three untyped stages being functions or not, and a typeof
test picks the branch at run time. A function stage is called with the
previous stage and { signal }; before the last stage its result must be
async iterable, and as the last stage a promise resolves the pipeline's
value and an async iterable feeds the PassThrough pipeline() returns
(SCR_PIPE_DEST_DYN). Any other value that is not a stream becomes
Duplex.from of it, which the next stage reads from without the previous
stage being piped into it, and Duplex.from's ERR_INVALID_ARG_TYPE for
the values it rejects.

An untyped stage followed by an untyped function stage hands its value to
that stage through a cell created per pipeline call, so the function
receives the previous stage's own iterator, as Node's makeAsyncIterable
answers it, instead of an async generator over it.

Untyped web streams, pairs after the first stage, and function stages
that return a stream throw the not-supported error.

RUNTIME_ABI_VERSION goes from 45 to 46 for the new stream exports.
2026-10-25 18:10:32 -05:00
Chris Tate 56099ae21c Erase lambdas that implement a generic method through their context (#2274) 2026-10-25 18:07:47 -05:00
Malte UblandClaude Opus 5.5 20a4a92aa6 Reserve the rc word's high half for layout ids
The single object model keeps an object's layout id in the high half of its
rc word, beside the program-lifetime and frozen bits. Class ids already live
there; this names the encoding and moves the runtime's tests that may see an
object with an id onto the count:

- scr_rc_layout_id reads the 30-bit layout id; scr_rc_class_id is now that id.
- scr_rc_immortal tests the count, and scr_rc_frozen and Object.freeze of a
  record use it instead of comparing the whole word with SIZE_MAX.
- Publication writes only the count of any object with a layout id, so an
  immortal object keeps its id, as a class instance keeps its class id.
- classIdRcWord refuses a class id outside the layout id range.

No object other than a class instance carries an id yet, so behavior and
emitted code are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 16:05:55 -07:00
Malte Ubl 895e979c4e Merge remote-tracking branch 'origin/feat/type-patch-command' into feat/type-patch-command
# Conflicts:
#	packages/compiler/src/frontend/type-patches/command.ts
#	packages/compiler/test/type-patches-command.test.ts
2026-10-25 16:03:05 -07:00
Malte Ubl c60bfe8b57 Merge branch 'feat/type-patch-origins' into feat/type-patch-command 2026-10-25 16:02:48 -07:00
Chris Tate 9067f06c02 Cache the zone offset at UTC midnights for local-time conversions (#2272) 2026-10-25 18:01:58 -05:00
Malte Ubl d38195b069 Merge remote-tracking branch 'origin/feat/type-patch-origins' into feat/type-patch-origins 2026-10-25 16:01:13 -07:00