Commit Graph
63 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 77faf0c9e6 Accept the ICU-answered Intl forms and narrow their refusals
The forms the first slice answers now lower: x.toLocaleString() (and an
explicit undefined locale) to icu.numFormatDefault, the argument-free
Date#toLocaleString/toLocaleTimeString to the ICU date formatters, and
Intl.Segmenter({ granularity: "word" }) to the ICU word iterator (a
literal language tag is the locale; the empty string is the default).
Sentence segmentation keeps its refusing construction in JavaScript
dependencies, the grapheme segmenter stays the engine-free fast path,
and the code notes the deletion I4 schedules.

The refusals that named these forms follow: the four stdlib hints, the
Intl identifier hint, the Date method hint and the Segmenter option
messages list what lowers and what still does not (locale negotiation,
options bags, toLocaleDateString, formatter values). The library mode
refuses the icu.* libCalls, whose ambient locale state and data file a
library archive cannot carry, and the ambient process.config comment says
what a compiled binary can honestly answer.

An argument that IS undefined (exactly undefined, or void) means the
absent form and lowers; one whose type merely includes undefined may hold
a locale or an options bag at run time, and the constructor cannot read
either, so it is refused rather than silently treated as absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:49:10 -07:00
Chris Tate 4c5c0823ce Address review feedback 2026-10-26 10:20:11 -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
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
Chris Tate 4a39f8e1b7 Address review feedback 2026-10-25 23:55:18 -05:00
Chris Tate 9847375966 Run registered classes' static initializers in place and keep retried statements' diagnostics
- A deferred class with static field initializers is reached where its
  statement evaluates: its file's %init retries it there
  (retryDeferredAtDeclaration), so its initializers lower in place. A failed
  retry reports nothing, restores the types it degraded, and leaves the
  class deferred; no later flush registers it, so it reports at its
  reference instead of registering after its initializers' place. Before, a
  class a later reference registered never ran its static initializers.
- A generic class that registers on retry registers the units of its
  static methods. Before, a call to one was an undeclared function
  (SC9001).
- A statement that lowers again after degrading a type keeps the
  diagnostics of the function-local class bodies it lowered the first time:
  the retry reuses those bodies instead of lowering them again.
- retriableDiags and failedTypes are a Set and a Map owned by the lowerer
  instead of weak collections, and the degraded types of a mode are copied
  without an empty-array fallback, which the self-hosted compiler could not
  lower.
- The coverage test of a class rejected behind a receiver probe pins its
  refusal under --tier=static-only, as the diagnostics corpus does; under
  localized failure the class compiles.
2026-10-25 23:22:35 -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 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 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 d74cc3b8bd Report which type failures localized failure would turn into dynamic slots
Stage L0 of the localized-failure design, report-only: no lowering changes.

A type with no static lowering poisons the slot that holds it, and the
poison rejects the declaration around it (a class, a signature, a
declaration statement); each later use then fails on its own line. This
records, on the committed diagnostics, where each type failure sits, whether
a checked dynamic slot would keep the program's meaning, and which rejected
declarations each failing statement used. coverage --explain-dynamic turns
that into the would-be dynamic slot list with the cascade each slot clears,
and the counts of errors that would disappear through stages L2 and L4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 16:16:41 -07:00
Chris Tate 4050c0fb5c Merge main and skip package.json files without a scriptc key 2026-10-25 03:58:49 -05:00
Chris Tate 9e55835014 Compile more npm and CommonJS JavaScript dependencies statically (#2122) 2026-10-24 18:30:56 -05:00
Chris Tate 02ec0e86f6 Address review findings in the npm and CommonJS interop batch
- Intl.Segmenter accepts an options object literal made only of
  `granularity` entries again, including the empty `{}` main accepted;
  with none the segmenter is a grapheme segmenter, and a repeated key
  takes its last value, as in JavaScript.
- Namespace reads of a CommonJS module refuse a name that any module it
  re-exports assigns inside a function, not only the module itself:
  Node's namespace keeps the values from the end of evaluation.
- The named-import link check treats `module.exports` as an export every
  CommonJS module provides, as Node 24 does, instead of throwing its
  missing-export SyntaxError.
- The npm cases cjs-esm-named and cjs-lexer-aligned, which the batch no
  longer refuses at build time, are listed with the run-time SC1090 their
  CommonJS exports-object reads report.
2026-10-24 16:54:24 -05:00
Chris Tate 4f0975cbbe Compile more npm and CommonJS JavaScript dependencies statically 2026-10-24 12:01:26 -05:00
Chris Tate d9f1457cd5 Run CommonJS top-level return, untyped assert forms, and untyped instanceof Object as Node does 2026-10-24 08:07:52 -05:00
Chris Tate ccd45c748f Address the Node island report review findings 2026-10-23 12:21:50 -05:00
Malte UblandClaude Opus 5.5 f32d0cd184 Make the packaging island fixture a webpack bundle
Main now compiles minified npm packages (#1989), so ni-minified no
longer refuses and the island report lost its packaging class. The
fixture is now ni-bundled, whose build-transform markers still refuse it
as shipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:06:38 -07:00
Malte Ubl c0a89e002b Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/src/diagnostics/diagnostic.ts
#	packages/compiler/src/frontend/pipeline.ts
2026-10-23 05:55:03 -07:00
Chris Tate 358d2df536 Keep package results that pass their argument through out of the declaration boundary
A package function declared to return its own type parameter, which also
types one of its parameters (<T extends object>(obj: T) => T), hands back
the argument or something standing in for it, and the package may track
that object's identity. Effect's Equal.byReference returns a proxy it
keeps in a WeakSet. The declaration-typed boundary cast such a result to
the argument's type and built a separate native record, so
Equal.equals(a, b) on two by-reference objects compared them by value and
printed true where Node prints false. The result's type comes from the
caller's argument, not from data the package declares, so the projection
no longer casts these calls.

The package re-export coverage test now expects three dynamic statements:
since the boundary, readRows's declared number result keeps its type, so
count - 1 in 7004-package-declared-type-reexports is native arithmetic.
2026-10-23 02:23:33 -05:00
Malte Ubl 5cb2329780 Merge remote-tracking branch 'origin/main' into ni-2-node-island-report 2026-10-22 14:31:26 -07:00
Chris Tate affe386bbf Throw for every object on the right of an arrow instanceof, and give blocked bodies their this
- An arrow function or method on the right of an untyped `instanceof`
  has no prototype. Node throws "Function has non-object prototype
  'undefined' in instanceof check" for any object, but the runtime threw
  only for plain objects, arrays and functions, and answered false for
  typed arrays, native handles, promises and typed references. It now
  answers false only for primitives.
- Coverage lowers the body of a signature-blocked function for its
  diagnostics. A declared `this` parameter now gives that body its
  receiver, as lowerFunction does, so `this.label` reports nothing.
  Before, each use reported SC1080.
2026-10-22 10:09:43 -05:00
Chris Tate aeb429c066 Type empty array literals by their slots, and report package diagnostics accurately
- An empty `[]` whose own type is `never[]` (a `return []` in an async
  function, whose contextual type is `T[] | PromiseLike<T[]>`, or an
  object literal's `files: []` field) builds as the unit-only element
  array, like the empty tuple, so typed array slots receive it through
  the checked empty-array lift. It was `number[]`, so `string[]` slots
  refused it with SC1090 or SC2002.
- The refusal "instanceof against this native constructor" names its
  site, and coverage lists the sites whose right-hand side is `Object` or
  `Function`, which always reach it. An arrow function or method on the
  right of an untyped `instanceof` throws Node's "Function has non-object
  prototype" TypeError for an object.
- Coverage lists the diagnostics of a function whose signature did not
  compile. A use of a parameter whose type did not compile reports SC2004.
- Coverage lists deferred fences in a module's top-level code under "runs
  at startup", and the diagnostics envelope marks them `startup: true`.
2026-10-22 05:45:38 -05:00
Chris Tate e597e2d64c Keep the .d.ts-only types of a package compiled from its JavaScript (#1729) 2026-10-22 00:38:08 -05:00
Chris Tate 5bdc3be385 Name function expressions by their source names in SC6101 and skip type names and generated helpers 2026-10-21 23:25:48 -05:00
Chris Tate c36c414065 Re-export only data shapes from package declarations, through imports and .mjs entries
- A local `export { Kind }` of a name the declaration file imports
  (`import type { Kind } from "./kinds"`) now resolves like
  `export { Kind } from "./kinds"`; it was dropped.
- The typedefs now reach a runtime entry outside the .js/.cjs rewrite
  path (an .mjs file).
- Only data shapes are re-exported: an interface, type alias or enum
  with no method, call or construct signature, function type or other
  computed type, whose referenced types are data shapes too. An
  interface with methods describes the package's JavaScript objects and
  stays as the JavaScript types it (@opentelemetry/api's Counter).
- NPM_STATIC_TYPES_SUFFIX moves to npm-static.ts, so that module does
  not import the syntax modules (the self-hosted frontend tests compile
  it natively).
2026-10-21 22:23:04 -05:00
Malte UblandClaude Opus 5.5 e61fbf8c0b Merge origin/main into ni-2-node-island-report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:07:41 -07:00
Chris Tate fa7bc7c7a0 Warn about likely-hot functions that use untyped values
A build now names the functions of the program's own files that compile
to the dynamic tier and likely run often, so a slow program with a little
untyped code says where the time goes:

  warning SC6101: 3 functions that likely run often use untyped values, which runs slower than typed code:
    advance (nbody.js:22): untyped JavaScript file, in a loop
    ...

- The functions come from the tier assignment lowering already makes
  (Lowerer.dynamicFunctions). Without a profile, a function counts as
  likely hot when a dynamic operation runs inside one of its loops, when
  a loop calls it, or when it calls itself (lowering/dynamic-tier.ts).
- A function is named only when its source says why its values are
  untyped: an `any` (for a for...of variable, the value it iterates), an
  untyped JSON.parse result, or an untyped JavaScript file. Generated
  %dyn.* helpers, module top levels and functions that are dynamic for an
  operation without a static lowering are left out.
- At most five functions are named, most likely hot first, with the
  total; dynamic-tier code in npm packages is counted per package on one
  line. All of the warning's text is in diagnostics/dynamic-tier.ts.
- SC6101 opens the SC61xx band, performance notes, with a new
  "performance" category in the diagnostics envelope; SC60xx stay the
  divergences.
- scriptc build prints it after the divergence warnings, and the
  executable cache stores it with the entry, so a cached build (including
  the CLI's routed fast path) repeats it. scriptc coverage reports it, and
  a comment-only edit no longer replays a verdict that carries it, since
  its text holds line numbers.
- --no-dynamic-tier-warning leaves it out of build and coverage;
  coverage --fail-on=dynamic fails on it.
- Docs: coverage.mdx (Dynamic tier, Gating builds and CI, the envelope
  categories) and cli.mdx.
2026-10-21 18:53:43 -05:00
Malte UblandClaude Opus 5.5 091b8f1934 Report which packages and modules would run in a Node island
Adds the report-only Node island policy, configuration and detector of
the scope-B plan (milestone NI-2). Coverage now lists the npm packages a
build would run in a Node island and why, gap islands first, without
changing code generation.

- project-config.ts reads the `scriptc` key of the entry's package.json
  with a schema; an unknown value in `scriptc.packages` or
  `scriptc.nodeIslands` is SC8004 instead of being ignored. Package shims
  and the WebAssembly scanner read through it.
- node-islands/: origin (user, npm, shim, builtin; package instances),
  rules R1.1-R2.5 and the four classes, a syntactic pre-scan for R1/R2
  markers, classification of refusals and runtime fences (reason ids at
  the refusal sites that need them), the policy (auto, explicit, off;
  forced node and native packages; marked user modules) and the report
  with island closure.
- coverage: a "Node islands" section, `--explain-island=<pkg>`, a
  `nodeIslands` field in the diagnostics envelope, and `--fail-on`
  `node-islands`, `eager-node-islands` and `island-gaps`.
- CLI: `--node-islands`, `--node-island`, `--native`, `--node` (validated,
  report-only) and `--strict-user-js` (SC8101-SC8105 for R2 constructs in
  user JavaScript).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:22:35 -07:00
Chris Tate a43f0a71a6 Merge main 2026-10-20 11:36:33 -05:00
Chris Tate 9e54b4a198 Merge main 2026-10-20 09:00:02 -05:00
Chris Tate acf8b28f40 Merge main 2026-10-20 08:57:09 -05:00
Chris Tate d438cf685f Merge main 2026-10-19 19:43:23 -05:00
Chris Tate c7329845b1 Merge main 2026-10-19 16:48:01 -05:00
Malte UblandClaude Opus 5.5 6e68a811f1 WebAssembly W5: the Memory, Table and Global constructors, Module.imports/exports/customSections, and function references from tables
new WebAssembly.Memory, Table and Global construct the runtime objects an
instance exports, with Node 24's descriptor order, defaults, conversions
and messages (V8's EnforceUint32 errors, the v128 and shared-memory
errors; a valid shared memory is refused with a TypeError until threads),
so either kind can be imported. Imports of memories, tables and globals
now check limits, types and mutability with V8's LinkError messages, and
exports keep V8's identity (imported objects re-export as themselves, one
object per memory and table). The classes called without new throw Node's
TypeError.

WebAssembly.Module.imports, exports and customSections read the
descriptor (ABI v2). Custom sections are carried only by programs that
call customSections. Every function a module references reaches
JavaScript as a callable function: the translator emits one call thunk
per signature, so table.get and funcref globals return the module's
unexported functions too, and exported functions can be stored in tables
and globals from any instance. Discovery also finds .so WebAssembly
shared libraries at resolved paths.

Fixes to W3/W4 found on the way: externref Table.grow fills with
undefined; Memory.grow/Table index errors and funcref messages match V8;
an invalid funcref global import is a LinkError; global.valueOf() on a
WebAssembly.Global-typed receiver answers the value; explicit-bounds
memories keep the blocks grow replaced, so old ArrayBuffers never
dangle.

This unblocks quickjs-wasi's extension loader: quickjs-wasi with its url,
encoding, headers and structured-clone extensions runs through its own
glue identically to Node, and pi codemode's static coverage goes from 83%
to 84% with no WebAssembly blocker left.

Evidence: differential corpus programs for the API (webassembly-jsapi),
a 261-test port of WPT wasm/jsapi (webassembly-wpt-jsapi), quickjs-wasi's
dist/extensions.js run unmodified against dylink-shaped modules
(webassembly-dylink), and the 2.0 spec suite's modules that import the
spectest memory and table (new webassembly-spec-imports group plus the
memory and table groups); all match Node in the plain and sanitized
lanes. The zero-cost gate leaves every non-WebAssembly program
byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:27:45 -07:00
Chris Tate b3feb6761b Merge main 2026-10-18 22:29:57 -05:00
Chris Tate 402558dc71 Merge main 2026-10-18 21:45:00 -05:00
Malte UblandClaude Opus 5.5 3ffcdfb08f Refuse a default-locale toLocaleString in the coverage tier fixtures
This branch lowers Number.prototype.toPrecision statically, so the
tier-demotions and dynamic-mix coverage fixtures lost the operation they
used as "no static lowering". Use n.toLocaleString() instead: the host's
default locale is something a compiled binary cannot carry, so it stays
refused (SC2020) and the fixtures keep the same blocker and demotion
request on the same lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:34:12 -07:00
Malte UblandClaude Opus 5.5 a1576d7c5f Keep the deferred-class-receiver fixture on a rejected field type
The fixture's class was rejected through a WeakMap-typed field, which
this branch makes legal. A MapIterator-typed field is still refused, so
the class stays rejected and the test still checks that a class reached
only through a receiver probe reports its own diagnostic exactly once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:30:54 -07:00
Chris Tate 9414bdc5fe Merge main 2026-10-18 18:58:47 -05:00
Malte UblandClaude Opus 5.5 daf4ff6a2b Decode and validate the WebAssembly modules a program can load, and report them in coverage
`scriptc coverage` now lists every WebAssembly module whose bytes the
build can see, with its size, whether it validates, its function, import
and export counts, and the WebAssembly proposals it uses. Nothing is
translated yet; builds are unchanged.

- packages/compiler/src/wasm/binary.ts decodes and validates a module in
  plain TypeScript (the native compiler compiles it too) at the feature
  level scriptc means to translate: the MVP plus sign-extension,
  non-trapping float-to-int, bulk memory, multi-value, mutable globals,
  reference types and extended constant expressions. Where WebAssembly
  3.0 and V8 relaxed a 2.0 rule (global initializers reading defined
  globals, memory indices), it follows V8, the behavior oracle. Modules
  that use SIMD, threads, exception handling, tail calls, function
  references or GC, memory64 or multiple memories still decode, and are
  reported as needing that proposal instead of as invalid.
- packages/compiler/src/wasm/discover.ts finds modules from four sources:
  paths the compiler resolves from the source text (require.resolve,
  createRequire(...).resolve, import.meta.resolve,
  new URL(..., import.meta.url), __dirname joins), inline base64 and byte-array
  literals, the .wasm files of reached packages whose code mentions
  WebAssembly, and a `scriptc.wasm` list in package.json or the new
  coverage-only `--wasm <file>` flag. Modules are reported once, by
  SHA-256, with every source that found them; module files are input
  probes of the coverage verdict cache. The JSON envelope
  (--print=diagnostics) carries the same records under `wasmModules`.

Evidence: packages/compiler/test/wasm-spec.test.ts runs the decoder over
the 3,017 distinct binary modules of the WebAssembly 2.0 core test suite
(without SIMD) and the extended-const proposal's tests, generated by
gen-wasm-spec-cases.mjs with wabt's wast2json, and requires V8's verdict
for each; 8,000 seeded mutations of those modules must also agree with
V8, as must undici's inline llhttp modules (scalar valid, SIMD reported)
and es-module-lexer's. src/wasm/binary.test.ts checks each feature and
each error class against V8. On the survey's real modules the decoder
agrees with V8 for quickjs-wasi (637 KB, 36 ms) and photon (1.9 MB,
121 ms). tests/coverage-fixtures/wasm pins the four discovery sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:19:39 -07:00
Malte UblandClaude Opus 5.5 5a342ad1a5 Parse date strings with V8's grammar when the bounded parser rejects them
Date.parse and new Date(string) answered NaN natively for every form
outside ISO 8601 with an explicit offset and the X509 certificate shape,
so RFC 1123 HTTP dates ("Mon, 01 Jan 2024 00:00:00 GMT") never parsed and
the fresh/express/send conditional GET never matched. The runtime now
falls back to a port of V8's DateParser (the ES5 date-time format, then
the Safari-compatible legacy grammar: weekday and month names, RFC 2822
offsets, zone abbreviations, AM/PM, parenthesized comments, slashes) for
exactly the strings the bounded grammars reject, so the timestamps they
accept cost nothing more. Zone-less dates are local time, converted with
the spec's UTC(t) over the host zone (DST gaps read with the offset before
the transition, overlaps answer the earlier instant).

With native parsing matching Node for every input, the SC6005 literal
warning has nothing left to report and is retired rather than extended to
non-literal inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:34:55 -07:00
Chris Tate 240f576805 Add execution tiers to the IR and a report-only tier analysis (#1315) 2026-10-18 14:08:12 -05:00
Malte UblandClaude Opus 5.5 577b0135a9 Add execution tiers to the IR and a report-only tier analysis
Stage S2 of the two-tier design. IrFunction gains a tier (absent means
static); lowering assigns it from function bodies, placing functions that
use the dyn vocabulary in the dynamic tier, and the validator enforces
invariant I1: a static function performs no dynamic operation, only
boundary operations (dynFrom, dynCheck, kind tests) and moves. Serialized
IR keeps the field out in this stage and loaders derive it again, so every
IR, LLVM and executable artifact is unchanged.

Every committed refusal and runtime fence becomes a recorded demotion
request attributed to the binding, field, function instance, class or
module the dynamic tier would take over, while the compiler still refuses
and fences as before. `scriptc coverage` reports dynamic-tier statements
(statementsDynamic), `--explain-dynamic[=<name>]` prints the demotion
chains, and `--tier=auto|static-only` is wired through build, run and
coverage for stage S6; both modes compile identically today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:12:27 -07:00
Malte UblandClaude Opus 5.5 542f6573bc Name the declared type in refused class field diagnostics
TypeScript class fields declared `unknown` or `object` compile since #1147,
but every other field type without a typed representation was still
reported as "'unknown'-typed class fields": `any`, `Array<any>`, and
`WeakMap<object, string>` alike. The refusal now names the declared type,
for example "'any'-typed class fields", so the blocker reads as what it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 07:07:45 -07:00
Malte Ubl 8f5ec2d94f Report record width copies at assertions, assignments and array elements
SC6001 only looked at call arguments and typed initializers, and only at
strict field subsets: a target that adds optional properties, the empty
record type, `r as T`, a later `v = r` into a typed binding, and records
stored as elements of a typed array literal copied silently.

The scan now reports a copy whenever the property sets differ, covers
those sites, and reports a class instance that reaches a plain record
type which does not keep its identity (the remaining boundary: interfaces
of only generic methods). Every corpus program still reports no
divergence.
2026-10-16 14:02:02 -07:00
Chris Tate 7e6294ca0f Make scriptc compilation native-only 2026-10-12 12:25:30 -05:00
Chris Tate f992727487 Report a rejected declaration's own diagnostic when a discarded probe flushed it first
Deferred collection diagnostics now stay attached to their declaration until a
copy reaches the final report, so a discarded probe buffer or a successful
nested collection can no longer drop them. Uses of a binding whose type names
a rejected class report that class's diagnostic before the inherited-blocker
cascade.
2026-10-09 23:07:57 -05:00
Chris Tate 3e41bcbfd0 Make compile blockers and Node differences actionable (#786) 2026-10-09 10:30:31 -05:00
Chris Tate 1f10e83720 Promote canary to main (#738)
- Preserve callback identity, receiver binding, and class references across unions and collections.
- Track record field presence independently of undefined values and improve native runtime behavior.
- Expand differential coverage and streamline CI bootstrap and compiler rebuild checks.
2026-10-08 11:00:13 -05:00