Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate f591e8e6d7 Print how a --profile-use build's profile matched
`scriptc build --profile-use` prints one line on stderr with the number of
functions the profile annotated, the number whose control flow changed since
it was collected (mismatched), and the number it has no record of. When more
than 10% of the functions the profile describes are mismatched, it also prints
a warning to collect the profile again. The build still succeeds.

Both the compiler library's build and the native compiler's build report the
match. Cached builds report the match of the build that produced them: the
native codegen and object caches store it beside the cached objects, and the
executable cache stores it in its entry.
2026-10-25 22:14:25 -05:00
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 89db5d47f7 Merge main 2026-10-25 22:02:35 -05:00
Chris Tate 8b1deff6e9 Step untyped loops over the program's generators on the typed path
A for…of or for await loop over an untyped iterable now tests, once it has
its iterator and next method, whether the iterator is one of the
program's own generators with its own next method. Each step of such a
loop resumes the generator directly and boxes only the value, where the
dynamic protocol boxed the result record and its promise and read them
back. Every other iterator keeps the dynamic protocol, and early exits
still close the iterator through it. The awaits are the protocol's: an
async generator's next() promise is awaited, and a sync generator under
for await awaits the result and then the value.
2026-10-25 22:01:42 -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 dfe89d7d7b Store identical runtime artifacts once and strip the copied CLI executables
Runtime packs: variants of a unit that do not read their defines, and
flavors that compile a unit the same way, produce identical objects and
bitcode. The pack build now points those manifest entries at one file
(shareIdenticalArtifacts) and deletes the copies. Each link selects one
flavor and one variant per unit, so no link names a shared file twice. The
linux-x64-gnu pack keeps 1,191 of its 1,448 artifact files.

Native CLI packages:
- The comptime evaluator builds with -g0 and the executable strip flags,
  and the Windows build no longer ships its .pdb.
- The copied tsc-ts executable is stripped with llvm-strip, which rewrites
  the linker-signed code signature of Mach-O executables.
- On arm64 macOS, verify-native-cli.mjs --run checks the code signatures
  of the compiler, tsc-ts and the comptime evaluator.
- SCRIPTC_NATIVE_KEEP_SYMBOLS=1 keeps the symbols of all three.
- The copied runtime sources and declarations get LF line endings. A
  Windows checkout converts them to CRLF, so the Windows CLI shipped files
  that differed from @scriptc/runtime's.
2026-10-25 21:55:14 -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 35c6717694 Merge perf/shape-analysis-budget 2026-10-25 21:52:58 -05:00
Malte UblandClaude Opus 5.5 1ae5f105ca Rank dynamic slots by their dynamic operations, name them in SC6101, and document them
Stage L5 of the localized-failure design.

- SC6101 names a dynamic slot as the cause of a likely-hot untyped function
  ('tool' is a dynamic slot, declared 'ToolDefinition', which does not
  compile).
- Coverage counts the dynamic operations each slot's values take part in,
  lists the costly slots first, and puts the count in the JSON envelope.
- A build whose only performance warnings are dynamic slots closes with a
  line about declared types instead of function types.
- Docs: a Dynamic slots section in the coverage guide, the limitations
  page bullet, and --fail-on=dynamic-slots in the CLI reference and usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:48:18 -07:00
Malte UblandClaude Opus 5.5 7f77a8fb24 Keep push-down only where it does not cost a failing program errors
A pushed-down type is baked into the globals, fields and signatures that
hold it, so a unit-level fallback cannot reach every use. When reachable
emit still has errors and some degraded type kept a pushed-down shape, the
emit runs once more with every degraded type held whole, and the pass
with fewer errors wins (ties keep push-down). A program that compiles
never takes this path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:48:17 -07:00
Malte UblandClaude Opus 5.5 b6af8bc0a0 Degrade the types of a statement's own expressions where the statement commits
Stage L4 of the localized-failure design.

A cast to a declared type that does not compile, a literal whose contextual
type does not compile, a spread clone of such a value or a temporary of
that type used to reject the statement. The statement commit point now
degrades those expression types too (with push-down), so the cast is a
checked conversion to the type's compilable shape and the literal builds
in it. The SC6104 of an expression names the expression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:48:17 -07: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
Chris Tate 8a71a20e32 Merge ni/i2-handles 2026-10-25 21:39:21 -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 a76b7e77b4 Address review feedback 2026-10-25 21:37:55 -05:00
Chris Tate cc2266ab1d Add --cpu-dispatch for x86-64 executables (#2288) 2026-10-25 21:36:00 -05:00
Malte UblandClaude Opus 5.5 53ebf05ff5 Add the canonical type normalizer, not yet wired into mapping
Stage N0 of the localized-failure design (part 2, canonical type
normalization), report-only.

canon(type, env) resolves a checker type under generic bindings to a small
set of canonical shapes: primitive, literal, unit, top, object (nominal for
classes and standard-library interfaces, structural otherwise, with
members resolved lazily through the checker), array, tuple, flat union,
unbound parameter, and an opaque leaf. Intersections whose constituents
agree, or that are all object-like, resolve to one object; a branded
primitive is the primitive; T & {} drops T's unit arms. Type parameters
substitute from the environment, so a generic alias spelled over a generic
body's own parameters resolves without asking the checker to instantiate,
and the alias reference keeps the original checker type. Nodes intern by
type and bindings, so recursion closes on the node under construction.

Nothing calls it yet: N1 runs it in shadow mode beside mapType.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:35:12 -07: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
Malte UblandClaude Opus 5.5 2d63ad13a5 Fall back to the whole-slot dynamic when a unit fails with a pushed-down type
Push-down is a refinement and must never add a failure: a type's lenient
shape can meet a conversion the whole-slot checked dynamic would have
taken (a width copy, a library member on a partial promise). Each
retryable unit (a statement, a flushed declaration, a generic instance)
records the pushed-down types it maps; when it still fails, those types
widen to the whole-slot checked dynamic and the unit lowers again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:28:46 -07:00
Chris Tate 835303d23a Merge perf/shape-table-dispatch 2026-10-25 21:26:46 -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 7373d3c776 Attribute a derived class's rejection to its rejected base
A class extending a program class whose own declaration was rejected
(a degradable field type) failed collection with an inheritance error
that carried no cause. The localized-failure report then counted the
derived class, and the uses of it, as remaining blockers instead of the
base field's cascade. The error now names the base's failure group as its
cause.
2026-10-25 21:25:51 -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 80ae660be6 Merge main 2026-10-25 21:22:05 -05:00
Chris Tate ee001517e1 Update tsc-ts to 0.0.10
tsc-ts 0.0.10 serves its API on a Unix socket with `--api --listen <path>`
on Linux and macOS, reads its default library files from disk with
`--default-libraries-from-disk`, and is built with scriptc 0.4.5 and a
profile collected with it. It depends on @scriptc/threads 0.4.5. The
release-age exclusions admit tsc-ts 0.0.10, its platform packages and
@scriptc/threads 0.4.5.
2026-10-25 21:21:26 -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
Malte UblandClaude Opus 5.5 95f9d259b7 Record the order-parity baseline for the push-down corpus program
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:14:37 -07: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 28b76f7b25 Merge ni/i1-stack-bound 2026-10-25 21:08:37 -05:00
Malte UblandClaude Opus 5.5 bb33ec50fe Push localized failure down to the failing component of a declared type
Stage L3 of the localized-failure design.

A degraded type used to become the checked dynamic as a whole slot. Its
lenient mapping now keeps the compilable shape: the type maps again with
every nested component whose mapping fails as the checked dynamic, so a
record keeps its static members with one dynamic member, an array of
records keeps its records, and a Map, Promise or function keeps its static
parts. When even that shape does not map, or a unit still fails with it,
the type falls back to the whole-slot checked dynamic (one more round).
Lenient forms are IR types of one lowerer's shape tables, so the coverage
remainder re-derives them. A pushed-down type is one SC6104, at the slot
where it degraded, marked as keeping its shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:07:57 -07: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
Malte UblandClaude Opus 5.5 624d2bf873 Record order-parity baselines for the stage L2 localized-failure corpus programs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:57:23 -07:00
Malte UblandClaude Opus 5.5 4da07089c9 Degrade parameters and returns of function expressions and generic instances where they commit
A function expression's or arrow's parameter and return types commit at the
statement that holds it, and a generic instance's at the instance drain.
Both are now localized-failure commit points for declaration slots: the
failing type degrades and the statement or instance lowers again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:57:23 -07:00
Malte UblandClaude Opus 5.5 f0eb427152 Retry declarations a probe flushed at the statement that commits them; keep the dyn field policy outside reached classes
A flush inside a capture buffer cannot retry (a probe that later declines
must see today's answer), so its copies of the declaration's diagnostics
reach the build when the statement commits. That statement commit point
now reopens such a declaration: when its diagnostics are retriable, they
leave the build, the declaration retries, and the statement lowers again.
Probe copies of a declaration that already registered are dropped.

The 'any'-typed class field refusal stays a fence at collection and is
lifted by the flush retry, so only reached classes change; the coverage
remainder registers the declarations reachable emit registered, and maps
the degraded types alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:57:22 -07:00
Malte UblandClaude Opus 5.5 8b07790325 Register classes and functions with dynamic slots when a reference flushes their type failures
Stage L2 of the localized-failure design.

A class whose field (or parameter property) type has no static lowering,
or a function or method whose parameter or return type has none, used to
be rejected, and every use of it failed on its own line. Its collection
deferred the diagnostics until a reachable reference flushed them into the
build. That flush is now a commit point: when every deferred diagnostic is
a degradable type failure at one of the declaration's slots, the failing
types degrade and the collection runs again, up to 16 rounds. The
declaration registers with checked dynamic slots (one SC6104 each), the
reference proceeds, and emitReachable (or run) lowers the bodies it had
skipped. A derived class whose base registers this way retries with it.

The 'any'-typed class field refusal is lifted: a TypeScript class field
whose type maps to the checked dynamic stores it, as JavaScript classes
already do.

A program that compiles today flushes no deferred diagnostics into its
build, so nothing degrades and its output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:57:22 -07:00
Malte UblandClaude Opus 5.5 cd5607493e Pin the diagnostics corpus to --tier=static-only
Its programs pin today's refusals; localized failure turns several of them
into checked dynamic slots in the default mode. Under static-only every
snapshot stays byte-identical, which is the stage's proof that the switch
restores the refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:57:21 -07:00
Chris Tate cd3e7b700e Merge perf/shape-overlay-fallback 2026-10-25 20:52:09 -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 Ubl 92a4b39ef6 Merge base branch 2026-10-25 18:38:31 -07:00
Malte Ubl f661d0f520 Merge base branch 2026-10-25 18:38:31 -07:00
Malte Ubl 8cde53d7cf Merge base branch 2026-10-25 18:38:30 -07:00
Malte Ubl 68eda2ec95 Merge base branch 2026-10-25 18:38:30 -07:00
Malte UblandClaude Opus 5.5 7687cf2d26 Type island packages from their declarations in failed builds and generics
When a native build failed and the packages it compiled with runtime fences
then failed as islands too, the native build's diagnostics were reported.
That build types those packages from their JavaScript, where declared-only
types (interfaces, `Static<T>`-style aliases) are `any`, so typed user code
reported cascades the island build does not have (a schema library's `TSchema`).
The island build's errors are reported instead.

In a generic function instance over island types (`<T extends TSchema>`),
a `T` value bound to dyn now crosses into the island the way `any` does,
instead of being refused as a native object.

Corpus 6829 types user code with an island package's interfaces, generic
aliases and a generic function; node-island.test.ts pins the reported
diagnostics of a failed build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:38:29 -07:00
Malte UblandClaude Opus 5.5 6a2c9eca21 Record order-parity baselines for the localized-failure corpus programs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:35:39 -07:00
Malte UblandClaude Opus 5.5 ac31a15a62 Hold locals and module globals whose declared type does not compile as checked dynamic slots
Stage L1 of the localized-failure design.

A local, loop variable or module global whose declared TypeScript type has
no static lowering used to reject its declaration statement, and every
later use of the binding failed with it. Where that failure is about to
enter the build (the statement's poison in a TypeScript file, or the
module global's registration), the failing type now joins the lowerer's
degraded types and the unit lowers again: the type maps to the checked
dynamic wherever its strict mapping fails, so the slot holds values by
reference (typed capsules, live records and arrays, function thunks) and
typed reads out of it are checked. Each such slot is an SC6104
performance warning, listed under coverage's statement counts and in the
JSON envelope; coverage --fail-on=dynamic-slots fails on them, and
--tier=static-only keeps today's refusals.

A program that compiles today reaches neither commit point, so nothing
degrades and its output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:35:38 -07: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