@scriptc/compiler 0.2.7 → 0.2.9

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 (474) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +3 -0
  2. package/dist/backend/cycle-analysis.js +50 -17
  3. package/dist/backend/cycle-analysis.js.map +1 -1
  4. package/dist/backend/cycle-analysis.test.js +138 -14
  5. package/dist/backend/cycle-analysis.test.js.map +1 -1
  6. package/dist/backend/cycle-mutability.d.ts +31 -6
  7. package/dist/backend/cycle-mutability.js +120 -18
  8. package/dist/backend/cycle-mutability.js.map +1 -1
  9. package/dist/backend/llvm/alloc.d.ts +16 -3
  10. package/dist/backend/llvm/alloc.js +78 -23
  11. package/dist/backend/llvm/alloc.js.map +1 -1
  12. package/dist/backend/llvm/alloc.test.js +5 -5
  13. package/dist/backend/llvm/alloc.test.js.map +1 -1
  14. package/dist/backend/llvm/blocks.d.ts +9 -0
  15. package/dist/backend/llvm/blocks.js +9 -0
  16. package/dist/backend/llvm/blocks.js.map +1 -1
  17. package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
  18. package/dist/backend/llvm/borrowed-receivers.js +39 -0
  19. package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
  20. package/dist/backend/llvm/byte-numbers.js +2 -3
  21. package/dist/backend/llvm/byte-numbers.js.map +1 -1
  22. package/dist/backend/llvm/call-lifetimes.d.ts +4 -1
  23. package/dist/backend/llvm/call-lifetimes.js +14 -3
  24. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  25. package/dist/backend/llvm/call-lifetimes.test.js +8 -3
  26. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  27. package/dist/backend/llvm/checked-narrows.d.ts +24 -0
  28. package/dist/backend/llvm/checked-narrows.js +108 -0
  29. package/dist/backend/llvm/checked-narrows.js.map +1 -0
  30. package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
  31. package/dist/backend/llvm/checked-narrows.test.js +208 -0
  32. package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
  33. package/dist/backend/llvm/classes.d.ts +13 -1
  34. package/dist/backend/llvm/classes.js +32 -23
  35. package/dist/backend/llvm/classes.js.map +1 -1
  36. package/dist/backend/llvm/classes.test.js +32 -3
  37. package/dist/backend/llvm/classes.test.js.map +1 -1
  38. package/dist/backend/llvm/common.d.ts +17 -0
  39. package/dist/backend/llvm/common.js +28 -0
  40. package/dist/backend/llvm/common.js.map +1 -1
  41. package/dist/backend/llvm/debug-info.d.ts +6 -2
  42. package/dist/backend/llvm/debug-info.js +11 -1
  43. package/dist/backend/llvm/debug-info.js.map +1 -1
  44. package/dist/backend/llvm/dense-array-access.js +1 -1
  45. package/dist/backend/llvm/dense-array-access.test.js +89 -0
  46. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  47. package/dist/backend/llvm/dyn.d.ts +8 -4
  48. package/dist/backend/llvm/dyn.js +58 -31
  49. package/dist/backend/llvm/dyn.js.map +1 -1
  50. package/dist/backend/llvm/emitter.d.ts +227 -12
  51. package/dist/backend/llvm/emitter.js +1492 -169
  52. package/dist/backend/llvm/emitter.js.map +1 -1
  53. package/dist/backend/llvm/emitter.test.js +54 -1
  54. package/dist/backend/llvm/emitter.test.js.map +1 -1
  55. package/dist/backend/llvm/expr-async.js +4 -4
  56. package/dist/backend/llvm/expr-async.js.map +1 -1
  57. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  58. package/dist/backend/llvm/expr-bytes.js +116 -32
  59. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  60. package/dist/backend/llvm/expr-callbacks.js +29 -21
  61. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  62. package/dist/backend/llvm/expr-calls.d.ts +5 -0
  63. package/dist/backend/llvm/expr-calls.js +55 -10
  64. package/dist/backend/llvm/expr-calls.js.map +1 -1
  65. package/dist/backend/llvm/expr-containers.d.ts +7 -1
  66. package/dist/backend/llvm/expr-containers.js +231 -28
  67. package/dist/backend/llvm/expr-containers.js.map +1 -1
  68. package/dist/backend/llvm/expr-context.d.ts +2 -0
  69. package/dist/backend/llvm/expr-control.js +70 -14
  70. package/dist/backend/llvm/expr-control.js.map +1 -1
  71. package/dist/backend/llvm/expr-dynamic.js +75 -21
  72. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  73. package/dist/backend/llvm/expr-island.js +3 -8
  74. package/dist/backend/llvm/expr-island.js.map +1 -1
  75. package/dist/backend/llvm/expr-primitives.js +41 -12
  76. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  77. package/dist/backend/llvm/expr-primitives.test.js +50 -2
  78. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  79. package/dist/backend/llvm/expr-records.js +28 -14
  80. package/dist/backend/llvm/expr-records.js.map +1 -1
  81. package/dist/backend/llvm/expr-stream-bridges.d.ts +3 -0
  82. package/dist/backend/llvm/expr-stream-bridges.js +114 -42
  83. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  84. package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
  85. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  86. package/dist/backend/llvm/layouts.d.ts +2 -1
  87. package/dist/backend/llvm/layouts.js +2 -2
  88. package/dist/backend/llvm/layouts.js.map +1 -1
  89. package/dist/backend/llvm/lib-abi.js +10 -0
  90. package/dist/backend/llvm/lib-abi.js.map +1 -1
  91. package/dist/backend/llvm/lib-filesystem.js +25 -13
  92. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  93. package/dist/backend/llvm/lib-process.js +8 -8
  94. package/dist/backend/llvm/lib-process.js.map +1 -1
  95. package/dist/backend/llvm/lib-shared.js +2 -0
  96. package/dist/backend/llvm/lib-shared.js.map +1 -1
  97. package/dist/backend/llvm/local-array-reads.d.ts +17 -6
  98. package/dist/backend/llvm/local-array-reads.js +223 -51
  99. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  100. package/dist/backend/llvm/local-array-reads.test.js +7 -3
  101. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  102. package/dist/backend/llvm/local-union-storage.js +10 -6
  103. package/dist/backend/llvm/local-union-storage.js.map +1 -1
  104. package/dist/backend/llvm/local-union-storage.test.js +3 -2
  105. package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
  106. package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
  107. package/dist/backend/llvm/map-number-lookup.js +62 -0
  108. package/dist/backend/llvm/map-number-lookup.js.map +1 -0
  109. package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
  110. package/dist/backend/llvm/map-read-lifetimes.js +24 -3
  111. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  112. package/dist/backend/llvm/map-read-lifetimes.test.js +38 -19
  113. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  114. package/dist/backend/llvm/nullable-unions.d.ts +68 -0
  115. package/dist/backend/llvm/nullable-unions.js +132 -0
  116. package/dist/backend/llvm/nullable-unions.js.map +1 -0
  117. package/dist/backend/llvm/publish.d.ts +58 -0
  118. package/dist/backend/llvm/publish.js +286 -0
  119. package/dist/backend/llvm/publish.js.map +1 -0
  120. package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
  121. package/dist/backend/llvm/rebound-parameters.js +72 -0
  122. package/dist/backend/llvm/rebound-parameters.js.map +1 -0
  123. package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
  124. package/dist/backend/llvm/rebound-parameters.test.js +67 -0
  125. package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
  126. package/dist/backend/llvm/reference-effects.d.ts +18 -2
  127. package/dist/backend/llvm/reference-effects.js +140 -17
  128. package/dist/backend/llvm/reference-effects.js.map +1 -1
  129. package/dist/backend/llvm/reference-effects.test.js +52 -1
  130. package/dist/backend/llvm/reference-effects.test.js.map +1 -1
  131. package/dist/backend/llvm/shapes.d.ts +58 -19
  132. package/dist/backend/llvm/shapes.js +171 -62
  133. package/dist/backend/llvm/shapes.js.map +1 -1
  134. package/dist/backend/llvm/shapes.test.js +36 -11
  135. package/dist/backend/llvm/shapes.test.js.map +1 -1
  136. package/dist/backend/llvm/stack-unions.test.js +5 -2
  137. package/dist/backend/llvm/stack-unions.test.js.map +1 -1
  138. package/dist/backend/llvm/string-construction.d.ts +10 -0
  139. package/dist/backend/llvm/string-construction.js +42 -0
  140. package/dist/backend/llvm/string-construction.js.map +1 -1
  141. package/dist/backend/llvm/string-key-hash.d.ts +9 -2
  142. package/dist/backend/llvm/string-key-hash.js +20 -3
  143. package/dist/backend/llvm/string-key-hash.js.map +1 -1
  144. package/dist/backend/llvm/string-key-hash.test.js +14 -3
  145. package/dist/backend/llvm/string-key-hash.test.js.map +1 -1
  146. package/dist/backend/llvm/string-lifetimes.test.js +24 -11
  147. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  148. package/dist/backend/llvm/string-reads.d.ts +7 -0
  149. package/dist/backend/llvm/string-reads.js +79 -0
  150. package/dist/backend/llvm/string-reads.js.map +1 -0
  151. package/dist/backend/llvm/string-slices.js +1 -1
  152. package/dist/backend/llvm/switch-dispatch.test.js +1 -1
  153. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  154. package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
  155. package/dist/backend/llvm/test-boxed-unions.js +13 -0
  156. package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
  157. package/dist/backend/llvm/trunc.d.ts +8 -0
  158. package/dist/backend/llvm/trunc.js +28 -0
  159. package/dist/backend/llvm/trunc.js.map +1 -0
  160. package/dist/backend/llvm/union-repr.d.ts +15 -0
  161. package/dist/backend/llvm/union-repr.js +89 -0
  162. package/dist/backend/llvm/union-repr.js.map +1 -0
  163. package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
  164. package/dist/backend/llvm/virtual-borrows.js +82 -0
  165. package/dist/backend/llvm/virtual-borrows.js.map +1 -0
  166. package/dist/backend/llvm/walk-borrows.d.ts +43 -0
  167. package/dist/backend/llvm/walk-borrows.js +150 -0
  168. package/dist/backend/llvm/walk-borrows.js.map +1 -0
  169. package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
  170. package/dist/backend/llvm/walk-borrows.test.js +119 -0
  171. package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
  172. package/dist/backend/llvm/walkers.d.ts +5 -1
  173. package/dist/backend/llvm/walkers.js +24 -21
  174. package/dist/backend/llvm/walkers.js.map +1 -1
  175. package/dist/backend/loop-polls.d.ts +14 -0
  176. package/dist/backend/loop-polls.js +100 -0
  177. package/dist/backend/loop-polls.js.map +1 -0
  178. package/dist/backend/loop-polls.test.d.ts +1 -0
  179. package/dist/backend/loop-polls.test.js +86 -0
  180. package/dist/backend/loop-polls.test.js.map +1 -0
  181. package/dist/backend/may-throw.d.ts +29 -1
  182. package/dist/backend/may-throw.js +365 -3
  183. package/dist/backend/may-throw.js.map +1 -1
  184. package/dist/backend/may-throw.test.js +136 -1
  185. package/dist/backend/may-throw.test.js.map +1 -1
  186. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  187. package/dist/backend/native/runtime-inputs.js +1 -0
  188. package/dist/backend/native/runtime-inputs.js.map +1 -1
  189. package/dist/backend/native-codegen-core.d.ts +7 -5
  190. package/dist/backend/native-codegen-core.js +14 -9
  191. package/dist/backend/native-codegen-core.js.map +1 -1
  192. package/dist/backend/native-codegen.d.ts +2 -0
  193. package/dist/backend/native-codegen.js +21 -2
  194. package/dist/backend/native-codegen.js.map +1 -1
  195. package/dist/backend/native-codegen.test.js +41 -10
  196. package/dist/backend/native-codegen.test.js.map +1 -1
  197. package/dist/backend/native-tools.d.ts +2 -0
  198. package/dist/backend/native-tools.js +3 -0
  199. package/dist/backend/native-tools.js.map +1 -1
  200. package/dist/backend/publication.d.ts +28 -0
  201. package/dist/backend/publication.js +155 -0
  202. package/dist/backend/publication.js.map +1 -0
  203. package/dist/backend/publication.test.d.ts +1 -0
  204. package/dist/backend/publication.test.js +72 -0
  205. package/dist/backend/publication.test.js.map +1 -0
  206. package/dist/backend/runtime-abi.d.ts +2 -2
  207. package/dist/backend/runtime-abi.js +2 -2
  208. package/dist/backend/runtime-abi.js.map +1 -1
  209. package/dist/backend/runtime-pack-targets.test.js +1 -1
  210. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  211. package/dist/backend/runtime-pack.test.js +1 -1
  212. package/dist/backend/runtime-pack.test.js.map +1 -1
  213. package/dist/cli/command.js +78 -8
  214. package/dist/cli/command.js.map +1 -1
  215. package/dist/cli/output-options.d.ts +1 -0
  216. package/dist/cli/output-options.js +3 -0
  217. package/dist/cli/output-options.js.map +1 -1
  218. package/dist/cli/usage.d.ts +8 -1
  219. package/dist/cli/usage.js +14 -1
  220. package/dist/cli/usage.js.map +1 -1
  221. package/dist/compile-types.d.ts +17 -3
  222. package/dist/coverage/report.d.ts +25 -2
  223. package/dist/coverage/report.js +161 -33
  224. package/dist/coverage/report.js.map +1 -1
  225. package/dist/coverage/verdict-cache.d.ts +63 -0
  226. package/dist/coverage/verdict-cache.js +193 -0
  227. package/dist/coverage/verdict-cache.js.map +1 -0
  228. package/dist/coverage/verdict-cache.test.d.ts +1 -0
  229. package/dist/coverage/verdict-cache.test.js +52 -0
  230. package/dist/coverage/verdict-cache.test.js.map +1 -0
  231. package/dist/coverage/verdict-semantic.d.ts +2 -0
  232. package/dist/coverage/verdict-semantic.js +24 -0
  233. package/dist/coverage/verdict-semantic.js.map +1 -0
  234. package/dist/coverage/verdict-startup.d.ts +15 -0
  235. package/dist/coverage/verdict-startup.js +66 -0
  236. package/dist/coverage/verdict-startup.js.map +1 -0
  237. package/dist/diagnostics/diagnostic.d.ts +29 -1
  238. package/dist/diagnostics/diagnostic.js +74 -4
  239. package/dist/diagnostics/diagnostic.js.map +1 -1
  240. package/dist/diagnostics/envelope.d.ts +130 -0
  241. package/dist/diagnostics/envelope.js +257 -0
  242. package/dist/diagnostics/envelope.js.map +1 -0
  243. package/dist/diagnostics/envelope.test.d.ts +1 -0
  244. package/dist/diagnostics/envelope.test.js +91 -0
  245. package/dist/diagnostics/envelope.test.js.map +1 -0
  246. package/dist/diagnostics/render.d.ts +3 -0
  247. package/dist/diagnostics/render.js +13 -2
  248. package/dist/diagnostics/render.js.map +1 -1
  249. package/dist/executable/executable-cache.d.ts +2 -0
  250. package/dist/executable/executable-cache.js +3 -0
  251. package/dist/executable/executable-cache.js.map +1 -1
  252. package/dist/executable/executable-cache.test.js +1 -0
  253. package/dist/executable/executable-cache.test.js.map +1 -1
  254. package/dist/executable/prepare.d.ts +4 -0
  255. package/dist/executable/prepare.js +7 -1
  256. package/dist/executable/prepare.js.map +1 -1
  257. package/dist/frontend/analysis.js +1 -0
  258. package/dist/frontend/analysis.js.map +1 -1
  259. package/dist/frontend/lowering/array-values.d.ts +7 -0
  260. package/dist/frontend/lowering/array-values.js +157 -5
  261. package/dist/frontend/lowering/array-values.js.map +1 -1
  262. package/dist/frontend/lowering/builtins/numbers.js +29 -1
  263. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  264. package/dist/frontend/lowering/builtins/process.d.ts +4 -0
  265. package/dist/frontend/lowering/builtins/process.js +10 -0
  266. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  267. package/dist/frontend/lowering/builtins/strings.js +4 -1
  268. package/dist/frontend/lowering/builtins/strings.js.map +1 -1
  269. package/dist/frontend/lowering/builtins/workers.js +4 -2
  270. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  271. package/dist/frontend/lowering/call-arguments.js +96 -4
  272. package/dist/frontend/lowering/call-arguments.js.map +1 -1
  273. package/dist/frontend/lowering/class-assertions.d.ts +8 -0
  274. package/dist/frontend/lowering/class-assertions.js +217 -1
  275. package/dist/frontend/lowering/class-assertions.js.map +1 -1
  276. package/dist/frontend/lowering/class-dynamic-dispatch.js +8 -5
  277. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  278. package/dist/frontend/lowering/class-method-mutations.js +88 -0
  279. package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
  280. package/dist/frontend/lowering/coercions/state.d.ts +6 -0
  281. package/dist/frontend/lowering/coercions/state.js +2 -0
  282. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  283. package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
  284. package/dist/frontend/lowering/coercions/unions.js +67 -2
  285. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  286. package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
  287. package/dist/frontend/lowering/collation-repertoire.js +19 -0
  288. package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
  289. package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
  290. package/dist/frontend/lowering/construction-escapes.js +146 -0
  291. package/dist/frontend/lowering/construction-escapes.js.map +1 -0
  292. package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
  293. package/dist/frontend/lowering/construction-escapes.test.js +150 -0
  294. package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
  295. package/dist/frontend/lowering/containers/array-callbacks.js +81 -44
  296. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  297. package/dist/frontend/lowering/containers/array-construction.js +15 -2
  298. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  299. package/dist/frontend/lowering/containers/array-methods.js +231 -12
  300. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  301. package/dist/frontend/lowering/containers/bytes.js +15 -4
  302. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  303. package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
  304. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  305. package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
  306. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  307. package/dist/frontend/lowering/cycle-initialization.d.ts +6 -1
  308. package/dist/frontend/lowering/cycle-initialization.js +59 -33
  309. package/dist/frontend/lowering/cycle-initialization.js.map +1 -1
  310. package/dist/frontend/lowering/divergences.d.ts +9 -0
  311. package/dist/frontend/lowering/divergences.js +630 -0
  312. package/dist/frontend/lowering/divergences.js.map +1 -0
  313. package/dist/frontend/lowering/expressions/object-literals.js +40 -2
  314. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  315. package/dist/frontend/lowering/expressions/union-fields.d.ts +19 -0
  316. package/dist/frontend/lowering/expressions/union-fields.js +236 -0
  317. package/dist/frontend/lowering/expressions/union-fields.js.map +1 -0
  318. package/dist/frontend/lowering/lower-calls.d.ts +5 -0
  319. package/dist/frontend/lowering/lower-calls.js +237 -158
  320. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  321. package/dist/frontend/lowering/lower-classes.d.ts +4 -0
  322. package/dist/frontend/lowering/lower-classes.js +72 -22
  323. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  324. package/dist/frontend/lowering/lower-exprs.d.ts +19 -0
  325. package/dist/frontend/lowering/lower-exprs.js +592 -109
  326. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  327. package/dist/frontend/lowering/lower-generators.d.ts +19 -13
  328. package/dist/frontend/lowering/lower-generators.js +67 -44
  329. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  330. package/dist/frontend/lowering/lower-inspect.js +3 -2
  331. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  332. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  333. package/dist/frontend/lowering/lower-island.js +10 -7
  334. package/dist/frontend/lowering/lower-island.js.map +1 -1
  335. package/dist/frontend/lowering/lower-modules.js +90 -1
  336. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  337. package/dist/frontend/lowering/lower-stmts.js +47 -7
  338. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  339. package/dist/frontend/lowering/lowerer.d.ts +83 -2
  340. package/dist/frontend/lowering/lowerer.js +1158 -79
  341. package/dist/frontend/lowering/lowerer.js.map +1 -1
  342. package/dist/frontend/lowering/nan-coded-reads.d.ts +161 -0
  343. package/dist/frontend/lowering/nan-coded-reads.js +1342 -0
  344. package/dist/frontend/lowering/nan-coded-reads.js.map +1 -0
  345. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +31 -0
  346. package/dist/frontend/lowering/runtime-optional-bounds.js +360 -0
  347. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
  348. package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
  349. package/dist/frontend/lowering/runtime-optional-bounds.test.js +94 -0
  350. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
  351. package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
  352. package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
  353. package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
  354. package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
  355. package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
  356. package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
  357. package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
  358. package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
  359. package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
  360. package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
  361. package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
  362. package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
  363. package/dist/frontend/lowering/runtime-optional-provenance.d.ts +2 -1
  364. package/dist/frontend/lowering/runtime-optional-provenance.js +2 -1
  365. package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -1
  366. package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
  367. package/dist/frontend/lowering/string-index-bounds.js +537 -0
  368. package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
  369. package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
  370. package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
  371. package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
  372. package/dist/frontend/lowering/surfaces.js +8 -0
  373. package/dist/frontend/lowering/surfaces.js.map +1 -1
  374. package/dist/frontend/lowering/union-narrow.d.ts +2 -0
  375. package/dist/frontend/lowering/union-narrow.js +10 -6
  376. package/dist/frontend/lowering/union-narrow.js.map +1 -1
  377. package/dist/frontend/program-analysis.d.ts +10 -0
  378. package/dist/frontend/program-analysis.js +7 -0
  379. package/dist/frontend/program-analysis.js.map +1 -1
  380. package/dist/frontend/program.d.ts +25 -0
  381. package/dist/frontend/program.js +613 -17
  382. package/dist/frontend/program.js.map +1 -1
  383. package/dist/frontend/program.test.js +64 -1
  384. package/dist/frontend/program.test.js.map +1 -1
  385. package/dist/frontend/resolve.d.ts +6 -0
  386. package/dist/frontend/resolve.js +14 -0
  387. package/dist/frontend/resolve.js.map +1 -1
  388. package/dist/frontend/threads-import.d.ts +5 -0
  389. package/dist/frontend/threads-import.js +21 -0
  390. package/dist/frontend/threads-import.js.map +1 -0
  391. package/dist/frontend/type-mapper.d.ts +3 -0
  392. package/dist/frontend/type-mapper.js +72 -6
  393. package/dist/frontend/type-mapper.js.map +1 -1
  394. package/dist/frontend/worker-target.d.ts +4 -0
  395. package/dist/frontend/worker-target.js +8 -1
  396. package/dist/frontend/worker-target.js.map +1 -1
  397. package/dist/index.d.ts +7 -2
  398. package/dist/index.js +67 -17
  399. package/dist/index.js.map +1 -1
  400. package/dist/ir/build.d.ts +5 -0
  401. package/dist/ir/build.js +7 -0
  402. package/dist/ir/build.js.map +1 -1
  403. package/dist/ir/builtin-signatures.d.ts +1 -1
  404. package/dist/ir/builtin-signatures.js +5 -0
  405. package/dist/ir/builtin-signatures.js.map +1 -1
  406. package/dist/ir/constant-globals.d.ts +14 -0
  407. package/dist/ir/constant-globals.js +81 -0
  408. package/dist/ir/constant-globals.js.map +1 -0
  409. package/dist/ir/constant-globals.test.d.ts +1 -0
  410. package/dist/ir/constant-globals.test.js +97 -0
  411. package/dist/ir/constant-globals.test.js.map +1 -0
  412. package/dist/ir/final-fields.d.ts +35 -0
  413. package/dist/ir/final-fields.js +187 -0
  414. package/dist/ir/final-fields.js.map +1 -0
  415. package/dist/ir/final-fields.test.d.ts +1 -0
  416. package/dist/ir/final-fields.test.js +106 -0
  417. package/dist/ir/final-fields.test.js.map +1 -0
  418. package/dist/ir/int32-slots.d.ts +81 -0
  419. package/dist/ir/int32-slots.js +850 -0
  420. package/dist/ir/int32-slots.js.map +1 -0
  421. package/dist/ir/int32-slots.test.d.ts +1 -0
  422. package/dist/ir/int32-slots.test.js +249 -0
  423. package/dist/ir/int32-slots.test.js.map +1 -0
  424. package/dist/ir/integer-loops.d.ts +5 -0
  425. package/dist/ir/integer-loops.js +20 -4
  426. package/dist/ir/integer-loops.js.map +1 -1
  427. package/dist/ir/integer-loops.test.js +17 -0
  428. package/dist/ir/integer-loops.test.js.map +1 -1
  429. package/dist/ir/integer-ranges.d.ts +19 -3
  430. package/dist/ir/integer-ranges.js +168 -23
  431. package/dist/ir/integer-ranges.js.map +1 -1
  432. package/dist/ir/integer-ranges.test.js +74 -2
  433. package/dist/ir/integer-ranges.test.js.map +1 -1
  434. package/dist/ir/ir.d.ts +30 -7
  435. package/dist/ir/ir.js +4 -0
  436. package/dist/ir/ir.js.map +1 -1
  437. package/dist/ir/serialize.d.ts +12 -2
  438. package/dist/ir/serialize.js +190 -39
  439. package/dist/ir/serialize.js.map +1 -1
  440. package/dist/ir/serialize.test.js +65 -1
  441. package/dist/ir/serialize.test.js.map +1 -1
  442. package/dist/ir/validate.js +21 -2
  443. package/dist/ir/validate.js.map +1 -1
  444. package/dist/ir/validate.test.js +39 -0
  445. package/dist/ir/validate.test.js.map +1 -1
  446. package/dist/native/cache.d.ts +11 -1
  447. package/dist/native/cache.js +87 -2
  448. package/dist/native/cache.js.map +1 -1
  449. package/dist/native/cache.test.js +32 -1
  450. package/dist/native/cache.test.js.map +1 -1
  451. package/dist/native/cli.js +1 -1
  452. package/dist/native/cli.js.map +1 -1
  453. package/dist/native/compiler.d.ts +5 -0
  454. package/dist/native/compiler.js +55 -7
  455. package/dist/native/compiler.js.map +1 -1
  456. package/dist/native/prepare.d.ts +7 -1
  457. package/dist/native/prepare.js +65 -29
  458. package/dist/native/prepare.js.map +1 -1
  459. package/dist/native/prepare.test.js +23 -1
  460. package/dist/native/prepare.test.js.map +1 -1
  461. package/dist/native/text-file.d.ts +10 -0
  462. package/dist/native/text-file.js +41 -0
  463. package/dist/native/text-file.js.map +1 -0
  464. package/dist/startup-cache.d.ts +2 -0
  465. package/dist/startup-cache.js +2 -0
  466. package/dist/startup-cache.js.map +1 -1
  467. package/dist/text-parts.d.ts +5 -0
  468. package/dist/text-parts.js +36 -0
  469. package/dist/text-parts.js.map +1 -0
  470. package/package.json +20 -20
  471. package/surface-manifest.json +7 -1
  472. package/dist/frontend/lowering/expressions/union-field-write.d.ts +0 -8
  473. package/dist/frontend/lowering/expressions/union-field-write.js +0 -108
  474. package/dist/frontend/lowering/expressions/union-field-write.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { checkCycleBindingAccesses, refuseUncheckedCycleReads, } from "./cycle-initialization.js";
2
2
  import { identityPreservingWidening } from "./coercions/identity.js";
3
+ import { scanDivergences } from "./divergences.js";
3
4
  import { checkedClassAssertion } from "./class-assertions.js";
4
5
  import { narrowStoredClassValue, narrowGenericClassValue } from "./class-unions.js";
5
6
  import { isOptionalProcessStreamProperty } from "./builtins/process.js";
@@ -12,11 +13,17 @@ import { CoercionState } from "./coercions/state.js";
12
13
  import { widthLiftPlan, unitOnlyElem, recordWidthPlan, describeRecordWidthBlocker, objToRecordPlan, recordToClassPlan, } from "./coercions/structural-plans.js";
13
14
  import { widthCoerce, applyWidthLift, unionRecordWidthHelper, recordWidthHelper, tupleArrayWidthHelper, emptyArrayLiftHelper, arrayWidthHelper, objRecordWidthHelper, recordClassWidthHelper, classStaticsProjection, } from "./coercions/structural.js";
14
15
  import { funcReturnWidthAdapter, coercibleValue, cleanFuncAdaptable, funcCoerceAdapter, spawnResFnAdapterPlan, spawnResFnAdapter, } from "./coercions/function-adapters.js";
15
- 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";
16
17
  import { jsvalLiftable, jsvalLiftExpr, unionToJsvalHelper, recordToJsvalHelper, arrayToJsvalHelper, arrayToJsvalArrayHelper, } from "./coercions/island.js";
17
18
  import { lowerUtilTypeValue, pathModuleValue, utilTypesModuleValue, } from "./lower-builtin-values.js";
18
19
  import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
19
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";
26
+ import { NanCodedReads } from "./nan-coded-reads.js";
20
27
  import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
21
28
  import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
22
29
  import { UnregisteredClassTypes } from "./unregistered-class-types.js";
@@ -52,7 +59,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
52
59
  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";
53
60
  import { provenanceActive } from "../provenance-registry.js";
54
61
  import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
55
- import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
62
+ import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
56
63
  import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
57
64
  import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
58
65
  import { prepareCjsModuleGraph } from "./lower-node-module.js";
@@ -197,6 +204,39 @@ export function stmtUsesIsland(stmts) {
197
204
  return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
198
205
  return !everyStmtChild(stmts, expr, nested);
199
206
  }
207
+ /** Statements a never-lowered body would have contributed to the coverage
208
+ * totals: every element of a statement list (blocks, case clauses), plus
209
+ * the single-statement bodies of control flow, nested functions included —
210
+ * the same accounting lowerStmts applies to bodies that do lower. */
211
+ export function countBodyStatements(root) {
212
+ let n = 0;
213
+ const visit = (node) => {
214
+ if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
215
+ n += node.statements.length;
216
+ }
217
+ else if ((ts.isIfStatement(node) ||
218
+ ts.isForStatement(node) ||
219
+ ts.isForInStatement(node) ||
220
+ ts.isForOfStatement(node) ||
221
+ ts.isWhileStatement(node) ||
222
+ ts.isDoStatement(node) ||
223
+ ts.isLabeledStatement(node)) &&
224
+ node.parent !== undefined) {
225
+ const bodies = ts.isIfStatement(node)
226
+ ? [node.thenStatement, node.elseStatement]
227
+ : ts.isLabeledStatement(node)
228
+ ? [node.statement]
229
+ : [node.statement];
230
+ for (const body of bodies) {
231
+ if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
232
+ n++;
233
+ }
234
+ }
235
+ };
236
+ // Iterative: absurdly deep nesting must not overflow the stack here.
237
+ ts.walkPreorder(root, visit);
238
+ return n;
239
+ }
200
240
  function directExternalTypeSpecifiersByFile(externalTypes) {
201
241
  const out = new Map();
202
242
  for (const [specifier, file] of externalTypes) {
@@ -349,6 +389,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
349
389
  resultLowerer = emit;
350
390
  timing("historical-order-relower");
351
391
  }
392
+ // Divergence warnings over the program's own modules: computed once,
393
+ // after lowering, from the same checker (never part of a failure).
394
+ const divergences = scanDivergences(resultLowerer, moduleOrder);
395
+ timing("divergence-scan", { found: divergences.length });
396
+ if (divergences.length > 0)
397
+ result = { ...result, divergences };
352
398
  if (options.coverage !== true)
353
399
  return result;
354
400
  const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
@@ -366,7 +412,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
366
412
  externalTypeSpecifiersByFile,
367
413
  });
368
414
  const rem = remainder.run();
369
- return { ...result, unreached: { diagnostics: rem.diagnostics, stats: rem.stats } };
415
+ // Unreached JS statements whose fences deferred to runtime count as
416
+ // failed statements in the remainder's stats, so their diagnostics must
417
+ // travel with them: a failed statement is never left unexplained.
418
+ return {
419
+ ...result,
420
+ unreached: {
421
+ diagnostics: rem.diagnostics,
422
+ stats: rem.stats,
423
+ ...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
424
+ },
425
+ };
370
426
  }
371
427
  /** The island-handle type a `import(...)` initializer gives a binding
372
428
  * whose DECLARED type has no static mapping (`Promise<typeof
@@ -882,6 +938,17 @@ export function jsFuncNameOf(node, lowerer) {
882
938
  }
883
939
  return null;
884
940
  }
941
+ /** Answers `fallback` when a checker query panics inside the type checker. */
942
+ function panicSafe(query, fallback) {
943
+ try {
944
+ return query();
945
+ }
946
+ catch (e) {
947
+ if (isCheckerPanic(e))
948
+ return fallback;
949
+ throw e;
950
+ }
951
+ }
885
952
  export class Lowerer {
886
953
  program;
887
954
  entry;
@@ -910,6 +977,18 @@ export class Lowerer {
910
977
  /** Per-symbol result of the never-reassigned file scan
911
978
  * (bindingNeverReassigned — object-literal generic-method receivers). */
912
979
  neverReassignedCache = new Map();
980
+ /** Typed-array element reads whose every consumer treats undefined like
981
+ * NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
982
+ * analyzeRuntimeOptionalArrayReads before any body lowers. */
983
+ nanCodedReads = null;
984
+ stringIndexBoundsCache = null;
985
+ /** Proofs that a string element read names an existing code unit. The
986
+ * indexed-read analysis and expression lowering share one instance, so a
987
+ * read's representation and its callers' signatures always agree. */
988
+ get stringIndexBounds() {
989
+ this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
990
+ return this.stringIndexBoundsCache;
991
+ }
913
992
  /** Syntactic writes indexed once per JS scope. Symbol and RHS type
914
993
  * checks remain contextual to each binding probe. */
915
994
  jsBindingWritesByOwner = new Map();
@@ -1156,6 +1235,11 @@ export class Lowerer {
1156
1235
  * prepass. This includes explicit annotations: unchecked TS annotations do
1157
1236
  * not prove that an array property read produced a value. */
1158
1237
  runtimeOptionalBindingTypes = new Map();
1238
+ /** Bindings that receive an unchecked indexed read (or a value derived
1239
+ * from one) through an initializer or assignment. The checker narrows a
1240
+ * declared optional binding to that read's bare type, so presence tests
1241
+ * on these bindings consult the stored tag instead. */
1242
+ runtimeOptionalAssignedSymbols = new Set();
1159
1243
  /** Concise callback/lambda returns promoted by the HOF callback prepass;
1160
1244
  * arrows have no declaration symbol to key in fnSigsBySymbol. */
1161
1245
  runtimeOptionalFunctionReturns = new Map();
@@ -1173,6 +1257,17 @@ export class Lowerer {
1173
1257
  * every name destructured from a present source optional. */
1174
1258
  runtimeOptionalPatternTypes = new Map();
1175
1259
  runtimeOptionalReduceTypes = new Map();
1260
+ /** Non-null assertions whose value is forwarded into a slot promoted to
1261
+ * hold undefined. `x!` has no runtime effect, so these keep the stored
1262
+ * undefined state instead of trapping before any dereference. */
1263
+ runtimeOptionalAssertions = new Set();
1264
+ /** Array callback calls whose receiver the optional-read analysis proved
1265
+ * never holds an explicit undefined (or a hole, for the find family):
1266
+ * their callbacks take the element type itself. */
1267
+ presentElementCalls = new Set();
1268
+ /** for-of loops over arrays that can hold neither holes nor undefined:
1269
+ * each pass reads the element payload directly. */
1270
+ presentElementLoops = new Set();
1176
1271
  /** Capture entries and their origin share one mutable box. Normalize each
1177
1272
  * entry to the origin so writes and flow proofs stay synchronized. */
1178
1273
  runtimeOptionalRoots = new Map();
@@ -1247,6 +1342,9 @@ export class Lowerer {
1247
1342
  * optional-read analysis, so ordinary assertions keep their generic
1248
1343
  * checked-narrow behavior. */
1249
1344
  runtimeOptionalSourceValue(node, value) {
1345
+ const slot = this.deferredFieldSlot(value);
1346
+ if (slot)
1347
+ return slot;
1250
1348
  let origin = node;
1251
1349
  while (ts.isParenthesizedExpression(origin))
1252
1350
  origin = origin.expression;
@@ -1281,12 +1379,9 @@ export class Lowerer {
1281
1379
  value.value.type.kind === "dyn")
1282
1380
  return value.value;
1283
1381
  let source = value;
1284
- if (value.kind === "call" &&
1285
- this.coercions.checkedNarrows.has(value.callee) &&
1286
- value.args.length === 1 &&
1287
- value.args[0]?.type.kind === "union") {
1288
- source = value.args[0];
1289
- }
1382
+ const narrowed = checkedNarrowOperand(this, value);
1383
+ if (narrowed?.type.kind === "union")
1384
+ source = narrowed;
1290
1385
  return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
1291
1386
  ? source
1292
1387
  : null;
@@ -1399,6 +1494,41 @@ export class Lowerer {
1399
1494
  runtimeOptionalFunctionReturnType(node, fallback) {
1400
1495
  return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
1401
1496
  }
1497
+ /** True when `actual` is `declared` with some record fields (directly,
1498
+ * in nested records, or in an array's element records) widened to runtime-optional unions:
1499
+ * fields filled from unchecked indexed reads the checker calls present.
1500
+ * Inferred bindings and destructured parameters keep that layout instead
1501
+ * of narrowing it away. */
1502
+ isRuntimeOptionalRecordVariant(actual, declared) {
1503
+ if (actual.kind === "array" && declared.kind === "array")
1504
+ return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
1505
+ if (actual.kind !== "record" || declared.kind !== "record")
1506
+ return false;
1507
+ if (actual.shapeId === declared.shapeId)
1508
+ return false;
1509
+ const a = this.shapes.get(actual.shapeId);
1510
+ const d = this.shapes.get(declared.shapeId);
1511
+ if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
1512
+ return false;
1513
+ if (a.indexValue || d.indexValue)
1514
+ return false;
1515
+ let widened = false;
1516
+ for (const field of d.fields) {
1517
+ const source = a.fields.find((f) => f.name === field.name);
1518
+ if (!source)
1519
+ return false;
1520
+ if (typeEquals(source.type, field.type))
1521
+ continue;
1522
+ if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
1523
+ (this.runtimeOptionalWidening(source.type, field.type) !== null ||
1524
+ this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
1525
+ widened = true;
1526
+ continue;
1527
+ }
1528
+ return false;
1529
+ }
1530
+ return widened;
1531
+ }
1402
1532
  /** Promote one field of a record storage shape to the value's runtime
1403
1533
  * type. Shapes are immutable by identity, so this interns a sibling shape
1404
1534
  * and records the field as runtime-optional for property-read narrowing. */
@@ -1699,6 +1829,9 @@ export class Lowerer {
1699
1829
  * and therefore becomes the cycle's evaluation root. Dynamic imports
1700
1830
  * wait on this shared verdict rather than a build-time-selected member. */
1701
1831
  asyncCyclePromiseOf = new Map();
1832
+ /** Modules whose evaluation an import() can request again after it threw,
1833
+ * computed on first use (lower-modules.ts importRetryModules). */
1834
+ importRetryModules = null;
1702
1835
  /** Record-shape interner: canonical (name-sorted) field list → shapeId.
1703
1836
  * Threaded into every mapType call; its `shapes` array becomes
1704
1837
  * IrModule.records. */
@@ -1795,6 +1928,13 @@ export class Lowerer {
1795
1928
  * coverage remainder as alreadyFlushed). */
1796
1929
  flushedSymbols = new Set();
1797
1930
  alreadyFlushed;
1931
+ /** Deferred diagnostics a flush handed to a capture buffer (diagSink)
1932
+ * instead of the build, keyed by the copy in that buffer. A buffer can
1933
+ * be discarded (a declined probe, a collection that succeeded) or
1934
+ * re-deferred under another declaration, so the declaration stays
1935
+ * deferred until one of these copies reaches `diags` (pushDiag commits
1936
+ * it); until then a later flush reports the diagnostics again. */
1937
+ pendingFlushes = new Map();
1798
1938
  /** The build's target platform ("win32" | "darwin" | "linux" | ...):
1799
1939
  * selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
1800
1940
  * builtinModuleConstOf in surfaces.ts). */
@@ -1973,6 +2113,12 @@ export class Lowerer {
1973
2113
  this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
1974
2114
  return this.classProtocols.usesCheckedIdentity(type);
1975
2115
  }
2116
+ /** Declared class fields may store a DYN value only when its static type
2117
+ * names checked protocol identity or an owned runtime handle (a Worker
2118
+ * or MessagePort); `unknown` and `any` fields stay refused. */
2119
+ classFieldStoresDyn(type) {
2120
+ return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
2121
+ }
1976
2122
  /** The nominal identity of a checker module-namespace type. Program
1977
2123
  * modules use their normalized absolute source name; supported builtins
1978
2124
  * use the lowering tables' canonical bare name. */
@@ -2992,6 +3138,32 @@ export class Lowerer {
2992
3138
  }
2993
3139
  }
2994
3140
  }
3141
+ const mappedOrNull = (node) => panicSafe(() => this.mapTypeOf(this.typeOf(node)), null);
3142
+ const nanCoded = new NanCodedReads({
3143
+ symbolOf,
3144
+ declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
3145
+ bytesElemOf: (receiver) => {
3146
+ const t = mappedOrNull(receiver);
3147
+ return t?.kind === "bytes" ? t.elem : null;
3148
+ },
3149
+ isNumber: (node) => mappedOrNull(node)?.kind === "f64",
3150
+ isString: (node) => mappedOrNull(node)?.kind === "string",
3151
+ hasLength: (node) => {
3152
+ const kind = mappedOrNull(node)?.kind;
3153
+ return kind === "array" || kind === "bytes";
3154
+ },
3155
+ isStdlibGlobal: (node, name) => this.isStdlibGlobal(node, name),
3156
+ paramIsNumber: (fn, index) => {
3157
+ const param = signatureBySymbol.get(fn)?.params[index];
3158
+ return (param !== undefined &&
3159
+ param.type.kind === "f64" &&
3160
+ (param.mode === "required" || param.mode === "omittable"));
3161
+ },
3162
+ returnIsNumber: (fn) => signatureBySymbol.get(fn)?.returnType.kind === "f64",
3163
+ methodOverridden: (fn) => (familyBySymbol.get(fn)?.length ?? 1) > 1,
3164
+ }, sourceFiles);
3165
+ nanCoded.analyze();
3166
+ this.nanCodedReads = nanCoded;
2995
3167
  const peel = (node) => {
2996
3168
  let e = node;
2997
3169
  while (ts.isParenthesizedExpression(e) ||
@@ -3043,12 +3215,79 @@ export class Lowerer {
3043
3215
  }
3044
3216
  return false;
3045
3217
  };
3218
+ // `parent!` passed to a call or bound to a name forwards the value
3219
+ // without reading it: a callee that never dereferences it observes
3220
+ // undefined, as in JavaScript. Only a class value with one undefined
3221
+ // arm qualifies; element reads keep the established bounds trap.
3222
+ const forwardedAssertion = (node) => {
3223
+ let e = node;
3224
+ while (ts.isParenthesizedExpression(e))
3225
+ e = e.expression;
3226
+ if (!ts.isNonNullExpression(e))
3227
+ return null;
3228
+ let operand = e.expression;
3229
+ while (ts.isParenthesizedExpression(operand))
3230
+ operand = operand.expression;
3231
+ if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
3232
+ return null;
3233
+ // A guard that already proves the binding present makes the
3234
+ // assertion true at runtime: nothing is forwarded as undefined.
3235
+ if (ts.isIdentifier(operand)) {
3236
+ const symbol = symbolOf(operand);
3237
+ if (symbol && provenPresent(operand, symbol))
3238
+ return null;
3239
+ }
3240
+ const stored = this.mapTypeOf(this.typeOf(e.expression));
3241
+ const asserted = this.mapTypeOf(this.typeOf(e));
3242
+ if (stored?.kind !== "union" || asserted?.kind !== "object")
3243
+ return null;
3244
+ const arms = this.unions.get(stored.unionId)?.arms;
3245
+ if (arms?.length !== 2 ||
3246
+ this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
3247
+ !arms.some((arm) => typeEquals(arm, asserted)))
3248
+ return null;
3249
+ return e;
3250
+ };
3251
+ // `decode(xs[i]!)`: a missing element is passed as undefined in
3252
+ // JavaScript, and the callee may never read it.
3253
+ const forwardedElementAssertion = (node) => {
3254
+ let e = node;
3255
+ while (ts.isParenthesizedExpression(e))
3256
+ e = e.expression;
3257
+ if (!ts.isNonNullExpression(e))
3258
+ return null;
3259
+ let read = e.expression;
3260
+ while (ts.isParenthesizedExpression(read))
3261
+ read = read.expression;
3262
+ if (!ts.isElementAccessExpression(read))
3263
+ return null;
3264
+ if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
3265
+ return null;
3266
+ // An in-bounds read of an array that never holds undefined or holes
3267
+ // always produces a present element.
3268
+ if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
3269
+ return null;
3270
+ const element = this.mapTypeOf(this.typeOf(e));
3271
+ if (!element ||
3272
+ element.kind === "void" ||
3273
+ element.kind === "dyn" ||
3274
+ element.kind === "jsval" ||
3275
+ element.kind === "generator")
3276
+ return null;
3277
+ return e;
3278
+ };
3046
3279
  const isIndexedRead = (node) => {
3047
3280
  const e = peel(node);
3048
3281
  if (!ts.isElementAccessExpression(e))
3049
3282
  return false;
3050
3283
  const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
3051
- return kind === "array" || kind === "string";
3284
+ if (kind === "string")
3285
+ return !this.stringIndexBounds.inBounds(e);
3286
+ // Typed-array reads answer undefined for an invalid index too, unless
3287
+ // every consumer treats undefined like NaN.
3288
+ if (kind === "bytes")
3289
+ return !nanCoded.isNumericRead(e);
3290
+ return kind === "array";
3052
3291
  };
3053
3292
  const isDynamicObjectEntryRead = (node) => {
3054
3293
  const read = peel(node);
@@ -3076,6 +3315,37 @@ export class Lowerer {
3076
3315
  return this.withUndefinedArm(t);
3077
3316
  };
3078
3317
  const fieldName = (node) => node.name.text;
3318
+ // The precision facts below ask about nodes ordinary lowering may never
3319
+ // query. A checker panic on one of them answers null, and each caller
3320
+ // falls back to its conservative fact.
3321
+ const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
3322
+ const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
3323
+ // Presence proofs only hold for a binding that keeps its one value for
3324
+ // its whole lifetime.
3325
+ const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
3326
+ const presenceProofs = new Map();
3327
+ const provenPresent = (use, symbol) => {
3328
+ if (reassigned.has(symbol))
3329
+ return false;
3330
+ const cached = presenceProofs.get(use);
3331
+ if (cached !== undefined)
3332
+ return cached;
3333
+ const proven = presentAtUse(use, {
3334
+ sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
3335
+ isUndefined: (node) => node.text === "undefined" &&
3336
+ ((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
3337
+ });
3338
+ presenceProofs.set(use, proven);
3339
+ return proven;
3340
+ };
3341
+ // Bindings of a for-of loop over an array that can hold holes or
3342
+ // undefined. Lowering keeps such a binding's runtime union, so storing
3343
+ // it into another array can store undefined.
3344
+ const iterationSymbols = new Set();
3345
+ // The subset whose iterated array's own facts show a hole or undefined.
3346
+ // Such a binding reads `T | undefined`, so a typed parameter, local or
3347
+ // field it flows into widens like any other missing element read.
3348
+ const holeyIterationSymbols = new Set();
3079
3349
  const mayBeOptional = (node) => {
3080
3350
  // `xs[i]!` is the explicit proven-present form. Its array read keeps
3081
3351
  // the established dense bounds trap and must not promote the enclosing
@@ -3101,9 +3371,18 @@ export class Lowerer {
3101
3371
  this.mapTypeOf(this.typeOf(e))?.kind === "union" &&
3102
3372
  this.armTag(this.mapTypeOf(this.typeOf(e)).unionId, UNDEFINED_T) >= 0)
3103
3373
  return true;
3374
+ // A NaN-coded value that reaches a consumer able to observe undefined
3375
+ // is rebuilt as the ordinary optional value there; every other read
3376
+ // of a NaN-coded slot is a plain number.
3377
+ if (nanCoded.reboxes(e))
3378
+ return true;
3379
+ if (nanCoded.readsNanCodedSlot(e))
3380
+ return false;
3104
3381
  if (ts.isIdentifier(e)) {
3105
3382
  const symbol = symbolOf(e);
3106
- return symbol !== null && optionalSymbols.has(symbol);
3383
+ return (symbol !== null &&
3384
+ (optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
3385
+ !provenPresent(e, symbol));
3107
3386
  }
3108
3387
  if (ts.isPropertyAccessExpression(e)) {
3109
3388
  const symbol = symbolOf(e.name);
@@ -3169,7 +3448,9 @@ export class Lowerer {
3169
3448
  if (t?.kind !== "string")
3170
3449
  return false;
3171
3450
  const recv = this.mapTypeOf(this.typeOf(p.expression));
3172
- return recv?.kind === "string" || (recv?.kind === "array" && recv.elem.kind === "string");
3451
+ if (recv?.kind === "string")
3452
+ return !this.stringIndexBounds.inBounds(p);
3453
+ return recv?.kind === "array" && recv.elem.kind === "string";
3173
3454
  };
3174
3455
  const primitive = (part) => {
3175
3456
  const t = this.mapTypeOf(this.typeOf(part));
@@ -3189,6 +3470,25 @@ export class Lowerer {
3189
3470
  return false;
3190
3471
  return noteFieldSymbol(symbol, name);
3191
3472
  };
3473
+ /** An instance field of a TypeScript class whose declared storage is
3474
+ * exactly `T | undefined`: an assignment from a missing read stores the
3475
+ * undefined arm while the checker narrows later reads to `T`. */
3476
+ const optionalInstanceField = (symbol) => {
3477
+ const decl = this.checker.valueDeclarationOf(symbol);
3478
+ if (!decl || !ts.isPropertyDeclaration(decl) || !ts.isIdentifier(decl.name))
3479
+ return false;
3480
+ if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Static)
3481
+ return false;
3482
+ const sf = decl.getSourceFile();
3483
+ if (isJsSourceFile(sf) || sf.isDeclarationFile)
3484
+ return false;
3485
+ const declared = typeOrNull(decl.name);
3486
+ const mapped = declared ? this.mapTypeOf(declared) : null;
3487
+ if (mapped?.kind !== "union" || this.armTag(mapped.unionId, UNDEFINED_T) < 0)
3488
+ return false;
3489
+ const arms = this.unions.get(mapped.unionId)?.arms ?? [];
3490
+ return (arms.length === 2 && arms.every((arm) => arm.kind === "undefinedT" || !isUnitType(arm)));
3491
+ };
3192
3492
  const noteFieldSymbol = (symbol, name) => {
3193
3493
  const set = optionalFields.get(symbol) ?? new Set();
3194
3494
  const before = set.size;
@@ -3196,6 +3496,43 @@ export class Lowerer {
3196
3496
  optionalFields.set(symbol, set);
3197
3497
  return set.size !== before;
3198
3498
  };
3499
+ // Runtime-optional record fields travel with the record value: a copy,
3500
+ // a spread or a parameter receives the source's optional fields.
3501
+ const fieldsOfValue = (node) => {
3502
+ const e = peel(node);
3503
+ if (!ts.isIdentifier(e))
3504
+ return undefined;
3505
+ const symbol = symbolOf(e);
3506
+ return symbol === null ? undefined : optionalFields.get(symbol);
3507
+ };
3508
+ const noteFieldsFrom = (symbol, source) => {
3509
+ const fields = fieldsOfValue(source);
3510
+ if (fields === undefined)
3511
+ return false;
3512
+ let changed = false;
3513
+ for (const field of fields)
3514
+ if (noteFieldSymbol(symbol, field))
3515
+ changed = true;
3516
+ return changed;
3517
+ };
3518
+ /** Optional fields an object literal takes from its spreads, minus
3519
+ * the properties a later member writes explicitly. */
3520
+ const spreadFieldsOf = (object) => {
3521
+ const out = new Set();
3522
+ for (const prop of object.properties) {
3523
+ if (ts.isSpreadAssignment(prop)) {
3524
+ const fields = fieldsOfValue(prop.expression);
3525
+ if (fields !== undefined)
3526
+ for (const field of fields)
3527
+ out.add(field);
3528
+ }
3529
+ else if (prop.name !== undefined &&
3530
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
3531
+ out.delete(prop.name.text);
3532
+ }
3533
+ }
3534
+ return out;
3535
+ };
3199
3536
  const scanPattern = (name, init) => {
3200
3537
  if (!ts.isArrayBindingPattern(name))
3201
3538
  return false;
@@ -3290,14 +3627,31 @@ export class Lowerer {
3290
3627
  return found;
3291
3628
  };
3292
3629
  const jsLambdas = [];
3630
+ // TypeScript lambdas keep the checker's inferred return type, which
3631
+ // spells a missing read as its bare element type and `read ?? "text"`
3632
+ // as a number. Their closures return the runtime result instead.
3633
+ const tsLambdas = [];
3293
3634
  for (const sf of sourceFiles) {
3294
- if (!isJsSourceFile(sf))
3635
+ const js = isJsSourceFile(sf);
3636
+ if (!js && sf.isDeclarationFile)
3295
3637
  continue;
3296
3638
  ts.walkPreorder(sf, (node) => {
3297
3639
  if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
3298
- jsLambdas.push(node);
3640
+ (js ? jsLambdas : tsLambdas).push(node);
3299
3641
  });
3300
3642
  }
3643
+ /** The primitive result union of a lambda's returns (see
3644
+ * optionalPrimitiveResultType), as declared functions record it. */
3645
+ const lambdaArithmeticReturn = (fn) => {
3646
+ if (fn.body && !ts.isBlock(fn.body))
3647
+ return optionalPrimitiveResultType(fn.body);
3648
+ for (const expression of returnsOf(fn)) {
3649
+ const result = optionalPrimitiveResultType(expression);
3650
+ if (result)
3651
+ return result;
3652
+ }
3653
+ return null;
3654
+ };
3301
3655
  for (const [symbol, signature] of signatureBySymbol) {
3302
3656
  const declaration = functionDeclBySymbol.get(symbol);
3303
3657
  if (!declaration || !isJsSourceFile(declaration.getSourceFile()))
@@ -3462,6 +3816,70 @@ export class Lowerer {
3462
3816
  }
3463
3817
  return changed;
3464
3818
  };
3819
+ // A reduce callback over present elements keeps the reduce helper's
3820
+ // optional element ABI for its parameter storage, so the helper still
3821
+ // splits each visit into a plain-element call. Its value is never
3822
+ // absent, so nothing it flows into is widened.
3823
+ const retypeInlineCallback = (callback, parameterIndices) => {
3824
+ const fn = peel(callback);
3825
+ if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
3826
+ return false;
3827
+ let changed = false;
3828
+ for (const parameterIndex of parameterIndices) {
3829
+ const parameter = fn.parameters[parameterIndex];
3830
+ if (!parameter)
3831
+ continue;
3832
+ const checkerType = this.typeOf(parameter.name);
3833
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3834
+ continue;
3835
+ const current = this.mapTypeOf(checkerType);
3836
+ if (!current)
3837
+ continue;
3838
+ const widened = this.runtimeOptionalType(current);
3839
+ if (!ts.isIdentifier(parameter.name)) {
3840
+ const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
3841
+ this.runtimeOptionalPatternTypes.set(parameter.name, widened);
3842
+ if (!previous || !typeEquals(previous, widened))
3843
+ changed = true;
3844
+ continue;
3845
+ }
3846
+ const symbol = symbolOf(parameter.name);
3847
+ if (!symbol)
3848
+ continue;
3849
+ const previous = this.runtimeOptionalBindingTypes.get(symbol);
3850
+ this.runtimeOptionalBindingTypes.set(symbol, widened);
3851
+ if (!previous || !typeEquals(previous, widened))
3852
+ changed = true;
3853
+ }
3854
+ return changed;
3855
+ };
3856
+ // A present-element helper passes the stored element itself, so the
3857
+ // callback must accept that exact value through a mechanical adapter.
3858
+ // A parameter spelled as a structural supertype (`(r: { pos: number })`
3859
+ // over a wider record) only converts through the optional element
3860
+ // ABI's union retag; keep that site on the optional visit. Unannotated
3861
+ // inline parameters take the element's contextual type, and generic
3862
+ // parameters specialize later, so neither restricts the site.
3863
+ const callbackTakesPresentElement = (callback, elem, index) => {
3864
+ const fn = peel(callback);
3865
+ let param;
3866
+ if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
3867
+ const parameter = fn.parameters[index];
3868
+ if (!parameter?.type || parameter.dotDotDotToken)
3869
+ return true;
3870
+ const checkerType = this.typeOf(parameter.name);
3871
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3872
+ return true;
3873
+ param = this.mapTypeOf(checkerType);
3874
+ }
3875
+ else {
3876
+ const value = this.mapTypeOf(this.typeOf(fn));
3877
+ if (value?.kind !== "func" || value.rest === true)
3878
+ return true;
3879
+ param = value.params[index] ?? null;
3880
+ }
3881
+ return param === null || this.coercibleValue(elem, param);
3882
+ };
3465
3883
  const callbackReturnsOptional = (callback, seen = new Set()) => {
3466
3884
  const node = peel(callback);
3467
3885
  if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
@@ -3479,6 +3897,253 @@ export class Lowerer {
3479
3897
  !!decl.initializer &&
3480
3898
  callbackReturnsOptional(decl.initializer, seen));
3481
3899
  };
3900
+ // Element ABIs whose own type admits undefined (`(T | undefined)[]`,
3901
+ // or a rest array widened for an absent argument).
3902
+ const optionalElementKeys = new Set();
3903
+ // Checker answers for the fixed point's repeated element-ABI queries.
3904
+ // Only a binding's widened storage can change between passes.
3905
+ const checkerTypes = new Map();
3906
+ const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
3907
+ const arrayKeysUnsafe = (node) => {
3908
+ let checkerType = checkerTypes.get(node);
3909
+ if (checkerType === undefined) {
3910
+ checkerType = this.typeOf(node);
3911
+ checkerTypes.set(node, checkerType);
3912
+ }
3913
+ const e = peel(node);
3914
+ // A binding the prepass widened stores its widened array ABI.
3915
+ const widened = ts.isIdentifier(e)
3916
+ ? (() => {
3917
+ const symbol = symbolOf(e);
3918
+ return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
3919
+ })()
3920
+ : undefined;
3921
+ let mapped = widened ?? this.mapTypeOf(checkerType);
3922
+ if (mapped?.kind === "promise")
3923
+ mapped = mapped.inner;
3924
+ const anyElements = mapped?.kind === "array"
3925
+ ? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
3926
+ : mapped === null &&
3927
+ this.checker.isArrayType(checkerType) &&
3928
+ !this.checker.isTupleType(checkerType) &&
3929
+ ((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
3930
+ (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
3931
+ 0;
3932
+ if (anyElements) {
3933
+ // `new Array(n)` has an `any` element type; its storage takes the
3934
+ // ABI of the slot it initializes.
3935
+ const contextual = this.checker.getContextualType(node);
3936
+ const target = contextual ? this.mapTypeOf(contextual) : null;
3937
+ return target?.kind === "array" ? [typeKey(target.elem)] : [];
3938
+ }
3939
+ if (mapped === null) {
3940
+ // An array whose element type is a type parameter: each
3941
+ // instantiation picks its own element ABI.
3942
+ return this.checker.isArrayType(checkerType) ? null : [];
3943
+ }
3944
+ const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
3945
+ const keys = [];
3946
+ for (const arm of arms) {
3947
+ if (arm.kind !== "array")
3948
+ continue;
3949
+ const key = typeKey(arm.elem);
3950
+ if (arm.elem.kind === "union"
3951
+ ? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
3952
+ : isUnitType(arm.elem) || arm.elem.kind === "void")
3953
+ optionalElementKeys.add(key);
3954
+ keys.push(key);
3955
+ }
3956
+ return keys;
3957
+ };
3958
+ const ownership = new ArrayOwnership(sourceFiles, {
3959
+ symbolOf,
3960
+ declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
3961
+ typeOf: typeOrNull,
3962
+ instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
3963
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
3964
+ isArray: (node) => {
3965
+ const type = typeOrNull(node);
3966
+ return type !== null && this.checker.isArrayType(type);
3967
+ },
3968
+ fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
3969
+ dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
3970
+ });
3971
+ let dynamicViews = null;
3972
+ // Element ABIs of the native arrays that an `any` or `unknown` value is
3973
+ // converted to, including arrays nested in the converted value.
3974
+ const collectDynamicViews = () => {
3975
+ const keys = new Set();
3976
+ const dynamicValue = (node) => {
3977
+ // A binding's declared type says whether it holds dynamic data; a
3978
+ // flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
3979
+ const e = peel(node);
3980
+ const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
3981
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
3982
+ const declared = declaration &&
3983
+ (ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
3984
+ ts.isIdentifier(declaration.name)
3985
+ ? declaration.name
3986
+ : null;
3987
+ const type = typeOrNull(declared ?? node);
3988
+ if (type === null)
3989
+ return true;
3990
+ if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
3991
+ return true;
3992
+ if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
3993
+ return false;
3994
+ const element = this.checker.getTypeArguments(type)[0];
3995
+ return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
3996
+ };
3997
+ const collect = (type, depth) => {
3998
+ if (!type || depth > 4)
3999
+ return;
4000
+ if (type.kind === "array") {
4001
+ keys.add(typeKey(type.elem));
4002
+ collect(type.elem, depth + 1);
4003
+ }
4004
+ else if (type.kind === "union") {
4005
+ for (const arm of this.unions.get(type.unionId)?.arms ?? [])
4006
+ collect(arm, depth + 1);
4007
+ }
4008
+ else if (type.kind === "record") {
4009
+ for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
4010
+ collect(field.type, depth + 1);
4011
+ }
4012
+ else if (type.kind === "promise")
4013
+ collect(type.inner, depth + 1);
4014
+ };
4015
+ const target = (type) => {
4016
+ // An unanswerable target may be any array.
4017
+ if (type === null)
4018
+ keys.add("*");
4019
+ else if (type)
4020
+ collect(this.mapTypeOf(type), 0);
4021
+ };
4022
+ const visit = (node) => {
4023
+ if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
4024
+ target(typeOrNull(node));
4025
+ else if (ts.isVariableDeclaration(node) &&
4026
+ node.type &&
4027
+ node.initializer &&
4028
+ dynamicValue(node.initializer))
4029
+ target(typeOrNull(node.name));
4030
+ else if (ts.isBinaryExpression(node) &&
4031
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4032
+ dynamicValue(node.right))
4033
+ target(typeOrNull(node.left));
4034
+ else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
4035
+ target(contextualOrNull(node.expression));
4036
+ else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
4037
+ for (const arg of node.arguments ?? [])
4038
+ if (!ts.isSpreadElement(arg) && dynamicValue(arg))
4039
+ target(contextualOrNull(arg));
4040
+ }
4041
+ else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
4042
+ target(contextualOrNull(node.initializer));
4043
+ else if (ts.isArrayLiteralExpression(node)) {
4044
+ for (const element of node.elements)
4045
+ if (!ts.isSpreadElement(element) &&
4046
+ !ts.isOmittedExpression(element) &&
4047
+ dynamicValue(element))
4048
+ target(contextualOrNull(element));
4049
+ }
4050
+ else if (ts.isArrowFunction(node) &&
4051
+ !ts.isBlock(node.body) &&
4052
+ dynamicValue(node.body))
4053
+ target(contextualOrNull(node.body));
4054
+ };
4055
+ for (const sf of sourceFiles)
4056
+ ts.walkPreorder(sf, (node) => {
4057
+ // A checker panic hides this conversion's target: any array.
4058
+ if (!panicSafe(() => (visit(node), true), false))
4059
+ keys.add("*");
4060
+ return undefined;
4061
+ });
4062
+ return keys;
4063
+ };
4064
+ const elementStates = new ArrayElementStates({
4065
+ elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
4066
+ ownerOf: (node) => ownership.ownerOf(node),
4067
+ allocationOwner: (node) => ownership.allocationOwner(node),
4068
+ arrayKeys: arrayKeysOf,
4069
+ mayBeUndefined: (node) => {
4070
+ if (explicitlyNonNull(node))
4071
+ return false;
4072
+ if (mayBeOptional(node))
4073
+ return true;
4074
+ // `undefined as unknown as T` keeps the undefined value.
4075
+ for (let inner = node; ts.isParenthesizedExpression(inner) ||
4076
+ ts.isAsExpression(inner) ||
4077
+ ts.isTypeAssertion(inner) ||
4078
+ ts.isSatisfiesExpression(inner);) {
4079
+ inner = inner.expression;
4080
+ const type = typeOrNull(inner);
4081
+ if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
4082
+ return true;
4083
+ }
4084
+ const e = peel(node);
4085
+ if (!ts.isIdentifier(e))
4086
+ return false;
4087
+ const symbol = symbolOf(e);
4088
+ return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
4089
+ },
4090
+ callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
4091
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
4092
+ isString: (node) => {
4093
+ const type = typeOrNull(node);
4094
+ return type !== null && this.mapTypeOf(type)?.kind === "string";
4095
+ },
4096
+ isArrayLikeObject: (node) => {
4097
+ if (ts.isObjectLiteralExpression(peel(node)))
4098
+ return true;
4099
+ const type = typeOrNull(node);
4100
+ const mapped = type === null ? null : this.mapTypeOf(type);
4101
+ return (mapped === null ||
4102
+ mapped.kind === "record" ||
4103
+ mapped.kind === "object" ||
4104
+ mapped.kind === "dyn" ||
4105
+ mapped.kind === "jsval" ||
4106
+ mapped.kind === "union");
4107
+ },
4108
+ restKeys: (call) => {
4109
+ const callee = peel(call.expression);
4110
+ if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
4111
+ return null;
4112
+ const symbol = callableSymbolOf(callee);
4113
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4114
+ if (!sig)
4115
+ return ts.isPropertyAccessExpression(callee) ? [] : null;
4116
+ const rest = sig.params.find((shape) => shape.mode === "rest");
4117
+ return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
4118
+ },
4119
+ markIterationBinding: (node, known) => {
4120
+ const symbol = symbolOf(node);
4121
+ if (!symbol)
4122
+ return false;
4123
+ let changed = false;
4124
+ if (!iterationSymbols.has(symbol)) {
4125
+ iterationSymbols.add(symbol);
4126
+ changed = true;
4127
+ }
4128
+ if (known && !holeyIterationSymbols.has(symbol)) {
4129
+ holeyIterationSymbols.add(symbol);
4130
+ // `for (name of xs)` assigns an existing binding, whose own
4131
+ // storage must then hold the loop's undefined too.
4132
+ if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
4133
+ optionalSymbols.add(symbol);
4134
+ changed = true;
4135
+ }
4136
+ return changed;
4137
+ },
4138
+ note: provenance
4139
+ ? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
4140
+ : null,
4141
+ });
4142
+ // Each pass revisits every array callback site; the last visit holds
4143
+ // the decision at the fixed point.
4144
+ const presentSites = new Map();
4145
+ // The same for each for-of loop over an array.
4146
+ const presentLoops = new Map();
3482
4147
  const scanSites = new Map();
3483
4148
  const sitesOf = (sf) => {
3484
4149
  const cached = scanSites.get(sf);
@@ -3488,7 +4153,14 @@ export class Lowerer {
3488
4153
  ts.walkPreorder(sf, (node) => {
3489
4154
  if (ts.isVariableDeclaration(node) ||
3490
4155
  ts.isBinaryExpression(node) ||
3491
- ts.isCallExpression(node))
4156
+ ts.isCallExpression(node) ||
4157
+ ts.isNewExpression(node) ||
4158
+ ts.isArrayLiteralExpression(node) ||
4159
+ ts.isDeleteExpression(node) ||
4160
+ ts.isForOfStatement(node) ||
4161
+ ts.isArrayBindingPattern(node) ||
4162
+ ts.isPrefixUnaryExpression(node) ||
4163
+ ts.isPostfixUnaryExpression(node))
3492
4164
  nodes.push(node);
3493
4165
  });
3494
4166
  scanSites.set(sf, nodes);
@@ -3497,6 +4169,10 @@ export class Lowerer {
3497
4169
  const scanFile = (sf) => {
3498
4170
  let changed = false;
3499
4171
  const scan = (node) => {
4172
+ if (elementStates.scan(node))
4173
+ changed = true;
4174
+ if (ts.isForOfStatement(node))
4175
+ presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
3500
4176
  if (ts.isVariableDeclaration(node)) {
3501
4177
  if (node.initializer) {
3502
4178
  const result = optionalPrimitiveResultType(node.initializer);
@@ -3524,6 +4200,16 @@ export class Lowerer {
3524
4200
  changed = true;
3525
4201
  }
3526
4202
  }
4203
+ else if (ts.isIdentifier(node.name)) {
4204
+ const assertion = forwardedAssertion(node.initializer);
4205
+ const symbol = assertion ? symbolOf(node.name) : null;
4206
+ if (assertion && symbol && !optionalSymbols.has(symbol)) {
4207
+ optionalSymbols.add(symbol);
4208
+ this.runtimeOptionalAssertions.add(assertion);
4209
+ provenance?.note("asserted-binding", node.name, node.initializer);
4210
+ changed = true;
4211
+ }
4212
+ }
3527
4213
  if (ts.isIdentifier(node.name)) {
3528
4214
  const valueSymbol = ts.isIdentifier(peel(node.initializer))
3529
4215
  ? symbolOf(peel(node.initializer))
@@ -3565,6 +4251,22 @@ export class Lowerer {
3565
4251
  }
3566
4252
  if (scanPattern(node.name, node.initializer))
3567
4253
  changed = true;
4254
+ if (ts.isIdentifier(node.name)) {
4255
+ const symbol = symbolOf(node.name);
4256
+ const init = peel(node.initializer);
4257
+ if (symbol && noteFieldsFrom(symbol, init)) {
4258
+ provenance?.note("record-copy", node.name, node.initializer);
4259
+ changed = true;
4260
+ }
4261
+ if (symbol && ts.isObjectLiteralExpression(init)) {
4262
+ for (const field of spreadFieldsOf(init)) {
4263
+ if (noteFieldSymbol(symbol, field)) {
4264
+ provenance?.note("record-spread", node.name, node.initializer);
4265
+ changed = true;
4266
+ }
4267
+ }
4268
+ }
4269
+ }
3568
4270
  if (ts.isIdentifier(node.name) &&
3569
4271
  ts.isObjectLiteralExpression(peel(node.initializer))) {
3570
4272
  const object = peel(node.initializer);
@@ -3586,6 +4288,36 @@ export class Lowerer {
3586
4288
  }
3587
4289
  }
3588
4290
  }
4291
+ if (ts.isBinaryExpression(node) &&
4292
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4293
+ ts.isIdentifier(node.left)) {
4294
+ const symbol = symbolOf(node.left);
4295
+ if (symbol && noteFieldsFrom(symbol, node.right)) {
4296
+ provenance?.note("record-copy", node.left, node.right);
4297
+ changed = true;
4298
+ }
4299
+ }
4300
+ if (ts.isBinaryExpression(node) &&
4301
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4302
+ ts.isIdentifier(node.left)) {
4303
+ const assertion = forwardedAssertion(node.right);
4304
+ const symbol = assertion ? symbolOf(node.left) : null;
4305
+ // Only a variable's storage is promoted (see the final pass).
4306
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
4307
+ if (assertion &&
4308
+ symbol &&
4309
+ declaration &&
4310
+ ts.isVariableDeclaration(declaration) &&
4311
+ ts.isIdentifier(declaration.name) &&
4312
+ !this.runtimeOptionalAssertions.has(assertion)) {
4313
+ this.runtimeOptionalAssertions.add(assertion);
4314
+ if (!optionalSymbols.has(symbol)) {
4315
+ optionalSymbols.add(symbol);
4316
+ provenance?.note("asserted-binding", node.left, node.right);
4317
+ }
4318
+ changed = true;
4319
+ }
4320
+ }
3589
4321
  if (ts.isBinaryExpression(node) &&
3590
4322
  (node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
3591
4323
  node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
@@ -3607,6 +4339,14 @@ export class Lowerer {
3607
4339
  provenance?.note("static-field", node.left, node.right);
3608
4340
  changed = true;
3609
4341
  }
4342
+ else if (field && !optionalSymbols.has(field) && optionalInstanceField(field)) {
4343
+ // The checker narrows the field to the assigned read's bare
4344
+ // type, but the stored value can be undefined: reads keep the
4345
+ // stored union (runtimeOptionalClassField).
4346
+ optionalSymbols.add(field);
4347
+ provenance?.note("assigned-field", node.left, node.right);
4348
+ changed = true;
4349
+ }
3610
4350
  else if (ts.isIdentifier(node.left.expression)) {
3611
4351
  const symbol = symbolOf(node.left.expression);
3612
4352
  if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
@@ -3655,11 +4395,25 @@ export class Lowerer {
3655
4395
  }
3656
4396
  if (nativeArrayReceiver) {
3657
4397
  provenanceSite = node;
3658
- if (promoteHofCallback(callback, callbackIndices))
3659
- changed = true;
3660
4398
  const method = node.expression.name.text;
4399
+ // Callbacks skip holes (except the find family), so an array
4400
+ // that never holds undefined only passes present elements.
4401
+ const reduce = method === "reduce" || method === "reduceRight";
4402
+ const elementsPresent = receiver?.kind === "array" &&
4403
+ elementStates.elementsPresent(receiverNode, method) &&
4404
+ callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
4405
+ // A callback returning a runtime-absent value still needs its
4406
+ // widened return, whatever its elements.
4407
+ if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
4408
+ changed = true;
4409
+ // Element-typed callback helpers cover every method but
4410
+ // reduce, whose accumulator keeps the split visit.
4411
+ presentSites.set(node, elementsPresent && !reduce);
4412
+ if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
4413
+ changed = true;
3661
4414
  if ((method === "reduce" || method === "reduceRight") &&
3662
- (node.arguments.length < 2 || callbackReturnsOptional(callback))) {
4415
+ ((!elementsPresent && node.arguments.length < 2) ||
4416
+ callbackReturnsOptional(callback))) {
3663
4417
  if (promoteHofCallback(callback, [0, 1]))
3664
4418
  changed = true;
3665
4419
  const result = this.runtimeOptionalType(this.irTypeOf(node));
@@ -3678,8 +4432,34 @@ export class Lowerer {
3678
4432
  if (!symbol)
3679
4433
  return;
3680
4434
  const sig = signatureBySymbol.get(symbol);
3681
- if (!sig)
4435
+ if (!sig) {
4436
+ // A direct call of a function-valued const (`const show = (n:
4437
+ // number) => ...`): a possibly missing argument widens that
4438
+ // parameter, as for a callback the same value is passed as.
4439
+ if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
4440
+ return;
4441
+ const declaration = this.checker.valueDeclarationOf(symbol);
4442
+ const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
4443
+ ? peel(declaration.initializer)
4444
+ : null;
4445
+ if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
4446
+ return;
4447
+ // Optional, defaulted and rest parameters keep their own ABI.
4448
+ if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
4449
+ parameter.initializer !== undefined ||
4450
+ parameter.dotDotDotToken !== undefined))
4451
+ return;
4452
+ const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
4453
+ value.parameters[i] !== undefined &&
4454
+ !value.parameters[i].dotDotDotToken &&
4455
+ mayBeOptional(arg)
4456
+ ? [i]
4457
+ : []);
4458
+ provenanceSite = node;
4459
+ if (indices.length > 0 && promoteHofCallback(node.expression, indices))
4460
+ changed = true;
3682
4461
  return;
4462
+ }
3683
4463
  // JSDoc describes the usual value, not a runtime arity check.
3684
4464
  // A short JavaScript call still passes undefined. Widen only the
3685
4465
  // observed slots before bodies lower so both caller and callee
@@ -3736,8 +4516,57 @@ export class Lowerer {
3736
4516
  changed = true;
3737
4517
  }
3738
4518
  }
3739
- if (ts.isSpreadElement(arg) || !mayBeOptional(arg) || !shape)
4519
+ if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
4520
+ const fields = fieldsOfValue(arg);
4521
+ for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
4522
+ const parameterShape = signatureBySymbol.get(target)?.params[i];
4523
+ const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4524
+ if (!parameterShape || parameterShape.type.kind !== "record")
4525
+ continue;
4526
+ if (!parameter || !ts.isIdentifier(parameter.name))
4527
+ continue;
4528
+ const bound = symbolOf(parameter.name);
4529
+ if (!bound)
4530
+ continue;
4531
+ let promoted = parameterShape.type;
4532
+ for (const field of fields) {
4533
+ const source = promoted.kind === "record"
4534
+ ? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
4535
+ : undefined;
4536
+ if (source) {
4537
+ promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
4538
+ }
4539
+ if (noteFieldSymbol(bound, field))
4540
+ changed = true;
4541
+ }
4542
+ if (!typeEquals(promoted, parameterShape.type)) {
4543
+ parameterShape.type = promoted;
4544
+ this.runtimeOptionalBindingTypes.set(bound, promoted);
4545
+ provenance?.note("record-parameter", parameter, arg);
4546
+ changed = true;
4547
+ }
4548
+ }
4549
+ }
4550
+ if (ts.isSpreadElement(arg) || !shape)
3740
4551
  return;
4552
+ const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
4553
+ if (!assertion && !mayBeOptional(arg))
4554
+ return;
4555
+ if (assertion) {
4556
+ if (i === restAt)
4557
+ return;
4558
+ // An omittable slot's ABI already holds undefined (an
4559
+ // optional or defaulted parameter applies its own default).
4560
+ if (shape.mode === "omittable") {
4561
+ if (shape.type.kind === "union" &&
4562
+ this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4563
+ this.runtimeOptionalAssertions.add(assertion);
4564
+ return;
4565
+ }
4566
+ if (shape.mode !== "required")
4567
+ return;
4568
+ this.runtimeOptionalAssertions.add(assertion);
4569
+ }
3741
4570
  if (i === restAt) {
3742
4571
  // A rest binding always receives an array. An unchecked
3743
4572
  // argument can make its elements undefined, not the pack.
@@ -3831,6 +4660,52 @@ export class Lowerer {
3831
4660
  for (const [symbol, decl] of functionDeclBySymbol)
3832
4661
  if (scanReturns(symbol, decl))
3833
4662
  changed = true;
4663
+ for (const fn of tsLambdas) {
4664
+ // An annotated return is the author's contract, as for parameters.
4665
+ if (fn.type)
4666
+ continue;
4667
+ const mapped = this.mapTypeOf(this.typeOf(fn));
4668
+ if (mapped?.kind !== "func")
4669
+ continue;
4670
+ const previous = this.runtimeOptionalFunctionReturnType(fn, mapped.ret);
4671
+ // Async, generator and void bodies keep their own result shapes.
4672
+ if (previous.kind === "promise" ||
4673
+ previous.kind === "generator" ||
4674
+ previous.kind === "void")
4675
+ continue;
4676
+ const arithmetic = lambdaArithmeticReturn(fn);
4677
+ const next = arithmetic &&
4678
+ (previous.kind === "f64" || previous.kind === "string" || previous.kind === "bool")
4679
+ ? arithmetic
4680
+ : bodyReturnsOptional(fn)
4681
+ ? this.runtimeOptionalType(previous)
4682
+ : previous;
4683
+ if (!typeEquals(previous, next)) {
4684
+ this.runtimeOptionalFunctionReturns.set(fn, next);
4685
+ provenance?.note("lambda-return", fn, fn.body);
4686
+ changed = true;
4687
+ }
4688
+ // Calls through the lambda's own binding (`const get = () => xs[i];
4689
+ // get()`) produce the same result as calls of a declared function.
4690
+ const binding = fn.parent;
4691
+ if (next !== previous &&
4692
+ ts.isVariableDeclaration(binding) &&
4693
+ binding.initializer === fn &&
4694
+ !binding.type &&
4695
+ ts.isIdentifier(binding.name)) {
4696
+ const symbol = symbolOf(binding.name);
4697
+ if (symbol && next.kind === "union" && this.armTag(next.unionId, UNDEFINED_T) >= 0) {
4698
+ if (!optionalReturns.has(symbol)) {
4699
+ optionalReturns.add(symbol);
4700
+ changed = true;
4701
+ }
4702
+ }
4703
+ else if (symbol && !arithmeticReturns.has(symbol)) {
4704
+ arithmeticReturns.set(symbol, next);
4705
+ changed = true;
4706
+ }
4707
+ }
4708
+ }
3834
4709
  for (const fn of jsLambdas) {
3835
4710
  if (!bodyReturnsOptional(fn))
3836
4711
  continue;
@@ -3862,6 +4737,31 @@ export class Lowerer {
3862
4737
  }
3863
4738
  }
3864
4739
  }
4740
+ // NaN-coded slots convert optional values on entry instead of holding
4741
+ // the union, so promotion never applies to them.
4742
+ for (const symbol of [...optionalSymbols])
4743
+ if (nanCoded.nanCodedBinding(symbol))
4744
+ optionalSymbols.delete(symbol);
4745
+ for (const symbol of [...optionalReturns])
4746
+ if (nanCoded.nanCodedReturn(symbol))
4747
+ optionalReturns.delete(symbol);
4748
+ for (const [symbol, params] of optionalParams) {
4749
+ const decl = functionDeclBySymbol.get(symbol);
4750
+ for (const index of [...params]) {
4751
+ const name = decl?.parameters[index]?.name;
4752
+ const param = name && ts.isIdentifier(name) ? symbolOf(name) : null;
4753
+ if (param && nanCoded.nanCodedBinding(param))
4754
+ params.delete(index);
4755
+ }
4756
+ if (params.size === 0)
4757
+ optionalParams.delete(symbol);
4758
+ }
4759
+ for (const [site, present] of presentSites)
4760
+ if (present)
4761
+ this.presentElementCalls.add(site);
4762
+ for (const [loop, present] of presentLoops)
4763
+ if (present)
4764
+ this.presentElementLoops.add(loop);
3865
4765
  const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
3866
4766
  for (const [symbol, field] of staticFieldsBySymbol) {
3867
4767
  if (!optionalSymbols.has(symbol))
@@ -3928,11 +4828,11 @@ export class Lowerer {
3928
4828
  const symbol = symbolOf(node.name);
3929
4829
  if (!symbol)
3930
4830
  return;
3931
- if (isJsSourceFile(sf) &&
3932
- !node.type &&
4831
+ if (!node.type &&
3933
4832
  !hasJsTypeAnnotation(node) &&
3934
4833
  node.initializer &&
3935
- (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
4834
+ (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) &&
4835
+ (isJsSourceFile(sf) || this.runtimeOptionalFunctionReturns.has(node.initializer))) {
3936
4836
  const global = this.globalsBySymbol.get(symbol);
3937
4837
  if (global?.type.kind === "func")
3938
4838
  global.type = {
@@ -4008,6 +4908,35 @@ export class Lowerer {
4008
4908
  }
4009
4909
  });
4010
4910
  }
4911
+ for (const symbol of optionalSymbols)
4912
+ this.runtimeOptionalAssignedSymbols.add(symbol);
4913
+ // `decode(xs[i]!)` into a parameter whose final ABI already holds
4914
+ // undefined: the assertion has no runtime effect, so a missing element
4915
+ // reaches the callee as undefined instead of trapping at the read.
4916
+ for (const sf of sourceFiles) {
4917
+ for (const node of sitesOf(sf)) {
4918
+ if (!ts.isCallExpression(node))
4919
+ continue;
4920
+ if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
4921
+ continue;
4922
+ const symbol = callableSymbolOf(node.expression);
4923
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4924
+ if (!sig || node.arguments.some(ts.isSpreadElement))
4925
+ continue;
4926
+ node.arguments.forEach((arg, i) => {
4927
+ let e = arg;
4928
+ while (ts.isParenthesizedExpression(e))
4929
+ e = e.expression;
4930
+ if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
4931
+ return;
4932
+ const shape = sig.params[i];
4933
+ if (shape?.mode !== "required" && shape?.mode !== "omittable")
4934
+ return;
4935
+ if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4936
+ this.runtimeOptionalAssertions.add(e);
4937
+ });
4938
+ }
4939
+ }
4011
4940
  provenance?.flush();
4012
4941
  }
4013
4942
  run() {
@@ -4058,14 +4987,14 @@ export class Lowerer {
4058
4987
  if (fn)
4059
4988
  functions.push(fn);
4060
4989
  else if (this.countsSkips())
4061
- this.stats.functionsSkipped++;
4990
+ this.countSkippedBody(decl);
4062
4991
  }
4063
4992
  for (const decl of fp.classDecls) {
4064
4993
  const info = this.classes.get(this.classNamer(decl));
4065
4994
  if (info)
4066
4995
  functions.push(...this.lowerClassMembers(info));
4067
4996
  else if (this.countsSkips())
4068
- this.stats.functionsSkipped++;
4997
+ this.countSkippedBody(decl);
4069
4998
  }
4070
4999
  }
4071
5000
  // Each file's top-level statements form its run-once init function.
@@ -4153,10 +5082,10 @@ export class Lowerer {
4153
5082
  // Deferred collection diagnostics nothing flushed — declarations no
4154
5083
  // reference ever made relevant. They belong to the unreached group
4155
5084
  // (collection order keeps them deterministic).
4156
- for (const [symbol, diags] of this.deferredDiags) {
5085
+ for (const [symbol, diags] of [...this.deferredDiags]) {
4157
5086
  if (this.alreadyFlushed.has(symbol))
4158
5087
  continue;
4159
- for (const d of diags)
5088
+ for (const d of [...diags])
4160
5089
  this.pushDiag(d);
4161
5090
  }
4162
5091
  return {
@@ -4277,6 +5206,7 @@ export class Lowerer {
4277
5206
  ...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
4278
5207
  entry: ENTRY_NAME,
4279
5208
  ...(this.usesWorkers ? { workers: true } : {}),
5209
+ ...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
4280
5210
  ...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
4281
5211
  };
4282
5212
  if (module)
@@ -4298,41 +5228,72 @@ export class Lowerer {
4298
5228
  * registered ClassInfo — the shape of a JS class whose collection fenced.
4299
5229
  * Used by run()'s global pruning; shapes/unions recurse with a seen-set
4300
5230
  * (interned ids can nest). */
4301
- typeNamesUnregisteredClass(t, seen = new Set()) {
5231
+ typeNamesUnregisteredClass(t) {
5232
+ return this.unregisteredClassOf(t, new Set()) !== null;
5233
+ }
5234
+ /** The first class name typeNamesUnregisteredClass finds in `t`. */
5235
+ unregisteredClassOf(t, seen) {
5236
+ const first = (types) => {
5237
+ for (const type of types) {
5238
+ const name = this.unregisteredClassOf(type, seen);
5239
+ if (name !== null)
5240
+ return name;
5241
+ }
5242
+ return null;
5243
+ };
4302
5244
  switch (t.kind) {
4303
5245
  case "object":
4304
- return !this.classes.has(t.className);
5246
+ return this.classes.has(t.className) ? null : t.className;
4305
5247
  case "array":
4306
5248
  case "set":
4307
- return this.typeNamesUnregisteredClass(t.elem, seen);
5249
+ return first([t.elem]);
4308
5250
  case "map":
4309
- return (this.typeNamesUnregisteredClass(t.key, seen) ||
4310
- this.typeNamesUnregisteredClass(t.value, seen));
5251
+ return first([t.key, t.value]);
4311
5252
  case "promise":
4312
- return this.typeNamesUnregisteredClass(t.inner, seen);
5253
+ return first([t.inner]);
4313
5254
  case "func":
4314
- return (t.params.some((p) => this.typeNamesUnregisteredClass(p, seen)) ||
4315
- this.typeNamesUnregisteredClass(t.ret, seen));
5255
+ return first([...t.params, t.ret]);
4316
5256
  case "record": {
4317
5257
  if (seen.has(t.shapeId))
4318
- return false;
5258
+ return null;
4319
5259
  seen.add(t.shapeId);
4320
5260
  const shape = this.shapes.get(t.shapeId);
4321
5261
  if (!shape)
4322
- return false;
4323
- if (shape.indexValue && this.typeNamesUnregisteredClass(shape.indexValue, seen))
4324
- return true;
4325
- return shape.fields.some((f) => this.typeNamesUnregisteredClass(f.type, seen));
5262
+ return null;
5263
+ return first([
5264
+ ...(shape.indexValue ? [shape.indexValue] : []),
5265
+ ...shape.fields.map((f) => f.type),
5266
+ ]);
4326
5267
  }
4327
5268
  case "union": {
4328
5269
  if (seen.has(t.unionId))
4329
- return false;
5270
+ return null;
4330
5271
  seen.add(t.unionId);
4331
5272
  const def = this.unions.get(t.unionId);
4332
- return !!def && def.arms.some((a) => this.typeNamesUnregisteredClass(a, seen));
5273
+ return def ? first(def.arms) : null;
4333
5274
  }
4334
5275
  default:
4335
- return false;
5276
+ return null;
5277
+ }
5278
+ }
5279
+ /** A declaration whose signature blocked never lowers its body, so its
5280
+ * statements would otherwise vanish from the coverage totals and the
5281
+ * percentage would overstate how much of the program compiles. Count
5282
+ * every statement the body holds as attempted and failed, in addition to
5283
+ * the skipped-function tally. */
5284
+ countSkippedBody(decl) {
5285
+ this.stats.functionsSkipped++;
5286
+ if (this.suppressStats)
5287
+ return;
5288
+ const n = countBodyStatements(decl);
5289
+ if (n === 0)
5290
+ return;
5291
+ this.stats.statementsTotal += n;
5292
+ this.stats.statementsFailed += n;
5293
+ const file = decl.getSourceFile().fileName;
5294
+ for (let i = 0; i < n; i++) {
5295
+ this.bumpFileStat(file, "total");
5296
+ this.bumpFileStat(file, "failed");
4336
5297
  }
4337
5298
  }
4338
5299
  /** Whether run() counts a signature-blocked declaration in
@@ -4868,13 +5829,18 @@ export class Lowerer {
4868
5829
  const d = this.instantiationContext
4869
5830
  ? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
4870
5831
  : diag;
5832
+ const pending = this.pendingFlushes.get(diag);
4871
5833
  // Deferred collection: the wrapper decides whether these ever report
4872
5834
  // (a reference flushes them; unreferenced declarations stay silent in
4873
5835
  // builds and report under coverage's unreached group).
4874
5836
  if (this.diagSink) {
5837
+ if (pending && d !== diag)
5838
+ this.pendingFlushes.set(d, pending);
4875
5839
  this.diagSink.push(d);
4876
5840
  return;
4877
5841
  }
5842
+ if (pending)
5843
+ this.commitFlush(pending.symbol, pending.source);
4878
5844
  // One site, one report: some declarations map a type twice (module
4879
5845
  // globals pre-register before their initializers lower) — an exact
4880
5846
  // duplicate (code + span + message) adds noise, not information.
@@ -5518,6 +6484,24 @@ export class Lowerer {
5518
6484
  this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
5519
6485
  throw new PoisonError();
5520
6486
  }
6487
+ // Instances of two unrelated classes: class identity is nominal here,
6488
+ // so a structurally compatible instance never becomes the other class.
6489
+ // An assertion (`x as Y`) names the mistake most directly.
6490
+ if (actual.kind === "object" &&
6491
+ expected.kind === "object" &&
6492
+ this.classes.has(actual.className) &&
6493
+ this.classes.has(expected.className) &&
6494
+ this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
6495
+ !this.isSubclassOf(actual.className, expected.className) &&
6496
+ !this.isSubclassOf(expected.className, actual.className)) {
6497
+ let site = node;
6498
+ while (ts.isParenthesizedExpression(site))
6499
+ site = site.expression;
6500
+ const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
6501
+ 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
6502
+ ? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
6503
+ : `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
6504
+ }
5521
6505
  // Iterator-typed slots hold generators and built-in collection
5522
6506
  // iterators; any other iterator object (a user class) stays fenced.
5523
6507
  if (expected.kind === "generator" && actual.kind !== "generator") {
@@ -5601,6 +6585,13 @@ export class Lowerer {
5601
6585
  * else (including a DIFFERENT union) is left for requireExactShape, which
5602
6586
  * rejects union mismatches with SC2003. */
5603
6587
  coerceToExpected(expr, expected) {
6588
+ // A destination that holds undefined takes an unassigned deferred-init
6589
+ // field's slot as is (Node passes the undefined along).
6590
+ if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
6591
+ const slot = this.deferredFieldSlot(expr);
6592
+ if (slot)
6593
+ expr = slot;
6594
+ }
5604
6595
  if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
5605
6596
  return this.divergentValue(expr, expected);
5606
6597
  // Iterator-typed slots: native iterators and differently typed
@@ -5759,9 +6750,18 @@ export class Lowerer {
5759
6750
  };
5760
6751
  }
5761
6752
  if (expr.kind === "unitLit" || this.dynConvertible(expr.type)) {
5762
- const shape = expr.type.kind === "record" ? this.shapes.get(expr.type.shapeId) : undefined;
5763
- const liveRef = expr.type.kind === "array" ||
5764
- (!!shape && (shape.fields.length > 0 || shape.indexValue !== undefined));
6753
+ // Records and arrays keep their identity as untyped values, including
6754
+ // when they arrive through a union, so `unknown` views, weak
6755
+ // collection keys and later typed reads all see the same object.
6756
+ const liveReference = (type) => {
6757
+ if (type.kind === "array")
6758
+ return true;
6759
+ const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
6760
+ return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
6761
+ };
6762
+ const liveRef = liveReference(expr.type) ||
6763
+ (expr.type.kind === "union" &&
6764
+ (this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
5765
6765
  return {
5766
6766
  kind: "dynFrom",
5767
6767
  value: expr,
@@ -5955,7 +6955,7 @@ export class Lowerer {
5955
6955
  this.armTag(expr.type.unionId, expected) >= 0) {
5956
6956
  const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
5957
6957
  if (helper) {
5958
- return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
6958
+ return this.checkedNarrowCall(helper, expr, expected, expr.loc);
5959
6959
  }
5960
6960
  }
5961
6961
  if (!typeEquals(expr.type, expected)) {
@@ -6206,6 +7206,9 @@ export class Lowerer {
6206
7206
  narrowedArmHelper(fromId, target, loc) {
6207
7207
  return narrowedArmHelper(this, fromId, target, loc);
6208
7208
  }
7209
+ checkedNarrowCall(helper, value, type, loc) {
7210
+ return checkedNarrowCall(this, helper, value, type, loc);
7211
+ }
6209
7212
  deferredReadHelper(fromId, target, loc) {
6210
7213
  return deferredReadHelper(this, fromId, target, loc);
6211
7214
  }
@@ -7024,15 +8027,47 @@ export class Lowerer {
7024
8027
  flushDeferred(symbol) {
7025
8028
  if (this.collecting)
7026
8029
  return;
8030
+ this.reportDeferred(symbol);
8031
+ }
8032
+ /** flushDeferred without the collection-pass guard: for declarations
8033
+ * whose diagnostics must report eagerly (class statements with
8034
+ * declaration-time code, extended bases — collectClassShapeDeferring). */
8035
+ reportDeferred(symbol) {
7027
8036
  const diags = this.deferredDiags.get(symbol);
7028
8037
  if (!diags)
7029
8038
  return;
7030
- this.deferredDiags.delete(symbol);
7031
- if (this.alreadyFlushed.has(symbol))
7032
- return; // the emit pass reported these
7033
- this.flushedSymbols.add(symbol);
7034
- for (const d of diags)
8039
+ if (this.alreadyFlushed.has(symbol)) {
8040
+ this.deferredDiags.delete(symbol); // the emit pass reported these
8041
+ return;
8042
+ }
8043
+ if (this.diagSink === null) {
8044
+ this.deferredDiags.delete(symbol);
8045
+ this.flushedSymbols.add(symbol);
8046
+ for (const d of diags)
8047
+ this.pushDiag(d);
8048
+ return;
8049
+ }
8050
+ // A capture buffer is active (a probe, or another declaration's
8051
+ // deferred collection): the copies ride that buffer, and the
8052
+ // declaration stays deferred until one of them reaches the build.
8053
+ for (const d of diags) {
8054
+ this.pendingFlushes.set(d, { symbol, source: d });
7035
8055
  this.pushDiag(d);
8056
+ }
8057
+ }
8058
+ /** A flushed copy of a deferred diagnostic reached `diags`: that
8059
+ * diagnostic is reported, so it leaves the declaration's deferred list
8060
+ * (the declaration counts as flushed for the coverage remainder). */
8061
+ commitFlush(symbol, source) {
8062
+ this.flushedSymbols.add(symbol);
8063
+ const list = this.deferredDiags.get(symbol);
8064
+ if (!list)
8065
+ return;
8066
+ const index = list.indexOf(source);
8067
+ if (index >= 0)
8068
+ list.splice(index, 1);
8069
+ if (list.length === 0)
8070
+ this.deferredDiags.delete(symbol);
7036
8071
  }
7037
8072
  flushDeferredClass(className) {
7038
8073
  const symbol = this.deferredClassByName.get(className);
@@ -7339,7 +8374,23 @@ export class Lowerer {
7339
8374
  }
7340
8375
  /* ── scoping and captures ─────────────────────────────────────────── */
7341
8376
  declareLocal(nameNode, name, type, mutable) {
7342
- return declareContextLocal(this.ctx, name, type, mutable, this.checker.getSymbolAtLocation(nameNode), bindingSource(nameNode));
8377
+ const symbol = this.checker.getSymbolAtLocation(nameNode);
8378
+ const local = declareContextLocal(this.ctx, name, type, mutable, symbol, bindingSource(nameNode));
8379
+ // A binding declared `T | undefined` that receives a missing read: the
8380
+ // checker narrows it to `T` after the assignment, but the stored value
8381
+ // can be undefined. Track it like widened storage so bare reads keep
8382
+ // the union until a guard proves presence.
8383
+ if (symbol &&
8384
+ this.runtimeOptionalAssignedSymbols.has(symbol) &&
8385
+ type.kind === "union" &&
8386
+ this.armTag(type.unionId, UNDEFINED_T) >= 0 &&
8387
+ this.unions.get(type.unionId)?.arms.length === 2 &&
8388
+ !isUnitType(this.stripUndefinedArm(type))) {
8389
+ const root = this.runtimeOptionalRootOf(local);
8390
+ this.runtimeOptionalStorageLocals.add(root);
8391
+ this.runtimeOptionalLocals.add(root);
8392
+ }
8393
+ return local;
7343
8394
  }
7344
8395
  /** A never-returning call in a value position of type `expected`: the
7345
8396
  * call runs for its effects (it throws or exits), and the expression ends
@@ -8111,13 +9162,7 @@ export class Lowerer {
8111
9162
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
8112
9163
  if (!helper)
8113
9164
  return lowerForOf(this, stmt);
8114
- return this.withExpressionOverride(stmt.expression, {
8115
- kind: "call",
8116
- callee: helper,
8117
- args: [optional.value],
8118
- type: optional.present,
8119
- loc: locOf(stmt.expression),
8120
- }, () => lowerForOf(this, stmt));
9165
+ return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
8121
9166
  }
8122
9167
  lowerForStatement(stmt) {
8123
9168
  return lowerForStatement(this, stmt);
@@ -8362,21 +9407,21 @@ export class Lowerer {
8362
9407
  const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
8363
9408
  if (!helper)
8364
9409
  return lower();
8365
- right = {
8366
- kind: "call",
8367
- callee: helper,
8368
- args: [optionalRight.value],
8369
- type: optionalRight.present,
8370
- loc: locOf(expr.right),
8371
- };
9410
+ right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
8372
9411
  }
8373
9412
  else {
8374
9413
  right = this.lowerExpr(expr.right);
8375
9414
  }
8376
9415
  if (right.type.kind !== "classval")
8377
9416
  return lower();
8378
- const stableRight = this.declareHiddenLocal("%instanceofRight", right.type);
8379
- const rightValue = varRef(stableRight.id, right.type, locOf(expr.right));
9417
+ // A capture-free class reference is the immortal static class object:
9418
+ // re-reading it is pure, so it needs no owned temporary (whose retain
9419
+ // and release would be out-of-line calls on every test).
9420
+ const constantRight = right.kind === "classRef" && right.captures === undefined;
9421
+ const stableRight = constantRight
9422
+ ? null
9423
+ : this.declareHiddenLocal("%instanceofRight", right.type);
9424
+ const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
8380
9425
  const body = this.withExpressionOverride(expr.left, {
8381
9426
  kind: "unionNarrow",
8382
9427
  unionId: left.type.unionId,
@@ -8389,7 +9434,16 @@ export class Lowerer {
8389
9434
  kind: "seqExpr",
8390
9435
  stmts: [
8391
9436
  { kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
8392
- { kind: "varDecl", localId: stableRight.id, init: right, loc: locOf(expr.right) },
9437
+ ...(stableRight
9438
+ ? [
9439
+ {
9440
+ kind: "varDecl",
9441
+ localId: stableRight.id,
9442
+ init: right,
9443
+ loc: locOf(expr.right),
9444
+ },
9445
+ ]
9446
+ : []),
8393
9447
  ],
8394
9448
  result: {
8395
9449
  kind: "ternary",
@@ -8420,13 +9474,7 @@ export class Lowerer {
8420
9474
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
8421
9475
  if (!helper)
8422
9476
  return lower();
8423
- return this.withExpressionOverride(node, {
8424
- kind: "call",
8425
- callee: helper,
8426
- args: [optional.value],
8427
- type: optional.present,
8428
- loc: locOf(node),
8429
- }, lower);
9477
+ return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
8430
9478
  }
8431
9479
  withExpressionOverride(node, value, lower) {
8432
9480
  const previous = this.chainRecvByNode.get(node);
@@ -8535,6 +9583,37 @@ export class Lowerer {
8535
9583
  fieldGetExpr(target, loc, blame) {
8536
9584
  return fieldGetExpr(this, target, loc, blame);
8537
9585
  }
9586
+ /** The undefined-armed slot read behind a checked deferred-init field
9587
+ * read, or null. Consumers that observe undefined itself (equality,
9588
+ * typeof, truthiness, nullish defaults) test the slot instead of the
9589
+ * checked extraction, which throws for an unassigned field. */
9590
+ deferredFieldSlot(value) {
9591
+ // The inline form: `slot is undefined ? throw : narrow(slot)`.
9592
+ if (value.kind === "ternary" &&
9593
+ value.cond.kind === "unionIsTag" &&
9594
+ value.else_.kind === "unionNarrow" &&
9595
+ value.else_.value === value.cond.value &&
9596
+ value.then.kind === "libCall" &&
9597
+ value.then.fn === "error.nodeThrow") {
9598
+ const read = value.cond.value;
9599
+ return read.kind === "fieldGet" &&
9600
+ read.type.kind === "union" &&
9601
+ this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
9602
+ ? read
9603
+ : null;
9604
+ }
9605
+ if (value.kind !== "call" || value.args.length !== 1)
9606
+ return null;
9607
+ const read = value.args[0];
9608
+ if (read.kind !== "fieldGet" || read.type.kind !== "union")
9609
+ return null;
9610
+ if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
9611
+ return null;
9612
+ return this.coercions.checkedNarrows.has(value.callee) ||
9613
+ value.callee.startsWith("%deferred.read.")
9614
+ ? read
9615
+ : null;
9616
+ }
8538
9617
  fieldSetStmt(target, value, loc, blame) {
8539
9618
  return fieldSetStmt(this, target, value, loc, blame);
8540
9619
  }
@@ -9218,8 +10297,8 @@ export class Lowerer {
9218
10297
  return lowerNumberStaticProperty(this, expr);
9219
10298
  }
9220
10299
  /* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
9221
- requireDynamicApi(feature, node) {
9222
- return requireDynamicApi(this, feature, node);
10300
+ requireDynamicApi(feature, node, hint) {
10301
+ return requireDynamicApi(this, feature, node, hint);
9223
10302
  }
9224
10303
  lowerMathProperty(expr) {
9225
10304
  return lowerMathProperty(this, expr);