@scriptc/compiler 0.0.8 → 0.0.10

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 (90) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +13 -0
  2. package/dist/backend/cc.js +14 -0
  3. package/dist/backend/cc.js.map +1 -1
  4. package/dist/backend/emission/emit-exprs.js +244 -17
  5. package/dist/backend/emission/emit-exprs.js.map +1 -1
  6. package/dist/backend/emission/emit-types.d.ts +3 -0
  7. package/dist/backend/emission/emit-types.js +12 -0
  8. package/dist/backend/emission/emit-types.js.map +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +7 -5
  10. package/dist/backend/emission/emit-walkers.js +52 -12
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +1 -1
  13. package/dist/backend/emission/emitter.js +30 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/llvm/dyn.d.ts +1 -0
  16. package/dist/backend/llvm/dyn.js +83 -1
  17. package/dist/backend/llvm/dyn.js.map +1 -1
  18. package/dist/backend/llvm/emitter.js +357 -20
  19. package/dist/backend/llvm/emitter.js.map +1 -1
  20. package/dist/diagnostics/diagnostic.d.ts +73 -14
  21. package/dist/diagnostics/diagnostic.js +112 -31
  22. package/dist/diagnostics/diagnostic.js.map +1 -1
  23. package/dist/diagnostics/render.js +8 -0
  24. package/dist/diagnostics/render.js.map +1 -1
  25. package/dist/frontend/lowering/lib-boundary.js +11 -1
  26. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  27. package/dist/frontend/lowering/lower-assert.js +16 -0
  28. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  29. package/dist/frontend/lowering/lower-builtins.d.ts +24 -0
  30. package/dist/frontend/lowering/lower-builtins.js +256 -3
  31. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  32. package/dist/frontend/lowering/lower-calls.d.ts +71 -11
  33. package/dist/frontend/lowering/lower-calls.js +942 -18
  34. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  35. package/dist/frontend/lowering/lower-classes.js +27 -1
  36. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  37. package/dist/frontend/lowering/lower-containers.d.ts +21 -2
  38. package/dist/frontend/lowering/lower-containers.js +386 -53
  39. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  40. package/dist/frontend/lowering/lower-dgram.js +92 -17
  41. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  42. package/dist/frontend/lowering/lower-emitter.js +54 -0
  43. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.js +231 -17
  45. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  46. package/dist/frontend/lowering/lower-modules.js +33 -2
  47. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  48. package/dist/frontend/lowering/lower-namespaces.d.ts +25 -11
  49. package/dist/frontend/lowering/lower-namespaces.js +50 -15
  50. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  51. package/dist/frontend/lowering/lower-server.js +211 -4
  52. package/dist/frontend/lowering/lower-server.js.map +1 -1
  53. package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
  54. package/dist/frontend/lowering/lower-stmts.js +660 -19
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lower-stream.js +92 -5
  57. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  58. package/dist/frontend/lowering/lowerer.d.ts +102 -15
  59. package/dist/frontend/lowering/lowerer.js +378 -30
  60. package/dist/frontend/lowering/lowerer.js.map +1 -1
  61. package/dist/frontend/lowering/surfaces.js +20 -3
  62. package/dist/frontend/lowering/surfaces.js.map +1 -1
  63. package/dist/frontend/npm-static-rewrite.d.ts +7 -2
  64. package/dist/frontend/npm-static-rewrite.js +566 -138
  65. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  66. package/dist/frontend/npm-static.js +11 -2
  67. package/dist/frontend/npm-static.js.map +1 -1
  68. package/dist/frontend/program.js +5 -5
  69. package/dist/frontend/program.js.map +1 -1
  70. package/dist/frontend/shared.d.ts +6 -1
  71. package/dist/frontend/shared.js +46 -1
  72. package/dist/frontend/shared.js.map +1 -1
  73. package/dist/frontend/types.d.ts +13 -0
  74. package/dist/frontend/types.js +88 -9
  75. package/dist/frontend/types.js.map +1 -1
  76. package/dist/index.js +169 -32
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +230 -12
  79. package/dist/ir/nodes.js +85 -6
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +109 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/fence-eval.d.ts +77 -0
  84. package/dist/library/fence-eval.js +385 -0
  85. package/dist/library/fence-eval.js.map +1 -0
  86. package/dist/library/profile.d.ts +11 -1
  87. package/dist/library/profile.js +102 -18
  88. package/dist/library/profile.js.map +1 -1
  89. package/package.json +2 -2
  90. package/surface-manifest.json +10 -3
@@ -26,7 +26,7 @@ import { mixinCallClassInfoOf, mixinIntersectionInstanceType } from "./lower-mix
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";
27
27
  import { lowerArrayMethodCall, lowerBufferStaticCall, lowerBytesMethodCall, lowerBytesNew, lowerMapMethodCall, lowerMapForEachCall, buildMapForEachFn, lowerRecordOvfCaptureHelper, lowerEnvToPairsHelper, lowerSetMethodCall, lowerSetForEachCall, buildSetForEachFn, lowerRegexMethodCall, lowerStringMethodCall } from "./lower-containers.js";
28
28
  import { lowerStreamModuleCall } from "./lower-stream.js";
29
- import { builtinImportOf, lowerBuiltinModuleCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerFsConstantsProperty, lowerHttp2ConstantsProperty, http2ConstantBindingOf, http2ConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall } from "./lower-builtins.js";
29
+ import { builtinImportOf, lowerBuiltinModuleCall, lowerFsToUnixTimestampCall, lowerFsLadderCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerFsConstantsProperty, lowerHttp2ConstantsProperty, http2ConstantBindingOf, http2ConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall } from "./lower-builtins.js";
30
30
  import { isIslandExpr, islandFuncValueFence, islandRegexpOf, jsvalIn, requireDynamicApi, islandGlobalFnOf, lowerDynamicImportCall, lowerFetchCall, lowerIslandMethodCall, lowerMathProperty, npmPackageOf, npmMemberFence, npmPackageOfSymbol } from "./lower-island.js";
31
31
  import { lowerHttpHeadersElement, lowerNetModuleCall, lowerServerMethodCall, lowerServerProperty } from "./lower-server.js";
32
32
  import { lowerDgramDnsModuleCall, lowerDgramMethodCall } from "./lower-dgram.js";
@@ -280,6 +280,45 @@ export function dynUndefinedExpr(loc) {
280
280
  loc,
281
281
  };
282
282
  }
283
+ /** An always-throwing Node-parity error expression (the error.nodeThrow
284
+ * libCall — the lowered form of arms Node rejects unconditionally:
285
+ * ERR_INVALID_THIS receivers, ERR_MISSING_ARGS arity ladders, the
286
+ * symbol-to-string TypeError). kind 0 Error / 1 TypeError / 2 RangeError;
287
+ * an empty code means no code slot. `type` is the replaced expression's
288
+ * own (never materialized — the global.undefRead pattern). */
289
+ export function nodeThrowExpr(kind, code, message, type, loc) {
290
+ return {
291
+ kind: "libCall",
292
+ fn: "error.nodeThrow",
293
+ args: [
294
+ { kind: "numLit", value: kind, type: F64, loc },
295
+ { kind: "strLit", value: code, type: STRING, loc },
296
+ { kind: "strLit", value: message, type: STRING, loc },
297
+ ],
298
+ type,
299
+ loc,
300
+ };
301
+ }
302
+ /** The post-validation fence STRING a validation-ladder Chk libCall
303
+ * throws after its Node-order checks pass: the same SC2020 text the
304
+ * per-statement runtime fence would have thrown (message + "[code at
305
+ * file:line]"), rendered eagerly so the runtime can throw it verbatim
306
+ * (scr_throw_lowering_fence). The diagnostic joins the runtime-fence
307
+ * ledger exactly like a deferred statement fence — nothing silently
308
+ * drops off the coverage report. */
309
+ export function ladderFenceExpr(L, surface, node, hint) {
310
+ const loc = locOf(node);
311
+ const d = noLoweringDiag(surface, loc, hint);
312
+ L.runtimeFences.push(d);
313
+ const sf = node.getSourceFile();
314
+ const pos = ts.getLineAndCharacterOfPosition(sf, loc.start);
315
+ return {
316
+ kind: "strLit",
317
+ value: `${d.message} [${d.code} at ${loc.file}:${pos.line + 1}]`,
318
+ type: STRING,
319
+ loc,
320
+ };
321
+ }
283
322
  /** The checked-dynamic declaration fallback for unmappable binding types
284
323
  * (see irTypeOf), two gates over one story:
285
324
  *
@@ -458,6 +497,29 @@ export class Lowerer {
458
497
  /** Per-symbol result of the never-reassigned file scan
459
498
  * (bindingNeverReassigned — object-literal generic-method receivers). */
460
499
  neverReassignedCache = new Map();
500
+ /** TRAP bindings: declarations whose initializer provably throws before
501
+ * producing a value (its chain roots at an ambient-undefined name —
502
+ * ambientUndefVarRootOf). Module init unwinds at the declaration, so no
503
+ * reference to the binding can ever execute; the statement lowers to the
504
+ * root's throw, no storage exists, and references lower to the same
505
+ * trap shape (never reached — sound whatever the type). */
506
+ trapBindings = new Set();
507
+ /** NULLISH bindings of unmappable (generic-signature) types: `const i:
508
+ * I<A & B> = null as any` — the binding provably holds null/undefined
509
+ * forever (every write's RHS is nullish too), so no storage exists and
510
+ * each READ knows the value. Member reads and method calls through one
511
+ * lower to Node's exact TypeError ("Cannot read properties of null
512
+ * (reading 'fn')"); nullish-to-nullish flows lower to nothing. The map
513
+ * answers which unit the binding holds (the TypeError names it). Null
514
+ * entries cache probed non-qualifiers. */
515
+ nullishBindings = new Map();
516
+ /** DEAD bindings of unmappable types: never READ anywhere in the
517
+ * program, declared with no initializer or a value-only one (a function
518
+ * literal), every write's RHS side-effect-free. Node materializes the
519
+ * value and drops it — zero observable effect — so the declaration and
520
+ * its writes lower to nothing, and no type fence fires for a value the
521
+ * program never consumes. */
522
+ deadBindings = new Set();
461
523
  /** IMPLICIT-ANY function-value bindings (npm-static JS — `const knownBy
462
524
  * = (cmd) => ...`), by their VariableDeclaration: the registered info,
463
525
  * or null for probed non-qualifiers (implicitLocalFnNodeOf). */
@@ -902,7 +964,11 @@ export class Lowerer {
902
964
  const recvDecl = recvDecls.find(ts.isImportClause) ?? recvDecls[0];
903
965
  if (recvDecl && ts.isVariableDeclaration(recvDecl) && recvDecl.initializer) {
904
966
  const spec = requireSpecOf(recvDecl.initializer);
905
- const dep = spec !== null ? resolveImport(this.program, recvDecl.getSourceFile(), spec) : null;
967
+ const dep = spec === null
968
+ ? null
969
+ : isRelativeSpecifier(spec)
970
+ ? resolveImport(this.program, recvDecl.getSourceFile(), spec)
971
+ : npmStaticDepSf7(this.program, recvDecl.getSourceFile(), spec);
906
972
  if (dep)
907
973
  symbol = this.cjsModuleExportSymbol(dep, ident.text);
908
974
  }
@@ -1099,12 +1165,14 @@ export class Lowerer {
1099
1165
  }
1100
1166
  /** True when `expr` is an identifier bound by a top-level
1101
1167
  * `const x = require("./local")` of a RELATIVE module — the CommonJS
1102
- * namespace binding — or by a DEFAULT import of a CommonJS JS module
1103
- * (`import d from "./lib.cjs"`: Node binds d to module.exports, the
1104
- * same value require answers). Member accesses on it resolve through
1105
- * the export table (property symbols → resolveValueSymbol); the bare
1106
- * value keeps the namespace-object fence, like ESM namespace imports
1107
- * of builtins. */
1168
+ * namespace binding — or of a bare specifier naming an opted-in
1169
+ * --npm-static package (its CJS entry is a program module, so the
1170
+ * binding is the same namespace over the same export table), or by a
1171
+ * DEFAULT import of a CommonJS JS module (`import d from "./lib.cjs"`:
1172
+ * Node binds d to module.exports, the same value require answers).
1173
+ * Member accesses on it resolve through the export table (property
1174
+ * symbols → resolveValueSymbol); the bare value keeps the
1175
+ * namespace-object fence, like ESM namespace imports of builtins. */
1108
1176
  cjsLocalModuleBindingOf(expr) {
1109
1177
  if (!ts.isIdentifier(expr))
1110
1178
  return false;
@@ -1122,8 +1190,12 @@ export class Lowerer {
1122
1190
  return false;
1123
1191
  }
1124
1192
  const spec = requireSpecOf(decl.initializer);
1125
- if (spec === null || !isRelativeSpecifier(spec))
1193
+ if (spec === null)
1194
+ return false;
1195
+ if (!isRelativeSpecifier(spec) &&
1196
+ npmStaticDepSf7(this.program, decl.getSourceFile(), spec) === null) {
1126
1197
  return false;
1198
+ }
1127
1199
  }
1128
1200
  // SINGLE-VALUE exporters (`module.exports = Countdown` / `= double` /
1129
1201
  // `= 42`): the requirer's binding IS the exported value, not a
@@ -1222,13 +1294,22 @@ export class Lowerer {
1222
1294
  /** The lowering of a CommonJS `require("./local")` occurrence: a call of
1223
1295
  * the required module's run-once %init at exactly this statement's
1224
1296
  * position — Node's inline evaluation, with the guard supplying the
1225
- * cache-hit behavior for every require after the first. Null when the
1226
- * specifier is not a relative program module (builtins load nothing;
1227
- * anything else kept its preflight fence). */
1297
+ * cache-hit behavior for every require after the first. Bare specifiers
1298
+ * naming an opted-in --npm-static package resolve to that package's
1299
+ * program entry (the same edge preflight admitted — bundle dists require
1300
+ * their external dependencies by name). Null for everything else
1301
+ * (builtins load nothing; the rest kept its preflight fence). */
1228
1302
  requireInitStmt(spec, node) {
1229
- if (!isRelativeSpecifier(spec))
1230
- return null;
1231
- const dep = resolveImport(this.program, node.getSourceFile(), spec);
1303
+ // Relative requires resolve within the program; a BARE require can be
1304
+ // a program-module edge too when it names an opted-in --npm-static
1305
+ // package (one package requiring another — the resolution answered
1306
+ // its shipped JS, the file is in the module order, and the reads
1307
+ // alias its globals). Without the guarded %init call at this position
1308
+ // those globals stay uninitialized: the dep's module body would never
1309
+ // run.
1310
+ const dep = isRelativeSpecifier(spec)
1311
+ ? resolveImport(this.program, node.getSourceFile(), spec)
1312
+ : npmStaticDepSf7(this.program, node.getSourceFile(), spec);
1232
1313
  if (!dep || dep.fileName.endsWith(".json"))
1233
1314
  return null;
1234
1315
  const initName = this.initNameOf.get(dep);
@@ -1830,8 +1911,8 @@ export class Lowerer {
1830
1911
  throw new PoisonError();
1831
1912
  }
1832
1913
  // Would-be records with INDEX SIGNATURES (`Record<string, T>`,
1833
- // `{ [k: string]: T }`) get a message that points at Map<K, V> — the
1834
- // container that holds unbounded keys — instead of the generic
1914
+ // `{ [k: string]: T }`) get the index-signature fence (SC2006) naming
1915
+ // the supported key/value domain instead of the generic
1835
1916
  // supported-types recitation.
1836
1917
  if (widened.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection) &&
1837
1918
  this.checker.getCallSignatures(widened).length === 0 &&
@@ -1950,9 +2031,27 @@ export class Lowerer {
1950
2031
  }
1951
2032
  }
1952
2033
  // STANDARD-LIBRARY nominal types with no lowering at all: the SC2020
1953
- // story, naming the type.
2034
+ // story, naming the type — and for the families with a WHY, the same
2035
+ // pointed reason the identifier/constructor chokepoints teach.
1954
2036
  if (stdlibNominal) {
1955
- this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node)));
2037
+ const intlHint = "Intl formatter values have no representation — the COMPOSED en-US forms lower: " +
2038
+ 'new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; ' +
2039
+ "the rest is ICU locale data the binary does not carry";
2040
+ const typeHints = {
2041
+ ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage " +
2042
+ "(new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
2043
+ SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
2044
+ NumberFormat: intlHint,
2045
+ DateTimeFormat: intlHint,
2046
+ DurationFormat: intlHint,
2047
+ PluralRules: intlHint,
2048
+ Collator: intlHint,
2049
+ ListFormat: intlHint,
2050
+ RelativeTimeFormat: intlHint,
2051
+ Segmenter: intlHint,
2052
+ DisplayNames: intlHint,
2053
+ };
2054
+ this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node), typeHints[stdlibOwnSym?.name ?? ""]));
1956
2055
  throw new PoisonError();
1957
2056
  }
1958
2057
  // USER record shapes blocked by ONE member (SC2009's record arm):
@@ -2147,8 +2246,10 @@ export class Lowerer {
2147
2246
  }
2148
2247
  /** Exact-shape enforcement (SC2002). Records are monomorphic structs, so
2149
2248
  * everywhere a value flows into a typed slot (call arg, initializer,
2150
- * assignment, field, return) the shapes must MATCH — TS's structural width
2151
- * subtyping (`{a, b}` where `{a}` is expected) has no runtime coercion.
2249
+ * assignment, field, return) the shapes must MATCH — or width-coerce:
2250
+ * coerceToExpected already ran widthCoerce (the copy-reshape family),
2251
+ * so what reaches here is the RESIDUE its rules decline, and the
2252
+ * diagnostic names the first blocking rule (describeRecordWidthBlocker).
2152
2253
  * Non-record mismatches are not reachable through tsc-clean programs; the
2153
2254
  * validator ICEs on them as the usual backstop. */
2154
2255
  requireExactShape(node, actual, expected) {
@@ -2207,7 +2308,13 @@ export class Lowerer {
2207
2308
  throw new PoisonError();
2208
2309
  }
2209
2310
  if (containsRecord(actual) || containsRecord(expected)) {
2210
- this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node)));
2311
+ // A record→record pair gets the pointed story: WHICH width rule
2312
+ // declined (a field that doesn't lift, a required field with no
2313
+ // source, an index signature that could hold the completed key).
2314
+ const detail = actual.kind === "record" && expected.kind === "record"
2315
+ ? (this.describeRecordWidthBlocker(actual.shapeId, expected.shapeId) ?? undefined)
2316
+ : undefined;
2317
+ this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
2211
2318
  throw new PoisonError();
2212
2319
  }
2213
2320
  // Everything else — a plain-kind mismatch like a string flowing into a
@@ -2297,6 +2404,21 @@ export class Lowerer {
2297
2404
  return expr;
2298
2405
  }
2299
2406
  if (expr.type.kind === "jsval" && expected.kind !== "jsval") {
2407
+ // The jsval→DOM crossing (the world unification's engine-handle
2408
+ // kind): an 'any'-typed engine value flowing into an 'unknown'/
2409
+ // 'object'/JS-residue slot wraps BY REFERENCE as the DOM's island
2410
+ // kind — engine scalars normalize to native DOM kinds at wrap time,
2411
+ // typeof/truthiness/String()/=== route to the engine, un-armed DOM
2412
+ // walks fence loudly, and the value unwraps back identity-preserved
2413
+ // (scr_jsval_from_dyn). Monotone under evolution/widening: a value
2414
+ // wraps once at its first dyn edge and stays valid through every
2415
+ // subsequent dyn slot; narrowing never changes representation.
2416
+ // This wrap RETIRES the silent-wrong-answer fence-closure box for
2417
+ // jsval members of DOM object literals (lowerDynObjectLiteral's
2418
+ // coercion lands here first).
2419
+ if (expected.kind === "dyn") {
2420
+ return { kind: "dynFromJsval", value: expr, type: DYN, loc: expr.loc };
2421
+ }
2300
2422
  // The exit set: everything round-trippable, plus bare undefined-armed
2301
2423
  // unions of JSON-safe data arms (the engine's undefined takes the
2302
2424
  // undefined arm before the JSON detour) — canExitIslandToType.
@@ -2705,10 +2827,23 @@ export class Lowerer {
2705
2827
  * recursively — NESTED width)
2706
2828
  * arr — array into array whose element pair lifts (per-element
2707
2829
  * copy loop, arrayWidthHelper)
2830
+ * dynIn — a typed value into an 'unknown' (dyn) slot — the same
2831
+ * static→DOM deep copy coerceToExpected applies top-level
2832
+ * upcast — a derived class instance into a base-typed slot (the
2833
+ * prefix-layout pointer reinterpret, no copy)
2834
+ * funcAdapt — a function into a slot whose signature differs only by
2835
+ * CLEAN mechanical conversions (cleanFuncAdaptable); the
2836
+ * stranded (trap-only) dispositions stay out of the plan
2708
2837
  * Null when the pair isn't in the relation — callers keep their fences. */
2709
2838
  widthLiftPlan(src, dst) {
2710
2839
  if (typeEquals(src, dst))
2711
2840
  return { how: "copy" };
2841
+ // An 'unknown' (dyn) DESTINATION slot: the static→DOM conversion —
2842
+ // dynFrom, a DEEP COPY (`{ v: 5 }` into `{ v: unknown }`, `number[]`
2843
+ // into `unknown[]` — tsc's top type over the width family's copies).
2844
+ if (dst.kind === "dyn" && src.kind !== "dyn" && this.dynConvertible(src)) {
2845
+ return { how: "dynIn" };
2846
+ }
2712
2847
  if (dst.kind === "union") {
2713
2848
  if (src.kind === "union") {
2714
2849
  return this.unionRetagMappable(src.unionId, dst.unionId) ? { how: "retag" } : null;
@@ -2747,6 +2882,23 @@ export class Lowerer {
2747
2882
  if (src.kind === "union" && !isUnitType(dst) && dst.kind !== "void" && this.armTag(src.unionId, dst) >= 0) {
2748
2883
  return { how: "narrow" };
2749
2884
  }
2885
+ // A DERIVED instance into a BASE-typed slot (`{ p: Q }` copying into
2886
+ // `{ p: P }`): the same implicit upcast coerceToExpected performs at
2887
+ // top level — prefix layout, a pointer reinterpret, no copy.
2888
+ if (dst.kind === "object" &&
2889
+ src.kind === "object" &&
2890
+ this.isSubclassOf(src.className, dst.className)) {
2891
+ return { how: "upcast" };
2892
+ }
2893
+ // A FUNCTION into a slot whose signature differs only by CLEAN
2894
+ // mechanical conversions (fewer params — JS ignores extras — and
2895
+ // coercibleValue pieces): the general function-value adapter, plan-
2896
+ // gated to the clean subset. The stranded (trap-only) dispositions
2897
+ // funcCoerceAdapter also builds stay TOP-LEVEL only: a width plan
2898
+ // never promises a bridge that can only throw.
2899
+ if (dst.kind === "func" && src.kind === "func" && this.cleanFuncAdaptable(src, dst)) {
2900
+ return { how: "funcAdapt" };
2901
+ }
2750
2902
  if (dst.kind === "record" && src.kind === "record") {
2751
2903
  return this.recordWidthPlan(src.shapeId, dst.shapeId) !== null ? { how: "width" } : null;
2752
2904
  }
@@ -2874,6 +3026,24 @@ export class Lowerer {
2874
3026
  throw new Error("lowerer bug: planned narrow lift failed to intern");
2875
3027
  return { kind: "call", callee: helper, args: [value], type: dst, loc };
2876
3028
  }
3029
+ case "dynIn": {
3030
+ if (dst.kind !== "dyn")
3031
+ throw new Error("lowerer bug: dynIn lift against a non-dyn slot");
3032
+ return { kind: "dynFrom", value, type: DYN, loc };
3033
+ }
3034
+ case "upcast": {
3035
+ if (dst.kind !== "object" || value.type.kind !== "object")
3036
+ throw new Error("lowerer bug: upcast lift shape");
3037
+ return this.upcastTo(value, dst.className);
3038
+ }
3039
+ case "funcAdapt": {
3040
+ if (dst.kind !== "func" || value.type.kind !== "func")
3041
+ throw new Error("lowerer bug: funcAdapt lift shape");
3042
+ const adapter = this.funcCoerceAdapter(value.type, dst, loc);
3043
+ if (!adapter)
3044
+ throw new Error("lowerer bug: planned funcAdapt lift failed to intern");
3045
+ return { kind: "call", callee: adapter, args: [value], type: dst, loc };
3046
+ }
2877
3047
  default: {
2878
3048
  const _exhaustive = lift;
2879
3049
  void _exhaustive;
@@ -2891,9 +3061,11 @@ export class Lowerer {
2891
3061
  * optional-flavored field completes to its undefined arm (the
2892
3062
  * literal-completion rule). TUPLES width-coerce too, arity-exact (TS
2893
3063
  * permits no other tuple width): per-position lifts, never completion.
2894
- * Index-signature shapes keep the exactness fences here (the overflow
2895
- * CAPTURE helper owns their reshapes). Null when the shapes don't
2896
- * relate that way. */
3064
+ * Index-signature SOURCES narrow here like any wider record — declared
3065
+ * fields copy, the overflow drops with the width (missing target
3066
+ * fields decline: the overflow could hold them). Index-signature
3067
+ * TARGETS keep the overflow CAPTURE helper (widthCoerce's other arm).
3068
+ * Null when the shapes don't relate that way. */
2897
3069
  /** The pure planning half of recordWidthHelper — every target field's
2898
3070
  * lift, or null when the pair isn't width-coercible. Callers that must
2899
3071
  * validate a WHOLE plan before interning anything (the retag helper's
@@ -2901,7 +3073,13 @@ export class Lowerer {
2901
3073
  recordWidthPlan(fromId, toId) {
2902
3074
  const from = this.shapes.get(fromId);
2903
3075
  const to = this.shapes.get(toId);
2904
- if (!from || !to || from.indexValue || to.indexValue)
3076
+ // INDEX-SIGNATURE sources narrow like any wider record — the target
3077
+ // fields copy off the declared struct slots and the overflow drops
3078
+ // with the rest of the width (divergence 36's stance; the absent-
3079
+ // completion rule below is the one extra fence). Index-signature
3080
+ // TARGETS keep the overflow CAPTURE helper (widthCoerce's other arm):
3081
+ // a fresh hybrid needs keyed writes, not a field-list literal.
3082
+ if (!from || !to || to.indexValue)
2905
3083
  return null;
2906
3084
  // Tuple↔record pairs never relate; tuple↔tuple only arity-exact.
2907
3085
  if (!!from.tuple !== !!to.tuple)
@@ -2926,8 +3104,12 @@ export class Lowerer {
2926
3104
  // the absent-property read: the options-record call shape
2927
3105
  // against `{ plugins: unknown, ... }`). Never for tuples: a
2928
3106
  // completed position would change .length and JSON where Node
2929
- // keeps the source arity.
2930
- if (from.tuple)
3107
+ // keeps the source arity. Never for INDEX-SIGNATURE sources:
3108
+ // the overflow may hold this very key at runtime (tsc lets the
3109
+ // signature satisfy optional target members), so completing to
3110
+ // undefined would drop a value Node keeps — the pair stays
3111
+ // fenced.
3112
+ if (from.tuple || from.indexValue)
2931
3113
  return null;
2932
3114
  if (tf.type.kind === "dyn") {
2933
3115
  plan.set(tf.name, { absentDyn: true });
@@ -2953,6 +3135,92 @@ export class Lowerer {
2953
3135
  this.widthPlanning.delete(key);
2954
3136
  }
2955
3137
  }
3138
+ /** Post-hoc classifier for SC2002's record→record residue: WHY the
3139
+ * width family (recordWidthPlan and the overflow capture — widthCoerce's
3140
+ * two record arms) declined this pair — the FIRST blocking rule, named.
3141
+ * Pure description on the failure path (the site already carries the
3142
+ * rejection): mirrors the planners' gates, never changes what coerces,
3143
+ * and answers null when no pointed story applies (the generic message
3144
+ * stands). */
3145
+ describeRecordWidthBlocker(fromId, toId) {
3146
+ const from = this.shapes.get(fromId);
3147
+ const to = this.shapes.get(toId);
3148
+ if (!from || !to)
3149
+ return null;
3150
+ if (to.indexValue) {
3151
+ // The overflow CAPTURE's gates (lowerRecordOvfCaptureHelper).
3152
+ if (from.tuple || to.tuple)
3153
+ return "a tuple cannot reshape into an index-signature record";
3154
+ const tIv = to.indexValue;
3155
+ const slotOk = (t) => typeEquals(t, tIv) ||
3156
+ (tIv.kind === "dyn" && (t.kind === "dyn" || this.dynConvertible(t))) ||
3157
+ this.widthLiftPlan(t, tIv) !== null;
3158
+ const consumed = new Set();
3159
+ for (const tf of to.fields) {
3160
+ const sf = from.fields.find((f) => f.name === tf.name);
3161
+ if (sf) {
3162
+ if (this.widthLiftPlan(sf.type, tf.type) !== null) {
3163
+ consumed.add(tf.name);
3164
+ continue;
3165
+ }
3166
+ return `field '${tf.name}': '${this.fmt(sf.type)}' does not lift into '${this.fmt(tf.type)}'`;
3167
+ }
3168
+ if (tf.type.kind !== "union" || this.armTag(tf.type.unionId, UNDEFINED_T) < 0) {
3169
+ return `the expected field '${tf.name}' is required and the source has no field to copy into it`;
3170
+ }
3171
+ if (tIv.kind === "dyn" ? !this.dynConvertible(tf.type) : !typeEquals(tf.type, tIv)) {
3172
+ return `the expected field '${tf.name}' ('${this.fmt(tf.type)}') cannot take a runtime key collision from the '${this.fmt(tIv)}' signature slot`;
3173
+ }
3174
+ }
3175
+ for (const ff of from.fields) {
3176
+ if (consumed.has(ff.name))
3177
+ continue;
3178
+ if (!slotOk(ff.type)) {
3179
+ return `the source field '${ff.name}' ('${this.fmt(ff.type)}') cannot enter the expected '[key: string]: ${this.fmt(tIv)}' slot`;
3180
+ }
3181
+ }
3182
+ if (from.indexValue && !slotOk(from.indexValue)) {
3183
+ return `the source's '[key: string]: ${this.fmt(from.indexValue)}' slot cannot enter the expected '[key: string]: ${this.fmt(tIv)}' slot`;
3184
+ }
3185
+ // The dispatch-writes gate: runtime-keyed writes can collide with a
3186
+ // declared field whose type is not the slot's.
3187
+ const dispatchWrites = from.indexValue !== undefined ||
3188
+ from.fields.some((ff) => !consumed.has(ff.name) && to.fields.some((f) => f.name === ff.name));
3189
+ if (dispatchWrites) {
3190
+ const bad = to.fields.find((f) => tIv.kind === "dyn" ? !this.dynConvertible(f.type) : !typeEquals(f.type, tIv));
3191
+ if (bad) {
3192
+ return `runtime-keyed writes can collide with the expected field '${bad.name}' ('${this.fmt(bad.type)}'), which cannot take a '${this.fmt(tIv)}' slot value`;
3193
+ }
3194
+ }
3195
+ return null;
3196
+ }
3197
+ // The field-copy plan's gates (recordWidthPlan).
3198
+ if (!!from.tuple !== !!to.tuple)
3199
+ return null;
3200
+ if (from.tuple && from.fields.length !== to.fields.length) {
3201
+ return `tuple arities differ (${from.fields.length} vs ${to.fields.length}; TS permits no tuple width)`;
3202
+ }
3203
+ for (const tf of to.fields) {
3204
+ const ff = from.fields.find((f) => f.name === tf.name);
3205
+ if (!ff) {
3206
+ if (from.tuple)
3207
+ return null;
3208
+ if (from.indexValue) {
3209
+ return `'${tf.name}' is not a declared field of the source, and the source's index signature could hold it at runtime (a completed undefined would drop that value)`;
3210
+ }
3211
+ if (tf.type.kind === "dyn")
3212
+ continue;
3213
+ if (tf.type.kind !== "union" || this.armTag(tf.type.unionId, UNDEFINED_T) < 0) {
3214
+ return `the expected field '${tf.name}' is missing on the source and is not optional`;
3215
+ }
3216
+ continue;
3217
+ }
3218
+ if (this.widthLiftPlan(ff.type, tf.type) === null) {
3219
+ return `field '${tf.name}': '${this.fmt(ff.type)}' does not lift into '${this.fmt(tf.type)}'`;
3220
+ }
3221
+ }
3222
+ return null;
3223
+ }
2956
3224
  recordWidthHelper(fromId, toId, loc) {
2957
3225
  const from = this.shapes.get(fromId);
2958
3226
  const to = this.shapes.get(toId);
@@ -3611,6 +3879,28 @@ export class Lowerer {
3611
3879
  }
3612
3880
  return false;
3613
3881
  }
3882
+ /** True when a `src` function value enters a `dst` slot through
3883
+ * funcCoerceAdapter with NO stranded (trap-only) piece: no rest packs,
3884
+ * no surplus source params, every slot parameter converts into the
3885
+ * wrapped function's own type, and the result converts back (a void
3886
+ * slot drops it; a void result answers the exact JS undefined for
3887
+ * dyn/jsval slots). The width family's func gate — widthLiftPlan
3888
+ * bridges only signatures whose every call succeeds by construction. */
3889
+ cleanFuncAdaptable(src, dst) {
3890
+ if (src.rest === true || dst.rest === true)
3891
+ return false;
3892
+ if (src.params.length > dst.params.length)
3893
+ return false;
3894
+ for (let i = 0; i < src.params.length; i++) {
3895
+ if (!this.coercibleValue(dst.params[i], src.params[i]))
3896
+ return false;
3897
+ }
3898
+ if (dst.ret.kind === "void")
3899
+ return src.ret.kind !== "jsval";
3900
+ if (this.coercibleValue(src.ret, dst.ret))
3901
+ return true;
3902
+ return src.ret.kind === "void" && (dst.ret.kind === "dyn" || dst.ret.kind === "jsval");
3903
+ }
3614
3904
  /** Interned `%fn.adapt.<n>(f)` — the GENERAL function-value adapter: a
3615
3905
  * `fromT` function value flowing into a `toT` slot whose pieces differ
3616
3906
  * only by coercibleValue conversions. The slot's callers pass toT's
@@ -4929,6 +5219,11 @@ export class Lowerer {
4929
5219
  if (value.kind === "unitLit") {
4930
5220
  return { kind: "dynFrom", value, type: DYN, loc: value.loc };
4931
5221
  }
5222
+ // An ISLAND ('any') value: the by-reference jsval→DOM wrap — the same
5223
+ // edge coerceToExpected converts (dyn slots accept engine values).
5224
+ if (value.type.kind === "jsval") {
5225
+ return { kind: "dynFromJsval", value, type: DYN, loc: value.loc };
5226
+ }
4932
5227
  if (!this.dynConvertible(value.type)) {
4933
5228
  this.unsupported("SC1100", node, `storing '${this.fmt(value.type)}' values under an 'unknown'-valued index signature (only numbers, strings, booleans, and JSON-safe records/arrays/unions convert)`);
4934
5229
  }
@@ -5884,7 +6179,7 @@ export class Lowerer {
5884
6179
  lowerDestructuringAssignParts(target, rhs, loc) {
5885
6180
  return lowerDestructuringAssignParts(this, target, rhs, loc);
5886
6181
  }
5887
- lowerBindingPattern(pattern, srcRef, srcType, isLet, out) {
6182
+ lowerBindingPattern(pattern, srcRef, srcType, isLet, out, dynSpell) {
5888
6183
  // An ISLAND source (`const { readFileSync } = await import("fs")` —
5889
6184
  // a namespace handle, or any 'any'-typed object): each bound name is
5890
6185
  // an engine property read, mirroring the island property-read rule —
@@ -5948,7 +6243,7 @@ export class Lowerer {
5948
6243
  }
5949
6244
  return;
5950
6245
  }
5951
- return lowerBindingPattern(this, pattern, srcRef, srcType, isLet, out);
6246
+ return lowerBindingPattern(this, pattern, srcRef, srcType, isLet, out, dynSpell);
5952
6247
  }
5953
6248
  checkBindingElement(el, allowDefault = false) {
5954
6249
  return checkBindingElement(this, el, allowDefault);
@@ -6029,6 +6324,16 @@ export class Lowerer {
6029
6324
  const local = this.declareLocal(decl.name, decl.name.text, JSVAL, isLet);
6030
6325
  return { kind: "varDecl", localId: local.id, init, loc: locOf(decl) };
6031
6326
  }
6327
+ // The CHECKED-DYNAMIC twin of the handle rescue (the runtime-world
6328
+ // local rule): an unmappable declared type over a dyn initializer
6329
+ // (`const first = plugins[0]` — the checker spells 'string |
6330
+ // object' while the read is a DOM keyed read) keeps the binding
6331
+ // dyn; typed use sites ride validated extractions and the routed
6332
+ // engine ops, exactly the JSON.parse-binding story.
6333
+ if (mapped === null && init.type.kind === "dyn") {
6334
+ const local = this.declareLocal(decl.name, decl.name.text, DYN, isLet);
6335
+ return { kind: "varDecl", localId: local.id, init, loc: locOf(decl) };
6336
+ }
6032
6337
  if (mapped === null)
6033
6338
  this.badType(decl.name, this.typeOf(decl.name));
6034
6339
  // A mappable declared type with a non-island initializer: the
@@ -6402,6 +6707,27 @@ export class Lowerer {
6402
6707
  const spec = requireSpecOf(decl.initializer);
6403
6708
  return spec !== null ? canonicalBuiltinModule(spec) : null;
6404
6709
  }
6710
+ // A DESTRUCTURED sub-namespace binding — `const { promises } =
6711
+ // fs` / `= require('fs')`: the member is itself a supported module
6712
+ // ("fs/promises"), so the binding carries that module's namespace
6713
+ // surface. canonicalBuiltinModule gates the composition (an
6714
+ // ordinary destructured FUNCTION binding composes to an unknown
6715
+ // name and answers null — builtinImportOf owns those).
6716
+ if (ts.isBindingElement(decl) && ts.isObjectBindingPattern(decl.parent) &&
6717
+ ts.isVariableDeclaration(decl.parent.parent) && decl.parent.parent.initializer !== undefined &&
6718
+ decl.propertyName === undefined && decl.initializer === undefined) {
6719
+ const name = decl.name;
6720
+ if (name === undefined || !ts.isIdentifier(name))
6721
+ return null;
6722
+ const init = decl.parent.parent.initializer;
6723
+ const spec = requireSpecOf(init);
6724
+ const outer = spec !== null
6725
+ ? canonicalBuiltinModule(spec)
6726
+ : ts.isIdentifier(init) ? this.builtinNamespaceModuleOf(init) : null;
6727
+ if (outer !== null)
6728
+ return canonicalBuiltinModule(`${outer}/${name.text}`);
6729
+ return null;
6730
+ }
6405
6731
  }
6406
6732
  // The INLINE CommonJS spelling: `require("cluster").isPrimary` — the
6407
6733
  // call expression IS the module namespace (Node evaluates the member
@@ -6509,6 +6835,17 @@ export class Lowerer {
6509
6835
  const streamServed = lowerStreamModuleCall(this, call, bi, locOf(access));
6510
6836
  if (streamServed)
6511
6837
  return streamServed;
6838
+ // fs._toUnixTimestamp — off the param tables (an underscore-stable
6839
+ // internal), served by its own spoke before the table fence.
6840
+ const fsTs = this.lowerFsToUnixTimestampCall(call, bi, locOf(access));
6841
+ if (fsTs)
6842
+ return fsTs;
6843
+ // The fs validation-ladder spoke (checked-dynamic lane): misuse of
6844
+ // implemented-namespace members throws Node's typed errors instead
6845
+ // of meeting the table fence.
6846
+ const fsLadder = this.lowerFsLadderCall(call, bi, locOf(access));
6847
+ if (fsLadder)
6848
+ return fsLadder;
6512
6849
  const builtinFn = builtinModuleFnOf(this, bi.module, bi.member);
6513
6850
  if (!builtinFn) {
6514
6851
  this.noLowering(`${bi.module}.${bi.member}`, call, builtinFenceHintOf(bi.module, bi.member), this.checker.getSymbolAtLocation(access.name));
@@ -6539,6 +6876,11 @@ export class Lowerer {
6539
6876
  const bi = this.builtinMemberOf(expr);
6540
6877
  if (!bi)
6541
6878
  return null;
6879
+ // events.defaultMaxListeners READS the process-wide default (its
6880
+ // write twin routes through emitter.setDefaultMaxChk).
6881
+ if (bi.module === "events" && bi.member === "defaultMaxListeners") {
6882
+ return { kind: "libCall", fn: "emitter.getDefaultMax", args: [], type: F64, loc };
6883
+ }
6542
6884
  const c = builtinModuleConstOf(this, bi.module, bi.member);
6543
6885
  if (c !== undefined)
6544
6886
  return builtinConstLit(c, loc);
@@ -6554,6 +6896,12 @@ export class Lowerer {
6554
6896
  lowerBuiltinModuleCall(expr, bi, fn, loc) {
6555
6897
  return lowerBuiltinModuleCall(this, expr, bi, fn, loc);
6556
6898
  }
6899
+ lowerFsToUnixTimestampCall(expr, bi, loc) {
6900
+ return lowerFsToUnixTimestampCall(this, expr, bi, loc);
6901
+ }
6902
+ lowerFsLadderCall(expr, bi, loc) {
6903
+ return lowerFsLadderCall(this, expr, bi, loc);
6904
+ }
6557
6905
  lowerChildArgsArg(node, loc) {
6558
6906
  return lowerChildArgsArg(this, node, loc);
6559
6907
  }