Commit Graph
144 Commits
Author SHA1 Message Date
Chris Tate 4abd8e4fff node:module compiles: createRequire's static erasure and the baked builtinModules
- createRequire(import.meta.url) (or __filename / import.meta.filename) in a const binding is compile-time plumbing that emits nothing; each require call through it resolves per site against the binding's own file, and the inline createRequire(import.meta.url)("spec") spelling erases identically
- The erasure per target: a builtin spec makes the binding a namespace import in const clothing (namespace, destructured, and inline-member spellings all key the existing module tables, with the fallback-typed 'as typeof import(...)' cast idiom stripped); a relative .json document validates at compile time and lowers to json.parse over its baked text (JSON.parse's checked-dynamic unknown stance — exactly Node's value); a bare name nothing installed resolves compiles to Node's catchable MODULE_NOT_FOUND throw (the optional-dependency try/require pattern); an installed npm package embeds under --dynamic through the island's NEW require-condition entry (NpmGraphBuilder.addRequire — a dual package answers its CJS arm, exactly Node's createRequire) and reports the requires-dynamic diagnostic in a static build
- Dynamic specifiers, '#' project imports, relative non-.json targets, --npm-static opt-ins, and non-this-file bases all fence by name; the island-handle rescue skips the plumbing decls so --dynamic builds erase them too
- module.builtinModules bakes as Node v24's exact list (a fresh string[] per read, pinned to the compat target so emitted programs stay host-independent); isBuiltin and syncBuiltinESMExports fence per site with pointed hints, and the createRequire hint, its diagnostics snapshot, the surface manifest, and the npm-static offender wording follow
- Corpus 2606 pins json/builtin/builtinModules/MODULE_NOT_FOUND differentially on both backends and under ASan; the npm create-require fixture case pins cjszoo's module.exports, dual's require-condition CJS arm, and the missing-package rejection against Node
2026-07-24 10:32:20 -05:00
Chris Tate e47ad23c0c Subclasses may override emit in the forwarding shape, monomorphized per event
- class C extends EventEmitter may re-declare emit(event: string, ...args: unknown[]): boolean when the rest parameter only forwards through super.emit(event, ...args) in the method's own body; the body lowers once per event name as the real hierarchy method emit:<event> (vtable participation, (this, ...tuple) => bool ABI), with event bound to the name and the forward lowered to the runtime emit — or to the nearest override ancestor's specialization, JS's super chain.
- Emit sites dispatch by the whole-program rule: receivers at-or-below an override call the specialization directly (virtually when a deeper override exists); receivers above every override route through an interned helper that instanceOf-tests the topmost override subtrees and falls through to the runtime emit, so plain EventEmitter bindings holding override instances still dispatch like Node.
- super.<member> over an emitter-rooted base now lowers through the emitter spoke with static dispatch (an override at-or-below the lexical class never answers a super call); registering 'newListener'/'removeListener' listeners fences while a comparable class overrides emit (the runtime fires meta events internally, past the override), and every other emitter member keeps the surface fence with the forwarding-shape rule spelled out for emit.
- Corpus 2604-2609 pin both backends against Node: the logging override with upcast dispatch, two-level override chains with literal super.emit, once/off/prepend ordering and handled errors under an override, the unhandled-'error' throw through an override, the filtering override, and an emitter-subclass job queue; the diagnostics snapshot pins the three fences.
2026-07-24 09:37:57 -05:00
Chris Tate f2a629ae06 The --dynamic import() subgraph rides the shared cycle admission
- Extract preflight's benign-cycle admission (the cheap per-edge rule plus the declaration-only init window analysis, with Tarjan SCC and cluster-verdict memos) into makeCycleAdmission; preflight's static walk and appendDynamicImportModules now judge back edges identically, so benign cycles behind dynamic import() compile as Node cache hits instead of blanket SC1016.
- appendDynamicImportModules runs once in lowerToIr before any pass constructs: the old per-constructor run minted cycle refusals into the discovery pass, whose diagnostics are discarded by design, so inadmissible import() cycles silently compiled; refusals now land on the emit pass with the same narrowed reason (binding and line, or the offending top-level statement) the static walk mints.
- SC1016 gains a registry hint naming the admitted shape and the fix; diagnostics snapshots, order-parity baselines, and the surface manifest repin with it.
- Corpus 2604 (mutual class cycle across two modules), 2605 (three-module function ring with two back edges and a function-body-only value use), 2606 (import() into an admitted mutual-recursion cycle) — all byte-identical to Node through both backends; diagnostics fixture dyn-import-cycle-tdz pins the dynamic-path TDZ refusal.
2026-07-24 09:32:53 -05:00
Chris Tate c8c8b8634f Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- declare-erased roots make every chain rooted there a catchable ReferenceError at first touch, exactly Node's erasure semantics
- Never-read unmappable bindings emit nothing; nullish-cast bindings answer Node's exact TypeError on member access
- Assertion-supplied generic signatures register like annotations and monomorphize per call
- Stdlib generic members take the lib fence naming the member

# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-stmts.ts
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-24 08:06:21 -05:00
Chris Tate c48a21b9ef Object.create, array entries and keys iterators, and variadic-spread Object.assign
- Object.create(null) is a real null-prototype dictionary: inspect, toString, and deepStrictEqual answer Node's forms; the dynamic tier routes to the engine's own Object.create for live delegation
- Object prototypes fence by name in static builds — an own-copy stance is observably wrong under Node's empty-own-set answers and live proto mutations
- entries() and keys() walk any static array live in for-of heads and iterator-helper chains
- Object.assign(target, ...sources) packs spreads with JS's evaluation order before any copy, with V8's position-dependent error texts

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
#	packages/runtime/src/scr_json.c
2026-07-24 08:03:58 -05:00
Chris Tate 42c2725e5d Collapse string|object-family unions into the checked-dynamic representation, retiring their SC2009 fences
- mapType: a union with an 'object'/'unknown'/'{}'-flavored (dyn) arm beside only dyn-subsumable siblings — scalars, units, dynFrom-convertible records/arrays — maps to DYN wholesale; class/Map/Set/func/promise/regex arms keep their typed homes, and the SC2009 classifier now names the sibling that cannot ride the collapse instead of blaming the dyn arm.
- A dyn ELEMENT makes the whole array the DOM value (unknown[], object[], the plugins-slot (string | object)[]): the DOM array literal builds from TS annotations, .length on collapsed-array receivers is a DOM keyed read, element writes ride dyn.keySet (ARR index set/extend with undefined-hole padding), and for-of over dyn gets a pointed operation fence instead of the stale component story.
- Appendix-A repro 4 stops SC2009-fencing: an island plugins array wraps at the collapsed slot (dynFromJsval) and the element read reaches the loud keyed-read ladder until the routing lane arms it (dyncheck pin jsval-collapsed-union-slot).
- Corpus 2584/2585 differential both backends (guard ladder, option tables, unknown[] first-class; SAN clean); json-dyn snapshot repinned where the unknown[] declaration fence retires; conformance movers verified locally: discriminateObjectTypesOnly, unknownPropertiesAreAssignableToObjectUnion, unknownLikeUnionObjectFlagsNotPropagated.
- SEMANTICS 398 documents the collapse rule, the exact subsumable-arm domain, and the width/identity consequences (deep-copy entry, full-width JSON, per-site fences for non-subsumable values).
2026-07-24 02:54:47 -05:00
Chris Tate e81e4f5ab5 Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- ambientUndefVarRootOf walks calls, `new`, optional chains, assertions, instantiation expressions, and tagged templates, and accepts `declare function` and trap-binding roots: Node erases the declaration, so the root read IS the crash — lowerCall, member reads, assignments, and variable declarations lower the whole statement to the catchable ReferenceError, arguments never evaluate, and the never-initialized binding registers as a trap whose references can never run
- never-read unmappable bindings with value-only initializers and writes emit nothing: Node builds the value and drops it, so `var xs2: typeof Array;` and the write-only `var f2: { <T, U>(x: T, y: U): T }` compile instead of fencing on a type the program never consumes
- null!/as-any bindings whose types keep generic signatures (or intern empty record shapes) register no storage; generic-method calls and member reads through them compile to Node's exact TypeError, nullish-to-nullish flows to nothing, heritage-clause uses decline the family so mixin resolution keeps its fence, and checked-dynamic (`any`) bindings keep their dyn story untouched
- assertion-shaped generic arrows (`< <T>(x: T) => T >((x) => x)`) register like annotated bindings and monomorphize per call; stdlib generic members (Promise.then, Object.defineProperty) take the SC2020 lib fence, and Function.prototype.apply/call/bind on compiled function values name the direct-call spelling
- diagnostics fixtures that used `declare` to conjure unconstructible values now conjure them by cast so each fence family keeps its pin; snapshots re-recorded, the deferred checker-panic diagnostic now flushes at its first reference as designed
2026-07-24 02:29:48 -05:00
Chris Tate 9f422210b1 Object.create(null) lands as the null-prototype dictionary; other prototypes fence by name
- Static builds mint a DOM object flagged prototype-free: own-member walks are already Node's null-proto answers; inspect prefixes [Object: null prototype] (nested/empty/beyond-depth forms), o.toString() throws Node's "is not a function" (dyn.toString carries the source spelling), and deepStrictEqual gains the prototype gate, diff rendering included
- Under --dynamic Object.create routes through the engine's own Object.create for null and engine-held prototypes — live delegation, shadowing writes, and mutation-through-proto all Node-exact
- DOM and static-record prototypes fence with the reason instead of the design doc's own-copy stance: Object.keys/inspect/JSON answer the EMPTY own set in Node and prototype mutations show through live, which no copy answers honestly
- Element writes on plain dyn receivers ride dyn.keySet generally (the result[key] = value memo idiom; unmapped static-any receivers probe their lowered world), unblocking the dictionary's use
- Corpus 2581 (static, byte-for-byte both backends) and 2582 (@dynamic engine route); diagnostics object-create-fences pins the three named fences
- Documented residue: String()/template/join over a null-proto DOM value answer [object Object] where Node throws — the emitted String(unknown) walkers are never-throw by contract
2026-07-24 01:23:17 -05:00
Chris Tate 0423093a99 Node's validation ladders throw where implemented APIs were fenced
- Buffer compare/equals/constructor ladders, URLSearchParams WHATWG brand checks and arity ladders, URL.revokeObjectURL arity, and the max-listeners family answer Node's exact typed errors
- ToPrimitive string-hint coercion runs user toString/valueOf with throw propagation; assert.throws accepts expected-object variables
- Reusable always-throwing libCall shapes (error.nodeThrow, error.argTypeThrow) seed the remaining validation-ladder work
- Two emittedReadable lifecycle bugs fixed; ERR_* codes stamped across the bytes/crypto/fs range ladders

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-23 23:37:03 -05:00
Chris Tate e56371e6cc Test scaffolding follows the new fixtures
- assert-fences drops its non-literal expected-object pin (that form lowers now through the errValue key walk — corpus 2556 pins the behavior); the diagnostics snapshot re-records.
- order-parity baselines re-record for the five new corpus fixtures.
2026-07-23 23:05:02 -05:00
Chris Tate 82cc41ab0a Checkpoint: dyn-handle compat members and the http Agent surface
- ServerResponse/IncomingMessage/Socket members that fenced on checked-dynamic values now dispatch: flushHeaders, getHeaders, setTimeout, cork/uncork with a real writableCorked counter and coalescing flush, writeHead's raw-array form, write/end encodings and deferred callbacks, req.pause/resume with buffered drain, destroyed/readable/bytesWritten/res.req reads, setNoDelay, destroySoon, end(cb), and dyn Buffer at()/slice()
- new http.Agent/https.Agent lowers to a checked-dynamic handle: literal option parsing, getName, destroy, sockets/requests/freeSockets snapshots, settable defaultPort, and REAL maxSockets accounting (over-limit requests defer their dial and queue); keepAlive: true throws the named pooling fence
- the request path threads the agent (http.requestAgent/https.requestAgent rows, both backends); dyn keys now stringify for indexing and the in-operator over dyn receivers (dyn.hasKey)
- differential fixtures 2573-2577 pin both families against Node
2026-07-23 22:40:41 -05:00
Chris Tate f8b36d9f74 stream/promises compiles statically; out-of-scope modules refuse with named reasons
- pipeline and finished settle real promises over the existing watcher machinery, on both backends
- domain, v8, inspector, node:sqlite, and the underscore-prefixed internals each name why a compiled binary cannot provide them
- stream/web, the Console class, and dns/promises stay honest not-yets with documented paths
- The SC1010 hint gains stream/promises and the long-missing querystring
2026-07-23 22:21:31 -05:00
Chris Tate fa847e2105 DataView setters, fresh-ArrayBuffer erasure, Object.is, and the en-US Intl slice
- The DataView setter family lands end-to-end; fresh ArrayBuffers erase into zero-filled typed-array views; resizable/shared surfaces fence by name
- Object.is compiles with the spec's SameValue across every kind, including union operands
- Composed en-US Intl.NumberFormat and toLocaleString print byte-exact through the Ryu digit core's shortest-round-trip rounding, 30k-double fuzz-verified; the rest of Intl fences by name with reasons
- DurationFormat deliberately fences: Node throws V8-internal Temporal errors with embedded source paths on plain inputs

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-23 22:20:44 -05:00
Chris Tate 6328f47bfd Out-of-scope builtin modules say why: v8, inspector, node:sqlite, domain, and the underscore internals
- SC1010's wording routes through one helper (unsupportedModuleFeatureOf): pending modules keep the plain not-yet story, out-of-scope ones append the reason
- v8/inspector name the missing V8 engine, node:sqlite the unbundled SQLite library, domain its deprecation and engine-level interception, and the fourteen underscore-prefixed legacy modules their Node-internal implementation objects
- All five fence sites reword together: the four import/require edges in program.ts and the fenced-binding use site in lower-exprs.ts
- New diagnostics fixture module-out-of-scope.ts pins the reasons (and that stream/web stays plain not-yet); the coverage import-fences snapshot repins with the v8 reason
2026-07-23 21:27:19 -05:00
Chris Tate a0d2ce24f5 Record width composes with hybrid shapes, and per-field lifts cover unknown, upcasts, and clean function adapters
- recordWidthPlan narrows index-signature sources into declared-only targets (declared fields copy off their struct slots, the overflow drops with the width); absent-field completion declines there — the overflow could hold the key at runtime, and a completed undefined would drop the value Node keeps
- the overflow capture initializes same-named declared target fields directly off the source, admitting REQUIRED declared members; literal writes to names the target does not declare go overflowOnly, and a dispatch-writes gate keeps the validator's collision rule for the rest
- widthLiftPlan gains dynIn (a typed field into an 'unknown' slot — the static→DOM deep copy), upcast (derived into base, the prefix-layout reinterpret), and funcAdapt (funcCoerceAdapter's CLEAN subset — the stranded trap-only dispositions stay top-level, never inside a width plan)
- SC2002 now names the first blocking rule per record pair (describeRecordWidthBlocker mirrors the planners' gates) and its message/hint/registry entry speak the width-copy truth — the manifest flips the one entry name
- corpus 2556/2557 pin both hybrid directions and the new lifts byte-identical vs Node on both backends; diagnostics fixture record-width pins the residue's pointed messages
2026-07-23 20:36:50 -05:00
Chris Tate 176d963ea1 Index signatures carry function, Map, and Set values through the overflow store
- isSupportedIndexValue admits func/map/set: the overflow map's RC and trace machinery is already fully type-directed (scr_closure/scr_map adapters, the cycle fixpoint's map rule recursing on the value), and nested index-signature records rode the record kind all along
- the C keyed-read miss trap can now describe Map/Set-typed slots (dynDesc), reachable only through that message — no dynCheck ever expects a container
- SC2006's message and hint name the real value domain (functions, Maps, Sets, RegExps, Promises, ...) and the honest blockers left: symbol keys, dual signatures with unequal value types, and values with no representation at all
- diagnostics fixtures pivot to still-fenced shapes (Uint8Array values, unequal duals, a Date-valued bag); corpus 2558/2559 pin the command-registry and container-table patterns — declared+overflow hybrids, enumeration, delete, registry-capturing cycles, container churn — byte-identical vs Node on both backends
2026-07-23 20:36:21 -05:00
Chris Tate fa7cc089a9 The en-US Intl slice: composed NumberFormat and toLocaleString go static, the rest fences by name
- new Intl.NumberFormat("en-US").format(x), the callable spelling, and x.toLocaleString("en-US") lower to one runtime formatter with default options: decimal notation, 0-3 fraction digits rounded half-up on the SHORTEST round-tripping decimal (ICU's rounding input, probed against Node — format(1.0005) is "1.001" though toFixed(3) answers "1.000"; 1e23 prints the shortest form's zeros, not the exact expansion), "," grouping, "∞"/"NaN" texts, and "-0" for negatives rounding to zero. The en-US/latn symbols are the whole embedded locale surface — no ICU is linked.
- The Ryū digit core factors out of scr_f64_to_str as scr_f64_digits (same bytes; the 1M-double oracle gate stays green) and feeds the formatter; a 30,000-double ad-hoc fuzz across magnitude regimes matched Node byte-for-byte on both backends.
- Every unlowered Intl form fences by NAME with its own reason: the environment-default locale a compiled binary cannot carry (no-locale forms), other locales and the rest of the namespace (DurationFormat, DateTimeFormat, PluralRules, getCanonicalLocales — ICU data the binary does not embed; Node 24's DurationFormat additionally throws V8-internal Temporal errors with embedded source paths, so byte parity there is a version-dependent landmine), options bags, formatter values (the composed form is the lowered shape), and non-number format arguments.
- Differential fixture 2562 pins the rounding discriminators, grouping, ±0/NaN/∞, tiny and huge edges, and both spellings byte-identically vs Node on both backends; the new intl diagnostics fixture pins every named fence, and the Intl hints across the identifier/member/type chokepoints now teach what lowers.
2026-07-23 20:30:47 -05:00
Chris Tate b31d160fbd Object.is goes static with the spec's SameValue
- Number pairs take a new scr_num_same_value runtime primitive (NaN equals NaN, +0 differs from -0 — the two divergences from ===); strings, booleans, unit literals, and every reference kind (arrays, records, class instances with hierarchy widening, functions, class values, Maps/Sets, symbols, typed arrays, promises) ride exactly the strict-equality machinery, whose answers SameValue shares.
- Union operands lower through unionEq, which grows a sameValue flag: the f64 arm's payload compare upgrades to SameValue (a separate interned C helper per union; the LLVM lane calls the runtime primitive inline), every other arm is shared between the two semantics.
- Statically disjoint kind pairs answer the constant false with the operands still evaluated for their effects (Object.is is (any, any) — tsc admits every pair); dyn/jsval operands and partially-overlapping unions fence by name with strict equality's validate/narrow-first stance.
- Differential fixture 2561 pins the NaN/±0 edges, mixed-kind calls with effect counting, reference identity across kinds, and union payloads (including NaN/±0 inside a union and union-vs-plain wraps) byte-identically vs Node on both backends; stdlib-fence pins the named fences.
- Found pre-existing (reproduced at the base commit): the C backend SIGSEGVs on console.log(asBase === d, asBase === new Base()) — an upcast identity compare plus a constructing compare in one call; the fixture avoids the shape and the bug is reported for a follow-up lane.
2026-07-23 20:15:31 -05:00
Chris Tate 9a7e42fd1b Fresh-ArrayBuffer erasure, DataView setters, and named buffer fences
- new Uint8Array/Uint32Array/Int32Array/Float32Array(new ArrayBuffer(n)) erases the buffer into a zero-filled view exactly like the SharedArrayBuffer sleep idiom (literal byte length divisible by the element size), and new DataView(new ArrayBuffer(n), off?, len?) views a fresh zeroed allocation — nothing else can reference the buffer, so the erasure is exact.
- The DataView setter family (setUint8/Int8/Uint16/Int16/Uint32/Int32/Float32/Float64) lowers end to end: one scr_dataview_set runtime mirror of the getter (ToIndex offsets, Node's constant RangeError, modular integer truncation, double→float rounding, both endiannesses) wired through both backends; the bigint and Float16 setters keep the member fence.
- The resizable surface fences by NAME everywhere it can appear: new ArrayBuffer/resize/transfer/maxByteLength and SharedArrayBuffer.grow now state the reason (no free-standing ArrayBuffer value exists; grow has no threads to share with) at the constructor, identifier, member, and declared-type chokepoints, including the maxByteLength options bag inside an otherwise-erasable position.
- Differential fixture 2560 pins the erasure forms, every setter width/endianness/coercion, aliasing through x.buffer views, and the RangeError edges byte-identically vs Node on both backends; es2025-fences/dataview/stdlib-fence snapshots pin the new named hints.
2026-07-23 19:45:15 -05:00
Chris Tate 7b8e1452b3 The monomorphization frontier: keyof constraints, interface receivers, and signature bindings
- keyof-constrained generics specialize per literal key; keyed record writes take the literal-key type rule
- Generic methods called through interface-typed receivers monomorphize against the proven class
- Generic-signature annotations monomorphize their function initializers; generic-fn aliases register their target
- Fixtures 2550-2555 byte-identical on both backends

# Conflicts:
#	packages/compiler/src/diagnostics/diagnostic.ts
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	packages/compiler/src/frontend/lowering/lower-stmts.ts
#	packages/compiler/src/frontend/types.ts
#	packages/compiler/test/ts7/baselines/order-parity.json
#	tests/harness/__snapshots__/generic-methods.ts.txt
#	tests/harness/__snapshots__/generic-value-bindings.ts.txt
#	tests/harness/__snapshots__/mixins.ts.txt
#	tests/harness/__snapshots__/stdlib.ts.txt
2026-07-23 18:48:49 -05:00
Chris Tate b2b880e462 Record order-parity baselines for the generics-frontier fixtures
- Baselines added for corpus 2550-2555 (plus the previously unrecorded 2490-2500 entries the sweep now sees); no recorded entry changed.
- tests/diagnostics/generics.ts keeps its alias/annotation sections as reached COMPILING bodies (their absence from the snapshot is the point), fixing the dangling calls the earlier edit left.
2026-07-23 17:51:13 -05:00
Chris Tate ced1822c24 Destructuring assignment targets, class-instance sources, class rest, and accessor defaults go static
- Nested patterns and member targets in assignment position, with JS's reference-before-value evaluation order pinned
- Class-instance sources call getters exactly once per element; a latent double-call in the accessor-default path is fixed
- Class rest packs the inheritance chain in Node's key order, fencing where enumeration can't be reproduced
- Fixtures 2536-2540 for the promotions; 2530-2535 and 2541 pin the already-static declaration-position surface

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-23 17:32:49 -05:00
Chris Tate 385b0a9199 Promote destructuring assignment targets, class-instance sources, class rest, and accessor defaults to the static tier
- Destructuring assignment now takes nested patterns (`({ p: { q } } = src)`, `[[a], b] = t`) and property/element targets (`[c.x, box.vals[i]] = arr`, `({ a: rec.f } = o)`, rest into members) — receivers temp at their pattern position, JS's reference-before-read order; defaults on member targets test against the target's own type. Defaults on nested targets keep a named fence (no single binding type to test against).
- Class instances destructure in assignment position like they already did in declarations: field slots and getter calls per element via the same fieldGetExpr dispatch, defaults, renames, and member targets; methods and undeclared names keep their pointed fences.
- Rest over class-instance sources packs the remaining instance FIELDS into a fresh record (JS's CopyDataProperties copies own enumerable properties — never prototype methods/accessors, matching the checker's rest type), in declarations and assignments; non-public fields, runtime-provided chains (Error/EventEmitter/stream), and key orders the packed shape cannot reproduce fence instead of diverging.
- Defaults on destructured getter results now lower (record and class accessors alike), with the getter result temped so the default's undefined test never calls it twice; numeric-literal pattern keys (`const { 2: x } = o`) fold to JS's canonical property name.
- Corpus 2536-2540 pin the new forms byte-identically against Node on both backends; the destructuring-fences diagnostics program keeps the honest residue (method extraction, non-public rest fields, nested-target defaults, index-signature rest).
- The ts7 order-parity baseline re-record backfills the entries the released file was missing (2490-2500 included) alongside the new fixtures; no existing entry changed.
2026-07-23 17:27:25 -05:00
Chris Tate 0386d8c00f keyof-constrained generics monomorphize per literal key; generic-fn aliases register their target
- Instantiations carry ts-level bindings alongside the mapped IrTypes: a `K extends keyof T` parameter keys its instance on the bound literal (no cross-literal sharing — the bodies read different fields), and literals forward through generic-to-generic calls.
- `T[K]` inside instance bodies resolves through the bound checker types (mapBoundIndexedAccess via the new resolveTypeParamTs hook), and `o[k]` reads whose key TYPE proves one literal — a keyof-bound parameter or a literal-typed const — lower to static field reads.
- `const h = id` registers the target's GenericFnInfo under the alias's symbol (chains, block scope, pinned values, and calls all resolve like the target's own name); non-const/reassigned aliases fence by name, and unpinned VALUE uses keep their fence at the reference.
- Fixtures 2550 (keyof/pick shapes, forwarding, runtime keys, literal-typed key vars) and 2551 (alias chains, contextual pinning, annotated generic aliases), byte-identical vs Node on both backends.
2026-07-23 17:20:03 -05:00
Chris Tate aa8d31c3cd SC2001 splits into named type-shape fences
- SC2005 generic call signatures, SC2006 index-signature shapes, SC2007 overloads, SC2008 unresolved intersections, SC2009 supported shapes over an unsupported component with the component named
- Stdlib nominal types reroute to the SC2020 story instead of a duplicate code
- SC2001 keeps only bigint/symbol, constructor objects, and unresolved library generics — its registry name now says so
- Five new diagnostics programs pin one frame per code; manifest entries added; zero behavior change to accepted programs

# Conflicts:
#	tests/harness/__snapshots__/regex.ts.txt
2026-07-23 17:17:31 -05:00
Chris Tate 44cde1510e Split the SC2001 catch-all into named type-fence families
- New codes, minted in badType's classification order: SC2005 values whose type keeps a generic call signature, SC2006 index-signature shapes outside the supported set, SC2007 overloaded function types, SC2008 intersections with no resolved lowering, SC2009 supported container/function/record shapes over a component outside its slot — the SC2009 message names the offending component (Map/Set key-value domains, array/tuple elements, union arms, function rest/params/returns, record members) via describeComponentBlocker/describeRecordMemberBlocker in frontend/types.ts, which mirror mapType's own rules on the failure path only.
- Standard-library nominal types with no lowering at all (Date, ArrayBuffer, WeakMap, the iterator and constructor interfaces) now route to the existing SC2020 story instead of the supported-set recitation; stdlib provenance is checked after the container claims so Map/Set/Promise instantiation failures keep their component story.
- SC2001 keeps the honest remainder — bigint and symbol primitives, constructor objects, and library-derived or unresolved generic shapes — and its registry name now says so; across the diagnostics corpus its rendered sites drop from 57 to 5.
- Every new code flows through the diagnostics registry, the regenerated surface manifest, and coverage-report grouping; five new tests/diagnostics programs pin one rendered frame per code, and the ts7 order-parity baselines re-record to include them.
- Zero behavior change to accepted programs: the differential corpus (900 programs) is untouched and green, and the full suite passes.
2026-07-23 16:55:31 -05:00
Chris Tate 5ba6cffcd4 Private statics resolve through provably-exact class values: const-bound class expressions, class aliases, and rebindable decorated names
- staticCallOn and lowerClassValueProperty fenced every #private static access with a through-a-VALUE receiver, so C.#m()/C.#f on a const-bound class expression, A.#f through a const alias of the class, and Example.#m() under a replacing decorator all died with SC1090 while the direct class-name spelling compiled.
- The static-WRITE path already knew the sound rule (exactClassOfReceiver plus the leaf-class arm); classValueIsExactlyOwn now shares it with the call and read paths: a receiver that provably IS the class, or a class with no strict descendants, always carries the declaring class's brand — the fence stays for slots a subclass can inhabit, where Node throws TypeError.
- Rebindable decorated names ride the leaf arm for free: replacement decorators fence any class with subclasses before minting the rebindable global, so the decoration result is always the declaring class object.
- The read path additionally gains the subclass-spelling fence (reading an inherited private static through a subclass-named binding) that the call and direct-read paths already had.
- Fixtures 2520-2523 pin the four call forms (class-name calls including static field initializers and subclassed declarers, const-bound class expressions with reads/writes/calls, decorated classes under both replacing and void decorators, aliased class values local/module/param) byte-identical vs Node on both backends; the private-members diagnostics fixture repins the residual fence on a descendant-capable slot.
2026-07-23 16:37:51 -05:00
Chris Tate b49052a389 Recursive record types compile statically as named recursive shapes
- mapType back-references mint placeholder shapes/unions finalized at frame exit; identity is per checker type, and context-sensitive (generic/mixin) knots stay fenced
- every type walk survives cyclic shape graphs: coinductive JSON/dyn-safety predicates, guarded formatIrType/jsvalLiftable, cycle headers via the existing collector fixpoint on both backends
- JSON.stringify throws V8's byte-exact circular TypeError (hop lines, ellipsis rule); cyclic values crossing into checked-dynamic slots trap instead of hanging
- console.log/util.inspect render <ref *N>/[Circular *N] exactly (SEMANTICS 129 resolved; depth:null lowers) and deepStrictEqual terminates through Node's pair memo
- corpus 2480-2488 green on C, LLVM, and the sanitized RC-audit lane, which proves child->parent cycles collect
2026-07-23 13:24:13 -05:00
Chris Tate 85ad8eba44 Console prints every inspectable shape; union literals widen per-field
- console.log routes non-scalar arguments through the static inspect machinery
- Union arguments dispatch per arm at runtime; string arms print raw
- Object literals widen per-field into a uniquely-fitting union arm
- The limitations page documents the type surface
2026-07-23 09:14:02 -05:00
Chris Tate 6157cad50a Whole-program validation holds: the SC9001 families zero out
- Island values exit validated into intrinsic slots; checker-any dispatches to the DOM machinery
- World-mixing backstops make jsval/dyn confusion structurally impossible
- Regex arrays compile; dyn-root JSON.stringify matches across backends
- A silent invalid-C escape and a giant-TU stack overflow fixed
2026-07-23 09:14:01 -05:00
Chris Tate c2d01522a1 The #private-members chain: fields, methods, accessors, generators
- Lexically-bound private calls devirtualize by construction; statics and static blocks included
- Private accessors with observable get-then-set order; private generator methods
- Brand checks fold statically or ride the vtable interval test
- util.inspect omits private members exactly like Node
2026-07-23 09:13:58 -05:00
Chris Tate 5307f87080 Web surface and workspace fixes from external use
- The performance global routes through the perf_hooks lowering in all spellings
- Response declares its implemented headers/arrayBuffer/bytes surface
- Math.max and Math.min lower at any arity with NaN and negative-zero exactness
- Copy-installed workspace members classify like symlinked ones
2026-07-23 09:13:55 -05:00
Chris Tate 9e8904ad3b External-use fixes: performance global, Response surface, n-ary Math, workspaces
- The performance global routes through the perf_hooks lowering in all spellings
- Response declares its implemented headers/arrayBuffer/bytes surface
- Math.max and Math.min lower at any arity
- Copy-installed workspace members classify like symlinked ones
2026-07-23 09:13:00 -05:00
Chris Tate 2b2dd83949 Zero the whole-program validator's SC9001 families: the big JS graph builds
- island ('any') arguments at intrinsic/libCall/callValue slots take the validated exit (dynCheck's stance); non-exit-safe slots fence with the slot named, and the boundary pass backstops every jsOp/dynCall/dynInvoke argument so world-mixing sites can never ICE
- a checker-'any' receiver/callee that LOWERED checked-dynamic dispatches to the DOM machinery: ambient-this reads are dynKeyGet, rest-args map/forEach ride the runtime dispatch, dyn callees take dynCall
- Object.entries/values push unit-armed (null | undefined) fields as the null literal; new Set(seed) exits an island-handle seed as a validated copy
- fence-bodied functions emit even when their ABI names an unregistered class, and a run()-end sweep rewrites leftover unregistered-class type slots to the inert f64 placeholder — the clang invalid-C escape closes with the ICE
- RegExp[] compiles (REF elements, both backends; map callbacks returning other unrepresentable kinds meet a named fence), and the C emitter appends program-sized line arrays by loop, not spread (500k-line TUs overflowed the stack)
2026-07-23 03:23:24 -05:00
Chris Tate f94067ec7e The #private-members chain: fields, methods, accessors, generators
- Privates ride the ordinary member maps under their unspellable spelled names; a subclass redeclaring an inherited private name is fenced, so base-chain walks ARE lexical resolution and privates never enter vtables — JS's no-dynamic-dispatch semantics by construction.
- Private GENERATOR methods compose the generator machinery with this as param 0 through the gen-spawn wrapper (the vtable problem that keeps public generator methods fenced does not exist for privates); async and generic privates ride their existing static-dispatch machinery.
- Private statics lower as module globals/functions through the declaring class's own name; subclass-named and class-value receivers fence (JS brands the declaring constructor alone).
- '#x in obj' lowers as instanceof-the-declarer: static folds at/below and disjoint, the vtable interval test when the declarer sits below the receiver's static class, and unions of decided class arms collapse to tag tests; unknown/generic/static brands fence with pointed hints.
- util.inspect omits private fields exactly like Node (an all-private class prints 'C {}'); corpus 2450-2457 pin every form differentially on both backends, diagnostics private-members pins the fences.
2026-07-23 02:42:10 -05:00
Chris Tate db6f0eb2f2 Command tables, generic async members, and deferred-init fields lower
- Primitive constructors as interned coercion closures with JS function identity
- Generic instance-field arrows, async generic methods and statics monomorphize
- Deferred-init fields ride typed extraction reads; a tsgo empty-tuple panic pinned syntactically
- Same-kind loose equality lowers as strict equality; lane fixtures renumbered 2431-2435
2026-07-23 01:38:33 -05:00
Chris Tate 7c93ab6505 Console prints every inspectable shape with Node's console semantics
- console.log/info/debug/error/warn take unknown[] and render every non-scalar argument through the static util.inspect at the rest-args depth 2, strings verbatim
- top-level unions dispatch per arm at runtime with string arms raw, and util.format's %s/rest args take the same per-arm rule
- info and debug lower as stdout aliases of log; the remaining stock console members are declared in the fallback so stock-clean programs meet per-site SC2020 hints instead of a fallback-manufactured SC0001
- corpus 2440/2441 pin the widened shapes on both backends, console.log(process.argv), a find() miss union, and console.log(undefined) included
- diagnostics snapshots re-pinned: the union console fences retire and utility-types reports its real per-site fences
2026-07-23 01:28:47 -05:00
Chris Tate b5f6b0dc46 Cite patterns, not target projects, across comments and hints
- Lowering rationale names the idiom; one user-facing hint reworded
- Diagnostics snapshot re-pinned to the neutral hint text
2026-07-23 01:18:50 -05:00
Chris Tate 4e88c0d920 Parity bookkeeping for the new fixtures
- order-parity baselines re-record for corpus 2426-2430 (new entries only, no behavior moves)
- multiple-errors pins mixed-kind loose equality (same-kind == lowers now)
2026-07-23 01:05:22 -05:00
Chris Tate fc84780ebe Deferred-init fields: stream!-style classes lower with typed extraction reads
- a !-asserted (or SPI-off) field first assigned past the constructor's top level rides an undefined-armed union slot: allocation writes the interned undefined, writes wrap, reads extract the declared type
- scalar reads take unit defaults where JS-undefined behavior survives (bool false, f64 NaN — conditions and arithmetic exact); string/ref reads keep the checked-extraction trap, JS's own first-member-use TypeError named earlier
- union/map/dyn-typed ! fields keep the fence; deferral propagates down the base chain with the fields map
- diagnostics fixtures re-pin the surviving fences; corpus 2429 pins the Output.initialize idiom on both backends; SEMANTICS.md 372-375
2026-07-23 00:40:54 -05:00
Chris Tate 0cb45afc27 Fixture and manifest bookkeeping for the graduated lowerings
- default-export.ts diagnostics fixture repoints to the surviving export= fence (the mutable-default form graduated to corpus 2426)
- destructuring-fences.ts pins the class-instance residue: method extraction, rest elements, setter-only reads (field/getter destructures graduated to corpus 2429)
- surface-manifest sc1031 sample moves to the method-extraction refusal; manifest regenerated for the new builtin list and hint text
- order-parity baselines re-recorded: new fixtures 2426-2430, create-require, npm-reexport, and the SC1010 hint text
2026-07-23 00:37:25 -05:00
Chris Tate 37b8632cf8 As-const command tables lower: the readonly-[] panic repair and width lifts
- tsgo panics computing readonly [] (the empty as-const array) and the facade answers any; the syntax pins the truth, so the record member walk, lowerArrayLiteral, the island-literal route, and lowerAsExpression each repair the shape to the unit-element array
- func arms now map beside data arms in unions (typeof narrows, unionEq compares closures per tag); the diagnostics pin updates
- widthLiftPlan learns tupleArr (tuple fields into array slots, per-position) and emptyArr (unit-only-element arrays reshape to a fresh empty array behind a runtime non-empty trap), so Pick-style {name, aliases} flows lift
- corpus 2428 pins the command-table idiom end to end on both backends
2026-07-23 00:25:48 -05:00
Chris Tate 009a24b469 Field redeclares, class destructuring, unknown-field completion, node:module fence
- redeclaring an inherited field WITH an initializer lowers as the [[Define]]-equivalent assignment into the base slot at the subclass's field-init position (builtin Error prefix included — prettier's errors.js `name = "ConfigError"`); bare and type-changing redeclares keep sharpened fences (corpus 2428, diagnostics inheritance re-record)
- object destructuring of CLASS instances desugars to one member read per element, left to right: fields read slots, accessors call getters through the fieldGetExpr dispatch, renames/parameter patterns/defaults included (corpus 2429 — prettier's AstPath { previous, next })
- an object literal missing an 'unknown' field completes with the DOM undefined (the absent-property read's answer), at literal completion and in the record width relation (corpus 2430 — prettier's getFileInfo(file, {}))
- node:module joins the supported builtins prefix-only as an admit-then-fence surface: createRequire keeps a per-member fence that defers to a catchable runtime error in JS sources (diagnostics create-require pins the hint naming the pattern)
2026-07-22 23:48:01 -05:00
Chris Tate 23d5918a53 init 2026-07-22 18:08:04 -05:00