@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
@@ -10,15 +10,16 @@ import { BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KIN
10
10
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
11
11
  import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModuleFnOf, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
12
12
  import { blockedBindingUseDiag, recordShapeMismatchDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
13
- import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, own } from "./lowerer.js";
13
+ import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
14
14
  import { lowerIndexMergeHelper, lowerNpmStaticSafeIndexRead, strCharsCall } from "./lower-containers.js";
15
15
  import { npmStaticPackageOfPath } from "../npm-static.js";
16
+ import { unsupportedModuleFeatureOf } from "../shared.js";
16
17
  import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
17
18
  import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, contextualUndefReadType, fenceEarlyAliasUse, fenceEarlyNsMemberRef, lowerNsIdentifierValue, nsMemberIdentOf, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
18
19
  import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
19
20
  import { lowerSocketInstanceOf } from "./lower-server.js";
20
21
  import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
21
- import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
22
+ import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
22
23
  import { mixinFnOfCallee } from "./lower-mixins.js";
23
24
  import { isConstAssertionTypeNode, isGenericCallableMemberType, underConstAssertion, unitOnlyUnion } from "../types.js";
24
25
  import { lowerYield } from "./lower-generators.js";
@@ -692,7 +693,7 @@ function lowerExprInner(L, expr) {
692
693
  {
693
694
  const spec = L.fencedBuiltinImportOf(expr);
694
695
  if (spec !== null) {
695
- L.pushDiag(unsupportedDiag("SC1010", loc, `the '${spec}' module`));
696
+ L.pushDiag(unsupportedDiag("SC1010", loc, unsupportedModuleFeatureOf(spec)));
696
697
  throw new PoisonError();
697
698
  }
698
699
  }
@@ -748,8 +749,9 @@ function lowerExprInner(L, expr) {
748
749
  BigInt: "arbitrary-precision integers have no static representation — f64 is the one number type (Number.MAX_SAFE_INTEGER bounds exact integers)",
749
750
  Proxy: "property-access metaprogramming has no static lowering (every property read must resolve at compile time)",
750
751
  Reflect: "reflective property access has no static lowering — read and call members directly",
751
- Intl: "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data)",
752
+ Intl: "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat(\"en-US\").format(x) and x.toLocaleString(\"en-US\") with default options",
752
753
  SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
754
+ ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
753
755
  WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
754
756
  FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
755
757
  eval: "runtime code evaluation cannot be compiled ahead of time",
@@ -794,6 +796,18 @@ function lowerExprInner(L, expr) {
794
796
  return nsUndefRead(L, expr.text, expr, t);
795
797
  }
796
798
  }
799
+ // A read of a TRAP binding — a declaration whose own initializer
800
+ // provably threw (module init unwound there), so this reference can
801
+ // never execute: any lowering is sound, and the trap keeps the
802
+ // shape honest. Typed by the use site when it maps, the F64 dummy
803
+ // otherwise (never observed — never even reached).
804
+ {
805
+ const sym = L.resolveValueSymbol(expr);
806
+ if (sym !== null && L.trapBindings.has(sym)) {
807
+ const t = ambientUndefReadType(L, expr) ?? contextualUndefReadType(L, expr) ?? F64;
808
+ return nsUndefRead(L, expr.text, expr, t);
809
+ }
810
+ }
797
811
  // A program CLASS NAME as a value: the classRef over the class's
798
812
  // immortal class object (member accesses and construction never
799
813
  // reach here — their hooks claim the property/new forms first).
@@ -1404,12 +1418,25 @@ function lowerExprInner(L, expr) {
1404
1418
  // read (ARR answers length; everything else JS's own-property
1405
1419
  // answer). Mapped receivers keep the fence-first order: their
1406
1420
  // members' gaps are real surface gaps ([1,2].entries), not
1407
- // representation artifacts.
1408
- if (isJsSourceFile(expr.getSourceFile()) &&
1421
+ // representation artifacts. `.length` on a receiver whose checker
1422
+ // ARRAY type lowers to the checked-dynamic representation —
1423
+ // `unknown[]`, the collapsed `(string | object)[]`, and the `any[]`
1424
+ // an Array.isArray guard narrows a collapsed union to — is the DOM
1425
+ // array's OWN length (a keyed read the ARR kind answers exactly),
1426
+ // never an `Array.prototype` surface gap; both source languages.
1427
+ // Prototype-method VALUE reads (`ps.map` unparenthesized) keep the
1428
+ // fence-first order: a stored-member undefined would mis-answer
1429
+ // them, and calls dispatch through the dyn method machinery
1430
+ // instead.
1431
+ if ((isJsSourceFile(expr.getSourceFile()) &&
1409
1432
  (L.mapTypeOf(L.typeOf(expr.expression)) === null ||
1410
1433
  // A never-tainted receiver type maps (never rides as f64) but
1411
1434
  // its VALUE lowered checked-dynamic — same DOM read.
1412
- neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression)))) {
1435
+ neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression)))) ||
1436
+ (expr.name.text === "length" &&
1437
+ (L.checkerAnyArray(expr.expression) ||
1438
+ (L.checker.isArrayType(L.typeOf(expr.expression)) &&
1439
+ L.mapTypeOf(L.typeOf(expr.expression))?.kind === "dyn")))) {
1413
1440
  const recv = L.lowerExpr(expr.expression);
1414
1441
  if (recv.type.kind === "dyn") {
1415
1442
  const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
@@ -1477,6 +1504,17 @@ function lowerExprInner(L, expr) {
1477
1504
  return nsUndefRead(L, ambientRoot.text, expr, t);
1478
1505
  }
1479
1506
  }
1507
+ // A member read through a NULLISH generic binding (`const i: I<A &
1508
+ // B> = null as any; const _i: I<A> = i.something` — the receiver
1509
+ // provably holds null/undefined forever): the read throws Node's
1510
+ // exact TypeError at the access.
1511
+ if (ts.isIdentifier(expr.expression) && expr.questionDotToken === undefined) {
1512
+ const unit = nullishGenericBindingUnitOf(L, L.resolveValueSymbol(expr.expression));
1513
+ if (unit !== null) {
1514
+ const t = ambientUndefReadType(L, expr) ?? contextualUndefReadType(L, expr) ?? F64;
1515
+ return nodeThrowExpr(1, "", `Cannot read properties of ${unit} (reading '${expr.name.text}')`, t, loc);
1516
+ }
1517
+ }
1480
1518
  // The lib fence's PROPERTY chokepoint: a stdlib-declared member that
1481
1519
  // no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
1482
1520
  // re.exec as a value, ...) reports SC2020 here.
@@ -2152,7 +2190,18 @@ function pureCondExpr(e) {
2152
2190
  export function lowerNullishCoalesce(L, expr, loc) {
2153
2191
  const left = L.lowerExpr(expr.left);
2154
2192
  if (left.type.kind === "dyn") {
2155
- L.unsupported("SC1100", expr, "nullish coalescing on 'unknown' values");
2193
+ // `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
2194
+ // runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
2195
+ // a wrapped island value routes to the engine's test, defensively:
2196
+ // the wrap constructor scalar-normalizes engine null/undefined
2197
+ // away). Both sides live in the DOM — the right converts through
2198
+ // the usual boundary and evaluates lazily in its branch; a default
2199
+ // with no dyn representation keeps the fence.
2200
+ const right = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
2201
+ if (right.type.kind !== "dyn") {
2202
+ L.unsupported("SC1100", expr, "nullish coalescing on 'unknown' values against defaults with no dynamic representation");
2203
+ }
2204
+ return { kind: "nullish", left, right, type: DYN, loc };
2156
2205
  }
2157
2206
  if (left.type.kind === "jsval") {
2158
2207
  // `a ?? b` on an ISLAND value: the engine's own nullish test — the
@@ -2986,6 +3035,24 @@ export function lowerIntrinsicProperty(L, expr) {
2986
3035
  return read;
2987
3036
  return { kind: "jsExit", value: read, type: F64, loc: locOf(expr) };
2988
3037
  }
3038
+ // A CHECKED-DYNAMIC value behind an array-typed checker spelling
3039
+ // (`Object.keys(u).length` — the dyn walk answers a DOM array while
3040
+ // tsc spells string[]): the runtime-world dispatch — a DOM keyed
3041
+ // read validated into the declared number (a lying length throws
3042
+ // the catchable TypeError, the dynCheck stance).
3043
+ if (receiver.type.kind === "dyn") {
3044
+ const read = {
3045
+ kind: "dynKeyGet",
3046
+ key: { kind: "strLit", value: "length", type: STRING, loc: locOf(expr) },
3047
+ ...(expr.questionDotToken ? { optional: true } : {}),
3048
+ value: receiver,
3049
+ type: DYN,
3050
+ loc: locOf(expr),
3051
+ };
3052
+ if (expr.questionDotToken)
3053
+ return read;
3054
+ return { kind: "dynCheck", value: read, type: F64, loc: locOf(expr) };
3055
+ }
2989
3056
  return kind === "string"
2990
3057
  ? { kind: "strIntrinsic", method: "length", receiver, args: [], type: F64, loc: locOf(expr) }
2991
3058
  : { kind: "arrIntrinsic", method: "length", receiver, args: [], type: F64, loc: locOf(expr) };
@@ -3146,8 +3213,12 @@ export function lowerArrayLiteral(L, expr, expected) {
3146
3213
  // []) builds as a DOM ARRAY — one dyn value whose elements each
3147
3214
  // convert through the usual boundary (dynFrom's JSON-safe domain);
3148
3215
  // an element that cannot convert fences per element. length/index
3149
- // reads and dynamic consumers ride the keyed-DOM paths.
3150
- if (isJsSourceFile(expr.getSourceFile())) {
3216
+ // reads and dynamic consumers ride the keyed-DOM paths. TS literals
3217
+ // take the same build when the slot ITSELF is checked-dynamic — an
3218
+ // `unknown[]` annotation or a collapsed `(string | object)[]` maps
3219
+ // to DYN wholesale now (mapType's dyn-element array rule), so the
3220
+ // literal IS the DOM array.
3221
+ if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
3151
3222
  const elems = expr.elements.map((el) => {
3152
3223
  if (ts.isSpreadElement(el)) {
3153
3224
  L.unsupported("SC1090", el, "spread elements in a dynamic (unknown[]) array literal");
@@ -4158,7 +4229,9 @@ export function lowerObjectLiteral(L, expr) {
4158
4229
  }
4159
4230
  const shapeMismatch = (node) => {
4160
4231
  const own = L.mapTypeOf(L.typeOf(expr));
4161
- L.pushDiag(recordShapeMismatchDiag(L.fmt(type), own ? L.fmt(own) : L.checker.typeToString(L.typeOf(expr)), locOf(node)));
4232
+ L.pushDiag(recordShapeMismatchDiag(L.fmt(type), own ? L.fmt(own) : L.checker.typeToString(L.typeOf(expr)), locOf(node), own?.kind === "record"
4233
+ ? (L.describeRecordWidthBlocker(own.shapeId, type.shapeId) ?? undefined)
4234
+ : undefined));
4162
4235
  throw new PoisonError();
4163
4236
  };
4164
4237
  const fields = [];
@@ -4340,7 +4413,7 @@ export function lowerObjectLiteral(L, expr) {
4340
4413
  if (targetType.kind === "union") {
4341
4414
  const retag = L.unionRetagHelper(f.type.unionId, targetType.unionId, locOf(prop));
4342
4415
  if (!retag) {
4343
- L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop)));
4416
+ L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop), `spread field '${f.name}': '${L.fmt(f.type)}' cannot re-tag into '${L.fmt(targetType)}' behind the present-test`));
4344
4417
  throw new PoisonError();
4345
4418
  }
4346
4419
  thenVal = { kind: "call", callee: retag, args: [fRead()], type: targetType, loc: locOf(prop) };
@@ -4349,7 +4422,7 @@ export function lowerObjectLiteral(L, expr) {
4349
4422
  thenVal = { kind: "unionNarrow", unionId: f.type.unionId, tag: L.armTag(f.type.unionId, targetType), value: fRead(), type: targetType, loc: locOf(prop) };
4350
4423
  }
4351
4424
  else {
4352
- L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop)));
4425
+ L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop), `spread field '${f.name}': '${L.fmt(f.type)}' cannot narrow into '${L.fmt(targetType)}' behind the present-test`));
4353
4426
  throw new PoisonError();
4354
4427
  }
4355
4428
  }
@@ -4361,7 +4434,7 @@ export function lowerObjectLiteral(L, expr) {
4361
4434
  // undefined trap must not shadow.
4362
4435
  const lift = L.widthLiftPlan(f.type, targetType);
4363
4436
  if (!lift) {
4364
- L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop)));
4437
+ L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(recArm), locOf(prop), `spread field '${f.name}': '${L.fmt(f.type)}' does not lift into '${L.fmt(targetType)}'`));
4365
4438
  throw new PoisonError();
4366
4439
  }
4367
4440
  thenVal = L.applyWidthLift(lift, fRead(), targetType, locOf(prop));
@@ -4441,7 +4514,7 @@ export function lowerObjectLiteral(L, expr) {
4441
4514
  // it can render identically to the target while the spread
4442
4515
  // source (the thing that actually mismatches) differs — an
4443
4516
  // invisible difference is a diagnostics bug.
4444
- L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(srcType), locOf(prop)));
4517
+ L.pushDiag(recordShapeMismatchDiag(L.fmt(type), L.fmt(srcType), locOf(prop), `spread field '${f.name}': '${L.fmt(f.type)}' does not lift into '${L.fmt(targetType)}'`));
4445
4518
  throw new PoisonError();
4446
4519
  }
4447
4520
  const obj = srcLowered ?? L.lowerExpr(srcNode);
@@ -5049,7 +5122,24 @@ export function lowerElementAccess(L, expr) {
5049
5122
  ? null
5050
5123
  : L.mapTypeOf(L.typeOf(expr.expression));
5051
5124
  if (receiverIr?.kind === "jsval") {
5052
- return islandElementRead(L, expr, L.lowerExpr(expr.expression));
5125
+ // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
5126
+ // receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
5127
+ // bag.list is a routed keyed read off a wrapped island value) takes
5128
+ // the DOM keyed read below — the routed JSVAL arm reads the real
5129
+ // engine element; a jsval-lowered receiver keeps the island read.
5130
+ const recv = L.lowerExpr(expr.expression);
5131
+ if (recv.type.kind !== "dyn")
5132
+ return islandElementRead(L, expr, recv);
5133
+ const rawKey = L.lowerExpr(expr.argumentExpression);
5134
+ const key = rawKey.type.kind === "string"
5135
+ ? rawKey
5136
+ : rawKey.type.kind === "f64"
5137
+ ? { kind: "toString", operand: rawKey, type: STRING, loc: rawKey.loc }
5138
+ : null;
5139
+ if (key) {
5140
+ const opt = chainGuardedByQuestionDot(expr.expression);
5141
+ return L.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recv, type: DYN, loc: locOf(expr) }, expr);
5142
+ }
5053
5143
  }
5054
5144
  // Typed-array element read `b[i]` — like arrayGet with a scalar
5055
5145
  // result; any invalid index traps (the array discipline; JS would
@@ -5127,9 +5217,12 @@ export function lowerElementAccess(L, expr) {
5127
5217
  const obj = L.lowerExpr(expr.expression);
5128
5218
  if (obj.type.kind === "dyn") {
5129
5219
  const rawKey = L.lowerExpr(expr.argumentExpression);
5220
+ // Number, bool, and DYN keys stringify (ToPropertyKey) — the
5221
+ // dyn-keyed read `catchWarning[warning.name]` where the property
5222
+ // chain itself lowered dyn.
5130
5223
  const key = rawKey.type.kind === "string"
5131
5224
  ? rawKey
5132
- : rawKey.type.kind === "f64"
5225
+ : rawKey.type.kind === "f64" || rawKey.type.kind === "bool" || rawKey.type.kind === "dyn"
5133
5226
  ? { kind: "toString", operand: rawKey, type: STRING, loc: rawKey.loc }
5134
5227
  : null;
5135
5228
  if (key) {
@@ -5495,11 +5588,58 @@ export function lowerElementWrite(L, expr) {
5495
5588
  const receiverIr = L.mapTypeOf(L.typeOf(target.expression));
5496
5589
  if (receiverIr?.kind === "jsval") {
5497
5590
  const obj = L.lowerExpr(target.expression);
5591
+ // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
5592
+ // receiver whose value LOWERED checked-dynamic takes the DOM keyed
5593
+ // write — the routed JSVAL arm lands it on the real engine object.
5594
+ if (obj.type.kind === "dyn") {
5595
+ const loc = locOf(expr);
5596
+ const rawKey = L.lowerExpr(target.argumentExpression);
5597
+ const key = rawKey.type.kind === "f64" || rawKey.type.kind === "bool" || rawKey.type.kind === "dyn"
5598
+ ? { kind: "toString", operand: rawKey, type: STRING, loc: rawKey.loc }
5599
+ : rawKey;
5600
+ if (key.type.kind !== "string") {
5601
+ L.unsupported("SC1090", target.argumentExpression, `'${L.fmt(key.type)}'-typed keys in keyed writes through 'unknown' values`);
5602
+ }
5603
+ const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5604
+ if (value.type.kind !== "dyn") {
5605
+ L.unsupported("SC1100", expr.right, `assigning '${L.fmt(value.type)}' values through 'unknown' receivers`);
5606
+ }
5607
+ return {
5608
+ kind: "exprStmt",
5609
+ expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc },
5610
+ loc,
5611
+ };
5612
+ }
5498
5613
  const key = L.jsvalIn(L.lowerExpr(target.argumentExpression), target.argumentExpression);
5499
5614
  const value = L.jsvalIn(L.lowerExpr(expr.right), expr.right);
5500
5615
  const loc = locOf(expr);
5501
5616
  return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [obj, key, value], type: VOID, loc }, loc };
5502
5617
  }
5618
+ // A checked-dynamic receiver TYPE (`unknown[]` and the collapsed
5619
+ // `(string | object)[]` map to DYN wholesale now): the DOM keyed
5620
+ // write — dyn.keySet. Number keys canonicalize through the JS-exact
5621
+ // formatter; ARR receivers take canonical index keys as element
5622
+ // set/extend (undefined-hole padding, JS's length growth exactly),
5623
+ // OBJ receivers set the member, and the runtime throws Node's
5624
+ // TypeErrors on every other kind. Values convert into the DOM;
5625
+ // unconvertible ones fence per site (the dynFrom stance).
5626
+ if (receiverIr?.kind === "dyn") {
5627
+ const obj = L.lowerExpr(target.expression);
5628
+ if (obj.type.kind === "dyn") {
5629
+ const loc = locOf(expr);
5630
+ let key = L.lowerExpr(target.argumentExpression);
5631
+ if (key.type.kind === "f64")
5632
+ key = L.ensureString(key, target.argumentExpression);
5633
+ if (key.type.kind !== "string") {
5634
+ L.unsupported("SC1090", target.argumentExpression, "indexing with non-string or non-number keys");
5635
+ }
5636
+ const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5637
+ if (value.type.kind !== "dyn") {
5638
+ L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic array (the value cannot convert into the DOM)`);
5639
+ }
5640
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5641
+ }
5642
+ }
5503
5643
  // Typed-array element write `b[i] = v` — bytesSet: the value is an f64
5504
5644
  // the runtime coerces JS-exactly; invalid indices trap (no appends —
5505
5645
  // typed arrays are fixed-length; JS would ignore the write).
@@ -5676,6 +5816,39 @@ export function lowerElementWrite(L, expr) {
5676
5816
  };
5677
5817
  }
5678
5818
  }
5819
+ // Keyed write `d[k] = v` on a CHECKED-DYNAMIC receiver (a JS `any`
5820
+ // dictionary — the Object.create(null) memo-table idiom writes
5821
+ // `result[key] = value`): dyn.keySet, exactly the record-mapped dyn
5822
+ // arm above — later writes win in insertion order, number keys
5823
+ // canonicalize through the JS-exact formatter (ToPropertyKey's string
5824
+ // side), index keys on DOM arrays set/extend elements, and non-object
5825
+ // receivers throw Node's TypeErrors at runtime. An UNMAPPED checker
5826
+ // type (static `any` — mapTypeOf answers null without --dynamic)
5827
+ // probes the receiver's own lowered world: a dyn value takes the same
5828
+ // write, anything else falls through to the fences.
5829
+ if (receiverIr?.kind === "dyn" || receiverIr === null) {
5830
+ const obj = receiverIr !== null ? L.lowerExpr(target.expression) : probeLower(L, target.expression);
5831
+ if (obj !== null && obj.type.kind === "dyn") {
5832
+ const loc = locOf(expr);
5833
+ const litKey = recordKeyLiteralText(target.argumentExpression);
5834
+ let key = litKey !== null
5835
+ ? { kind: "strLit", value: litKey, type: STRING, loc: locOf(target.argumentExpression) }
5836
+ : L.lowerExpr(target.argumentExpression);
5837
+ if (key.type.kind === "f64")
5838
+ key = L.ensureString(key, target.argumentExpression);
5839
+ if (key.type.kind === "bool" || key.type.kind === "dyn") {
5840
+ key = { kind: "toString", operand: key, type: STRING, loc: locOf(target.argumentExpression) };
5841
+ }
5842
+ if (key.type.kind !== "string") {
5843
+ L.unsupported("SC1090", target.argumentExpression, "indexing checked-dynamic values with non-string or non-number keys");
5844
+ }
5845
+ const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5846
+ if (value.type.kind !== "dyn") {
5847
+ L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the DOM)`);
5848
+ }
5849
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5850
+ }
5851
+ }
5679
5852
  if (receiverIr?.kind !== "array") {
5680
5853
  L.unsupported("SC1090", target, "assignment to non-array elements");
5681
5854
  }
@@ -6126,6 +6299,46 @@ export function lowerBinary(L, expr) {
6126
6299
  const value = L.lowerExprExpecting(expr.right, target.type);
6127
6300
  return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
6128
6301
  }
6302
+ // `events.defaultMaxListeners = v` — the module-property write
6303
+ // Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
6304
+ // the value crosses into the DOM and the runtime ladder throws
6305
+ // ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE with Node's exact slot
6306
+ // name; valid numbers apply. The expression's value is the RHS
6307
+ // (JS's assignment yield).
6308
+ if (op === ts.SyntaxKind.EqualsToken &&
6309
+ ts.isPropertyAccessExpression(expr.left) &&
6310
+ !expr.left.questionDotToken &&
6311
+ expr.left.name.text === "defaultMaxListeners") {
6312
+ const bi = L.builtinMemberOf(expr.left);
6313
+ if (bi && bi.module === "events" && bi.member === "defaultMaxListeners") {
6314
+ const rhs = L.lowerExpr(expr.right);
6315
+ if (rhs.type.kind === "dyn" || rhs.kind === "unitLit" || L.dynConvertible(rhs.type)) {
6316
+ const valTmp = L.declareHiddenLocal("%setVal", rhs.type);
6317
+ const valRef = () => ({ kind: "varRef", localId: valTmp.id, type: rhs.type, loc });
6318
+ const dynVal = rhs.type.kind === "dyn" ? valRef() : { kind: "dynFrom", value: valRef(), type: DYN, loc };
6319
+ return {
6320
+ kind: "seqExpr",
6321
+ stmts: [
6322
+ { kind: "varDecl", localId: valTmp.id, init: rhs, loc },
6323
+ {
6324
+ kind: "exprStmt",
6325
+ expr: {
6326
+ kind: "libCall",
6327
+ fn: "emitter.setDefaultMaxChk",
6328
+ args: [dynVal, { kind: "strLit", value: "defaultMaxListeners", type: STRING, loc }],
6329
+ type: VOID,
6330
+ loc,
6331
+ },
6332
+ loc,
6333
+ },
6334
+ ],
6335
+ result: valRef(),
6336
+ type: rhs.type,
6337
+ loc,
6338
+ };
6339
+ }
6340
+ }
6341
+ }
6129
6342
  // Member targets whose storage IS a module global — namespace
6130
6343
  // members (`N.x`) and expando function members (`Foo.baz`): the
6131
6344
  // same assignExpr, since the "member" is a variable.
@@ -6611,6 +6824,7 @@ export function lowerBinary(L, expr) {
6611
6824
  kind: "unionEq",
6612
6825
  unionId: ut.unionId,
6613
6826
  negated,
6827
+ sameValue: false,
6614
6828
  left: L.coerceInto(expr.left, left, ut),
6615
6829
  right: L.coerceInto(expr.right, right, ut),
6616
6830
  type: BOOL,