`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.
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.
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.
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>
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>
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>
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.
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>
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>
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.
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.
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.
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>
- 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.
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>
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>
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>
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>
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.
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>
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>
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>