Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 5a14c0a3dc Merge main 2026-10-26 08:37:13 -05:00
Chris Tate 7567ab4360 Rank dynamic slots by their dynamic operations, name them in SC6101, and document them (#2312) 2026-10-26 08:35:19 -05:00
Chris Tate b1614d6e75 Release a substitution's operands when it returns
The temps a regex replace or replaceAll consumed (the subject that a
previous substitution in a chain produced, a computed replacement) were
released at the end of the statement. A chain of substitutions over a
large string therefore held every intermediate result until the last one
returned. The operands now live in a frame of their own that is released
right after the call, so a chain holds at most its current subject and
result.
2026-10-26 08:30:42 -05:00
Chris Tate 6a8650be5f Throw the async generator depth RangeError through scr_throw_error_msg 2026-10-26 08:22:47 -05:00
Chris Tate f9b5533ee6 Read IR nodes' fields by name in three llvm-emit passes 2026-10-26 08:13:15 -05:00
Chris Tate acb2d23df5 Attribute slot causes to the owner only for types without their own slot 2026-10-26 07:59:53 -05:00
Chris Tate f1298287d6 Resolve each shipped declaration path once
isFallbackDtsPath runs for every type lowering maps (isStdlibFile), and
since the fallback declarations moved into per-module files (#2256) it
asked declarationPath for the fallback module directory each time. Without
an installed declaration root, each answer resolved
@scriptc/compiler/scriptc-node-fallback.d.ts through Node's module
resolver. Lowering three.js's buffers-animation fixture went from 6.3 s to
43 s. declarationPath now keeps its answers by name until the declaration
root changes.
2026-10-26 07:58:58 -05:00
Chris Tate 238cb0e7cd Free sanitized fibers' fake stacks 2026-10-26 07:56:46 -05:00
Chris Tate 7e6327740b Merge perf/static-objects 2026-10-26 07:53:16 -05:00
Chris Tate 52a228d0f5 Keep declarations a removed branch shares with another 2026-10-26 07:50:59 -05:00
Malte UblandClaude Opus 5.5 c81758a08d Map a failing type exactly from its canonical form where localized failure would degrade it
Stage N2 of the localized-failure design (canonical type normalization).

At localized failure's commit points, before a failing type degrades to
its pushed-down or whole-slot dynamic form, its canonical form maps
through mapCanonical under the current bindings. When that gives a static
IR type, the type maps exactly: no dynamic slot, no SC6104. Opaque and
unbound forms, and forms that map only to the checked dynamic, degrade as
before. The coverage remainder re-derives canonical forms in its own
tables. Because it runs only at commit points, a program that compiles
today never consults it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:50:51 -07:00
Malte Ubl 177b4cd326 Merge branch 'lf/n1-shadow' into lf/n2-fallback
# Conflicts:
#	packages/compiler/src/frontend/type-mapper.ts
2026-10-26 05:50:08 -07:00
Malte UblandClaude Opus 5.5 f03741b03e Keep the branded-primitive rule compilable by the self-hosted frontend
Once shadow mode imports the normalizer, the self-hosted frontend lowers
it, and it cannot compare union-typed canonical nodes by identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:49:39 -07:00
Chris Tate d1a0bea84e Have the native CLI packages depend on their LLVM helper and runtime pack
Each @scriptc/cli-<platform> package bundled its platform's LLVM helper
and runtime pack, which are also published as @scriptc/llvm-<platform> and
@scriptc/runtime-<platform>. The CLI packages now depend on those packages
at the same version (workspace:*, which pnpm pack writes as the exact
version) and leave them out: cli-linux-x64-gnu goes from 115 MB to 60 MB
packed.

- build-native-cli.mts --platform-packages, which each CLI package's
  build:native passes, writes a manifest without llvm_package, runtime_pack
  and runtime_packs.
- loadNativeToolchain resolves a missing helper or host runtime pack as
  Node resolves the package from the compiler's directory: nested in the
  platform package, beside it (hoisted, or pnpm's virtual store) or higher
  up. It requires the compiler's version, and an absent or mismatched
  package fails with an error that names it.
- installNativeCli records where the package manager put both packages in
  the installed manifest, with the same checks.
- verify-native-cli.mjs requires the exact dependencies of a platform
  package and checks the resolved packages' versions.
- package-native-cli.mjs bundles the helper and runtime pack into the
  standalone archives, which stay self-contained.
- smoke-native-install.mjs installs the helper and runtime pack tarballs
  with the platform package; the bootstrap's exported package drops its
  bundled copies first.
- release.yml publishes the CLI packages only once every helper and
  runtime pack package resolves on npm, and scriptc only once the CLI
  packages resolve, each wait at most 25 minutes. A rerun skips the
  uploads npm already accepted and waits again.
2026-10-26 07:48:11 -05:00
Malte UblandClaude Opus 5.5 5c14941617 Bound canonical keys and keep the shadow from breaking a compile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:40:13 -07:00
Malte UblandClaude Opus 5.5 9820569fc0 Compare canonical mapping with mapType in an opt-in shadow mode
Stage N1 of the localized-failure design (canonical type normalization).

mapCanonical (frontend/canon-map.ts) maps a canonical node to IR from its
structural form: primitives, literals, units, the top types, arrays, flat
unions and plain records. Nominal objects, callables, index-signature
shapes, tuples, bigint and symbol keep their per-type recognitions through
mapType on the node's checker type.

Under SCRIPTC_CANON_SHADOW=1, every successful top-level mapType also runs
canon and mapCanonical in throwaway registries and appends a JSON line per
comparison (equal, diverge, canon-fail) to SCRIPTC_CANON_SHADOW_OUT. The
census decides which rule families can later move to the normalizer. The
hook is absent otherwise, so no output changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:40:12 -07:00
Malte UblandClaude Opus 5.5 7ac0542aa9 Read ordinary objects' properties through accessors outside the object core
Runtime units outside the object core (scr_json.c, scr_js_object.c) read and
adjusted SCR_DYN_OBJ entries in place: v.obj.len, v.obj.entries[i] and the
48-byte ScrDynEntry fields. The single object model's generic objects
(stage S3) change that storage, so these units now go through own-property
accessors in scr_dyn.h:

- scr_dyn_obj_count, scr_dyn_obj_key_at, scr_dyn_obj_value_at and
  scr_dyn_obj_prop_at (a ScrDynProp view: key, value, getter, setter and
  attributes) walk the properties in creation order;
- scr_dyn_obj_index_of finds one by key;
- scr_dyn_obj_set_attrs_at restricts a member a builtin object just added;
- scr_dyn_own_symbol_prop and scr_dyn_copy_prop_at wrap the symbol-entry
  and copy primitives.

The accessors are inline over today's entries, so behavior is unchanged.
21 units move (inspect, assert, util compare, console, http, http2, fetch,
tls, fs, os, lib, array, message, publish, region, symbol, worker events,
handle properties, builtin values, declaration adoption). A package test
keeps entry storage reads inside the core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:39:34 -07:00
Chris Tate 895763f3b6 Keep a give-up statement's first-attempt diagnostics 2026-10-26 07:38:24 -05:00
Malte Ubl 572e7e3eac Merge remote-tracking branch 'origin/lf/l4-expressions' into lf/l5-polish
# Conflicts:
#	packages/compiler/src/frontend/lowering/lowerer.ts
2026-10-26 05:37:00 -07:00
Malte UblandClaude Opus 5.5 ec9624997a Attribute the dynamic pieces of pushed-down types in SC6101 and the slot operation counts
A pushed-down slot's dynamic operations act on its failing components,
whose checker types are not degraded themselves. The lenient mapping
records those components with the declared type they belong to; SC6101
names them ('hook' is a dynamic piece of 'Plugin[]'), and the operation
counts go to the owning slot. Causes read a binding's declared type, not
its narrowed flow type. The coverage test uses its own fixture, so it does
not depend on which interface members main can map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:35:58 -07:00
Malte Ubl 3bfe3f7c85 Merge base branch 2026-10-26 05:35:29 -07:00
Malte Ubl 44729131df Merge base branch 2026-10-26 05:35:27 -07:00
Malte Ubl 55b20ab200 Merge base branch 2026-10-26 05:35:23 -07:00
Malte Ubl 80b5fdeb54 Merge base branch 2026-10-26 05:35:21 -07:00
Malte Ubl ffff3bfc19 Merge base branch
# Conflicts:
#	tests/harness/node-island.test.ts
2026-10-26 05:35:15 -07:00
Malte Ubl bcef522b73 Merge base branch 2026-10-26 05:34:58 -07:00
Malte Ubl f2fe256f45 Merge base branch 2026-10-26 05:34:57 -07:00
Malte Ubl 99753193aa Merge base branch 2026-10-26 05:34:56 -07:00
Malte Ubl 797650db73 Merge base branch 2026-10-26 05:34:56 -07:00
Malte UblandClaude Opus 5.5 91b437c090 Keep path-mapped island declarations out of the module graph
A re-export from an island package that tsconfig `paths` maps to its
installed directory pushed the package's declaration file into the module
order, so the lowering compiled the declarations' own imports (SC2013 in
the package's .d.mts). Such a re-export now adds no module edge; the
lowering reports the island boundary as for any island re-export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:34:47 -07:00
Malte UblandClaude Opus 5.5 637d78bb67 Load island packages that tsconfig paths map to their directory
A monorepo can pin one copy of a package with a tsconfig `paths` entry
that names its installed directory. Its import then resolves as a project
import to the package's declarations, so when the package runs in a Node
island, preflight refused it as types-only (SC1010) and the lowering
skipped it as a project module, leaving its bindings unlowered (SC2013).
Both now treat it as the island import it is: typed by its declarations,
loaded in Node.

Corpus 6839 imports an ESM island package through such a mapping and uses
its interfaces and generic aliases from typed code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:34:47 -07:00
Chris Tate fe244085d4 Run native async generators as stackless coroutines
An async generator whose body suspends only at its own awaits and yields
now compiles to an LLVM switched coroutine, as async functions of that
kind already do, instead of running on a fiber with its own stack. Its
steps run on the stack that resumes it (the loop's after an await), its
awaits suspend through scr_coro_await_prepare and scr_coro_hop_prepare,
and its yields store OUT through scr_coro_yield_* before suspending.

- scr_async_gen_new_stackless creates the generator. The stackless hop
  settles an async generator's state as the fiber hop does, and a step
  that would start below the stack guard throws a RangeError, which
  rejects its request.
- Calling an async generator's next, return or throw no longer counts as
  parking the caller: they queue a request and run the body to its next
  suspension, which returns to the caller. Bodies that consume async
  generators can now be stackless too.
- An untyped await (async.awaitDyn) has a native coroutine form,
  extracted by scr_coro_await_dyn_value, so async bodies that await
  untyped values can be stackless.
- An async generator's return through a finally region takes its await
  turn through the coroutine when the body is one.

Debug builds, libraries, worker programs and Windows executables keep
fibers, as for stackless async functions.
2026-10-26 07:28:50 -05:00
Chris Tate a59df23c71 Merge main 2026-10-26 07:21:12 -05:00
Chris Tate e9977107fe Use a kept layout only for the binding the current lowering resolves
A const that kept its initializer's layout records the initializer's
checker type by declaration symbol. A function body lowers once per
instance, and only an instance for a wider argument keeps the layout, so
the record from that instance made the function's own lowering bind the
wider layout for `grow(alias)`: the argument converted into a copy and the
callee's writes did not reach the caller. The recorded type now applies
only while it maps to the layout of the binding the current lowering
resolves.
2026-10-26 07:18:25 -05:00
Chris Tate 28b61086c3 Merge main 2026-10-26 07:16:46 -05:00
Chris Tate 641f779fa3 Store identical runtime artifacts once and strip the copied CLI executables (#2301) 2026-10-26 07:06:31 -05:00
Chris Tate b221a13369 Reuse the completion callback a stream passes to its option callbacks (#2323) 2026-10-26 07:00:49 -05:00
Chris Tate 75e48ddec3 Merge lf/l3-push-down 2026-10-26 06:55:57 -05:00
Chris Tate de17158254 Merge main 2026-10-26 06:51:26 -05:00
Chris Tate 5ff9ffde03 Merge main 2026-10-26 06:47:53 -05:00
Chris Tate d98fce9e27 Merge main 2026-10-26 06:45:43 -05:00
Chris Tate 5ad9c266dc Read missing elements through non-null asserted operands as Node does 2026-10-26 06:40:02 -05:00
Chris Tate eeef447216 Merge ni/1-libnode-loader 2026-10-26 06:36:38 -05:00
Chris Tate 636c7631b3 Keep program partitions local to the code an edit changes (#2328) 2026-10-26 06:35:05 -05:00
Chris Tate 6589911a3b Address review and CI: aliases that kept a layout, recursive layouts, divergence scan
- A const that kept its initializer's layout (`const view: Counted[] = entries`)
  is typed by its annotation, so a call that passed it compared the annotated
  layout with the parameter's and copied it. The const now records its
  initializer's type, and a call that passes it binds that type, at function
  and module scope.
- Comparing two recursive layouts (`{ kids: Tree[]; note? }` against
  `{ kids: Plain[] }`) recursed without end and stopped compilation with
  "Maximum call stack size exceeded". A pair already being compared now holds
  for the fields that reach it again.
- The divergence scan reported SC6001 for calls and consts whose layout the
  lowering kept, which the coverage test refuses for corpus programs. The
  lowering records those arguments and initializers, and the scan skips them.
2026-10-26 06:28:48 -05:00
Chris Tate 92e42f7c6f Merge lf/l2-declarations and main, and fix self-hosting 2026-10-26 06:24:09 -05:00
Chris Tate 067f146e87 Read builtin globals through the global object, and support Node's navigator (#2242) 2026-10-26 06:16:08 -05:00
Chris Tate a758ff1b4e Select sanitized runtime variants by unit, and skip stripping without llvm-strip
The native sanitizer build chose the variants to compile by artifact path.
Since the runtime pack stores identical objects once, two variants of a
unit can share a path, so it compiled such a unit for both variants and
linked it twice (duplicate symbols in the sanitized native bootstrap).
selectedRuntimeUnits now returns each selected unit with its variant;
selectRuntimePackArtifacts and the sanitizer both use it.

Staging the native CLI's assets ran llvm-strip unconditionally and failed
on hosts without it. Without llvm-strip on PATH, tsc-ts now keeps its
symbols and the build prints a warning; the release hosts install LLVM.
2026-10-26 06:02:01 -05:00
Chris Tate 7ae99f63bc Give stream option callbacks Node's callback identities
Node passes a stream's one onwrite function to every _write and _writev
call, and a new function to every _transform call. A write or writev
option callback now receives the same closure on every call
(scr_stream_write_closure), kept or not, so a Set, WeakSet or WeakMap
finds it again. A reused transform, flush, final, construct or destroy
callback is first forgotten by weak collections, as at its free, so a
WeakSet or WeakMap that recorded the previous call's callback does not
find it on the next call.
2026-10-26 05:52:57 -05:00
Chris Tate e559baacde Skip repeated probes in lowering (#2330) 2026-10-26 05:47:59 -05:00