@scriptc/compiler 0.1.1 → 0.1.3

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 (279) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +104 -31
  2. package/ambient/scriptc.d.ts +2 -1
  3. package/dist/backend/build-cache.js.map +1 -1
  4. package/dist/backend/c/async.d.ts +5 -0
  5. package/dist/backend/c/async.js +81 -2
  6. package/dist/backend/c/async.js.map +1 -1
  7. package/dist/backend/c/c-emitter.d.ts +25 -10
  8. package/dist/backend/c/c-emitter.js +74 -22
  9. package/dist/backend/c/c-emitter.js.map +1 -1
  10. package/dist/backend/c/exprs.d.ts +8 -1
  11. package/dist/backend/c/exprs.js +371 -18
  12. package/dist/backend/c/exprs.js.map +1 -1
  13. package/dist/backend/c/island.js.map +1 -1
  14. package/dist/backend/c/may-throw.js.map +1 -1
  15. package/dist/backend/c/shapes.js +15 -13
  16. package/dist/backend/c/shapes.js.map +1 -1
  17. package/dist/backend/c/stmts.d.ts +3 -5
  18. package/dist/backend/c/stmts.js +58 -17
  19. package/dist/backend/c/stmts.js.map +1 -1
  20. package/dist/backend/c/types.d.ts +4 -0
  21. package/dist/backend/c/types.js +18 -1
  22. package/dist/backend/c/types.js.map +1 -1
  23. package/dist/backend/c/types.test.js +8 -1
  24. package/dist/backend/c/types.test.js.map +1 -1
  25. package/dist/backend/c/walkers.js +63 -16
  26. package/dist/backend/c/walkers.js.map +1 -1
  27. package/dist/backend/external-c.js.map +1 -1
  28. package/dist/backend/json-literal.d.ts +7 -0
  29. package/dist/backend/json-literal.js +17 -0
  30. package/dist/backend/json-literal.js.map +1 -0
  31. package/dist/backend/json-literal.test.d.ts +1 -0
  32. package/dist/backend/json-literal.test.js +17 -0
  33. package/dist/backend/json-literal.test.js.map +1 -0
  34. package/dist/backend/link-plan.d.ts +1 -1
  35. package/dist/backend/link-plan.js +5 -1
  36. package/dist/backend/link-plan.js.map +1 -1
  37. package/dist/backend/linker.js.map +1 -1
  38. package/dist/backend/llvm/blocks.js.map +1 -1
  39. package/dist/backend/llvm/classes.js +8 -7
  40. package/dist/backend/llvm/classes.js.map +1 -1
  41. package/dist/backend/llvm/common.d.ts +4 -0
  42. package/dist/backend/llvm/common.js +6 -0
  43. package/dist/backend/llvm/common.js.map +1 -1
  44. package/dist/backend/llvm/common.test.d.ts +1 -0
  45. package/dist/backend/llvm/common.test.js +9 -0
  46. package/dist/backend/llvm/common.test.js.map +1 -0
  47. package/dist/backend/llvm/dyn.d.ts +4 -0
  48. package/dist/backend/llvm/dyn.js +80 -16
  49. package/dist/backend/llvm/dyn.js.map +1 -1
  50. package/dist/backend/llvm/emitter.js +132 -25
  51. package/dist/backend/llvm/emitter.js.map +1 -1
  52. package/dist/backend/llvm/expr-async.js +85 -3
  53. package/dist/backend/llvm/expr-async.js.map +1 -1
  54. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  55. package/dist/backend/llvm/expr-calls.js.map +1 -1
  56. package/dist/backend/llvm/expr-containers.js.map +1 -1
  57. package/dist/backend/llvm/expr-context.d.ts +5 -0
  58. package/dist/backend/llvm/expr-control.js.map +1 -1
  59. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  60. package/dist/backend/llvm/expr-dynamic.js +15 -3
  61. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  62. package/dist/backend/llvm/expr-island.js.map +1 -1
  63. package/dist/backend/llvm/expr-primitives.js +8 -0
  64. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  65. package/dist/backend/llvm/expr-records.js +3 -3
  66. package/dist/backend/llvm/expr-records.js.map +1 -1
  67. package/dist/backend/llvm/expr-stream-bridges.js +222 -10
  68. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  69. package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
  70. package/dist/backend/llvm/expr-stream-callbacks.js +53 -0
  71. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  72. package/dist/backend/llvm/lib-dispatch.js +24 -0
  73. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  74. package/dist/backend/llvm/lib-process.js +50 -5
  75. package/dist/backend/llvm/lib-process.js.map +1 -1
  76. package/dist/backend/llvm/lib-shared.js +64 -0
  77. package/dist/backend/llvm/lib-shared.js.map +1 -1
  78. package/dist/backend/llvm/lib-stream.js +6 -3
  79. package/dist/backend/llvm/lib-stream.js.map +1 -1
  80. package/dist/backend/llvm/shapes.js +10 -8
  81. package/dist/backend/llvm/shapes.js.map +1 -1
  82. package/dist/backend/llvm/split.js.map +1 -1
  83. package/dist/backend/llvm/unsupported.js.map +1 -1
  84. package/dist/backend/llvm/walkers.js +7 -5
  85. package/dist/backend/llvm/walkers.js.map +1 -1
  86. package/dist/backend/native-codegen.js.map +1 -1
  87. package/dist/backend/native-codegen.test.js +2 -0
  88. package/dist/backend/native-codegen.test.js.map +1 -1
  89. package/dist/backend/native-link-info.d.ts +2 -1
  90. package/dist/backend/native-link-info.js +4 -0
  91. package/dist/backend/native-link-info.js.map +1 -1
  92. package/dist/backend/native-link-info.test.js +24 -1
  93. package/dist/backend/native-link-info.test.js.map +1 -1
  94. package/dist/backend/native-toolchain.d.ts +8 -6
  95. package/dist/backend/native-toolchain.js +28 -13
  96. package/dist/backend/native-toolchain.js.map +1 -1
  97. package/dist/backend/native-toolchain.test.js +36 -1
  98. package/dist/backend/native-toolchain.test.js.map +1 -1
  99. package/dist/backend/runtime-pack-targets.test.js +1 -1
  100. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  101. package/dist/backend/runtime-pack.js.map +1 -1
  102. package/dist/backend/runtime-pack.test.js +1 -0
  103. package/dist/backend/runtime-pack.test.js.map +1 -1
  104. package/dist/backend/targets.d.ts +5 -0
  105. package/dist/backend/targets.js +9 -0
  106. package/dist/backend/targets.js.map +1 -1
  107. package/dist/backend/targets.test.js +6 -1
  108. package/dist/backend/targets.test.js.map +1 -1
  109. package/dist/backend/vendor-archives.js.map +1 -1
  110. package/dist/compat/fetch-profile.js +4 -2
  111. package/dist/compat/fetch-profile.js.map +1 -1
  112. package/dist/coverage/report.js.map +1 -1
  113. package/dist/coverage/surface-manifest.js +13 -0
  114. package/dist/coverage/surface-manifest.js.map +1 -1
  115. package/dist/diagnostics/diagnostic.d.ts +6 -5
  116. package/dist/diagnostics/diagnostic.js +10 -9
  117. package/dist/diagnostics/diagnostic.js.map +1 -1
  118. package/dist/diagnostics/render.js.map +1 -1
  119. package/dist/executable/executable-cache.d.ts +1 -0
  120. package/dist/executable/executable-cache.js +1 -0
  121. package/dist/executable/executable-cache.js.map +1 -1
  122. package/dist/executable/executable-cache.test.js +1 -0
  123. package/dist/executable/executable-cache.test.js.map +1 -1
  124. package/dist/ffi/ffi-manifest.js.map +1 -1
  125. package/dist/frontend/cjs-lexer.js.map +1 -1
  126. package/dist/frontend/dts-paths.js.map +1 -1
  127. package/dist/frontend/input-tracker.d.ts +10 -0
  128. package/dist/frontend/input-tracker.js +26 -3
  129. package/dist/frontend/input-tracker.js.map +1 -1
  130. package/dist/frontend/input-tracker.test.js +8 -1
  131. package/dist/frontend/input-tracker.test.js.map +1 -1
  132. package/dist/frontend/lib-contract.js.map +1 -1
  133. package/dist/frontend/lowering/array-values.d.ts +4 -0
  134. package/dist/frontend/lowering/array-values.js +18 -2
  135. package/dist/frontend/lowering/array-values.js.map +1 -1
  136. package/dist/frontend/lowering/callback-arg.js.map +1 -1
  137. package/dist/frontend/lowering/containers/bytes.d.ts +31 -0
  138. package/dist/frontend/lowering/containers/bytes.js +1105 -0
  139. package/dist/frontend/lowering/containers/bytes.js.map +1 -0
  140. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
  141. package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
  142. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
  143. package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
  144. package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
  145. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
  146. package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
  147. package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
  148. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
  149. package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
  150. package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
  151. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
  152. package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
  153. package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
  154. package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
  155. package/dist/frontend/lowering/lib-boundary.js +7 -2
  156. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  157. package/dist/frontend/lowering/lower-assert.js +16 -10
  158. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  159. package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
  160. package/dist/frontend/lowering/lower-async-iteration.js +657 -0
  161. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
  162. package/dist/frontend/lowering/lower-builtins.d.ts +40 -21
  163. package/dist/frontend/lowering/lower-builtins.js +611 -115
  164. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  165. package/dist/frontend/lowering/lower-calls.d.ts +21 -10
  166. package/dist/frontend/lowering/lower-calls.js +516 -75
  167. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  168. package/dist/frontend/lowering/lower-classes.d.ts +7 -13
  169. package/dist/frontend/lowering/lower-classes.js +128 -35
  170. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  171. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  172. package/dist/frontend/lowering/lower-containers.d.ts +1 -47
  173. package/dist/frontend/lowering/lower-containers.js +147 -1475
  174. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  175. package/dist/frontend/lowering/lower-event-emitter.js +51 -5
  176. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  177. package/dist/frontend/lowering/lower-exprs.d.ts +0 -78
  178. package/dist/frontend/lowering/lower-exprs.js +503 -2353
  179. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  180. package/dist/frontend/lowering/lower-generators.d.ts +18 -3
  181. package/dist/frontend/lowering/lower-generators.js +145 -9
  182. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  183. package/dist/frontend/lowering/lower-inspect.js +10 -3
  184. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  185. package/dist/frontend/lowering/lower-island.d.ts +6 -0
  186. package/dist/frontend/lowering/lower-island.js +37 -5
  187. package/dist/frontend/lowering/lower-island.js.map +1 -1
  188. package/dist/frontend/lowering/lower-modules.js +60 -15
  189. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  190. package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
  191. package/dist/frontend/lowering/lower-node-module.js +302 -0
  192. package/dist/frontend/lowering/lower-node-module.js.map +1 -0
  193. package/dist/frontend/lowering/lower-server.js +1 -1
  194. package/dist/frontend/lowering/lower-server.js.map +1 -1
  195. package/dist/frontend/lowering/lower-stmts.d.ts +10 -28
  196. package/dist/frontend/lowering/lower-stmts.js +542 -259
  197. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  198. package/dist/frontend/lowering/lower-stream.js +4 -4
  199. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  200. package/dist/frontend/lowering/lowerer.d.ts +41 -38
  201. package/dist/frontend/lowering/lowerer.js +148 -62
  202. package/dist/frontend/lowering/lowerer.js.map +1 -1
  203. package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
  204. package/dist/frontend/lowering/optional-arguments.js +57 -0
  205. package/dist/frontend/lowering/optional-arguments.js.map +1 -0
  206. package/dist/frontend/lowering/surfaces.d.ts +5 -3
  207. package/dist/frontend/lowering/surfaces.js +33 -13
  208. package/dist/frontend/lowering/surfaces.js.map +1 -1
  209. package/dist/frontend/npm-static-declarations.d.ts +35 -0
  210. package/dist/frontend/npm-static-declarations.js +519 -0
  211. package/dist/frontend/npm-static-declarations.js.map +1 -0
  212. package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
  213. package/dist/frontend/npm-static-declarations.test.js +132 -0
  214. package/dist/frontend/npm-static-declarations.test.js.map +1 -0
  215. package/dist/frontend/npm-static.d.ts +2 -0
  216. package/dist/frontend/npm-static.js +26 -10
  217. package/dist/frontend/npm-static.js.map +1 -1
  218. package/dist/frontend/npm.d.ts +60 -0
  219. package/dist/frontend/npm.js +38 -4
  220. package/dist/frontend/npm.js.map +1 -1
  221. package/dist/frontend/npm.test.d.ts +1 -0
  222. package/dist/frontend/npm.test.js +50 -0
  223. package/dist/frontend/npm.test.js.map +1 -0
  224. package/dist/frontend/program.d.ts +3 -2
  225. package/dist/frontend/program.js +513 -70
  226. package/dist/frontend/program.js.map +1 -1
  227. package/dist/frontend/resolve.d.ts +26 -3
  228. package/dist/frontend/resolve.js +166 -29
  229. package/dist/frontend/resolve.js.map +1 -1
  230. package/dist/frontend/resolve.test.js +24 -1
  231. package/dist/frontend/resolve.test.js.map +1 -1
  232. package/dist/frontend/runtime-resolve.d.ts +22 -0
  233. package/dist/frontend/runtime-resolve.js +101 -0
  234. package/dist/frontend/runtime-resolve.js.map +1 -0
  235. package/dist/frontend/runtime-resolve.test.d.ts +1 -0
  236. package/dist/frontend/runtime-resolve.test.js +18 -0
  237. package/dist/frontend/runtime-resolve.test.js.map +1 -0
  238. package/dist/frontend/ts7/adapter.js +12 -13
  239. package/dist/frontend/ts7/adapter.js.map +1 -1
  240. package/dist/frontend/ts7/ast.js.map +1 -1
  241. package/dist/frontend/ts7/census-check.js +3 -3
  242. package/dist/frontend/ts7/census-check.js.map +1 -1
  243. package/dist/frontend/ts7/checker.d.ts +3 -0
  244. package/dist/frontend/ts7/checker.js +12 -3
  245. package/dist/frontend/ts7/checker.js.map +1 -1
  246. package/dist/frontend/ts7/enums.js.map +1 -1
  247. package/dist/frontend/ts7/program-adapter.js +42 -3
  248. package/dist/frontend/ts7/program-adapter.js.map +1 -1
  249. package/dist/frontend/type-mapper.js +171 -16
  250. package/dist/frontend/type-mapper.js.map +1 -1
  251. package/dist/index.js +11 -5
  252. package/dist/index.js.map +1 -1
  253. package/dist/ir/analysis.js +1 -0
  254. package/dist/ir/analysis.js.map +1 -1
  255. package/dist/ir/ir.d.ts +125 -52
  256. package/dist/ir/ir.js +136 -7
  257. package/dist/ir/ir.js.map +1 -1
  258. package/dist/ir/serialize.d.ts +1 -1
  259. package/dist/ir/serialize.js +1 -1
  260. package/dist/ir/validate.js +120 -63
  261. package/dist/ir/validate.js.map +1 -1
  262. package/dist/library/cache-primitives.js.map +1 -1
  263. package/dist/library/cache-reset.test.js.map +1 -1
  264. package/dist/library/compiler-self-identity.js.map +1 -1
  265. package/dist/library/int-infer.js.map +1 -1
  266. package/dist/library/int-infer.test.js +4 -4
  267. package/dist/library/int-infer.test.js.map +1 -1
  268. package/dist/library/library-cache.d.ts +1 -0
  269. package/dist/library/library-cache.js +1 -0
  270. package/dist/library/library-cache.js.map +1 -1
  271. package/dist/library/library-cache.test.js +18 -5
  272. package/dist/library/library-cache.test.js.map +1 -1
  273. package/dist/library/library-profile.js.map +1 -1
  274. package/dist/library/sidecar-validate.js.map +1 -1
  275. package/dist/library/sidecar.js.map +1 -1
  276. package/dist/startup-cache.js.map +1 -1
  277. package/dist/wasi-paths.js.map +1 -1
  278. package/package.json +18 -18
  279. package/surface-manifest.json +91 -20
@@ -1,11 +1,12 @@
1
1
  import { InternalCompilerError } from "../../errors.js";
2
2
  /* Statement lowering: the statement dispatch (lowerStmt), variable
3
3
  * declarations including destructuring patterns, scoped blocks, control
4
- * flow (if/while/for/for-of/do, switch, try/catch, jumps with the
5
- * finally-crossing fence), and blocked-binding poisoning. */
4
+ * flow (if/while/for/for-of/do, switch, try/catch, abrupt completions),
5
+ * explicit resource management, and blocked-binding poisoning. */
6
6
  import * as ts from "../ts7/adapter.js";
7
7
  import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
8
- import { lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
8
+ import { lowerForAwaitGenerator, lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
9
+ import { lowerForAwaitBuiltin } from "./lower-async-iteration.js";
9
10
  import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arrayOf, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
10
11
  import { PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, neverTaintedJsType, stmtUsesIsland, uncheckedOverloadHandleCall } from "./lowerer.js";
11
12
  import { enforceLibBoundary } from "./lib-boundary.js";
@@ -15,20 +16,262 @@ import { isProvenanceSourceFile } from "../provenance-registry.js";
15
16
  import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget, trapDeclRootOf } from "./lower-namespaces.js";
16
17
  import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
17
18
  import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, lowerSafeIndexRead, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
18
- import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
19
+ import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, implicitMethodCallInfersReturn, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
19
20
  import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
20
21
  import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
21
- import { lowerStreamUnderscoreAssign, streamClassAliasDecl, streamSidesOf } from "./lower-stream.js";
22
+ import { lowerStreamUnderscoreAssign, streamClassAliasDecl } from "./lower-stream.js";
22
23
  import { lowerHttpResPropertyAssignment, lowerHttpServerTimeoutAssignment, lowerServerCloseOverrideAssignment } from "./lower-server.js";
23
24
  import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, createRequireBindingDecl, createRequireCalleeFileOf, createRequireNamespaceDecl, textCodecBindingDecl } from "./lower-builtins.js";
24
25
  import { lowerEnumDeclaration } from "./lower-enums.js";
25
- import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf, probeLower, pureReemittable, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
26
+ import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
27
+ import { isSafeToRepeat } from "./expressions/evaluation-safety.js";
28
+ import { tryLowerExpression } from "./expressions/try-lower-expression.js";
26
29
  import { checkerPanicDiag, isCheckerPanic, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
27
30
  import { isParseArgsDynTypeName, isUnitOnlyTsType, unitOnlyUnion } from "../type-mapper.js";
28
31
  import { canonicalBuiltinModule } from "../builtin-modules.js";
29
32
  import { isRelativeSpecifier } from "../workspace-registry.js";
30
33
  import { probeNodeRequireRefusal } from "../npm.js";
31
34
  import { numLit, varRef } from "../../ir/build.js";
35
+ import { constituentTypes } from "../ts7/checker.js";
36
+ import { npmStaticPackageOfPath } from "../npm-static.js";
37
+ import { fenceNodeModuleMutation } from "./lower-node-module.js";
38
+ /** An opted package's directly-returned empty array may take the function's
39
+ * explicit static return element type. This is the common JS accumulator
40
+ * shape (`const result = []; result.push(value); return result`) and is
41
+ * narrower than treating every evolving array in the function alike. */
42
+ function npmStaticReturnedEmptyArrayType(lowerer, decl) {
43
+ if (!ts.isIdentifier(decl.name) ||
44
+ decl.initializer === undefined ||
45
+ !ts.isArrayLiteralExpression(decl.initializer) ||
46
+ decl.initializer.elements.length !== 0 ||
47
+ !isJsSourceFile(decl.getSourceFile()) ||
48
+ npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
49
+ lowerer.ctx.returnType.kind !== "array") {
50
+ return null;
51
+ }
52
+ const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
53
+ if (symbol === undefined)
54
+ return null;
55
+ let owner = decl.parent;
56
+ while (owner !== undefined && !ts.isFunctionLike(owner))
57
+ owner = owner.parent;
58
+ const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
59
+ if (ownerBody === undefined || !ts.isBlock(ownerBody))
60
+ return null;
61
+ let returned = false;
62
+ ts.walkPreorder(ownerBody, (node) => {
63
+ if (node !== ownerBody && ts.isFunctionLike(node))
64
+ return "skip";
65
+ if (!ts.isReturnStatement(node) || node.expression === undefined)
66
+ return undefined;
67
+ let expression = node.expression;
68
+ while (ts.isParenthesizedExpression(expression))
69
+ expression = expression.expression;
70
+ if (ts.isIdentifier(expression) && lowerer.checker.getSymbolAtLocation(expression) === symbol) {
71
+ returned = true;
72
+ return "stop";
73
+ }
74
+ return undefined;
75
+ });
76
+ return returned ? lowerer.ctx.returnType : null;
77
+ }
78
+ /** A JS loop cursor in opted package source can be initialized with `T`
79
+ * and later assigned a nullable self link (`cursor = cursor.parent`). Keep
80
+ * that optional value in the local so the next loop condition, rather than
81
+ * the assignment, narrows it. */
82
+ function npmStaticOptionalSelfWriteType(lowerer, decl, initial) {
83
+ if (!ts.isIdentifier(decl.name) ||
84
+ !isJsSourceFile(decl.getSourceFile()) ||
85
+ npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
86
+ initial.kind === "union") {
87
+ return null;
88
+ }
89
+ const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
90
+ if (symbol === undefined)
91
+ return null;
92
+ let owner = decl.parent;
93
+ while (owner !== undefined && !ts.isFunctionLike(owner))
94
+ owner = owner.parent;
95
+ const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
96
+ if (ownerBody === undefined || !ts.isBlock(ownerBody))
97
+ return null;
98
+ let candidate = null;
99
+ let invalid = false;
100
+ ts.walkPreorder(ownerBody, (node) => {
101
+ if (node !== ownerBody && ts.isFunctionLike(node))
102
+ return "skip";
103
+ if (ts.isBinaryExpression(node)) {
104
+ let left = node.left;
105
+ while (ts.isParenthesizedExpression(left))
106
+ left = left.expression;
107
+ if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
108
+ if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
109
+ invalid = true;
110
+ return "stop";
111
+ }
112
+ const written = lowerer.mapTypeOf(lowerer.typeOf(node.right));
113
+ if (written !== null && typeEquals(written, initial))
114
+ return undefined;
115
+ const arms = written?.kind === "union" ? lowerer.unions.get(written.unionId)?.arms : undefined;
116
+ if (written?.kind !== "union" ||
117
+ arms === undefined ||
118
+ !arms.some((arm) => typeEquals(arm, initial)) ||
119
+ !arms.every((arm) => typeEquals(arm, initial) || isUnitType(arm)) ||
120
+ (candidate !== null && !typeEquals(candidate, written))) {
121
+ invalid = true;
122
+ return "stop";
123
+ }
124
+ candidate = written;
125
+ }
126
+ }
127
+ if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
128
+ ts.isIdentifier(node.operand) &&
129
+ lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
130
+ invalid = true;
131
+ return "stop";
132
+ }
133
+ return undefined;
134
+ });
135
+ return invalid ? null : candidate;
136
+ }
137
+ function isAwaitUsing(list) {
138
+ return (list.flags & ts.NodeFlags.AwaitUsing) === ts.NodeFlags.AwaitUsing;
139
+ }
140
+ function stdlibTypeNamed(lowerer, node, name) {
141
+ const visit = (t) => {
142
+ if (t.flags & ts.TypeFlags.Union)
143
+ return constituentTypes(t).some(visit);
144
+ const sym = t.getAliasSymbol() ?? t.getSymbol();
145
+ return sym?.name === name && lowerer.checker.declarationsOf(sym).some((d) => lowerer.isStdlibFile(d.getSourceFile()));
146
+ };
147
+ return visit(lowerer.typeOf(node));
148
+ }
149
+ function cleanupCall(lowerer, node, value, awaited) {
150
+ const loc = locOf(node);
151
+ if (value.type.kind === "object") {
152
+ const info = lowerer.classes.get(value.type.className);
153
+ if (!info)
154
+ lowerer.flushDeferredClass(value.type.className);
155
+ const asyncFound = awaited && info ? lowerer.findMethodOn(info, "sym:asyncDispose") : null;
156
+ const syncFound = info ? lowerer.findMethodOn(info, "sym:dispose") : null;
157
+ const member = asyncFound ? "sym:asyncDispose" : syncFound ? "sym:dispose" : null;
158
+ const found = asyncFound ?? syncFound;
159
+ if (!info || !member || !found)
160
+ return null;
161
+ if (found.sig.params.length !== 0) {
162
+ lowerer.unsupported("SC1090", node, `${member === "sym:asyncDispose" ? "[Symbol.asyncDispose]" : "[Symbol.dispose]"} methods with parameters`);
163
+ }
164
+ return {
165
+ call: lowerer.accessorCall(value.type.className, member, value, [], found.sig.ret, loc),
166
+ async: member === "sym:asyncDispose",
167
+ };
168
+ }
169
+ if (value.type.kind === "fileHandle") {
170
+ if (!awaited)
171
+ lowerer.unsupported("SC1090", node, "synchronous 'using' with an async-only FileHandle (use 'await using')");
172
+ return {
173
+ call: { kind: "libCall", fn: "fileHandle.close", args: [value], type: { kind: "promise", inner: VOID }, loc },
174
+ async: true,
175
+ };
176
+ }
177
+ if (value.type.kind === "child") {
178
+ return {
179
+ call: {
180
+ kind: "libCall",
181
+ fn: "child.kill",
182
+ args: [value, { kind: "strLit", value: "SIGTERM", type: STRING, loc }],
183
+ type: BOOL,
184
+ loc,
185
+ },
186
+ async: false,
187
+ };
188
+ }
189
+ if (value.type.kind === "f64") {
190
+ if (stdlibTypeNamed(lowerer, node, "Timeout")) {
191
+ return { call: { kind: "libCall", fn: "timers.clearTimeout", args: [value], type: VOID, loc }, async: false };
192
+ }
193
+ if (stdlibTypeNamed(lowerer, node, "Immediate")) {
194
+ return { call: { kind: "libCall", fn: "timers.clearImmediate", args: [value], type: VOID, loc }, async: false };
195
+ }
196
+ if (stdlibTypeNamed(lowerer, node, "Interface")) {
197
+ return { call: { kind: "libCall", fn: "rl.close", args: [value], type: VOID, loc }, async: false };
198
+ }
199
+ }
200
+ return null;
201
+ }
202
+ function cleanupForValue(lowerer, node, value, awaited) {
203
+ const loc = locOf(node);
204
+ if (isUnitType(value.type))
205
+ return [];
206
+ if (value.type.kind === "union") {
207
+ const def = lowerer.unions.get(value.type.unionId);
208
+ if (!def)
209
+ throw new InternalCompilerError(`lowerer bug: unknown using union ${value.type.unionId}`);
210
+ const out = [];
211
+ for (let tag = 0; tag < def.arms.length; tag++) {
212
+ const arm = def.arms[tag];
213
+ if (isUnitType(arm))
214
+ continue;
215
+ const narrowed = { kind: "unionNarrow", unionId: value.type.unionId, tag, value, type: arm, loc };
216
+ const body = cleanupForValue(lowerer, node, narrowed, awaited);
217
+ out.push({
218
+ kind: "if",
219
+ cond: { kind: "unionIsTag", unionId: value.type.unionId, tag, negated: false, value, type: BOOL, loc },
220
+ then: body,
221
+ else_: null,
222
+ loc,
223
+ });
224
+ }
225
+ return out;
226
+ }
227
+ const cleanup = cleanupCall(lowerer, node, value, awaited);
228
+ if (!cleanup) {
229
+ lowerer.unsupported("SC1090", node, `disposing values of type '${lowerer.fmt(value.type)}' (use a class with a zero-parameter [Symbol.dispose] or [Symbol.asyncDispose] method, or a supported Node resource)`);
230
+ }
231
+ const call = cleanup.call;
232
+ if (cleanup.async) {
233
+ if (call.type.kind !== "promise" || call.type.inner.kind !== "void") {
234
+ lowerer.unsupported("SC1090", node, "[Symbol.asyncDispose] methods whose result is not Promise<void>");
235
+ }
236
+ return [{ kind: "exprStmt", expr: { kind: "awaitExpr", value: call, type: VOID, loc }, loc }];
237
+ }
238
+ const out = [{ kind: "exprStmt", expr: call, loc }];
239
+ if (awaited)
240
+ out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "async.hop", args: [], type: VOID, loc }, loc });
241
+ return out;
242
+ }
243
+ function lowerUsingStatement(lowerer, stmt) {
244
+ if (ts.isSourceFile(stmt.parent)) {
245
+ lowerer.unsupported("SC1090", stmt, "top-level 'using' declarations (wrap the resource lifetime in a block or function)");
246
+ }
247
+ if (ts.isCaseClause(stmt.parent) || ts.isDefaultClause(stmt.parent)) {
248
+ lowerer.unsupported("SC1090", stmt, "'using' declarations directly in switch clauses (wrap the clause body in a block)");
249
+ }
250
+ const awaited = isAwaitUsing(stmt.declarationList);
251
+ if (awaited && !lowerer.ctx.isAsync) {
252
+ lowerer.unsupported("SC1090", stmt, "'await using' outside an async function or top-level-await module");
253
+ }
254
+ const registrations = [];
255
+ for (const decl of stmt.declarationList.declarations) {
256
+ if (!ts.isIdentifier(decl.name))
257
+ lowerer.unsupported("SC1031", decl.name);
258
+ if (decl.initializer === undefined)
259
+ lowerer.unsupported("SC1090", decl, "a 'using' declaration without an initializer");
260
+ const acquire = lowerer.lowerVarDecl(decl, false);
261
+ if (!acquire)
262
+ throw new InternalCompilerError("lowerer bug: using declaration emitted no acquisition");
263
+ const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
264
+ if (!symbol)
265
+ throw new InternalCompilerError("lowerer bug: using declaration has no symbol");
266
+ const binding = lowerer.bindingIn(lowerer.ctx, symbol) ?? lowerer.globalsBySymbol.get(symbol) ?? null;
267
+ if (!binding)
268
+ throw new InternalCompilerError("lowerer bug: using declaration has no runtime binding");
269
+ const value = varRef(binding.id, binding.type, locOf(decl.name));
270
+ const cleanup = cleanupForValue(lowerer, decl.name, value, awaited);
271
+ registrations.push({ acquire, cleanup, loc: locOf(decl) });
272
+ }
273
+ return registrations;
274
+ }
32
275
  /** `const X = /* @__PURE__ *\/ makeX()` at a module's top level: every
33
276
  * declarator initialized by a call (or `new`) carrying the bundler PURE
34
277
  * annotation in its leading trivia — the shape --provenance-sources may
@@ -113,6 +356,7 @@ export function provenanceElidedConstDecl(lowerer, decl) {
113
356
  * `let a = 1, b = a + 1;`) but is counted once. */
114
357
  export function lowerStmts(lowerer, stmts) {
115
358
  const out = [];
359
+ const usingMarkers = [];
116
360
  // JAVASCRIPT sources defer their compile fences to runtime (the
117
361
  // JS-input design: no annotations exist to change what lowers, so a
118
362
  // statement whose construct has no static lowering compiles to a
@@ -144,7 +388,25 @@ export function lowerStmts(lowerer, stmts) {
144
388
  }
145
389
  const diagsBefore = lowerer.diags.length;
146
390
  try {
147
- const lowered = lowerer.lowerStmt(stmt);
391
+ let lowered;
392
+ let appended = false;
393
+ if (ts.isVariableStatement(stmt) &&
394
+ (stmt.declarationList.flags & ts.NodeFlags.Using) !== 0) {
395
+ const registrations = lowerUsingStatement(lowerer, stmt);
396
+ const accounting = [];
397
+ for (const registration of registrations) {
398
+ out.push(registration.acquire);
399
+ accounting.push(registration.acquire, ...registration.cleanup);
400
+ if (registration.cleanup.length > 0) {
401
+ usingMarkers.push({ start: out.length, cleanup: registration.cleanup, loc: registration.loc });
402
+ }
403
+ }
404
+ lowered = accounting;
405
+ appended = true;
406
+ }
407
+ else {
408
+ lowered = lowerer.lowerStmt(stmt);
409
+ }
148
410
  // The lib-boundary chokepoint (lib-boundary.ts): checked-dynamic
149
411
  // arguments that reached builtin-call slots without the checked
150
412
  // coercion get their dynCheck here, and the uncoercible ones
@@ -153,10 +415,12 @@ export function lowerStmts(lowerer, stmts) {
153
415
  // deferred rejection.
154
416
  if (lowered)
155
417
  enforceLibBoundary(lowerer, lowered);
156
- if (Array.isArray(lowered))
157
- out.push(...lowered);
158
- else if (lowered)
159
- out.push(lowered);
418
+ if (!appended) {
419
+ if (Array.isArray(lowered))
420
+ out.push(...lowered);
421
+ else if (lowered)
422
+ out.push(lowered);
423
+ }
160
424
  // Island accounting (--dynamic coverage): a statement that lowered
161
425
  // but carries island constructs compiles DYNAMICALLY — its work
162
426
  // runs in the embedded engine, and the report says so instead of
@@ -215,6 +479,23 @@ export function lowerStmts(lowerer, stmts) {
215
479
  }
216
480
  }
217
481
  }
482
+ // Each acquisition protects the remainder of its lexical statement
483
+ // list. Folding from the last registration outward produces the
484
+ // spec's LIFO nesting and ensures a later initializer failure only
485
+ // disposes resources acquired before it.
486
+ for (let i = usingMarkers.length - 1; i >= 0; i--) {
487
+ const marker = usingMarkers[i];
488
+ const body = out.splice(marker.start);
489
+ out.push({
490
+ kind: "tryCatch",
491
+ tryBody: body,
492
+ catchBody: null,
493
+ catchLocalId: null,
494
+ finallyBody: marker.cleanup,
495
+ suppressFinallyErrors: true,
496
+ loc: marker.loc,
497
+ });
498
+ }
218
499
  }
219
500
  finally {
220
501
  lowerer.activeStmtLists.pop();
@@ -237,7 +518,7 @@ const TDZ_KINDS = new Set([
237
518
  // shape is the const's own initializer capturing the const:
238
519
  // `const server = http.createServer(h).listen(0, done)` with `done`
239
520
  // reading `server` — a TDZ read until the assign completes, JS exactly.
240
- "child", "childStream", "netServer", "netSocket", "dgramSocket", "testCtx",
521
+ "child", "childStream", "childWriter", "netServer", "netSocket", "dgramSocket", "testCtx",
241
522
  "httpReq", "httpRes", "httpClientReq",
242
523
  // The h2 handles — `const client = http2.connect(url, mustCall(() =>
243
524
  // ...client...))` is the suite's canonical self-capturing const.
@@ -313,10 +594,11 @@ export function predeclareForwardCapture(lowerer, symbol) {
313
594
  /** The FUNCTION-DECLARATION twin of predeclareForwardCapture: JS hoists a
314
595
  * nested `function f() {}` to scope entry — the binding is LIVE from the
315
596
  * first statement, so a reference lexically above the declaration in the
316
- * SAME function lowers the declaration eagerly at the reference (varDecl
317
- * pushed before the current statement's IR, registered in the list's
318
- * scope frame) and the statement loop skips the source statement when it
319
- * arrives (lowerer.hoistedFnDecls). Same-function only: the declaration's own
597
+ * SAME function lowers the declaration eagerly at the reference (storage
598
+ * declaration and initializing assignment are pushed before the current
599
+ * statement's IR, registered in the list's scope frame) and the statement
600
+ * loop skips the source statement when it arrives
601
+ * (lowerer.hoistedFnDecls). Same-function only: the declaration's own
320
602
  * lowering runs under the ctx that owns its statement list, and a
321
603
  * cross-function early capture would need lowering under a DIFFERENT
322
604
  * ctx than the one currently open — that shape keeps the honest fence. */
@@ -589,44 +871,6 @@ export function lowerScopedBlock(lowerer, stmt) {
589
871
  lowerer.scopes.pop();
590
872
  }
591
873
  }
592
- /** The finally fences. `return` crossing OUT of a try/catch body guarded
593
- * by a finally is SUPPORTED now (the backend's pending-return path runs
594
- * every crossed finally inner-to-outer before the function returns, the
595
- * value snapshotted first — Node-exact); what stays rejected is (a) any
596
- * jump out of a finally BLOCK itself (a return there would REPLACE a
597
- * pending completion — clearing a still-propagating exception or
598
- * abandoning a pending return, a model the emitter doesn't implement),
599
- * and (b) break/continue crossing a try-with-finally (their targets bind
600
- * inside the function; the pending-action plumbing exists only for
601
- * return, the shape real code needs). Rejected, not miscompiled. Plain
602
- * try/catch is transparent to jumps (it never appears in ctl). A LABELED
603
- * jump walks outward past every construct (labeled targets can be
604
- * arbitrarily far out) until the entry carrying its label, applying the
605
- * same finally fences to everything crossed on the way. */
606
- export function rejectJumpCrossingFinally(lowerer, kw, stmt, label) {
607
- const ctl = lowerer.ctx.ctl;
608
- for (let i = ctl.length - 1; i >= 0; i--) {
609
- const c = ctl[i];
610
- if (c.kind === "finallyBlock") {
611
- lowerer.unsupported("SC1090", stmt, `'${kw}' out of a 'finally' block (it would replace the pending completion — restructure so the finally only cleans up)`);
612
- }
613
- if (c.kind === "tryFinally") {
614
- if (kw !== "return") {
615
- lowerer.unsupported("SC1090", stmt, `'${kw}' crossing a 'finally' block`);
616
- }
617
- continue; // return runs the finally on the way out — supported
618
- }
619
- if (kw === "return")
620
- continue; // return crosses every construct
621
- if (label !== undefined) {
622
- if (c.labels?.includes(label))
623
- return; // the labeled target — binds here
624
- continue; // an inner construct the labeled jump exits
625
- }
626
- if (c.kind === "loop" || (kw === "break" && c.kind === "switch"))
627
- return; // binds inside the region
628
- }
629
- }
630
874
  /** `lbl: stmt` — labeled statements. A label chain collapses to one name
631
875
  * list (`a: b: while ...` — both names target the same loop, JS-exact).
632
876
  * Loops and switches take the labels DIRECTLY (a labeled continue needs
@@ -720,7 +964,6 @@ export function lowerStmt(lowerer, stmt) {
720
964
  if (ts.isLabeledStatement(stmt))
721
965
  return lowerLabeled(lowerer, stmt);
722
966
  if (ts.isReturnStatement(stmt)) {
723
- lowerer.rejectJumpCrossingFinally("return", stmt);
724
967
  // Implicit-any instance RETURN INFERENCE (lower-calls'
725
968
  // resolveInferredReturn): the value lowers BARE and the statement
726
969
  // records itself — the post-pass settles the instance's return type
@@ -784,10 +1027,8 @@ export function lowerStmt(lowerer, stmt) {
784
1027
  if (!target || (kw === "continue" && target.kind !== "loop")) {
785
1028
  lowerer.unsupported("SC1050", stmt, `'${kw} ${label}' targeting this statement form (the labeled statement lowers through a desugar that has no label point)`);
786
1029
  }
787
- lowerer.rejectJumpCrossingFinally(kw, stmt, label);
788
1030
  return { kind: kw, label, loc: locOf(stmt) };
789
1031
  }
790
- lowerer.rejectJumpCrossingFinally(kw, stmt);
791
1032
  // tsc rejects break outside loops/switches and continue outside loops,
792
1033
  // so the context is always valid here (the validator re-checks).
793
1034
  return { kind: kw, loc: locOf(stmt) };
@@ -846,7 +1087,8 @@ export function lowerStmt(lowerer, stmt) {
846
1087
  return null;
847
1088
  if (ts.isFunctionDeclaration(stmt)) {
848
1089
  // Already lowered eagerly by the forward-hoisting machinery
849
- // (predeclareForwardFnDecl) — the varDecl is in the list's output.
1090
+ // (predeclareForwardFnDecl) — its declaration and assignment are in
1091
+ // the list's output.
850
1092
  if (lowerer.hoistedFnDecls.has(stmt))
851
1093
  return null;
852
1094
  return lowerer.lowerNestedFunctionDecl(stmt);
@@ -2904,6 +3146,95 @@ export function lowerVarDeclList(lowerer, list) {
2904
3146
  throw new InternalCompilerError("lowerer bug: for-init declarator resolved to a global");
2905
3147
  return lowered;
2906
3148
  }
3149
+ /** A single static type for an initializer-less JavaScript `let` whose
3150
+ * later plain writes all agree. An evolving JS local reads as `undefined`
3151
+ * until control flow proves a write, so a scalar slot is sound only when
3152
+ * every ordinary use already has that concrete flow type. Any closure
3153
+ * capture stays checked-dynamic: it can observe the local before an outer
3154
+ * assignment, and the checker deliberately leaves captured evolving lets
3155
+ * broad. */
3156
+ function inferredEvolvingLetType(lowerer, decl) {
3157
+ if (!ts.isIdentifier(decl.name))
3158
+ return null;
3159
+ const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
3160
+ const scope = decl.parent.parent.parent;
3161
+ if (symbol === undefined || (!ts.isBlock(scope) && !ts.isSourceFile(scope)))
3162
+ return null;
3163
+ const isPlainAssignmentTarget = (node) => {
3164
+ let target = node;
3165
+ while (ts.isParenthesizedExpression(target.parent) && target.parent.expression === target) {
3166
+ target = target.parent;
3167
+ }
3168
+ const parent = target.parent;
3169
+ return ts.isBinaryExpression(parent) &&
3170
+ parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
3171
+ parent.left === target;
3172
+ };
3173
+ const captures = (fn) => {
3174
+ let captured = false;
3175
+ ts.walkPreorder(fn, (node) => {
3176
+ if (node !== fn &&
3177
+ ts.isIdentifier(node) &&
3178
+ lowerer.checker.getSymbolAtLocation(node) === symbol) {
3179
+ captured = true;
3180
+ return "stop";
3181
+ }
3182
+ return undefined;
3183
+ });
3184
+ return captured;
3185
+ };
3186
+ const writes = [];
3187
+ const reads = [];
3188
+ let invalid = false;
3189
+ ts.walkPreorder(scope, (node) => {
3190
+ if (node !== scope && ts.isFunctionLike(node)) {
3191
+ if (captures(node))
3192
+ invalid = true;
3193
+ return "skip";
3194
+ }
3195
+ if (ts.isBinaryExpression(node)) {
3196
+ let left = node.left;
3197
+ while (ts.isParenthesizedExpression(left))
3198
+ left = left.expression;
3199
+ if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
3200
+ if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
3201
+ invalid = true;
3202
+ return "stop";
3203
+ }
3204
+ if (ts.isCallExpression(node.right) && implicitMethodCallInfersReturn(lowerer, node.right)) {
3205
+ invalid = true;
3206
+ return "stop";
3207
+ }
3208
+ const mapped = lowerer.mapTypeOf(lowerer.typeOf(node.right));
3209
+ if (!mapped || mapped.kind === "dyn" || mapped.kind === "jsval" || mapped.kind === "void" || isUnitType(mapped)) {
3210
+ invalid = true;
3211
+ return "stop";
3212
+ }
3213
+ writes.push(mapped);
3214
+ }
3215
+ }
3216
+ if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
3217
+ ts.isIdentifier(node.operand) &&
3218
+ lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
3219
+ invalid = true;
3220
+ return "stop";
3221
+ }
3222
+ if (ts.isIdentifier(node) &&
3223
+ node !== decl.name &&
3224
+ lowerer.checker.getSymbolAtLocation(node) === symbol &&
3225
+ !isPlainAssignmentTarget(node)) {
3226
+ reads.push(node);
3227
+ }
3228
+ return undefined;
3229
+ });
3230
+ const first = writes[0];
3231
+ if (invalid || first === undefined || !writes.every((write) => typeEquals(write, first)))
3232
+ return null;
3233
+ return reads.every((read) => {
3234
+ const mapped = lowerer.mapTypeOf(lowerer.typeOf(read));
3235
+ return mapped !== null && typeEquals(mapped, first);
3236
+ }) ? first : null;
3237
+ }
2907
3238
  export function lowerVarDecl(lowerer, decl, isLet) {
2908
3239
  // --provenance-sources: an elided pure-annotated dead const emits
2909
3240
  // nothing (collectGlobals registered no global by the same test —
@@ -3163,6 +3494,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3163
3494
  // immediately (the type covers the unassigned state), so they
3164
3495
  // initialize to the interned undefined arm like an omitted optional.
3165
3496
  let type = lowerer.irTypeOf(decl.name);
3497
+ if (type.kind === "dyn" && isLet && isJsSourceFile(decl.getSourceFile())) {
3498
+ type = inferredEvolvingLetType(lowerer, decl) ?? type;
3499
+ }
3166
3500
  // `var v: void;` / `let x: undefined;` — a unit-only binding rides
3167
3501
  // the unit-only union (its undefined arm is the unassigned state,
3168
3502
  // which is also the only state).
@@ -3233,6 +3567,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3233
3567
  (lowerer.dynamic && init.type.kind === "dyn" && jsvalFlavoredType(lowerer.mapTypeOf(lowerer.typeOf(decl.name)) ?? DYN) ? DYN : null) ??
3234
3568
  (bindingTainted ? null : lowerer.mapTypeOf(lowerer.typeOf(decl.name))) ??
3235
3569
  (init.type.kind === "dyn" ? DYN : null);
3570
+ if (lowerer.implicitParamTypes !== null &&
3571
+ init.type.kind === "dyn" &&
3572
+ isJsSourceFile(decl.getSourceFile()) &&
3573
+ npmStaticPackageOfPath(decl.getSourceFile().fileName) !== null) {
3574
+ type = DYN;
3575
+ }
3236
3576
  // A JS `let x = {}`: TS's empty-object-literal type admits ANY later
3237
3577
  // non-nullish assignment (`envs = {}`, later `envs =
3238
3578
  // Object.fromEntries(...)` — tsc accepts every such write, since
@@ -3245,6 +3585,11 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3245
3585
  if (shape && shape.fields.length === 0 && !shape.indexValue && !shape.tuple)
3246
3586
  type = DYN;
3247
3587
  }
3588
+ const returnedEmptyArrayType = npmStaticReturnedEmptyArrayType(lowerer, decl);
3589
+ if (returnedEmptyArrayType !== null) {
3590
+ type = returnedEmptyArrayType;
3591
+ init = lowerer.lowerExprExpecting(decl.initializer, returnedEmptyArrayType);
3592
+ }
3248
3593
  // A JS `const leaked = [];` (the evolving-array idiom — test/common's
3249
3594
  // leak ledger): tsc types the binding by its LATER pushes, but this
3250
3595
  // frontend answers the declaration name with the uninhabited never[],
@@ -3254,7 +3599,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3254
3599
  // checked-dynamic instead (the `let x = {}` stance, and lower-modules'
3255
3600
  // file-scope evolving-array rule): pushes ride the dyn array, typed
3256
3601
  // exits dynCheck.
3257
- if ((type === null || type.kind === "array") && isJsSourceFile(decl.getSourceFile())) {
3602
+ if (returnedEmptyArrayType === null &&
3603
+ (type === null || type.kind === "array") &&
3604
+ isJsSourceFile(decl.getSourceFile())) {
3258
3605
  let initExpr = decl.initializer;
3259
3606
  while (ts.isParenthesizedExpression(initExpr))
3260
3607
  initExpr = initExpr.expression;
@@ -3316,14 +3663,15 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3316
3663
  // their tuple record likewise). Const only — an evolving-`any`
3317
3664
  // `let` may be reassigned a different shape later; genuine `any`
3318
3665
  // only — every other unmappable keeps its own diagnostic.
3319
- if (type === null && !isLet &&
3320
- ((lowerer.typeOf(decl.name).flags & ts.TypeFlags.Any) !== 0 ||
3666
+ const checkerAny = (lowerer.typeOf(decl.name).flags & ts.TypeFlags.Any) !== 0;
3667
+ if ((type === null || (type.kind === "dyn" && checkerAny)) && !isLet &&
3668
+ (checkerAny ||
3321
3669
  (lowerer.checkerAnyArray(decl.name) && lowerer.isArrayValueType(init.type)) ||
3322
3670
  // JS declarations carry no annotations: an unmappable inferred
3323
3671
  // type (a union with a fence-folded arm — the typeof-'bigint'
3324
3672
  // dual-mode ternary) adopts the initializer's static type, the
3325
3673
  // same rule genuine `any` consts take.
3326
- isJsSourceFile(decl.getSourceFile())) &&
3674
+ (type === null && isJsSourceFile(decl.getSourceFile()))) &&
3327
3675
  init.type.kind !== "void" && init.type.kind !== "caught" && init.type.kind !== "jsval" &&
3328
3676
  !isUnitType(init.type)) {
3329
3677
  type = init.type;
@@ -3338,6 +3686,7 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3338
3686
  }
3339
3687
  if (!type)
3340
3688
  lowerer.badType(decl.name, lowerer.typeOf(decl.name));
3689
+ let settledType = type;
3341
3690
  const arithmeticType = decl.initializer ? lowerer.runtimeOptionalArithmeticTypes.get(decl.initializer) : undefined;
3342
3691
  const isStringArithmeticUnion = (t) => {
3343
3692
  if (t.kind !== "union")
@@ -3346,35 +3695,42 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3346
3695
  return !!arms && arms.length === 2 &&
3347
3696
  arms.some((a) => a.kind === "f64") && arms.some((a) => a.kind === "string");
3348
3697
  };
3349
- const runtimeStringArithmetic = type?.kind === "string" &&
3698
+ const runtimeStringArithmetic = settledType.kind === "string" &&
3350
3699
  ((arithmeticType && typeEquals(arithmeticType, init.type)) || isStringArithmeticUnion(init.type));
3351
3700
  if (runtimeStringArithmetic) {
3352
- type = init.type;
3701
+ settledType = init.type;
3353
3702
  }
3354
- type = lowerer.runtimeOptionalBindingType(decl.name, type);
3703
+ const optionalSelfWriteType = isLet
3704
+ ? npmStaticOptionalSelfWriteType(lowerer, decl, settledType)
3705
+ : null;
3706
+ settledType = lowerer.runtimeOptionalBindingType(decl.name, settledType);
3707
+ if (optionalSelfWriteType !== null)
3708
+ settledType = optionalSelfWriteType;
3709
+ if (returnedEmptyArrayType !== null)
3710
+ settledType = returnedEmptyArrayType;
3355
3711
  // `const x: void = undefined` / `let y: undefined = undefined`: the
3356
3712
  // unit-only union — the initializer's unit literal wraps into its arm
3357
3713
  // like any optional completion. Non-literal void initializers (a
3358
3714
  // void CALL's result) keep their fence at the coercion below.
3359
- if (type.kind === "void" && isUnitOnlyTsType(lowerer.typeOf(decl.name))) {
3360
- type = unitOnlyUnion(lowerer.unions);
3715
+ if (settledType.kind === "void" && isUnitOnlyTsType(lowerer.typeOf(decl.name))) {
3716
+ settledType = unitOnlyUnion(lowerer.unions);
3361
3717
  }
3362
- if (type.kind === "void")
3718
+ if (settledType.kind === "void")
3363
3719
  lowerer.badType(decl.name, lowerer.typeOf(decl.name));
3364
3720
  // A PACKAGE promise stored in a local stays an island HANDLE — the
3365
3721
  // engine's promise has no static promise value to coerce into. Awaits
3366
3722
  // and .catch/.finally chains on the local bridge per use site (each
3367
3723
  // bridge is an independent observer of the same settlement).
3368
- if (init.type.kind === "jsval" && type.kind === "promise")
3369
- type = JSVAL;
3724
+ if (init.type.kind === "jsval" && settledType.kind === "promise")
3725
+ settledType = JSVAL;
3370
3726
  // An island value under an `any[]`-declared LOCAL spelling stays an
3371
3727
  // island HANDLE too (the runtime-world rule): the jsval-element-array
3372
3728
  // exit is a CALL-boundary conversion (the loadPlugins param ABI) —
3373
3729
  // converting a local would strand the engine's own prototypes
3374
3730
  // (join/map/... on a native handle-element array), where the handle
3375
3731
  // routes every use engine-side.
3376
- if (init.type.kind === "jsval" && type.kind === "array" && type.elem.kind === "jsval") {
3377
- type = JSVAL;
3732
+ if (init.type.kind === "jsval" && settledType.kind === "array" && settledType.elem.kind === "jsval") {
3733
+ settledType = JSVAL;
3378
3734
  }
3379
3735
  // A STATIC array of island HANDLES under an unannotated declared type
3380
3736
  // spelling evolved elements (`const kept = fns.filter(...)` over an
@@ -3387,8 +3743,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3387
3743
  // validated-boundary fence.
3388
3744
  if (!decl.type &&
3389
3745
  init.type.kind === "array" && init.type.elem.kind === "jsval" &&
3390
- type.kind === "array" && type.elem.kind !== "jsval") {
3391
- type = init.type;
3746
+ settledType.kind === "array" && settledType.elem.kind !== "jsval") {
3747
+ settledType = init.type;
3392
3748
  }
3393
3749
  // `const runner = options.runner || defaultRunner`: the checker types
3394
3750
  // the || as the union/merge of two structurally-compatible function
@@ -3398,13 +3754,13 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3398
3754
  // when the parameters agree and the declared return either contains
3399
3755
  // the init's return as a union arm or is the opaque spawnRes the
3400
3756
  // adapter converts (nothing coerces INTO either declared form).
3401
- if (type.kind === "func" && init.type.kind === "func" && !typeEquals(type, init.type)) {
3757
+ if (settledType.kind === "func" && init.type.kind === "func" && !typeEquals(settledType, init.type)) {
3402
3758
  const initFn = init.type;
3403
- const paramsAgree = type.params.length === initFn.params.length &&
3404
- type.params.every((p, i) => typeEquals(p, initFn.params[i]));
3405
- const retUnionArm = type.ret.kind === "union" && lowerer.armTag(type.ret.unionId, initFn.ret) >= 0;
3406
- if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(type, initFn) !== null)) {
3407
- type = init.type;
3759
+ const paramsAgree = settledType.params.length === initFn.params.length &&
3760
+ settledType.params.every((p, i) => typeEquals(p, initFn.params[i]));
3761
+ const retUnionArm = settledType.ret.kind === "union" && lowerer.armTag(settledType.ret.unionId, initFn.ret) >= 0;
3762
+ if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(settledType, initFn) !== null)) {
3763
+ settledType = init.type;
3408
3764
  }
3409
3765
  }
3410
3766
  // The downstream twin: `const result = runner(cmd, args)` where the
@@ -3413,12 +3769,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3413
3769
  // can narrow (spawnRes has no discriminant). The VALUE is statically
3414
3770
  // the record arm, so the local adopts it; flows into the union slot
3415
3771
  // re-wrap via the ordinary arm coercion.
3416
- if (type.kind === "union" && init.type.kind === "record") {
3417
- const arms = lowerer.unions.get(type.unionId)?.arms ?? [];
3772
+ if (settledType.kind === "union" && init.type.kind === "record") {
3773
+ const arms = lowerer.unions.get(settledType.unionId)?.arms ?? [];
3418
3774
  if (arms.length === 2 &&
3419
3775
  arms.some((a) => a.kind === "spawnRes") &&
3420
3776
  arms.some((a) => typeEquals(a, init.type))) {
3421
- type = init.type;
3777
+ settledType = init.type;
3422
3778
  }
3423
3779
  }
3424
3780
  // `const r: Repo = new MemRepo()` over an all-generic-method
@@ -3426,8 +3782,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3426
3782
  // (the record shape maps empty and the width copy would drop the
3427
3783
  // class the generic-method calls monomorphize against) — see
3428
3784
  // genericIfaceBindingKeepsClass.
3429
- if (type.kind === "record" && init.type.kind === "object" && genericIfaceBindingKeepsClass(lowerer, decl, type)) {
3430
- type = init.type;
3785
+ if (settledType.kind === "record" && init.type.kind === "object" && genericIfaceBindingKeepsClass(lowerer, decl, settledType)) {
3786
+ settledType = init.type;
3431
3787
  }
3432
3788
  // A TDZ box minted DURING this very initializer (a callback inside it
3433
3789
  // captured this const — predeclareForwardCapture's current-statement
@@ -3442,8 +3798,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
3442
3798
  }
3443
3799
  // Slot coercion: `const r: A | B = bValue;` wraps implicitly; width
3444
3800
  // subtyping (`const p: {a: number} = wider;`) is rejected, not coerced.
3445
- init = lowerer.coerceInto(decl.initializer, init, type);
3446
- const local = lowerer.declareLocal(decl.name, decl.name.text, type, isLet);
3801
+ init = lowerer.coerceInto(decl.initializer, init, settledType);
3802
+ const local = lowerer.declareLocal(decl.name, decl.name.text, settledType, isLet);
3447
3803
  if (runtimeStringArithmetic && isStringArithmeticUnion(init.type)) {
3448
3804
  lowerer.runtimeOptionalArithmeticLocals.add(lowerer.runtimeOptionalRootOf(local));
3449
3805
  }
@@ -3545,9 +3901,8 @@ export function lowerSwitch(lowerer, stmt) {
3545
3901
  const labels = lowerer.takeLabels();
3546
3902
  const disc = lowerer.lowerExpr(stmt.expression);
3547
3903
  const dk = disc.type.kind;
3548
- if (dk === "dyn") {
3549
- lowerer.unsupported("SC1100", stmt.expression, "switch statements on 'unknown' values");
3550
- }
3904
+ if (dk === "dyn")
3905
+ return lowerDynSwitch(lowerer, stmt, disc);
3551
3906
  if (dk === "union")
3552
3907
  return lowerUnionSwitch(lowerer, stmt, disc);
3553
3908
  if (dk !== "f64" && dk !== "string" && dk !== "bool") {
@@ -3596,19 +3951,51 @@ export function lowerSwitch(lowerer, stmt) {
3596
3951
  * is last).
3597
3952
  * Case bodies share ONE lexical scope, exactly like the real switch. */
3598
3953
  function lowerUnionSwitch(lowerer, stmt, disc) {
3599
- const loc = locOf(stmt);
3600
3954
  if (disc.type.kind !== "union")
3601
3955
  throw new InternalCompilerError("lowerer bug: non-union disc");
3602
3956
  const unionType = disc.type;
3603
3957
  if (!lowerer.eqComparableUnion(unionType.unionId)) {
3604
3958
  lowerer.unsupported("SC1090", stmt.expression, "switch on a union whose arms do not have static equality");
3605
3959
  }
3960
+ return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
3961
+ const unitTest = test.kind === "unitLit"
3962
+ ? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(expression))
3963
+ : null;
3964
+ return unitTest ?? {
3965
+ kind: "unionEq",
3966
+ unionId: unionType.unionId,
3967
+ negated: false,
3968
+ sameValue: false,
3969
+ left: stableDisc,
3970
+ right: lowerer.coerceInto(expression, test, unionType),
3971
+ type: BOOL,
3972
+ loc: locOf(expression),
3973
+ };
3974
+ });
3975
+ }
3976
+ /** A checked-dynamic switch whose tests have native strict-equality
3977
+ * answers. Scalar tests use dynScalarEq; null/undefined use dyn kind tests.
3978
+ * Other case values retain the existing unknown-switch fence. */
3979
+ function lowerDynSwitch(lowerer, stmt, disc) {
3980
+ return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
3981
+ if (test.kind === "unitLit") {
3982
+ return { kind: "dynTest", test: test.unit, value: stableDisc, type: BOOL, loc: locOf(expression) };
3983
+ }
3984
+ if (test.type.kind !== "string" && test.type.kind !== "f64" &&
3985
+ test.type.kind !== "bool" && test.type.kind !== "dyn") {
3986
+ lowerer.unsupported("SC1100", expression, "switch case values that cannot compare strictly with 'unknown'");
3987
+ }
3988
+ return { kind: "dynScalarEq", left: stableDisc, right: test, type: BOOL, loc: locOf(expression) };
3989
+ });
3990
+ }
3991
+ function lowerBranchSwitch(lowerer, stmt, disc, compare) {
3992
+ const loc = locOf(stmt);
3606
3993
  const prefix = [];
3607
3994
  let stableDisc = disc;
3608
- if (!pureReemittable(disc)) {
3609
- const temp = lowerer.declareHiddenLocal("%switch", unionType);
3995
+ if (!isSafeToRepeat(disc)) {
3996
+ const temp = lowerer.declareHiddenLocal("%switch", disc.type);
3610
3997
  prefix.push({ kind: "varDecl", localId: temp.id, init: disc, loc });
3611
- stableDisc = { kind: "varRef", localId: temp.id, type: unionType, loc };
3998
+ stableDisc = { kind: "varRef", localId: temp.id, type: disc.type, loc };
3612
3999
  }
3613
4000
  const clauses = stmt.caseBlock.clauses;
3614
4001
  // An unlabeled break at a clause's END exits the switch — the chain's
@@ -3667,7 +4054,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
3667
4054
  // final else, so matching them changes nothing when pure).
3668
4055
  if (test.kind !== "strLit" && test.kind !== "numLit" &&
3669
4056
  test.kind !== "boolLit" && test.kind !== "unitLit" &&
3670
- !pureReemittable(test)) {
4057
+ !isSafeToRepeat(test)) {
3671
4058
  lowerer.unsupported("SC1090", clause.expression, "effectful case tests in a union-typed switch (bind the test value to a const first)");
3672
4059
  }
3673
4060
  // A unit-literal test takes the unit-comparison lowering: a tag
@@ -3675,19 +4062,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
3675
4062
  // lacks it (`case null:` on a `number | undefined` — legal TS,
3676
4063
  // never matches; coercing the literal into the union would hit
3677
4064
  // the stranded-arm trap and throw where JS just skips the case).
3678
- const unitTest = test.kind === "unitLit"
3679
- ? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(clause.expression))
3680
- : null;
3681
- pendingTests.push(unitTest ?? {
3682
- kind: "unionEq",
3683
- unionId: unionType.unionId,
3684
- negated: false,
3685
- sameValue: false,
3686
- left: stableDisc,
3687
- right: lowerer.coerceInto(clause.expression, test, unionType),
3688
- type: BOOL,
3689
- loc: locOf(clause.expression),
3690
- });
4065
+ pendingTests.push(compare(stableDisc, test, clause.expression));
3691
4066
  }
3692
4067
  const isDefault = ts.isDefaultClause(clause);
3693
4068
  if (clause.statements.length === 0 && !isDefault && i < clauses.length - 1) {
@@ -3730,7 +4105,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
3730
4105
  lowerer.scopes.pop();
3731
4106
  }
3732
4107
  }
3733
- /** try/catch/finally. Supported subset:
4108
+ /** try/catch/finally:
3734
4109
  * - `catch { }` (bindingless) discards the thrown value on entry.
3735
4110
  * - `catch (e)` binds it as a CAUGHT local — a snapshot of the exception
3736
4111
  * cell, deliberately narrower than dyn: the supported uses are the
@@ -3741,23 +4116,14 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
3741
4116
  * assignment are fenced (SC1063 and friends); destructuring patterns
3742
4117
  * are SC1062. The `: any` / `: unknown` annotations tsc admits both
3743
4118
  * lower the same way (tsc narrows either through the tests).
3744
- * - `finally` runs on normal completion, on exception paths, and on the
3745
- * way out of a `return` in the try/catch body (the backend's
3746
- * pending-return path — inner-to-outer through nested finallys, the
3747
- * value snapshotted before the finally runs, Node-exact).
3748
- * break/continue crossing a try-with-finally stay rejected, as does
3749
- * any jump out of the finally body itself (it would replace a pending
3750
- * completion — rejectJumpCrossingFinally). Plain try/catch places no
3751
- * restriction on jumps.
4119
+ * - `finally` runs on normal completion, exception paths, and every
4120
+ * abrupt completion that crosses it. Return values are snapshotted
4121
+ * before cleanup; an abrupt completion inside the finally replaces the
4122
+ * pending one, matching JavaScript completion-record semantics.
3752
4123
  * Each block is its own lexical scope (the binding lives in a scope
3753
4124
  * WRAPPING the catch block, like the spec's catch environment). */
3754
4125
  export function lowerTry(lowerer, stmt) {
3755
- const hasFinally = stmt.finallyBlock !== undefined;
3756
- // try/catch bodies are jump-fenced only when a finally guards them.
3757
- const lowerGuarded = (block) => hasFinally
3758
- ? lowerer.inCtl("tryFinally", () => lowerer.lowerScopedBlock(block))
3759
- : lowerer.lowerScopedBlock(block);
3760
- const tryBody = lowerGuarded(stmt.tryBlock);
4126
+ const tryBody = lowerer.lowerScopedBlock(stmt.tryBlock);
3761
4127
  let catchBody = null;
3762
4128
  let catchLocalId = null;
3763
4129
  if (stmt.catchClause) {
@@ -3770,19 +4136,17 @@ export function lowerTry(lowerer, stmt) {
3770
4136
  try {
3771
4137
  const local = lowerer.declareLocal(vd.name, vd.name.text, CAUGHT, false);
3772
4138
  catchLocalId = local.id;
3773
- catchBody = lowerGuarded(stmt.catchClause.block);
4139
+ catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
3774
4140
  }
3775
4141
  finally {
3776
4142
  lowerer.scopes.pop();
3777
4143
  }
3778
4144
  }
3779
4145
  else {
3780
- catchBody = lowerGuarded(stmt.catchClause.block);
4146
+ catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
3781
4147
  }
3782
4148
  }
3783
- const finallyBody = stmt.finallyBlock
3784
- ? lowerer.inCtl("finallyBlock", () => lowerer.lowerScopedBlock(stmt.finallyBlock))
3785
- : null;
4149
+ const finallyBody = stmt.finallyBlock ? lowerer.lowerScopedBlock(stmt.finallyBlock) : null;
3786
4150
  return {
3787
4151
  kind: "tryCatch",
3788
4152
  tryBody,
@@ -3806,6 +4170,7 @@ function lowerDeleteStatement(lowerer, expr) {
3806
4170
  if (!ts.isElementAccessExpression(target) && !ts.isPropertyAccessExpression(target)) {
3807
4171
  lowerer.unsupported("SC1090", expr, "'delete' of non-property expressions");
3808
4172
  }
4173
+ fenceNodeModuleMutation(lowerer, target, "delete");
3809
4174
  const lowerKey = () => {
3810
4175
  if (ts.isPropertyAccessExpression(target)) {
3811
4176
  return { kind: "strLit", value: target.name.text, type: STRING, loc: locOf(target.name) };
@@ -4131,6 +4496,21 @@ export function lowerExprStatement(lowerer, expr) {
4131
4496
  const stmtNode = expr.parent;
4132
4497
  while (ts.isParenthesizedExpression(expr))
4133
4498
  expr = expr.expression;
4499
+ // A discarded conditional has no value to merge: represent its lazy
4500
+ // branch selection directly as statement control flow. In particular,
4501
+ // both arms may be void calls (`flag ? emitA() : emitB()`), for which a
4502
+ // value-shaped void ternary has no IR representation. Preserve the same
4503
+ // narrowing environments as a source if statement, and lower only a
4504
+ // statically selected arm when the condition folded without effects.
4505
+ if (ts.isConditionalExpression(expr)) {
4506
+ const cond = lowerer.lowerCondition(expr.condition);
4507
+ if (cond.kind === "boolLit") {
4508
+ return lowerExprStatement(lowerer, cond.value ? expr.whenTrue : expr.whenFalse);
4509
+ }
4510
+ const then = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, true), () => withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () => [lowerExprStatement(lowerer, expr.whenTrue)]));
4511
+ const else_ = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, false), () => [lowerExprStatement(lowerer, expr.whenFalse)]);
4512
+ return { kind: "if", cond, then, else_, loc: locOf(expr) };
4513
+ }
4134
4514
  // Assignment statements over the no-storage binding families:
4135
4515
  // - `f1 = f2` where the RHS roots at an ambient-undefined name:
4136
4516
  // Node evaluates the RHS first and dies on the root's
@@ -4299,6 +4679,7 @@ export function lowerExprStatement(lowerer, expr) {
4299
4679
  if (ts.isElementAccessExpression(expr.left))
4300
4680
  return lowerer.lowerElementWrite(expr);
4301
4681
  if (ts.isPropertyAccessExpression(expr.left)) {
4682
+ fenceNodeModuleMutation(lowerer, expr.left, "assignment");
4302
4683
  // Native arrays have a writable length property. The runtime grows
4303
4684
  // by creating holes and truncates with RC-aware removal; keep this
4304
4685
  // as a statement operation so invalid lengths can use the normal
@@ -4451,10 +4832,10 @@ export function lowerExprStatement(lowerer, expr) {
4451
4832
  // chain): the dyn keyed write — the value converts INTO dyn
4452
4833
  // (closures box), the runtime sets the member on OBJ receivers
4453
4834
  // and throws Node's TypeErrors on the rest. The receiver is
4454
- // PROBED (probeLower) and claimed exactly when it lowers to
4835
+ // PROBED (tryLowerExpression) and claimed exactly when it lowers to
4455
4836
  // dyn; typed receivers keep their own lowerings and fences.
4456
4837
  if (!expr.left.questionDotToken) {
4457
- const recv = probeLower(lowerer, expr.left.expression);
4838
+ const recv = tryLowerExpression(lowerer, expr.left.expression);
4458
4839
  if (recv && recv.type.kind === "dyn") {
4459
4840
  const loc = locOf(expr);
4460
4841
  const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
@@ -5810,29 +6191,27 @@ export function lowerForOf(lowerer, stmt) {
5810
6191
  // desugars drop them — a labeled jump naming those loops fences at the
5811
6192
  // jump site (no label point exists in their desugared shape).
5812
6193
  const labels = lowerer.takeLabels();
5813
- // `for await` is ASYNC ITERATION, not the shipped async/await. ONE
5814
- // async iterable has a lowering: process.stdin (the piped-input
5815
- // pattern real CLIs use) — see lowerForAwaitStdin. Everything else is
5816
- // named specifically (the bare SC1070 text would claim async/await
5817
- // itself is missing).
6194
+ // `for await` is ASYNC ITERATION, not the shipped async/await. Typed
6195
+ // async generators use the generic request protocol; process.stdin
6196
+ // and readable streams retain their dedicated next-chunk paths.
5818
6197
  if (stmt.awaitModifier) {
5819
- if (ts.isPropertyAccessExpression(stmt.expression) &&
5820
- lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
5821
- return lowerForAwaitStdin(lowerer, stmt);
5822
- }
5823
- // Readable streams (the readableAsyncIterator surface): the mapped
5824
- // receiver class roots at a readable-sided stream class.
5825
6198
  {
5826
- const recvT = lowerer.mapTypeOf(lowerer.typeOf(stmt.expression));
5827
- if (recvT?.kind === "object") {
5828
- const info = lowerer.classes.get(recvT.className);
5829
- const sides = streamSidesOf(lowerer, info);
5830
- if (sides === "r" || sides === "rw") {
5831
- return lowerForAwaitReadable(lowerer, stmt, recvT);
6199
+ const genT = lowerer.mapTypeOf(lowerer.typeOf(stmt.expression));
6200
+ if (genT?.kind === "generator" && genT.async) {
6201
+ const iterable = lowerer.lowerExpr(stmt.expression);
6202
+ if (iterable.type.kind === "generator" && iterable.type.async) {
6203
+ return lowerForAwaitGenerator(lowerer, stmt, iterable, labels);
5832
6204
  }
5833
6205
  }
5834
6206
  }
5835
- lowerer.unsupported("SC1070", stmt, "'for await' (async iteration over anything but process.stdin and readable streams)");
6207
+ if (ts.isPropertyAccessExpression(stmt.expression) &&
6208
+ lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
6209
+ return lowerForAwaitStdin(lowerer, stmt);
6210
+ }
6211
+ const builtin = lowerForAwaitBuiltin(lowerer, stmt);
6212
+ if (builtin)
6213
+ return builtin;
6214
+ lowerer.unsupported("SC1070", stmt, "'for await' over this value (supported: typed async generators, represented class async iterators, process.stdin, and Node/Web readable streams)");
5836
6215
  }
5837
6216
  lowerer.fenceStaticHeadersIteration(stmt.expression);
5838
6217
  // A stored numeric value iterator declared in this function keeps its
@@ -6051,7 +6430,7 @@ export function lowerForOf(lowerer, stmt) {
6051
6430
  elemT = { kind: "union", unionId: lowerer.unions.intern(arms) };
6052
6431
  }
6053
6432
  }
6054
- if (elemT !== null && pureReemittable(iterable)) {
6433
+ if (elemT !== null && isSafeToRepeat(iterable)) {
6055
6434
  const loc = locOf(stmt.expression);
6056
6435
  const shapeId = iterable.type.shapeId;
6057
6436
  const snapshotElem = elemT;
@@ -6161,9 +6540,7 @@ export function lowerForOf(lowerer, stmt) {
6161
6540
  lowerer.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
6162
6541
  }
6163
6542
  const list = stmt.initializer;
6164
- if ((list.flags & ts.NodeFlags.Using) !== 0) {
6165
- lowerer.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
6166
- }
6543
+ const resourceLoop = (list.flags & ts.NodeFlags.Using) !== 0;
6167
6544
  const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || (list.flags & ts.NodeFlags.BlockScoped) === 0;
6168
6545
  const decl = list.declarations[0]; // the grammar allows exactly one
6169
6546
  // `for (const [k, v] of pairs)` — a destructuring loop variable: the
@@ -6216,7 +6593,19 @@ export function lowerForOf(lowerer, stmt) {
6216
6593
  lowerer.runtimeOptionalStorageLocals.add(root);
6217
6594
  }
6218
6595
  const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
6219
- return forValues(local.id, body);
6596
+ if (!resourceLoop)
6597
+ return forValues(local.id, body);
6598
+ const resource = varRef(local.id, local.type, locOf(decl.name));
6599
+ const cleanup = cleanupForValue(lowerer, decl.name, resource, isAwaitUsing(list));
6600
+ return forValues(local.id, [{
6601
+ kind: "tryCatch",
6602
+ tryBody: body,
6603
+ catchBody: null,
6604
+ catchLocalId: null,
6605
+ finallyBody: cleanup,
6606
+ suppressFinallyErrors: true,
6607
+ loc: locOf(decl),
6608
+ }]);
6220
6609
  }
6221
6610
  finally {
6222
6611
  lowerer.scopes.pop();
@@ -7372,112 +7761,6 @@ function lowerForAwaitStdin(lowerer, stmt) {
7372
7761
  lowerer.scopes.pop();
7373
7762
  }
7374
7763
  }
7375
- /** `for await (const chunk of readable)` — the stream async iterator
7376
- * (the stdin desugar's sibling): every pass awaits the runtime's
7377
- * next-chunk promise — buffered content, or a REJECTION carrying the
7378
- * stream's error (the await rethrows it, Node's iterator contract) —
7379
- * and exits on the EOF sentinel. Chunks are Buffers in typed code
7380
- * (encoded streams fence at the runtime entry); in the JS lane the
7381
- * chunk is checked-dynamic and boxes by runtime tag (dyn strings once
7382
- * an encoding applies, dyn undefined as the sentinel — chunks are
7383
- * never undefined). Early exit leaves the stream alive (Node's
7384
- * iterator return() would destroy it — a documented divergence). */
7385
- function lowerForAwaitReadable(lowerer, stmt, recvT) {
7386
- if (!lowerer.ctx.isAsync) {
7387
- lowerer.unsupported("SC1090", stmt, "top-level 'for await' (await outside async functions)");
7388
- }
7389
- if (!ts.isVariableDeclarationList(stmt.initializer)) {
7390
- lowerer.unsupported("SC1090", stmt.initializer, "for-await over a pre-declared variable (declare the loop variable in the loop: for await (const chunk of ...))");
7391
- }
7392
- const list = stmt.initializer;
7393
- const isConst = (list.flags & ts.NodeFlags.Const) !== 0;
7394
- const isLet = (list.flags & ts.NodeFlags.Let) !== 0;
7395
- // `for await (var chunk of ...)`: the chunk binding is per-await
7396
- // machinery (a fresh value each pass, fiber-parked in between) —
7397
- // threading it through a shared hoisted slot has no user yet.
7398
- if (!isConst && !isLet)
7399
- lowerer.unsupported("SC1030", list, "'var' loop bindings in 'for await' (use const)");
7400
- const decl = list.declarations[0];
7401
- if (!ts.isIdentifier(decl.name))
7402
- lowerer.unsupported("SC1031", decl.name);
7403
- const loc = locOf(stmt);
7404
- // The chunk is CHECKED-DYNAMIC in both lanes (the shim types the
7405
- // iterator's element as any): the runtime boxes by tag — Buffers
7406
- // normally, strings once an encoding applies — and the dyn surface
7407
- // carries the common consumptions (.length, [i], toString, +=,
7408
- // typeof). A statically-typed chunk would have to pick one side of
7409
- // the encoding question at compile time.
7410
- const dynLane = true;
7411
- const chunkT = DYN;
7412
- const promiseT = { kind: "promise", inner: chunkT };
7413
- lowerer.scopes.push(new Map());
7414
- try {
7415
- // The receiver evaluates ONCE, before the loop.
7416
- const recvLocal = lowerer.declareHiddenLocal("%faStream", recvT);
7417
- const recvDecl = {
7418
- kind: "varDecl",
7419
- localId: recvLocal.id,
7420
- init: lowerer.lowerExpr(stmt.expression),
7421
- loc,
7422
- };
7423
- const p = lowerer.declareHiddenLocal("%streamNext", promiseT);
7424
- const chunk = lowerer.declareLocal(decl.name, decl.name.text, chunkT, isLet);
7425
- const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement));
7426
- const chunkRef = { kind: "varRef", localId: chunk.id, type: chunkT, loc };
7427
- const eofCond = dynLane
7428
- ? { kind: "dynTest", test: "undefined", value: chunkRef, type: BOOL, loc }
7429
- : {
7430
- kind: "bin",
7431
- op: "===",
7432
- left: { kind: "bytesIntrinsic", method: "length", receiver: chunkRef, args: [], type: F64, loc },
7433
- right: { kind: "numLit", value: 0, type: F64, loc },
7434
- type: BOOL,
7435
- loc,
7436
- };
7437
- const head = [
7438
- {
7439
- kind: "varDecl",
7440
- localId: p.id,
7441
- init: {
7442
- kind: "libCall",
7443
- fn: dynLane ? "readable.nextChunkDyn" : "readable.nextChunk",
7444
- args: [{ kind: "varRef", localId: recvLocal.id, type: recvT, loc }],
7445
- type: promiseT,
7446
- loc,
7447
- },
7448
- loc,
7449
- },
7450
- {
7451
- kind: "varDecl",
7452
- localId: chunk.id,
7453
- init: {
7454
- kind: "awaitExpr",
7455
- value: { kind: "varRef", localId: p.id, type: promiseT, loc },
7456
- type: chunkT,
7457
- loc,
7458
- },
7459
- loc,
7460
- },
7461
- { kind: "if", cond: eofCond, then: [{ kind: "break", loc }], else_: null, loc },
7462
- ];
7463
- return {
7464
- kind: "block",
7465
- body: [
7466
- recvDecl,
7467
- {
7468
- kind: "while",
7469
- cond: { kind: "boolLit", value: true, type: BOOL, loc },
7470
- body: [...head, ...body],
7471
- loc,
7472
- },
7473
- ],
7474
- loc,
7475
- };
7476
- }
7477
- finally {
7478
- lowerer.scopes.pop();
7479
- }
7480
- }
7481
7764
  export function lowerForStatement(lowerer, stmt) {
7482
7765
  const labels = lowerer.takeLabels();
7483
7766
  lowerer.scopes.push(new Map());