@scriptc/compiler 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +145 -9
  2. package/dist/backend/cc.d.ts +7 -0
  3. package/dist/backend/cc.js +1 -0
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-exprs.js +35 -0
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/emission/emit-shapes.js +1 -0
  8. package/dist/backend/emission/emit-shapes.js.map +1 -1
  9. package/dist/backend/emission/emitter.js +15 -3
  10. package/dist/backend/emission/emitter.js.map +1 -1
  11. package/dist/backend/llvm/emitter.js +65 -11
  12. package/dist/backend/llvm/emitter.js.map +1 -1
  13. package/dist/backend/llvm/shapes.js +1 -0
  14. package/dist/backend/llvm/shapes.js.map +1 -1
  15. package/dist/diagnostics/diagnostic.js +2 -2
  16. package/dist/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/frontend/lowering/lib-boundary.js +76 -6
  18. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  19. package/dist/frontend/lowering/lower-builtins.d.ts +37 -4
  20. package/dist/frontend/lowering/lower-builtins.js +240 -6
  21. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  22. package/dist/frontend/lowering/lower-calls.js +122 -32
  23. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  24. package/dist/frontend/lowering/lower-classes.d.ts +34 -2
  25. package/dist/frontend/lowering/lower-classes.js +256 -33
  26. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  27. package/dist/frontend/lowering/lower-containers.js +25 -3
  28. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  29. package/dist/frontend/lowering/lower-exprs.d.ts +10 -0
  30. package/dist/frontend/lowering/lower-exprs.js +410 -9
  31. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  32. package/dist/frontend/lowering/lower-inspect.d.ts +16 -1
  33. package/dist/frontend/lowering/lower-inspect.js +132 -5
  34. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  35. package/dist/frontend/lowering/lower-island.js +24 -3
  36. package/dist/frontend/lowering/lower-island.js.map +1 -1
  37. package/dist/frontend/lowering/lower-modules.d.ts +6 -5
  38. package/dist/frontend/lowering/lower-modules.js +56 -18
  39. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  40. package/dist/frontend/lowering/lower-stmts.js +56 -0
  41. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  42. package/dist/frontend/lowering/lowerer.d.ts +57 -1
  43. package/dist/frontend/lowering/lowerer.js +291 -10
  44. package/dist/frontend/lowering/lowerer.js.map +1 -1
  45. package/dist/frontend/lowering/surfaces.d.ts +9 -2
  46. package/dist/frontend/lowering/surfaces.js +44 -7
  47. package/dist/frontend/lowering/surfaces.js.map +1 -1
  48. package/dist/frontend/npm.js +2 -2
  49. package/dist/frontend/npm.js.map +1 -1
  50. package/dist/frontend/program.js +59 -8
  51. package/dist/frontend/program.js.map +1 -1
  52. package/dist/frontend/resolve.js +120 -5
  53. package/dist/frontend/resolve.js.map +1 -1
  54. package/dist/frontend/shared.d.ts +8 -2
  55. package/dist/frontend/shared.js +22 -6
  56. package/dist/frontend/shared.js.map +1 -1
  57. package/dist/frontend/ts7/checker.js +1 -1
  58. package/dist/frontend/ts7/checker.js.map +1 -1
  59. package/dist/frontend/types.d.ts +25 -1
  60. package/dist/frontend/types.js +121 -7
  61. package/dist/frontend/types.js.map +1 -1
  62. package/dist/index.js +4 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/ir/nodes.d.ts +43 -1
  65. package/dist/ir/nodes.js +41 -0
  66. package/dist/ir/nodes.js.map +1 -1
  67. package/dist/ir/validate.js +38 -6
  68. package/dist/ir/validate.js.map +1 -1
  69. package/package.json +2 -2
  70. package/surface-manifest.json +67 -2
@@ -33,6 +33,10 @@ export type WidthLift = {
33
33
  how: "width";
34
34
  } | {
35
35
  how: "arr";
36
+ } | {
37
+ how: "tupleArr";
38
+ } | {
39
+ how: "emptyArr";
36
40
  } | {
37
41
  how: "objWidth";
38
42
  } | {
@@ -436,6 +440,12 @@ export declare class Lowerer {
436
440
  /** Synthetic URLSearchParams.forEach loop functions, interned per
437
441
  * callback arity/return — Map's pattern over the sp index walk. */
438
442
  readonly spHofHelpers: Map<string, string>;
443
+ /** The primitive-constructor VALUES (`String`/`Number`/`Boolean` as
444
+ * bare identifiers — CLI option tables store and compare them): one
445
+ * synthesized coercion function per constructor per program, interned
446
+ * here by name so every reference is the SAME zero-capture closure and
447
+ * `opt.type === String` is JS identity (see primitiveCtorClosure). */
448
+ readonly primitiveCtorFns: Map<string, string>;
439
449
  /** Optional-chain lowering state. While a chain body lowers, the guarded
440
450
  * receiver NODE reads as a chainRecv (typed by the narrowed arm) instead
441
451
  * of re-lowering, its checker type reads non-nullish (typeOf), and the
@@ -672,6 +682,17 @@ export declare class Lowerer {
672
682
  * point for deferred collection diagnostics: resolving a reference to a
673
683
  * broken declaration reports what collection deferred. */
674
684
  resolveValueSymbol(ident: ts.Identifier): ts.Symbol | null;
685
+ /** The default-snapshot storage symbol a DEFAULT-import alias chain
686
+ * lands on, or null. A mutable entity-name default (`export default
687
+ * someLet`) registers its Node-semantics snapshot global under the
688
+ * exporter's default ALIAS symbol; the checker's getAliasedSymbol
689
+ * resolves through that symbol to the live let, so this walk follows
690
+ * the default hops syntactically — default import clauses, `{ default
691
+ * as x }` specifiers, `export { default } from` re-exports — and stops
692
+ * at the first default symbol carrying registered storage. Local
693
+ * `export { x as default }` specifiers (no module specifier) are LIVE
694
+ * bindings in Node and fall through to ordinary alias resolution. */
695
+ private defaultSnapshotSymbolOf;
675
696
  /** A module's CJS export-table member symbol by NAME (the checker's
676
697
  * module-symbol exports map — present in tsgo even where no expando
677
698
  * property symbol exists at the attachment/use sites). */
@@ -734,6 +755,15 @@ export declare class Lowerer {
734
755
  collectNpmImports(parts: FileParts[]): void;
735
756
  collectJsonImports(parts: FileParts[]): void;
736
757
  run(): LowerResult;
758
+ /** The unregistered-class type sweep (run()'s last step before the
759
+ * module assembles): every `{kind:"object"}` TYPE naming a class with
760
+ * no registered ClassInfo is rewritten IN PLACE to the f64 dummy.
761
+ * classval types are exempt (they emit the class-independent
762
+ * `ScrClassObj *` — inert-but-valid storage, the validator's own
763
+ * stance), and only type objects rewrite — node-level classNames
764
+ * (`new`, upcasts) cannot reach here (their lowerings fence without a
765
+ * registered class), so the validator still backstops those. */
766
+ sanitizeUnregisteredClassTypes(roots: unknown[]): void;
737
767
  /** True when `t` (recursively) names a class instance type with no
738
768
  * registered ClassInfo — the shape of a JS class whose collection fenced.
739
769
  * Used by run()'s global pruning; shapes/unions recurse with a seen-set
@@ -904,6 +934,9 @@ export declare class Lowerer {
904
934
  * copy loop, arrayWidthHelper)
905
935
  * Null when the pair isn't in the relation — callers keep their fences. */
906
936
  widthLiftPlan(src: IrType, dst: IrType): WidthLift | null;
937
+ /** True for the unit-only element types (`(null | undefined)[]`, the
938
+ * `readonly []` mapping): a union whose every arm is a unit. */
939
+ unitOnlyElem(t: IrType): boolean;
907
940
  /** The build side of widthLiftPlan: the IrExpr converting `value` into
908
941
  * `dst` under a plan the caller validated. Interns whatever helpers the
909
942
  * lift needs (planned first, so the interns cannot fail — a failure here
@@ -932,6 +965,8 @@ export declare class Lowerer {
932
965
  } | {
933
966
  absent: true;
934
967
  utag: number;
968
+ } | {
969
+ absentDyn: true;
935
970
  }> | null;
936
971
  recordWidthHelper(fromId: string, toId: string, loc: SrcLoc): string | null;
937
972
  /** Interned `%arr.width.<n>(a)` — the per-element copy loop over
@@ -947,6 +982,16 @@ export declare class Lowerer {
947
982
  tupleArrayWidthHelper(fromId: string, toT: IrType & {
948
983
  kind: "array";
949
984
  }, loc: SrcLoc): string | null;
985
+ /** Interned `%arr.empty.<n>(a)` — the EMPTY-array lift's build side: a
986
+ * unit-only-element array reshapes into any data-element array by
987
+ * answering a FRESH empty array, after a runtime non-empty trap (a
988
+ * genuinely inhabited `(null | undefined)[]` cannot reshape — the
989
+ * catchable-TypeError stance every checked extraction takes). */
990
+ emptyArrayLiftHelper(fromT: IrType & {
991
+ kind: "array";
992
+ }, toT: IrType & {
993
+ kind: "array";
994
+ }, loc: SrcLoc): string;
950
995
  arrayWidthHelper(fromT: IrType & {
951
996
  kind: "array";
952
997
  }, toT: IrType & {
@@ -1145,6 +1190,17 @@ export declare class Lowerer {
1145
1190
  * impossible value flow on). Null when the target isn't a non-unit arm
1146
1191
  * of the union — those uses keep their erasure/fences. */
1147
1192
  narrowedArmHelper(fromId: string, target: IrType, loc: SrcLoc): string | null;
1193
+ /** The DEFERRED-INIT field read (`stream!: T` assigned past the
1194
+ * constructor's top level — the slot is `T | undefined`): interned
1195
+ * `%deferred.read.<n>(u)` extracting the declared type. SCALAR arms
1196
+ * whose JS-undefined behavior a unit default reproduces read that
1197
+ * default — bool false (conditions are exact: undefined and false are
1198
+ * both falsy; only printing/strict-equality could tell) and f64 NaN
1199
+ * (arithmetic and conditions exact) — while string and REF arms keep
1200
+ * the checked-extraction TRAP: JS itself TypeErrors the first member
1201
+ * use of such an undefined, so the catchable TypeError at the read is
1202
+ * the same failure, named earlier (SEMANTICS.md). */
1203
+ deferredReadHelper(fromId: string, target: IrType, loc: SrcLoc): string | null;
1148
1204
  /** True when a static value can become ONE island value: jsval itself,
1149
1205
  * anything boundary-safe (the deep JSON marshal), a record whose fields
1150
1206
  * all can (built as an island OBJECT literal, field by field), or an
@@ -1625,7 +1681,7 @@ export declare class Lowerer {
1625
1681
  rejectComptimeCaptures(cb: ts.ArrowFunction | ts.FunctionExpression): void;
1626
1682
  comptimeValueToIr(value: unknown, expected: IrType, path: string, blame: ts.Node): IrExpr;
1627
1683
  isConsoleLog(call: ts.CallExpression): boolean;
1628
- consoleCallMember(call: ts.CallExpression): "log" | "error" | "warn" | null;
1684
+ consoleCallMember(call: ts.CallExpression): "log" | "info" | "debug" | "error" | "warn" | null;
1629
1685
  builtinImportOf(ident: ts.Identifier): {
1630
1686
  module: string;
1631
1687
  member: string;
@@ -20,7 +20,7 @@ import { provenanceActive } from "../provenance-registry.js";
20
20
  import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, overridesDtsPath, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
21
21
  import { containsRecord, containsUnion, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../types.js";
22
22
  import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalMember, nodeTypesOnlySymbol } from "./surfaces.js";
23
- import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
23
+ import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
24
24
  import { registerBuiltinErrorClasses, registerBuiltinEmitterClass, registerBuiltinStreamClasses, builtinErrorInfoOf, builtinEmitterInfoOf, builtinStreamInfoOf, analyzeClassDecoration, classIteratorDrainCall, classIteratorNextCall, classIteratorOf, classIteratorOpenCall, classIteratorRestDrainCall, classMemberNameOf, classValueRef, collectClassShape, exactClassOfReceiver, collectClassShapeInner, ctorAbiEquals, findMethodOn, findStaticOn, findGenericMethodOn, findGenericStaticOn, genericClassInstanceType, isSubclassOf, inHierarchy, overrideBelow, staticShadowBelow, upcastTo, lowerClassMembers, lowerClassCtor, lowerClassExpression, lowerClassExpressionInfo, lowerClassMethodMember, lowerClassValueProperty, lowerStaticMethod, throwingSetterFn, fieldInitStmts, lowerStaticFieldInits, lowerStaticFieldRead, lowerDerivedCtorBody, superCallStmt, lowerSuperMethodCall, superThisRef, lowerSuperAccessorRead, lowerSuperAccessorWrite, inheritsBuiltinErrorCtor, inheritsBuiltinEmitterCtor, errorMessageArg, lowerNew, accessorCall } from "./lower-classes.js";
25
25
  import { mixinCallClassInfoOf, mixinIntersectionInstanceType } from "./lower-mixins.js";
26
26
  import { bindingNeverReassigned, bodyReadsArguments, isThisParameter, paramShape, paramShapes, checkDefaultParamBodyType, completeArgs, wrappedUndefined, undefinedArgFor, requireExactArityValue, bodyReturnType, declaredReturnType, collectSignature, collectSignatureInner, collectGenericSignature, genericFnOf, lowerGenericCall, lowerGenericFnValue, inferTypeParamBindings, lowerGenericInstance, lowerCall, lowerTimersMemberCall, lowerPromiseMethodCall, lowerFilterNarrowCall, isTopLevelFnSymbol, lowerNestedFunctionDecl, lambdaSignature, lowerLambda, lowerFunction } from "./lower-calls.js";
@@ -568,6 +568,12 @@ export class Lowerer {
568
568
  /** Synthetic URLSearchParams.forEach loop functions, interned per
569
569
  * callback arity/return — Map's pattern over the sp index walk. */
570
570
  spHofHelpers = new Map();
571
+ /** The primitive-constructor VALUES (`String`/`Number`/`Boolean` as
572
+ * bare identifiers — CLI option tables store and compare them): one
573
+ * synthesized coercion function per constructor per program, interned
574
+ * here by name so every reference is the SAME zero-capture closure and
575
+ * `opt.type === String` is JS identity (see primitiveCtorClosure). */
576
+ primitiveCtorFns = new Map();
571
577
  /** Optional-chain lowering state. While a chain body lowers, the guarded
572
578
  * receiver NODE reads as a chainRecv (typed by the narrowed arm) instead
573
579
  * of re-lowering, its checker type reads non-nullish (typeOf), and the
@@ -918,6 +924,16 @@ export class Lowerer {
918
924
  this.flushDeferred(symbol);
919
925
  return symbol;
920
926
  }
927
+ // DEFAULT-SNAPSHOT storage lives on the EXPORTER'S default alias
928
+ // symbol (`export default someLet` — collectGlobals registers the
929
+ // Node-semantics snapshot there). getAliasedSymbol would resolve
930
+ // PAST it to the live let; walk the default-import hops and stop at
931
+ // the first default symbol carrying storage instead.
932
+ const snap = this.defaultSnapshotSymbolOf(symbol);
933
+ if (snap) {
934
+ this.flushDeferred(snap);
935
+ return snap;
936
+ }
921
937
  symbol = this.checker.getAliasedSymbol(symbol);
922
938
  }
923
939
  // CommonJS export plumbing: a binding that resolved to a PROPERTY of a
@@ -933,6 +949,51 @@ export class Lowerer {
933
949
  this.flushDeferred(symbol);
934
950
  return symbol;
935
951
  }
952
+ /** The default-snapshot storage symbol a DEFAULT-import alias chain
953
+ * lands on, or null. A mutable entity-name default (`export default
954
+ * someLet`) registers its Node-semantics snapshot global under the
955
+ * exporter's default ALIAS symbol; the checker's getAliasedSymbol
956
+ * resolves through that symbol to the live let, so this walk follows
957
+ * the default hops syntactically — default import clauses, `{ default
958
+ * as x }` specifiers, `export { default } from` re-exports — and stops
959
+ * at the first default symbol carrying registered storage. Local
960
+ * `export { x as default }` specifiers (no module specifier) are LIVE
961
+ * bindings in Node and fall through to ordinary alias resolution. */
962
+ defaultSnapshotSymbolOf(alias) {
963
+ let sym = alias;
964
+ for (let hop = 0; sym !== undefined && hop < 32; hop++) {
965
+ if (hop > 0 && sym.flags & ts.SymbolFlags.Alias && this.globalsBySymbol.has(sym))
966
+ return sym;
967
+ const d = this.checker
968
+ .declarationsOf(sym)
969
+ .find((x) => ts.isImportClause(x) || ts.isImportSpecifier(x) || ts.isExportSpecifier(x));
970
+ let spec;
971
+ let name;
972
+ if (d && ts.isImportClause(d) && ts.isImportDeclaration(d.parent)) {
973
+ spec = d.parent.moduleSpecifier;
974
+ name = "default";
975
+ }
976
+ else if (d && ts.isImportSpecifier(d)) {
977
+ const idecl = d.parent.parent.parent;
978
+ if (ts.isImportDeclaration(idecl))
979
+ spec = idecl.moduleSpecifier;
980
+ name = (d.propertyName ?? d.name).text;
981
+ }
982
+ else if (d && ts.isExportSpecifier(d)) {
983
+ const edecl = d.parent.parent;
984
+ if (ts.isExportDeclaration(edecl))
985
+ spec = edecl.moduleSpecifier;
986
+ name = (d.propertyName ?? d.name).text;
987
+ }
988
+ if (d === undefined || spec === undefined || !ts.isStringLiteral(spec) || name !== "default")
989
+ return null;
990
+ const dep = resolveImport(this.program, d.getSourceFile(), spec.text);
991
+ if (!dep)
992
+ return null;
993
+ sym = defaultExportSymbolOf(this, dep) ?? undefined;
994
+ }
995
+ return null;
996
+ }
936
997
  /** A module's CJS export-table member symbol by NAME (the checker's
937
998
  * module-symbol exports map — present in tsgo even where no expando
938
999
  * property symbol exists at the attachment/use sites). */
@@ -1376,6 +1437,20 @@ export class Lowerer {
1376
1437
  // the emitted program references, flushing deferred class diagnostics
1377
1438
  // that a reached type makes relevant.
1378
1439
  const artifacts = this.moduleArtifacts(functions);
1440
+ // Types still naming a class that never REGISTERED after retention's
1441
+ // flush (JS graphs whose class fences deferred to runtime — the
1442
+ // sentence-walker's path params, printer tables whose func-typed
1443
+ // fields spell the fenced class): the emitter would name a struct
1444
+ // that does not exist — the compile-C escape family. No instance of
1445
+ // such a class can ever exist (every construction site fenced), so
1446
+ // the slots are inert by construction: rewrite each to the f64 dummy
1447
+ // placeholder (boxNewC's uncollected-class stance, applied to unboxed
1448
+ // slots), uniformly across params/locals/globals/fields/body types so
1449
+ // every producer and consumer agrees. Programs with no unregistered
1450
+ // reference are untouched — byte-stability holds.
1451
+ if (this.diags.length === 0) {
1452
+ this.sanitizeUnregisteredClassTypes([functions, this.globalsList, artifacts.classes, artifacts.records, artifacts.unions]);
1453
+ }
1379
1454
  const module = this.diags.length > 0
1380
1455
  ? null
1381
1456
  : {
@@ -1400,6 +1475,45 @@ export class Lowerer {
1400
1475
  ...(this.npmLazyTraps ? { npmLazyTraps: this.npmLazyTraps } : {}),
1401
1476
  };
1402
1477
  }
1478
+ /** The unregistered-class type sweep (run()'s last step before the
1479
+ * module assembles): every `{kind:"object"}` TYPE naming a class with
1480
+ * no registered ClassInfo is rewritten IN PLACE to the f64 dummy.
1481
+ * classval types are exempt (they emit the class-independent
1482
+ * `ScrClassObj *` — inert-but-valid storage, the validator's own
1483
+ * stance), and only type objects rewrite — node-level classNames
1484
+ * (`new`, upcasts) cannot reach here (their lowerings fence without a
1485
+ * registered class), so the validator still backstops those. */
1486
+ sanitizeUnregisteredClassTypes(roots) {
1487
+ const isUnregisteredObjectType = (v) => typeof v === "object" && v !== null &&
1488
+ v.kind === "object" &&
1489
+ typeof v.className === "string" &&
1490
+ !this.classes.has(v.className);
1491
+ const sweep = (node) => {
1492
+ if (node === null || typeof node !== "object")
1493
+ return;
1494
+ if (Array.isArray(node)) {
1495
+ node.forEach((item, i) => {
1496
+ if (isUnregisteredObjectType(item))
1497
+ node[i] = F64;
1498
+ else
1499
+ sweep(item);
1500
+ });
1501
+ return;
1502
+ }
1503
+ const rec = node;
1504
+ for (const key of Object.keys(rec)) {
1505
+ if (key === "loc")
1506
+ continue;
1507
+ const v = rec[key];
1508
+ if (isUnregisteredObjectType(v))
1509
+ rec[key] = F64;
1510
+ else
1511
+ sweep(v);
1512
+ }
1513
+ };
1514
+ for (const root of roots)
1515
+ sweep(root);
1516
+ }
1403
1517
  /** True when `t` (recursively) names a class instance type with no
1404
1518
  * registered ClassInfo — the shape of a JS class whose collection fenced.
1405
1519
  * Used by run()'s global pruning; shapes/unions recurse with a seen-set
@@ -2459,9 +2573,15 @@ export class Lowerer {
2459
2573
  }
2460
2574
  if (expected.kind === "array" && expr.type.kind === "array" && expected.elem.kind !== "jsval") {
2461
2575
  const helper = this.arrayWidthHelper(expr.type, expected, expr.loc);
2462
- if (!helper)
2576
+ if (helper)
2577
+ return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
2578
+ // The EMPTY-array lift (widthLiftPlan's emptyArr rule), top-level:
2579
+ // `cmd.aliases` typed `(null | undefined)[]` (an `aliases: []`
2580
+ // table) flowing into a `string[]` slot.
2581
+ const lift = this.widthLiftPlan(expr.type, expected);
2582
+ if (lift?.how !== "emptyArr")
2463
2583
  return null;
2464
- return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
2584
+ return this.applyWidthLift(lift, expr, expected, expr.loc);
2465
2585
  }
2466
2586
  // A TUPLE flowing into an array slot (`const NAMES = [...] as const`
2467
2587
  // assigned to a `readonly T[]` — the const-table pattern): TS erases
@@ -2547,10 +2667,39 @@ export class Lowerer {
2547
2667
  return this.recordToClassPlan(src.shapeId, dst.className) !== null ? { how: "clsWidth" } : null;
2548
2668
  }
2549
2669
  if (dst.kind === "array" && src.kind === "array") {
2550
- return this.widthLiftPlan(src.elem, dst.elem) !== null ? { how: "arr" } : null;
2670
+ if (this.widthLiftPlan(src.elem, dst.elem) !== null)
2671
+ return { how: "arr" };
2672
+ // The EMPTY-array lift: a unit-only element type (`readonly []`
2673
+ // mapped as the unit-element array, `(null | undefined)[]`) has no
2674
+ // per-element conversion into a data element — but the only value
2675
+ // such a slot honestly holds in the width family is EMPTY, so the
2676
+ // lift is a fresh empty array of the target type, guarded by a
2677
+ // runtime non-empty trap (the checked-extraction stance).
2678
+ if (this.unitOnlyElem(src.elem) && dst.elem.kind !== "jsval" && !this.unitOnlyElem(dst.elem)) {
2679
+ return { how: "emptyArr" };
2680
+ }
2681
+ return null;
2682
+ }
2683
+ // A TUPLE flowing into an array FIELD/ELEMENT (`aliases: ["ls"]` into
2684
+ // an `aliases: string[]` slot): per-position lifts, the top-level
2685
+ // tuple-into-array coercion applied recursively.
2686
+ if (dst.kind === "array" && src.kind === "record" && dst.elem.kind !== "jsval") {
2687
+ const from = this.shapes.get(src.shapeId);
2688
+ if (from?.tuple && from.fields.every((f) => this.widthLiftPlan(f.type, dst.elem) !== null)) {
2689
+ return { how: "tupleArr" };
2690
+ }
2691
+ return null;
2551
2692
  }
2552
2693
  return null;
2553
2694
  }
2695
+ /** True for the unit-only element types (`(null | undefined)[]`, the
2696
+ * `readonly []` mapping): a union whose every arm is a unit. */
2697
+ unitOnlyElem(t) {
2698
+ if (t.kind !== "union")
2699
+ return false;
2700
+ const def = this.unions.get(t.unionId);
2701
+ return def !== undefined && def.arms.every((a) => isUnitType(a));
2702
+ }
2554
2703
  /** The build side of widthLiftPlan: the IrExpr converting `value` into
2555
2704
  * `dst` under a plan the caller validated. Interns whatever helpers the
2556
2705
  * lift needs (planned first, so the interns cannot fail — a failure here
@@ -2597,6 +2746,20 @@ export class Lowerer {
2597
2746
  throw new Error("lowerer bug: planned arr lift failed to intern");
2598
2747
  return { kind: "call", callee: helper, args: [value], type: dst, loc };
2599
2748
  }
2749
+ case "tupleArr": {
2750
+ if (dst.kind !== "array" || value.type.kind !== "record")
2751
+ throw new Error("lowerer bug: tupleArr lift shape");
2752
+ const helper = this.tupleArrayWidthHelper(value.type.shapeId, dst, loc);
2753
+ if (!helper)
2754
+ throw new Error("lowerer bug: planned tupleArr lift failed to intern");
2755
+ return { kind: "call", callee: helper, args: [value], type: dst, loc };
2756
+ }
2757
+ case "emptyArr": {
2758
+ if (dst.kind !== "array" || value.type.kind !== "array")
2759
+ throw new Error("lowerer bug: emptyArr lift shape");
2760
+ const helper = this.emptyArrayLiftHelper(value.type, dst, loc);
2761
+ return { kind: "call", callee: helper, args: [value], type: dst, loc };
2762
+ }
2600
2763
  case "objWidth": {
2601
2764
  if (dst.kind !== "record" || value.type.kind !== "object")
2602
2765
  throw new Error("lowerer bug: objWidth lift shape");
@@ -2660,10 +2823,19 @@ export class Lowerer {
2660
2823
  if (!ff) {
2661
2824
  // A target field MISSING on the source: legal exactly when it is
2662
2825
  // optional-flavored (an undefined-armed union) — the unset field
2663
- // IS the undefined arm, the same rule literal completion applies.
2664
- // Never for tuples: a completed position would change .length
2665
- // and JSON where Node keeps the source arity.
2666
- if (from.tuple || tf.type.kind !== "union")
2826
+ // IS the undefined arm, the same rule literal completion applies
2827
+ // — or 'unknown' (a dyn slot holds the DOM undefined, exactly
2828
+ // the absent-property read: the options-record call shape
2829
+ // against `{ plugins: unknown, ... }`). Never for tuples: a
2830
+ // completed position would change .length and JSON where Node
2831
+ // keeps the source arity.
2832
+ if (from.tuple)
2833
+ return null;
2834
+ if (tf.type.kind === "dyn") {
2835
+ plan.set(tf.name, { absentDyn: true });
2836
+ continue;
2837
+ }
2838
+ if (tf.type.kind !== "union")
2667
2839
  return null;
2668
2840
  const def = this.unions.get(tf.type.unionId);
2669
2841
  const utag = def ? def.arms.findIndex((a) => a.kind === "undefinedT") : -1;
@@ -2717,6 +2889,11 @@ export class Lowerer {
2717
2889
  kind: "recordLit",
2718
2890
  fields: to.fields.map((f) => {
2719
2891
  const lift = plan.get(f.name);
2892
+ if ("absentDyn" in lift) {
2893
+ // The unset 'unknown' field: the DOM undefined — exactly
2894
+ // the absent-property read's answer.
2895
+ return { name: f.name, value: dynUndefinedExpr(loc) };
2896
+ }
2720
2897
  if ("absent" in lift) {
2721
2898
  if (f.type.kind !== "union")
2722
2899
  throw new Error("lowerer bug: absent lift against a non-union field");
@@ -2796,6 +2973,57 @@ export class Lowerer {
2796
2973
  });
2797
2974
  return name;
2798
2975
  }
2976
+ /** Interned `%arr.empty.<n>(a)` — the EMPTY-array lift's build side: a
2977
+ * unit-only-element array reshapes into any data-element array by
2978
+ * answering a FRESH empty array, after a runtime non-empty trap (a
2979
+ * genuinely inhabited `(null | undefined)[]` cannot reshape — the
2980
+ * catchable-TypeError stance every checked extraction takes). */
2981
+ emptyArrayLiftHelper(fromT, toT, loc) {
2982
+ const key = `emptyarr:${typeKey(fromT.elem)}:${typeKey(toT.elem)}`;
2983
+ const existing = this.widthHelpers.get(key);
2984
+ if (existing)
2985
+ return existing;
2986
+ const name = `%arr.empty.${this.widthHelpers.size}`;
2987
+ this.widthHelpers.set(key, name);
2988
+ const a = { kind: "varRef", localId: "a.0", type: fromT, loc };
2989
+ this.liftedFns.push({
2990
+ name,
2991
+ params: [{ localId: "a.0", name: "a", type: fromT }],
2992
+ returnType: toT,
2993
+ locals: [{ id: "a.0", name: "a", type: fromT, mutable: true }],
2994
+ body: [
2995
+ {
2996
+ kind: "if",
2997
+ cond: {
2998
+ kind: "bin",
2999
+ op: "!==",
3000
+ left: { kind: "arrIntrinsic", method: "length", receiver: a, args: [], type: F64, loc },
3001
+ right: { kind: "numLit", value: 0, type: F64, loc },
3002
+ type: BOOL,
3003
+ loc,
3004
+ },
3005
+ then: [
3006
+ {
3007
+ kind: "throw",
3008
+ value: {
3009
+ kind: "libCall",
3010
+ fn: "error.new",
3011
+ args: [{ kind: "strLit", value: `expected ${this.fmt(toT)} (a non-empty ${this.fmt(fromT)} has no elements the target can hold)`, type: STRING, loc }],
3012
+ type: { kind: "object", className: "%TypeError" },
3013
+ loc,
3014
+ },
3015
+ loc,
3016
+ },
3017
+ ],
3018
+ else_: [],
3019
+ loc,
3020
+ },
3021
+ { kind: "return", value: { kind: "arrayLit", elems: [], type: toT, loc }, loc },
3022
+ ],
3023
+ loc,
3024
+ });
3025
+ return name;
3026
+ }
2799
3027
  arrayWidthHelper(fromT, toT, loc) {
2800
3028
  const fromElem = fromT.elem;
2801
3029
  const toElem = toT.elem;
@@ -3932,6 +4160,59 @@ export class Lowerer {
3932
4160
  });
3933
4161
  return name;
3934
4162
  }
4163
+ /** The DEFERRED-INIT field read (`stream!: T` assigned past the
4164
+ * constructor's top level — the slot is `T | undefined`): interned
4165
+ * `%deferred.read.<n>(u)` extracting the declared type. SCALAR arms
4166
+ * whose JS-undefined behavior a unit default reproduces read that
4167
+ * default — bool false (conditions are exact: undefined and false are
4168
+ * both falsy; only printing/strict-equality could tell) and f64 NaN
4169
+ * (arithmetic and conditions exact) — while string and REF arms keep
4170
+ * the checked-extraction TRAP: JS itself TypeErrors the first member
4171
+ * use of such an undefined, so the catchable TypeError at the read is
4172
+ * the same failure, named earlier (SEMANTICS.md). */
4173
+ deferredReadHelper(fromId, target, loc) {
4174
+ if (target.kind !== "bool" && target.kind !== "f64") {
4175
+ return this.narrowedArmHelper(fromId, target, loc);
4176
+ }
4177
+ const from = this.unions.get(fromId);
4178
+ const tag = this.armTag(fromId, target);
4179
+ const utag = from ? from.arms.findIndex((a) => a.kind === "undefinedT") : -1;
4180
+ if (!from || tag < 0 || utag < 0)
4181
+ return null;
4182
+ const key = `deferred:${fromId}:${tag}`;
4183
+ const existing = this.narrowHelpers.get(key);
4184
+ if (existing)
4185
+ return existing;
4186
+ const name = `%deferred.read.${this.narrowHelpers.size}`;
4187
+ this.narrowHelpers.set(key, name);
4188
+ const fromT = { kind: "union", unionId: fromId };
4189
+ const u = { kind: "varRef", localId: "u.0", type: fromT, loc };
4190
+ const dflt = target.kind === "bool"
4191
+ ? { kind: "boolLit", value: false, type: BOOL, loc }
4192
+ : { kind: "numLit", value: NaN, type: F64, loc };
4193
+ this.liftedFns.push({
4194
+ name,
4195
+ params: [{ localId: "u.0", name: "u", type: fromT }],
4196
+ returnType: target,
4197
+ locals: [{ id: "u.0", name: "u", type: fromT, mutable: true }],
4198
+ body: [
4199
+ {
4200
+ kind: "if",
4201
+ cond: { kind: "unionIsTag", unionId: fromId, tag: utag, negated: false, value: u, type: BOOL, loc },
4202
+ then: [{ kind: "return", value: dflt, loc }],
4203
+ else_: null,
4204
+ loc,
4205
+ },
4206
+ {
4207
+ kind: "return",
4208
+ value: { kind: "unionNarrow", unionId: fromId, tag, value: u, type: target, loc },
4209
+ loc,
4210
+ },
4211
+ ],
4212
+ loc,
4213
+ });
4214
+ return name;
4215
+ }
3935
4216
  /** True when a static value can become ONE island value: jsval itself,
3936
4217
  * anything boundary-safe (the deep JSON marshal), a record whose fields
3937
4218
  * all can (built as an island OBJECT literal, field by field), or an
@@ -4851,7 +5132,7 @@ export class Lowerer {
4851
5132
  // unresolvable ones never collected, so they skip here too.
4852
5133
  const baseName = ts.isMethodDeclaration(fnLike)
4853
5134
  ? classMemberNameOf(this, fnLike.name)
4854
- : ts.isIdentifier(fnLike.name)
5135
+ : ts.isIdentifier(fnLike.name) || ts.isPrivateIdentifier(fnLike.name)
4855
5136
  ? fnLike.name.text
4856
5137
  : null;
4857
5138
  if (baseName === null)
@@ -5947,7 +6228,7 @@ export class Lowerer {
5947
6228
  if (module !== null)
5948
6229
  return { module, member: access.name.text };
5949
6230
  // The RE-EXPORT FACADE's namespace spelling: `import * as assert from
5950
- // "./facade.js"` over `export { ok } from "node:assert"` (prettier's
6231
+ // "./facade.js"` over `export { ok } from "node:assert"` (the formatter idiom's
5951
6232
  // universal/assert). The member's symbol is the facade's
5952
6233
  // ExportSpecifier, and builtinImportOf's alias chase answers the
5953
6234
  // builtin's own module/member — the same tables as a direct import;