@scriptc/compiler 0.0.36 → 0.1.2

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 (256) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +52 -22
  2. package/ambient/scriptc-overrides.d.ts +0 -7
  3. package/ambient/scriptc.d.ts +3 -2
  4. package/dist/backend/build-cache.js.map +1 -1
  5. package/dist/backend/c/async.js +24 -2
  6. package/dist/backend/c/async.js.map +1 -1
  7. package/dist/backend/c/c-emitter.d.ts +22 -10
  8. package/dist/backend/c/c-emitter.js +60 -15
  9. package/dist/backend/c/c-emitter.js.map +1 -1
  10. package/dist/backend/c/exprs.js +289 -55
  11. package/dist/backend/c/exprs.js.map +1 -1
  12. package/dist/backend/c/island.js.map +1 -1
  13. package/dist/backend/c/may-throw.js +3 -0
  14. package/dist/backend/c/may-throw.js.map +1 -1
  15. package/dist/backend/c/stmts.d.ts +3 -5
  16. package/dist/backend/c/stmts.js +76 -17
  17. package/dist/backend/c/stmts.js.map +1 -1
  18. package/dist/backend/c/types.js.map +1 -1
  19. package/dist/backend/c/walkers.js +36 -12
  20. package/dist/backend/c/walkers.js.map +1 -1
  21. package/dist/backend/external-c.d.ts +33 -0
  22. package/dist/backend/external-c.js +51 -0
  23. package/dist/backend/external-c.js.map +1 -0
  24. package/dist/backend/link-plan.d.ts +37 -0
  25. package/dist/backend/link-plan.js +26 -0
  26. package/dist/backend/link-plan.js.map +1 -0
  27. package/dist/backend/linker.d.ts +22 -0
  28. package/dist/backend/linker.js +236 -0
  29. package/dist/backend/linker.js.map +1 -0
  30. package/dist/backend/llvm/blocks.js.map +1 -1
  31. package/dist/backend/llvm/dyn.js +63 -3
  32. package/dist/backend/llvm/dyn.js.map +1 -1
  33. package/dist/backend/llvm/emitter.js +132 -26
  34. package/dist/backend/llvm/emitter.js.map +1 -1
  35. package/dist/backend/llvm/expr-async.js +85 -3
  36. package/dist/backend/llvm/expr-async.js.map +1 -1
  37. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  38. package/dist/backend/llvm/expr-calls.js.map +1 -1
  39. package/dist/backend/llvm/expr-containers.js +68 -54
  40. package/dist/backend/llvm/expr-containers.js.map +1 -1
  41. package/dist/backend/llvm/expr-context.d.ts +2 -1
  42. package/dist/backend/llvm/expr-control.js.map +1 -1
  43. package/dist/backend/llvm/expr-dispatch.js +2 -0
  44. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  45. package/dist/backend/llvm/expr-dynamic.js +6 -3
  46. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  47. package/dist/backend/llvm/expr-island.js.map +1 -1
  48. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  49. package/dist/backend/llvm/expr-primitives.js +20 -0
  50. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  51. package/dist/backend/llvm/expr-records.js.map +1 -1
  52. package/dist/backend/llvm/expr-stream-bridges.js +245 -10
  53. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  54. package/dist/backend/llvm/lib-dispatch.js +2 -0
  55. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  56. package/dist/backend/llvm/lib-shared.js +18 -0
  57. package/dist/backend/llvm/lib-shared.js.map +1 -1
  58. package/dist/backend/llvm/lib-stream.js +6 -3
  59. package/dist/backend/llvm/lib-stream.js.map +1 -1
  60. package/dist/backend/llvm/split.js.map +1 -1
  61. package/dist/backend/llvm/unsupported.js.map +1 -1
  62. package/dist/backend/llvm/walkers.js +31 -4
  63. package/dist/backend/llvm/walkers.js.map +1 -1
  64. package/dist/backend/native-codegen.d.ts +8 -1
  65. package/dist/backend/native-codegen.js +39 -25
  66. package/dist/backend/native-codegen.js.map +1 -1
  67. package/dist/backend/native-codegen.test.js +44 -4
  68. package/dist/backend/native-codegen.test.js.map +1 -1
  69. package/dist/backend/native-link-info.d.ts +1 -1
  70. package/dist/backend/native-link-info.js +42 -18
  71. package/dist/backend/native-link-info.js.map +1 -1
  72. package/dist/backend/native-toolchain.js.map +1 -1
  73. package/dist/backend/native-toolchain.test.js +5 -1
  74. package/dist/backend/native-toolchain.test.js.map +1 -1
  75. package/dist/backend/runtime-pack-targets.test.d.ts +1 -0
  76. package/dist/backend/runtime-pack-targets.test.js +54 -0
  77. package/dist/backend/runtime-pack-targets.test.js.map +1 -0
  78. package/dist/backend/runtime-pack.d.ts +5 -30
  79. package/dist/backend/runtime-pack.js +46 -141
  80. package/dist/backend/runtime-pack.js.map +1 -1
  81. package/dist/backend/runtime-pack.test.js +49 -14
  82. package/dist/backend/runtime-pack.test.js.map +1 -1
  83. package/dist/backend/targets.d.ts +63 -17
  84. package/dist/backend/targets.js +251 -35
  85. package/dist/backend/targets.js.map +1 -1
  86. package/dist/backend/targets.test.js +27 -5
  87. package/dist/backend/targets.test.js.map +1 -1
  88. package/dist/backend/vendor-archives.js.map +1 -1
  89. package/dist/compat/fetch-profile.js +4 -2
  90. package/dist/compat/fetch-profile.js.map +1 -1
  91. package/dist/coverage/report.js.map +1 -1
  92. package/dist/coverage/surface-manifest.js +22 -1
  93. package/dist/coverage/surface-manifest.js.map +1 -1
  94. package/dist/diagnostics/diagnostic.d.ts +6 -5
  95. package/dist/diagnostics/diagnostic.js +7 -6
  96. package/dist/diagnostics/diagnostic.js.map +1 -1
  97. package/dist/diagnostics/render.js.map +1 -1
  98. package/dist/ffi/ffi-manifest.js.map +1 -1
  99. package/dist/frontend/cjs-lexer.js.map +1 -1
  100. package/dist/frontend/dts-paths.js.map +1 -1
  101. package/dist/frontend/input-tracker.d.ts +10 -0
  102. package/dist/frontend/input-tracker.js +26 -3
  103. package/dist/frontend/input-tracker.js.map +1 -1
  104. package/dist/frontend/input-tracker.test.js +8 -1
  105. package/dist/frontend/input-tracker.test.js.map +1 -1
  106. package/dist/frontend/lib-contract.js.map +1 -1
  107. package/dist/frontend/lowering/array-values.d.ts +12 -0
  108. package/dist/frontend/lowering/array-values.js +76 -0
  109. package/dist/frontend/lowering/array-values.js.map +1 -0
  110. package/dist/frontend/lowering/callback-arg.js.map +1 -1
  111. package/dist/frontend/lowering/containers/bytes.d.ts +35 -0
  112. package/dist/frontend/lowering/containers/bytes.js +1021 -0
  113. package/dist/frontend/lowering/containers/bytes.js.map +1 -0
  114. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
  115. package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
  116. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
  117. package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
  118. package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
  119. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
  120. package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
  121. package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
  122. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
  123. package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
  124. package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
  125. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
  126. package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
  127. package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
  128. package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
  129. package/dist/frontend/lowering/lib-boundary.js +7 -2
  130. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  131. package/dist/frontend/lowering/lower-array-sort.d.ts +22 -0
  132. package/dist/frontend/lowering/lower-array-sort.js +619 -0
  133. package/dist/frontend/lowering/lower-array-sort.js.map +1 -0
  134. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  135. package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
  136. package/dist/frontend/lowering/lower-async-iteration.js +657 -0
  137. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
  138. package/dist/frontend/lowering/lower-builtins.d.ts +18 -0
  139. package/dist/frontend/lowering/lower-builtins.js +379 -48
  140. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  141. package/dist/frontend/lowering/lower-calls.d.ts +16 -8
  142. package/dist/frontend/lowering/lower-calls.js +667 -91
  143. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  144. package/dist/frontend/lowering/lower-classes.d.ts +7 -13
  145. package/dist/frontend/lowering/lower-classes.js +128 -35
  146. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  147. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  148. package/dist/frontend/lowering/lower-containers.d.ts +1 -47
  149. package/dist/frontend/lowering/lower-containers.js +779 -2038
  150. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  151. package/dist/frontend/lowering/lower-event-emitter.js +51 -5
  152. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  153. package/dist/frontend/lowering/lower-exprs.d.ts +4 -78
  154. package/dist/frontend/lowering/lower-exprs.js +1128 -2409
  155. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  156. package/dist/frontend/lowering/lower-generators.d.ts +18 -3
  157. package/dist/frontend/lowering/lower-generators.js +145 -9
  158. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  159. package/dist/frontend/lowering/lower-inspect.js +50 -36
  160. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  161. package/dist/frontend/lowering/lower-island.d.ts +11 -4
  162. package/dist/frontend/lowering/lower-island.js +47 -10
  163. package/dist/frontend/lowering/lower-island.js.map +1 -1
  164. package/dist/frontend/lowering/lower-modules.js +52 -15
  165. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  166. package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
  167. package/dist/frontend/lowering/lower-node-module.js +302 -0
  168. package/dist/frontend/lowering/lower-node-module.js.map +1 -0
  169. package/dist/frontend/lowering/lower-server.js +11 -1
  170. package/dist/frontend/lowering/lower-server.js.map +1 -1
  171. package/dist/frontend/lowering/lower-stmts.d.ts +5 -24
  172. package/dist/frontend/lowering/lower-stmts.js +811 -314
  173. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  174. package/dist/frontend/lowering/lower-stream.js +4 -4
  175. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  176. package/dist/frontend/lowering/lowerer.d.ts +109 -34
  177. package/dist/frontend/lowering/lowerer.js +1225 -62
  178. package/dist/frontend/lowering/lowerer.js.map +1 -1
  179. package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
  180. package/dist/frontend/lowering/optional-arguments.js +57 -0
  181. package/dist/frontend/lowering/optional-arguments.js.map +1 -0
  182. package/dist/frontend/lowering/surfaces.d.ts +10 -2
  183. package/dist/frontend/lowering/surfaces.js +15 -3
  184. package/dist/frontend/lowering/surfaces.js.map +1 -1
  185. package/dist/frontend/npm-static-declarations.d.ts +35 -0
  186. package/dist/frontend/npm-static-declarations.js +506 -0
  187. package/dist/frontend/npm-static-declarations.js.map +1 -0
  188. package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
  189. package/dist/frontend/npm-static-declarations.test.js +122 -0
  190. package/dist/frontend/npm-static-declarations.test.js.map +1 -0
  191. package/dist/frontend/npm-static.d.ts +2 -0
  192. package/dist/frontend/npm-static.js +26 -10
  193. package/dist/frontend/npm-static.js.map +1 -1
  194. package/dist/frontend/npm.d.ts +60 -0
  195. package/dist/frontend/npm.js +38 -4
  196. package/dist/frontend/npm.js.map +1 -1
  197. package/dist/frontend/npm.test.d.ts +1 -0
  198. package/dist/frontend/npm.test.js +50 -0
  199. package/dist/frontend/npm.test.js.map +1 -0
  200. package/dist/frontend/program.d.ts +3 -2
  201. package/dist/frontend/program.js +495 -68
  202. package/dist/frontend/program.js.map +1 -1
  203. package/dist/frontend/resolve.d.ts +10 -2
  204. package/dist/frontend/resolve.js +90 -14
  205. package/dist/frontend/resolve.js.map +1 -1
  206. package/dist/frontend/resolve.test.js +4 -1
  207. package/dist/frontend/resolve.test.js.map +1 -1
  208. package/dist/frontend/runtime-resolve.d.ts +22 -0
  209. package/dist/frontend/runtime-resolve.js +101 -0
  210. package/dist/frontend/runtime-resolve.js.map +1 -0
  211. package/dist/frontend/runtime-resolve.test.d.ts +1 -0
  212. package/dist/frontend/runtime-resolve.test.js +18 -0
  213. package/dist/frontend/runtime-resolve.test.js.map +1 -0
  214. package/dist/frontend/ts7/adapter.js +12 -13
  215. package/dist/frontend/ts7/adapter.js.map +1 -1
  216. package/dist/frontend/ts7/ast.js.map +1 -1
  217. package/dist/frontend/ts7/census-check.js +3 -3
  218. package/dist/frontend/ts7/census-check.js.map +1 -1
  219. package/dist/frontend/ts7/checker.d.ts +3 -0
  220. package/dist/frontend/ts7/checker.js +12 -3
  221. package/dist/frontend/ts7/checker.js.map +1 -1
  222. package/dist/frontend/ts7/enums.js.map +1 -1
  223. package/dist/frontend/ts7/program-adapter.js +42 -3
  224. package/dist/frontend/ts7/program-adapter.js.map +1 -1
  225. package/dist/frontend/type-mapper.js +120 -7
  226. package/dist/frontend/type-mapper.js.map +1 -1
  227. package/dist/index.d.ts +3 -3
  228. package/dist/index.js +50 -28
  229. package/dist/index.js.map +1 -1
  230. package/dist/ir/analysis.js.map +1 -1
  231. package/dist/ir/ir.d.ts +125 -41
  232. package/dist/ir/ir.js +82 -31
  233. package/dist/ir/ir.js.map +1 -1
  234. package/dist/ir/serialize.d.ts +1 -1
  235. package/dist/ir/serialize.js +1 -1
  236. package/dist/ir/validate.js +127 -95
  237. package/dist/ir/validate.js.map +1 -1
  238. package/dist/library/cache-primitives.js.map +1 -1
  239. package/dist/library/cache-reset.test.js.map +1 -1
  240. package/dist/library/compiler-self-identity.js.map +1 -1
  241. package/dist/library/fence-eval.js +3 -1
  242. package/dist/library/fence-eval.js.map +1 -1
  243. package/dist/library/int-infer.js +12 -1
  244. package/dist/library/int-infer.js.map +1 -1
  245. package/dist/library/int-infer.test.js +4 -4
  246. package/dist/library/int-infer.test.js.map +1 -1
  247. package/dist/library/library-cache.test.js +5 -5
  248. package/dist/library/library-profile.js.map +1 -1
  249. package/dist/library/sidecar-validate.js.map +1 -1
  250. package/dist/library/sidecar.js.map +1 -1
  251. package/dist/startup-cache.d.ts +9 -1
  252. package/dist/startup-cache.js +16 -1
  253. package/dist/startup-cache.js.map +1 -1
  254. package/dist/wasi-paths.js.map +1 -1
  255. package/package.json +18 -5
  256. package/surface-manifest.json +64 -8
@@ -1,3 +1,4 @@
1
+ import { InternalCompilerError } from "../../errors.js";
1
2
  /* Call lowering: the lowerCall dispatch chain, parameter-shape analysis and
2
3
  * argument completion (optional/default/rest, explicit-undefined ≡ omission),
3
4
  * function/lambda lowering and signature collection, and monomorphizing
@@ -6,15 +7,19 @@ import * as ts from "../ts7/adapter.js";
6
7
  import { lowerGenMethodCall } from "./lower-generators.js";
7
8
  import { 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";
8
9
  import { isJsSourceFile, locOf } from "../program.js";
9
- import { isGenericCallableMemberType, typeKey } from "../type-mapper.js";
10
+ import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
10
11
  import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
11
12
  import { enforceLibBoundary } from "./lib-boundary.js";
12
13
  import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
13
14
  import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
14
15
  import { mixinFnShapeOf } from "./lower-mixins.js";
15
- import { bufEncoding, dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerRegexMethodCall, lowerStringMethodCall, lowerTupleReadMethodCall } from "./lower-containers.js";
16
- import { lowerChildStreamMethodCall, lowerCreateRequireCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerWatcherMethodCall } from "./lower-builtins.js";
17
- import { droppableStatic, lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
16
+ import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
17
+ import { bufEncoding } from "./containers/bytes.js";
18
+ import { lowerRegexMethodCall, lowerStringMethodCall } from "./containers/string-and-regexp.js";
19
+ import { lowerChildStreamMethodCall, lowerCreateRequireCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
20
+ import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, templateRawTextOf } from "./lower-exprs.js";
21
+ import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
22
+ import { tryLowerExpression } from "./expressions/try-lower-expression.js";
18
23
  import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerCompatReqStreamOptionalCall, lowerHttpClientFnCall } from "./lower-server.js";
19
24
  import { EMITTER_API_MEMBERS, exactInstanceClassOf, findGenericMethodOn, lowerClassGenericMethodCall, lowerStaticMethodCall } from "./lower-classes.js";
20
25
  import { emitterRooted, lowerEmitterMethodCall } from "./lower-event-emitter.js";
@@ -26,6 +31,14 @@ import { expandoMemberRead } from "./lower-expando.js";
26
31
  import { npmStaticPackageOfPath } from "../npm-static.js";
27
32
  import { countedFor, varRef } from "../../ir/build.js";
28
33
  import { rejectStaticThis } from "./static-this.js";
34
+ import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
35
+ export function generatorMeta(lowerer, type) {
36
+ const resultType = genResultRecord(type.yieldT, type.retT, lowerer.shapes, lowerer.unions);
37
+ if (resultType === null) {
38
+ throw new InternalCompilerError("lowerer bug: mapped generator without an IteratorResult record");
39
+ }
40
+ return { yieldT: type.yieldT, nextT: type.nextT, resultType };
41
+ }
29
42
  /** Instantiation cap per generic function: same-key recursion (`len<T>`
30
43
  * calling itself) converges, but POLYMORPHIC recursion (`f<T>` calling
31
44
  * `f<T[]>`) would request new instances forever — the cap turns that into a
@@ -101,7 +114,7 @@ export function paramShape(lowerer, param) {
101
114
  }
102
115
  return { type: abi, mode: "omittable", bodyType };
103
116
  }
104
- return { type: lowerer.irTypeOf(param.name), mode: "required" };
117
+ return { type: lowerer.runtimeOptionalBindingType(param.name, lowerer.irTypeOf(param.name)), mode: "required" };
105
118
  }
106
119
  if (param.dotDotDotToken) {
107
120
  // A JS rest param with no static element type (`(...args)` — any[]):
@@ -124,7 +137,7 @@ export function paramShape(lowerer, param) {
124
137
  return { type: DYN, mode: "dynRest" };
125
138
  }
126
139
  }
127
- const type = lowerer.irTypeOf(param.name);
140
+ const type = lowerer.runtimeOptionalBindingType(param.name, lowerer.irTypeOf(param.name));
128
141
  // Tuple-typed rest params don't map to an array; generic rest is the
129
142
  // generic path's business. Anything non-array here is unmappable.
130
143
  if (type.kind !== "array")
@@ -167,7 +180,7 @@ export function paramShape(lowerer, param) {
167
180
  }
168
181
  return { type: abi, mode: "omittable", bodyType };
169
182
  }
170
- const type = lowerer.irTypeOf(param.name);
183
+ const type = lowerer.runtimeOptionalBindingType(param.name, lowerer.irTypeOf(param.name));
171
184
  if (param.questionToken && !lowerer.bareUndefinedArmedUnion(type) && type.kind !== "dyn" && type.kind !== "jsval") {
172
185
  // `x?: unknown` where unknown came from an annotation: undefined is
173
186
  // absorbed into the hole type, so no undefined ARM exists — but a
@@ -195,7 +208,7 @@ export function paramShapes(lowerer, params, signature, blameOf) {
195
208
  return params.map((declParam, i) => {
196
209
  const symbol = signature.getParameters()[i];
197
210
  const tsType = symbol ? lowerer.checker.getTypeOfSymbol(symbol) : lowerer.typeOf(declParam.name);
198
- const mapped = lowerer.mapTypeOf(tsType);
211
+ const mapped = lowerer.runtimeOptionalBindingType(declParam.name, lowerer.mapTypeOf(tsType) ?? lowerer.irTypeOf(declParam.name));
199
212
  if (!mapped || mapped.kind === "void")
200
213
  lowerer.badType(blameOf?.(declParam, i) ?? declParam.name, tsType);
201
214
  if (declParam.dotDotDotToken) {
@@ -585,6 +598,16 @@ export function appendImplicitUndefinedReturn(lowerer, body, bodyReturn, loc) {
585
598
  /** A declaration's checker-derived IR return type. The unmappable-type
586
599
  * diagnostic points at `blame` (the name for top-level declarations, the
587
600
  * whole node for lambda-likes — preserving each caller's historical loc). */
601
+ function hasExplicitJsDocReturn(decl) {
602
+ const sourceFile = decl.getSourceFile();
603
+ return /@returns?\b/.test(sourceFile.text.slice(decl.pos, decl.getStart(sourceFile)));
604
+ }
605
+ function promiseCarriesDyn(lowerer, type) {
606
+ if (type.kind === "promise")
607
+ return type.inner.kind === "dyn";
608
+ return type.kind === "union" &&
609
+ (lowerer.unions.get(type.unionId)?.arms.some((arm) => promiseCarriesDyn(lowerer, arm)) ?? false);
610
+ }
588
611
  export function declaredReturnType(lowerer, decl, blame) {
589
612
  const sig = lowerer.checker.getSignatureFromDeclaration(decl);
590
613
  if (!sig)
@@ -596,6 +619,17 @@ export function declaredReturnType(lowerer, decl, blame) {
596
619
  // (`.action(() => { throw ... })`). `never` VALUES stay unmapped.
597
620
  if (retTsType.flags & ts.TypeFlags.Never)
598
621
  return VOID;
622
+ const mappedReturn = lowerer.mapTypeOf(retTsType);
623
+ if (isJsSourceFile(decl.getSourceFile()) &&
624
+ !hasExplicitJsDocReturn(decl) &&
625
+ mappedReturn !== null &&
626
+ promiseCarriesDyn(lowerer, mappedReturn) &&
627
+ decl.parameters.some((param) => {
628
+ const mapped = lowerer.mapTypeOf(lowerer.typeOf(param.name));
629
+ return mapped === null || mapped.kind === "dyn";
630
+ })) {
631
+ return DYN;
632
+ }
599
633
  // A JS function whose UNANNOTATED return infers a FUNCTION type
600
634
  // (test/common's mustCall — tsc infers `() => any` from the wrapper
601
635
  // it returns): the inferred arity is the wrapper's spelling, not a
@@ -691,9 +725,6 @@ export function collectSignatureInner(lowerer, decl) {
691
725
  return;
692
726
  const isAsync = decl.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) === true;
693
727
  const isGenerator = decl.asteriskToken !== undefined;
694
- if (isGenerator && isAsync) {
695
- lowerer.unsupported("SC1071", decl, "async generators (async function*)");
696
- }
697
728
  if (decl.typeParameters) {
698
729
  // Generic async composes: each monomorphized instance is an async
699
730
  // IrFunction like any other — its own spawn wrapper, its body
@@ -742,10 +773,11 @@ export function collectSignatureInner(lowerer, decl) {
742
773
  }
743
774
  const nameBlame = decl.name ?? decl;
744
775
  const returnType = lowerer.declaredReturnType(decl, nameBlame);
745
- if (isAsync && returnType.kind !== "promise") {
776
+ if (isAsync && !isGenerator && returnType.kind !== "promise") {
746
777
  lowerer.badType(nameBlame, lowerer.typeOf(nameBlame));
747
778
  }
748
- if (isGenerator && returnType.kind !== "generator") {
779
+ if (isGenerator &&
780
+ (returnType.kind !== "generator" || (returnType.async === true) !== isAsync)) {
749
781
  lowerer.badType(nameBlame, lowerer.typeOf(nameBlame));
750
782
  }
751
783
  const symbol = declSymbolOf(lowerer, decl);
@@ -761,7 +793,7 @@ export function collectSignatureInner(lowerer, decl) {
761
793
  returnType,
762
794
  isAsync,
763
795
  ...(isGenerator && returnType.kind === "generator"
764
- ? { generator: { yieldT: returnType.yieldT, nextT: returnType.nextT } }
796
+ ? { generator: generatorMeta(lowerer, returnType) }
765
797
  : {}),
766
798
  });
767
799
  }
@@ -1172,17 +1204,15 @@ export function lowerGenericInstance(lowerer, info, inst) {
1172
1204
  // and awaits park this instance's fibers.
1173
1205
  const isAsync = decl.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) === true;
1174
1206
  const nameBlame = (ts.isArrowFunction(decl) ? undefined : decl.name) ?? decl;
1175
- if (isAsync && inst.returnType.kind !== "promise") {
1207
+ const isGenerator = decl.asteriskToken !== undefined;
1208
+ if (isAsync && !isGenerator && inst.returnType.kind !== "promise") {
1176
1209
  lowerer.badType(nameBlame, lowerer.checker.getTypeAtLocation(nameBlame));
1177
1210
  }
1178
1211
  // A generic GENERATOR instance mirrors async: the body returns the
1179
1212
  // resolved TReturn channel, calls enter through the instance's own
1180
1213
  // gen-spawn wrapper (the emitter routes by fn.generator).
1181
- const isGenerator = decl.asteriskToken !== undefined;
1182
- if (isGenerator && isAsync) {
1183
- lowerer.unsupported("SC1071", decl, "async generators (async function*)");
1184
- }
1185
- if (isGenerator && inst.returnType.kind !== "generator") {
1214
+ if (isGenerator &&
1215
+ (inst.returnType.kind !== "generator" || (inst.returnType.async === true) !== isAsync)) {
1186
1216
  lowerer.badType(nameBlame, lowerer.checker.getTypeAtLocation(nameBlame));
1187
1217
  }
1188
1218
  let bodyReturn = isGenerator
@@ -1198,7 +1228,7 @@ export function lowerGenericInstance(lowerer, info, inst) {
1198
1228
  if (cls && info.member.kind === "method")
1199
1229
  lowerer.currentClass = cls;
1200
1230
  if (isGenerator && inst.returnType.kind === "generator") {
1201
- fnCtx.generator = { yieldT: inst.returnType.yieldT, nextT: inst.returnType.nextT };
1231
+ fnCtx.generator = generatorMeta(lowerer, inst.returnType);
1202
1232
  }
1203
1233
  lowerer.fnStack.push(fnCtx);
1204
1234
  try {
@@ -1437,6 +1467,9 @@ export function lowerGenericFnValue(lowerer, ref, info) {
1437
1467
  fenceUnpinned();
1438
1468
  const bindings = new Map();
1439
1469
  unifySignatureBindings(lowerer, info, target, bindings);
1470
+ const hofBinding = runtimeOptionalHofGenericBinding(lowerer, ref, info);
1471
+ if (hofBinding)
1472
+ bindings.set(hofBinding.typeParam, hofBinding.type);
1440
1473
  bindDefaultTypeParams(lowerer, info.typeParams, info.decl.typeParameters, bindings);
1441
1474
  // A pinning signature that itself keeps type parameters (`let g: <T>(x:
1442
1475
  // T) => T = identity` — storing the generic signature as such) binds
@@ -1457,6 +1490,31 @@ export function lowerGenericFnValue(lowerer, ref, info) {
1457
1490
  lowerer.noteEdge(inst.name);
1458
1491
  return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
1459
1492
  }
1493
+ function runtimeOptionalHofGenericBinding(lowerer, ref, info) {
1494
+ let argument = ref;
1495
+ while (argument.parent && ts.isParenthesizedExpression(argument.parent))
1496
+ argument = argument.parent;
1497
+ const call = argument.parent;
1498
+ if (!ts.isCallExpression(call) || call.arguments[0] !== argument)
1499
+ return null;
1500
+ if (!ts.isPropertyAccessExpression(call.expression) || call.expression.name.text !== "map")
1501
+ return null;
1502
+ if (!lowerer.isStdlibMember(call.expression))
1503
+ return null;
1504
+ const receiver = lowerer.mapTypeOf(lowerer.typeOf(call.expression.expression));
1505
+ if (receiver?.kind !== "array")
1506
+ return null;
1507
+ const parameter = info.decl.parameters[0];
1508
+ if (!parameter || !ts.isIdentifier(parameter.name))
1509
+ return null;
1510
+ const parameterType = lowerer.typeOf(parameter.name);
1511
+ if ((parameterType.flags & ts.TypeFlags.TypeParameter) === 0)
1512
+ return null;
1513
+ const typeParam = parameterType.getSymbol();
1514
+ if (!typeParam || !info.typeParams.includes(typeParam))
1515
+ return null;
1516
+ return { typeParam, type: lowerer.runtimeOptionalType(receiver.elem) };
1517
+ }
1460
1518
  /** The instance a pinned VALUE reference names: the declaration's modes
1461
1519
  * over the DECLARED types mapped under the bindings (mapType's resolver
1462
1520
  * substitutes — the instance-body trick), which is the same result the
@@ -1595,6 +1653,15 @@ function paramWrittenInBody(lowerer, body, sym, name) {
1595
1653
  walk(body);
1596
1654
  return written;
1597
1655
  }
1656
+ function broadPromiseParam(lowerer, type) {
1657
+ const mapped = lowerer.mapTypeOf(type);
1658
+ const arms = mapped?.kind === "union"
1659
+ ? lowerer.unions.get(mapped.unionId)?.arms
1660
+ : mapped === null ? undefined : [mapped];
1661
+ return !!arms &&
1662
+ arms.some((arm) => arm.kind === "promise" && arm.inner.kind === "dyn") &&
1663
+ arms.every((arm) => isUnitType(arm) || (arm.kind === "promise" && arm.inner.kind === "dyn"));
1664
+ }
1598
1665
  /** The implicit-type-parameter slots of a JS function-like: parallel to
1599
1666
  * decl.parameters, the param SYMBOL where the slot is a bindable
1600
1667
  * implicit-any param (identifier-named, no annotation/JSDoc type, not
@@ -1623,7 +1690,10 @@ export function implicitAnyParamSymbolsOf(lowerer, decl) {
1623
1690
  if (param.name.text === "this")
1624
1691
  return null;
1625
1692
  const t = lowerer.typeOf(param.name);
1626
- if ((t.flags & ts.TypeFlags.Any) === 0)
1693
+ const broadFunction = lowerer.checker.typeToString(t) === "Function";
1694
+ const broadArray = lowerer.checkerAnyArrayType(t);
1695
+ const broadPromise = broadPromiseParam(lowerer, t);
1696
+ if ((t.flags & ts.TypeFlags.Any) === 0 && !broadFunction && !broadArray && !broadPromise)
1627
1697
  return null;
1628
1698
  const sym = lowerer.checker.getSymbolAtLocation(param.name);
1629
1699
  if (!sym)
@@ -1642,13 +1712,54 @@ export function implicitAnyParamSymbolsOf(lowerer, decl) {
1642
1712
  * selects return INFERENCE with DYN as the recursion pin. */
1643
1713
  function implicitDeclaredReturn(lowerer, info) {
1644
1714
  try {
1715
+ const broadCallbackSymbols = new Set(info.decl.parameters.flatMap((param, index) => {
1716
+ const symbol = info.implicitParams?.[index];
1717
+ return symbol !== null && symbol !== undefined &&
1718
+ lowerer.checker.typeToString(lowerer.typeOf(param.name)) === "Function"
1719
+ ? [symbol]
1720
+ : [];
1721
+ }));
1722
+ let returnsBroadCallback = false;
1723
+ if (broadCallbackSymbols.size > 0 && info.decl.body !== undefined) {
1724
+ ts.walkPreorder(info.decl.body, (node) => {
1725
+ if (node !== info.decl.body && ts.isFunctionLike(node))
1726
+ return "skip";
1727
+ if (!ts.isReturnStatement(node) || node.expression === undefined)
1728
+ return undefined;
1729
+ let expression = node.expression;
1730
+ while (ts.isParenthesizedExpression(expression))
1731
+ expression = expression.expression;
1732
+ if (ts.isCallExpression(expression) &&
1733
+ ts.isIdentifier(expression.expression) &&
1734
+ broadCallbackSymbols.has(lowerer.checker.getSymbolAtLocation(expression.expression) ?? {})) {
1735
+ returnsBroadCallback = true;
1736
+ return "stop";
1737
+ }
1738
+ return undefined;
1739
+ });
1740
+ }
1741
+ if (returnsBroadCallback)
1742
+ return null;
1645
1743
  const declSig = lowerer.checker.getSignatureFromDeclaration(info.decl);
1646
1744
  if (!declSig)
1647
1745
  return null;
1648
1746
  const retTs = lowerer.checker.getReturnTypeOfSignature(declSig);
1649
1747
  if (retTs.flags & ts.TypeFlags.Any)
1650
1748
  return null;
1651
- return lowerer.mapTypeOf(retTs);
1749
+ if (lowerer.checker.isArrayType(retTs)) {
1750
+ const elem = lowerer.checker.getTypeArguments(retTs)[0];
1751
+ // An implicit-any parameter poisons an inferred array result to
1752
+ // any[] even when this instance's body produces one concrete
1753
+ // element type (`knownBy(cmd)` in commander). Let the existing
1754
+ // lowered-return unifier recover that type. Explicit unknown[]
1755
+ // remains a declared checked-dynamic contract and stays pinned.
1756
+ if (elem && (elem.flags & ts.TypeFlags.Any) !== 0)
1757
+ return null;
1758
+ }
1759
+ const mapped = lowerer.mapTypeOf(retTs);
1760
+ if (mapped !== null && promiseCarriesDyn(lowerer, mapped) && !hasExplicitJsDocReturn(info.decl))
1761
+ return null;
1762
+ return mapped;
1652
1763
  }
1653
1764
  catch (e) {
1654
1765
  if (!(e instanceof PoisonError))
@@ -1656,6 +1767,19 @@ function implicitDeclaredReturn(lowerer, info) {
1656
1767
  return null;
1657
1768
  }
1658
1769
  }
1770
+ /** True when a `this.method(...)` call targets an implicit npm-static
1771
+ * method whose result is settled from its lowered body rather than the
1772
+ * checker declaration. Uninitialized-local inference uses this to avoid
1773
+ * freezing an evolving `any` slot to stale JSDoc. */
1774
+ export function implicitMethodCallInfersReturn(lowerer, call) {
1775
+ if (!ts.isPropertyAccessExpression(call.expression) ||
1776
+ call.expression.expression.kind !== ts.SyntaxKind.ThisKeyword ||
1777
+ lowerer.currentClass === null) {
1778
+ return false;
1779
+ }
1780
+ const found = findGenericMethodOn(lowerer, lowerer.currentClass, call.expression.name.text);
1781
+ return found?.info.implicitParams !== undefined && implicitDeclaredReturn(lowerer, found.info) === null;
1782
+ }
1659
1783
  /** True when an IR type may BIND an implicit param (a concrete static
1660
1784
  * type — the checked-dynamic kinds keep the dyn slot, units have no
1661
1785
  * standalone representation). */
@@ -1681,15 +1805,37 @@ export function implicitCallInstance(lowerer, call, info) {
1681
1805
  return;
1682
1806
  }
1683
1807
  let bound = DYN;
1808
+ const declared = lowerer.paramShape(param);
1809
+ if ((lowerer.typeOf(param.name).flags & ts.TypeFlags.Any) === 0)
1810
+ bound = declared.type;
1684
1811
  const arg = call.arguments[i];
1685
1812
  if (arg && !ts.isSpreadElement(arg)) {
1813
+ const runtimeLocal = ts.isIdentifier(arg) ? lowerer.peekLocal(arg) : null;
1814
+ if (runtimeLocal?.type.kind === "dyn") {
1815
+ bound = DYN;
1816
+ argTypes.set(sym, lowerer.checker.getUnknownType());
1817
+ shapes.push({ type: bound, mode: "required" });
1818
+ return;
1819
+ }
1686
1820
  // The argument's own checker type, literal-widened ('add' binds
1687
1821
  // string) — typeOf consults the ACTIVE instance's bindings, so a
1688
1822
  // bound param forwarded into another implicit call transitively
1689
1823
  // instantiates it (this._initCommandGroup(command)).
1690
1824
  const t = lowerer.checker.getBaseTypeOfLiteralType(lowerer.typeOf(arg));
1691
- const mapped = lowerer.mapTypeOf(t);
1692
- if (bindableImplicitIr(mapped)) {
1825
+ const mapped = lowerer.mapTypeOf(t) ?? dynFallbackType(lowerer, arg, t);
1826
+ if (ts.isArrowFunction(arg) &&
1827
+ arg.type === undefined &&
1828
+ mapped?.kind === "func" &&
1829
+ promiseCarriesDyn(lowerer, mapped.ret)) {
1830
+ bound = { ...mapped, ret: DYN };
1831
+ lowerer.runtimeOptionalFunctionReturns.set(arg, DYN);
1832
+ argTypes.set(sym, t);
1833
+ }
1834
+ else if (mapped?.kind === "dyn") {
1835
+ bound = DYN;
1836
+ argTypes.set(sym, t);
1837
+ }
1838
+ else if (bindableImplicitIr(mapped)) {
1693
1839
  bound = mapped;
1694
1840
  argTypes.set(sym, t);
1695
1841
  }
@@ -1715,7 +1861,11 @@ function internImplicitInstance(lowerer, blame, info, shapes, argTypes) {
1715
1861
  `${MAX_GENERIC_INSTANCES} instances — polymorphic recursion?)`);
1716
1862
  }
1717
1863
  const rendered = shapes.map((s) => lowerer.fmt(s.type)).join(", ");
1718
- const declared = implicitDeclaredReturn(lowerer, info);
1864
+ const dynamicBroadPromise = info.decl.parameters.some((param, index) => info.implicitParams?.[index] !== null &&
1865
+ info.implicitParams?.[index] !== undefined &&
1866
+ shapes[index]?.type.kind === "dyn" &&
1867
+ broadPromiseParam(lowerer, lowerer.typeOf(param.name)));
1868
+ const declared = dynamicBroadPromise ? null : implicitDeclaredReturn(lowerer, info);
1719
1869
  inst = {
1720
1870
  name: `${info.qualifiedName}%${info.instances.size}`,
1721
1871
  ordinal: info.instances.size,
@@ -2684,6 +2834,11 @@ export function lowerCall(lowerer, expr) {
2684
2834
  if (crServed)
2685
2835
  return crServed;
2686
2836
  }
2837
+ {
2838
+ const resolved = lowerImportMetaResolveCall(lowerer, expr) ?? lowerRequireResolveCall(lowerer, expr);
2839
+ if (resolved)
2840
+ return resolved;
2841
+ }
2687
2842
  // `process.getuid?.()` — intercepted BEFORE the optional-chain
2688
2843
  // machinery (the member always exists on a POSIX target, so the
2689
2844
  // optional call IS the call; `process.getuid` itself has no value
@@ -3044,6 +3199,9 @@ export function lowerCall(lowerer, expr) {
3044
3199
  if (arg.type.kind === "string") {
3045
3200
  return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
3046
3201
  }
3202
+ const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
3203
+ if (optionalNumber)
3204
+ return optionalNumber;
3047
3205
  lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
3048
3206
  ? "numbers, booleans, and strings lower (the full ToNumber string grammar included) — narrow the union first"
3049
3207
  : undefined);
@@ -3110,7 +3268,7 @@ export function lowerCall(lowerer, expr) {
3110
3268
  !expr.expression.questionDotToken &&
3111
3269
  !expr.questionDotToken &&
3112
3270
  !expr.arguments.some((a) => ts.isSpreadElement(a))) {
3113
- const recvProbe = probeLower(lowerer, expr.expression.expression);
3271
+ const recvProbe = tryLowerExpression(lowerer, expr.expression.expression);
3114
3272
  if (recvProbe?.type.kind === "dyn") {
3115
3273
  const args = expr.arguments.map((a) => lowerer.lowerExprExpecting(a, DYN));
3116
3274
  return {
@@ -3256,6 +3414,9 @@ export function lowerCall(lowerer, expr) {
3256
3414
  const cryptoServed = lowerer.lowerCryptoModuleCall(expr, bi, loc);
3257
3415
  if (cryptoServed)
3258
3416
  return cryptoServed;
3417
+ const timersInterval = lowerer.lowerTimersPromisesSetInterval(expr, bi, loc);
3418
+ if (timersInterval)
3419
+ return timersInterval;
3259
3420
  const builtinFn = builtinModuleFnOf(lowerer, bi.module, bi.member);
3260
3421
  if (!builtinFn) {
3261
3422
  // Typed by @types/node (the fallback declarations only declare
@@ -3335,7 +3496,7 @@ export function lowerCall(lowerer, expr) {
3335
3496
  const radix = expr.arguments[1]
3336
3497
  ? lowerer.lowerExprExpecting(expr.arguments[1], F64)
3337
3498
  : { kind: "numLit", value: 0, type: F64, loc };
3338
- const optional = lowerAbsenceProbe(lowerer, expr.arguments[0]);
3499
+ const optional = optionalCallValue(lowerer, expr.arguments[0]);
3339
3500
  if (optional?.type.kind === "union") {
3340
3501
  const def = lowerer.unions.get(optional.type.unionId);
3341
3502
  const stringTag = def?.arms.findIndex((arm) => arm.kind === "string") ?? -1;
@@ -3403,7 +3564,15 @@ export function lowerCall(lowerer, expr) {
3403
3564
  expr.arguments.length === 1 &&
3404
3565
  lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(expr.expression) ?? undefined)) {
3405
3566
  const name = expr.expression.text;
3406
- const probed = probeLower(lowerer, expr.arguments[0]);
3567
+ if (name === "parseFloat") {
3568
+ const optional = optionalCallValue(lowerer, expr.arguments[0]);
3569
+ if (optional) {
3570
+ const parsed = lowerOptionalStringNumber(lowerer, optional, loc, "num.parseFloat", "parseFloat");
3571
+ if (parsed)
3572
+ return parsed;
3573
+ }
3574
+ }
3575
+ const probed = tryLowerExpression(lowerer, expr.arguments[0]);
3407
3576
  if (name === "parseFloat" && probed?.type.kind === "string") {
3408
3577
  return { kind: "libCall", fn: "num.parseFloat", args: [probed], type: F64, loc };
3409
3578
  }
@@ -3433,13 +3602,12 @@ export function lowerCall(lowerer, expr) {
3433
3602
  const loc = locOf(expr);
3434
3603
  const argNode = expr.arguments[0];
3435
3604
  if (name === "decodeURIComponent") {
3436
- const d = lowerer.lowerExpr(argNode);
3437
- if (d.type.kind !== "string") {
3438
- lowerer.noLowering(`${name} with a '${lowerer.fmt(d.type)}' argument`, argNode, "only string arguments lower (Node would ToString the value — convert it explicitly)");
3439
- }
3440
- return { kind: "libCall", fn: "str.decodeUriComponent", args: [d], type: STRING, loc };
3605
+ const d = optionalCallValue(lowerer, argNode) ?? lowerer.lowerExpr(argNode);
3606
+ const s = lowerer.ensureString(d, argNode);
3607
+ return { kind: "libCall", fn: "str.decodeUriComponent", args: [s], type: STRING, loc };
3441
3608
  }
3442
- const s = lowerer.ensureString(lowerer.lowerExpr(argNode), argNode);
3609
+ const value = optionalCallValue(lowerer, argNode) ?? lowerer.lowerExpr(argNode);
3610
+ const s = lowerer.ensureString(value, argNode);
3443
3611
  return {
3444
3612
  kind: "libCall",
3445
3613
  fn: name === "encodeURIComponent" ? "str.encodeUriComponent" : "str.encodeUri",
@@ -3718,12 +3886,12 @@ export function lowerCall(lowerer, expr) {
3718
3886
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
3719
3887
  }
3720
3888
  if (ts.isPropertyAccessExpression(expr.expression)) {
3721
- const intrinsic =
3722
- // Builtin namespace imports first (`fs.readFileSync(...)` where fs
3723
- // is `import * as fs from "node:fs"`): the same tables and fences
3724
- // as named builtin imports — before anything below tries to lower
3725
- // the namespace object itself as a receiver.
3726
- lowerer.lowerNamespaceBuiltinCall(expr, expr.expression) ??
3889
+ const intrinsic = fenceNodeModuleMutationCall(lowerer, expr, expr.expression) ??
3890
+ // Builtin namespace imports first (`fs.readFileSync(...)` where fs
3891
+ // is `import * as fs from "node:fs"`): the same tables and fences
3892
+ // as named builtin imports — before anything below tries to lower
3893
+ // the namespace object itself as a receiver.
3894
+ lowerer.lowerNamespaceBuiltinCall(expr, expr.expression) ??
3727
3895
  // The node:perf_hooks spoke: performance.now() and its
3728
3896
  // .bind(performance) function value over the runtime's
3729
3897
  // process-start-anchored monotonic clock.
@@ -3750,7 +3918,7 @@ export function lowerCall(lowerer, expr) {
3750
3918
  lowerPromiseRejectCall(lowerer, expr, expr.expression) ??
3751
3919
  // Before the island path: regex-argument replace/replaceAll/split
3752
3920
  // lower STATICALLY; only the string-pattern overloads are island.
3753
- lowerer.lowerRegexMethodCall(expr, expr.expression) ??
3921
+ lowerRegexMethodCallWithOptionalArg(lowerer, expr, expr.expression) ??
3754
3922
  lowerer.lowerUrlMethodCall(expr, expr.expression) ??
3755
3923
  lowerer.lowerSearchParamsMethodCall(expr, expr.expression) ??
3756
3924
  lowerer.lowerStatsMethodCall(expr, expr.expression) ??
@@ -3789,11 +3957,11 @@ export function lowerCall(lowerer, expr) {
3789
3957
  // TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
3790
3958
  lowerer.lowerTestMethodCall(expr, expr.expression) ??
3791
3959
  lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
3792
- lowerer.lowerStringMethodCall(expr, expr.expression) ??
3960
+ lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
3793
3961
  // Typed-array/Buffer receivers and the Buffer statics — before the
3794
3962
  // island path (bytes never cross the boundary).
3795
3963
  lowerer.lowerBytesMethodCall(expr, expr.expression) ??
3796
- lowerer.lowerBufferStaticCall(expr, expr.expression) ??
3964
+ lowerBufferStaticCallWithOptionalArg(lowerer, expr, expr.expression) ??
3797
3965
  // URL.revokeObjectURL's zero-argument contract (the one-argument
3798
3966
  // form keeps the fence — createObjectURL does too).
3799
3967
  lowerUrlStaticCall(lowerer, expr, expr.expression) ??
@@ -4057,6 +4225,160 @@ export function lowerCall(lowerer, expr) {
4057
4225
  }
4058
4226
  return { kind: "callValue", callee, args, type: callee.type.ret, loc };
4059
4227
  }
4228
+ function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
4229
+ const lowered = lowerer.lowerRegexMethodCall(call, access);
4230
+ if (lowered?.kind !== "regexIntrinsic" || lowered.method !== "test" || call.arguments.length !== 1)
4231
+ return lowered;
4232
+ const subject = lowered.args[0];
4233
+ if (subject?.type.kind !== "union" || lowerer.armTag(subject.type.unionId, UNDEFINED_T) < 0)
4234
+ return lowered;
4235
+ if (lowerer.mapTypeOf(lowerer.typeOf(call.arguments[0]))?.kind !== "string")
4236
+ return lowered;
4237
+ const present = lowerer.stripUndefinedArm(subject.type);
4238
+ if (present.kind !== "string")
4239
+ return lowered;
4240
+ return {
4241
+ ...lowered,
4242
+ args: [lowerer.ensureString(subject, call.arguments[0])],
4243
+ };
4244
+ }
4245
+ function optionalCallValue(lowerer, node) {
4246
+ return lowerer.runtimeOptionalIdentifierValue(node)?.value ?? lowerAbsenceProbe(lowerer, node);
4247
+ }
4248
+ function lowerBufferStaticCallWithOptionalArg(lowerer, call, access) {
4249
+ const lowered = lowerer.lowerBufferStaticCall(call, access);
4250
+ if (lowered?.kind !== "bytesNew" || !lowered.source || lowered.source.type.kind !== "union")
4251
+ return lowered;
4252
+ const present = lowerer.stripUndefinedArm(lowered.source.type);
4253
+ const validSource = (present.kind === "array" && present.elem.kind === "f64") ||
4254
+ (present.kind === "bytes" && present.elem === "u8");
4255
+ if (!validSource)
4256
+ return lowered;
4257
+ const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, present, lowered.source.loc);
4258
+ return helper
4259
+ ? {
4260
+ ...lowered,
4261
+ source: { kind: "call", callee: helper, args: [lowered.source], type: present, loc: lowered.source.loc },
4262
+ }
4263
+ : lowered;
4264
+ }
4265
+ function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
4266
+ const widened = lowerer.runtimeOptionalWidening(value.type, F64);
4267
+ if (!widened || widened.kind !== "union")
4268
+ return null;
4269
+ const numberTag = lowerer.armTag(widened.unionId, F64);
4270
+ const undefinedTag = lowerer.armTag(widened.unionId, UNDEFINED_T);
4271
+ if (numberTag < 0 || undefinedTag < 0)
4272
+ return null;
4273
+ const key = `number.optionalDefault:${widened.unionId}:${dflt}`;
4274
+ let helper = lowerer.widthHelpers.get(key);
4275
+ if (!helper) {
4276
+ helper = `%number.optionalDefault.${lowerer.widthHelpers.size}`;
4277
+ lowerer.widthHelpers.set(key, helper);
4278
+ const input = varRef("value.0", widened, loc);
4279
+ lowerer.liftedFns.push({
4280
+ name: helper,
4281
+ params: [{ localId: "value.0", name: "value", type: widened }],
4282
+ returnType: F64,
4283
+ locals: [{ id: "value.0", name: "value", type: widened, mutable: false }],
4284
+ body: [
4285
+ {
4286
+ kind: "if",
4287
+ cond: { kind: "unionIsTag", unionId: widened.unionId, tag: undefinedTag, negated: false, value: input, type: BOOL, loc },
4288
+ then: [{ kind: "return", value: { kind: "numLit", value: dflt, type: F64, loc }, loc }],
4289
+ else_: null,
4290
+ loc,
4291
+ },
4292
+ {
4293
+ kind: "return",
4294
+ value: { kind: "unionNarrow", unionId: widened.unionId, tag: numberTag, value: input, type: F64, loc },
4295
+ loc,
4296
+ },
4297
+ ],
4298
+ loc,
4299
+ });
4300
+ }
4301
+ return { kind: "call", callee: helper, args: [value], type: F64, loc };
4302
+ }
4303
+ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
4304
+ const lowered = lowerer.lowerStringMethodCall(call, access);
4305
+ if (lowered?.kind !== "strIntrinsic")
4306
+ return lowered;
4307
+ const args = [...lowered.args];
4308
+ let changed = false;
4309
+ const zeroDefaultArgs = lowered.method === "charCodeAt" || lowered.method === "charAt" ||
4310
+ lowered.method === "repeat" || lowered.method === "padStart" || lowered.method === "padEnd"
4311
+ ? [0]
4312
+ : lowered.method === "slice" || lowered.method === "substring"
4313
+ ? [0]
4314
+ : lowered.method === "indexOf" || lowered.method === "includes"
4315
+ ? [1]
4316
+ : [];
4317
+ for (const index of zeroDefaultArgs) {
4318
+ const value = args[index];
4319
+ if (!value)
4320
+ continue;
4321
+ const coerced = lowerOptionalNumberDefault(lowerer, value, 0, value.loc);
4322
+ if (coerced) {
4323
+ args[index] = coerced;
4324
+ changed = true;
4325
+ }
4326
+ }
4327
+ if ((lowered.method === "indexOf" || lowered.method === "includes" ||
4328
+ lowered.method === "startsWith" || lowered.method === "endsWith") &&
4329
+ args[0]?.type.kind === "union" &&
4330
+ lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null) {
4331
+ args[0] = lowerer.ensureString(args[0], call.arguments[0]);
4332
+ changed = true;
4333
+ }
4334
+ return changed ? { ...lowered, args } : lowered;
4335
+ }
4336
+ function lowerOptionalStringNumber(lowerer, arg, loc, fn = "num.fromString", label = "number") {
4337
+ if (arg.type.kind !== "union")
4338
+ return null;
4339
+ const def = lowerer.unions.get(arg.type.unionId);
4340
+ if (!def || def.arms.length !== 2)
4341
+ return null;
4342
+ const stringTag = lowerer.armTag(arg.type.unionId, STRING);
4343
+ const undefinedTag = lowerer.armTag(arg.type.unionId, UNDEFINED_T);
4344
+ if (stringTag < 0 || undefinedTag < 0)
4345
+ return null;
4346
+ const key = `${label}.optionalString:${arg.type.unionId}`;
4347
+ let helper = lowerer.widthHelpers.get(key);
4348
+ if (!helper) {
4349
+ helper = `%${label}.optionalString.${lowerer.widthHelpers.size}`;
4350
+ lowerer.widthHelpers.set(key, helper);
4351
+ const value = varRef("value.0", arg.type, loc);
4352
+ lowerer.liftedFns.push({
4353
+ name: helper,
4354
+ params: [{ localId: "value.0", name: "value", type: arg.type }],
4355
+ returnType: F64,
4356
+ locals: [{ id: "value.0", name: "value", type: arg.type, mutable: false }],
4357
+ body: [
4358
+ {
4359
+ kind: "if",
4360
+ cond: { kind: "unionIsTag", unionId: arg.type.unionId, tag: undefinedTag, negated: false, value, type: BOOL, loc },
4361
+ then: [{ kind: "return", value: { kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc }, right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc }, loc }],
4362
+ else_: null,
4363
+ loc,
4364
+ },
4365
+ {
4366
+ kind: "return",
4367
+ value: {
4368
+ kind: "libCall",
4369
+ fn,
4370
+ args: [{ kind: "unionNarrow", unionId: arg.type.unionId, tag: stringTag, value, type: STRING, loc }],
4371
+ type: F64,
4372
+ loc,
4373
+ },
4374
+ loc,
4375
+ },
4376
+ ],
4377
+ loc,
4378
+ });
4379
+ }
4380
+ return { kind: "call", callee: helper, args: [arg], type: F64, loc };
4381
+ }
4060
4382
  /** The RUNTIME-ARITY spread call — `f(...args)`, the rest-forwarding idiom
4061
4383
  * (`const f = (...args) => from(...args)`): a spread whose length is a
4062
4384
  * runtime fact has no home in the compile-time completion, so the call
@@ -4187,7 +4509,7 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
4187
4509
  // evolving `let h = {}` object flowing back out of a JS helper —
4188
4510
  // tsc types the return by the evolved shape, the binding lowered
4189
4511
  // dyn): probe the lowering and claim exactly the dyn results.
4190
- const probed = probeLower(lowerer, access.expression);
4512
+ const probed = tryLowerExpression(lowerer, access.expression);
4191
4513
  if (probed?.type.kind !== "dyn")
4192
4514
  return null;
4193
4515
  recv = probed;
@@ -4380,6 +4702,71 @@ export function lowerDynDispatchMethodCall(lowerer, call, access, recv, arrayRec
4380
4702
  if (arrayReceiver && predicate?.type.kind === "func" && predicate.type.ret.kind === "void") {
4381
4703
  lowerer.unsupported("SC1090", call.arguments[0], "'.filter()' with a void-returning predicate (the callback return value is erased before its truthiness can be tested)");
4382
4704
  }
4705
+ // A dynamic argument array consumed immediately by the fixed-arity arrow
4706
+ // apply lowering below never exposes its surplus tail. Keep a pushed
4707
+ // static-only value's evaluation and the array length mutation, but store
4708
+ // undefined in that unobserved slot so no impossible dyn conversion is
4709
+ // invented. If the slot falls inside the typed prefix, its dynCheck fails
4710
+ // at the callback boundary; valid callers preserve the original prefix.
4711
+ if (method === "push" &&
4712
+ call.arguments.length === 1 &&
4713
+ ts.isIdentifier(access.expression) &&
4714
+ ts.isExpressionStatement(call.parent) &&
4715
+ ts.isBlock(call.parent.parent)) {
4716
+ const receiverSymbol = lowerer.checker.getSymbolAtLocation(access.expression);
4717
+ const block = call.parent.parent;
4718
+ const statementIndex = block.statements.indexOf(call.parent);
4719
+ const next = statementIndex >= 0 ? block.statements[statementIndex + 1] : undefined;
4720
+ let returned = next && ts.isReturnStatement(next) ? next.expression : undefined;
4721
+ while (returned && ts.isParenthesizedExpression(returned))
4722
+ returned = returned.expression;
4723
+ const apply = returned && ts.isCallExpression(returned) &&
4724
+ ts.isPropertyAccessExpression(returned.expression) &&
4725
+ returned.expression.name.text === "apply"
4726
+ ? returned
4727
+ : null;
4728
+ const argsNode = apply?.arguments[1];
4729
+ const sameArgs = receiverSymbol !== undefined &&
4730
+ argsNode !== undefined &&
4731
+ ts.isIdentifier(argsNode) &&
4732
+ lowerer.checker.getSymbolAtLocation(argsNode) === receiverSymbol;
4733
+ const applyAccess = apply && ts.isPropertyAccessExpression(apply.expression)
4734
+ ? apply.expression
4735
+ : null;
4736
+ const applyTarget = applyAccess?.expression;
4737
+ const arrow = applyTarget ? arrowSignatureOf(lowerer, applyTarget) : null;
4738
+ const target = applyTarget && ts.isIdentifier(applyTarget)
4739
+ ? lowerer.peekLocal(applyTarget)
4740
+ : null;
4741
+ const targetParams = target?.type.kind === "func" && target.type.rest !== true
4742
+ ? target.type.params
4743
+ : null;
4744
+ if (sameArgs && arrow !== null && targetParams !== null &&
4745
+ targetParams.every((param) => canDynCheckTo(param, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))) {
4746
+ const value = lowerer.lowerExpr(call.arguments[0]);
4747
+ if (value.type.kind !== "dyn" && !lowerer.dynConvertible(value.type)) {
4748
+ const saved = lowerer.declareHiddenLocal("%applyTail", DYN);
4749
+ return {
4750
+ kind: "seqExpr",
4751
+ stmts: [
4752
+ { kind: "varDecl", localId: saved.id, init: recv, loc: locOf(access.expression) },
4753
+ { kind: "exprStmt", expr: value, loc: value.loc },
4754
+ ],
4755
+ result: {
4756
+ kind: "dynInvoke",
4757
+ recv: { kind: "varRef", localId: saved.id, type: DYN, loc: locOf(access.expression) },
4758
+ method,
4759
+ calleeName: access.getText(),
4760
+ args: [dynUndefinedExpr(locOf(call.arguments[0]))],
4761
+ type: DYN,
4762
+ loc: locOf(call),
4763
+ },
4764
+ type: DYN,
4765
+ loc: locOf(call),
4766
+ };
4767
+ }
4768
+ }
4769
+ }
4383
4770
  const args = call.arguments.map((arg, i) => i === 0 && predicate ? lowerer.coerceInto(arg, predicate, DYN) : lowerer.lowerExprExpecting(arg, DYN));
4384
4771
  return {
4385
4772
  kind: "dynInvoke",
@@ -4732,7 +5119,7 @@ function lowerPrimitiveProtoCall(lowerer, call, recv, name, memberSym) {
4732
5119
  // optional number selects 0 at runtime through the same narrowed
4733
5120
  // nullish IR used by `digits ?? 0`.
4734
5121
  const zero = { kind: "numLit", value: 0, type: F64, loc: digits.loc };
4735
- const defaultUnitDigits = (value) => droppableStatic(value)
5122
+ const defaultUnitDigits = (value) => isSafeToDiscard(value)
4736
5123
  ? zero
4737
5124
  : {
4738
5125
  kind: "seqExpr",
@@ -4818,6 +5205,46 @@ function lowerPrimitiveProtoCall(lowerer, call, recv, name, memberSym) {
4818
5205
  return null;
4819
5206
  return { kind: "boolLit", value: false, type: BOOL, loc };
4820
5207
  }
5208
+ /** The uniquely arity-selected overload on an implicit-any receiver whose
5209
+ * active monomorphization supplies a concrete type. The checker resolved
5210
+ * the original call through `any`, so getResolvedSignature cannot name
5211
+ * the declaration-authored overload; typeOf carries the instance's real
5212
+ * receiver type. Restricting the fallback to exactly one bodyless
5213
+ * signature at this arity avoids recreating TypeScript's overload
5214
+ * resolution for same-arity type distinctions. */
5215
+ function implicitReceiverOverloadReturn(lowerer, expr) {
5216
+ if (!ts.isCallExpression(expr) ||
5217
+ !ts.isPropertyAccessExpression(expr.expression) ||
5218
+ expr.arguments.some(ts.isSpreadElement)) {
5219
+ return null;
5220
+ }
5221
+ const access = expr.expression;
5222
+ const original = lowerer.checker.getTypeAtLocation(access.expression);
5223
+ if ((original.flags & ts.TypeFlags.Any) === 0)
5224
+ return null;
5225
+ const effective = lowerer.typeOf(access.expression);
5226
+ if ((effective.flags & ts.TypeFlags.Any) !== 0)
5227
+ return null;
5228
+ const member = lowerer.checker.getPropertyOfType(effective, access.name.text);
5229
+ if (!member)
5230
+ return null;
5231
+ const signatures = lowerer.checker.getCallSignatures(lowerer.checker.getTypeOfSymbol(member));
5232
+ const matches = signatures.filter((signature) => {
5233
+ const decl = lowerer.checker.signatureDeclaration(signature);
5234
+ if (!decl ||
5235
+ !(ts.isFunctionDeclaration(decl) || ts.isMethodDeclaration(decl)) ||
5236
+ decl.body) {
5237
+ return false;
5238
+ }
5239
+ const params = decl.parameters.filter((param) => !isThisParameter(param));
5240
+ const required = params.filter((param) => !param.questionToken && !param.initializer && !param.dotDotDotToken).length;
5241
+ const maximum = params.some((param) => param.dotDotDotToken) ? Infinity : params.length;
5242
+ return expr.arguments.length >= required && expr.arguments.length <= maximum;
5243
+ });
5244
+ if (matches.length !== 1)
5245
+ return null;
5246
+ return lowerer.mapTypeOf(lowerer.checker.getReturnTypeOfSignature(matches[0]));
5247
+ }
4821
5248
  /** Reconciles a direct call's recorded type with the checker's answer at
4822
5249
  * the site when the callee is OVERLOADED: tsc resolved the call against
4823
5250
  * one overload SIGNATURE, so every downstream lowering sees that
@@ -4837,10 +5264,12 @@ function lowerPrimitiveProtoCall(lowerer, call, recv, name, memberSym) {
4837
5264
  function reconcileOverloadReturn(lowerer, expr, call) {
4838
5265
  const rsig = lowerer.checker.getResolvedSignature(expr);
4839
5266
  const rdecl = rsig ? lowerer.checker.signatureDeclaration(rsig) : undefined;
4840
- if (!rsig || !rdecl || !(ts.isFunctionDeclaration(rdecl) || ts.isMethodDeclaration(rdecl)) || rdecl.body) {
4841
- return call;
4842
- }
4843
- const rt = lowerer.mapTypeOf(lowerer.checker.getReturnTypeOfSignature(rsig));
5267
+ const resolvedOverload = rsig && rdecl &&
5268
+ (ts.isFunctionDeclaration(rdecl) || ts.isMethodDeclaration(rdecl)) &&
5269
+ !rdecl.body
5270
+ ? lowerer.mapTypeOf(lowerer.checker.getReturnTypeOfSignature(rsig))
5271
+ : null;
5272
+ const rt = resolvedOverload ?? implicitReceiverOverloadReturn(lowerer, expr);
4844
5273
  // Unmappable, void, or unit resolved returns keep the implementation's
4845
5274
  // type: a discarded result never looks, a USED one meets its use
4846
5275
  // site's own mapping (and that site's honest fences). Unit narrowing
@@ -4920,10 +5349,9 @@ function templateEscapesValid(raw) {
4920
5349
  return true;
4921
5350
  }
4922
5351
  /** Tagged templates `tag\`a${x}b\`` — ES's call: tag(strings, ...values).
4923
- * The strings object is the per-SITE interned cooked array (the
4924
- * templateStrings node: one immortal string[] per occurrence, so the
4925
- * spec's identity contract holds — the same site evaluated twice hands
4926
- * the tag the SAME array; two sites never share). TemplateStringsArray
5352
+ * The strings object is a per-SITE lazily initialized hidden array global,
5353
+ * so the spec's identity contract holds — the same site evaluated twice
5354
+ * hands the tag the SAME array; two sites never share. TemplateStringsArray
4927
5355
  * maps to string[] (type-mapper.ts), so the array rides the ordinary
4928
5356
  * slot-directed coercion into whatever the tag's first parameter wants
4929
5357
  * — string[] exactly, an `any` slot through the dyn boundary, a rest
@@ -4949,13 +5377,7 @@ export function lowerTaggedTemplate(lowerer, expr) {
4949
5377
  lowerer.unsupported("SC1090", p, "tagged templates with invalid escape sequences (the span cooks to undefined, which the strings array cannot carry)");
4950
5378
  }
4951
5379
  }
4952
- const strings = {
4953
- kind: "templateStrings",
4954
- key: `${loc.file}:${expr.template.getStart()}`,
4955
- cooked: pieces.map((p) => p.text),
4956
- type: arrayOf(STRING),
4957
- loc,
4958
- };
5380
+ const strings = () => loweredTemplateStrings(lowerer, expr, pieces.map((piece) => piece.text), loc);
4959
5381
  const values = ts.isNoSubstitutionTemplateLiteral(expr.template)
4960
5382
  ? []
4961
5383
  : expr.template.templateSpans.map((s) => s.expression);
@@ -5007,7 +5429,7 @@ export function lowerTaggedTemplate(lowerer, expr) {
5007
5429
  const sig = lowerer.fnSigOf(expr.tag);
5008
5430
  if (sig) {
5009
5431
  lowerer.noteEdge(sig.name);
5010
- const args = completeArgs(lowerer, values, sig.params, loc, expr, [strings]);
5432
+ const args = completeArgs(lowerer, values, sig.params, loc, expr, [strings()]);
5011
5433
  return reconcileOverloadReturn(lowerer, expr, { kind: "call", callee: sig.name, args, type: sig.returnType, loc });
5012
5434
  }
5013
5435
  // An ambient `declare function` nothing defines: Node throws
@@ -5034,7 +5456,7 @@ export function lowerTaggedTemplate(lowerer, expr) {
5034
5456
  const callee = lowerer.lowerExpr(expr.tag);
5035
5457
  if (callee.type.kind === "dyn") {
5036
5458
  const args = [
5037
- lowerer.coerceInto(expr.template, strings, DYN),
5459
+ lowerer.coerceInto(expr.template, strings(), DYN),
5038
5460
  ...values.map((a) => lowerer.lowerExprExpecting(a, DYN)),
5039
5461
  ];
5040
5462
  const calleeName = ts.isPropertyAccessExpression(expr.tag) || ts.isElementAccessExpression(expr.tag)
@@ -5046,6 +5468,53 @@ export function lowerTaggedTemplate(lowerer, expr) {
5046
5468
  }
5047
5469
  lowerer.unsupported("SC1090", expr, "tagged templates with this tag form (top-level functions and dynamic values tag; call the function directly otherwise)");
5048
5470
  }
5471
+ function loweredTemplateStrings(lowerer, expr, cooked, loc) {
5472
+ // Normal runtime construction keeps this array aligned with the current
5473
+ // ScrArr layout in both backends. The old static-header IR node encoded the
5474
+ // pre-sparse layout in LLVM and crashed as soon as a tag read its strings.
5475
+ const type = arrayOf(STRING);
5476
+ const key = `templateStringsGlobal:${loc.file}:${expr.template.getStart()}`;
5477
+ let getter = lowerer.widthHelpers.get(key);
5478
+ if (!getter) {
5479
+ const site = lowerer.globalsList.length;
5480
+ const globalId = `%g.templateStrings.${site}`;
5481
+ const readyId = `%g.templateStringsReady.${site}`;
5482
+ getter = `%templateStrings.get.${lowerer.widthHelpers.size}`;
5483
+ lowerer.widthHelpers.set(key, getter);
5484
+ lowerer.globalsList.push({ id: globalId, name: "%templateStrings", type, mutable: true }, { id: readyId, name: "%templateStringsReady", type: BOOL, mutable: true });
5485
+ lowerer.liftedFns.push({
5486
+ name: getter,
5487
+ params: [],
5488
+ returnType: type,
5489
+ locals: [],
5490
+ body: [
5491
+ {
5492
+ kind: "if",
5493
+ cond: { kind: "unary", op: "!", operand: varRef(readyId, BOOL, loc), type: BOOL, loc },
5494
+ then: [
5495
+ {
5496
+ kind: "assign",
5497
+ localId: globalId,
5498
+ value: {
5499
+ kind: "arrayLit",
5500
+ elems: cooked.map((value) => ({ kind: "strLit", value, type: STRING, loc })),
5501
+ type,
5502
+ loc,
5503
+ },
5504
+ loc,
5505
+ },
5506
+ { kind: "assign", localId: readyId, value: { kind: "boolLit", value: true, type: BOOL, loc }, loc },
5507
+ ],
5508
+ else_: null,
5509
+ loc,
5510
+ },
5511
+ { kind: "return", value: varRef(globalId, type, loc), loc },
5512
+ ],
5513
+ loc,
5514
+ });
5515
+ }
5516
+ return { kind: "call", callee: getter, args: [], type, loc };
5517
+ }
5049
5518
  /** True when the identifier resolves (through import aliases) to a
5050
5519
  * top-level function declaration of ANY program file (not merely a
5051
5520
  * same-named local shadowing one). Functions declared directly in a
@@ -5080,10 +5549,6 @@ export function lowerNestedFunctionDecl(lowerer, stmt) {
5080
5549
  export function lambdaSignature(lowerer, node) {
5081
5550
  if (!node.body)
5082
5551
  lowerer.unsupported("SC1090", node, "function overload signatures");
5083
- if (node.asteriskToken &&
5084
- node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
5085
- lowerer.unsupported("SC1071", node, "async generators (async function*)");
5086
- }
5087
5552
  if (node.typeParameters) {
5088
5553
  // Generic function-like forms monomorphize only where a static home
5089
5554
  // exists: top-level generic function declarations, generic methods
@@ -5107,6 +5572,7 @@ export function lambdaSignature(lowerer, node) {
5107
5572
  let ret = ts.isArrowFunction(node) && !ts.isBlock(node.body) && isStreamUndefCallExpr(lowerer, node.body)
5108
5573
  ? VOID
5109
5574
  : lowerer.declaredReturnType(node, node);
5575
+ ret = lowerer.runtimeOptionalFunctionReturnType(node, ret);
5110
5576
  // A contextually-typed arrow/function EXPRESSION whose slot signature
5111
5577
  // returns a UNION the inferred return doesn't spell adopts the slot's
5112
5578
  // return as its ABI: `(n) => work()` (inferring Promise<void>) against
@@ -5253,13 +5719,14 @@ export function lowerLambda(lowerer, node) {
5253
5719
  // the lifted body returns the inner T (a `return v` fulfills with v).
5254
5720
  const isAsync = !ts.isAccessor(node) &&
5255
5721
  node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) === true;
5256
- if (isAsync && funcType.ret.kind !== "promise")
5722
+ const isGenerator = node.asteriskToken !== undefined;
5723
+ if (isAsync && !isGenerator && funcType.ret.kind !== "promise")
5257
5724
  lowerer.badType(node, lowerer.typeOf(node));
5258
5725
  // Generator lambdas (function* expressions and object-literal
5259
5726
  // *methods): the VALUE's type returns the generator; the lifted body
5260
5727
  // returns the TReturn channel (a `return v` is the done-value).
5261
- const isGenerator = node.asteriskToken !== undefined;
5262
- if (isGenerator && funcType.ret.kind !== "generator")
5728
+ if (isGenerator &&
5729
+ (funcType.ret.kind !== "generator" || (funcType.ret.async === true) !== isAsync))
5263
5730
  lowerer.badType(node, lowerer.typeOf(node));
5264
5731
  const bodyReturn = isGenerator
5265
5732
  ? lowerer.genBodyReturnType(funcType.ret)
@@ -5267,7 +5734,7 @@ export function lowerLambda(lowerer, node) {
5267
5734
  const fnCtx = newFnCtx(true, selfSymbol, funcType, bodyReturn);
5268
5735
  fnCtx.isAsync = isAsync;
5269
5736
  if (isGenerator && funcType.ret.kind === "generator") {
5270
- fnCtx.generator = { yieldT: funcType.ret.yieldT, nextT: funcType.ret.nextT };
5737
+ fnCtx.generator = generatorMeta(lowerer, funcType.ret);
5271
5738
  }
5272
5739
  const diagsBefore = lowerer.diags.length;
5273
5740
  lowerer.fnStack.push(fnCtx);
@@ -6609,7 +7076,7 @@ function lowerIntlNumberFormatCall(lowerer, call, access) {
6609
7076
  function objectIsDisjointFalse(left, right, loc) {
6610
7077
  const stmts = [];
6611
7078
  for (const e of [left, right]) {
6612
- if (!droppableStatic(e))
7079
+ if (!isSafeToDiscard(e))
6613
7080
  stmts.push({ kind: "exprStmt", expr: e, loc });
6614
7081
  }
6615
7082
  const answer = { kind: "boolLit", value: false, type: BOOL, loc };
@@ -6623,6 +7090,9 @@ function lowerObjectStaticCall(lowerer, call, access) {
6623
7090
  if (!lowerer.isStdlibGlobal(access.expression, "Object"))
6624
7091
  return null;
6625
7092
  const member = access.name.text;
7093
+ const cacheKeys = lowerRequireCacheKeys(lowerer, call, member);
7094
+ if (cacheKeys)
7095
+ return cacheKeys;
6626
7096
  // Object.is — the spec's SameValue over the static kinds. Number
6627
7097
  // pairs take the runtime SameValue (NaN equals NaN, +0 differs from
6628
7098
  // -0 — the two divergences from ===); every other supported pair
@@ -6813,8 +7283,8 @@ function lowerObjectStaticCall(lowerer, call, access) {
6813
7283
  // the explicit-undefined-is-absent stance). Everything else keeps
6814
7284
  // the spread hint.
6815
7285
  if (call.arguments.length === 2 && !call.arguments.some((a) => ts.isSpreadElement(a))) {
6816
- const tProbe = probeLower(lowerer, call.arguments[0]);
6817
- const sProbe = probeLower(lowerer, call.arguments[1]);
7286
+ const tProbe = tryLowerExpression(lowerer, call.arguments[0]);
7287
+ const sProbe = tryLowerExpression(lowerer, call.arguments[1]);
6818
7288
  // CHECKED-DYNAMIC target and source (the JS file-scope
6819
7289
  // object-literal identity story): the runtime dyn copy — own
6820
7290
  // members of the source land on the target, which returns.
@@ -6872,7 +7342,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
6872
7342
  while (ts.isParenthesizedExpression(targetNode))
6873
7343
  targetNode = targetNode.expression;
6874
7344
  const freshLiteralTarget = ts.isObjectLiteralExpression(targetNode);
6875
- const tProbe = freshLiteralTarget ? null : probeLower(lowerer, call.arguments[0]);
7345
+ const tProbe = freshLiteralTarget ? null : tryLowerExpression(lowerer, call.arguments[0]);
6876
7346
  const tKind = tProbe?.type.kind;
6877
7347
  if (freshLiteralTarget || tKind === "dyn" || tKind === "nullT" || tKind === "undefinedT") {
6878
7348
  const loc = locOf(call);
@@ -6944,7 +7414,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
6944
7414
  // static shapes have no property table to extend.
6945
7415
  if (member === "defineProperties" && call.arguments.length === 2 &&
6946
7416
  !call.arguments.some((a) => ts.isSpreadElement(a))) {
6947
- let target = probeLower(lowerer, call.arguments[0]);
7417
+ let target = tryLowerExpression(lowerer, call.arguments[0]);
6948
7418
  // A FUNCTION-typed target boxes through the dyn boundary: the
6949
7419
  // property table lives on the CLOSURE (shared by every box of this
6950
7420
  // function value), so defining through a fresh box sticks — the
@@ -6999,7 +7469,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
6999
7469
  if (member === "hasOwn" && call.arguments.length === 2 && !call.arguments.some((a) => ts.isSpreadElement(a))) {
7000
7470
  const recvNode = call.arguments[0];
7001
7471
  const keyNode = call.arguments[1];
7002
- const probed = probeLower(lowerer, recvNode);
7472
+ const probed = tryLowerExpression(lowerer, recvNode);
7003
7473
  // A CHECKED-DYNAMIC receiver (the JS file-scope object-literal
7004
7474
  // identity story): the runtime dyn probe — OBJ member presence, ARR
7005
7475
  // index bounds, Node's ToObject TypeError on nullish.
@@ -7045,7 +7515,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
7045
7515
  // runtime walks the dyn node's own keys (integer-like keys first,
7046
7516
  // JS's own-key order) and answers a dyn array.
7047
7517
  {
7048
- const probed = probeLower(lowerer, argNode);
7518
+ const probed = tryLowerExpression(lowerer, argNode);
7049
7519
  const isDyn = probed?.type.kind === "dyn";
7050
7520
  // Unit-typed arguments (Object.keys(null)) ride the same runtime
7051
7521
  // walk: it throws Node's catchable TypeError.
@@ -7063,7 +7533,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
7063
7533
  // record (the narrowed export-table literal) — the lowered value's
7064
7534
  // shape is the honest dispatch key, exactly the identity-Set stance.
7065
7535
  if (argIr === null && isJsSourceFile(argNode.getSourceFile())) {
7066
- const probed = probeLower(lowerer, argNode);
7536
+ const probed = tryLowerExpression(lowerer, argNode);
7067
7537
  if (probed?.type.kind === "record")
7068
7538
  argIr = probed.type;
7069
7539
  }
@@ -8104,6 +8574,69 @@ const SP_BRAND_METHODS = new Set([
8104
8574
  "append", "delete", "get", "getAll", "has", "set", "sort",
8105
8575
  "forEach", "keys", "values", "entries", "toString",
8106
8576
  ]);
8577
+ function arrowSignatureOf(lowerer, node) {
8578
+ const signatures = lowerer.checker.getCallSignatures(lowerer.typeOf(node));
8579
+ if (signatures.length !== 1)
8580
+ return null;
8581
+ const declaration = lowerer.checker.signatureDeclaration(signatures[0]);
8582
+ return declaration !== undefined && ts.isArrowFunction(declaration) ? signatures[0] : null;
8583
+ }
8584
+ /** `arrow.apply(thisArg, dynArgs)` for a fixed-arity compiled arrow. Arrows
8585
+ * ignore `thisArg` and surplus arguments by language definition; declared
8586
+ * parameters validate from the dynamic array in order. */
8587
+ function lowerArrowFunctionApply(lowerer, call, access) {
8588
+ if (access.name.text !== "apply" ||
8589
+ call.arguments.length !== 2 ||
8590
+ call.arguments.some(ts.isSpreadElement) ||
8591
+ arrowSignatureOf(lowerer, access.expression) === null) {
8592
+ return null;
8593
+ }
8594
+ const callee = lowerer.lowerExpr(access.expression);
8595
+ if (callee.type.kind !== "func" || callee.type.rest === true)
8596
+ return null;
8597
+ if (!callee.type.params.every((param) => canDynCheckTo(param, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))) {
8598
+ return null;
8599
+ }
8600
+ const argsList = lowerer.lowerExpr(call.arguments[1]);
8601
+ if (argsList.type.kind !== "dyn")
8602
+ return null;
8603
+ const loc = locOf(call);
8604
+ const savedCallee = lowerer.declareHiddenLocal("%applyFn", callee.type);
8605
+ const savedArgs = lowerer.declareHiddenLocal("%applyArgs", DYN);
8606
+ const stmts = [
8607
+ { kind: "varDecl", localId: savedCallee.id, init: callee, loc },
8608
+ { kind: "exprStmt", expr: lowerer.lowerExpr(call.arguments[0]), loc },
8609
+ { kind: "varDecl", localId: savedArgs.id, init: argsList, loc },
8610
+ ];
8611
+ const args = callee.type.params.map((param, index) => {
8612
+ const read = {
8613
+ kind: "dynKeyGet",
8614
+ key: { kind: "strLit", value: String(index), type: STRING, loc },
8615
+ value: { kind: "varRef", localId: savedArgs.id, type: DYN, loc },
8616
+ type: DYN,
8617
+ loc,
8618
+ };
8619
+ return lowerer.coerceInto(call.arguments[1], read, param);
8620
+ });
8621
+ const invoked = {
8622
+ kind: "callValue",
8623
+ callee: { kind: "varRef", localId: savedCallee.id, type: callee.type, loc },
8624
+ args,
8625
+ type: callee.type.ret,
8626
+ loc,
8627
+ };
8628
+ let result;
8629
+ if (invoked.type.kind === "void") {
8630
+ stmts.push({ kind: "exprStmt", expr: invoked, loc });
8631
+ result = dynUndefinedExpr(loc);
8632
+ }
8633
+ else {
8634
+ result = lowerer.coerceInto(call, invoked, DYN);
8635
+ if (result.type.kind !== "dyn")
8636
+ return null;
8637
+ }
8638
+ return { kind: "seqExpr", stmts, result, type: DYN, loc };
8639
+ }
8107
8640
  function lowerObjLitGenericMethodCall(lowerer, call, access) {
8108
8641
  if (lowerer.chainBlocked(access, call))
8109
8642
  return null;
@@ -8165,6 +8698,9 @@ function lowerObjLitGenericMethodCall(lowerer, call, access) {
8165
8698
  }
8166
8699
  const found = objLitGenericFnNodeOf(lowerer, propSym);
8167
8700
  if (!found) {
8701
+ const arrowApply = lowerArrowFunctionApply(lowerer, call, access);
8702
+ if (arrowApply !== null)
8703
+ return arrowApply;
8168
8704
  // Function.prototype.apply/call/bind spelled through a FUNCTION
8169
8705
  // receiver: compiled functions are direct calls with no runtime
8170
8706
  // `this`/arguments object to re-route — name the working spelling
@@ -8211,9 +8747,32 @@ function lowerObjLitGenericMethodCall(lowerer, call, access) {
8211
8747
  export function lowerObjectMethodCall(lowerer, call, access) {
8212
8748
  if (lowerer.chainBlocked(access, call))
8213
8749
  return null;
8214
- const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
8215
- if (receiverIr?.kind !== "object")
8750
+ const mappedReceiver = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
8751
+ const receiverIr = mappedReceiver?.kind === "object"
8752
+ ? mappedReceiver
8753
+ : mappedReceiver?.kind === "union"
8754
+ ? (() => {
8755
+ const arms = lowerer.unions.get(mappedReceiver.unionId)?.arms ?? [];
8756
+ const objects = arms.filter((arm) => arm.kind === "object");
8757
+ return objects.length === 1 && arms.every((arm) => arm.kind === "object" || isUnitType(arm))
8758
+ ? objects[0]
8759
+ : null;
8760
+ })()
8761
+ : null;
8762
+ if (receiverIr === null)
8216
8763
  return null;
8764
+ const lowerReceiver = () => {
8765
+ const receiver = lowerer.lowerExpr(access.expression);
8766
+ const optional = lowerer.runtimeOptionalPropertyReceiver(access.expression, receiver, receiverIr, access.name.text);
8767
+ if (optional !== null)
8768
+ return optional;
8769
+ if (receiver.type.kind === "union") {
8770
+ const helper = lowerer.narrowedArmHelper(receiver.type.unionId, receiverIr, locOf(access.expression));
8771
+ if (helper !== null)
8772
+ return { kind: "call", callee: helper, args: [receiver], type: receiverIr, loc: locOf(access.expression) };
8773
+ }
8774
+ return receiver;
8775
+ };
8217
8776
  const info = lowerer.classes.get(receiverIr.className);
8218
8777
  if (!info)
8219
8778
  lowerer.flushDeferredClass(receiverIr.className);
@@ -8241,17 +8800,34 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8241
8800
  if (gfound)
8242
8801
  return lowerClassGenericMethodCall(lowerer, call, access, info, gfound);
8243
8802
  }
8244
- // A FUNC- or DYN-typed FIELD in call position: `this.cb()` — the
8245
- // ctor-assigned callback field (countdown.js's shape). The call is an
8246
- // ordinary call through the field's VALUE — read the field, then
8247
- // callValue (func fields) or the dynCall boundary (checked-dynamic
8248
- // fields: implicit-any ctor params, validated at the call like every
8249
- // dyn callee). Every other field type falls through to the fences.
8803
+ // A FUNC-, nullable-FUNC-, or DYN-typed FIELD in call position:
8804
+ // `this.cb()` — the ctor-assigned callback field (countdown.js's
8805
+ // shape). The call is an ordinary call through the field's VALUE —
8806
+ // read and, when necessary, check-narrow the callable arm, then
8807
+ // callValue; checked-dynamic fields use the dynCall boundary. Every
8808
+ // other field type falls through to the fences.
8250
8809
  if (info && !found) {
8251
8810
  const fieldType = info.fields.get(access.name.text);
8252
- if (fieldType && (fieldType.kind === "func" || fieldType.kind === "dyn")) {
8811
+ const callableType = fieldType?.kind === "func"
8812
+ ? fieldType
8813
+ : fieldType?.kind === "union"
8814
+ ? (() => {
8815
+ const arms = lowerer.unions.get(fieldType.unionId)?.arms ?? [];
8816
+ const funcs = arms.filter((arm) => arm.kind === "func");
8817
+ return funcs.length === 1 && arms.every((arm) => arm.kind === "func" || isUnitType(arm))
8818
+ ? funcs[0]
8819
+ : null;
8820
+ })()
8821
+ : null;
8822
+ if (fieldType && (callableType !== null || fieldType.kind === "dyn")) {
8253
8823
  const target = lowerer.fieldTarget(access);
8254
- const callee = target ? lowerer.fieldGetExpr(target, locOf(access), access) : null;
8824
+ let callee = target ? lowerer.fieldGetExpr(target, locOf(access), access) : null;
8825
+ if (callee?.type.kind === "union" && callableType !== null) {
8826
+ const helper = lowerer.narrowedArmHelper(callee.type.unionId, callableType, locOf(access));
8827
+ if (helper !== null) {
8828
+ callee = { kind: "call", callee: helper, args: [callee], type: callableType, loc: locOf(access) };
8829
+ }
8830
+ }
8255
8831
  if (callee?.type.kind === "func") {
8256
8832
  const params = callee.type.params;
8257
8833
  const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
@@ -8281,7 +8857,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8281
8857
  // The one builtin method: Error.prototype.toString, a runtime
8282
8858
  // implementation called directly (overriding it is fenced, so no
8283
8859
  // dispatch can ever be needed). Receiver BORROWED by the libCall.
8284
- const receiver = lowerer.lowerExpr(access.expression);
8860
+ const receiver = lowerReceiver();
8285
8861
  return {
8286
8862
  kind: "libCall",
8287
8863
  fn: "error.toString",
@@ -8303,7 +8879,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
8303
8879
  lowerer.noteVirtualEdge(info, method);
8304
8880
  else
8305
8881
  lowerer.noteEdge(`%${found.declarer.def.name}.${method}`);
8306
- const receiver = lowerer.lowerExpr(access.expression);
8882
+ const receiver = lowerReceiver();
8307
8883
  const args = lowerer.completeArgs(call.arguments, found.sig.params, locOf(call), call);
8308
8884
  if (lowerer.overrideBelow(info, method)) {
8309
8885
  return reconcileOverloadReturn(lowerer, call, {