@scriptc/compiler 0.0.8 → 0.0.9

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 (66) 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 +108 -5
  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 +23 -10
  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/emitter.js +206 -11
  16. package/dist/backend/llvm/emitter.js.map +1 -1
  17. package/dist/diagnostics/diagnostic.d.ts +55 -10
  18. package/dist/diagnostics/diagnostic.js +82 -20
  19. package/dist/diagnostics/diagnostic.js.map +1 -1
  20. package/dist/frontend/lowering/lower-assert.js +16 -0
  21. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  22. package/dist/frontend/lowering/lower-builtins.d.ts +11 -0
  23. package/dist/frontend/lowering/lower-builtins.js +111 -3
  24. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  25. package/dist/frontend/lowering/lower-calls.d.ts +30 -11
  26. package/dist/frontend/lowering/lower-calls.js +418 -8
  27. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  28. package/dist/frontend/lowering/lower-classes.js +27 -1
  29. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  30. package/dist/frontend/lowering/lower-containers.d.ts +4 -2
  31. package/dist/frontend/lowering/lower-containers.js +193 -43
  32. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  33. package/dist/frontend/lowering/lower-emitter.js +54 -0
  34. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  35. package/dist/frontend/lowering/lower-exprs.js +52 -7
  36. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  37. package/dist/frontend/lowering/lower-stmts.js +445 -13
  38. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  39. package/dist/frontend/lowering/lower-stream.js +28 -2
  40. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  41. package/dist/frontend/lowering/lowerer.d.ts +66 -14
  42. package/dist/frontend/lowering/lowerer.js +273 -28
  43. package/dist/frontend/lowering/lowerer.js.map +1 -1
  44. package/dist/frontend/lowering/surfaces.js +20 -3
  45. package/dist/frontend/lowering/surfaces.js.map +1 -1
  46. package/dist/frontend/npm-static-rewrite.d.ts +7 -2
  47. package/dist/frontend/npm-static-rewrite.js +566 -138
  48. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  49. package/dist/frontend/npm-static.js +11 -2
  50. package/dist/frontend/npm-static.js.map +1 -1
  51. package/dist/frontend/program.js +5 -5
  52. package/dist/frontend/program.js.map +1 -1
  53. package/dist/frontend/shared.d.ts +6 -1
  54. package/dist/frontend/shared.js +46 -1
  55. package/dist/frontend/shared.js.map +1 -1
  56. package/dist/frontend/types.js +4 -2
  57. package/dist/frontend/types.js.map +1 -1
  58. package/dist/index.js +150 -25
  59. package/dist/index.js.map +1 -1
  60. package/dist/ir/nodes.d.ts +131 -7
  61. package/dist/ir/nodes.js +41 -3
  62. package/dist/ir/nodes.js.map +1 -1
  63. package/dist/ir/validate.js +58 -8
  64. package/dist/ir/validate.js.map +1 -1
  65. package/package.json +2 -2
  66. 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, 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,25 @@ 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
+ }
283
302
  /** The checked-dynamic declaration fallback for unmappable binding types
284
303
  * (see irTypeOf), two gates over one story:
285
304
  *
@@ -902,7 +921,11 @@ export class Lowerer {
902
921
  const recvDecl = recvDecls.find(ts.isImportClause) ?? recvDecls[0];
903
922
  if (recvDecl && ts.isVariableDeclaration(recvDecl) && recvDecl.initializer) {
904
923
  const spec = requireSpecOf(recvDecl.initializer);
905
- const dep = spec !== null ? resolveImport(this.program, recvDecl.getSourceFile(), spec) : null;
924
+ const dep = spec === null
925
+ ? null
926
+ : isRelativeSpecifier(spec)
927
+ ? resolveImport(this.program, recvDecl.getSourceFile(), spec)
928
+ : npmStaticDepSf7(this.program, recvDecl.getSourceFile(), spec);
906
929
  if (dep)
907
930
  symbol = this.cjsModuleExportSymbol(dep, ident.text);
908
931
  }
@@ -1099,12 +1122,14 @@ export class Lowerer {
1099
1122
  }
1100
1123
  /** True when `expr` is an identifier bound by a top-level
1101
1124
  * `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. */
1125
+ * namespace binding — or of a bare specifier naming an opted-in
1126
+ * --npm-static package (its CJS entry is a program module, so the
1127
+ * binding is the same namespace over the same export table), or by a
1128
+ * DEFAULT import of a CommonJS JS module (`import d from "./lib.cjs"`:
1129
+ * Node binds d to module.exports, the same value require answers).
1130
+ * Member accesses on it resolve through the export table (property
1131
+ * symbols → resolveValueSymbol); the bare value keeps the
1132
+ * namespace-object fence, like ESM namespace imports of builtins. */
1108
1133
  cjsLocalModuleBindingOf(expr) {
1109
1134
  if (!ts.isIdentifier(expr))
1110
1135
  return false;
@@ -1122,8 +1147,12 @@ export class Lowerer {
1122
1147
  return false;
1123
1148
  }
1124
1149
  const spec = requireSpecOf(decl.initializer);
1125
- if (spec === null || !isRelativeSpecifier(spec))
1150
+ if (spec === null)
1126
1151
  return false;
1152
+ if (!isRelativeSpecifier(spec) &&
1153
+ npmStaticDepSf7(this.program, decl.getSourceFile(), spec) === null) {
1154
+ return false;
1155
+ }
1127
1156
  }
1128
1157
  // SINGLE-VALUE exporters (`module.exports = Countdown` / `= double` /
1129
1158
  // `= 42`): the requirer's binding IS the exported value, not a
@@ -1222,13 +1251,22 @@ export class Lowerer {
1222
1251
  /** The lowering of a CommonJS `require("./local")` occurrence: a call of
1223
1252
  * the required module's run-once %init at exactly this statement's
1224
1253
  * 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). */
1254
+ * cache-hit behavior for every require after the first. Bare specifiers
1255
+ * naming an opted-in --npm-static package resolve to that package's
1256
+ * program entry (the same edge preflight admitted — bundle dists require
1257
+ * their external dependencies by name). Null for everything else
1258
+ * (builtins load nothing; the rest kept its preflight fence). */
1228
1259
  requireInitStmt(spec, node) {
1229
- if (!isRelativeSpecifier(spec))
1230
- return null;
1231
- const dep = resolveImport(this.program, node.getSourceFile(), spec);
1260
+ // Relative requires resolve within the program; a BARE require can be
1261
+ // a program-module edge too when it names an opted-in --npm-static
1262
+ // package (one package requiring another — the resolution answered
1263
+ // its shipped JS, the file is in the module order, and the reads
1264
+ // alias its globals). Without the guarded %init call at this position
1265
+ // those globals stay uninitialized: the dep's module body would never
1266
+ // run.
1267
+ const dep = isRelativeSpecifier(spec)
1268
+ ? resolveImport(this.program, node.getSourceFile(), spec)
1269
+ : npmStaticDepSf7(this.program, node.getSourceFile(), spec);
1232
1270
  if (!dep || dep.fileName.endsWith(".json"))
1233
1271
  return null;
1234
1272
  const initName = this.initNameOf.get(dep);
@@ -1830,8 +1868,8 @@ export class Lowerer {
1830
1868
  throw new PoisonError();
1831
1869
  }
1832
1870
  // 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
1871
+ // `{ [k: string]: T }`) get the index-signature fence (SC2006) naming
1872
+ // the supported key/value domain instead of the generic
1835
1873
  // supported-types recitation.
1836
1874
  if (widened.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection) &&
1837
1875
  this.checker.getCallSignatures(widened).length === 0 &&
@@ -1950,9 +1988,27 @@ export class Lowerer {
1950
1988
  }
1951
1989
  }
1952
1990
  // STANDARD-LIBRARY nominal types with no lowering at all: the SC2020
1953
- // story, naming the type.
1991
+ // story, naming the type — and for the families with a WHY, the same
1992
+ // pointed reason the identifier/constructor chokepoints teach.
1954
1993
  if (stdlibNominal) {
1955
- this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node)));
1994
+ const intlHint = "Intl formatter values have no representation — the COMPOSED en-US forms lower: " +
1995
+ 'new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; ' +
1996
+ "the rest is ICU locale data the binary does not carry";
1997
+ const typeHints = {
1998
+ ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage " +
1999
+ "(new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
2000
+ SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
2001
+ NumberFormat: intlHint,
2002
+ DateTimeFormat: intlHint,
2003
+ DurationFormat: intlHint,
2004
+ PluralRules: intlHint,
2005
+ Collator: intlHint,
2006
+ ListFormat: intlHint,
2007
+ RelativeTimeFormat: intlHint,
2008
+ Segmenter: intlHint,
2009
+ DisplayNames: intlHint,
2010
+ };
2011
+ this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node), typeHints[stdlibOwnSym?.name ?? ""]));
1956
2012
  throw new PoisonError();
1957
2013
  }
1958
2014
  // USER record shapes blocked by ONE member (SC2009's record arm):
@@ -2147,8 +2203,10 @@ export class Lowerer {
2147
2203
  }
2148
2204
  /** Exact-shape enforcement (SC2002). Records are monomorphic structs, so
2149
2205
  * 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.
2206
+ * assignment, field, return) the shapes must MATCH — or width-coerce:
2207
+ * coerceToExpected already ran widthCoerce (the copy-reshape family),
2208
+ * so what reaches here is the RESIDUE its rules decline, and the
2209
+ * diagnostic names the first blocking rule (describeRecordWidthBlocker).
2152
2210
  * Non-record mismatches are not reachable through tsc-clean programs; the
2153
2211
  * validator ICEs on them as the usual backstop. */
2154
2212
  requireExactShape(node, actual, expected) {
@@ -2207,7 +2265,13 @@ export class Lowerer {
2207
2265
  throw new PoisonError();
2208
2266
  }
2209
2267
  if (containsRecord(actual) || containsRecord(expected)) {
2210
- this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node)));
2268
+ // A record→record pair gets the pointed story: WHICH width rule
2269
+ // declined (a field that doesn't lift, a required field with no
2270
+ // source, an index signature that could hold the completed key).
2271
+ const detail = actual.kind === "record" && expected.kind === "record"
2272
+ ? (this.describeRecordWidthBlocker(actual.shapeId, expected.shapeId) ?? undefined)
2273
+ : undefined;
2274
+ this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
2211
2275
  throw new PoisonError();
2212
2276
  }
2213
2277
  // Everything else — a plain-kind mismatch like a string flowing into a
@@ -2705,10 +2769,23 @@ export class Lowerer {
2705
2769
  * recursively — NESTED width)
2706
2770
  * arr — array into array whose element pair lifts (per-element
2707
2771
  * copy loop, arrayWidthHelper)
2772
+ * dynIn — a typed value into an 'unknown' (dyn) slot — the same
2773
+ * static→DOM deep copy coerceToExpected applies top-level
2774
+ * upcast — a derived class instance into a base-typed slot (the
2775
+ * prefix-layout pointer reinterpret, no copy)
2776
+ * funcAdapt — a function into a slot whose signature differs only by
2777
+ * CLEAN mechanical conversions (cleanFuncAdaptable); the
2778
+ * stranded (trap-only) dispositions stay out of the plan
2708
2779
  * Null when the pair isn't in the relation — callers keep their fences. */
2709
2780
  widthLiftPlan(src, dst) {
2710
2781
  if (typeEquals(src, dst))
2711
2782
  return { how: "copy" };
2783
+ // An 'unknown' (dyn) DESTINATION slot: the static→DOM conversion —
2784
+ // dynFrom, a DEEP COPY (`{ v: 5 }` into `{ v: unknown }`, `number[]`
2785
+ // into `unknown[]` — tsc's top type over the width family's copies).
2786
+ if (dst.kind === "dyn" && src.kind !== "dyn" && this.dynConvertible(src)) {
2787
+ return { how: "dynIn" };
2788
+ }
2712
2789
  if (dst.kind === "union") {
2713
2790
  if (src.kind === "union") {
2714
2791
  return this.unionRetagMappable(src.unionId, dst.unionId) ? { how: "retag" } : null;
@@ -2747,6 +2824,23 @@ export class Lowerer {
2747
2824
  if (src.kind === "union" && !isUnitType(dst) && dst.kind !== "void" && this.armTag(src.unionId, dst) >= 0) {
2748
2825
  return { how: "narrow" };
2749
2826
  }
2827
+ // A DERIVED instance into a BASE-typed slot (`{ p: Q }` copying into
2828
+ // `{ p: P }`): the same implicit upcast coerceToExpected performs at
2829
+ // top level — prefix layout, a pointer reinterpret, no copy.
2830
+ if (dst.kind === "object" &&
2831
+ src.kind === "object" &&
2832
+ this.isSubclassOf(src.className, dst.className)) {
2833
+ return { how: "upcast" };
2834
+ }
2835
+ // A FUNCTION into a slot whose signature differs only by CLEAN
2836
+ // mechanical conversions (fewer params — JS ignores extras — and
2837
+ // coercibleValue pieces): the general function-value adapter, plan-
2838
+ // gated to the clean subset. The stranded (trap-only) dispositions
2839
+ // funcCoerceAdapter also builds stay TOP-LEVEL only: a width plan
2840
+ // never promises a bridge that can only throw.
2841
+ if (dst.kind === "func" && src.kind === "func" && this.cleanFuncAdaptable(src, dst)) {
2842
+ return { how: "funcAdapt" };
2843
+ }
2750
2844
  if (dst.kind === "record" && src.kind === "record") {
2751
2845
  return this.recordWidthPlan(src.shapeId, dst.shapeId) !== null ? { how: "width" } : null;
2752
2846
  }
@@ -2874,6 +2968,24 @@ export class Lowerer {
2874
2968
  throw new Error("lowerer bug: planned narrow lift failed to intern");
2875
2969
  return { kind: "call", callee: helper, args: [value], type: dst, loc };
2876
2970
  }
2971
+ case "dynIn": {
2972
+ if (dst.kind !== "dyn")
2973
+ throw new Error("lowerer bug: dynIn lift against a non-dyn slot");
2974
+ return { kind: "dynFrom", value, type: DYN, loc };
2975
+ }
2976
+ case "upcast": {
2977
+ if (dst.kind !== "object" || value.type.kind !== "object")
2978
+ throw new Error("lowerer bug: upcast lift shape");
2979
+ return this.upcastTo(value, dst.className);
2980
+ }
2981
+ case "funcAdapt": {
2982
+ if (dst.kind !== "func" || value.type.kind !== "func")
2983
+ throw new Error("lowerer bug: funcAdapt lift shape");
2984
+ const adapter = this.funcCoerceAdapter(value.type, dst, loc);
2985
+ if (!adapter)
2986
+ throw new Error("lowerer bug: planned funcAdapt lift failed to intern");
2987
+ return { kind: "call", callee: adapter, args: [value], type: dst, loc };
2988
+ }
2877
2989
  default: {
2878
2990
  const _exhaustive = lift;
2879
2991
  void _exhaustive;
@@ -2891,9 +3003,11 @@ export class Lowerer {
2891
3003
  * optional-flavored field completes to its undefined arm (the
2892
3004
  * literal-completion rule). TUPLES width-coerce too, arity-exact (TS
2893
3005
  * 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. */
3006
+ * Index-signature SOURCES narrow here like any wider record — declared
3007
+ * fields copy, the overflow drops with the width (missing target
3008
+ * fields decline: the overflow could hold them). Index-signature
3009
+ * TARGETS keep the overflow CAPTURE helper (widthCoerce's other arm).
3010
+ * Null when the shapes don't relate that way. */
2897
3011
  /** The pure planning half of recordWidthHelper — every target field's
2898
3012
  * lift, or null when the pair isn't width-coercible. Callers that must
2899
3013
  * validate a WHOLE plan before interning anything (the retag helper's
@@ -2901,7 +3015,13 @@ export class Lowerer {
2901
3015
  recordWidthPlan(fromId, toId) {
2902
3016
  const from = this.shapes.get(fromId);
2903
3017
  const to = this.shapes.get(toId);
2904
- if (!from || !to || from.indexValue || to.indexValue)
3018
+ // INDEX-SIGNATURE sources narrow like any wider record — the target
3019
+ // fields copy off the declared struct slots and the overflow drops
3020
+ // with the rest of the width (divergence 36's stance; the absent-
3021
+ // completion rule below is the one extra fence). Index-signature
3022
+ // TARGETS keep the overflow CAPTURE helper (widthCoerce's other arm):
3023
+ // a fresh hybrid needs keyed writes, not a field-list literal.
3024
+ if (!from || !to || to.indexValue)
2905
3025
  return null;
2906
3026
  // Tuple↔record pairs never relate; tuple↔tuple only arity-exact.
2907
3027
  if (!!from.tuple !== !!to.tuple)
@@ -2926,8 +3046,12 @@ export class Lowerer {
2926
3046
  // the absent-property read: the options-record call shape
2927
3047
  // against `{ plugins: unknown, ... }`). Never for tuples: a
2928
3048
  // completed position would change .length and JSON where Node
2929
- // keeps the source arity.
2930
- if (from.tuple)
3049
+ // keeps the source arity. Never for INDEX-SIGNATURE sources:
3050
+ // the overflow may hold this very key at runtime (tsc lets the
3051
+ // signature satisfy optional target members), so completing to
3052
+ // undefined would drop a value Node keeps — the pair stays
3053
+ // fenced.
3054
+ if (from.tuple || from.indexValue)
2931
3055
  return null;
2932
3056
  if (tf.type.kind === "dyn") {
2933
3057
  plan.set(tf.name, { absentDyn: true });
@@ -2953,6 +3077,92 @@ export class Lowerer {
2953
3077
  this.widthPlanning.delete(key);
2954
3078
  }
2955
3079
  }
3080
+ /** Post-hoc classifier for SC2002's record→record residue: WHY the
3081
+ * width family (recordWidthPlan and the overflow capture — widthCoerce's
3082
+ * two record arms) declined this pair — the FIRST blocking rule, named.
3083
+ * Pure description on the failure path (the site already carries the
3084
+ * rejection): mirrors the planners' gates, never changes what coerces,
3085
+ * and answers null when no pointed story applies (the generic message
3086
+ * stands). */
3087
+ describeRecordWidthBlocker(fromId, toId) {
3088
+ const from = this.shapes.get(fromId);
3089
+ const to = this.shapes.get(toId);
3090
+ if (!from || !to)
3091
+ return null;
3092
+ if (to.indexValue) {
3093
+ // The overflow CAPTURE's gates (lowerRecordOvfCaptureHelper).
3094
+ if (from.tuple || to.tuple)
3095
+ return "a tuple cannot reshape into an index-signature record";
3096
+ const tIv = to.indexValue;
3097
+ const slotOk = (t) => typeEquals(t, tIv) ||
3098
+ (tIv.kind === "dyn" && (t.kind === "dyn" || this.dynConvertible(t))) ||
3099
+ this.widthLiftPlan(t, tIv) !== null;
3100
+ const consumed = new Set();
3101
+ for (const tf of to.fields) {
3102
+ const sf = from.fields.find((f) => f.name === tf.name);
3103
+ if (sf) {
3104
+ if (this.widthLiftPlan(sf.type, tf.type) !== null) {
3105
+ consumed.add(tf.name);
3106
+ continue;
3107
+ }
3108
+ return `field '${tf.name}': '${this.fmt(sf.type)}' does not lift into '${this.fmt(tf.type)}'`;
3109
+ }
3110
+ if (tf.type.kind !== "union" || this.armTag(tf.type.unionId, UNDEFINED_T) < 0) {
3111
+ return `the expected field '${tf.name}' is required and the source has no field to copy into it`;
3112
+ }
3113
+ if (tIv.kind === "dyn" ? !this.dynConvertible(tf.type) : !typeEquals(tf.type, tIv)) {
3114
+ return `the expected field '${tf.name}' ('${this.fmt(tf.type)}') cannot take a runtime key collision from the '${this.fmt(tIv)}' signature slot`;
3115
+ }
3116
+ }
3117
+ for (const ff of from.fields) {
3118
+ if (consumed.has(ff.name))
3119
+ continue;
3120
+ if (!slotOk(ff.type)) {
3121
+ return `the source field '${ff.name}' ('${this.fmt(ff.type)}') cannot enter the expected '[key: string]: ${this.fmt(tIv)}' slot`;
3122
+ }
3123
+ }
3124
+ if (from.indexValue && !slotOk(from.indexValue)) {
3125
+ return `the source's '[key: string]: ${this.fmt(from.indexValue)}' slot cannot enter the expected '[key: string]: ${this.fmt(tIv)}' slot`;
3126
+ }
3127
+ // The dispatch-writes gate: runtime-keyed writes can collide with a
3128
+ // declared field whose type is not the slot's.
3129
+ const dispatchWrites = from.indexValue !== undefined ||
3130
+ from.fields.some((ff) => !consumed.has(ff.name) && to.fields.some((f) => f.name === ff.name));
3131
+ if (dispatchWrites) {
3132
+ const bad = to.fields.find((f) => tIv.kind === "dyn" ? !this.dynConvertible(f.type) : !typeEquals(f.type, tIv));
3133
+ if (bad) {
3134
+ 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`;
3135
+ }
3136
+ }
3137
+ return null;
3138
+ }
3139
+ // The field-copy plan's gates (recordWidthPlan).
3140
+ if (!!from.tuple !== !!to.tuple)
3141
+ return null;
3142
+ if (from.tuple && from.fields.length !== to.fields.length) {
3143
+ return `tuple arities differ (${from.fields.length} vs ${to.fields.length}; TS permits no tuple width)`;
3144
+ }
3145
+ for (const tf of to.fields) {
3146
+ const ff = from.fields.find((f) => f.name === tf.name);
3147
+ if (!ff) {
3148
+ if (from.tuple)
3149
+ return null;
3150
+ if (from.indexValue) {
3151
+ 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)`;
3152
+ }
3153
+ if (tf.type.kind === "dyn")
3154
+ continue;
3155
+ if (tf.type.kind !== "union" || this.armTag(tf.type.unionId, UNDEFINED_T) < 0) {
3156
+ return `the expected field '${tf.name}' is missing on the source and is not optional`;
3157
+ }
3158
+ continue;
3159
+ }
3160
+ if (this.widthLiftPlan(ff.type, tf.type) === null) {
3161
+ return `field '${tf.name}': '${this.fmt(ff.type)}' does not lift into '${this.fmt(tf.type)}'`;
3162
+ }
3163
+ }
3164
+ return null;
3165
+ }
2956
3166
  recordWidthHelper(fromId, toId, loc) {
2957
3167
  const from = this.shapes.get(fromId);
2958
3168
  const to = this.shapes.get(toId);
@@ -3611,6 +3821,28 @@ export class Lowerer {
3611
3821
  }
3612
3822
  return false;
3613
3823
  }
3824
+ /** True when a `src` function value enters a `dst` slot through
3825
+ * funcCoerceAdapter with NO stranded (trap-only) piece: no rest packs,
3826
+ * no surplus source params, every slot parameter converts into the
3827
+ * wrapped function's own type, and the result converts back (a void
3828
+ * slot drops it; a void result answers the exact JS undefined for
3829
+ * dyn/jsval slots). The width family's func gate — widthLiftPlan
3830
+ * bridges only signatures whose every call succeeds by construction. */
3831
+ cleanFuncAdaptable(src, dst) {
3832
+ if (src.rest === true || dst.rest === true)
3833
+ return false;
3834
+ if (src.params.length > dst.params.length)
3835
+ return false;
3836
+ for (let i = 0; i < src.params.length; i++) {
3837
+ if (!this.coercibleValue(dst.params[i], src.params[i]))
3838
+ return false;
3839
+ }
3840
+ if (dst.ret.kind === "void")
3841
+ return src.ret.kind !== "jsval";
3842
+ if (this.coercibleValue(src.ret, dst.ret))
3843
+ return true;
3844
+ return src.ret.kind === "void" && (dst.ret.kind === "dyn" || dst.ret.kind === "jsval");
3845
+ }
3614
3846
  /** Interned `%fn.adapt.<n>(f)` — the GENERAL function-value adapter: a
3615
3847
  * `fromT` function value flowing into a `toT` slot whose pieces differ
3616
3848
  * only by coercibleValue conversions. The slot's callers pass toT's
@@ -6509,6 +6741,11 @@ export class Lowerer {
6509
6741
  const streamServed = lowerStreamModuleCall(this, call, bi, locOf(access));
6510
6742
  if (streamServed)
6511
6743
  return streamServed;
6744
+ // fs._toUnixTimestamp — off the param tables (an underscore-stable
6745
+ // internal), served by its own spoke before the table fence.
6746
+ const fsTs = this.lowerFsToUnixTimestampCall(call, bi, locOf(access));
6747
+ if (fsTs)
6748
+ return fsTs;
6512
6749
  const builtinFn = builtinModuleFnOf(this, bi.module, bi.member);
6513
6750
  if (!builtinFn) {
6514
6751
  this.noLowering(`${bi.module}.${bi.member}`, call, builtinFenceHintOf(bi.module, bi.member), this.checker.getSymbolAtLocation(access.name));
@@ -6539,6 +6776,11 @@ export class Lowerer {
6539
6776
  const bi = this.builtinMemberOf(expr);
6540
6777
  if (!bi)
6541
6778
  return null;
6779
+ // events.defaultMaxListeners READS the process-wide default (its
6780
+ // write twin routes through emitter.setDefaultMaxChk).
6781
+ if (bi.module === "events" && bi.member === "defaultMaxListeners") {
6782
+ return { kind: "libCall", fn: "emitter.getDefaultMax", args: [], type: F64, loc };
6783
+ }
6542
6784
  const c = builtinModuleConstOf(this, bi.module, bi.member);
6543
6785
  if (c !== undefined)
6544
6786
  return builtinConstLit(c, loc);
@@ -6554,6 +6796,9 @@ export class Lowerer {
6554
6796
  lowerBuiltinModuleCall(expr, bi, fn, loc) {
6555
6797
  return lowerBuiltinModuleCall(this, expr, bi, fn, loc);
6556
6798
  }
6799
+ lowerFsToUnixTimestampCall(expr, bi, loc) {
6800
+ return lowerFsToUnixTimestampCall(this, expr, bi, loc);
6801
+ }
6557
6802
  lowerChildArgsArg(node, loc) {
6558
6803
  return lowerChildArgsArg(this, node, loc);
6559
6804
  }