@scriptc/compiler 0.2.6 → 0.2.8

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 (588) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +7 -18
  2. package/dist/backend/cycle-analysis.d.ts +21 -9
  3. package/dist/backend/cycle-analysis.js +139 -67
  4. package/dist/backend/cycle-analysis.js.map +1 -1
  5. package/dist/backend/cycle-analysis.test.js +283 -3
  6. package/dist/backend/cycle-analysis.test.js.map +1 -1
  7. package/dist/backend/cycle-mutability.d.ts +39 -0
  8. package/dist/backend/cycle-mutability.js +205 -0
  9. package/dist/backend/cycle-mutability.js.map +1 -0
  10. package/dist/backend/link-plan-core.d.ts +2 -1
  11. package/dist/backend/link-plan-core.js +15 -3
  12. package/dist/backend/link-plan-core.js.map +1 -1
  13. package/dist/backend/link-plan.d.ts +2 -1
  14. package/dist/backend/link-plan.js.map +1 -1
  15. package/dist/backend/llvm/alloc.d.ts +43 -0
  16. package/dist/backend/llvm/alloc.js +246 -0
  17. package/dist/backend/llvm/alloc.js.map +1 -0
  18. package/dist/backend/llvm/alloc.test.d.ts +1 -0
  19. package/dist/backend/llvm/alloc.test.js +102 -0
  20. package/dist/backend/llvm/alloc.test.js.map +1 -0
  21. package/dist/backend/llvm/blocks.d.ts +9 -0
  22. package/dist/backend/llvm/blocks.js +9 -0
  23. package/dist/backend/llvm/blocks.js.map +1 -1
  24. package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
  25. package/dist/backend/llvm/borrowed-receivers.js +39 -0
  26. package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
  27. package/dist/backend/llvm/call-lifetimes.d.ts +18 -3
  28. package/dist/backend/llvm/call-lifetimes.js +54 -19
  29. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  30. package/dist/backend/llvm/call-lifetimes.test.js +178 -3
  31. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  32. package/dist/backend/llvm/checked-narrows.d.ts +24 -0
  33. package/dist/backend/llvm/checked-narrows.js +108 -0
  34. package/dist/backend/llvm/checked-narrows.js.map +1 -0
  35. package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
  36. package/dist/backend/llvm/checked-narrows.test.js +208 -0
  37. package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
  38. package/dist/backend/llvm/classes.d.ts +16 -1
  39. package/dist/backend/llvm/classes.js +60 -49
  40. package/dist/backend/llvm/classes.js.map +1 -1
  41. package/dist/backend/llvm/classes.test.js +68 -6
  42. package/dist/backend/llvm/classes.test.js.map +1 -1
  43. package/dist/backend/llvm/common.d.ts +13 -0
  44. package/dist/backend/llvm/common.js +30 -0
  45. package/dist/backend/llvm/common.js.map +1 -1
  46. package/dist/backend/llvm/constant-callbacks.d.ts +19 -3
  47. package/dist/backend/llvm/constant-callbacks.js +289 -52
  48. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  49. package/dist/backend/llvm/constant-callbacks.test.js +140 -3
  50. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
  51. package/dist/backend/llvm/debug-info.d.ts +6 -2
  52. package/dist/backend/llvm/debug-info.js +11 -1
  53. package/dist/backend/llvm/debug-info.js.map +1 -1
  54. package/dist/backend/llvm/dense-array-access.d.ts +29 -0
  55. package/dist/backend/llvm/dense-array-access.js +335 -0
  56. package/dist/backend/llvm/dense-array-access.js.map +1 -0
  57. package/dist/backend/llvm/dense-array-access.test.d.ts +1 -0
  58. package/dist/backend/llvm/dense-array-access.test.js +238 -0
  59. package/dist/backend/llvm/dense-array-access.test.js.map +1 -0
  60. package/dist/backend/llvm/dyn.d.ts +38 -0
  61. package/dist/backend/llvm/dyn.js +213 -14
  62. package/dist/backend/llvm/dyn.js.map +1 -1
  63. package/dist/backend/llvm/emitter.d.ts +308 -15
  64. package/dist/backend/llvm/emitter.js +1788 -206
  65. package/dist/backend/llvm/emitter.js.map +1 -1
  66. package/dist/backend/llvm/emitter.test.js +89 -7
  67. package/dist/backend/llvm/emitter.test.js.map +1 -1
  68. package/dist/backend/llvm/expr-async.js +62 -8
  69. package/dist/backend/llvm/expr-async.js.map +1 -1
  70. package/dist/backend/llvm/expr-async.test.d.ts +1 -0
  71. package/dist/backend/llvm/expr-async.test.js +62 -0
  72. package/dist/backend/llvm/expr-async.test.js.map +1 -0
  73. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  74. package/dist/backend/llvm/expr-bytes.js +85 -8
  75. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  76. package/dist/backend/llvm/expr-callbacks.js +29 -21
  77. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  78. package/dist/backend/llvm/expr-calls.d.ts +5 -0
  79. package/dist/backend/llvm/expr-calls.js +118 -15
  80. package/dist/backend/llvm/expr-calls.js.map +1 -1
  81. package/dist/backend/llvm/expr-containers.d.ts +7 -1
  82. package/dist/backend/llvm/expr-containers.js +246 -32
  83. package/dist/backend/llvm/expr-containers.js.map +1 -1
  84. package/dist/backend/llvm/expr-control.js +113 -8
  85. package/dist/backend/llvm/expr-control.js.map +1 -1
  86. package/dist/backend/llvm/expr-dispatch.js +2 -0
  87. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  88. package/dist/backend/llvm/expr-dynamic.js +244 -44
  89. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  90. package/dist/backend/llvm/expr-island.js +3 -8
  91. package/dist/backend/llvm/expr-island.js.map +1 -1
  92. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  93. package/dist/backend/llvm/expr-primitives.js +93 -36
  94. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  95. package/dist/backend/llvm/expr-primitives.test.js +51 -3
  96. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  97. package/dist/backend/llvm/expr-records.js +31 -17
  98. package/dist/backend/llvm/expr-records.js.map +1 -1
  99. package/dist/backend/llvm/expr-stream-bridges.js +96 -29
  100. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  101. package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
  102. package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
  103. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  104. package/dist/backend/llvm/integer-remainder.d.ts +11 -0
  105. package/dist/backend/llvm/integer-remainder.js +91 -0
  106. package/dist/backend/llvm/integer-remainder.js.map +1 -1
  107. package/dist/backend/llvm/layouts.d.ts +3 -1
  108. package/dist/backend/llvm/layouts.js +3 -2
  109. package/dist/backend/llvm/layouts.js.map +1 -1
  110. package/dist/backend/llvm/lazy-captures.d.ts +16 -0
  111. package/dist/backend/llvm/lazy-captures.js +96 -0
  112. package/dist/backend/llvm/lazy-captures.js.map +1 -0
  113. package/dist/backend/llvm/lazy-captures.test.d.ts +1 -0
  114. package/dist/backend/llvm/lazy-captures.test.js +78 -0
  115. package/dist/backend/llvm/lazy-captures.test.js.map +1 -0
  116. package/dist/backend/llvm/lib-abi.js +10 -0
  117. package/dist/backend/llvm/lib-abi.js.map +1 -1
  118. package/dist/backend/llvm/lib-filesystem.js +10 -15
  119. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  120. package/dist/backend/llvm/lib-process.js +8 -8
  121. package/dist/backend/llvm/lib-process.js.map +1 -1
  122. package/dist/backend/llvm/lib-shared.js +1 -0
  123. package/dist/backend/llvm/lib-shared.js.map +1 -1
  124. package/dist/backend/llvm/local-array-reads.d.ts +5 -1
  125. package/dist/backend/llvm/local-array-reads.js +147 -26
  126. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  127. package/dist/backend/llvm/local-array-reads.test.js +85 -4
  128. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  129. package/dist/backend/llvm/local-union-storage.d.ts +5 -1
  130. package/dist/backend/llvm/local-union-storage.js +54 -8
  131. package/dist/backend/llvm/local-union-storage.js.map +1 -1
  132. package/dist/backend/llvm/local-union-storage.test.js +94 -6
  133. package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
  134. package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
  135. package/dist/backend/llvm/map-number-lookup.js +62 -0
  136. package/dist/backend/llvm/map-number-lookup.js.map +1 -0
  137. package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
  138. package/dist/backend/llvm/map-read-lifetimes.js +24 -3
  139. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  140. package/dist/backend/llvm/map-read-lifetimes.test.js +39 -20
  141. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  142. package/dist/backend/llvm/nullable-fields.d.ts +28 -0
  143. package/dist/backend/llvm/nullable-fields.js +45 -0
  144. package/dist/backend/llvm/nullable-fields.js.map +1 -0
  145. package/dist/backend/llvm/nullable-fields.test.d.ts +1 -0
  146. package/dist/backend/llvm/nullable-fields.test.js +48 -0
  147. package/dist/backend/llvm/nullable-fields.test.js.map +1 -0
  148. package/dist/backend/llvm/nullable-unions.d.ts +68 -0
  149. package/dist/backend/llvm/nullable-unions.js +132 -0
  150. package/dist/backend/llvm/nullable-unions.js.map +1 -0
  151. package/dist/backend/llvm/publish.d.ts +58 -0
  152. package/dist/backend/llvm/publish.js +286 -0
  153. package/dist/backend/llvm/publish.js.map +1 -0
  154. package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
  155. package/dist/backend/llvm/rebound-parameters.js +72 -0
  156. package/dist/backend/llvm/rebound-parameters.js.map +1 -0
  157. package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
  158. package/dist/backend/llvm/rebound-parameters.test.js +67 -0
  159. package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
  160. package/dist/backend/llvm/reference-effects.d.ts +18 -2
  161. package/dist/backend/llvm/reference-effects.js +106 -16
  162. package/dist/backend/llvm/reference-effects.js.map +1 -1
  163. package/dist/backend/llvm/reference-effects.test.js +52 -1
  164. package/dist/backend/llvm/reference-effects.test.js.map +1 -1
  165. package/dist/backend/llvm/serialized-ir-emission.test.d.ts +1 -0
  166. package/dist/backend/llvm/serialized-ir-emission.test.js +52 -0
  167. package/dist/backend/llvm/serialized-ir-emission.test.js.map +1 -0
  168. package/dist/backend/llvm/shapes.d.ts +104 -9
  169. package/dist/backend/llvm/shapes.js +356 -46
  170. package/dist/backend/llvm/shapes.js.map +1 -1
  171. package/dist/backend/llvm/shapes.test.js +139 -5
  172. package/dist/backend/llvm/shapes.test.js.map +1 -1
  173. package/dist/backend/llvm/stack-callbacks.js +2 -3
  174. package/dist/backend/llvm/stack-callbacks.js.map +1 -1
  175. package/dist/backend/llvm/stack-captures.d.ts +3 -1
  176. package/dist/backend/llvm/stack-captures.js +5 -3
  177. package/dist/backend/llvm/stack-captures.js.map +1 -1
  178. package/dist/backend/llvm/stack-unions.test.js +43 -3
  179. package/dist/backend/llvm/stack-unions.test.js.map +1 -1
  180. package/dist/backend/llvm/string-construction.d.ts +10 -0
  181. package/dist/backend/llvm/string-construction.js +42 -0
  182. package/dist/backend/llvm/string-construction.js.map +1 -1
  183. package/dist/backend/llvm/string-construction.test.js +1 -1
  184. package/dist/backend/llvm/string-key-hash.d.ts +15 -0
  185. package/dist/backend/llvm/string-key-hash.js +76 -0
  186. package/dist/backend/llvm/string-key-hash.js.map +1 -0
  187. package/dist/backend/llvm/string-key-hash.test.d.ts +1 -0
  188. package/dist/backend/llvm/string-key-hash.test.js +45 -0
  189. package/dist/backend/llvm/string-key-hash.test.js.map +1 -0
  190. package/dist/backend/llvm/string-lifetimes.test.js +65 -15
  191. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  192. package/dist/backend/llvm/string-reads.d.ts +7 -0
  193. package/dist/backend/llvm/string-reads.js +76 -0
  194. package/dist/backend/llvm/string-reads.js.map +1 -0
  195. package/dist/backend/llvm/switch-dispatch.js +1 -1
  196. package/dist/backend/llvm/switch-dispatch.js.map +1 -1
  197. package/dist/backend/llvm/switch-dispatch.test.js +3 -3
  198. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  199. package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
  200. package/dist/backend/llvm/test-boxed-unions.js +13 -0
  201. package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
  202. package/dist/backend/llvm/union-repr.d.ts +15 -0
  203. package/dist/backend/llvm/union-repr.js +89 -0
  204. package/dist/backend/llvm/union-repr.js.map +1 -0
  205. package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
  206. package/dist/backend/llvm/virtual-borrows.js +82 -0
  207. package/dist/backend/llvm/virtual-borrows.js.map +1 -0
  208. package/dist/backend/llvm/walk-borrows.d.ts +43 -0
  209. package/dist/backend/llvm/walk-borrows.js +150 -0
  210. package/dist/backend/llvm/walk-borrows.js.map +1 -0
  211. package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
  212. package/dist/backend/llvm/walk-borrows.test.js +119 -0
  213. package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
  214. package/dist/backend/llvm/walkers.d.ts +5 -1
  215. package/dist/backend/llvm/walkers.js +24 -21
  216. package/dist/backend/llvm/walkers.js.map +1 -1
  217. package/dist/backend/may-throw.d.ts +29 -1
  218. package/dist/backend/may-throw.js +349 -3
  219. package/dist/backend/may-throw.js.map +1 -1
  220. package/dist/backend/may-throw.test.js +114 -2
  221. package/dist/backend/may-throw.test.js.map +1 -1
  222. package/dist/backend/native/cache-warm.d.ts +2 -1
  223. package/dist/backend/native/cache-warm.js.map +1 -1
  224. package/dist/backend/native/contracts.d.ts +3 -2
  225. package/dist/backend/native/contracts.js.map +1 -1
  226. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  227. package/dist/backend/native/runtime-inputs.js +2 -0
  228. package/dist/backend/native/runtime-inputs.js.map +1 -1
  229. package/dist/backend/native-codegen-core.d.ts +7 -4
  230. package/dist/backend/native-codegen-core.js +14 -9
  231. package/dist/backend/native-codegen-core.js.map +1 -1
  232. package/dist/backend/native-codegen.d.ts +9 -0
  233. package/dist/backend/native-codegen.js +53 -2
  234. package/dist/backend/native-codegen.js.map +1 -1
  235. package/dist/backend/native-codegen.test.js +64 -10
  236. package/dist/backend/native-codegen.test.js.map +1 -1
  237. package/dist/backend/native-link-info-core.d.ts +3 -2
  238. package/dist/backend/native-link-info-core.js.map +1 -1
  239. package/dist/backend/native-link-info.d.ts +2 -1
  240. package/dist/backend/native-link-info.js.map +1 -1
  241. package/dist/backend/native-link-info.test.js +2 -2
  242. package/dist/backend/native-link-info.test.js.map +1 -1
  243. package/dist/backend/native-tools.d.ts +5 -1
  244. package/dist/backend/native-tools.js +3 -1
  245. package/dist/backend/native-tools.js.map +1 -1
  246. package/dist/backend/optimization.d.ts +25 -0
  247. package/dist/backend/optimization.js +21 -0
  248. package/dist/backend/optimization.js.map +1 -0
  249. package/dist/backend/optimization.test.d.ts +1 -0
  250. package/dist/backend/optimization.test.js +24 -0
  251. package/dist/backend/optimization.test.js.map +1 -0
  252. package/dist/backend/publication.d.ts +28 -0
  253. package/dist/backend/publication.js +155 -0
  254. package/dist/backend/publication.js.map +1 -0
  255. package/dist/backend/publication.test.d.ts +1 -0
  256. package/dist/backend/publication.test.js +72 -0
  257. package/dist/backend/publication.test.js.map +1 -0
  258. package/dist/backend/runtime-abi.d.ts +2 -2
  259. package/dist/backend/runtime-abi.js +2 -2
  260. package/dist/backend/runtime-pack-core.d.ts +15 -4
  261. package/dist/backend/runtime-pack-core.js +23 -12
  262. package/dist/backend/runtime-pack-core.js.map +1 -1
  263. package/dist/backend/runtime-pack-native.d.ts +7 -3
  264. package/dist/backend/runtime-pack-native.js +0 -3
  265. package/dist/backend/runtime-pack-native.js.map +1 -1
  266. package/dist/backend/runtime-pack-targets.test.js +1 -1
  267. package/dist/backend/runtime-pack.d.ts +20 -2
  268. package/dist/backend/runtime-pack.js +23 -0
  269. package/dist/backend/runtime-pack.js.map +1 -1
  270. package/dist/backend/runtime-pack.test.js +115 -2
  271. package/dist/backend/runtime-pack.test.js.map +1 -1
  272. package/dist/backend/target-diagnostics.test.js +1 -1
  273. package/dist/backend/targets.d.ts +2 -1
  274. package/dist/backend/targets.js +1 -1
  275. package/dist/backend/targets.js.map +1 -1
  276. package/dist/cli/command.js +75 -11
  277. package/dist/cli/command.js.map +1 -1
  278. package/dist/cli/host.d.ts +2 -1
  279. package/dist/cli/usage.d.ts +4 -1
  280. package/dist/cli/usage.js +13 -3
  281. package/dist/cli/usage.js.map +1 -1
  282. package/dist/compile-types.d.ts +17 -5
  283. package/dist/coverage/report.d.ts +25 -2
  284. package/dist/coverage/report.js +161 -33
  285. package/dist/coverage/report.js.map +1 -1
  286. package/dist/coverage/verdict-cache.d.ts +63 -0
  287. package/dist/coverage/verdict-cache.js +193 -0
  288. package/dist/coverage/verdict-cache.js.map +1 -0
  289. package/dist/coverage/verdict-cache.test.d.ts +1 -0
  290. package/dist/coverage/verdict-cache.test.js +52 -0
  291. package/dist/coverage/verdict-cache.test.js.map +1 -0
  292. package/dist/coverage/verdict-semantic.d.ts +2 -0
  293. package/dist/coverage/verdict-semantic.js +24 -0
  294. package/dist/coverage/verdict-semantic.js.map +1 -0
  295. package/dist/coverage/verdict-startup.d.ts +15 -0
  296. package/dist/coverage/verdict-startup.js +66 -0
  297. package/dist/coverage/verdict-startup.js.map +1 -0
  298. package/dist/diagnostics/diagnostic.d.ts +29 -1
  299. package/dist/diagnostics/diagnostic.js +84 -12
  300. package/dist/diagnostics/diagnostic.js.map +1 -1
  301. package/dist/diagnostics/envelope.d.ts +130 -0
  302. package/dist/diagnostics/envelope.js +257 -0
  303. package/dist/diagnostics/envelope.js.map +1 -0
  304. package/dist/diagnostics/envelope.test.d.ts +1 -0
  305. package/dist/diagnostics/envelope.test.js +91 -0
  306. package/dist/diagnostics/envelope.test.js.map +1 -0
  307. package/dist/diagnostics/render.d.ts +3 -0
  308. package/dist/diagnostics/render.js +13 -2
  309. package/dist/diagnostics/render.js.map +1 -1
  310. package/dist/executable/executable-cache.d.ts +2 -2
  311. package/dist/executable/executable-cache.js +7 -4
  312. package/dist/executable/executable-cache.js.map +1 -1
  313. package/dist/executable/executable-cache.test.js +20 -0
  314. package/dist/executable/executable-cache.test.js.map +1 -1
  315. package/dist/executable/prepare.d.ts +4 -0
  316. package/dist/executable/prepare.js +7 -1
  317. package/dist/executable/prepare.js.map +1 -1
  318. package/dist/frontend/analysis.js +1 -0
  319. package/dist/frontend/analysis.js.map +1 -1
  320. package/dist/frontend/lowering/abstract-equality.js +5 -49
  321. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  322. package/dist/frontend/lowering/array-values.d.ts +30 -0
  323. package/dist/frontend/lowering/array-values.js +246 -1
  324. package/dist/frontend/lowering/array-values.js.map +1 -1
  325. package/dist/frontend/lowering/builtins/errors.js +17 -2
  326. package/dist/frontend/lowering/builtins/errors.js.map +1 -1
  327. package/dist/frontend/lowering/builtins/numbers.js +29 -1
  328. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  329. package/dist/frontend/lowering/builtins/process.d.ts +10 -17
  330. package/dist/frontend/lowering/builtins/process.js +66 -29
  331. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  332. package/dist/frontend/lowering/builtins/workers.js +4 -2
  333. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  334. package/dist/frontend/lowering/caught-value-dispatch.d.ts +11 -0
  335. package/dist/frontend/lowering/caught-value-dispatch.js +87 -0
  336. package/dist/frontend/lowering/caught-value-dispatch.js.map +1 -0
  337. package/dist/frontend/lowering/class-assertions.d.ts +12 -0
  338. package/dist/frontend/lowering/class-assertions.js +229 -5
  339. package/dist/frontend/lowering/class-assertions.js.map +1 -1
  340. package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
  341. package/dist/frontend/lowering/class-callbacks.js +18 -5
  342. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  343. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +1 -0
  344. package/dist/frontend/lowering/class-dynamic-dispatch.js +12 -6
  345. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  346. package/dist/frontend/lowering/class-method-mutations.d.ts +5 -0
  347. package/dist/frontend/lowering/class-method-mutations.js +472 -0
  348. package/dist/frontend/lowering/class-method-mutations.js.map +1 -0
  349. package/dist/frontend/lowering/class-method-values.js +25 -5
  350. package/dist/frontend/lowering/class-method-values.js.map +1 -1
  351. package/dist/frontend/lowering/class-prototypes.js +8 -2
  352. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  353. package/dist/frontend/lowering/class-unions.d.ts +16 -0
  354. package/dist/frontend/lowering/class-unions.js +127 -0
  355. package/dist/frontend/lowering/class-unions.js.map +1 -0
  356. package/dist/frontend/lowering/coercions/builders.js +8 -1
  357. package/dist/frontend/lowering/coercions/builders.js.map +1 -1
  358. package/dist/frontend/lowering/coercions/function-adapters.js +2 -0
  359. package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
  360. package/dist/frontend/lowering/coercions/identity.d.ts +7 -0
  361. package/dist/frontend/lowering/coercions/identity.js +26 -0
  362. package/dist/frontend/lowering/coercions/identity.js.map +1 -0
  363. package/dist/frontend/lowering/coercions/state.d.ts +6 -0
  364. package/dist/frontend/lowering/coercions/state.js +2 -0
  365. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  366. package/dist/frontend/lowering/coercions/structural.js +9 -6
  367. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  368. package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
  369. package/dist/frontend/lowering/coercions/unions.js +67 -2
  370. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  371. package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
  372. package/dist/frontend/lowering/collation-repertoire.js +19 -0
  373. package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
  374. package/dist/frontend/lowering/collection-narrowing.d.ts +6 -0
  375. package/dist/frontend/lowering/collection-narrowing.js +31 -0
  376. package/dist/frontend/lowering/collection-narrowing.js.map +1 -0
  377. package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
  378. package/dist/frontend/lowering/construction-escapes.js +146 -0
  379. package/dist/frontend/lowering/construction-escapes.js.map +1 -0
  380. package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
  381. package/dist/frontend/lowering/construction-escapes.test.js +150 -0
  382. package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
  383. package/dist/frontend/lowering/containers/array-callback-call.d.ts +7 -0
  384. package/dist/frontend/lowering/containers/array-callback-call.js +52 -0
  385. package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -0
  386. package/dist/frontend/lowering/containers/array-callbacks.d.ts +1 -1
  387. package/dist/frontend/lowering/containers/array-callbacks.js +416 -65
  388. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  389. package/dist/frontend/lowering/containers/array-concat.d.ts +7 -0
  390. package/dist/frontend/lowering/containers/array-concat.js +130 -0
  391. package/dist/frontend/lowering/containers/array-concat.js.map +1 -0
  392. package/dist/frontend/lowering/containers/array-construction.d.ts +2 -0
  393. package/dist/frontend/lowering/containers/array-construction.js +110 -5
  394. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  395. package/dist/frontend/lowering/containers/array-methods.d.ts +1 -11
  396. package/dist/frontend/lowering/containers/array-methods.js +274 -144
  397. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  398. package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
  399. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  400. package/dist/frontend/lowering/containers/collection-methods.js +10 -1
  401. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  402. package/dist/frontend/lowering/containers/indexed-objects.js +56 -70
  403. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  404. package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
  405. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  406. package/dist/frontend/lowering/cycle-initialization.d.ts +46 -0
  407. package/dist/frontend/lowering/cycle-initialization.js +303 -0
  408. package/dist/frontend/lowering/cycle-initialization.js.map +1 -0
  409. package/dist/frontend/lowering/divergences.d.ts +9 -0
  410. package/dist/frontend/lowering/divergences.js +630 -0
  411. package/dist/frontend/lowering/divergences.js.map +1 -0
  412. package/dist/frontend/lowering/expressions/evaluation-safety.js +4 -0
  413. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
  414. package/dist/frontend/lowering/expressions/object-literals.js +178 -34
  415. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  416. package/dist/frontend/lowering/expressions/union-field-write.d.ts +4 -4
  417. package/dist/frontend/lowering/expressions/union-field-write.js +54 -44
  418. package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
  419. package/dist/frontend/lowering/iterator-adapters.d.ts +20 -0
  420. package/dist/frontend/lowering/iterator-adapters.js +605 -0
  421. package/dist/frontend/lowering/iterator-adapters.js.map +1 -0
  422. package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
  423. package/dist/frontend/lowering/lower-array-sort.js +44 -5
  424. package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
  425. package/dist/frontend/lowering/lower-array-sort.test.d.ts +1 -0
  426. package/dist/frontend/lowering/lower-array-sort.test.js +31 -0
  427. package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -0
  428. package/dist/frontend/lowering/lower-assert.js +7 -0
  429. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  430. package/dist/frontend/lowering/lower-builtin-values.js +1 -0
  431. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  432. package/dist/frontend/lowering/lower-calls.d.ts +8 -3
  433. package/dist/frontend/lowering/lower-calls.js +388 -278
  434. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  435. package/dist/frontend/lowering/lower-classes.d.ts +6 -0
  436. package/dist/frontend/lowering/lower-classes.js +117 -65
  437. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  438. package/dist/frontend/lowering/lower-exprs.d.ts +22 -2
  439. package/dist/frontend/lowering/lower-exprs.js +811 -254
  440. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  441. package/dist/frontend/lowering/lower-functions.js +32 -19
  442. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  443. package/dist/frontend/lowering/lower-generators.d.ts +19 -13
  444. package/dist/frontend/lowering/lower-generators.js +67 -44
  445. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  446. package/dist/frontend/lowering/lower-inspect.js +29 -4
  447. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  448. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  449. package/dist/frontend/lowering/lower-island.js +24 -7
  450. package/dist/frontend/lowering/lower-island.js.map +1 -1
  451. package/dist/frontend/lowering/lower-modules.js +100 -3
  452. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  453. package/dist/frontend/lowering/lower-stmts.js +82 -64
  454. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  455. package/dist/frontend/lowering/lowerer.d.ts +115 -2
  456. package/dist/frontend/lowering/lowerer.js +1266 -88
  457. package/dist/frontend/lowering/lowerer.js.map +1 -1
  458. package/dist/frontend/lowering/native-ffi.js +3 -1
  459. package/dist/frontend/lowering/native-ffi.js.map +1 -1
  460. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
  461. package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
  462. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
  463. package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
  464. package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
  465. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
  466. package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
  467. package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
  468. package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
  469. package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
  470. package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
  471. package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
  472. package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
  473. package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
  474. package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
  475. package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
  476. package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
  477. package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
  478. package/dist/frontend/lowering/runtime-optional-provenance.d.ts +16 -0
  479. package/dist/frontend/lowering/runtime-optional-provenance.js +41 -0
  480. package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -0
  481. package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
  482. package/dist/frontend/lowering/strict-equality.d.ts +9 -0
  483. package/dist/frontend/lowering/strict-equality.js +116 -0
  484. package/dist/frontend/lowering/strict-equality.js.map +1 -0
  485. package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
  486. package/dist/frontend/lowering/string-index-bounds.js +537 -0
  487. package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
  488. package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
  489. package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
  490. package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
  491. package/dist/frontend/lowering/surfaces.js +3 -0
  492. package/dist/frontend/lowering/surfaces.js.map +1 -1
  493. package/dist/frontend/lowering/union-comparison.d.ts +5 -0
  494. package/dist/frontend/lowering/union-comparison.js +47 -0
  495. package/dist/frontend/lowering/union-comparison.js.map +1 -0
  496. package/dist/frontend/lowering/union-narrow.d.ts +2 -0
  497. package/dist/frontend/lowering/union-narrow.js +10 -6
  498. package/dist/frontend/lowering/union-narrow.js.map +1 -1
  499. package/dist/frontend/program-analysis.d.ts +10 -0
  500. package/dist/frontend/program-analysis.js +7 -0
  501. package/dist/frontend/program-analysis.js.map +1 -1
  502. package/dist/frontend/program.d.ts +63 -2
  503. package/dist/frontend/program.js +1038 -61
  504. package/dist/frontend/program.js.map +1 -1
  505. package/dist/frontend/program.test.js +122 -6
  506. package/dist/frontend/program.test.js.map +1 -1
  507. package/dist/frontend/resolve.d.ts +6 -0
  508. package/dist/frontend/resolve.js +14 -0
  509. package/dist/frontend/resolve.js.map +1 -1
  510. package/dist/frontend/threads-import.d.ts +5 -0
  511. package/dist/frontend/threads-import.js +21 -0
  512. package/dist/frontend/threads-import.js.map +1 -0
  513. package/dist/frontend/type-mapper.d.ts +4 -1
  514. package/dist/frontend/type-mapper.js +161 -20
  515. package/dist/frontend/type-mapper.js.map +1 -1
  516. package/dist/frontend/type-mapper.test.js +15 -3
  517. package/dist/frontend/type-mapper.test.js.map +1 -1
  518. package/dist/frontend/worker-target.d.ts +4 -0
  519. package/dist/frontend/worker-target.js +8 -1
  520. package/dist/frontend/worker-target.js.map +1 -1
  521. package/dist/index.d.ts +6 -1
  522. package/dist/index.js +70 -16
  523. package/dist/index.js.map +1 -1
  524. package/dist/ir/analysis.d.ts +3 -1
  525. package/dist/ir/analysis.js +4 -0
  526. package/dist/ir/analysis.js.map +1 -1
  527. package/dist/ir/build.d.ts +5 -0
  528. package/dist/ir/build.js +7 -0
  529. package/dist/ir/build.js.map +1 -1
  530. package/dist/ir/builtin-signatures.d.ts +1 -1
  531. package/dist/ir/builtin-signatures.js +5 -3
  532. package/dist/ir/builtin-signatures.js.map +1 -1
  533. package/dist/ir/constant-tables.test.js +1 -1
  534. package/dist/ir/initializer-bindings.test.js +1 -1
  535. package/dist/ir/int32-slots.d.ts +72 -0
  536. package/dist/ir/int32-slots.js +837 -0
  537. package/dist/ir/int32-slots.js.map +1 -0
  538. package/dist/ir/int32-slots.test.d.ts +1 -0
  539. package/dist/ir/int32-slots.test.js +222 -0
  540. package/dist/ir/int32-slots.test.js.map +1 -0
  541. package/dist/ir/integer-ranges.d.ts +19 -3
  542. package/dist/ir/integer-ranges.js +168 -23
  543. package/dist/ir/integer-ranges.js.map +1 -1
  544. package/dist/ir/integer-ranges.test.js +74 -2
  545. package/dist/ir/integer-ranges.test.js.map +1 -1
  546. package/dist/ir/ir.d.ts +101 -20
  547. package/dist/ir/ir.js +20 -6
  548. package/dist/ir/ir.js.map +1 -1
  549. package/dist/ir/scalar-records.test.js +1 -1
  550. package/dist/ir/serialize.d.ts +1 -1
  551. package/dist/ir/serialize.js +3 -3
  552. package/dist/ir/serialize.js.map +1 -1
  553. package/dist/ir/traverse.js +8 -0
  554. package/dist/ir/traverse.js.map +1 -1
  555. package/dist/ir/validate.js +139 -22
  556. package/dist/ir/validate.js.map +1 -1
  557. package/dist/ir/validate.test.js +308 -8
  558. package/dist/ir/validate.test.js.map +1 -1
  559. package/dist/library/fence-eval.test.js +1 -1
  560. package/dist/library/int-infer.test.js +4 -4
  561. package/dist/library/library-cache.test.js +5 -5
  562. package/dist/library/library-profile.d.ts +2 -1
  563. package/dist/library/library-profile.js +4 -3
  564. package/dist/library/library-profile.js.map +1 -1
  565. package/dist/native/cache.d.ts +3 -0
  566. package/dist/native/cache.js +49 -1
  567. package/dist/native/cache.js.map +1 -1
  568. package/dist/native/cache.test.js +21 -1
  569. package/dist/native/cache.test.js.map +1 -1
  570. package/dist/native/cli.js +1 -1
  571. package/dist/native/cli.js.map +1 -1
  572. package/dist/native/compiler.d.ts +5 -0
  573. package/dist/native/compiler.js +51 -4
  574. package/dist/native/compiler.js.map +1 -1
  575. package/dist/native/compiler.test.js +2 -2
  576. package/dist/native/library.d.ts +2 -1
  577. package/dist/native/library.js.map +1 -1
  578. package/dist/native/prepare.d.ts +4 -0
  579. package/dist/native/prepare.js +28 -3
  580. package/dist/native/prepare.js.map +1 -1
  581. package/dist/native/sanitizer.d.ts +3 -2
  582. package/dist/native/sanitizer.js +3 -13
  583. package/dist/native/sanitizer.js.map +1 -1
  584. package/dist/startup-cache.d.ts +2 -0
  585. package/dist/startup-cache.js +2 -0
  586. package/dist/startup-cache.js.map +1 -1
  587. package/package.json +20 -20
  588. package/surface-manifest.json +8 -2
@@ -1,14 +1,28 @@
1
+ import { checkCycleBindingAccesses, refuseUncheckedCycleReads, } from "./cycle-initialization.js";
2
+ import { identityPreservingWidening } from "./coercions/identity.js";
3
+ import { scanDivergences } from "./divergences.js";
4
+ import { checkedClassAssertion } from "./class-assertions.js";
5
+ import { narrowStoredClassValue, narrowGenericClassValue } from "./class-unions.js";
6
+ import { isOptionalProcessStreamProperty } from "./builtins/process.js";
7
+ import { concreteCollectionNarrow } from "./collection-narrowing.js";
1
8
  import { lowerWorkerMetadata } from "./builtins/workers.js";
2
9
  import { dynUndefinedExpr, nodeThrowExpr, numLit, varRef } from "../../ir/build.js";
10
+ import { iteratorSlotAdapter } from "./iterator-adapters.js";
3
11
  import { lowerBuiltinCall } from "./builtin-calls.js";
4
12
  import { CoercionState } from "./coercions/state.js";
5
13
  import { widthLiftPlan, unitOnlyElem, recordWidthPlan, describeRecordWidthBlocker, objToRecordPlan, recordToClassPlan, } from "./coercions/structural-plans.js";
6
14
  import { widthCoerce, applyWidthLift, unionRecordWidthHelper, recordWidthHelper, tupleArrayWidthHelper, emptyArrayLiftHelper, arrayWidthHelper, objRecordWidthHelper, recordClassWidthHelper, classStaticsProjection, } from "./coercions/structural.js";
7
15
  import { funcReturnWidthAdapter, coercibleValue, cleanFuncAdaptable, funcCoerceAdapter, spawnResFnAdapterPlan, spawnResFnAdapter, } from "./coercions/function-adapters.js";
8
- import { unionRetagMappable, narrowedRetagHelper, strandedUnitTrap, strandedCoercionTrap, recordUnionWrapHelper, unionRetagHelper, narrowedArmHelper, deferredReadHelper, } from "./coercions/unions.js";
16
+ import { unionRetagMappable, narrowedRetagHelper, strandedUnitTrap, strandedCoercionTrap, recordUnionWrapHelper, unionRetagHelper, narrowedArmHelper, checkedNarrowCall, checkedNarrowOperand, deferredReadHelper, } from "./coercions/unions.js";
9
17
  import { jsvalLiftable, jsvalLiftExpr, unionToJsvalHelper, recordToJsvalHelper, arrayToJsvalHelper, arrayToJsvalArrayHelper, } from "./coercions/island.js";
10
18
  import { lowerUtilTypeValue, pathModuleValue, utilTypesModuleValue, } from "./lower-builtin-values.js";
11
19
  import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
20
+ import { RuntimeOptionalProvenance } from "./runtime-optional-provenance.js";
21
+ import { presentAtUse, reassignedBindings } from "./runtime-optional-guards.js";
22
+ import { ArrayElementStates } from "./runtime-optional-elements.js";
23
+ import { indexReadInBounds } from "./runtime-optional-bounds.js";
24
+ import { ArrayOwnership } from "./runtime-optional-ownership.js";
25
+ import { StringIndexBounds } from "./string-index-bounds.js";
12
26
  import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
13
27
  import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
14
28
  import { UnregisteredClassTypes } from "./unregistered-class-types.js";
@@ -44,7 +58,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
44
58
  import { arrayOf, BOOL, canAdaptDynFuncTo, canDynCheckTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, DYN_HANDLE_KINDS, F64, isDynTypedRefType, isJsonSafeType, isJsonStringifySafeType, isPrimitiveCollectionKey, isSupportedMapKey, isSupportedMapValue, isUndefinedArmedUnion, isUnitType, JSVAL, NULL_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID, } from "../../ir/ir.js";
45
59
  import { provenanceActive } from "../provenance-registry.js";
46
60
  import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
47
- import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
61
+ import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
48
62
  import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
49
63
  import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
50
64
  import { prepareCjsModuleGraph } from "./lower-node-module.js";
@@ -189,6 +203,39 @@ export function stmtUsesIsland(stmts) {
189
203
  return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
190
204
  return !everyStmtChild(stmts, expr, nested);
191
205
  }
206
+ /** Statements a never-lowered body would have contributed to the coverage
207
+ * totals: every element of a statement list (blocks, case clauses), plus
208
+ * the single-statement bodies of control flow, nested functions included —
209
+ * the same accounting lowerStmts applies to bodies that do lower. */
210
+ export function countBodyStatements(root) {
211
+ let n = 0;
212
+ const visit = (node) => {
213
+ if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
214
+ n += node.statements.length;
215
+ }
216
+ else if ((ts.isIfStatement(node) ||
217
+ ts.isForStatement(node) ||
218
+ ts.isForInStatement(node) ||
219
+ ts.isForOfStatement(node) ||
220
+ ts.isWhileStatement(node) ||
221
+ ts.isDoStatement(node) ||
222
+ ts.isLabeledStatement(node)) &&
223
+ node.parent !== undefined) {
224
+ const bodies = ts.isIfStatement(node)
225
+ ? [node.thenStatement, node.elseStatement]
226
+ : ts.isLabeledStatement(node)
227
+ ? [node.statement]
228
+ : [node.statement];
229
+ for (const body of bodies) {
230
+ if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
231
+ n++;
232
+ }
233
+ }
234
+ };
235
+ // Iterative: absurdly deep nesting must not overflow the stack here.
236
+ ts.walkPreorder(root, visit);
237
+ return n;
238
+ }
192
239
  function directExternalTypeSpecifiersByFile(externalTypes) {
193
240
  const out = new Map();
194
241
  for (const [specifier, file] of externalTypes) {
@@ -341,6 +388,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
341
388
  resultLowerer = emit;
342
389
  timing("historical-order-relower");
343
390
  }
391
+ // Divergence warnings over the program's own modules: computed once,
392
+ // after lowering, from the same checker (never part of a failure).
393
+ const divergences = scanDivergences(resultLowerer, moduleOrder);
394
+ timing("divergence-scan", { found: divergences.length });
395
+ if (divergences.length > 0)
396
+ result = { ...result, divergences };
344
397
  if (options.coverage !== true)
345
398
  return result;
346
399
  const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
@@ -358,7 +411,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
358
411
  externalTypeSpecifiersByFile,
359
412
  });
360
413
  const rem = remainder.run();
361
- return { ...result, unreached: { diagnostics: rem.diagnostics, stats: rem.stats } };
414
+ // Unreached JS statements whose fences deferred to runtime count as
415
+ // failed statements in the remainder's stats, so their diagnostics must
416
+ // travel with them: a failed statement is never left unexplained.
417
+ return {
418
+ ...result,
419
+ unreached: {
420
+ diagnostics: rem.diagnostics,
421
+ stats: rem.stats,
422
+ ...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
423
+ },
424
+ };
362
425
  }
363
426
  /** The island-handle type a `import(...)` initializer gives a binding
364
427
  * whose DECLARED type has no static mapping (`Promise<typeof
@@ -874,6 +937,17 @@ export function jsFuncNameOf(node, lowerer) {
874
937
  }
875
938
  return null;
876
939
  }
940
+ /** Answers `fallback` when a checker query panics inside the type checker. */
941
+ function panicSafe(query, fallback) {
942
+ try {
943
+ return query();
944
+ }
945
+ catch (e) {
946
+ if (isCheckerPanic(e))
947
+ return fallback;
948
+ throw e;
949
+ }
950
+ }
877
951
  export class Lowerer {
878
952
  program;
879
953
  entry;
@@ -902,6 +976,14 @@ export class Lowerer {
902
976
  /** Per-symbol result of the never-reassigned file scan
903
977
  * (bindingNeverReassigned — object-literal generic-method receivers). */
904
978
  neverReassignedCache = new Map();
979
+ stringIndexBoundsCache = null;
980
+ /** Proofs that a string element read names an existing code unit. The
981
+ * indexed-read analysis and expression lowering share one instance, so a
982
+ * read's representation and its callers' signatures always agree. */
983
+ get stringIndexBounds() {
984
+ this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
985
+ return this.stringIndexBoundsCache;
986
+ }
905
987
  /** Syntactic writes indexed once per JS scope. Symbol and RHS type
906
988
  * checks remain contextual to each binding probe. */
907
989
  jsBindingWritesByOwner = new Map();
@@ -1148,6 +1230,11 @@ export class Lowerer {
1148
1230
  * prepass. This includes explicit annotations: unchecked TS annotations do
1149
1231
  * not prove that an array property read produced a value. */
1150
1232
  runtimeOptionalBindingTypes = new Map();
1233
+ /** Bindings that receive an unchecked indexed read (or a value derived
1234
+ * from one) through an initializer or assignment. The checker narrows a
1235
+ * declared optional binding to that read's bare type, so presence tests
1236
+ * on these bindings consult the stored tag instead. */
1237
+ runtimeOptionalAssignedSymbols = new Set();
1151
1238
  /** Concise callback/lambda returns promoted by the HOF callback prepass;
1152
1239
  * arrows have no declaration symbol to key in fnSigsBySymbol. */
1153
1240
  runtimeOptionalFunctionReturns = new Map();
@@ -1165,6 +1252,17 @@ export class Lowerer {
1165
1252
  * every name destructured from a present source optional. */
1166
1253
  runtimeOptionalPatternTypes = new Map();
1167
1254
  runtimeOptionalReduceTypes = new Map();
1255
+ /** Non-null assertions whose value is forwarded into a slot promoted to
1256
+ * hold undefined. `x!` has no runtime effect, so these keep the stored
1257
+ * undefined state instead of trapping before any dereference. */
1258
+ runtimeOptionalAssertions = new Set();
1259
+ /** Array callback calls whose receiver the optional-read analysis proved
1260
+ * never holds an explicit undefined (or a hole, for the find family):
1261
+ * their callbacks take the element type itself. */
1262
+ presentElementCalls = new Set();
1263
+ /** for-of loops over arrays that can hold neither holes nor undefined:
1264
+ * each pass reads the element payload directly. */
1265
+ presentElementLoops = new Set();
1168
1266
  /** Capture entries and their origin share one mutable box. Normalize each
1169
1267
  * entry to the origin so writes and flow proofs stay synchronized. */
1170
1268
  runtimeOptionalRoots = new Map();
@@ -1239,6 +1337,9 @@ export class Lowerer {
1239
1337
  * optional-read analysis, so ordinary assertions keep their generic
1240
1338
  * checked-narrow behavior. */
1241
1339
  runtimeOptionalSourceValue(node, value) {
1340
+ const slot = this.deferredFieldSlot(value);
1341
+ if (slot)
1342
+ return slot;
1242
1343
  let origin = node;
1243
1344
  while (ts.isParenthesizedExpression(origin))
1244
1345
  origin = origin.expression;
@@ -1273,12 +1374,9 @@ export class Lowerer {
1273
1374
  value.value.type.kind === "dyn")
1274
1375
  return value.value;
1275
1376
  let source = value;
1276
- if (value.kind === "call" &&
1277
- this.coercions.checkedNarrows.has(value.callee) &&
1278
- value.args.length === 1 &&
1279
- value.args[0]?.type.kind === "union") {
1280
- source = value.args[0];
1281
- }
1377
+ const narrowed = checkedNarrowOperand(this, value);
1378
+ if (narrowed?.type.kind === "union")
1379
+ source = narrowed;
1282
1380
  return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
1283
1381
  ? source
1284
1382
  : null;
@@ -1358,7 +1456,16 @@ export class Lowerer {
1358
1456
  runtimeOptionalWidening(actual, expected) {
1359
1457
  if (actual.kind !== "union" || this.armTag(actual.unionId, UNDEFINED_T) < 0)
1360
1458
  return null;
1361
- return typeEquals(this.stripUndefinedArm(actual), expected) ? actual : null;
1459
+ const present = this.stripUndefinedArm(actual);
1460
+ if (typeEquals(present, expected))
1461
+ return actual;
1462
+ // Only a bare expected arm gains runtime-optional storage. A slot that
1463
+ // already admits undefined keeps its own checked representation.
1464
+ if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0)
1465
+ return null;
1466
+ return identityPreservingWidening(this, present, expected)
1467
+ ? this.runtimeOptionalType(expected)
1468
+ : null;
1362
1469
  }
1363
1470
  runtimeOptionalType(t) {
1364
1471
  if (t.kind === "union")
@@ -1382,6 +1489,41 @@ export class Lowerer {
1382
1489
  runtimeOptionalFunctionReturnType(node, fallback) {
1383
1490
  return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
1384
1491
  }
1492
+ /** True when `actual` is `declared` with some record fields (directly,
1493
+ * in nested records, or in an array's element records) widened to runtime-optional unions:
1494
+ * fields filled from unchecked indexed reads the checker calls present.
1495
+ * Inferred bindings and destructured parameters keep that layout instead
1496
+ * of narrowing it away. */
1497
+ isRuntimeOptionalRecordVariant(actual, declared) {
1498
+ if (actual.kind === "array" && declared.kind === "array")
1499
+ return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
1500
+ if (actual.kind !== "record" || declared.kind !== "record")
1501
+ return false;
1502
+ if (actual.shapeId === declared.shapeId)
1503
+ return false;
1504
+ const a = this.shapes.get(actual.shapeId);
1505
+ const d = this.shapes.get(declared.shapeId);
1506
+ if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
1507
+ return false;
1508
+ if (a.indexValue || d.indexValue)
1509
+ return false;
1510
+ let widened = false;
1511
+ for (const field of d.fields) {
1512
+ const source = a.fields.find((f) => f.name === field.name);
1513
+ if (!source)
1514
+ return false;
1515
+ if (typeEquals(source.type, field.type))
1516
+ continue;
1517
+ if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
1518
+ (this.runtimeOptionalWidening(source.type, field.type) !== null ||
1519
+ this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
1520
+ widened = true;
1521
+ continue;
1522
+ }
1523
+ return false;
1524
+ }
1525
+ return widened;
1526
+ }
1385
1527
  /** Promote one field of a record storage shape to the value's runtime
1386
1528
  * type. Shapes are immutable by identity, so this interns a sibling shape
1387
1529
  * and records the field as runtime-optional for property-read narrowing. */
@@ -1427,6 +1569,8 @@ export class Lowerer {
1427
1569
  /** True while re-lowering a base function's 2nd+ instance: the same source
1428
1570
  * statements were already counted for the first instance. */
1429
1571
  suppressStats = false;
1572
+ /** Comparisons interned by operand representations and equality semantics. */
1573
+ equalityHelpers = new Map();
1430
1574
  /** Synthetic array-HOF loop functions (map/filter/forEach desugar),
1431
1575
  * interned per method + element/callback-result type: key → fn name. */
1432
1576
  arrHofHelpers = new Map();
@@ -1518,6 +1662,10 @@ export class Lowerer {
1518
1662
  * node carrying the ?. token is marked handled so the receiver-typed
1519
1663
  * lowerings stop declining it (chainBlocked). */
1520
1664
  chainRecvByNode = new Map();
1665
+ /** Lowered void calls whose checked type is `never` (they throw or exit).
1666
+ * TypeScript lets such a call stand where any value is expected;
1667
+ * coerceToExpected turns them into diverging sequences. */
1668
+ neverValued = new Set();
1521
1669
  chainNarrowedType = new Map();
1522
1670
  chainHandled = new Set();
1523
1671
  /** for-of-over-matchAll bindings whose `.index` reads the companion-index
@@ -1549,6 +1697,8 @@ export class Lowerer {
1549
1697
  /** Named method slots observed before class collection need a callable
1550
1698
  * ABI and shared prototype descriptors instead of call-site specialization. */
1551
1699
  prototypeMethodAccesses = new Map();
1700
+ /** Field names whose complete source census stores only lexical arrows. */
1701
+ receiverFreeCallbackFields = new Set();
1552
1702
  /** Inferred JS methods participating in an override chain keep a vtable
1553
1703
  * ABI instead of call-site specialization. Filled before class collection. */
1554
1704
  virtualJsMethods = new Set();
@@ -1674,6 +1824,9 @@ export class Lowerer {
1674
1824
  * and therefore becomes the cycle's evaluation root. Dynamic imports
1675
1825
  * wait on this shared verdict rather than a build-time-selected member. */
1676
1826
  asyncCyclePromiseOf = new Map();
1827
+ /** Modules whose evaluation an import() can request again after it threw,
1828
+ * computed on first use (lower-modules.ts importRetryModules). */
1829
+ importRetryModules = null;
1677
1830
  /** Record-shape interner: canonical (name-sorted) field list → shapeId.
1678
1831
  * Threaded into every mapType call; its `shapes` array becomes
1679
1832
  * IrModule.records. */
@@ -1726,6 +1879,10 @@ export class Lowerer {
1726
1879
  * function called above the declaration statement reads that, never a
1727
1880
  * NULL slot. Filled by collectGlobals. */
1728
1881
  varGlobalEntryInits = new Map();
1882
+ /** Import-cycle bindings with an initialization flag, by global id, and
1883
+ * the top-level statements declaring them (cycle-initialization.ts). */
1884
+ cycleInitFlags = new Map();
1885
+ cycleInitDeclarations = new Map();
1729
1886
  get ctx() {
1730
1887
  const top = this.fnStack[this.fnStack.length - 1];
1731
1888
  if (!top)
@@ -1766,6 +1923,13 @@ export class Lowerer {
1766
1923
  * coverage remainder as alreadyFlushed). */
1767
1924
  flushedSymbols = new Set();
1768
1925
  alreadyFlushed;
1926
+ /** Deferred diagnostics a flush handed to a capture buffer (diagSink)
1927
+ * instead of the build, keyed by the copy in that buffer. A buffer can
1928
+ * be discarded (a declined probe, a collection that succeeded) or
1929
+ * re-deferred under another declaration, so the declaration stays
1930
+ * deferred until one of these copies reaches `diags` (pushDiag commits
1931
+ * it); until then a later flush reports the diagnostics again. */
1932
+ pendingFlushes = new Map();
1769
1933
  /** The build's target platform ("win32" | "darwin" | "linux" | ...):
1770
1934
  * selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
1771
1935
  * builtinModuleConstOf in surfaces.ts). */
@@ -1944,6 +2108,12 @@ export class Lowerer {
1944
2108
  this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
1945
2109
  return this.classProtocols.usesCheckedIdentity(type);
1946
2110
  }
2111
+ /** Declared class fields may store a DYN value only when its static type
2112
+ * names checked protocol identity or an owned runtime handle (a Worker
2113
+ * or MessagePort); `unknown` and `any` fields stay refused. */
2114
+ classFieldStoresDyn(type) {
2115
+ return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
2116
+ }
1947
2117
  /** The nominal identity of a checker module-namespace type. Program
1948
2118
  * modules use their normalized absolute source name; supported builtins
1949
2119
  * use the lowering tables' canonical bare name. */
@@ -2874,9 +3044,13 @@ export class Lowerer {
2874
3044
  * but it is not a runtime proof; promoting only the affected slots keeps
2875
3045
  * the rest of the dense ABI unchanged. */
2876
3046
  analyzeRuntimeOptionalArrayReads(parts) {
3047
+ const provenance = RuntimeOptionalProvenance.fromEnvironment();
3048
+ // The site whose value the current promotion follows (debug only).
3049
+ let provenanceSite;
2877
3050
  const optionalSymbols = new Set();
2878
3051
  const optionalReturns = new Set();
2879
3052
  const arithmeticReturns = new Map();
3053
+ const primitiveBindings = new Map();
2880
3054
  const optionalParams = new Map();
2881
3055
  const optionalFields = new Map();
2882
3056
  const dynamicObjectEntryRows = new Set();
@@ -2903,6 +3077,17 @@ export class Lowerer {
2903
3077
  (projection?.kind === "declared-function" ? projection.signature : null));
2904
3078
  };
2905
3079
  const functionDeclBySymbol = new Map();
3080
+ const staticFieldsBySymbol = new Map();
3081
+ for (const info of this.classes.values()) {
3082
+ for (const field of info.staticFields) {
3083
+ const declaration = field.initializer.parent;
3084
+ if (!ts.isPropertyDeclaration(declaration))
3085
+ continue;
3086
+ const symbol = symbolOf(declaration.name);
3087
+ if (symbol)
3088
+ staticFieldsBySymbol.set(symbol, field);
3089
+ }
3090
+ }
2906
3091
  const signatureBySymbol = new Map();
2907
3092
  const methodFamilies = new Map();
2908
3093
  const familyBySymbol = new Map();
@@ -2999,11 +3184,76 @@ export class Lowerer {
2999
3184
  }
3000
3185
  return false;
3001
3186
  };
3002
- const isArrayRead = (node) => {
3187
+ // `parent!` passed to a call or bound to a name forwards the value
3188
+ // without reading it: a callee that never dereferences it observes
3189
+ // undefined, as in JavaScript. Only a class value with one undefined
3190
+ // arm qualifies; element reads keep the established bounds trap.
3191
+ const forwardedAssertion = (node) => {
3192
+ let e = node;
3193
+ while (ts.isParenthesizedExpression(e))
3194
+ e = e.expression;
3195
+ if (!ts.isNonNullExpression(e))
3196
+ return null;
3197
+ let operand = e.expression;
3198
+ while (ts.isParenthesizedExpression(operand))
3199
+ operand = operand.expression;
3200
+ if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
3201
+ return null;
3202
+ // A guard that already proves the binding present makes the
3203
+ // assertion true at runtime: nothing is forwarded as undefined.
3204
+ if (ts.isIdentifier(operand)) {
3205
+ const symbol = symbolOf(operand);
3206
+ if (symbol && provenPresent(operand, symbol))
3207
+ return null;
3208
+ }
3209
+ const stored = this.mapTypeOf(this.typeOf(e.expression));
3210
+ const asserted = this.mapTypeOf(this.typeOf(e));
3211
+ if (stored?.kind !== "union" || asserted?.kind !== "object")
3212
+ return null;
3213
+ const arms = this.unions.get(stored.unionId)?.arms;
3214
+ if (arms?.length !== 2 ||
3215
+ this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
3216
+ !arms.some((arm) => typeEquals(arm, asserted)))
3217
+ return null;
3218
+ return e;
3219
+ };
3220
+ // `decode(xs[i]!)`: a missing element is passed as undefined in
3221
+ // JavaScript, and the callee may never read it.
3222
+ const forwardedElementAssertion = (node) => {
3223
+ let e = node;
3224
+ while (ts.isParenthesizedExpression(e))
3225
+ e = e.expression;
3226
+ if (!ts.isNonNullExpression(e))
3227
+ return null;
3228
+ let read = e.expression;
3229
+ while (ts.isParenthesizedExpression(read))
3230
+ read = read.expression;
3231
+ if (!ts.isElementAccessExpression(read))
3232
+ return null;
3233
+ if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
3234
+ return null;
3235
+ // An in-bounds read of an array that never holds undefined or holes
3236
+ // always produces a present element.
3237
+ if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
3238
+ return null;
3239
+ const element = this.mapTypeOf(this.typeOf(e));
3240
+ if (!element ||
3241
+ element.kind === "void" ||
3242
+ element.kind === "dyn" ||
3243
+ element.kind === "jsval" ||
3244
+ element.kind === "generator")
3245
+ return null;
3246
+ return e;
3247
+ };
3248
+ const isIndexedRead = (node) => {
3003
3249
  const e = peel(node);
3004
3250
  if (!ts.isElementAccessExpression(e))
3005
3251
  return false;
3006
- return this.mapTypeOf(this.typeOf(e.expression))?.kind === "array";
3252
+ const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
3253
+ if (kind === "string")
3254
+ return !this.stringIndexBounds.inBounds(e);
3255
+ // Typed-array reads answer undefined for an invalid index too.
3256
+ return kind === "array" || kind === "bytes";
3007
3257
  };
3008
3258
  const isDynamicObjectEntryRead = (node) => {
3009
3259
  const read = peel(node);
@@ -3031,6 +3281,37 @@ export class Lowerer {
3031
3281
  return this.withUndefinedArm(t);
3032
3282
  };
3033
3283
  const fieldName = (node) => node.name.text;
3284
+ // The precision facts below ask about nodes ordinary lowering may never
3285
+ // query. A checker panic on one of them answers null, and each caller
3286
+ // falls back to its conservative fact.
3287
+ const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
3288
+ const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
3289
+ // Presence proofs only hold for a binding that keeps its one value for
3290
+ // its whole lifetime.
3291
+ const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
3292
+ const presenceProofs = new Map();
3293
+ const provenPresent = (use, symbol) => {
3294
+ if (reassigned.has(symbol))
3295
+ return false;
3296
+ const cached = presenceProofs.get(use);
3297
+ if (cached !== undefined)
3298
+ return cached;
3299
+ const proven = presentAtUse(use, {
3300
+ sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
3301
+ isUndefined: (node) => node.text === "undefined" &&
3302
+ ((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
3303
+ });
3304
+ presenceProofs.set(use, proven);
3305
+ return proven;
3306
+ };
3307
+ // Bindings of a for-of loop over an array that can hold holes or
3308
+ // undefined. Lowering keeps such a binding's runtime union, so storing
3309
+ // it into another array can store undefined.
3310
+ const iterationSymbols = new Set();
3311
+ // The subset whose iterated array's own facts show a hole or undefined.
3312
+ // Such a binding reads `T | undefined`, so a typed parameter, local or
3313
+ // field it flows into widens like any other missing element read.
3314
+ const holeyIterationSymbols = new Set();
3034
3315
  const mayBeOptional = (node) => {
3035
3316
  // `xs[i]!` is the explicit proven-present form. Its array read keeps
3036
3317
  // the established dense bounds trap and must not promote the enclosing
@@ -3038,7 +3319,9 @@ export class Lowerer {
3038
3319
  if (explicitlyNonNull(node))
3039
3320
  return false;
3040
3321
  const e = peel(node);
3041
- if (isArrayRead(e))
3322
+ if (isIndexedRead(e))
3323
+ return true;
3324
+ if (ts.isPropertyAccessExpression(e) && isOptionalProcessStreamProperty(this, e))
3042
3325
  return true;
3043
3326
  if (ts.isCallExpression(e) && this.runtimeOptionalReduceTypes.has(e))
3044
3327
  return true;
@@ -3056,7 +3339,14 @@ export class Lowerer {
3056
3339
  return true;
3057
3340
  if (ts.isIdentifier(e)) {
3058
3341
  const symbol = symbolOf(e);
3059
- return symbol !== null && optionalSymbols.has(symbol);
3342
+ return (symbol !== null &&
3343
+ (optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
3344
+ !provenPresent(e, symbol));
3345
+ }
3346
+ if (ts.isPropertyAccessExpression(e)) {
3347
+ const symbol = symbolOf(e.name);
3348
+ if (symbol && optionalSymbols.has(symbol))
3349
+ return true;
3060
3350
  }
3061
3351
  if (ts.isCallExpression(e) && ts.isIdentifier(e.expression)) {
3062
3352
  const symbol = callableSymbolOf(e.expression);
@@ -3085,11 +3375,31 @@ export class Lowerer {
3085
3375
  }
3086
3376
  return false;
3087
3377
  };
3088
- const optionalStringArithmeticType = (node) => {
3378
+ const optionalPrimitiveResultType = (node) => {
3089
3379
  const e = peel(node);
3090
- if (!ts.isBinaryExpression(e) || e.operatorToken.kind !== ts.SyntaxKind.PlusToken)
3380
+ if (ts.isIdentifier(e)) {
3381
+ const symbol = symbolOf(e);
3382
+ return symbol ? (primitiveBindings.get(symbol) ?? null) : null;
3383
+ }
3384
+ if (ts.isCallExpression(e)) {
3385
+ const symbol = callableSymbolOf(e.expression);
3386
+ return symbol ? (arithmeticReturns.get(symbol) ?? null) : null;
3387
+ }
3388
+ if (!ts.isBinaryExpression(e))
3389
+ return null;
3390
+ if (e.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken && mayBeOptional(e.left)) {
3391
+ const left = this.mapTypeOf(this.typeOf(e.left));
3392
+ const right = this.mapTypeOf(this.typeOf(e.right));
3393
+ const primitive = (t) => t?.kind === "f64" || t?.kind === "string" || t?.kind === "bool";
3394
+ if (primitive(left) && primitive(right) && !typeEquals(left, right)) {
3395
+ const arms = [left, right].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
3396
+ return { kind: "union", unionId: this.unions.intern(arms) };
3397
+ }
3398
+ return null;
3399
+ }
3400
+ if (e.operatorToken.kind !== ts.SyntaxKind.PlusToken)
3091
3401
  return null;
3092
- const stringArrayRead = (part) => {
3402
+ const stringRead = (part) => {
3093
3403
  const p = peel(part);
3094
3404
  if (!ts.isElementAccessExpression(p))
3095
3405
  return false;
@@ -3097,16 +3407,17 @@ export class Lowerer {
3097
3407
  if (t?.kind !== "string")
3098
3408
  return false;
3099
3409
  const recv = this.mapTypeOf(this.typeOf(p.expression));
3410
+ if (recv?.kind === "string")
3411
+ return !this.stringIndexBounds.inBounds(p);
3100
3412
  return recv?.kind === "array" && recv.elem.kind === "string";
3101
3413
  };
3102
3414
  const primitive = (part) => {
3103
3415
  const t = this.mapTypeOf(this.typeOf(part));
3104
3416
  return t?.kind === "f64" || t?.kind === "string";
3105
3417
  };
3106
- if (!stringArrayRead(e.left) && !stringArrayRead(e.right))
3418
+ if (!stringRead(e.left) && !stringRead(e.right))
3107
3419
  return null;
3108
- if (!(primitive(e.left) || stringArrayRead(e.left)) ||
3109
- !(primitive(e.right) || stringArrayRead(e.right)))
3420
+ if (!primitive(e.left) || !primitive(e.right))
3110
3421
  return null;
3111
3422
  return { kind: "union", unionId: this.unions.intern([F64, STRING]) };
3112
3423
  };
@@ -3125,6 +3436,43 @@ export class Lowerer {
3125
3436
  optionalFields.set(symbol, set);
3126
3437
  return set.size !== before;
3127
3438
  };
3439
+ // Runtime-optional record fields travel with the record value: a copy,
3440
+ // a spread or a parameter receives the source's optional fields.
3441
+ const fieldsOfValue = (node) => {
3442
+ const e = peel(node);
3443
+ if (!ts.isIdentifier(e))
3444
+ return undefined;
3445
+ const symbol = symbolOf(e);
3446
+ return symbol === null ? undefined : optionalFields.get(symbol);
3447
+ };
3448
+ const noteFieldsFrom = (symbol, source) => {
3449
+ const fields = fieldsOfValue(source);
3450
+ if (fields === undefined)
3451
+ return false;
3452
+ let changed = false;
3453
+ for (const field of fields)
3454
+ if (noteFieldSymbol(symbol, field))
3455
+ changed = true;
3456
+ return changed;
3457
+ };
3458
+ /** Optional fields an object literal takes from its spreads, minus
3459
+ * the properties a later member writes explicitly. */
3460
+ const spreadFieldsOf = (object) => {
3461
+ const out = new Set();
3462
+ for (const prop of object.properties) {
3463
+ if (ts.isSpreadAssignment(prop)) {
3464
+ const fields = fieldsOfValue(prop.expression);
3465
+ if (fields !== undefined)
3466
+ for (const field of fields)
3467
+ out.add(field);
3468
+ }
3469
+ else if (prop.name !== undefined &&
3470
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
3471
+ out.delete(prop.name.text);
3472
+ }
3473
+ }
3474
+ return out;
3475
+ };
3128
3476
  const scanPattern = (name, init) => {
3129
3477
  if (!ts.isArrayBindingPattern(name))
3130
3478
  return false;
@@ -3139,6 +3487,7 @@ export class Lowerer {
3139
3487
  const symbol = symbolOf(el.name);
3140
3488
  if (symbol && !el.initializer && !optionalSymbols.has(symbol)) {
3141
3489
  optionalSymbols.add(symbol);
3490
+ provenance?.note("pattern-binding", el.name, init);
3142
3491
  // Indexed flow facts can survive a mutating method call in the
3143
3492
  // checker. The array's element ABI describes every value a
3144
3493
  // position may hold after that call, so bind from that type.
@@ -3282,6 +3631,7 @@ export class Lowerer {
3282
3631
  const widened = this.runtimeOptionalType(current);
3283
3632
  const previous = this.runtimeOptionalBindingTypes.get(symbol);
3284
3633
  this.runtimeOptionalBindingTypes.set(symbol, widened);
3634
+ provenance?.note("callback-parameter", parameter.name, provenanceSite);
3285
3635
  if (!previous || !typeEquals(previous, widened))
3286
3636
  changed = true;
3287
3637
  if (!optionalSymbols.has(symbol)) {
@@ -3293,6 +3643,7 @@ export class Lowerer {
3293
3643
  const fnType = this.mapTypeOf(this.typeOf(fn));
3294
3644
  if (fnType?.kind === "func" && bodyReturnsOptional(fn)) {
3295
3645
  const widenedReturn = this.runtimeOptionalType(fnType.ret);
3646
+ provenance?.note("callback-return", fn, provenanceSite);
3296
3647
  const previousReturn = this.runtimeOptionalFunctionReturnType(fn, fnType.ret);
3297
3648
  this.runtimeOptionalFunctionReturns.set(fn, widenedReturn);
3298
3649
  if (!typeEquals(previousReturn, widenedReturn))
@@ -3388,6 +3739,70 @@ export class Lowerer {
3388
3739
  }
3389
3740
  return changed;
3390
3741
  };
3742
+ // A reduce callback over present elements keeps the reduce helper's
3743
+ // optional element ABI for its parameter storage, so the helper still
3744
+ // splits each visit into a plain-element call. Its value is never
3745
+ // absent, so nothing it flows into is widened.
3746
+ const retypeInlineCallback = (callback, parameterIndices) => {
3747
+ const fn = peel(callback);
3748
+ if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
3749
+ return false;
3750
+ let changed = false;
3751
+ for (const parameterIndex of parameterIndices) {
3752
+ const parameter = fn.parameters[parameterIndex];
3753
+ if (!parameter)
3754
+ continue;
3755
+ const checkerType = this.typeOf(parameter.name);
3756
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3757
+ continue;
3758
+ const current = this.mapTypeOf(checkerType);
3759
+ if (!current)
3760
+ continue;
3761
+ const widened = this.runtimeOptionalType(current);
3762
+ if (!ts.isIdentifier(parameter.name)) {
3763
+ const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
3764
+ this.runtimeOptionalPatternTypes.set(parameter.name, widened);
3765
+ if (!previous || !typeEquals(previous, widened))
3766
+ changed = true;
3767
+ continue;
3768
+ }
3769
+ const symbol = symbolOf(parameter.name);
3770
+ if (!symbol)
3771
+ continue;
3772
+ const previous = this.runtimeOptionalBindingTypes.get(symbol);
3773
+ this.runtimeOptionalBindingTypes.set(symbol, widened);
3774
+ if (!previous || !typeEquals(previous, widened))
3775
+ changed = true;
3776
+ }
3777
+ return changed;
3778
+ };
3779
+ // A present-element helper passes the stored element itself, so the
3780
+ // callback must accept that exact value through a mechanical adapter.
3781
+ // A parameter spelled as a structural supertype (`(r: { pos: number })`
3782
+ // over a wider record) only converts through the optional element
3783
+ // ABI's union retag; keep that site on the optional visit. Unannotated
3784
+ // inline parameters take the element's contextual type, and generic
3785
+ // parameters specialize later, so neither restricts the site.
3786
+ const callbackTakesPresentElement = (callback, elem, index) => {
3787
+ const fn = peel(callback);
3788
+ let param;
3789
+ if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
3790
+ const parameter = fn.parameters[index];
3791
+ if (!parameter?.type || parameter.dotDotDotToken)
3792
+ return true;
3793
+ const checkerType = this.typeOf(parameter.name);
3794
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3795
+ return true;
3796
+ param = this.mapTypeOf(checkerType);
3797
+ }
3798
+ else {
3799
+ const value = this.mapTypeOf(this.typeOf(fn));
3800
+ if (value?.kind !== "func" || value.rest === true)
3801
+ return true;
3802
+ param = value.params[index] ?? null;
3803
+ }
3804
+ return param === null || this.coercibleValue(elem, param);
3805
+ };
3391
3806
  const callbackReturnsOptional = (callback, seen = new Set()) => {
3392
3807
  const node = peel(callback);
3393
3808
  if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
@@ -3405,6 +3820,253 @@ export class Lowerer {
3405
3820
  !!decl.initializer &&
3406
3821
  callbackReturnsOptional(decl.initializer, seen));
3407
3822
  };
3823
+ // Element ABIs whose own type admits undefined (`(T | undefined)[]`,
3824
+ // or a rest array widened for an absent argument).
3825
+ const optionalElementKeys = new Set();
3826
+ // Checker answers for the fixed point's repeated element-ABI queries.
3827
+ // Only a binding's widened storage can change between passes.
3828
+ const checkerTypes = new Map();
3829
+ const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
3830
+ const arrayKeysUnsafe = (node) => {
3831
+ let checkerType = checkerTypes.get(node);
3832
+ if (checkerType === undefined) {
3833
+ checkerType = this.typeOf(node);
3834
+ checkerTypes.set(node, checkerType);
3835
+ }
3836
+ const e = peel(node);
3837
+ // A binding the prepass widened stores its widened array ABI.
3838
+ const widened = ts.isIdentifier(e)
3839
+ ? (() => {
3840
+ const symbol = symbolOf(e);
3841
+ return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
3842
+ })()
3843
+ : undefined;
3844
+ let mapped = widened ?? this.mapTypeOf(checkerType);
3845
+ if (mapped?.kind === "promise")
3846
+ mapped = mapped.inner;
3847
+ const anyElements = mapped?.kind === "array"
3848
+ ? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
3849
+ : mapped === null &&
3850
+ this.checker.isArrayType(checkerType) &&
3851
+ !this.checker.isTupleType(checkerType) &&
3852
+ ((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
3853
+ (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
3854
+ 0;
3855
+ if (anyElements) {
3856
+ // `new Array(n)` has an `any` element type; its storage takes the
3857
+ // ABI of the slot it initializes.
3858
+ const contextual = this.checker.getContextualType(node);
3859
+ const target = contextual ? this.mapTypeOf(contextual) : null;
3860
+ return target?.kind === "array" ? [typeKey(target.elem)] : [];
3861
+ }
3862
+ if (mapped === null) {
3863
+ // An array whose element type is a type parameter: each
3864
+ // instantiation picks its own element ABI.
3865
+ return this.checker.isArrayType(checkerType) ? null : [];
3866
+ }
3867
+ const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
3868
+ const keys = [];
3869
+ for (const arm of arms) {
3870
+ if (arm.kind !== "array")
3871
+ continue;
3872
+ const key = typeKey(arm.elem);
3873
+ if (arm.elem.kind === "union"
3874
+ ? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
3875
+ : isUnitType(arm.elem) || arm.elem.kind === "void")
3876
+ optionalElementKeys.add(key);
3877
+ keys.push(key);
3878
+ }
3879
+ return keys;
3880
+ };
3881
+ const ownership = new ArrayOwnership(sourceFiles, {
3882
+ symbolOf,
3883
+ declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
3884
+ typeOf: typeOrNull,
3885
+ instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
3886
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
3887
+ isArray: (node) => {
3888
+ const type = typeOrNull(node);
3889
+ return type !== null && this.checker.isArrayType(type);
3890
+ },
3891
+ fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
3892
+ dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
3893
+ });
3894
+ let dynamicViews = null;
3895
+ // Element ABIs of the native arrays that an `any` or `unknown` value is
3896
+ // converted to, including arrays nested in the converted value.
3897
+ const collectDynamicViews = () => {
3898
+ const keys = new Set();
3899
+ const dynamicValue = (node) => {
3900
+ // A binding's declared type says whether it holds dynamic data; a
3901
+ // flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
3902
+ const e = peel(node);
3903
+ const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
3904
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
3905
+ const declared = declaration &&
3906
+ (ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
3907
+ ts.isIdentifier(declaration.name)
3908
+ ? declaration.name
3909
+ : null;
3910
+ const type = typeOrNull(declared ?? node);
3911
+ if (type === null)
3912
+ return true;
3913
+ if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
3914
+ return true;
3915
+ if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
3916
+ return false;
3917
+ const element = this.checker.getTypeArguments(type)[0];
3918
+ return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
3919
+ };
3920
+ const collect = (type, depth) => {
3921
+ if (!type || depth > 4)
3922
+ return;
3923
+ if (type.kind === "array") {
3924
+ keys.add(typeKey(type.elem));
3925
+ collect(type.elem, depth + 1);
3926
+ }
3927
+ else if (type.kind === "union") {
3928
+ for (const arm of this.unions.get(type.unionId)?.arms ?? [])
3929
+ collect(arm, depth + 1);
3930
+ }
3931
+ else if (type.kind === "record") {
3932
+ for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
3933
+ collect(field.type, depth + 1);
3934
+ }
3935
+ else if (type.kind === "promise")
3936
+ collect(type.inner, depth + 1);
3937
+ };
3938
+ const target = (type) => {
3939
+ // An unanswerable target may be any array.
3940
+ if (type === null)
3941
+ keys.add("*");
3942
+ else if (type)
3943
+ collect(this.mapTypeOf(type), 0);
3944
+ };
3945
+ const visit = (node) => {
3946
+ if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
3947
+ target(typeOrNull(node));
3948
+ else if (ts.isVariableDeclaration(node) &&
3949
+ node.type &&
3950
+ node.initializer &&
3951
+ dynamicValue(node.initializer))
3952
+ target(typeOrNull(node.name));
3953
+ else if (ts.isBinaryExpression(node) &&
3954
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
3955
+ dynamicValue(node.right))
3956
+ target(typeOrNull(node.left));
3957
+ else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
3958
+ target(contextualOrNull(node.expression));
3959
+ else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
3960
+ for (const arg of node.arguments ?? [])
3961
+ if (!ts.isSpreadElement(arg) && dynamicValue(arg))
3962
+ target(contextualOrNull(arg));
3963
+ }
3964
+ else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
3965
+ target(contextualOrNull(node.initializer));
3966
+ else if (ts.isArrayLiteralExpression(node)) {
3967
+ for (const element of node.elements)
3968
+ if (!ts.isSpreadElement(element) &&
3969
+ !ts.isOmittedExpression(element) &&
3970
+ dynamicValue(element))
3971
+ target(contextualOrNull(element));
3972
+ }
3973
+ else if (ts.isArrowFunction(node) &&
3974
+ !ts.isBlock(node.body) &&
3975
+ dynamicValue(node.body))
3976
+ target(contextualOrNull(node.body));
3977
+ };
3978
+ for (const sf of sourceFiles)
3979
+ ts.walkPreorder(sf, (node) => {
3980
+ // A checker panic hides this conversion's target: any array.
3981
+ if (!panicSafe(() => (visit(node), true), false))
3982
+ keys.add("*");
3983
+ return undefined;
3984
+ });
3985
+ return keys;
3986
+ };
3987
+ const elementStates = new ArrayElementStates({
3988
+ elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
3989
+ ownerOf: (node) => ownership.ownerOf(node),
3990
+ allocationOwner: (node) => ownership.allocationOwner(node),
3991
+ arrayKeys: arrayKeysOf,
3992
+ mayBeUndefined: (node) => {
3993
+ if (explicitlyNonNull(node))
3994
+ return false;
3995
+ if (mayBeOptional(node))
3996
+ return true;
3997
+ // `undefined as unknown as T` keeps the undefined value.
3998
+ for (let inner = node; ts.isParenthesizedExpression(inner) ||
3999
+ ts.isAsExpression(inner) ||
4000
+ ts.isTypeAssertion(inner) ||
4001
+ ts.isSatisfiesExpression(inner);) {
4002
+ inner = inner.expression;
4003
+ const type = typeOrNull(inner);
4004
+ if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
4005
+ return true;
4006
+ }
4007
+ const e = peel(node);
4008
+ if (!ts.isIdentifier(e))
4009
+ return false;
4010
+ const symbol = symbolOf(e);
4011
+ return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
4012
+ },
4013
+ callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
4014
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
4015
+ isString: (node) => {
4016
+ const type = typeOrNull(node);
4017
+ return type !== null && this.mapTypeOf(type)?.kind === "string";
4018
+ },
4019
+ isArrayLikeObject: (node) => {
4020
+ if (ts.isObjectLiteralExpression(peel(node)))
4021
+ return true;
4022
+ const type = typeOrNull(node);
4023
+ const mapped = type === null ? null : this.mapTypeOf(type);
4024
+ return (mapped === null ||
4025
+ mapped.kind === "record" ||
4026
+ mapped.kind === "object" ||
4027
+ mapped.kind === "dyn" ||
4028
+ mapped.kind === "jsval" ||
4029
+ mapped.kind === "union");
4030
+ },
4031
+ restKeys: (call) => {
4032
+ const callee = peel(call.expression);
4033
+ if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
4034
+ return null;
4035
+ const symbol = callableSymbolOf(callee);
4036
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4037
+ if (!sig)
4038
+ return ts.isPropertyAccessExpression(callee) ? [] : null;
4039
+ const rest = sig.params.find((shape) => shape.mode === "rest");
4040
+ return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
4041
+ },
4042
+ markIterationBinding: (node, known) => {
4043
+ const symbol = symbolOf(node);
4044
+ if (!symbol)
4045
+ return false;
4046
+ let changed = false;
4047
+ if (!iterationSymbols.has(symbol)) {
4048
+ iterationSymbols.add(symbol);
4049
+ changed = true;
4050
+ }
4051
+ if (known && !holeyIterationSymbols.has(symbol)) {
4052
+ holeyIterationSymbols.add(symbol);
4053
+ // `for (name of xs)` assigns an existing binding, whose own
4054
+ // storage must then hold the loop's undefined too.
4055
+ if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
4056
+ optionalSymbols.add(symbol);
4057
+ changed = true;
4058
+ }
4059
+ return changed;
4060
+ },
4061
+ note: provenance
4062
+ ? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
4063
+ : null,
4064
+ });
4065
+ // Each pass revisits every array callback site; the last visit holds
4066
+ // the decision at the fixed point.
4067
+ const presentSites = new Map();
4068
+ // The same for each for-of loop over an array.
4069
+ const presentLoops = new Map();
3408
4070
  const scanSites = new Map();
3409
4071
  const sitesOf = (sf) => {
3410
4072
  const cached = scanSites.get(sf);
@@ -3414,7 +4076,14 @@ export class Lowerer {
3414
4076
  ts.walkPreorder(sf, (node) => {
3415
4077
  if (ts.isVariableDeclaration(node) ||
3416
4078
  ts.isBinaryExpression(node) ||
3417
- ts.isCallExpression(node))
4079
+ ts.isCallExpression(node) ||
4080
+ ts.isNewExpression(node) ||
4081
+ ts.isArrayLiteralExpression(node) ||
4082
+ ts.isDeleteExpression(node) ||
4083
+ ts.isForOfStatement(node) ||
4084
+ ts.isArrayBindingPattern(node) ||
4085
+ ts.isPrefixUnaryExpression(node) ||
4086
+ ts.isPostfixUnaryExpression(node))
3418
4087
  nodes.push(node);
3419
4088
  });
3420
4089
  scanSites.set(sf, nodes);
@@ -3423,8 +4092,22 @@ export class Lowerer {
3423
4092
  const scanFile = (sf) => {
3424
4093
  let changed = false;
3425
4094
  const scan = (node) => {
4095
+ if (elementStates.scan(node))
4096
+ changed = true;
4097
+ if (ts.isForOfStatement(node))
4098
+ presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
3426
4099
  if (ts.isVariableDeclaration(node)) {
3427
4100
  if (node.initializer) {
4101
+ const result = optionalPrimitiveResultType(node.initializer);
4102
+ const symbol = ts.isIdentifier(node.name) ? symbolOf(node.name) : null;
4103
+ if (result?.kind === "union" && symbol) {
4104
+ const before = primitiveBindings.get(symbol);
4105
+ if (!before || !typeEquals(before, result)) {
4106
+ primitiveBindings.set(symbol, result);
4107
+ this.runtimeOptionalBindingTypes.set(symbol, result);
4108
+ changed = true;
4109
+ }
4110
+ }
3428
4111
  if (ts.isIdentifier(node.name) && isDynamicObjectEntryRead(node.initializer)) {
3429
4112
  const symbol = symbolOf(node.name);
3430
4113
  if (symbol && !dynamicObjectEntryRows.has(symbol)) {
@@ -3436,6 +4119,17 @@ export class Lowerer {
3436
4119
  const symbol = symbolOf(node.name);
3437
4120
  if (symbol && !optionalSymbols.has(symbol)) {
3438
4121
  optionalSymbols.add(symbol);
4122
+ provenance?.note("binding", node.name, node.initializer);
4123
+ changed = true;
4124
+ }
4125
+ }
4126
+ else if (ts.isIdentifier(node.name)) {
4127
+ const assertion = forwardedAssertion(node.initializer);
4128
+ const symbol = assertion ? symbolOf(node.name) : null;
4129
+ if (assertion && symbol && !optionalSymbols.has(symbol)) {
4130
+ optionalSymbols.add(symbol);
4131
+ this.runtimeOptionalAssertions.add(assertion);
4132
+ provenance?.note("asserted-binding", node.name, node.initializer);
3439
4133
  changed = true;
3440
4134
  }
3441
4135
  }
@@ -3456,6 +4150,8 @@ export class Lowerer {
3456
4150
  const aliasNew = !optionalReturns.has(symbol);
3457
4151
  if (aliasNew)
3458
4152
  optionalReturns.add(symbol);
4153
+ if (aliasNew)
4154
+ provenance?.note("function-alias", node.name, node.initializer);
3459
4155
  if (aliasNew)
3460
4156
  changed = true;
3461
4157
  const valueType = this.mapTypeOf(this.typeOf(node.name));
@@ -3478,6 +4174,22 @@ export class Lowerer {
3478
4174
  }
3479
4175
  if (scanPattern(node.name, node.initializer))
3480
4176
  changed = true;
4177
+ if (ts.isIdentifier(node.name)) {
4178
+ const symbol = symbolOf(node.name);
4179
+ const init = peel(node.initializer);
4180
+ if (symbol && noteFieldsFrom(symbol, init)) {
4181
+ provenance?.note("record-copy", node.name, node.initializer);
4182
+ changed = true;
4183
+ }
4184
+ if (symbol && ts.isObjectLiteralExpression(init)) {
4185
+ for (const field of spreadFieldsOf(init)) {
4186
+ if (noteFieldSymbol(symbol, field)) {
4187
+ provenance?.note("record-spread", node.name, node.initializer);
4188
+ changed = true;
4189
+ }
4190
+ }
4191
+ }
4192
+ }
3481
4193
  if (ts.isIdentifier(node.name) &&
3482
4194
  ts.isObjectLiteralExpression(peel(node.initializer))) {
3483
4195
  const object = peel(node.initializer);
@@ -3491,12 +4203,44 @@ export class Lowerer {
3491
4203
  const value = ts.isPropertyAssignment(prop)
3492
4204
  ? prop.initializer
3493
4205
  : prop.name;
3494
- if (mayBeOptional(value) && noteField(node, name.text))
4206
+ if (mayBeOptional(value) && noteField(node, name.text)) {
4207
+ provenance?.note("literal-field", name, value);
3495
4208
  changed = true;
4209
+ }
3496
4210
  }
3497
4211
  }
3498
4212
  }
3499
4213
  }
4214
+ if (ts.isBinaryExpression(node) &&
4215
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4216
+ ts.isIdentifier(node.left)) {
4217
+ const symbol = symbolOf(node.left);
4218
+ if (symbol && noteFieldsFrom(symbol, node.right)) {
4219
+ provenance?.note("record-copy", node.left, node.right);
4220
+ changed = true;
4221
+ }
4222
+ }
4223
+ if (ts.isBinaryExpression(node) &&
4224
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4225
+ ts.isIdentifier(node.left)) {
4226
+ const assertion = forwardedAssertion(node.right);
4227
+ const symbol = assertion ? symbolOf(node.left) : null;
4228
+ // Only a variable's storage is promoted (see the final pass).
4229
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
4230
+ if (assertion &&
4231
+ symbol &&
4232
+ declaration &&
4233
+ ts.isVariableDeclaration(declaration) &&
4234
+ ts.isIdentifier(declaration.name) &&
4235
+ !this.runtimeOptionalAssertions.has(assertion)) {
4236
+ this.runtimeOptionalAssertions.add(assertion);
4237
+ if (!optionalSymbols.has(symbol)) {
4238
+ optionalSymbols.add(symbol);
4239
+ provenance?.note("asserted-binding", node.left, node.right);
4240
+ }
4241
+ changed = true;
4242
+ }
4243
+ }
3500
4244
  if (ts.isBinaryExpression(node) &&
3501
4245
  (node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
3502
4246
  node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
@@ -3507,14 +4251,24 @@ export class Lowerer {
3507
4251
  const symbol = symbolOf(node.left);
3508
4252
  if (symbol && !optionalSymbols.has(symbol)) {
3509
4253
  optionalSymbols.add(symbol);
4254
+ provenance?.note("assigned-binding", node.left, node.right);
3510
4255
  changed = true;
3511
4256
  }
3512
4257
  }
3513
- else if (ts.isPropertyAccessExpression(node.left) &&
3514
- ts.isIdentifier(node.left.expression)) {
3515
- const symbol = symbolOf(node.left.expression);
3516
- if (symbol && noteFieldSymbol(symbol, node.left.name.text))
4258
+ else if (ts.isPropertyAccessExpression(node.left)) {
4259
+ const field = symbolOf(node.left.name);
4260
+ if (field && staticFieldsBySymbol.has(field) && !optionalSymbols.has(field)) {
4261
+ optionalSymbols.add(field);
4262
+ provenance?.note("static-field", node.left, node.right);
3517
4263
  changed = true;
4264
+ }
4265
+ else if (ts.isIdentifier(node.left.expression)) {
4266
+ const symbol = symbolOf(node.left.expression);
4267
+ if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
4268
+ provenance?.note("assigned-field", node.left, node.right);
4269
+ changed = true;
4270
+ }
4271
+ }
3518
4272
  }
3519
4273
  }
3520
4274
  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
@@ -3555,11 +4309,26 @@ export class Lowerer {
3555
4309
  }
3556
4310
  }
3557
4311
  if (nativeArrayReceiver) {
3558
- if (promoteHofCallback(callback, callbackIndices))
3559
- changed = true;
4312
+ provenanceSite = node;
3560
4313
  const method = node.expression.name.text;
4314
+ // Callbacks skip holes (except the find family), so an array
4315
+ // that never holds undefined only passes present elements.
4316
+ const reduce = method === "reduce" || method === "reduceRight";
4317
+ const elementsPresent = receiver?.kind === "array" &&
4318
+ elementStates.elementsPresent(receiverNode, method) &&
4319
+ callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
4320
+ // A callback returning a runtime-absent value still needs its
4321
+ // widened return, whatever its elements.
4322
+ if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
4323
+ changed = true;
4324
+ // Element-typed callback helpers cover every method but
4325
+ // reduce, whose accumulator keeps the split visit.
4326
+ presentSites.set(node, elementsPresent && !reduce);
4327
+ if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
4328
+ changed = true;
3561
4329
  if ((method === "reduce" || method === "reduceRight") &&
3562
- (node.arguments.length < 2 || callbackReturnsOptional(callback))) {
4330
+ ((!elementsPresent && node.arguments.length < 2) ||
4331
+ callbackReturnsOptional(callback))) {
3563
4332
  if (promoteHofCallback(callback, [0, 1]))
3564
4333
  changed = true;
3565
4334
  const result = this.runtimeOptionalType(this.irTypeOf(node));
@@ -3578,8 +4347,34 @@ export class Lowerer {
3578
4347
  if (!symbol)
3579
4348
  return;
3580
4349
  const sig = signatureBySymbol.get(symbol);
3581
- if (!sig)
4350
+ if (!sig) {
4351
+ // A direct call of a function-valued const (`const show = (n:
4352
+ // number) => ...`): a possibly missing argument widens that
4353
+ // parameter, as for a callback the same value is passed as.
4354
+ if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
4355
+ return;
4356
+ const declaration = this.checker.valueDeclarationOf(symbol);
4357
+ const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
4358
+ ? peel(declaration.initializer)
4359
+ : null;
4360
+ if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
4361
+ return;
4362
+ // Optional, defaulted and rest parameters keep their own ABI.
4363
+ if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
4364
+ parameter.initializer !== undefined ||
4365
+ parameter.dotDotDotToken !== undefined))
4366
+ return;
4367
+ const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
4368
+ value.parameters[i] !== undefined &&
4369
+ !value.parameters[i].dotDotDotToken &&
4370
+ mayBeOptional(arg)
4371
+ ? [i]
4372
+ : []);
4373
+ provenanceSite = node;
4374
+ if (indices.length > 0 && promoteHofCallback(node.expression, indices))
4375
+ changed = true;
3582
4376
  return;
4377
+ }
3583
4378
  // JSDoc describes the usual value, not a runtime arity check.
3584
4379
  // A short JavaScript call still passes undefined. Widen only the
3585
4380
  // observed slots before bodies lower so both caller and callee
@@ -3605,6 +4400,7 @@ export class Lowerer {
3605
4400
  continue;
3606
4401
  shape.type = widened;
3607
4402
  const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4403
+ provenance?.note("parameter", parameter, node, "(omitted or undefined argument)");
3608
4404
  if (parameter) {
3609
4405
  for (const bound of boundIdentifiersOf(parameter.name)) {
3610
4406
  const boundSymbol = symbolOf(bound);
@@ -3629,13 +4425,63 @@ export class Lowerer {
3629
4425
  const callbackSlot = shape?.type;
3630
4426
  if (callbackSlot?.kind === "func" && !ts.isSpreadElement(arg)) {
3631
4427
  const optionalCallbackParams = callbackSlot.params.flatMap((type, index) => type.kind === "union" && this.armTag(type.unionId, UNDEFINED_T) >= 0 ? [index] : []);
4428
+ provenanceSite = node;
3632
4429
  if (optionalCallbackParams.length > 0 &&
3633
4430
  promoteHofCallback(arg, optionalCallbackParams)) {
3634
4431
  changed = true;
3635
4432
  }
3636
4433
  }
3637
- if (ts.isSpreadElement(arg) || !mayBeOptional(arg) || !shape)
4434
+ if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
4435
+ const fields = fieldsOfValue(arg);
4436
+ for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
4437
+ const parameterShape = signatureBySymbol.get(target)?.params[i];
4438
+ const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4439
+ if (!parameterShape || parameterShape.type.kind !== "record")
4440
+ continue;
4441
+ if (!parameter || !ts.isIdentifier(parameter.name))
4442
+ continue;
4443
+ const bound = symbolOf(parameter.name);
4444
+ if (!bound)
4445
+ continue;
4446
+ let promoted = parameterShape.type;
4447
+ for (const field of fields) {
4448
+ const source = promoted.kind === "record"
4449
+ ? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
4450
+ : undefined;
4451
+ if (source) {
4452
+ promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
4453
+ }
4454
+ if (noteFieldSymbol(bound, field))
4455
+ changed = true;
4456
+ }
4457
+ if (!typeEquals(promoted, parameterShape.type)) {
4458
+ parameterShape.type = promoted;
4459
+ this.runtimeOptionalBindingTypes.set(bound, promoted);
4460
+ provenance?.note("record-parameter", parameter, arg);
4461
+ changed = true;
4462
+ }
4463
+ }
4464
+ }
4465
+ if (ts.isSpreadElement(arg) || !shape)
3638
4466
  return;
4467
+ const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
4468
+ if (!assertion && !mayBeOptional(arg))
4469
+ return;
4470
+ if (assertion) {
4471
+ if (i === restAt)
4472
+ return;
4473
+ // An omittable slot's ABI already holds undefined (an
4474
+ // optional or defaulted parameter applies its own default).
4475
+ if (shape.mode === "omittable") {
4476
+ if (shape.type.kind === "union" &&
4477
+ this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4478
+ this.runtimeOptionalAssertions.add(assertion);
4479
+ return;
4480
+ }
4481
+ if (shape.mode !== "required")
4482
+ return;
4483
+ this.runtimeOptionalAssertions.add(assertion);
4484
+ }
3639
4485
  if (i === restAt) {
3640
4486
  // A rest binding always receives an array. An unchecked
3641
4487
  // argument can make its elements undefined, not the pack.
@@ -3649,6 +4495,7 @@ export class Lowerer {
3649
4495
  continue;
3650
4496
  rest.type = widened;
3651
4497
  const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4498
+ provenance?.note("rest-parameter", parameter, arg);
3652
4499
  if (parameter && ts.isIdentifier(parameter.name)) {
3653
4500
  const bound = symbolOf(parameter.name);
3654
4501
  if (bound)
@@ -3677,6 +4524,7 @@ export class Lowerer {
3677
4524
  if (set.size !== before) {
3678
4525
  changed = true;
3679
4526
  const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4527
+ provenance?.note("parameter", parameter, arg, target === symbol ? "" : "(override)");
3680
4528
  if (parameter) {
3681
4529
  for (const bound of boundIdentifiersOf(parameter.name)) {
3682
4530
  const boundSymbol = symbolOf(bound);
@@ -3700,9 +4548,10 @@ export class Lowerer {
3700
4548
  for (const expression of returnsOf(decl)) {
3701
4549
  if (mayBeOptional(expression) && !optionalReturns.has(symbol)) {
3702
4550
  optionalReturns.add(symbol);
4551
+ provenance?.note("return", decl.name ?? decl, expression);
3703
4552
  changed = true;
3704
4553
  }
3705
- const arithmetic = optionalStringArithmeticType(expression);
4554
+ const arithmetic = optionalPrimitiveResultType(expression);
3706
4555
  if (arithmetic && !arithmeticReturns.has(symbol)) {
3707
4556
  arithmeticReturns.set(symbol, arithmetic);
3708
4557
  changed = true;
@@ -3713,6 +4562,13 @@ export class Lowerer {
3713
4562
  let changed = true;
3714
4563
  while (changed) {
3715
4564
  changed = false;
4565
+ for (const [symbol, field] of staticFieldsBySymbol) {
4566
+ if (!optionalSymbols.has(symbol) && mayBeOptional(field.initializer)) {
4567
+ optionalSymbols.add(symbol);
4568
+ provenance?.note("static-field", field.initializer.parent, field.initializer);
4569
+ changed = true;
4570
+ }
4571
+ }
3716
4572
  for (const sf of sourceFiles)
3717
4573
  if (scanFile(sf))
3718
4574
  changed = true;
@@ -3744,11 +4600,33 @@ export class Lowerer {
3744
4600
  for (const symbol of family) {
3745
4601
  if (!optionalReturns.has(symbol)) {
3746
4602
  optionalReturns.add(symbol);
4603
+ provenance?.note("return", functionDeclBySymbol.get(symbol), undefined, "(override family)");
3747
4604
  changed = true;
3748
4605
  }
3749
4606
  }
3750
4607
  }
3751
4608
  }
4609
+ for (const [site, present] of presentSites)
4610
+ if (present)
4611
+ this.presentElementCalls.add(site);
4612
+ for (const [loop, present] of presentLoops)
4613
+ if (present)
4614
+ this.presentElementLoops.add(loop);
4615
+ const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
4616
+ for (const [symbol, field] of staticFieldsBySymbol) {
4617
+ if (!optionalSymbols.has(symbol))
4618
+ continue;
4619
+ const promoted = addUndefined(field.type);
4620
+ // An explicit optional annotation already has the right layout, but
4621
+ // an indexed assignment still invalidates the checker's presence fact.
4622
+ field.type = promoted;
4623
+ field.runtimeOptional = true;
4624
+ const global = globalsById.get(field.globalId);
4625
+ if (global) {
4626
+ global.type = promoted;
4627
+ this.runtimeOptionalGlobals.add(global);
4628
+ }
4629
+ }
3752
4630
  for (const [symbol, sig] of signatureBySymbol) {
3753
4631
  const params = optionalParams.get(symbol);
3754
4632
  if (params) {
@@ -3880,6 +4758,36 @@ export class Lowerer {
3880
4758
  }
3881
4759
  });
3882
4760
  }
4761
+ for (const symbol of optionalSymbols)
4762
+ this.runtimeOptionalAssignedSymbols.add(symbol);
4763
+ // `decode(xs[i]!)` into a parameter whose final ABI already holds
4764
+ // undefined: the assertion has no runtime effect, so a missing element
4765
+ // reaches the callee as undefined instead of trapping at the read.
4766
+ for (const sf of sourceFiles) {
4767
+ for (const node of sitesOf(sf)) {
4768
+ if (!ts.isCallExpression(node))
4769
+ continue;
4770
+ if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
4771
+ continue;
4772
+ const symbol = callableSymbolOf(node.expression);
4773
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4774
+ if (!sig || node.arguments.some(ts.isSpreadElement))
4775
+ continue;
4776
+ node.arguments.forEach((arg, i) => {
4777
+ let e = arg;
4778
+ while (ts.isParenthesizedExpression(e))
4779
+ e = e.expression;
4780
+ if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
4781
+ return;
4782
+ const shape = sig.params[i];
4783
+ if (shape?.mode !== "required" && shape?.mode !== "omittable")
4784
+ return;
4785
+ if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4786
+ this.runtimeOptionalAssertions.add(e);
4787
+ });
4788
+ }
4789
+ }
4790
+ provenance?.flush();
3883
4791
  }
3884
4792
  run() {
3885
4793
  const parts = this.splitFiles();
@@ -3929,14 +4837,14 @@ export class Lowerer {
3929
4837
  if (fn)
3930
4838
  functions.push(fn);
3931
4839
  else if (this.countsSkips())
3932
- this.stats.functionsSkipped++;
4840
+ this.countSkippedBody(decl);
3933
4841
  }
3934
4842
  for (const decl of fp.classDecls) {
3935
4843
  const info = this.classes.get(this.classNamer(decl));
3936
4844
  if (info)
3937
4845
  functions.push(...this.lowerClassMembers(info));
3938
4846
  else if (this.countsSkips())
3939
- this.stats.functionsSkipped++;
4847
+ this.countSkippedBody(decl);
3940
4848
  }
3941
4849
  }
3942
4850
  // Each file's top-level statements form its run-once init function.
@@ -4024,10 +4932,10 @@ export class Lowerer {
4024
4932
  // Deferred collection diagnostics nothing flushed — declarations no
4025
4933
  // reference ever made relevant. They belong to the unreached group
4026
4934
  // (collection order keeps them deterministic).
4027
- for (const [symbol, diags] of this.deferredDiags) {
4935
+ for (const [symbol, diags] of [...this.deferredDiags]) {
4028
4936
  if (this.alreadyFlushed.has(symbol))
4029
4937
  continue;
4030
- for (const d of diags)
4938
+ for (const d of [...diags])
4031
4939
  this.pushDiag(d);
4032
4940
  }
4033
4941
  return {
@@ -4123,6 +5031,7 @@ export class Lowerer {
4123
5031
  // the emitted program references, flushing deferred class diagnostics
4124
5032
  // that a reached type makes relevant.
4125
5033
  const artifacts = this.moduleArtifacts(functions);
5034
+ refuseUncheckedCycleReads(this, artifacts.classes ?? []);
4126
5035
  // Types still naming a class that never REGISTERED after retention's
4127
5036
  // flush (JS graphs whose class fences deferred to runtime — the
4128
5037
  // sentence-walker's path params, printer tables whose func-typed
@@ -4137,7 +5046,7 @@ export class Lowerer {
4137
5046
  const module = this.diags.length > 0
4138
5047
  ? null
4139
5048
  : {
4140
- irVersion: 14,
5049
+ irVersion: 15,
4141
5050
  sourceFile: this.entry.fileName,
4142
5051
  functions,
4143
5052
  classes: artifacts.classes,
@@ -4147,10 +5056,13 @@ export class Lowerer {
4147
5056
  ...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
4148
5057
  entry: ENTRY_NAME,
4149
5058
  ...(this.usesWorkers ? { workers: true } : {}),
5059
+ ...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
4150
5060
  ...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
4151
5061
  };
4152
5062
  if (module)
4153
5063
  sanitizeUnregisteredClassTypes(module, (name) => this.classes.has(name));
5064
+ if (module)
5065
+ checkCycleBindingAccesses(this, module);
4154
5066
  return {
4155
5067
  module,
4156
5068
  diagnostics: this.diags,
@@ -4166,41 +5078,72 @@ export class Lowerer {
4166
5078
  * registered ClassInfo — the shape of a JS class whose collection fenced.
4167
5079
  * Used by run()'s global pruning; shapes/unions recurse with a seen-set
4168
5080
  * (interned ids can nest). */
4169
- typeNamesUnregisteredClass(t, seen = new Set()) {
5081
+ typeNamesUnregisteredClass(t) {
5082
+ return this.unregisteredClassOf(t, new Set()) !== null;
5083
+ }
5084
+ /** The first class name typeNamesUnregisteredClass finds in `t`. */
5085
+ unregisteredClassOf(t, seen) {
5086
+ const first = (types) => {
5087
+ for (const type of types) {
5088
+ const name = this.unregisteredClassOf(type, seen);
5089
+ if (name !== null)
5090
+ return name;
5091
+ }
5092
+ return null;
5093
+ };
4170
5094
  switch (t.kind) {
4171
5095
  case "object":
4172
- return !this.classes.has(t.className);
5096
+ return this.classes.has(t.className) ? null : t.className;
4173
5097
  case "array":
4174
5098
  case "set":
4175
- return this.typeNamesUnregisteredClass(t.elem, seen);
5099
+ return first([t.elem]);
4176
5100
  case "map":
4177
- return (this.typeNamesUnregisteredClass(t.key, seen) ||
4178
- this.typeNamesUnregisteredClass(t.value, seen));
5101
+ return first([t.key, t.value]);
4179
5102
  case "promise":
4180
- return this.typeNamesUnregisteredClass(t.inner, seen);
5103
+ return first([t.inner]);
4181
5104
  case "func":
4182
- return (t.params.some((p) => this.typeNamesUnregisteredClass(p, seen)) ||
4183
- this.typeNamesUnregisteredClass(t.ret, seen));
5105
+ return first([...t.params, t.ret]);
4184
5106
  case "record": {
4185
5107
  if (seen.has(t.shapeId))
4186
- return false;
5108
+ return null;
4187
5109
  seen.add(t.shapeId);
4188
5110
  const shape = this.shapes.get(t.shapeId);
4189
5111
  if (!shape)
4190
- return false;
4191
- if (shape.indexValue && this.typeNamesUnregisteredClass(shape.indexValue, seen))
4192
- return true;
4193
- return shape.fields.some((f) => this.typeNamesUnregisteredClass(f.type, seen));
5112
+ return null;
5113
+ return first([
5114
+ ...(shape.indexValue ? [shape.indexValue] : []),
5115
+ ...shape.fields.map((f) => f.type),
5116
+ ]);
4194
5117
  }
4195
5118
  case "union": {
4196
5119
  if (seen.has(t.unionId))
4197
- return false;
5120
+ return null;
4198
5121
  seen.add(t.unionId);
4199
5122
  const def = this.unions.get(t.unionId);
4200
- return !!def && def.arms.some((a) => this.typeNamesUnregisteredClass(a, seen));
5123
+ return def ? first(def.arms) : null;
4201
5124
  }
4202
5125
  default:
4203
- return false;
5126
+ return null;
5127
+ }
5128
+ }
5129
+ /** A declaration whose signature blocked never lowers its body, so its
5130
+ * statements would otherwise vanish from the coverage totals and the
5131
+ * percentage would overstate how much of the program compiles. Count
5132
+ * every statement the body holds as attempted and failed, in addition to
5133
+ * the skipped-function tally. */
5134
+ countSkippedBody(decl) {
5135
+ this.stats.functionsSkipped++;
5136
+ if (this.suppressStats)
5137
+ return;
5138
+ const n = countBodyStatements(decl);
5139
+ if (n === 0)
5140
+ return;
5141
+ this.stats.statementsTotal += n;
5142
+ this.stats.statementsFailed += n;
5143
+ const file = decl.getSourceFile().fileName;
5144
+ for (let i = 0; i < n; i++) {
5145
+ this.bumpFileStat(file, "total");
5146
+ this.bumpFileStat(file, "failed");
4204
5147
  }
4205
5148
  }
4206
5149
  /** Whether run() counts a signature-blocked declaration in
@@ -4736,13 +5679,18 @@ export class Lowerer {
4736
5679
  const d = this.instantiationContext
4737
5680
  ? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
4738
5681
  : diag;
5682
+ const pending = this.pendingFlushes.get(diag);
4739
5683
  // Deferred collection: the wrapper decides whether these ever report
4740
5684
  // (a reference flushes them; unreferenced declarations stay silent in
4741
5685
  // builds and report under coverage's unreached group).
4742
5686
  if (this.diagSink) {
5687
+ if (pending && d !== diag)
5688
+ this.pendingFlushes.set(d, pending);
4743
5689
  this.diagSink.push(d);
4744
5690
  return;
4745
5691
  }
5692
+ if (pending)
5693
+ this.commitFlush(pending.symbol, pending.source);
4746
5694
  // One site, one report: some declarations map a type twice (module
4747
5695
  // globals pre-register before their initializers lower) — an exact
4748
5696
  // duplicate (code + span + message) adds noise, not information.
@@ -5060,6 +6008,9 @@ export class Lowerer {
5060
6008
  if (arm !== undefined)
5061
6009
  return arm;
5062
6010
  }
6011
+ const collection = concreteCollectionNarrow(this, node, t);
6012
+ if (collection)
6013
+ return collection;
5063
6014
  // IMPLICIT-ANY instance bodies: an identifier reference to a BOUND
5064
6015
  // param answers the call site's concrete type wherever the checker
5065
6016
  // still says `any` (there is no `T` for mapType to substitute — the
@@ -5383,6 +6334,29 @@ export class Lowerer {
5383
6334
  this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
5384
6335
  throw new PoisonError();
5385
6336
  }
6337
+ // Instances of two unrelated classes: class identity is nominal here,
6338
+ // so a structurally compatible instance never becomes the other class.
6339
+ // An assertion (`x as Y`) names the mistake most directly.
6340
+ if (actual.kind === "object" &&
6341
+ expected.kind === "object" &&
6342
+ this.classes.has(actual.className) &&
6343
+ this.classes.has(expected.className) &&
6344
+ this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
6345
+ !this.isSubclassOf(actual.className, expected.className) &&
6346
+ !this.isSubclassOf(expected.className, actual.className)) {
6347
+ let site = node;
6348
+ while (ts.isParenthesizedExpression(site))
6349
+ site = site.expression;
6350
+ const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
6351
+ this.unsupported("SC1090", node, `${assertion ? "asserting" : "using"} a '${this.fmt(actual)}' instance as the unrelated class '${this.fmt(expected)}' (neither class extends the other, and class identity is nominal in compiled code)`, assertion
6352
+ ? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
6353
+ : `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
6354
+ }
6355
+ // Iterator-typed slots hold generators and built-in collection
6356
+ // iterators; any other iterator object (a user class) stays fenced.
6357
+ if (expected.kind === "generator" && actual.kind !== "generator") {
6358
+ this.unsupported("SC1090", node, `'${this.fmt(actual)}' values in an iterator-typed slot (Iterator and IterableIterator slots hold generators and built-in collection iterators)`);
6359
+ }
5386
6360
  // Everything else — a plain-kind mismatch like a string flowing into a
5387
6361
  // class-instance slot — is tsc-rejected in the lowering world and only
5388
6362
  // reaches here through preflight's project-world second chance (e.g. a
@@ -5438,6 +6412,21 @@ export class Lowerer {
5438
6412
  }
5439
6413
  return literalUnionArm(def, values, (id) => this.shapes.get(id), cached.owners);
5440
6414
  }
6415
+ /** Widen only the result view; identical parameter ABIs preserve closure identity. */
6416
+ coerceCovariantFunction(expr, expected) {
6417
+ if (expr.type.kind === "func" &&
6418
+ expected.kind === "func" &&
6419
+ expr.type.rest === expected.rest &&
6420
+ expr.type.restAbi === expected.restAbi &&
6421
+ expr.type.argumentsAll === expected.argumentsAll &&
6422
+ expr.type.params.length === expected.params.length &&
6423
+ expr.type.params.every((type, i) => typeEquals(type, expected.params[i])) &&
6424
+ expr.type.ret.kind === "object" &&
6425
+ expected.ret.kind === "object" &&
6426
+ this.isSubclassOf(expr.type.ret.className, expected.ret.className))
6427
+ return { kind: "upcast", value: expr, type: expected, loc: expr.loc };
6428
+ return null;
6429
+ }
5441
6430
  /** Implicit union construction. Wherever a value flows into a typed slot
5442
6431
  * (initializer, assignment, call argument, return, field write, record
5443
6432
  * literal field, ternary arm) whose expected type is a union and the
@@ -5446,6 +6435,22 @@ export class Lowerer {
5446
6435
  * else (including a DIFFERENT union) is left for requireExactShape, which
5447
6436
  * rejects union mismatches with SC2003. */
5448
6437
  coerceToExpected(expr, expected) {
6438
+ // A destination that holds undefined takes an unassigned deferred-init
6439
+ // field's slot as is (Node passes the undefined along).
6440
+ if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
6441
+ const slot = this.deferredFieldSlot(expr);
6442
+ if (slot)
6443
+ expr = slot;
6444
+ }
6445
+ if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
6446
+ return this.divergentValue(expr, expected);
6447
+ // Iterator-typed slots: native iterators and differently typed
6448
+ // generators step through an adapter generator.
6449
+ if (expected.kind === "generator" && !typeEquals(expr.type, expected)) {
6450
+ const adapted = iteratorSlotAdapter(this, expr, expected);
6451
+ if (adapted)
6452
+ return adapted;
6453
+ }
5449
6454
  // Island boundary, both directions. IN: any static value flowing into
5450
6455
  // an any-typed slot marshals implicitly (tsc allows the assignment;
5451
6456
  // the marshal is where its semantics live). OUT: an 'any' value
@@ -5595,9 +6600,18 @@ export class Lowerer {
5595
6600
  };
5596
6601
  }
5597
6602
  if (expr.kind === "unitLit" || this.dynConvertible(expr.type)) {
5598
- const shape = expr.type.kind === "record" ? this.shapes.get(expr.type.shapeId) : undefined;
5599
- const liveRef = expr.type.kind === "array" ||
5600
- (!!shape && (shape.fields.length > 0 || shape.indexValue !== undefined));
6603
+ // Records and arrays keep their identity as untyped values, including
6604
+ // when they arrive through a union, so `unknown` views, weak
6605
+ // collection keys and later typed reads all see the same object.
6606
+ const liveReference = (type) => {
6607
+ if (type.kind === "array")
6608
+ return true;
6609
+ const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
6610
+ return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
6611
+ };
6612
+ const liveRef = liveReference(expr.type) ||
6613
+ (expr.type.kind === "union" &&
6614
+ (this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
5601
6615
  return {
5602
6616
  kind: "dynFrom",
5603
6617
  value: expr,
@@ -5705,6 +6719,9 @@ export class Lowerer {
5705
6719
  return { kind: "call", callee: adapter, args: [expr], type: expected, loc: expr.loc };
5706
6720
  }
5707
6721
  }
6722
+ const covariant = this.coerceCovariantFunction(expr, expected);
6723
+ if (covariant)
6724
+ return covariant;
5708
6725
  // The GENERAL function-value adapter (funcCoerceAdapter): a function
5709
6726
  // whose signature differs from the slot's only by coercible pieces —
5710
6727
  // fewer parameters (JS ignores extras: `load(function () {})` into an
@@ -5728,6 +6745,31 @@ export class Lowerer {
5728
6745
  this.isSubclassOf(expr.type.className, expected.className)) {
5729
6746
  return { kind: "upcast", value: expr, type: expected, loc: expr.loc };
5730
6747
  }
6748
+ // Optional subclass storage still has the same object payload. Extract
6749
+ // it with a checked tag test, then widen the class view without copying.
6750
+ if (expected.kind === "object" && expr.type.kind === "union") {
6751
+ const arms = this.unions.get(expr.type.unionId)?.arms;
6752
+ const present = arms?.filter((arm) => !isUnitType(arm)) ?? [];
6753
+ if (present.length === 1 &&
6754
+ present[0].kind === "object" &&
6755
+ present[0].className !== expected.className &&
6756
+ this.isSubclassOf(present[0].className, expected.className)) {
6757
+ // One class arm: the ordinary checked narrow reports a missing value
6758
+ // exactly like same-class storage before the prefix view widens it.
6759
+ return {
6760
+ kind: "upcast",
6761
+ value: this.coerceToExpected(expr, present[0]),
6762
+ type: expected,
6763
+ loc: expr.loc,
6764
+ };
6765
+ }
6766
+ if (present.length > 1 &&
6767
+ present.every((arm) => identityPreservingWidening(this, arm, expected))) {
6768
+ const view = checkedClassAssertion(this, expr, expected, expr.loc);
6769
+ if (view)
6770
+ return view;
6771
+ }
6772
+ }
5731
6773
  // CLASS-VALUE widening (classval:D into a classval:C slot): the same
5732
6774
  // pointer with only the static type changing — legal exactly when D
5733
6775
  // strictly descends from C AND the two completed constructor ABIs
@@ -5763,7 +6805,7 @@ export class Lowerer {
5763
6805
  this.armTag(expr.type.unionId, expected) >= 0) {
5764
6806
  const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
5765
6807
  if (helper) {
5766
- return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
6808
+ return this.checkedNarrowCall(helper, expr, expected, expr.loc);
5767
6809
  }
5768
6810
  }
5769
6811
  if (!typeEquals(expr.type, expected)) {
@@ -6014,6 +7056,9 @@ export class Lowerer {
6014
7056
  narrowedArmHelper(fromId, target, loc) {
6015
7057
  return narrowedArmHelper(this, fromId, target, loc);
6016
7058
  }
7059
+ checkedNarrowCall(helper, value, type, loc) {
7060
+ return checkedNarrowCall(this, helper, value, type, loc);
7061
+ }
6017
7062
  deferredReadHelper(fromId, target, loc) {
6018
7063
  return deferredReadHelper(this, fromId, target, loc);
6019
7064
  }
@@ -6110,6 +7155,17 @@ export class Lowerer {
6110
7155
  loc: expr.loc,
6111
7156
  }
6112
7157
  : this.coerceToExpected(expr, expected);
7158
+ // Keep storage intact when ordinary widening already accepts it. Only
7159
+ // a destination needing a more specific class layout uses the site's
7160
+ // refinement and checked extraction.
7161
+ if (!typeEquals(e.type, expected) &&
7162
+ (expected.kind === "object" || expected.kind === "union")) {
7163
+ const site = e.type.kind === "union" ? this.mapTypeOf(this.typeOf(node)) : null;
7164
+ const narrowed = (site && narrowStoredClassValue(this, e, site)) ??
7165
+ narrowGenericClassValue(this, e, expected);
7166
+ if (narrowed)
7167
+ e = this.coerceToExpected(narrowed, expected);
7168
+ }
6113
7169
  // Existing JavaScript arrays retain mutations and identity across checked
6114
7170
  // slots. Fresh literals have no prior identity and use checked storage
6115
7171
  // directly, so later writes can change their inferred element type.
@@ -6698,6 +7754,42 @@ export class Lowerer {
6698
7754
  wrappedUndefined(type, loc) {
6699
7755
  return wrappedUndefined(this, type, loc);
6700
7756
  }
7757
+ /** The value an OMITTED or deleted property leaves in a record field
7758
+ * slot of `type`: the ABSENT state for undefined-armed unions (the slot
7759
+ * reads as undefined but the property does not exist), the dyn
7760
+ * undefined for 'unknown' slots, and null when the slot cannot be
7761
+ * missing. Only legal as a direct field-slot write (IR fieldAbsent). */
7762
+ absentFieldValue(type, loc) {
7763
+ if (type.kind === "union" && this.armTag(type.unionId, UNDEFINED_T) >= 0) {
7764
+ return { kind: "fieldAbsent", unionId: type.unionId, type, loc };
7765
+ }
7766
+ return this.wrappedUndefined(type, loc);
7767
+ }
7768
+ /** A field copy that keeps absence: `value` (the converted read of
7769
+ * `field` from record `obj`, which must be repeatable) when the source
7770
+ * property exists, else the target slot's ABSENT state. Fields that are
7771
+ * always present, and targets that cannot be absent, copy `value`
7772
+ * as-is. Only legal as a direct field-slot write. */
7773
+ presenceKeepingCopy(obj, shapeId, field, value, loc) {
7774
+ const present = this.recordFieldPresent(obj, shapeId, field, loc);
7775
+ if (present.kind !== "recordHas")
7776
+ return value;
7777
+ const absent = this.absentFieldValue(value.type, loc);
7778
+ if (absent?.kind !== "fieldAbsent")
7779
+ return value;
7780
+ return { kind: "ternary", cond: present, then: value, else_: absent, type: value.type, loc };
7781
+ }
7782
+ /** Own-property presence of declared field `field` on record `obj`
7783
+ * (borrowed — callers pass a repeatable receiver): a runtime test for
7784
+ * undefined-armed union fields, which can be absent, and `true` for
7785
+ * every other declared field. */
7786
+ recordFieldPresent(obj, shapeId, field, loc) {
7787
+ const f = this.shapes.get(shapeId)?.fields.find((x) => x.name === field);
7788
+ if (f && f.type.kind === "union" && this.armTag(f.type.unionId, UNDEFINED_T) >= 0) {
7789
+ return { kind: "recordHas", obj, shapeId, field, type: BOOL, loc };
7790
+ }
7791
+ return { kind: "boolLit", value: true, type: BOOL, loc };
7792
+ }
6701
7793
  /** The entry value of a binding JS initializes to `undefined` (an
6702
7794
  * initializer-less declaration, a hoisted `var` before its statement):
6703
7795
  * undefined-armed unions hold the interned undefined arm, and 'any'
@@ -6785,15 +7877,47 @@ export class Lowerer {
6785
7877
  flushDeferred(symbol) {
6786
7878
  if (this.collecting)
6787
7879
  return;
7880
+ this.reportDeferred(symbol);
7881
+ }
7882
+ /** flushDeferred without the collection-pass guard: for declarations
7883
+ * whose diagnostics must report eagerly (class statements with
7884
+ * declaration-time code, extended bases — collectClassShapeDeferring). */
7885
+ reportDeferred(symbol) {
6788
7886
  const diags = this.deferredDiags.get(symbol);
6789
7887
  if (!diags)
6790
7888
  return;
6791
- this.deferredDiags.delete(symbol);
6792
- if (this.alreadyFlushed.has(symbol))
6793
- return; // the emit pass reported these
6794
- this.flushedSymbols.add(symbol);
6795
- for (const d of diags)
7889
+ if (this.alreadyFlushed.has(symbol)) {
7890
+ this.deferredDiags.delete(symbol); // the emit pass reported these
7891
+ return;
7892
+ }
7893
+ if (this.diagSink === null) {
7894
+ this.deferredDiags.delete(symbol);
7895
+ this.flushedSymbols.add(symbol);
7896
+ for (const d of diags)
7897
+ this.pushDiag(d);
7898
+ return;
7899
+ }
7900
+ // A capture buffer is active (a probe, or another declaration's
7901
+ // deferred collection): the copies ride that buffer, and the
7902
+ // declaration stays deferred until one of them reaches the build.
7903
+ for (const d of diags) {
7904
+ this.pendingFlushes.set(d, { symbol, source: d });
6796
7905
  this.pushDiag(d);
7906
+ }
7907
+ }
7908
+ /** A flushed copy of a deferred diagnostic reached `diags`: that
7909
+ * diagnostic is reported, so it leaves the declaration's deferred list
7910
+ * (the declaration counts as flushed for the coverage remainder). */
7911
+ commitFlush(symbol, source) {
7912
+ this.flushedSymbols.add(symbol);
7913
+ const list = this.deferredDiags.get(symbol);
7914
+ if (!list)
7915
+ return;
7916
+ const index = list.indexOf(source);
7917
+ if (index >= 0)
7918
+ list.splice(index, 1);
7919
+ if (list.length === 0)
7920
+ this.deferredDiags.delete(symbol);
6797
7921
  }
6798
7922
  flushDeferredClass(className) {
6799
7923
  const symbol = this.deferredClassByName.get(className);
@@ -7102,6 +8226,32 @@ export class Lowerer {
7102
8226
  declareLocal(nameNode, name, type, mutable) {
7103
8227
  return declareContextLocal(this.ctx, name, type, mutable, this.checker.getSymbolAtLocation(nameNode), bindingSource(nameNode));
7104
8228
  }
8229
+ /** A never-returning call in a value position of type `expected`: the
8230
+ * call runs for its effects (it throws or exits), and the expression ends
8231
+ * in an unreachable trap instead of producing a value. The result reads an
8232
+ * uninitialized hidden local that control never reaches. */
8233
+ divergentValue(call, expected) {
8234
+ const loc = call.loc;
8235
+ const slot = this.declareHiddenLocal("%never", expected);
8236
+ slot.mutable = true;
8237
+ return {
8238
+ kind: "seqExpr",
8239
+ stmts: [
8240
+ { kind: "exprStmt", expr: call, loc },
8241
+ { kind: "varDecl", localId: slot.id, init: null, loc },
8242
+ {
8243
+ kind: "runtimeFence",
8244
+ code: "SC9002",
8245
+ message: "unreachable: a 'never' call returned — please report this",
8246
+ loc,
8247
+ },
8248
+ ],
8249
+ result: varRef(slot.id, expected, loc),
8250
+ diverges: true,
8251
+ type: expected,
8252
+ loc,
8253
+ };
8254
+ }
7105
8255
  /** A function-scope local bound to NO ts.Symbol — the hidden ABI slot of a
7106
8256
  * defaulted parameter (the parameter's symbol binds to the separately-
7107
8257
  * declared body local; nothing in the source can name this one). */
@@ -7846,13 +8996,7 @@ export class Lowerer {
7846
8996
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
7847
8997
  if (!helper)
7848
8998
  return lowerForOf(this, stmt);
7849
- return this.withExpressionOverride(stmt.expression, {
7850
- kind: "call",
7851
- callee: helper,
7852
- args: [optional.value],
7853
- type: optional.present,
7854
- loc: locOf(stmt.expression),
7855
- }, () => lowerForOf(this, stmt));
8999
+ return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
7856
9000
  }
7857
9001
  lowerForStatement(stmt) {
7858
9002
  return lowerForStatement(this, stmt);
@@ -8097,21 +9241,21 @@ export class Lowerer {
8097
9241
  const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
8098
9242
  if (!helper)
8099
9243
  return lower();
8100
- right = {
8101
- kind: "call",
8102
- callee: helper,
8103
- args: [optionalRight.value],
8104
- type: optionalRight.present,
8105
- loc: locOf(expr.right),
8106
- };
9244
+ right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
8107
9245
  }
8108
9246
  else {
8109
9247
  right = this.lowerExpr(expr.right);
8110
9248
  }
8111
9249
  if (right.type.kind !== "classval")
8112
9250
  return lower();
8113
- const stableRight = this.declareHiddenLocal("%instanceofRight", right.type);
8114
- const rightValue = varRef(stableRight.id, right.type, locOf(expr.right));
9251
+ // A capture-free class reference is the immortal static class object:
9252
+ // re-reading it is pure, so it needs no owned temporary (whose retain
9253
+ // and release would be out-of-line calls on every test).
9254
+ const constantRight = right.kind === "classRef" && right.captures === undefined;
9255
+ const stableRight = constantRight
9256
+ ? null
9257
+ : this.declareHiddenLocal("%instanceofRight", right.type);
9258
+ const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
8115
9259
  const body = this.withExpressionOverride(expr.left, {
8116
9260
  kind: "unionNarrow",
8117
9261
  unionId: left.type.unionId,
@@ -8124,7 +9268,16 @@ export class Lowerer {
8124
9268
  kind: "seqExpr",
8125
9269
  stmts: [
8126
9270
  { kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
8127
- { kind: "varDecl", localId: stableRight.id, init: right, loc: locOf(expr.right) },
9271
+ ...(stableRight
9272
+ ? [
9273
+ {
9274
+ kind: "varDecl",
9275
+ localId: stableRight.id,
9276
+ init: right,
9277
+ loc: locOf(expr.right),
9278
+ },
9279
+ ]
9280
+ : []),
8128
9281
  ],
8129
9282
  result: {
8130
9283
  kind: "ternary",
@@ -8155,13 +9308,7 @@ export class Lowerer {
8155
9308
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
8156
9309
  if (!helper)
8157
9310
  return lower();
8158
- return this.withExpressionOverride(node, {
8159
- kind: "call",
8160
- callee: helper,
8161
- args: [optional.value],
8162
- type: optional.present,
8163
- loc: locOf(node),
8164
- }, lower);
9311
+ return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
8165
9312
  }
8166
9313
  withExpressionOverride(node, value, lower) {
8167
9314
  const previous = this.chainRecvByNode.get(node);
@@ -8270,6 +9417,37 @@ export class Lowerer {
8270
9417
  fieldGetExpr(target, loc, blame) {
8271
9418
  return fieldGetExpr(this, target, loc, blame);
8272
9419
  }
9420
+ /** The undefined-armed slot read behind a checked deferred-init field
9421
+ * read, or null. Consumers that observe undefined itself (equality,
9422
+ * typeof, truthiness, nullish defaults) test the slot instead of the
9423
+ * checked extraction, which throws for an unassigned field. */
9424
+ deferredFieldSlot(value) {
9425
+ // The inline form: `slot is undefined ? throw : narrow(slot)`.
9426
+ if (value.kind === "ternary" &&
9427
+ value.cond.kind === "unionIsTag" &&
9428
+ value.else_.kind === "unionNarrow" &&
9429
+ value.else_.value === value.cond.value &&
9430
+ value.then.kind === "libCall" &&
9431
+ value.then.fn === "error.nodeThrow") {
9432
+ const read = value.cond.value;
9433
+ return read.kind === "fieldGet" &&
9434
+ read.type.kind === "union" &&
9435
+ this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
9436
+ ? read
9437
+ : null;
9438
+ }
9439
+ if (value.kind !== "call" || value.args.length !== 1)
9440
+ return null;
9441
+ const read = value.args[0];
9442
+ if (read.kind !== "fieldGet" || read.type.kind !== "union")
9443
+ return null;
9444
+ if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
9445
+ return null;
9446
+ return this.coercions.checkedNarrows.has(value.callee) ||
9447
+ value.callee.startsWith("%deferred.read.")
9448
+ ? read
9449
+ : null;
9450
+ }
8273
9451
  fieldSetStmt(target, value, loc, blame) {
8274
9452
  return fieldSetStmt(this, target, value, loc, blame);
8275
9453
  }
@@ -8953,8 +10131,8 @@ export class Lowerer {
8953
10131
  return lowerNumberStaticProperty(this, expr);
8954
10132
  }
8955
10133
  /* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
8956
- requireDynamicApi(feature, node) {
8957
- return requireDynamicApi(this, feature, node);
10134
+ requireDynamicApi(feature, node, hint) {
10135
+ return requireDynamicApi(this, feature, node, hint);
8958
10136
  }
8959
10137
  lowerMathProperty(expr) {
8960
10138
  return lowerMathProperty(this, expr);