@scriptc/compiler 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +200 -51
  2. package/dist/backend/c/async.d.ts +25 -0
  3. package/dist/backend/c/async.js +167 -0
  4. package/dist/backend/c/async.js.map +1 -1
  5. package/dist/backend/c/c-emitter.d.ts +10 -0
  6. package/dist/backend/c/c-emitter.js +14 -2
  7. package/dist/backend/c/c-emitter.js.map +1 -1
  8. package/dist/backend/c/exprs.js +293 -51
  9. package/dist/backend/c/exprs.js.map +1 -1
  10. package/dist/backend/c/types.js +2 -0
  11. package/dist/backend/c/types.js.map +1 -1
  12. package/dist/backend/link-plan.d.ts +2 -1
  13. package/dist/backend/link-plan.js +2 -1
  14. package/dist/backend/link-plan.js.map +1 -1
  15. package/dist/backend/llvm/emitter.js +20 -6
  16. package/dist/backend/llvm/emitter.js.map +1 -1
  17. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  18. package/dist/backend/llvm/expr-bytes.js +8 -8
  19. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  20. package/dist/backend/llvm/expr-callbacks.d.ts +9 -0
  21. package/dist/backend/llvm/expr-callbacks.js +125 -0
  22. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  23. package/dist/backend/llvm/expr-context.d.ts +14 -2
  24. package/dist/backend/llvm/expr-dispatch.js +1 -0
  25. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  26. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  27. package/dist/backend/llvm/expr-primitives.js +30 -9
  28. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  29. package/dist/backend/llvm/expr-primitives.test.d.ts +1 -0
  30. package/dist/backend/llvm/expr-primitives.test.js +38 -0
  31. package/dist/backend/llvm/expr-primitives.test.js.map +1 -0
  32. package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
  33. package/dist/backend/llvm/expr-stream-callbacks.js +69 -0
  34. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  35. package/dist/backend/llvm/lib-filesystem.js +68 -10
  36. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  37. package/dist/backend/llvm/lib-network.js +18 -2
  38. package/dist/backend/llvm/lib-network.js.map +1 -1
  39. package/dist/backend/llvm/lib-process.js +156 -6
  40. package/dist/backend/llvm/lib-process.js.map +1 -1
  41. package/dist/backend/llvm/lib-shared.js +36 -0
  42. package/dist/backend/llvm/lib-shared.js.map +1 -1
  43. package/dist/backend/llvm/lib-stream.js +23 -0
  44. package/dist/backend/llvm/lib-stream.js.map +1 -1
  45. package/dist/backend/native-toolchain.d.ts +3 -0
  46. package/dist/backend/native-toolchain.js +3 -1
  47. package/dist/backend/native-toolchain.js.map +1 -1
  48. package/dist/backend/runtime-pack.test.js +28 -10
  49. package/dist/backend/runtime-pack.test.js.map +1 -1
  50. package/dist/backend/targets.d.ts +4 -0
  51. package/dist/backend/targets.js +13 -0
  52. package/dist/backend/targets.js.map +1 -1
  53. package/dist/backend/targets.test.js +8 -1
  54. package/dist/backend/targets.test.js.map +1 -1
  55. package/dist/coverage/surface-manifest.js +8 -0
  56. package/dist/coverage/surface-manifest.js.map +1 -1
  57. package/dist/diagnostics/diagnostic.js +1 -1
  58. package/dist/diagnostics/diagnostic.js.map +1 -1
  59. package/dist/executable/executable-cache.d.ts +2 -0
  60. package/dist/executable/executable-cache.js +3 -0
  61. package/dist/executable/executable-cache.js.map +1 -1
  62. package/dist/frontend/fork-target.d.ts +17 -0
  63. package/dist/frontend/fork-target.js +256 -0
  64. package/dist/frontend/fork-target.js.map +1 -0
  65. package/dist/frontend/lowering/abstract-equality.d.ts +6 -0
  66. package/dist/frontend/lowering/abstract-equality.js +250 -0
  67. package/dist/frontend/lowering/abstract-equality.js.map +1 -0
  68. package/dist/frontend/lowering/lower-assert.js +71 -2
  69. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  70. package/dist/frontend/lowering/lower-builtins.d.ts +50 -23
  71. package/dist/frontend/lowering/lower-builtins.js +939 -106
  72. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  73. package/dist/frontend/lowering/lower-calls.d.ts +29 -21
  74. package/dist/frontend/lowering/lower-calls.js +573 -121
  75. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  76. package/dist/frontend/lowering/lower-classes.js +39 -8
  77. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  78. package/dist/frontend/lowering/lower-containers.js +150 -0
  79. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  80. package/dist/frontend/lowering/lower-event-emitter.js +212 -21
  81. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  82. package/dist/frontend/lowering/lower-exprs.js +135 -83
  83. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  84. package/dist/frontend/lowering/lower-inspect.d.ts +4 -0
  85. package/dist/frontend/lowering/lower-inspect.js +1 -1
  86. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  87. package/dist/frontend/lowering/lower-island.d.ts +6 -3
  88. package/dist/frontend/lowering/lower-island.js +165 -6
  89. package/dist/frontend/lowering/lower-island.js.map +1 -1
  90. package/dist/frontend/lowering/lower-modules.d.ts +6 -0
  91. package/dist/frontend/lowering/lower-modules.js +184 -38
  92. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  93. package/dist/frontend/lowering/lower-namespaces.js +7 -0
  94. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  95. package/dist/frontend/lowering/lower-server.d.ts +1 -1
  96. package/dist/frontend/lowering/lower-server.js +130 -27
  97. package/dist/frontend/lowering/lower-server.js.map +1 -1
  98. package/dist/frontend/lowering/lower-stmts.js +118 -15
  99. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  100. package/dist/frontend/lowering/lower-stream.d.ts +3 -0
  101. package/dist/frontend/lowering/lower-stream.js +7 -0
  102. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  103. package/dist/frontend/lowering/lowerer.d.ts +86 -9
  104. package/dist/frontend/lowering/lowerer.js +412 -72
  105. package/dist/frontend/lowering/lowerer.js.map +1 -1
  106. package/dist/frontend/lowering/surfaces.d.ts +26 -0
  107. package/dist/frontend/lowering/surfaces.js +140 -77
  108. package/dist/frontend/lowering/surfaces.js.map +1 -1
  109. package/dist/frontend/npm-static-declarations.d.ts +4 -0
  110. package/dist/frontend/npm-static-declarations.js +56 -0
  111. package/dist/frontend/npm-static-declarations.js.map +1 -1
  112. package/dist/frontend/npm-static-declarations.test.js +23 -2
  113. package/dist/frontend/npm-static-declarations.test.js.map +1 -1
  114. package/dist/frontend/npm-static.js +6 -5
  115. package/dist/frontend/npm-static.js.map +1 -1
  116. package/dist/frontend/npm.d.ts +15 -3
  117. package/dist/frontend/npm.js +80 -8
  118. package/dist/frontend/npm.js.map +1 -1
  119. package/dist/frontend/npm.test.js +76 -9
  120. package/dist/frontend/npm.test.js.map +1 -1
  121. package/dist/frontend/program.js +92 -10
  122. package/dist/frontend/program.js.map +1 -1
  123. package/dist/frontend/resolve.d.ts +6 -0
  124. package/dist/frontend/resolve.js +7 -4
  125. package/dist/frontend/resolve.js.map +1 -1
  126. package/dist/frontend/type-mapper.d.ts +5 -0
  127. package/dist/frontend/type-mapper.js +64 -24
  128. package/dist/frontend/type-mapper.js.map +1 -1
  129. package/dist/index.d.ts +5 -0
  130. package/dist/index.js +24 -3
  131. package/dist/index.js.map +1 -1
  132. package/dist/ir/ir.d.ts +93 -47
  133. package/dist/ir/ir.js +63 -4
  134. package/dist/ir/ir.js.map +1 -1
  135. package/dist/ir/ir.test.js +14 -1
  136. package/dist/ir/ir.test.js.map +1 -1
  137. package/dist/ir/serialize.d.ts +1 -1
  138. package/dist/ir/serialize.js +1 -1
  139. package/dist/ir/serialize.js.map +1 -1
  140. package/dist/ir/validate.js +165 -13
  141. package/dist/ir/validate.js.map +1 -1
  142. package/dist/library/int-infer.test.js +4 -4
  143. package/dist/library/int-infer.test.js.map +1 -1
  144. package/dist/library/library-cache.test.js +5 -5
  145. package/dist/library/library-cache.test.js.map +1 -1
  146. package/package.json +17 -17
  147. package/surface-manifest.json +132 -38
@@ -8,7 +8,7 @@ import { lowerGenMethodCall } from "./lower-generators.js";
8
8
  import { BIGINT_T, BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
9
9
  import { isJsSourceFile, locOf } from "../program.js";
10
10
  import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
11
- import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
11
+ import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
12
12
  import { enforceLibBoundary } from "./lib-boundary.js";
13
13
  import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
14
14
  import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
@@ -16,7 +16,7 @@ import { mixinFnShapeOf } from "./lower-mixins.js";
16
16
  import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
17
17
  import { bufEncoding } from "./containers/bytes.js";
18
18
  import { lowerRegexMethodCall, lowerStringMethodCall } from "./containers/string-and-regexp.js";
19
- import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
19
+ import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
20
20
  import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, templateRawTextOf } from "./lower-exprs.js";
21
21
  import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
22
22
  import { tryLowerExpression } from "./expressions/try-lower-expression.js";
@@ -32,6 +32,58 @@ import { npmStaticPackageOfPath } from "../npm-static.js";
32
32
  import { countedFor, varRef } from "../../ir/build.js";
33
33
  import { rejectStaticThis } from "./static-this.js";
34
34
  import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
35
+ /** The one func-type projection of completed parameter shapes. Typed rest
36
+ * parameters keep their packed array as the trailing native ABI slot and
37
+ * mark the value variadic so indirect call sites run completeArgs before
38
+ * callValue. Dynamic rest remains the historical hidden ScrDyn slot, while
39
+ * island rest spells its engine-array slot directly. */
40
+ export function funcTypeFromParamShapes(shapes, ret) {
41
+ const typedRest = shapes.some((shape) => shape.mode === "rest");
42
+ const dynRest = shapes.some((shape) => shape.mode === "dynRest");
43
+ const islandRest = shapes.some((shape) => shape.mode === "islandRest");
44
+ return {
45
+ kind: "func",
46
+ params: shapes.filter((shape) => shape.mode !== "dynRest").map((shape) => shape.type),
47
+ ret,
48
+ ...(typedRest || dynRest || islandRest ? { rest: true } : {}),
49
+ ...(typedRest
50
+ ? { restAbi: "typed" }
51
+ : islandRest
52
+ ? { restAbi: "jsval" }
53
+ : {}),
54
+ };
55
+ }
56
+ /** Registers `const alias = overloadedDeclaration` as a compile-time
57
+ * callable projection. The source function's one implementation ABI is
58
+ * already collected in fnSigsBySymbol; each direct call still uses the
59
+ * TS7-resolved overload result bridge. The alias owns no storage, and a
60
+ * value read materializes the source function's interned closure, so JS
61
+ * identity (`alias === source`) is preserved. */
62
+ export function registerOverloadedCallableAlias(lowerer, decl) {
63
+ if (!ts.isIdentifier(decl.name) || !decl.initializer)
64
+ return false;
65
+ let init = decl.initializer;
66
+ while (ts.isParenthesizedExpression(init))
67
+ init = init.expression;
68
+ if (!ts.isIdentifier(init))
69
+ return false;
70
+ const aliasType = lowerer.typeOf(decl.name);
71
+ if (lowerer.checker.getCallSignatures(aliasType).length < 2)
72
+ return false;
73
+ const sourceSymbol = lowerer.resolveValueSymbol(init);
74
+ if (!sourceSymbol)
75
+ return false;
76
+ const sourceProjection = lowerer.staticCallables.get(sourceSymbol);
77
+ const signature = lowerer.fnSigsBySymbol.get(sourceSymbol) ??
78
+ (sourceProjection?.kind === "declared-function" ? sourceProjection.signature : undefined);
79
+ if (!signature)
80
+ return false;
81
+ const aliasSymbol = lowerer.checker.getSymbolAtLocation(decl.name);
82
+ if (!aliasSymbol)
83
+ return false;
84
+ lowerer.staticCallables.set(aliasSymbol, { kind: "declared-function", signature });
85
+ return true;
86
+ }
35
87
  export function generatorMeta(lowerer, type) {
36
88
  const resultType = genResultRecord(type.yieldT, type.retT, lowerer.shapes, lowerer.unions);
37
89
  if (resultType === null) {
@@ -61,6 +113,10 @@ export const MAX_GENERIC_INSTANCES = 100;
61
113
  * - `...xs: T[]`: the ABI type is the array; call sites pack the surplus.
62
114
  */
63
115
  export function paramShape(lowerer, param) {
116
+ const moduleNs = lowerer.moduleNsParamOverrides.get(param);
117
+ if (moduleNs !== undefined) {
118
+ return { type: moduleNs, mode: param.questionToken ? "omittable" : "required" };
119
+ }
64
120
  // Island-handle params (a then-handler receiving a dynamic import's
65
121
  // namespace handle — markJsvalHandlerParams): jsval, whatever the
66
122
  // contextual type spelled.
@@ -256,6 +312,18 @@ export function checkDefaultParamBodyType(lowerer, param, bodyType) {
256
312
  * explicitly-passed `undefined` wraps to — both trigger a default, JS-
257
313
  * exact); a rest param packs the surplus args (possibly zero) into one
258
314
  * array literal, evaluated in source order at the call site. */
315
+ function fixedTupleSpreadInfo(lowerer, node) {
316
+ const type = lowerer.mapTypeOf(lowerer.typeOf(node));
317
+ if (type?.kind !== "record")
318
+ return null;
319
+ const shape = lowerer.shapes.get(type.shapeId);
320
+ if (!shape?.tuple || shape.fields.length === 0)
321
+ return null;
322
+ return {
323
+ type,
324
+ fields: [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name)),
325
+ };
326
+ }
259
327
  export function completeArgs(lowerer, argNodes, shapes, loc, blame,
260
328
  /** Pre-lowered values virtually PREPENDED to the argument list — the
261
329
  * tagged-template strings object, which has no ts.Expression to lower
@@ -264,7 +332,56 @@ export function completeArgs(lowerer, argNodes, shapes, loc, blame,
264
332
  * DYN conversion in a dyn rest, element coercion in a typed rest). */
265
333
  leading) {
266
334
  const isIr = (s) => s !== undefined && !("kind" in s);
267
- const sources = leading && leading.length > 0 ? [...leading.map((ir) => ({ ir })), ...argNodes] : argNodes;
335
+ const sources = leading?.map((ir) => ({ ir })) ?? [];
336
+ for (const arg of argNodes) {
337
+ if (!ts.isSpreadElement(arg)) {
338
+ sources.push(arg);
339
+ continue;
340
+ }
341
+ const tuple = fixedTupleSpreadInfo(lowerer, arg.expression);
342
+ if (!tuple) {
343
+ sources.push(arg);
344
+ continue;
345
+ }
346
+ // A fixed tuple has a compile-time argument count, so flatten it into
347
+ // the callee's ordinary positional ABI. The operand itself still
348
+ // evaluates exactly once and at the spread's source-order position:
349
+ // the first extracted field carries a seqExpr that initializes one
350
+ // hidden tuple local, and every later field reads that same local.
351
+ // Tuple fields are owned reads, so the call receives the same +1
352
+ // values as separately written arguments while the saved tuple stays
353
+ // alive through the call.
354
+ const spreadLoc = locOf(arg);
355
+ const value = lowerer.coerceInto(arg.expression, lowerer.lowerExpr(arg.expression), tuple.type);
356
+ const saved = lowerer.declareHiddenLocal("%callSpread", tuple.type);
357
+ const savedRef = () => ({
358
+ kind: "varRef",
359
+ localId: saved.id,
360
+ type: tuple.type,
361
+ loc: spreadLoc,
362
+ });
363
+ tuple.fields.forEach((field, i) => {
364
+ const read = {
365
+ kind: "recordGet",
366
+ obj: savedRef(),
367
+ shapeId: tuple.type.shapeId,
368
+ field: field.name,
369
+ type: field.type,
370
+ loc: spreadLoc,
371
+ };
372
+ sources.push({
373
+ ir: i === 0
374
+ ? {
375
+ kind: "seqExpr",
376
+ stmts: [{ kind: "varDecl", localId: saved.id, init: value, loc: spreadLoc }],
377
+ result: read,
378
+ type: field.type,
379
+ loc: spreadLoc,
380
+ }
381
+ : read,
382
+ });
383
+ });
384
+ }
268
385
  const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
269
386
  const positional = restAt >= 0 ? shapes.slice(0, restAt) : [...shapes];
270
387
  const out = positional.map((shape, i) => {
@@ -293,6 +410,8 @@ leading) {
293
410
  // reaching here means a call form we don't model — defensive.
294
411
  lowerer.unsupported("SC1090", blame, "this call form");
295
412
  }
413
+ if (shape.callDefault)
414
+ return shape.callDefault;
296
415
  return lowerer.undefinedArgFor(shape.type, loc, blame);
297
416
  });
298
417
  if (restAt >= 0 && shapes[restAt].mode === "islandRest") {
@@ -439,6 +558,34 @@ export function omittedArgFor(lowerer, type, loc) {
439
558
  return { kind: "jsOp", op: "undefLit", args: [], type: JSVAL, loc };
440
559
  return lowerer.wrappedUndefined(type, loc);
441
560
  }
561
+ /** Complete an indirect static call against its func value ABI. Ordinary
562
+ * fixed-width values retain the historical optional-suffix completion.
563
+ * Typed-rest values reinterpret their final array slot as a rest ParamShape
564
+ * and reuse completeArgs, including fixed-tuple and same-element array
565
+ * spreads with source-order/evaluate-once semantics. */
566
+ function completeFuncValueArgs(lowerer, call, funcType, loc) {
567
+ if (funcType.rest === true && funcType.restAbi === "typed") {
568
+ const rest = funcType.params[funcType.params.length - 1];
569
+ if (!rest || rest.kind !== "array") {
570
+ throw new InternalCompilerError("typed-rest function value has no trailing array ABI slot");
571
+ }
572
+ const shapes = funcType.params.slice(0, -1).map((type) => ({
573
+ type,
574
+ mode: omittedArgFor(lowerer, type, loc) ? "omittable" : "required",
575
+ }));
576
+ shapes.push({ type: rest, mode: "rest" });
577
+ return completeArgs(lowerer, call.arguments, shapes, loc, call);
578
+ }
579
+ const args = call.arguments.map((arg, index) => lowerer.lowerExprExpecting(arg, funcType.params[index]));
580
+ for (let i = args.length; i < funcType.params.length; i++) {
581
+ const absent = omittedArgFor(lowerer, funcType.params[i], loc);
582
+ if (!absent) {
583
+ lowerer.unsupported("SC1090", call, "calls omitting a non-optional parameter of the callee's type");
584
+ }
585
+ args.push(absent);
586
+ }
587
+ return args;
588
+ }
442
589
  export function undefinedArgFor(lowerer, type, loc, blame) {
443
590
  if (type.kind === "dyn")
444
591
  return dynUndefinedExpr(loc);
@@ -454,13 +601,12 @@ export function undefinedArgFor(lowerer, type, loc, blame) {
454
601
  }
455
602
  return wrapped;
456
603
  }
457
- /** DECISION (docs/ir.md): function VALUES keep exact-arity semantics — a
458
- * func-typed IrType spells one completed signature, so a function whose
459
- * declaration has optional/default/rest parameters can become a value only
460
- * where the target type spells that exact signature with required
461
- * parameters (`x?: T` / `x: T = e` params appear as literal `T | undefined`
462
- * unions; a rest signature is never spellable without `...`, which func
463
- * types reject). Direct calls get the full feature. */
604
+ /** DECISION (docs/ir.md): function VALUES carry one completed native ABI.
605
+ * Optional/default parameters appear as literal `T | undefined` slots;
606
+ * typed rest parameters appear as one trailing typed-array slot plus the
607
+ * typed-rest marker that makes indirect call sites pack source arguments.
608
+ * A value is admitted only where its target type projects to that same
609
+ * completed signature. */
464
610
  export function requireExactArityValue(lowerer, blame, contextual, shapes, funcType) {
465
611
  // dynRest params ride the boxed thunk (JS arity — no completed-ABI
466
612
  // spelling exists or is needed); they don't gate the value form.
@@ -475,9 +621,6 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
475
621
  (s.mode === "omittable" && (s.type.kind === "dyn" || s.type.kind === "jsval")))) {
476
622
  return;
477
623
  }
478
- if (shapes.some((s) => s.mode === "rest")) {
479
- lowerer.unsupported("SC1090", blame, "functions with rest parameters as values (call them directly)");
480
- }
481
624
  // The type the value FLOWS under must spell the completed ABI: the
482
625
  // contextual (target) type when one exists, otherwise the expression's
483
626
  // OWN inferred type — the unannotated-const case (`const f = (x = 5) =>
@@ -520,6 +663,20 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
520
663
  lowerer.spawnResFnAdapterPlan(funcType, mappedFn) !== null) {
521
664
  return;
522
665
  }
666
+ // An uncontextualized npm-static implementation function can still
667
+ // have a declaration-backed completed ABI: the package's .d.ts
668
+ // projection supplies the body-facing parameter/return types while
669
+ // the shipped JavaScript's own inferred type remains wider (`any`
670
+ // callback fields are the common case). lambdaSignature has already
671
+ // constructed and validated that concrete ABI, and every eventual
672
+ // storage/call site independently checks it. Admit the value at birth;
673
+ // do not make the unprojected JS inference veto its authored surface.
674
+ if (target === undefined &&
675
+ contextual !== null &&
676
+ (ts.isArrowFunction(contextual) || ts.isFunctionExpression(contextual)) &&
677
+ npmStaticPackageOfPath(contextual.getSourceFile().fileName) !== null) {
678
+ return;
679
+ }
523
680
  // An 'any'-typed slot is the ISLAND boundary: the host-function
524
681
  // trampoline already implements JS call semantics over the completed
525
682
  // signature — a missing engine argument arrives as undefined and takes
@@ -1426,12 +1583,7 @@ export function lowerGenericFnValue(lowerer, ref, info) {
1426
1583
  // closure per function, so `f === f` holds like any declaration).
1427
1584
  if (info.implicitParams) {
1428
1585
  const inst = implicitDefaultInstance(lowerer, ref, info);
1429
- const funcType = {
1430
- kind: "func",
1431
- params: inst.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
1432
- ret: inst.returnType,
1433
- ...(inst.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
1434
- };
1586
+ const funcType = funcTypeFromParamShapes(inst.params, inst.returnType);
1435
1587
  lowerer.requireExactArityValue(ref, ref, inst.params, funcType);
1436
1588
  lowerer.noteEdge(inst.name);
1437
1589
  return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
@@ -1477,15 +1629,9 @@ export function lowerGenericFnValue(lowerer, ref, info) {
1477
1629
  if (info.typeParams.some((tp) => !bindings.get(tp)))
1478
1630
  fenceUnpinned();
1479
1631
  const inst = genericValueInstance(lowerer, ref, info, bindings);
1480
- // The value's type is the completed ABI signature — exact-arity, the
1481
- // declared-function value rule (dynRest slots stay out of the param
1482
- // list; the rest marker carries the trailing dyn-array ABI).
1483
- const funcType = {
1484
- kind: "func",
1485
- params: inst.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
1486
- ret: inst.returnType,
1487
- ...(inst.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
1488
- };
1632
+ // The value's type is the completed ABI signature. Typed rest keeps its
1633
+ // packed-array slot; dynRest stays hidden behind the rest marker.
1634
+ const funcType = funcTypeFromParamShapes(inst.params, inst.returnType);
1489
1635
  lowerer.requireExactArityValue(ref, ref, inst.params, funcType);
1490
1636
  lowerer.noteEdge(inst.name);
1491
1637
  return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
@@ -2780,6 +2926,57 @@ export function lowerFfiCall(lowerer, expr) {
2780
2926
  loc,
2781
2927
  };
2782
2928
  }
2929
+ /** Full builtin dispatch for compile-time callable projections. A projected
2930
+ * alias must traverse the same spoke/validation ladder as its direct import;
2931
+ * jumping to the table tail would bypass checked-dynamic argument errors. */
2932
+ function lowerProjectedBuiltinCall(lowerer, expr, bi, loc) {
2933
+ if (bi.module === "child_process" && bi.member === "execFile") {
2934
+ return lowerer.lowerExecFileCall(expr, loc);
2935
+ }
2936
+ if (bi.module === "timers") {
2937
+ const timersServed = lowerTimersMemberCall(lowerer, expr, bi.member, loc);
2938
+ if (timersServed)
2939
+ return timersServed;
2940
+ }
2941
+ const served = lowerer.lowerNetModuleCall(expr, bi, loc);
2942
+ if (served)
2943
+ return served;
2944
+ const dgramServed = lowerer.lowerDgramDnsModuleCall(expr, bi, loc);
2945
+ if (dgramServed)
2946
+ return dgramServed;
2947
+ const assertServed = lowerer.lowerAssertModuleCall(expr, bi, loc);
2948
+ if (assertServed)
2949
+ return assertServed;
2950
+ const testServed = lowerer.lowerNodeTestModuleCall(expr, bi, loc);
2951
+ if (testServed)
2952
+ return testServed;
2953
+ const utilServed = lowerer.lowerUtilModuleCall(expr, bi, loc);
2954
+ if (utilServed)
2955
+ return utilServed;
2956
+ const streamServed = lowerStreamModuleCall(lowerer, expr, bi, loc);
2957
+ if (streamServed)
2958
+ return streamServed;
2959
+ const fsTs = lowerer.lowerFsToUnixTimestampCall(expr, bi, loc);
2960
+ if (fsTs)
2961
+ return fsTs;
2962
+ const fsLadder = lowerer.lowerFsLadderCall(expr, bi, loc);
2963
+ if (fsLadder)
2964
+ return fsLadder;
2965
+ const cryptoServed = lowerer.lowerCryptoModuleCall(expr, bi, loc);
2966
+ if (cryptoServed)
2967
+ return cryptoServed;
2968
+ const timersInterval = lowerer.lowerTimersPromisesSetInterval(expr, bi, loc);
2969
+ if (timersInterval)
2970
+ return timersInterval;
2971
+ const nodeModuleServed = lowerNodeModuleCall(lowerer, expr, bi, loc);
2972
+ if (nodeModuleServed)
2973
+ return nodeModuleServed;
2974
+ const builtinFn = builtinModuleFnOf(lowerer, bi.module, bi.member);
2975
+ if (!builtinFn) {
2976
+ lowerer.noLowering(`${bi.module}.${bi.member}`, expr, builtinFenceHintOf(bi.module, bi.member), ts.isIdentifier(expr.expression) ? lowerer.resolveValueSymbol(expr.expression) : undefined);
2977
+ }
2978
+ return lowerer.lowerBuiltinModuleCall(expr, bi, builtinFn, loc);
2979
+ }
2783
2980
  export function lowerCall(lowerer, expr) {
2784
2981
  const loc = locOf(expr);
2785
2982
  // A call whose chain ROOTS at an ambient-undefined name (`declare
@@ -3371,15 +3568,21 @@ export function lowerCall(lowerer, expr) {
3371
3568
  // supported form is fenced here per site. Members with no lowering at
3372
3569
  // all (fs.watch, os.cpus, ...) fence with the module-qualified name.
3373
3570
  if (ts.isIdentifier(expr.expression)) {
3374
- // A call through a `const execFileAsync = promisify(execFile)`
3375
- // binding — the one lowered util.promisify shape: the interned
3376
- // async-exec helper (Node's promisified execFile behind an
3377
- // already-settled promise).
3571
+ // A call through a compile-time util.promisify projection. execFile
3572
+ // keeps its custom result helper; ordinary builtins route through
3573
+ // their existing promise-module lowering.
3378
3574
  {
3379
3575
  const sym = lowerer.resolveValueSymbol(expr.expression);
3380
- if (sym && lowerer.promisifiedExecFile.has(sym)) {
3576
+ const projection = sym ? lowerer.staticCallables.get(sym) : undefined;
3577
+ if (projection?.kind === "promisified-exec-file") {
3381
3578
  return lowerer.lowerExecFileAsyncCall(expr, loc);
3382
3579
  }
3580
+ if (projection?.kind === "promisified-builtin") {
3581
+ return lowerProjectedBuiltinCall(lowerer, expr, { module: projection.module, member: projection.member }, loc);
3582
+ }
3583
+ if (projection?.kind === "builtin-function") {
3584
+ return lowerProjectedBuiltinCall(lowerer, expr, { module: projection.module, member: projection.member }, loc);
3585
+ }
3383
3586
  // A call through a `const requestFn = tls ? https.request :
3384
3587
  // http.request` binding (the client-function ternary): the http
3385
3588
  // client lowering with the RUNTIME-secure dial.
@@ -3389,6 +3592,9 @@ export function lowerCall(lowerer, expr) {
3389
3592
  }
3390
3593
  const bi = lowerer.builtinImportOf(expr.expression);
3391
3594
  if (bi) {
3595
+ if (bi.module === "child_process" && bi.member === "execFile") {
3596
+ return lowerer.lowerExecFileCall(expr, loc);
3597
+ }
3392
3598
  // The timers spoke: the node:timers module's exports ARE the
3393
3599
  // timer globals (Node re-exports them), so a named/destructured
3394
3600
  // import lands on the same shared lowering. Unknown members fall
@@ -3448,6 +3654,9 @@ export function lowerCall(lowerer, expr) {
3448
3654
  const timersInterval = lowerer.lowerTimersPromisesSetInterval(expr, bi, loc);
3449
3655
  if (timersInterval)
3450
3656
  return timersInterval;
3657
+ const nodeModuleServed = lowerNodeModuleCall(lowerer, expr, bi, loc);
3658
+ if (nodeModuleServed)
3659
+ return nodeModuleServed;
3451
3660
  const builtinFn = builtinModuleFnOf(lowerer, bi.module, bi.member);
3452
3661
  if (!builtinFn) {
3453
3662
  // Typed by @types/node (the fallback declarations only declare
@@ -3754,7 +3963,7 @@ export function lowerCall(lowerer, expr) {
3754
3963
  if (generic && !(jsSpreadArgs && generic.implicitParams))
3755
3964
  return lowerer.lowerGenericCall(expr, generic);
3756
3965
  const sig = generic ? null : lowerer.fnSigOf(expr.expression);
3757
- if (sig && !(jsSpreadArgs && spreadNeedsRuntimeArity(sig.params, expr.arguments))) {
3966
+ if (sig && !(jsSpreadArgs && spreadNeedsRuntimeArity(lowerer, sig.params, expr.arguments))) {
3758
3967
  lowerer.noteEdge(sig.name);
3759
3968
  const args = lowerer.completeArgs(expr.arguments, sig.params, loc, expr);
3760
3969
  return reconcileOverloadReturn(lowerer, expr, { kind: "call", callee: sig.name, args, type: sig.returnType, loc });
@@ -3796,15 +4005,7 @@ export function lowerCall(lowerer, expr) {
3796
4005
  if (callee) {
3797
4006
  if (callee.type.kind !== "func")
3798
4007
  lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
3799
- const params = callee.type.params;
3800
- const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
3801
- for (let i = args.length; i < params.length; i++) {
3802
- const absent = omittedArgFor(lowerer, params[i], loc);
3803
- if (!absent) {
3804
- lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
3805
- }
3806
- args.push(absent);
3807
- }
4008
+ const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
3808
4009
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
3809
4010
  }
3810
4011
  }
@@ -3844,15 +4045,7 @@ export function lowerCall(lowerer, expr) {
3844
4045
  callee = lowerer.hybridCallUnwrap(callee);
3845
4046
  if (callee.type.kind !== "func")
3846
4047
  lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
3847
- const params = callee.type.params;
3848
- const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
3849
- for (let i = args.length; i < params.length; i++) {
3850
- const absent = omittedArgFor(lowerer, params[i], loc);
3851
- if (!absent) {
3852
- lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
3853
- }
3854
- args.push(absent);
3855
- }
4048
+ const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
3856
4049
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
3857
4050
  }
3858
4051
  // An AMBIENT namespace callee (`M.f()` where only `declare
@@ -3905,15 +4098,7 @@ export function lowerCall(lowerer, expr) {
3905
4098
  callee = lowerer.hybridCallUnwrap(callee);
3906
4099
  if (callee.type.kind !== "func")
3907
4100
  lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
3908
- const params = callee.type.params;
3909
- const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
3910
- for (let i = args.length; i < params.length; i++) {
3911
- const absent = omittedArgFor(lowerer, params[i], loc);
3912
- if (!absent) {
3913
- lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
3914
- }
3915
- args.push(absent);
3916
- }
4101
+ const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
3917
4102
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
3918
4103
  }
3919
4104
  if (ts.isPropertyAccessExpression(expr.expression)) {
@@ -4187,6 +4372,14 @@ export function lowerCall(lowerer, expr) {
4187
4372
  if (callee.type.kind !== "func") {
4188
4373
  lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
4189
4374
  }
4375
+ // Typed-rest closures already have a fixed native ABI: complete the
4376
+ // source-level variadic call into their trailing typed-array slot.
4377
+ // Claim before the runtime-arity spread lane; completeArgs handles
4378
+ // typed spreads without boxing the function.
4379
+ if (callee.type.rest === true && callee.type.restAbi === "typed") {
4380
+ const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
4381
+ return { kind: "callValue", callee, args, type: callee.type.ret, loc };
4382
+ }
4190
4383
  // A SPREAD argument on a func-typed callee — the rest-forwarding
4191
4384
  // idiom (`(...args) => from(...args)`): the runtime-arity lane boxes
4192
4385
  // or marshals the callee and applies through a runtime-built argument
@@ -4238,26 +4431,7 @@ export function lowerCall(lowerer, expr) {
4238
4431
  const boxed = { kind: "dynFrom", value: callee, type: DYN, loc };
4239
4432
  return { kind: "dynCall", callee: boxed, calleeName, args, type: DYN, loc };
4240
4433
  }
4241
- const params = callee.type.params;
4242
- const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
4243
- // Optional-param func TYPES map their `x?: T` slots as `T | undefined`
4244
- // ABI unions, and tsc admits calls that omit the optional suffix —
4245
- // complete the missing trailing args with the interned undefined arm,
4246
- // exactly what completeArgs does for direct calls (the ABI stays
4247
- // count-exact). A missing arg whose param has no undefined arm means
4248
- // the callee value's type spelled a required param tsc let the caller
4249
- // skip — not a shape this surface models; fence.
4250
- for (let i = args.length; i < params.length; i++) {
4251
- // A missing argument completes with the slot's absent value — the
4252
- // interned undefined arm, the dyn undefined for checked-dynamic
4253
- // slots (a JS-inferred wrapper like mustCall's, called short), or
4254
- // the engine undefined for island slots.
4255
- const absent = omittedArgFor(lowerer, params[i], loc);
4256
- if (!absent) {
4257
- lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
4258
- }
4259
- args.push(absent);
4260
- }
4434
+ const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
4261
4435
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
4262
4436
  }
4263
4437
  function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
@@ -4505,9 +4679,22 @@ function lowerSpreadArgsCall(lowerer, expr, callee, loc) {
4505
4679
  * (dynRest/islandRest, whose packs are built per-argument): the shapes
4506
4680
  * the runtime-arity lane (lowerSpreadArgsCall) serves. Typed `rest`
4507
4681
  * slots keep completeArgs' same-element spread packing. */
4508
- function spreadNeedsRuntimeArity(shapes, argNodes) {
4682
+ function spreadNeedsRuntimeArity(lowerer, shapes, argNodes) {
4509
4683
  const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
4510
- return argNodes.some((a, i) => ts.isSpreadElement(a) && (restAt < 0 || i < restAt || shapes[restAt].mode !== "rest"));
4684
+ let position = 0;
4685
+ for (const arg of argNodes) {
4686
+ if (ts.isSpreadElement(arg)) {
4687
+ const tuple = fixedTupleSpreadInfo(lowerer, arg.expression);
4688
+ if (tuple) {
4689
+ position += tuple.fields.length;
4690
+ continue;
4691
+ }
4692
+ if (restAt < 0 || position < restAt || shapes[restAt].mode !== "rest")
4693
+ return true;
4694
+ }
4695
+ position++;
4696
+ }
4697
+ return false;
4511
4698
  }
4512
4699
  /** METHOD calls on dyn receivers (`pkg.name.replace(...)`, `rawName.split`,
4513
4700
  * `ws.packages.filter(...)` — JSON.parse-derived values): validate the
@@ -4535,8 +4722,9 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
4535
4722
  const anyArray = arrayReceiver &&
4536
4723
  ((lowerer.checker.getTypeArguments(recvTs)[0]?.flags ?? 0) &
4537
4724
  (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0;
4725
+ const checkerUntyped = (recvTs.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0 || anyArray;
4538
4726
  let recv;
4539
- if (recvTs.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown) || anyArray) {
4727
+ if (checkerUntyped) {
4540
4728
  recv = lowerer.lowerExpr(access.expression);
4541
4729
  }
4542
4730
  else {
@@ -4625,7 +4813,23 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
4625
4813
  // member models. Order note: JS reads the callee before evaluating
4626
4814
  // arguments — dynKeyGet's undefined-receiver TypeError fires first,
4627
4815
  // exactly Node.
4628
- if (DYN_PROTO_METHOD_NAMES.has(access.name.text))
4816
+ // A checker-typed object whose represented value remains dyn still
4817
+ // has an authored own-member contract. Let that member win even when
4818
+ // its name overlaps a primitive prototype (`colors.bold(...)`): the
4819
+ // source value was compiled from that object shape, while genuinely
4820
+ // untyped/any receivers retain the runtime-kind ambiguity and fence.
4821
+ const ownCallable = !checkerUntyped && (() => {
4822
+ const prop = lowerer.checker.getPropertyOfType(recvTs, access.name.text);
4823
+ if (prop === undefined)
4824
+ return false;
4825
+ // getPropertyOfType includes inherited Object.prototype members.
4826
+ // Those are not stored members of a checked-dynamic object, so
4827
+ // dynKeyGet cannot stand in for JavaScript's prototype lookup. Only
4828
+ // an authored declaration can justify the own-member path.
4829
+ const authored = lowerer.checker.declarationsOf(prop).some((decl) => !lowerer.isStdlibFile(decl.getSourceFile()));
4830
+ return authored && lowerer.checker.getCallSignatures(lowerer.checker.getTypeOfSymbol(prop)).length > 0;
4831
+ })();
4832
+ if (DYN_PROTO_METHOD_NAMES.has(access.name.text) && !ownCallable)
4629
4833
  return null;
4630
4834
  // Optional forms (`obj.cb?.()`, `obj?.cb()`) belong to the chain
4631
4835
  // machinery's short-circuit semantics — not modeled here yet.
@@ -5557,6 +5761,8 @@ function loweredTemplateStrings(lowerer, expr, cooked, loc) {
5557
5761
  * same collection lists top-level declarations ride. */
5558
5762
  export function isTopLevelFnSymbol(lowerer, ident) {
5559
5763
  const symbol = lowerer.resolveValueSymbol(ident);
5764
+ if (symbol && lowerer.staticCallables.get(symbol)?.kind === "declared-function")
5765
+ return true;
5560
5766
  const decl = symbol ? lowerer.checker.declarationsOf(symbol)[0] : undefined;
5561
5767
  return (!!decl &&
5562
5768
  ts.isFunctionDeclaration(decl) &&
@@ -5596,10 +5802,9 @@ export function lowerNestedFunctionDecl(lowerer, stmt) {
5596
5802
  return { kind: "assign", localId: local.id, value: init, loc: locOf(stmt) };
5597
5803
  }
5598
5804
  /** Signature checks + param shapes + IR func type for any lambda-like
5599
- * node. The func type's params are the ABI types, so a lambda with
5600
- * optional/default params has the same IR type as one spelling the
5601
- * `T | undefined` unions with required params — exactly the exact-arity
5602
- * value rule (requireExactArityValue decides who may become a value). */
5805
+ * node. The func type's params are the ABI types: optional/default params
5806
+ * become `T | undefined`, and typed rest becomes one packed-array slot.
5807
+ * requireExactArityValue decides whether that completed ABI may escape. */
5603
5808
  export function lambdaSignature(lowerer, node) {
5604
5809
  if (!node.body)
5605
5810
  lowerer.unsupported("SC1090", node, "function overload signatures");
@@ -5699,7 +5904,6 @@ export function lambdaSignature(lowerer, node) {
5699
5904
  // use a rest parameter. Arrows never claim it (JS: an arrow's
5700
5905
  // `arguments` is the enclosing function's).
5701
5906
  const hasDynRest = shapes.some((s) => s.mode === "dynRest");
5702
- const hasIslandRest = shapes.some((s) => s.mode === "islandRest");
5703
5907
  const usesArguments = !hasDynRest &&
5704
5908
  !ts.isArrowFunction(node) &&
5705
5909
  isJsSourceFile(node.getSourceFile()) &&
@@ -5707,20 +5911,10 @@ export function lambdaSignature(lowerer, node) {
5707
5911
  if (usesArguments && node.parameters.length > 0) {
5708
5912
  lowerer.unsupported("SC1090", node, "'arguments' in functions with declared parameters (use a rest parameter: (...args))");
5709
5913
  }
5710
- return {
5711
- shapes,
5712
- funcType: {
5713
- kind: "func",
5714
- // dynRest is EXCLUDED (the boxed thunk fills the trailing dyn
5715
- // array — no spelled slot); islandRest is INCLUDED (the trailing
5716
- // jsval param IS the engine arguments array, the REST host-call
5717
- // adapter's one uniform shape).
5718
- params: shapes.filter((s) => s.mode !== "dynRest").map((s) => s.type),
5719
- ret,
5720
- ...(hasDynRest || usesArguments || hasIslandRest ? { rest: true } : {}),
5721
- ...(hasIslandRest ? { restAbi: "jsval" } : {}),
5722
- },
5723
- };
5914
+ const funcType = funcTypeFromParamShapes(shapes, ret);
5915
+ if (usesArguments && !hasDynRest)
5916
+ funcType.rest = true;
5917
+ return { shapes, funcType };
5724
5918
  }
5725
5919
  /** Does this function's OWN body read `arguments`? Nested plain functions
5726
5920
  * and methods have their own `arguments` (the walk skips them); arrows
@@ -5753,7 +5947,7 @@ export function bodyReadsArguments(fn) {
5753
5947
  export function lowerLambda(lowerer, node) {
5754
5948
  const loc = locOf(node);
5755
5949
  const { shapes, funcType } = lowerer.lambdaSignature(node);
5756
- // A lambda IS a value: the exact-arity rule applies at birth. The
5950
+ // A lambda IS a value: the completed-ABI rule applies at birth. The
5757
5951
  // contextual (target) type decides — `(x?: number) => void` may flow
5758
5952
  // into a slot annotated `(x: number | undefined) => void` (same ABI
5759
5953
  // signature), anything else is fenced. Nested function declarations and
@@ -5900,7 +6094,7 @@ export function lowerLambda(lowerer, node) {
5900
6094
  // closure-signature check trips (SC9001). Island rest types SPELL
5901
6095
  // their trailing engine-array param, so funcType.params already
5902
6096
  // covers those.
5903
- if (funcType.rest === true && funcType.restAbi !== "jsval") {
6097
+ if (funcType.rest === true && funcType.restAbi === undefined) {
5904
6098
  params.push({ localId: "%pfrest", name: "%pfrest", type: DYN });
5905
6099
  }
5906
6100
  const fence = lowerer.deferToRuntimeFence(diagsBefore, node, {
@@ -5955,13 +6149,16 @@ export function lowerLambda(lowerer, node) {
5955
6149
  * to a promise-of-jsval local or module global answers the binding's
5956
6150
  * type. Null everywhere else. */
5957
6151
  function islandPromiseStorageTypeOf(lowerer, e) {
5958
- const direct = importCallHandleType(e);
6152
+ const direct = lowerer.dynamic
6153
+ ? importCallHandleType(e)
6154
+ : staticImportNamespaceType(lowerer, e);
5959
6155
  if (direct?.kind === "promise")
5960
6156
  return direct;
5961
6157
  if (!ts.isIdentifier(e))
5962
6158
  return null;
5963
6159
  const local = lowerer.resolveLocal(e);
5964
- if (local?.type.kind === "promise" && local.type.inner.kind === "jsval")
6160
+ if (local?.type.kind === "promise" &&
6161
+ (local.type.inner.kind === "jsval" || local.type.inner.kind === "moduleNs"))
5965
6162
  return local.type;
5966
6163
  if (local)
5967
6164
  return null;
@@ -5969,7 +6166,8 @@ function islandPromiseStorageTypeOf(lowerer, e) {
5969
6166
  if (sym && sym.flags & ts.SymbolFlags.Alias)
5970
6167
  sym = lowerer.checker.getAliasedSymbol(sym);
5971
6168
  const g = sym ? lowerer.globalsBySymbol.get(sym) : undefined;
5972
- if (g?.type.kind === "promise" && g.type.inner.kind === "jsval")
6169
+ if (g?.type.kind === "promise" &&
6170
+ (g.type.inner.kind === "jsval" || g.type.inner.kind === "moduleNs"))
5973
6171
  return g.type;
5974
6172
  return null;
5975
6173
  }
@@ -5989,6 +6187,18 @@ function markJsvalHandlerParams(lowerer, handler) {
5989
6187
  }
5990
6188
  }
5991
6189
  }
6190
+ function markModuleNsHandlerParams(lowerer, handler, type) {
6191
+ let e = handler;
6192
+ while (ts.isParenthesizedExpression(e))
6193
+ e = e.expression;
6194
+ if (!ts.isArrowFunction(e) && !ts.isFunctionExpression(e))
6195
+ return;
6196
+ for (const p of e.parameters) {
6197
+ if (ts.isIdentifier(p.name) && !p.dotDotDotToken && !p.initializer) {
6198
+ lowerer.moduleNsParamOverrides.set(p, type);
6199
+ }
6200
+ }
6201
+ }
5992
6202
  export function lowerPromiseMethodCall(lowerer, call, access) {
5993
6203
  if (call.questionDotToken || access.questionDotToken)
5994
6204
  return null;
@@ -6002,7 +6212,7 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
6002
6212
  // (importCallHandleType / the island-HANDLE var rules), so the storage
6003
6213
  // type is the receiver's truth. Direct `import("./m").then(...)`
6004
6214
  // spells the same promise with no binding at all.
6005
- if (!recvT && lowerer.dynamic)
6215
+ if (!recvT)
6006
6216
  recvT = islandPromiseStorageTypeOf(lowerer, access.expression);
6007
6217
  if (recvT?.kind !== "promise")
6008
6218
  return null;
@@ -6107,6 +6317,8 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
6107
6317
  // the handle is the value's only story, isIslandExpr's local rule).
6108
6318
  if (inner.kind === "jsval")
6109
6319
  markJsvalHandlerParams(lowerer, call.arguments[0]);
6320
+ if (inner.kind === "moduleNs")
6321
+ markModuleNsHandlerParams(lowerer, call.arguments[0], inner);
6110
6322
  let cb = lowerer.lowerExpr(call.arguments[0]);
6111
6323
  // A TYPED handler on a DYN-settling promise (the tracePromise
6112
6324
  // result's `.then((value) => ...)` — the checker's generic
@@ -7607,6 +7819,48 @@ function lowerObjectStaticCall(lowerer, call, access) {
7607
7819
  if (call.arguments.length !== 1 || ts.isSpreadElement(call.arguments[0]))
7608
7820
  return null;
7609
7821
  const argNode = call.arguments[0];
7822
+ // A compiled PROGRAM module namespace is a nominal token whose members
7823
+ // remain live bindings, but its enumerable key set is static and Node
7824
+ // sorts it in code-unit order. Materialize exactly those value-export
7825
+ // names for Object.keys; values/entries would need a heterogeneous live
7826
+ // view and stay explicitly fenced. Builtin namespaces also stay fenced:
7827
+ // their exact runtime export set is Node-owned, not the ambient subset.
7828
+ {
7829
+ const probed = tryLowerExpression(lowerer, argNode);
7830
+ if (probed?.type.kind === "moduleNs") {
7831
+ if (member !== "keys") {
7832
+ lowerer.noLowering(`Object.${member} over a module namespace`, call, "read the named exports directly (module namespace values are live and may have heterogeneous representations)");
7833
+ }
7834
+ const source = lowerer.sourceFileOfModuleNamespace(probed.type);
7835
+ if (source === null) {
7836
+ lowerer.noLowering("Object.keys over a builtin module namespace", call, "access the builtin's named exports directly (the exact runtime key census is Node-owned)");
7837
+ }
7838
+ const moduleSymbol = source ? lowerer.checker.getSymbolAtLocation(source) : undefined;
7839
+ const names = [];
7840
+ moduleSymbol?.getExports().forEach((symbol, key) => {
7841
+ const name = String(key);
7842
+ if (name.startsWith("__") || name === "export=")
7843
+ return;
7844
+ const target = symbol.flags & ts.SymbolFlags.Alias
7845
+ ? lowerer.checker.getAliasedSymbol(symbol)
7846
+ : symbol;
7847
+ if (target.flags & ts.SymbolFlags.Value)
7848
+ names.push(name);
7849
+ });
7850
+ names.sort();
7851
+ const loc = locOf(call);
7852
+ const result = {
7853
+ kind: "arrayLit",
7854
+ elems: names.map((name) => ({ kind: "strLit", value: name, type: STRING, loc })),
7855
+ type: arrayOf(STRING),
7856
+ loc,
7857
+ };
7858
+ const receiver = lowerer.lowerExpr(argNode);
7859
+ return isSafeToDiscard(receiver)
7860
+ ? result
7861
+ : { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc }], result, type: result.type, loc };
7862
+ }
7863
+ }
7610
7864
  // A CHECKED-DYNAMIC argument — the checker may still spell a record
7611
7865
  // type (the JS file-scope object-literal identity story stores the
7612
7866
  // dyn object), so the LOWERED value's kind is the dispatch: the
@@ -7991,8 +8245,7 @@ export function lowerRecordFieldCall(lowerer, call, access) {
7991
8245
  callee = lowerer.hybridCallUnwrap(callee);
7992
8246
  if (callee.type.kind !== "func")
7993
8247
  lowerer.badType(access, lowerer.typeOf(access));
7994
- const params = callee.type.params;
7995
- const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
8248
+ const args = completeFuncValueArgs(lowerer, call, callee.type, locOf(call));
7996
8249
  return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
7997
8250
  }
7998
8251
  /** The function-like node behind an object-literal generic-method member:
@@ -8846,6 +9099,12 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8846
9099
  if (lowerer.chainBlocked(access, call))
8847
9100
  return null;
8848
9101
  const mappedReceiver = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
9102
+ if (mappedReceiver?.kind === "union") {
9103
+ const dispatched = lowerUnionObjectMethodCall(lowerer, call, access, mappedReceiver) ??
9104
+ lowerUnionObjectDynFieldCall(lowerer, call, access, mappedReceiver);
9105
+ if (dispatched)
9106
+ return dispatched;
9107
+ }
8849
9108
  const receiverIr = mappedReceiver?.kind === "object"
8850
9109
  ? mappedReceiver
8851
9110
  : mappedReceiver?.kind === "union"
@@ -8927,15 +9186,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8927
9186
  }
8928
9187
  }
8929
9188
  if (callee?.type.kind === "func") {
8930
- const params = callee.type.params;
8931
- const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
8932
- for (let i = args.length; i < params.length; i++) {
8933
- const absent = omittedArgFor(lowerer, params[i], locOf(call));
8934
- if (!absent) {
8935
- lowerer.unsupported("SC1090", call, "calls omitting a non-optional parameter of the callee's type");
8936
- }
8937
- args.push(absent);
8938
- }
9189
+ const args = completeFuncValueArgs(lowerer, call, callee.type, locOf(call));
8939
9190
  return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
8940
9191
  }
8941
9192
  if (callee?.type.kind === "dyn") {
@@ -8997,4 +9248,205 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8997
9248
  loc: locOf(call),
8998
9249
  });
8999
9250
  }
9251
+ /** A method TypeScript proves callable on every arm of an unrelated class
9252
+ * union. Each runtime tag keeps its own direct/virtual dispatch; only the
9253
+ * completed argument ABI must agree. Results flow into the checker-selected
9254
+ * call type through the ordinary wrap/retag/width machinery. */
9255
+ function lowerUnionObjectMethodCall(lowerer, call, access, receiverT) {
9256
+ const def = lowerer.unions.get(receiverT.unionId);
9257
+ if (!def || def.arms.length < 2 || !def.arms.every((arm) => arm.kind === "object"))
9258
+ return null;
9259
+ const method = access.name.text;
9260
+ const plans = [];
9261
+ for (const arm of def.arms) {
9262
+ const info = lowerer.classes.get(arm.className);
9263
+ if (!info) {
9264
+ lowerer.flushDeferredClass(arm.className);
9265
+ return null;
9266
+ }
9267
+ const found = lowerer.findMethodOn(info, method);
9268
+ if (!found)
9269
+ return null;
9270
+ if (found.sig.abstract === true && !lowerer.overrideBelow(info, method))
9271
+ return null;
9272
+ plans.push({ arm, info, found });
9273
+ }
9274
+ const shapes = plans[0].found.sig.params;
9275
+ if (!plans.every((plan) => paramAbisEqual(shapes, plan.found.sig.params)))
9276
+ return null;
9277
+ const resultT = lowerer.mapTypeOf(lowerer.typeOf(call));
9278
+ if (!resultT || isUnitType(resultT))
9279
+ return null;
9280
+ const loc = locOf(call);
9281
+ const receiver = lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), receiverT);
9282
+ const args = lowerer.completeArgs(call.arguments, shapes, loc, call);
9283
+ const helper = unionObjectMethodHelper(lowerer, call, receiverT, method, plans, shapes, resultT, loc);
9284
+ return { kind: "call", callee: helper, args: [receiver, ...args], type: resultT, loc };
9285
+ }
9286
+ function paramAbisEqual(left, right) {
9287
+ return left.length === right.length && left.every((shape, i) => {
9288
+ const other = right[i];
9289
+ return other !== undefined && shape.mode === other.mode && typeEquals(shape.type, other.type);
9290
+ });
9291
+ }
9292
+ /** The JS-class sibling of union method dispatch: every arm stores the
9293
+ * named callable in a checked-dynamic field. A helper selects and retains
9294
+ * the field value before the dynCall evaluates its source arguments, so an
9295
+ * argument that overwrites the field cannot change this invocation. */
9296
+ function lowerUnionObjectDynFieldCall(lowerer, call, access, receiverT) {
9297
+ const def = lowerer.unions.get(receiverT.unionId);
9298
+ if (!def || def.arms.length < 2 || !def.arms.every((arm) => arm.kind === "object"))
9299
+ return null;
9300
+ const field = access.name.text;
9301
+ const plans = [];
9302
+ for (const arm of def.arms) {
9303
+ const info = lowerer.classes.get(arm.className);
9304
+ if (!info || info.fields.get(field)?.kind !== "dyn")
9305
+ return null;
9306
+ plans.push({ arm, info });
9307
+ }
9308
+ if (call.arguments.some(ts.isSpreadElement)) {
9309
+ lowerer.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
9310
+ }
9311
+ const loc = locOf(call);
9312
+ const receiver = lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), receiverT);
9313
+ const key = `${receiverT.unionId}:dyn-field:${field}`;
9314
+ let helper = lowerer.unionCallHelpers.get(key);
9315
+ if (!helper) {
9316
+ helper = `%union.call.${lowerer.unionCallHelpers.size}`;
9317
+ lowerer.unionCallHelpers.set(key, helper);
9318
+ const receiverRef = varRef("this.0", receiverT, loc);
9319
+ const branch = (plan) => {
9320
+ const concrete = {
9321
+ kind: "unionNarrow",
9322
+ unionId: receiverT.unionId,
9323
+ tag: lowerer.armTag(receiverT.unionId, plan.arm),
9324
+ value: receiverRef,
9325
+ type: plan.arm,
9326
+ loc,
9327
+ };
9328
+ return [{
9329
+ kind: "return",
9330
+ value: {
9331
+ kind: "fieldGet",
9332
+ obj: concrete,
9333
+ className: plan.info.def.name,
9334
+ field,
9335
+ type: DYN,
9336
+ loc,
9337
+ },
9338
+ loc,
9339
+ }];
9340
+ };
9341
+ let body = branch(plans[plans.length - 1]);
9342
+ for (let i = plans.length - 2; i >= 0; i--) {
9343
+ const plan = plans[i];
9344
+ body = [{
9345
+ kind: "if",
9346
+ cond: {
9347
+ kind: "unionIsTag",
9348
+ unionId: receiverT.unionId,
9349
+ tag: lowerer.armTag(receiverT.unionId, plan.arm),
9350
+ negated: false,
9351
+ value: receiverRef,
9352
+ type: BOOL,
9353
+ loc,
9354
+ },
9355
+ then: branch(plan),
9356
+ else_: body,
9357
+ loc,
9358
+ }];
9359
+ }
9360
+ const params = [
9361
+ { localId: "this.0", name: "this", type: receiverT },
9362
+ ];
9363
+ const locals = params.map((param) => ({ id: param.localId, name: param.name, type: param.type, mutable: false }));
9364
+ lowerer.liftedFns.push({ name: helper, params, returnType: DYN, locals, body, loc });
9365
+ }
9366
+ const callee = { kind: "call", callee: helper, args: [receiver], type: DYN, loc };
9367
+ const args = call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN));
9368
+ return { kind: "dynCall", callee, calleeName: access.getText(), args, type: DYN, loc };
9369
+ }
9370
+ function unionObjectMethodHelper(lowerer, sourceCall, receiverT, method, plans, shapes, resultT, loc) {
9371
+ const key = `${receiverT.unionId}:${method}:${shapes.map((shape) => `${shape.mode}:${typeKey(shape.type)}`).join(",")}:${typeKey(resultT)}`;
9372
+ const existing = lowerer.unionCallHelpers.get(key);
9373
+ if (existing)
9374
+ return existing;
9375
+ const name = `%union.call.${lowerer.unionCallHelpers.size}`;
9376
+ lowerer.unionCallHelpers.set(key, name);
9377
+ const receiver = varRef("this.0", receiverT, loc);
9378
+ const argRefs = shapes.map((shape, i) => varRef(`a.${i}`, shape.type, loc));
9379
+ const branch = (plan) => {
9380
+ const concrete = {
9381
+ kind: "unionNarrow",
9382
+ unionId: receiverT.unionId,
9383
+ tag: lowerer.armTag(receiverT.unionId, plan.arm),
9384
+ value: receiver,
9385
+ type: plan.arm,
9386
+ loc,
9387
+ };
9388
+ let invoke;
9389
+ if (plan.found.declarer.builtinError) {
9390
+ invoke = {
9391
+ kind: "libCall",
9392
+ fn: "error.toString",
9393
+ args: [lowerer.upcastTo(concrete, plan.found.declarer.def.name)],
9394
+ type: STRING,
9395
+ loc,
9396
+ };
9397
+ }
9398
+ else if (lowerer.overrideBelow(plan.info, method)) {
9399
+ lowerer.noteVirtualEdge(plan.info, method);
9400
+ invoke = {
9401
+ kind: "virtualCall",
9402
+ className: plan.info.def.name,
9403
+ method,
9404
+ args: [lowerer.upcastTo(concrete, plan.info.def.name), ...argRefs],
9405
+ type: plan.found.sig.ret,
9406
+ loc,
9407
+ };
9408
+ }
9409
+ else {
9410
+ lowerer.noteEdge(`%${plan.found.declarer.def.name}.${method}`);
9411
+ invoke = {
9412
+ kind: "call",
9413
+ callee: `%${plan.found.declarer.def.name}.${method}`,
9414
+ args: [lowerer.upcastTo(concrete, plan.found.declarer.def.name), ...argRefs],
9415
+ type: plan.found.sig.ret,
9416
+ loc,
9417
+ };
9418
+ }
9419
+ invoke = reconcileOverloadReturn(lowerer, sourceCall, invoke);
9420
+ const result = lowerer.coerceInto(sourceCall, invoke, resultT);
9421
+ return resultT.kind === "void"
9422
+ ? [{ kind: "exprStmt", expr: result, loc }, { kind: "return", value: null, loc }]
9423
+ : [{ kind: "return", value: result, loc }];
9424
+ };
9425
+ let body = branch(plans[plans.length - 1]);
9426
+ for (let i = plans.length - 2; i >= 0; i--) {
9427
+ const plan = plans[i];
9428
+ body = [{
9429
+ kind: "if",
9430
+ cond: {
9431
+ kind: "unionIsTag",
9432
+ unionId: receiverT.unionId,
9433
+ tag: lowerer.armTag(receiverT.unionId, plan.arm),
9434
+ negated: false,
9435
+ value: receiver,
9436
+ type: BOOL,
9437
+ loc,
9438
+ },
9439
+ then: branch(plan),
9440
+ else_: body,
9441
+ loc,
9442
+ }];
9443
+ }
9444
+ const params = [
9445
+ { localId: "this.0", name: "this", type: receiverT },
9446
+ ...shapes.map((shape, i) => ({ localId: `a.${i}`, name: `a${i}`, type: shape.type })),
9447
+ ];
9448
+ const locals = params.map((param) => ({ id: param.localId, name: param.name, type: param.type, mutable: false }));
9449
+ lowerer.liftedFns.push({ name, params, returnType: resultT, locals, body, loc });
9450
+ return name;
9451
+ }
9000
9452
  //# sourceMappingURL=lower-calls.js.map