@scriptc/compiler 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +2 -0
  2. package/dist/backend/llvm/alloc.d.ts +3 -0
  3. package/dist/backend/llvm/alloc.js +54 -13
  4. package/dist/backend/llvm/alloc.js.map +1 -1
  5. package/dist/backend/llvm/blocks.d.ts +9 -0
  6. package/dist/backend/llvm/blocks.js +9 -0
  7. package/dist/backend/llvm/blocks.js.map +1 -1
  8. package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
  9. package/dist/backend/llvm/borrowed-receivers.js +39 -0
  10. package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
  11. package/dist/backend/llvm/call-lifetimes.d.ts +4 -1
  12. package/dist/backend/llvm/call-lifetimes.js +14 -3
  13. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  14. package/dist/backend/llvm/call-lifetimes.test.js +8 -3
  15. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  16. package/dist/backend/llvm/checked-narrows.d.ts +24 -0
  17. package/dist/backend/llvm/checked-narrows.js +108 -0
  18. package/dist/backend/llvm/checked-narrows.js.map +1 -0
  19. package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
  20. package/dist/backend/llvm/checked-narrows.test.js +208 -0
  21. package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
  22. package/dist/backend/llvm/classes.d.ts +13 -1
  23. package/dist/backend/llvm/classes.js +26 -18
  24. package/dist/backend/llvm/classes.js.map +1 -1
  25. package/dist/backend/llvm/classes.test.js +32 -3
  26. package/dist/backend/llvm/classes.test.js.map +1 -1
  27. package/dist/backend/llvm/debug-info.d.ts +6 -2
  28. package/dist/backend/llvm/debug-info.js +11 -1
  29. package/dist/backend/llvm/debug-info.js.map +1 -1
  30. package/dist/backend/llvm/dense-array-access.test.js +89 -0
  31. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  32. package/dist/backend/llvm/dyn.d.ts +4 -0
  33. package/dist/backend/llvm/dyn.js +31 -20
  34. package/dist/backend/llvm/dyn.js.map +1 -1
  35. package/dist/backend/llvm/emitter.d.ts +184 -11
  36. package/dist/backend/llvm/emitter.js +1189 -125
  37. package/dist/backend/llvm/emitter.js.map +1 -1
  38. package/dist/backend/llvm/emitter.test.js +21 -1
  39. package/dist/backend/llvm/emitter.test.js.map +1 -1
  40. package/dist/backend/llvm/expr-async.js +4 -4
  41. package/dist/backend/llvm/expr-async.js.map +1 -1
  42. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  43. package/dist/backend/llvm/expr-bytes.js +85 -8
  44. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  45. package/dist/backend/llvm/expr-callbacks.js +29 -21
  46. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  47. package/dist/backend/llvm/expr-calls.d.ts +5 -0
  48. package/dist/backend/llvm/expr-calls.js +55 -10
  49. package/dist/backend/llvm/expr-calls.js.map +1 -1
  50. package/dist/backend/llvm/expr-containers.d.ts +7 -1
  51. package/dist/backend/llvm/expr-containers.js +230 -27
  52. package/dist/backend/llvm/expr-containers.js.map +1 -1
  53. package/dist/backend/llvm/expr-control.js +25 -10
  54. package/dist/backend/llvm/expr-control.js.map +1 -1
  55. package/dist/backend/llvm/expr-dynamic.js +72 -21
  56. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  57. package/dist/backend/llvm/expr-island.js +3 -8
  58. package/dist/backend/llvm/expr-island.js.map +1 -1
  59. package/dist/backend/llvm/expr-primitives.js +41 -12
  60. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  61. package/dist/backend/llvm/expr-primitives.test.js +50 -2
  62. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  63. package/dist/backend/llvm/expr-records.js +28 -14
  64. package/dist/backend/llvm/expr-records.js.map +1 -1
  65. package/dist/backend/llvm/expr-stream-bridges.js +41 -31
  66. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  67. package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
  68. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  69. package/dist/backend/llvm/layouts.d.ts +2 -1
  70. package/dist/backend/llvm/layouts.js +2 -2
  71. package/dist/backend/llvm/layouts.js.map +1 -1
  72. package/dist/backend/llvm/lib-abi.js +10 -0
  73. package/dist/backend/llvm/lib-abi.js.map +1 -1
  74. package/dist/backend/llvm/lib-filesystem.js +7 -11
  75. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  76. package/dist/backend/llvm/lib-process.js +8 -8
  77. package/dist/backend/llvm/lib-process.js.map +1 -1
  78. package/dist/backend/llvm/lib-shared.js +1 -0
  79. package/dist/backend/llvm/lib-shared.js.map +1 -1
  80. package/dist/backend/llvm/local-array-reads.js +73 -13
  81. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  82. package/dist/backend/llvm/local-array-reads.test.js +7 -3
  83. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  84. package/dist/backend/llvm/local-union-storage.js +10 -6
  85. package/dist/backend/llvm/local-union-storage.js.map +1 -1
  86. package/dist/backend/llvm/local-union-storage.test.js +3 -2
  87. package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
  88. package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
  89. package/dist/backend/llvm/map-number-lookup.js +62 -0
  90. package/dist/backend/llvm/map-number-lookup.js.map +1 -0
  91. package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
  92. package/dist/backend/llvm/map-read-lifetimes.js +24 -3
  93. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  94. package/dist/backend/llvm/map-read-lifetimes.test.js +38 -19
  95. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  96. package/dist/backend/llvm/nullable-unions.d.ts +68 -0
  97. package/dist/backend/llvm/nullable-unions.js +132 -0
  98. package/dist/backend/llvm/nullable-unions.js.map +1 -0
  99. package/dist/backend/llvm/publish.d.ts +58 -0
  100. package/dist/backend/llvm/publish.js +286 -0
  101. package/dist/backend/llvm/publish.js.map +1 -0
  102. package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
  103. package/dist/backend/llvm/rebound-parameters.js +72 -0
  104. package/dist/backend/llvm/rebound-parameters.js.map +1 -0
  105. package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
  106. package/dist/backend/llvm/rebound-parameters.test.js +67 -0
  107. package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
  108. package/dist/backend/llvm/reference-effects.d.ts +18 -2
  109. package/dist/backend/llvm/reference-effects.js +106 -16
  110. package/dist/backend/llvm/reference-effects.js.map +1 -1
  111. package/dist/backend/llvm/reference-effects.test.js +52 -1
  112. package/dist/backend/llvm/reference-effects.test.js.map +1 -1
  113. package/dist/backend/llvm/shapes.d.ts +45 -17
  114. package/dist/backend/llvm/shapes.js +131 -53
  115. package/dist/backend/llvm/shapes.js.map +1 -1
  116. package/dist/backend/llvm/shapes.test.js +33 -8
  117. package/dist/backend/llvm/shapes.test.js.map +1 -1
  118. package/dist/backend/llvm/stack-unions.test.js +5 -2
  119. package/dist/backend/llvm/stack-unions.test.js.map +1 -1
  120. package/dist/backend/llvm/string-construction.d.ts +10 -0
  121. package/dist/backend/llvm/string-construction.js +42 -0
  122. package/dist/backend/llvm/string-construction.js.map +1 -1
  123. package/dist/backend/llvm/string-key-hash.d.ts +9 -2
  124. package/dist/backend/llvm/string-key-hash.js +20 -3
  125. package/dist/backend/llvm/string-key-hash.js.map +1 -1
  126. package/dist/backend/llvm/string-key-hash.test.js +14 -3
  127. package/dist/backend/llvm/string-key-hash.test.js.map +1 -1
  128. package/dist/backend/llvm/string-lifetimes.test.js +24 -11
  129. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  130. package/dist/backend/llvm/string-reads.d.ts +7 -0
  131. package/dist/backend/llvm/string-reads.js +76 -0
  132. package/dist/backend/llvm/string-reads.js.map +1 -0
  133. package/dist/backend/llvm/switch-dispatch.test.js +1 -1
  134. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  135. package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
  136. package/dist/backend/llvm/test-boxed-unions.js +13 -0
  137. package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
  138. package/dist/backend/llvm/union-repr.d.ts +15 -0
  139. package/dist/backend/llvm/union-repr.js +89 -0
  140. package/dist/backend/llvm/union-repr.js.map +1 -0
  141. package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
  142. package/dist/backend/llvm/virtual-borrows.js +82 -0
  143. package/dist/backend/llvm/virtual-borrows.js.map +1 -0
  144. package/dist/backend/llvm/walk-borrows.d.ts +43 -0
  145. package/dist/backend/llvm/walk-borrows.js +150 -0
  146. package/dist/backend/llvm/walk-borrows.js.map +1 -0
  147. package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
  148. package/dist/backend/llvm/walk-borrows.test.js +119 -0
  149. package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
  150. package/dist/backend/llvm/walkers.d.ts +5 -1
  151. package/dist/backend/llvm/walkers.js +24 -21
  152. package/dist/backend/llvm/walkers.js.map +1 -1
  153. package/dist/backend/may-throw.d.ts +29 -1
  154. package/dist/backend/may-throw.js +346 -3
  155. package/dist/backend/may-throw.js.map +1 -1
  156. package/dist/backend/may-throw.test.js +113 -1
  157. package/dist/backend/may-throw.test.js.map +1 -1
  158. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  159. package/dist/backend/native/runtime-inputs.js +1 -0
  160. package/dist/backend/native/runtime-inputs.js.map +1 -1
  161. package/dist/backend/native-codegen-core.d.ts +7 -5
  162. package/dist/backend/native-codegen-core.js +14 -9
  163. package/dist/backend/native-codegen-core.js.map +1 -1
  164. package/dist/backend/native-codegen.d.ts +4 -0
  165. package/dist/backend/native-codegen.js +49 -2
  166. package/dist/backend/native-codegen.js.map +1 -1
  167. package/dist/backend/native-codegen.test.js +41 -10
  168. package/dist/backend/native-codegen.test.js.map +1 -1
  169. package/dist/backend/native-tools.d.ts +2 -0
  170. package/dist/backend/native-tools.js +3 -0
  171. package/dist/backend/native-tools.js.map +1 -1
  172. package/dist/backend/publication.d.ts +28 -0
  173. package/dist/backend/publication.js +155 -0
  174. package/dist/backend/publication.js.map +1 -0
  175. package/dist/backend/publication.test.d.ts +1 -0
  176. package/dist/backend/publication.test.js +72 -0
  177. package/dist/backend/publication.test.js.map +1 -0
  178. package/dist/backend/runtime-abi.d.ts +2 -2
  179. package/dist/backend/runtime-abi.js +2 -2
  180. package/dist/backend/runtime-pack-targets.test.js +1 -1
  181. package/dist/backend/runtime-pack.test.js +1 -1
  182. package/dist/cli/command.js +70 -7
  183. package/dist/cli/command.js.map +1 -1
  184. package/dist/cli/usage.d.ts +4 -1
  185. package/dist/cli/usage.js +8 -1
  186. package/dist/cli/usage.js.map +1 -1
  187. package/dist/compile-types.d.ts +12 -3
  188. package/dist/coverage/report.d.ts +25 -2
  189. package/dist/coverage/report.js +161 -33
  190. package/dist/coverage/report.js.map +1 -1
  191. package/dist/coverage/verdict-cache.d.ts +63 -0
  192. package/dist/coverage/verdict-cache.js +193 -0
  193. package/dist/coverage/verdict-cache.js.map +1 -0
  194. package/dist/coverage/verdict-cache.test.d.ts +1 -0
  195. package/dist/coverage/verdict-cache.test.js +52 -0
  196. package/dist/coverage/verdict-cache.test.js.map +1 -0
  197. package/dist/coverage/verdict-semantic.d.ts +2 -0
  198. package/dist/coverage/verdict-semantic.js +24 -0
  199. package/dist/coverage/verdict-semantic.js.map +1 -0
  200. package/dist/coverage/verdict-startup.d.ts +15 -0
  201. package/dist/coverage/verdict-startup.js +66 -0
  202. package/dist/coverage/verdict-startup.js.map +1 -0
  203. package/dist/diagnostics/diagnostic.d.ts +29 -1
  204. package/dist/diagnostics/diagnostic.js +74 -4
  205. package/dist/diagnostics/diagnostic.js.map +1 -1
  206. package/dist/diagnostics/envelope.d.ts +130 -0
  207. package/dist/diagnostics/envelope.js +257 -0
  208. package/dist/diagnostics/envelope.js.map +1 -0
  209. package/dist/diagnostics/envelope.test.d.ts +1 -0
  210. package/dist/diagnostics/envelope.test.js +91 -0
  211. package/dist/diagnostics/envelope.test.js.map +1 -0
  212. package/dist/diagnostics/render.d.ts +3 -0
  213. package/dist/diagnostics/render.js +13 -2
  214. package/dist/diagnostics/render.js.map +1 -1
  215. package/dist/executable/prepare.d.ts +4 -0
  216. package/dist/executable/prepare.js +7 -1
  217. package/dist/executable/prepare.js.map +1 -1
  218. package/dist/frontend/analysis.js +1 -0
  219. package/dist/frontend/analysis.js.map +1 -1
  220. package/dist/frontend/lowering/array-values.d.ts +7 -0
  221. package/dist/frontend/lowering/array-values.js +120 -4
  222. package/dist/frontend/lowering/array-values.js.map +1 -1
  223. package/dist/frontend/lowering/builtins/numbers.js +29 -1
  224. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  225. package/dist/frontend/lowering/builtins/process.d.ts +4 -0
  226. package/dist/frontend/lowering/builtins/process.js +10 -0
  227. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  228. package/dist/frontend/lowering/builtins/workers.js +4 -2
  229. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  230. package/dist/frontend/lowering/class-assertions.d.ts +8 -0
  231. package/dist/frontend/lowering/class-assertions.js +217 -1
  232. package/dist/frontend/lowering/class-assertions.js.map +1 -1
  233. package/dist/frontend/lowering/class-dynamic-dispatch.js +8 -5
  234. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  235. package/dist/frontend/lowering/coercions/state.d.ts +6 -0
  236. package/dist/frontend/lowering/coercions/state.js +2 -0
  237. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  238. package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
  239. package/dist/frontend/lowering/coercions/unions.js +67 -2
  240. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  241. package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
  242. package/dist/frontend/lowering/collation-repertoire.js +19 -0
  243. package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
  244. package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
  245. package/dist/frontend/lowering/construction-escapes.js +146 -0
  246. package/dist/frontend/lowering/construction-escapes.js.map +1 -0
  247. package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
  248. package/dist/frontend/lowering/construction-escapes.test.js +150 -0
  249. package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
  250. package/dist/frontend/lowering/containers/array-callbacks.js +81 -44
  251. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  252. package/dist/frontend/lowering/containers/array-construction.js +15 -2
  253. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  254. package/dist/frontend/lowering/containers/array-methods.js +231 -12
  255. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  256. package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
  257. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  258. package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
  259. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  260. package/dist/frontend/lowering/cycle-initialization.d.ts +6 -1
  261. package/dist/frontend/lowering/cycle-initialization.js +59 -33
  262. package/dist/frontend/lowering/cycle-initialization.js.map +1 -1
  263. package/dist/frontend/lowering/divergences.d.ts +9 -0
  264. package/dist/frontend/lowering/divergences.js +630 -0
  265. package/dist/frontend/lowering/divergences.js.map +1 -0
  266. package/dist/frontend/lowering/expressions/object-literals.js +40 -2
  267. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  268. package/dist/frontend/lowering/lower-calls.d.ts +5 -0
  269. package/dist/frontend/lowering/lower-calls.js +226 -157
  270. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  271. package/dist/frontend/lowering/lower-classes.d.ts +4 -0
  272. package/dist/frontend/lowering/lower-classes.js +72 -22
  273. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  274. package/dist/frontend/lowering/lower-exprs.d.ts +13 -0
  275. package/dist/frontend/lowering/lower-exprs.js +431 -107
  276. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  277. package/dist/frontend/lowering/lower-generators.d.ts +19 -13
  278. package/dist/frontend/lowering/lower-generators.js +67 -44
  279. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  280. package/dist/frontend/lowering/lower-inspect.js +3 -2
  281. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  282. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  283. package/dist/frontend/lowering/lower-island.js +24 -7
  284. package/dist/frontend/lowering/lower-island.js.map +1 -1
  285. package/dist/frontend/lowering/lower-modules.js +90 -1
  286. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  287. package/dist/frontend/lowering/lower-stmts.js +36 -6
  288. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  289. package/dist/frontend/lowering/lowerer.d.ts +78 -2
  290. package/dist/frontend/lowering/lowerer.js +986 -73
  291. package/dist/frontend/lowering/lowerer.js.map +1 -1
  292. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
  293. package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
  294. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
  295. package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
  296. package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
  297. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
  298. package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
  299. package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
  300. package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
  301. package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
  302. package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
  303. package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
  304. package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
  305. package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
  306. package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
  307. package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
  308. package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
  309. package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
  310. package/dist/frontend/lowering/runtime-optional-provenance.d.ts +2 -1
  311. package/dist/frontend/lowering/runtime-optional-provenance.js +2 -1
  312. package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -1
  313. package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
  314. package/dist/frontend/lowering/string-index-bounds.js +537 -0
  315. package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
  316. package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
  317. package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
  318. package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
  319. package/dist/frontend/lowering/union-narrow.d.ts +2 -0
  320. package/dist/frontend/lowering/union-narrow.js +10 -6
  321. package/dist/frontend/lowering/union-narrow.js.map +1 -1
  322. package/dist/frontend/program-analysis.d.ts +10 -0
  323. package/dist/frontend/program-analysis.js +7 -0
  324. package/dist/frontend/program-analysis.js.map +1 -1
  325. package/dist/frontend/program.d.ts +25 -0
  326. package/dist/frontend/program.js +613 -17
  327. package/dist/frontend/program.js.map +1 -1
  328. package/dist/frontend/program.test.js +64 -1
  329. package/dist/frontend/program.test.js.map +1 -1
  330. package/dist/frontend/resolve.d.ts +6 -0
  331. package/dist/frontend/resolve.js +14 -0
  332. package/dist/frontend/resolve.js.map +1 -1
  333. package/dist/frontend/threads-import.d.ts +5 -0
  334. package/dist/frontend/threads-import.js +21 -0
  335. package/dist/frontend/threads-import.js.map +1 -0
  336. package/dist/frontend/type-mapper.d.ts +3 -0
  337. package/dist/frontend/type-mapper.js +72 -6
  338. package/dist/frontend/type-mapper.js.map +1 -1
  339. package/dist/frontend/worker-target.d.ts +4 -0
  340. package/dist/frontend/worker-target.js +8 -1
  341. package/dist/frontend/worker-target.js.map +1 -1
  342. package/dist/index.d.ts +6 -1
  343. package/dist/index.js +47 -9
  344. package/dist/index.js.map +1 -1
  345. package/dist/ir/build.d.ts +5 -0
  346. package/dist/ir/build.js +7 -0
  347. package/dist/ir/build.js.map +1 -1
  348. package/dist/ir/builtin-signatures.d.ts +1 -1
  349. package/dist/ir/builtin-signatures.js +3 -0
  350. package/dist/ir/builtin-signatures.js.map +1 -1
  351. package/dist/ir/int32-slots.d.ts +72 -0
  352. package/dist/ir/int32-slots.js +837 -0
  353. package/dist/ir/int32-slots.js.map +1 -0
  354. package/dist/ir/int32-slots.test.d.ts +1 -0
  355. package/dist/ir/int32-slots.test.js +222 -0
  356. package/dist/ir/int32-slots.test.js.map +1 -0
  357. package/dist/ir/integer-ranges.d.ts +19 -3
  358. package/dist/ir/integer-ranges.js +168 -23
  359. package/dist/ir/integer-ranges.js.map +1 -1
  360. package/dist/ir/integer-ranges.test.js +74 -2
  361. package/dist/ir/integer-ranges.test.js.map +1 -1
  362. package/dist/ir/ir.d.ts +24 -6
  363. package/dist/ir/ir.js.map +1 -1
  364. package/dist/ir/validate.js +21 -2
  365. package/dist/ir/validate.js.map +1 -1
  366. package/dist/ir/validate.test.js +39 -0
  367. package/dist/ir/validate.test.js.map +1 -1
  368. package/dist/native/cache.d.ts +3 -0
  369. package/dist/native/cache.js +49 -1
  370. package/dist/native/cache.js.map +1 -1
  371. package/dist/native/cache.test.js +21 -1
  372. package/dist/native/cache.test.js.map +1 -1
  373. package/dist/native/cli.js +1 -1
  374. package/dist/native/cli.js.map +1 -1
  375. package/dist/native/compiler.d.ts +5 -0
  376. package/dist/native/compiler.js +51 -4
  377. package/dist/native/compiler.js.map +1 -1
  378. package/dist/native/prepare.d.ts +4 -0
  379. package/dist/native/prepare.js +2 -1
  380. package/dist/native/prepare.js.map +1 -1
  381. package/dist/startup-cache.d.ts +2 -0
  382. package/dist/startup-cache.js +2 -0
  383. package/dist/startup-cache.js.map +1 -1
  384. package/package.json +20 -20
  385. package/surface-manifest.json +1 -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,16 @@ 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";
20
26
  import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
21
27
  import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
22
28
  import { UnregisteredClassTypes } from "./unregistered-class-types.js";
@@ -52,7 +58,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
52
58
  import { arrayOf, BOOL, canAdaptDynFuncTo, canDynCheckTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, DYN_HANDLE_KINDS, F64, isDynTypedRefType, isJsonSafeType, isJsonStringifySafeType, isPrimitiveCollectionKey, isSupportedMapKey, isSupportedMapValue, isUndefinedArmedUnion, isUnitType, JSVAL, NULL_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID, } from "../../ir/ir.js";
53
59
  import { provenanceActive } from "../provenance-registry.js";
54
60
  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";
61
+ import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
56
62
  import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
57
63
  import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
58
64
  import { prepareCjsModuleGraph } from "./lower-node-module.js";
@@ -197,6 +203,39 @@ export function stmtUsesIsland(stmts) {
197
203
  return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
198
204
  return !everyStmtChild(stmts, expr, nested);
199
205
  }
206
+ /** Statements a never-lowered body would have contributed to the coverage
207
+ * totals: every element of a statement list (blocks, case clauses), plus
208
+ * the single-statement bodies of control flow, nested functions included —
209
+ * the same accounting lowerStmts applies to bodies that do lower. */
210
+ export function countBodyStatements(root) {
211
+ let n = 0;
212
+ const visit = (node) => {
213
+ if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
214
+ n += node.statements.length;
215
+ }
216
+ else if ((ts.isIfStatement(node) ||
217
+ ts.isForStatement(node) ||
218
+ ts.isForInStatement(node) ||
219
+ ts.isForOfStatement(node) ||
220
+ ts.isWhileStatement(node) ||
221
+ ts.isDoStatement(node) ||
222
+ ts.isLabeledStatement(node)) &&
223
+ node.parent !== undefined) {
224
+ const bodies = ts.isIfStatement(node)
225
+ ? [node.thenStatement, node.elseStatement]
226
+ : ts.isLabeledStatement(node)
227
+ ? [node.statement]
228
+ : [node.statement];
229
+ for (const body of bodies) {
230
+ if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
231
+ n++;
232
+ }
233
+ }
234
+ };
235
+ // Iterative: absurdly deep nesting must not overflow the stack here.
236
+ ts.walkPreorder(root, visit);
237
+ return n;
238
+ }
200
239
  function directExternalTypeSpecifiersByFile(externalTypes) {
201
240
  const out = new Map();
202
241
  for (const [specifier, file] of externalTypes) {
@@ -349,6 +388,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
349
388
  resultLowerer = emit;
350
389
  timing("historical-order-relower");
351
390
  }
391
+ // Divergence warnings over the program's own modules: computed once,
392
+ // after lowering, from the same checker (never part of a failure).
393
+ const divergences = scanDivergences(resultLowerer, moduleOrder);
394
+ timing("divergence-scan", { found: divergences.length });
395
+ if (divergences.length > 0)
396
+ result = { ...result, divergences };
352
397
  if (options.coverage !== true)
353
398
  return result;
354
399
  const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
@@ -366,7 +411,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
366
411
  externalTypeSpecifiersByFile,
367
412
  });
368
413
  const rem = remainder.run();
369
- return { ...result, unreached: { diagnostics: rem.diagnostics, stats: rem.stats } };
414
+ // Unreached JS statements whose fences deferred to runtime count as
415
+ // failed statements in the remainder's stats, so their diagnostics must
416
+ // travel with them: a failed statement is never left unexplained.
417
+ return {
418
+ ...result,
419
+ unreached: {
420
+ diagnostics: rem.diagnostics,
421
+ stats: rem.stats,
422
+ ...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
423
+ },
424
+ };
370
425
  }
371
426
  /** The island-handle type a `import(...)` initializer gives a binding
372
427
  * whose DECLARED type has no static mapping (`Promise<typeof
@@ -882,6 +937,17 @@ export function jsFuncNameOf(node, lowerer) {
882
937
  }
883
938
  return null;
884
939
  }
940
+ /** Answers `fallback` when a checker query panics inside the type checker. */
941
+ function panicSafe(query, fallback) {
942
+ try {
943
+ return query();
944
+ }
945
+ catch (e) {
946
+ if (isCheckerPanic(e))
947
+ return fallback;
948
+ throw e;
949
+ }
950
+ }
885
951
  export class Lowerer {
886
952
  program;
887
953
  entry;
@@ -910,6 +976,14 @@ export class Lowerer {
910
976
  /** Per-symbol result of the never-reassigned file scan
911
977
  * (bindingNeverReassigned — object-literal generic-method receivers). */
912
978
  neverReassignedCache = new Map();
979
+ stringIndexBoundsCache = null;
980
+ /** Proofs that a string element read names an existing code unit. The
981
+ * indexed-read analysis and expression lowering share one instance, so a
982
+ * read's representation and its callers' signatures always agree. */
983
+ get stringIndexBounds() {
984
+ this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
985
+ return this.stringIndexBoundsCache;
986
+ }
913
987
  /** Syntactic writes indexed once per JS scope. Symbol and RHS type
914
988
  * checks remain contextual to each binding probe. */
915
989
  jsBindingWritesByOwner = new Map();
@@ -1156,6 +1230,11 @@ export class Lowerer {
1156
1230
  * prepass. This includes explicit annotations: unchecked TS annotations do
1157
1231
  * not prove that an array property read produced a value. */
1158
1232
  runtimeOptionalBindingTypes = new Map();
1233
+ /** Bindings that receive an unchecked indexed read (or a value derived
1234
+ * from one) through an initializer or assignment. The checker narrows a
1235
+ * declared optional binding to that read's bare type, so presence tests
1236
+ * on these bindings consult the stored tag instead. */
1237
+ runtimeOptionalAssignedSymbols = new Set();
1159
1238
  /** Concise callback/lambda returns promoted by the HOF callback prepass;
1160
1239
  * arrows have no declaration symbol to key in fnSigsBySymbol. */
1161
1240
  runtimeOptionalFunctionReturns = new Map();
@@ -1173,6 +1252,17 @@ export class Lowerer {
1173
1252
  * every name destructured from a present source optional. */
1174
1253
  runtimeOptionalPatternTypes = new Map();
1175
1254
  runtimeOptionalReduceTypes = new Map();
1255
+ /** Non-null assertions whose value is forwarded into a slot promoted to
1256
+ * hold undefined. `x!` has no runtime effect, so these keep the stored
1257
+ * undefined state instead of trapping before any dereference. */
1258
+ runtimeOptionalAssertions = new Set();
1259
+ /** Array callback calls whose receiver the optional-read analysis proved
1260
+ * never holds an explicit undefined (or a hole, for the find family):
1261
+ * their callbacks take the element type itself. */
1262
+ presentElementCalls = new Set();
1263
+ /** for-of loops over arrays that can hold neither holes nor undefined:
1264
+ * each pass reads the element payload directly. */
1265
+ presentElementLoops = new Set();
1176
1266
  /** Capture entries and their origin share one mutable box. Normalize each
1177
1267
  * entry to the origin so writes and flow proofs stay synchronized. */
1178
1268
  runtimeOptionalRoots = new Map();
@@ -1247,6 +1337,9 @@ export class Lowerer {
1247
1337
  * optional-read analysis, so ordinary assertions keep their generic
1248
1338
  * checked-narrow behavior. */
1249
1339
  runtimeOptionalSourceValue(node, value) {
1340
+ const slot = this.deferredFieldSlot(value);
1341
+ if (slot)
1342
+ return slot;
1250
1343
  let origin = node;
1251
1344
  while (ts.isParenthesizedExpression(origin))
1252
1345
  origin = origin.expression;
@@ -1281,12 +1374,9 @@ export class Lowerer {
1281
1374
  value.value.type.kind === "dyn")
1282
1375
  return value.value;
1283
1376
  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
- }
1377
+ const narrowed = checkedNarrowOperand(this, value);
1378
+ if (narrowed?.type.kind === "union")
1379
+ source = narrowed;
1290
1380
  return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
1291
1381
  ? source
1292
1382
  : null;
@@ -1399,6 +1489,41 @@ export class Lowerer {
1399
1489
  runtimeOptionalFunctionReturnType(node, fallback) {
1400
1490
  return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
1401
1491
  }
1492
+ /** True when `actual` is `declared` with some record fields (directly,
1493
+ * in nested records, or in an array's element records) widened to runtime-optional unions:
1494
+ * fields filled from unchecked indexed reads the checker calls present.
1495
+ * Inferred bindings and destructured parameters keep that layout instead
1496
+ * of narrowing it away. */
1497
+ isRuntimeOptionalRecordVariant(actual, declared) {
1498
+ if (actual.kind === "array" && declared.kind === "array")
1499
+ return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
1500
+ if (actual.kind !== "record" || declared.kind !== "record")
1501
+ return false;
1502
+ if (actual.shapeId === declared.shapeId)
1503
+ return false;
1504
+ const a = this.shapes.get(actual.shapeId);
1505
+ const d = this.shapes.get(declared.shapeId);
1506
+ if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
1507
+ return false;
1508
+ if (a.indexValue || d.indexValue)
1509
+ return false;
1510
+ let widened = false;
1511
+ for (const field of d.fields) {
1512
+ const source = a.fields.find((f) => f.name === field.name);
1513
+ if (!source)
1514
+ return false;
1515
+ if (typeEquals(source.type, field.type))
1516
+ continue;
1517
+ if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
1518
+ (this.runtimeOptionalWidening(source.type, field.type) !== null ||
1519
+ this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
1520
+ widened = true;
1521
+ continue;
1522
+ }
1523
+ return false;
1524
+ }
1525
+ return widened;
1526
+ }
1402
1527
  /** Promote one field of a record storage shape to the value's runtime
1403
1528
  * type. Shapes are immutable by identity, so this interns a sibling shape
1404
1529
  * and records the field as runtime-optional for property-read narrowing. */
@@ -1699,6 +1824,9 @@ export class Lowerer {
1699
1824
  * and therefore becomes the cycle's evaluation root. Dynamic imports
1700
1825
  * wait on this shared verdict rather than a build-time-selected member. */
1701
1826
  asyncCyclePromiseOf = new Map();
1827
+ /** Modules whose evaluation an import() can request again after it threw,
1828
+ * computed on first use (lower-modules.ts importRetryModules). */
1829
+ importRetryModules = null;
1702
1830
  /** Record-shape interner: canonical (name-sorted) field list → shapeId.
1703
1831
  * Threaded into every mapType call; its `shapes` array becomes
1704
1832
  * IrModule.records. */
@@ -1795,6 +1923,13 @@ export class Lowerer {
1795
1923
  * coverage remainder as alreadyFlushed). */
1796
1924
  flushedSymbols = new Set();
1797
1925
  alreadyFlushed;
1926
+ /** Deferred diagnostics a flush handed to a capture buffer (diagSink)
1927
+ * instead of the build, keyed by the copy in that buffer. A buffer can
1928
+ * be discarded (a declined probe, a collection that succeeded) or
1929
+ * re-deferred under another declaration, so the declaration stays
1930
+ * deferred until one of these copies reaches `diags` (pushDiag commits
1931
+ * it); until then a later flush reports the diagnostics again. */
1932
+ pendingFlushes = new Map();
1798
1933
  /** The build's target platform ("win32" | "darwin" | "linux" | ...):
1799
1934
  * selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
1800
1935
  * builtinModuleConstOf in surfaces.ts). */
@@ -1973,6 +2108,12 @@ export class Lowerer {
1973
2108
  this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
1974
2109
  return this.classProtocols.usesCheckedIdentity(type);
1975
2110
  }
2111
+ /** Declared class fields may store a DYN value only when its static type
2112
+ * names checked protocol identity or an owned runtime handle (a Worker
2113
+ * or MessagePort); `unknown` and `any` fields stay refused. */
2114
+ classFieldStoresDyn(type) {
2115
+ return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
2116
+ }
1976
2117
  /** The nominal identity of a checker module-namespace type. Program
1977
2118
  * modules use their normalized absolute source name; supported builtins
1978
2119
  * use the lowering tables' canonical bare name. */
@@ -3043,12 +3184,76 @@ export class Lowerer {
3043
3184
  }
3044
3185
  return false;
3045
3186
  };
3187
+ // `parent!` passed to a call or bound to a name forwards the value
3188
+ // without reading it: a callee that never dereferences it observes
3189
+ // undefined, as in JavaScript. Only a class value with one undefined
3190
+ // arm qualifies; element reads keep the established bounds trap.
3191
+ const forwardedAssertion = (node) => {
3192
+ let e = node;
3193
+ while (ts.isParenthesizedExpression(e))
3194
+ e = e.expression;
3195
+ if (!ts.isNonNullExpression(e))
3196
+ return null;
3197
+ let operand = e.expression;
3198
+ while (ts.isParenthesizedExpression(operand))
3199
+ operand = operand.expression;
3200
+ if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
3201
+ return null;
3202
+ // A guard that already proves the binding present makes the
3203
+ // assertion true at runtime: nothing is forwarded as undefined.
3204
+ if (ts.isIdentifier(operand)) {
3205
+ const symbol = symbolOf(operand);
3206
+ if (symbol && provenPresent(operand, symbol))
3207
+ return null;
3208
+ }
3209
+ const stored = this.mapTypeOf(this.typeOf(e.expression));
3210
+ const asserted = this.mapTypeOf(this.typeOf(e));
3211
+ if (stored?.kind !== "union" || asserted?.kind !== "object")
3212
+ return null;
3213
+ const arms = this.unions.get(stored.unionId)?.arms;
3214
+ if (arms?.length !== 2 ||
3215
+ this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
3216
+ !arms.some((arm) => typeEquals(arm, asserted)))
3217
+ return null;
3218
+ return e;
3219
+ };
3220
+ // `decode(xs[i]!)`: a missing element is passed as undefined in
3221
+ // JavaScript, and the callee may never read it.
3222
+ const forwardedElementAssertion = (node) => {
3223
+ let e = node;
3224
+ while (ts.isParenthesizedExpression(e))
3225
+ e = e.expression;
3226
+ if (!ts.isNonNullExpression(e))
3227
+ return null;
3228
+ let read = e.expression;
3229
+ while (ts.isParenthesizedExpression(read))
3230
+ read = read.expression;
3231
+ if (!ts.isElementAccessExpression(read))
3232
+ return null;
3233
+ if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
3234
+ return null;
3235
+ // An in-bounds read of an array that never holds undefined or holes
3236
+ // always produces a present element.
3237
+ if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
3238
+ return null;
3239
+ const element = this.mapTypeOf(this.typeOf(e));
3240
+ if (!element ||
3241
+ element.kind === "void" ||
3242
+ element.kind === "dyn" ||
3243
+ element.kind === "jsval" ||
3244
+ element.kind === "generator")
3245
+ return null;
3246
+ return e;
3247
+ };
3046
3248
  const isIndexedRead = (node) => {
3047
3249
  const e = peel(node);
3048
3250
  if (!ts.isElementAccessExpression(e))
3049
3251
  return false;
3050
3252
  const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
3051
- return kind === "array" || kind === "string";
3253
+ if (kind === "string")
3254
+ return !this.stringIndexBounds.inBounds(e);
3255
+ // Typed-array reads answer undefined for an invalid index too.
3256
+ return kind === "array" || kind === "bytes";
3052
3257
  };
3053
3258
  const isDynamicObjectEntryRead = (node) => {
3054
3259
  const read = peel(node);
@@ -3076,6 +3281,37 @@ export class Lowerer {
3076
3281
  return this.withUndefinedArm(t);
3077
3282
  };
3078
3283
  const fieldName = (node) => node.name.text;
3284
+ // The precision facts below ask about nodes ordinary lowering may never
3285
+ // query. A checker panic on one of them answers null, and each caller
3286
+ // falls back to its conservative fact.
3287
+ const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
3288
+ const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
3289
+ // Presence proofs only hold for a binding that keeps its one value for
3290
+ // its whole lifetime.
3291
+ const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
3292
+ const presenceProofs = new Map();
3293
+ const provenPresent = (use, symbol) => {
3294
+ if (reassigned.has(symbol))
3295
+ return false;
3296
+ const cached = presenceProofs.get(use);
3297
+ if (cached !== undefined)
3298
+ return cached;
3299
+ const proven = presentAtUse(use, {
3300
+ sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
3301
+ isUndefined: (node) => node.text === "undefined" &&
3302
+ ((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
3303
+ });
3304
+ presenceProofs.set(use, proven);
3305
+ return proven;
3306
+ };
3307
+ // Bindings of a for-of loop over an array that can hold holes or
3308
+ // undefined. Lowering keeps such a binding's runtime union, so storing
3309
+ // it into another array can store undefined.
3310
+ const iterationSymbols = new Set();
3311
+ // The subset whose iterated array's own facts show a hole or undefined.
3312
+ // Such a binding reads `T | undefined`, so a typed parameter, local or
3313
+ // field it flows into widens like any other missing element read.
3314
+ const holeyIterationSymbols = new Set();
3079
3315
  const mayBeOptional = (node) => {
3080
3316
  // `xs[i]!` is the explicit proven-present form. Its array read keeps
3081
3317
  // the established dense bounds trap and must not promote the enclosing
@@ -3103,7 +3339,9 @@ export class Lowerer {
3103
3339
  return true;
3104
3340
  if (ts.isIdentifier(e)) {
3105
3341
  const symbol = symbolOf(e);
3106
- return symbol !== null && optionalSymbols.has(symbol);
3342
+ return (symbol !== null &&
3343
+ (optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
3344
+ !provenPresent(e, symbol));
3107
3345
  }
3108
3346
  if (ts.isPropertyAccessExpression(e)) {
3109
3347
  const symbol = symbolOf(e.name);
@@ -3169,7 +3407,9 @@ export class Lowerer {
3169
3407
  if (t?.kind !== "string")
3170
3408
  return false;
3171
3409
  const recv = this.mapTypeOf(this.typeOf(p.expression));
3172
- return recv?.kind === "string" || (recv?.kind === "array" && recv.elem.kind === "string");
3410
+ if (recv?.kind === "string")
3411
+ return !this.stringIndexBounds.inBounds(p);
3412
+ return recv?.kind === "array" && recv.elem.kind === "string";
3173
3413
  };
3174
3414
  const primitive = (part) => {
3175
3415
  const t = this.mapTypeOf(this.typeOf(part));
@@ -3196,6 +3436,43 @@ export class Lowerer {
3196
3436
  optionalFields.set(symbol, set);
3197
3437
  return set.size !== before;
3198
3438
  };
3439
+ // Runtime-optional record fields travel with the record value: a copy,
3440
+ // a spread or a parameter receives the source's optional fields.
3441
+ const fieldsOfValue = (node) => {
3442
+ const e = peel(node);
3443
+ if (!ts.isIdentifier(e))
3444
+ return undefined;
3445
+ const symbol = symbolOf(e);
3446
+ return symbol === null ? undefined : optionalFields.get(symbol);
3447
+ };
3448
+ const noteFieldsFrom = (symbol, source) => {
3449
+ const fields = fieldsOfValue(source);
3450
+ if (fields === undefined)
3451
+ return false;
3452
+ let changed = false;
3453
+ for (const field of fields)
3454
+ if (noteFieldSymbol(symbol, field))
3455
+ changed = true;
3456
+ return changed;
3457
+ };
3458
+ /** Optional fields an object literal takes from its spreads, minus
3459
+ * the properties a later member writes explicitly. */
3460
+ const spreadFieldsOf = (object) => {
3461
+ const out = new Set();
3462
+ for (const prop of object.properties) {
3463
+ if (ts.isSpreadAssignment(prop)) {
3464
+ const fields = fieldsOfValue(prop.expression);
3465
+ if (fields !== undefined)
3466
+ for (const field of fields)
3467
+ out.add(field);
3468
+ }
3469
+ else if (prop.name !== undefined &&
3470
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
3471
+ out.delete(prop.name.text);
3472
+ }
3473
+ }
3474
+ return out;
3475
+ };
3199
3476
  const scanPattern = (name, init) => {
3200
3477
  if (!ts.isArrayBindingPattern(name))
3201
3478
  return false;
@@ -3462,6 +3739,70 @@ export class Lowerer {
3462
3739
  }
3463
3740
  return changed;
3464
3741
  };
3742
+ // A reduce callback over present elements keeps the reduce helper's
3743
+ // optional element ABI for its parameter storage, so the helper still
3744
+ // splits each visit into a plain-element call. Its value is never
3745
+ // absent, so nothing it flows into is widened.
3746
+ const retypeInlineCallback = (callback, parameterIndices) => {
3747
+ const fn = peel(callback);
3748
+ if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
3749
+ return false;
3750
+ let changed = false;
3751
+ for (const parameterIndex of parameterIndices) {
3752
+ const parameter = fn.parameters[parameterIndex];
3753
+ if (!parameter)
3754
+ continue;
3755
+ const checkerType = this.typeOf(parameter.name);
3756
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3757
+ continue;
3758
+ const current = this.mapTypeOf(checkerType);
3759
+ if (!current)
3760
+ continue;
3761
+ const widened = this.runtimeOptionalType(current);
3762
+ if (!ts.isIdentifier(parameter.name)) {
3763
+ const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
3764
+ this.runtimeOptionalPatternTypes.set(parameter.name, widened);
3765
+ if (!previous || !typeEquals(previous, widened))
3766
+ changed = true;
3767
+ continue;
3768
+ }
3769
+ const symbol = symbolOf(parameter.name);
3770
+ if (!symbol)
3771
+ continue;
3772
+ const previous = this.runtimeOptionalBindingTypes.get(symbol);
3773
+ this.runtimeOptionalBindingTypes.set(symbol, widened);
3774
+ if (!previous || !typeEquals(previous, widened))
3775
+ changed = true;
3776
+ }
3777
+ return changed;
3778
+ };
3779
+ // A present-element helper passes the stored element itself, so the
3780
+ // callback must accept that exact value through a mechanical adapter.
3781
+ // A parameter spelled as a structural supertype (`(r: { pos: number })`
3782
+ // over a wider record) only converts through the optional element
3783
+ // ABI's union retag; keep that site on the optional visit. Unannotated
3784
+ // inline parameters take the element's contextual type, and generic
3785
+ // parameters specialize later, so neither restricts the site.
3786
+ const callbackTakesPresentElement = (callback, elem, index) => {
3787
+ const fn = peel(callback);
3788
+ let param;
3789
+ if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
3790
+ const parameter = fn.parameters[index];
3791
+ if (!parameter?.type || parameter.dotDotDotToken)
3792
+ return true;
3793
+ const checkerType = this.typeOf(parameter.name);
3794
+ if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
3795
+ return true;
3796
+ param = this.mapTypeOf(checkerType);
3797
+ }
3798
+ else {
3799
+ const value = this.mapTypeOf(this.typeOf(fn));
3800
+ if (value?.kind !== "func" || value.rest === true)
3801
+ return true;
3802
+ param = value.params[index] ?? null;
3803
+ }
3804
+ return param === null || this.coercibleValue(elem, param);
3805
+ };
3465
3806
  const callbackReturnsOptional = (callback, seen = new Set()) => {
3466
3807
  const node = peel(callback);
3467
3808
  if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
@@ -3479,6 +3820,253 @@ export class Lowerer {
3479
3820
  !!decl.initializer &&
3480
3821
  callbackReturnsOptional(decl.initializer, seen));
3481
3822
  };
3823
+ // Element ABIs whose own type admits undefined (`(T | undefined)[]`,
3824
+ // or a rest array widened for an absent argument).
3825
+ const optionalElementKeys = new Set();
3826
+ // Checker answers for the fixed point's repeated element-ABI queries.
3827
+ // Only a binding's widened storage can change between passes.
3828
+ const checkerTypes = new Map();
3829
+ const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
3830
+ const arrayKeysUnsafe = (node) => {
3831
+ let checkerType = checkerTypes.get(node);
3832
+ if (checkerType === undefined) {
3833
+ checkerType = this.typeOf(node);
3834
+ checkerTypes.set(node, checkerType);
3835
+ }
3836
+ const e = peel(node);
3837
+ // A binding the prepass widened stores its widened array ABI.
3838
+ const widened = ts.isIdentifier(e)
3839
+ ? (() => {
3840
+ const symbol = symbolOf(e);
3841
+ return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
3842
+ })()
3843
+ : undefined;
3844
+ let mapped = widened ?? this.mapTypeOf(checkerType);
3845
+ if (mapped?.kind === "promise")
3846
+ mapped = mapped.inner;
3847
+ const anyElements = mapped?.kind === "array"
3848
+ ? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
3849
+ : mapped === null &&
3850
+ this.checker.isArrayType(checkerType) &&
3851
+ !this.checker.isTupleType(checkerType) &&
3852
+ ((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
3853
+ (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
3854
+ 0;
3855
+ if (anyElements) {
3856
+ // `new Array(n)` has an `any` element type; its storage takes the
3857
+ // ABI of the slot it initializes.
3858
+ const contextual = this.checker.getContextualType(node);
3859
+ const target = contextual ? this.mapTypeOf(contextual) : null;
3860
+ return target?.kind === "array" ? [typeKey(target.elem)] : [];
3861
+ }
3862
+ if (mapped === null) {
3863
+ // An array whose element type is a type parameter: each
3864
+ // instantiation picks its own element ABI.
3865
+ return this.checker.isArrayType(checkerType) ? null : [];
3866
+ }
3867
+ const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
3868
+ const keys = [];
3869
+ for (const arm of arms) {
3870
+ if (arm.kind !== "array")
3871
+ continue;
3872
+ const key = typeKey(arm.elem);
3873
+ if (arm.elem.kind === "union"
3874
+ ? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
3875
+ : isUnitType(arm.elem) || arm.elem.kind === "void")
3876
+ optionalElementKeys.add(key);
3877
+ keys.push(key);
3878
+ }
3879
+ return keys;
3880
+ };
3881
+ const ownership = new ArrayOwnership(sourceFiles, {
3882
+ symbolOf,
3883
+ declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
3884
+ typeOf: typeOrNull,
3885
+ instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
3886
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
3887
+ isArray: (node) => {
3888
+ const type = typeOrNull(node);
3889
+ return type !== null && this.checker.isArrayType(type);
3890
+ },
3891
+ fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
3892
+ dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
3893
+ });
3894
+ let dynamicViews = null;
3895
+ // Element ABIs of the native arrays that an `any` or `unknown` value is
3896
+ // converted to, including arrays nested in the converted value.
3897
+ const collectDynamicViews = () => {
3898
+ const keys = new Set();
3899
+ const dynamicValue = (node) => {
3900
+ // A binding's declared type says whether it holds dynamic data; a
3901
+ // flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
3902
+ const e = peel(node);
3903
+ const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
3904
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
3905
+ const declared = declaration &&
3906
+ (ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
3907
+ ts.isIdentifier(declaration.name)
3908
+ ? declaration.name
3909
+ : null;
3910
+ const type = typeOrNull(declared ?? node);
3911
+ if (type === null)
3912
+ return true;
3913
+ if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
3914
+ return true;
3915
+ if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
3916
+ return false;
3917
+ const element = this.checker.getTypeArguments(type)[0];
3918
+ return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
3919
+ };
3920
+ const collect = (type, depth) => {
3921
+ if (!type || depth > 4)
3922
+ return;
3923
+ if (type.kind === "array") {
3924
+ keys.add(typeKey(type.elem));
3925
+ collect(type.elem, depth + 1);
3926
+ }
3927
+ else if (type.kind === "union") {
3928
+ for (const arm of this.unions.get(type.unionId)?.arms ?? [])
3929
+ collect(arm, depth + 1);
3930
+ }
3931
+ else if (type.kind === "record") {
3932
+ for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
3933
+ collect(field.type, depth + 1);
3934
+ }
3935
+ else if (type.kind === "promise")
3936
+ collect(type.inner, depth + 1);
3937
+ };
3938
+ const target = (type) => {
3939
+ // An unanswerable target may be any array.
3940
+ if (type === null)
3941
+ keys.add("*");
3942
+ else if (type)
3943
+ collect(this.mapTypeOf(type), 0);
3944
+ };
3945
+ const visit = (node) => {
3946
+ if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
3947
+ target(typeOrNull(node));
3948
+ else if (ts.isVariableDeclaration(node) &&
3949
+ node.type &&
3950
+ node.initializer &&
3951
+ dynamicValue(node.initializer))
3952
+ target(typeOrNull(node.name));
3953
+ else if (ts.isBinaryExpression(node) &&
3954
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
3955
+ dynamicValue(node.right))
3956
+ target(typeOrNull(node.left));
3957
+ else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
3958
+ target(contextualOrNull(node.expression));
3959
+ else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
3960
+ for (const arg of node.arguments ?? [])
3961
+ if (!ts.isSpreadElement(arg) && dynamicValue(arg))
3962
+ target(contextualOrNull(arg));
3963
+ }
3964
+ else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
3965
+ target(contextualOrNull(node.initializer));
3966
+ else if (ts.isArrayLiteralExpression(node)) {
3967
+ for (const element of node.elements)
3968
+ if (!ts.isSpreadElement(element) &&
3969
+ !ts.isOmittedExpression(element) &&
3970
+ dynamicValue(element))
3971
+ target(contextualOrNull(element));
3972
+ }
3973
+ else if (ts.isArrowFunction(node) &&
3974
+ !ts.isBlock(node.body) &&
3975
+ dynamicValue(node.body))
3976
+ target(contextualOrNull(node.body));
3977
+ };
3978
+ for (const sf of sourceFiles)
3979
+ ts.walkPreorder(sf, (node) => {
3980
+ // A checker panic hides this conversion's target: any array.
3981
+ if (!panicSafe(() => (visit(node), true), false))
3982
+ keys.add("*");
3983
+ return undefined;
3984
+ });
3985
+ return keys;
3986
+ };
3987
+ const elementStates = new ArrayElementStates({
3988
+ elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
3989
+ ownerOf: (node) => ownership.ownerOf(node),
3990
+ allocationOwner: (node) => ownership.allocationOwner(node),
3991
+ arrayKeys: arrayKeysOf,
3992
+ mayBeUndefined: (node) => {
3993
+ if (explicitlyNonNull(node))
3994
+ return false;
3995
+ if (mayBeOptional(node))
3996
+ return true;
3997
+ // `undefined as unknown as T` keeps the undefined value.
3998
+ for (let inner = node; ts.isParenthesizedExpression(inner) ||
3999
+ ts.isAsExpression(inner) ||
4000
+ ts.isTypeAssertion(inner) ||
4001
+ ts.isSatisfiesExpression(inner);) {
4002
+ inner = inner.expression;
4003
+ const type = typeOrNull(inner);
4004
+ if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
4005
+ return true;
4006
+ }
4007
+ const e = peel(node);
4008
+ if (!ts.isIdentifier(e))
4009
+ return false;
4010
+ const symbol = symbolOf(e);
4011
+ return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
4012
+ },
4013
+ callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
4014
+ isGlobal: (node, name) => this.isStdlibGlobal(node, name),
4015
+ isString: (node) => {
4016
+ const type = typeOrNull(node);
4017
+ return type !== null && this.mapTypeOf(type)?.kind === "string";
4018
+ },
4019
+ isArrayLikeObject: (node) => {
4020
+ if (ts.isObjectLiteralExpression(peel(node)))
4021
+ return true;
4022
+ const type = typeOrNull(node);
4023
+ const mapped = type === null ? null : this.mapTypeOf(type);
4024
+ return (mapped === null ||
4025
+ mapped.kind === "record" ||
4026
+ mapped.kind === "object" ||
4027
+ mapped.kind === "dyn" ||
4028
+ mapped.kind === "jsval" ||
4029
+ mapped.kind === "union");
4030
+ },
4031
+ restKeys: (call) => {
4032
+ const callee = peel(call.expression);
4033
+ if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
4034
+ return null;
4035
+ const symbol = callableSymbolOf(callee);
4036
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4037
+ if (!sig)
4038
+ return ts.isPropertyAccessExpression(callee) ? [] : null;
4039
+ const rest = sig.params.find((shape) => shape.mode === "rest");
4040
+ return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
4041
+ },
4042
+ markIterationBinding: (node, known) => {
4043
+ const symbol = symbolOf(node);
4044
+ if (!symbol)
4045
+ return false;
4046
+ let changed = false;
4047
+ if (!iterationSymbols.has(symbol)) {
4048
+ iterationSymbols.add(symbol);
4049
+ changed = true;
4050
+ }
4051
+ if (known && !holeyIterationSymbols.has(symbol)) {
4052
+ holeyIterationSymbols.add(symbol);
4053
+ // `for (name of xs)` assigns an existing binding, whose own
4054
+ // storage must then hold the loop's undefined too.
4055
+ if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
4056
+ optionalSymbols.add(symbol);
4057
+ changed = true;
4058
+ }
4059
+ return changed;
4060
+ },
4061
+ note: provenance
4062
+ ? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
4063
+ : null,
4064
+ });
4065
+ // Each pass revisits every array callback site; the last visit holds
4066
+ // the decision at the fixed point.
4067
+ const presentSites = new Map();
4068
+ // The same for each for-of loop over an array.
4069
+ const presentLoops = new Map();
3482
4070
  const scanSites = new Map();
3483
4071
  const sitesOf = (sf) => {
3484
4072
  const cached = scanSites.get(sf);
@@ -3488,7 +4076,14 @@ export class Lowerer {
3488
4076
  ts.walkPreorder(sf, (node) => {
3489
4077
  if (ts.isVariableDeclaration(node) ||
3490
4078
  ts.isBinaryExpression(node) ||
3491
- ts.isCallExpression(node))
4079
+ ts.isCallExpression(node) ||
4080
+ ts.isNewExpression(node) ||
4081
+ ts.isArrayLiteralExpression(node) ||
4082
+ ts.isDeleteExpression(node) ||
4083
+ ts.isForOfStatement(node) ||
4084
+ ts.isArrayBindingPattern(node) ||
4085
+ ts.isPrefixUnaryExpression(node) ||
4086
+ ts.isPostfixUnaryExpression(node))
3492
4087
  nodes.push(node);
3493
4088
  });
3494
4089
  scanSites.set(sf, nodes);
@@ -3497,6 +4092,10 @@ export class Lowerer {
3497
4092
  const scanFile = (sf) => {
3498
4093
  let changed = false;
3499
4094
  const scan = (node) => {
4095
+ if (elementStates.scan(node))
4096
+ changed = true;
4097
+ if (ts.isForOfStatement(node))
4098
+ presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
3500
4099
  if (ts.isVariableDeclaration(node)) {
3501
4100
  if (node.initializer) {
3502
4101
  const result = optionalPrimitiveResultType(node.initializer);
@@ -3524,6 +4123,16 @@ export class Lowerer {
3524
4123
  changed = true;
3525
4124
  }
3526
4125
  }
4126
+ else if (ts.isIdentifier(node.name)) {
4127
+ const assertion = forwardedAssertion(node.initializer);
4128
+ const symbol = assertion ? symbolOf(node.name) : null;
4129
+ if (assertion && symbol && !optionalSymbols.has(symbol)) {
4130
+ optionalSymbols.add(symbol);
4131
+ this.runtimeOptionalAssertions.add(assertion);
4132
+ provenance?.note("asserted-binding", node.name, node.initializer);
4133
+ changed = true;
4134
+ }
4135
+ }
3527
4136
  if (ts.isIdentifier(node.name)) {
3528
4137
  const valueSymbol = ts.isIdentifier(peel(node.initializer))
3529
4138
  ? symbolOf(peel(node.initializer))
@@ -3565,6 +4174,22 @@ export class Lowerer {
3565
4174
  }
3566
4175
  if (scanPattern(node.name, node.initializer))
3567
4176
  changed = true;
4177
+ if (ts.isIdentifier(node.name)) {
4178
+ const symbol = symbolOf(node.name);
4179
+ const init = peel(node.initializer);
4180
+ if (symbol && noteFieldsFrom(symbol, init)) {
4181
+ provenance?.note("record-copy", node.name, node.initializer);
4182
+ changed = true;
4183
+ }
4184
+ if (symbol && ts.isObjectLiteralExpression(init)) {
4185
+ for (const field of spreadFieldsOf(init)) {
4186
+ if (noteFieldSymbol(symbol, field)) {
4187
+ provenance?.note("record-spread", node.name, node.initializer);
4188
+ changed = true;
4189
+ }
4190
+ }
4191
+ }
4192
+ }
3568
4193
  if (ts.isIdentifier(node.name) &&
3569
4194
  ts.isObjectLiteralExpression(peel(node.initializer))) {
3570
4195
  const object = peel(node.initializer);
@@ -3586,6 +4211,36 @@ export class Lowerer {
3586
4211
  }
3587
4212
  }
3588
4213
  }
4214
+ if (ts.isBinaryExpression(node) &&
4215
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4216
+ ts.isIdentifier(node.left)) {
4217
+ const symbol = symbolOf(node.left);
4218
+ if (symbol && noteFieldsFrom(symbol, node.right)) {
4219
+ provenance?.note("record-copy", node.left, node.right);
4220
+ changed = true;
4221
+ }
4222
+ }
4223
+ if (ts.isBinaryExpression(node) &&
4224
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
4225
+ ts.isIdentifier(node.left)) {
4226
+ const assertion = forwardedAssertion(node.right);
4227
+ const symbol = assertion ? symbolOf(node.left) : null;
4228
+ // Only a variable's storage is promoted (see the final pass).
4229
+ const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
4230
+ if (assertion &&
4231
+ symbol &&
4232
+ declaration &&
4233
+ ts.isVariableDeclaration(declaration) &&
4234
+ ts.isIdentifier(declaration.name) &&
4235
+ !this.runtimeOptionalAssertions.has(assertion)) {
4236
+ this.runtimeOptionalAssertions.add(assertion);
4237
+ if (!optionalSymbols.has(symbol)) {
4238
+ optionalSymbols.add(symbol);
4239
+ provenance?.note("asserted-binding", node.left, node.right);
4240
+ }
4241
+ changed = true;
4242
+ }
4243
+ }
3589
4244
  if (ts.isBinaryExpression(node) &&
3590
4245
  (node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
3591
4246
  node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
@@ -3655,11 +4310,25 @@ export class Lowerer {
3655
4310
  }
3656
4311
  if (nativeArrayReceiver) {
3657
4312
  provenanceSite = node;
3658
- if (promoteHofCallback(callback, callbackIndices))
3659
- changed = true;
3660
4313
  const method = node.expression.name.text;
4314
+ // Callbacks skip holes (except the find family), so an array
4315
+ // that never holds undefined only passes present elements.
4316
+ const reduce = method === "reduce" || method === "reduceRight";
4317
+ const elementsPresent = receiver?.kind === "array" &&
4318
+ elementStates.elementsPresent(receiverNode, method) &&
4319
+ callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
4320
+ // A callback returning a runtime-absent value still needs its
4321
+ // widened return, whatever its elements.
4322
+ if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
4323
+ changed = true;
4324
+ // Element-typed callback helpers cover every method but
4325
+ // reduce, whose accumulator keeps the split visit.
4326
+ presentSites.set(node, elementsPresent && !reduce);
4327
+ if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
4328
+ changed = true;
3661
4329
  if ((method === "reduce" || method === "reduceRight") &&
3662
- (node.arguments.length < 2 || callbackReturnsOptional(callback))) {
4330
+ ((!elementsPresent && node.arguments.length < 2) ||
4331
+ callbackReturnsOptional(callback))) {
3663
4332
  if (promoteHofCallback(callback, [0, 1]))
3664
4333
  changed = true;
3665
4334
  const result = this.runtimeOptionalType(this.irTypeOf(node));
@@ -3678,8 +4347,34 @@ export class Lowerer {
3678
4347
  if (!symbol)
3679
4348
  return;
3680
4349
  const sig = signatureBySymbol.get(symbol);
3681
- if (!sig)
4350
+ if (!sig) {
4351
+ // A direct call of a function-valued const (`const show = (n:
4352
+ // number) => ...`): a possibly missing argument widens that
4353
+ // parameter, as for a callback the same value is passed as.
4354
+ if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
4355
+ return;
4356
+ const declaration = this.checker.valueDeclarationOf(symbol);
4357
+ const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
4358
+ ? peel(declaration.initializer)
4359
+ : null;
4360
+ if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
4361
+ return;
4362
+ // Optional, defaulted and rest parameters keep their own ABI.
4363
+ if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
4364
+ parameter.initializer !== undefined ||
4365
+ parameter.dotDotDotToken !== undefined))
4366
+ return;
4367
+ const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
4368
+ value.parameters[i] !== undefined &&
4369
+ !value.parameters[i].dotDotDotToken &&
4370
+ mayBeOptional(arg)
4371
+ ? [i]
4372
+ : []);
4373
+ provenanceSite = node;
4374
+ if (indices.length > 0 && promoteHofCallback(node.expression, indices))
4375
+ changed = true;
3682
4376
  return;
4377
+ }
3683
4378
  // JSDoc describes the usual value, not a runtime arity check.
3684
4379
  // A short JavaScript call still passes undefined. Widen only the
3685
4380
  // observed slots before bodies lower so both caller and callee
@@ -3736,8 +4431,57 @@ export class Lowerer {
3736
4431
  changed = true;
3737
4432
  }
3738
4433
  }
3739
- if (ts.isSpreadElement(arg) || !mayBeOptional(arg) || !shape)
4434
+ if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
4435
+ const fields = fieldsOfValue(arg);
4436
+ for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
4437
+ const parameterShape = signatureBySymbol.get(target)?.params[i];
4438
+ const parameter = functionDeclBySymbol.get(target)?.parameters[i];
4439
+ if (!parameterShape || parameterShape.type.kind !== "record")
4440
+ continue;
4441
+ if (!parameter || !ts.isIdentifier(parameter.name))
4442
+ continue;
4443
+ const bound = symbolOf(parameter.name);
4444
+ if (!bound)
4445
+ continue;
4446
+ let promoted = parameterShape.type;
4447
+ for (const field of fields) {
4448
+ const source = promoted.kind === "record"
4449
+ ? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
4450
+ : undefined;
4451
+ if (source) {
4452
+ promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
4453
+ }
4454
+ if (noteFieldSymbol(bound, field))
4455
+ changed = true;
4456
+ }
4457
+ if (!typeEquals(promoted, parameterShape.type)) {
4458
+ parameterShape.type = promoted;
4459
+ this.runtimeOptionalBindingTypes.set(bound, promoted);
4460
+ provenance?.note("record-parameter", parameter, arg);
4461
+ changed = true;
4462
+ }
4463
+ }
4464
+ }
4465
+ if (ts.isSpreadElement(arg) || !shape)
4466
+ return;
4467
+ const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
4468
+ if (!assertion && !mayBeOptional(arg))
3740
4469
  return;
4470
+ if (assertion) {
4471
+ if (i === restAt)
4472
+ return;
4473
+ // An omittable slot's ABI already holds undefined (an
4474
+ // optional or defaulted parameter applies its own default).
4475
+ if (shape.mode === "omittable") {
4476
+ if (shape.type.kind === "union" &&
4477
+ this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4478
+ this.runtimeOptionalAssertions.add(assertion);
4479
+ return;
4480
+ }
4481
+ if (shape.mode !== "required")
4482
+ return;
4483
+ this.runtimeOptionalAssertions.add(assertion);
4484
+ }
3741
4485
  if (i === restAt) {
3742
4486
  // A rest binding always receives an array. An unchecked
3743
4487
  // argument can make its elements undefined, not the pack.
@@ -3862,6 +4606,12 @@ export class Lowerer {
3862
4606
  }
3863
4607
  }
3864
4608
  }
4609
+ for (const [site, present] of presentSites)
4610
+ if (present)
4611
+ this.presentElementCalls.add(site);
4612
+ for (const [loop, present] of presentLoops)
4613
+ if (present)
4614
+ this.presentElementLoops.add(loop);
3865
4615
  const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
3866
4616
  for (const [symbol, field] of staticFieldsBySymbol) {
3867
4617
  if (!optionalSymbols.has(symbol))
@@ -4008,6 +4758,35 @@ export class Lowerer {
4008
4758
  }
4009
4759
  });
4010
4760
  }
4761
+ for (const symbol of optionalSymbols)
4762
+ this.runtimeOptionalAssignedSymbols.add(symbol);
4763
+ // `decode(xs[i]!)` into a parameter whose final ABI already holds
4764
+ // undefined: the assertion has no runtime effect, so a missing element
4765
+ // reaches the callee as undefined instead of trapping at the read.
4766
+ for (const sf of sourceFiles) {
4767
+ for (const node of sitesOf(sf)) {
4768
+ if (!ts.isCallExpression(node))
4769
+ continue;
4770
+ if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
4771
+ continue;
4772
+ const symbol = callableSymbolOf(node.expression);
4773
+ const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
4774
+ if (!sig || node.arguments.some(ts.isSpreadElement))
4775
+ continue;
4776
+ node.arguments.forEach((arg, i) => {
4777
+ let e = arg;
4778
+ while (ts.isParenthesizedExpression(e))
4779
+ e = e.expression;
4780
+ if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
4781
+ return;
4782
+ const shape = sig.params[i];
4783
+ if (shape?.mode !== "required" && shape?.mode !== "omittable")
4784
+ return;
4785
+ if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
4786
+ this.runtimeOptionalAssertions.add(e);
4787
+ });
4788
+ }
4789
+ }
4011
4790
  provenance?.flush();
4012
4791
  }
4013
4792
  run() {
@@ -4058,14 +4837,14 @@ export class Lowerer {
4058
4837
  if (fn)
4059
4838
  functions.push(fn);
4060
4839
  else if (this.countsSkips())
4061
- this.stats.functionsSkipped++;
4840
+ this.countSkippedBody(decl);
4062
4841
  }
4063
4842
  for (const decl of fp.classDecls) {
4064
4843
  const info = this.classes.get(this.classNamer(decl));
4065
4844
  if (info)
4066
4845
  functions.push(...this.lowerClassMembers(info));
4067
4846
  else if (this.countsSkips())
4068
- this.stats.functionsSkipped++;
4847
+ this.countSkippedBody(decl);
4069
4848
  }
4070
4849
  }
4071
4850
  // Each file's top-level statements form its run-once init function.
@@ -4153,10 +4932,10 @@ export class Lowerer {
4153
4932
  // Deferred collection diagnostics nothing flushed — declarations no
4154
4933
  // reference ever made relevant. They belong to the unreached group
4155
4934
  // (collection order keeps them deterministic).
4156
- for (const [symbol, diags] of this.deferredDiags) {
4935
+ for (const [symbol, diags] of [...this.deferredDiags]) {
4157
4936
  if (this.alreadyFlushed.has(symbol))
4158
4937
  continue;
4159
- for (const d of diags)
4938
+ for (const d of [...diags])
4160
4939
  this.pushDiag(d);
4161
4940
  }
4162
4941
  return {
@@ -4277,6 +5056,7 @@ export class Lowerer {
4277
5056
  ...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
4278
5057
  entry: ENTRY_NAME,
4279
5058
  ...(this.usesWorkers ? { workers: true } : {}),
5059
+ ...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
4280
5060
  ...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
4281
5061
  };
4282
5062
  if (module)
@@ -4298,41 +5078,72 @@ export class Lowerer {
4298
5078
  * registered ClassInfo — the shape of a JS class whose collection fenced.
4299
5079
  * Used by run()'s global pruning; shapes/unions recurse with a seen-set
4300
5080
  * (interned ids can nest). */
4301
- typeNamesUnregisteredClass(t, seen = new Set()) {
5081
+ typeNamesUnregisteredClass(t) {
5082
+ return this.unregisteredClassOf(t, new Set()) !== null;
5083
+ }
5084
+ /** The first class name typeNamesUnregisteredClass finds in `t`. */
5085
+ unregisteredClassOf(t, seen) {
5086
+ const first = (types) => {
5087
+ for (const type of types) {
5088
+ const name = this.unregisteredClassOf(type, seen);
5089
+ if (name !== null)
5090
+ return name;
5091
+ }
5092
+ return null;
5093
+ };
4302
5094
  switch (t.kind) {
4303
5095
  case "object":
4304
- return !this.classes.has(t.className);
5096
+ return this.classes.has(t.className) ? null : t.className;
4305
5097
  case "array":
4306
5098
  case "set":
4307
- return this.typeNamesUnregisteredClass(t.elem, seen);
5099
+ return first([t.elem]);
4308
5100
  case "map":
4309
- return (this.typeNamesUnregisteredClass(t.key, seen) ||
4310
- this.typeNamesUnregisteredClass(t.value, seen));
5101
+ return first([t.key, t.value]);
4311
5102
  case "promise":
4312
- return this.typeNamesUnregisteredClass(t.inner, seen);
5103
+ return first([t.inner]);
4313
5104
  case "func":
4314
- return (t.params.some((p) => this.typeNamesUnregisteredClass(p, seen)) ||
4315
- this.typeNamesUnregisteredClass(t.ret, seen));
5105
+ return first([...t.params, t.ret]);
4316
5106
  case "record": {
4317
5107
  if (seen.has(t.shapeId))
4318
- return false;
5108
+ return null;
4319
5109
  seen.add(t.shapeId);
4320
5110
  const shape = this.shapes.get(t.shapeId);
4321
5111
  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));
5112
+ return null;
5113
+ return first([
5114
+ ...(shape.indexValue ? [shape.indexValue] : []),
5115
+ ...shape.fields.map((f) => f.type),
5116
+ ]);
4326
5117
  }
4327
5118
  case "union": {
4328
5119
  if (seen.has(t.unionId))
4329
- return false;
5120
+ return null;
4330
5121
  seen.add(t.unionId);
4331
5122
  const def = this.unions.get(t.unionId);
4332
- return !!def && def.arms.some((a) => this.typeNamesUnregisteredClass(a, seen));
5123
+ return def ? first(def.arms) : null;
4333
5124
  }
4334
5125
  default:
4335
- return false;
5126
+ return null;
5127
+ }
5128
+ }
5129
+ /** A declaration whose signature blocked never lowers its body, so its
5130
+ * statements would otherwise vanish from the coverage totals and the
5131
+ * percentage would overstate how much of the program compiles. Count
5132
+ * every statement the body holds as attempted and failed, in addition to
5133
+ * the skipped-function tally. */
5134
+ countSkippedBody(decl) {
5135
+ this.stats.functionsSkipped++;
5136
+ if (this.suppressStats)
5137
+ return;
5138
+ const n = countBodyStatements(decl);
5139
+ if (n === 0)
5140
+ return;
5141
+ this.stats.statementsTotal += n;
5142
+ this.stats.statementsFailed += n;
5143
+ const file = decl.getSourceFile().fileName;
5144
+ for (let i = 0; i < n; i++) {
5145
+ this.bumpFileStat(file, "total");
5146
+ this.bumpFileStat(file, "failed");
4336
5147
  }
4337
5148
  }
4338
5149
  /** Whether run() counts a signature-blocked declaration in
@@ -4868,13 +5679,18 @@ export class Lowerer {
4868
5679
  const d = this.instantiationContext
4869
5680
  ? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
4870
5681
  : diag;
5682
+ const pending = this.pendingFlushes.get(diag);
4871
5683
  // Deferred collection: the wrapper decides whether these ever report
4872
5684
  // (a reference flushes them; unreferenced declarations stay silent in
4873
5685
  // builds and report under coverage's unreached group).
4874
5686
  if (this.diagSink) {
5687
+ if (pending && d !== diag)
5688
+ this.pendingFlushes.set(d, pending);
4875
5689
  this.diagSink.push(d);
4876
5690
  return;
4877
5691
  }
5692
+ if (pending)
5693
+ this.commitFlush(pending.symbol, pending.source);
4878
5694
  // One site, one report: some declarations map a type twice (module
4879
5695
  // globals pre-register before their initializers lower) — an exact
4880
5696
  // duplicate (code + span + message) adds noise, not information.
@@ -5518,6 +6334,24 @@ export class Lowerer {
5518
6334
  this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
5519
6335
  throw new PoisonError();
5520
6336
  }
6337
+ // Instances of two unrelated classes: class identity is nominal here,
6338
+ // so a structurally compatible instance never becomes the other class.
6339
+ // An assertion (`x as Y`) names the mistake most directly.
6340
+ if (actual.kind === "object" &&
6341
+ expected.kind === "object" &&
6342
+ this.classes.has(actual.className) &&
6343
+ this.classes.has(expected.className) &&
6344
+ this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
6345
+ !this.isSubclassOf(actual.className, expected.className) &&
6346
+ !this.isSubclassOf(expected.className, actual.className)) {
6347
+ let site = node;
6348
+ while (ts.isParenthesizedExpression(site))
6349
+ site = site.expression;
6350
+ const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
6351
+ this.unsupported("SC1090", node, `${assertion ? "asserting" : "using"} a '${this.fmt(actual)}' instance as the unrelated class '${this.fmt(expected)}' (neither class extends the other, and class identity is nominal in compiled code)`, assertion
6352
+ ? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
6353
+ : `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
6354
+ }
5521
6355
  // Iterator-typed slots hold generators and built-in collection
5522
6356
  // iterators; any other iterator object (a user class) stays fenced.
5523
6357
  if (expected.kind === "generator" && actual.kind !== "generator") {
@@ -5601,6 +6435,13 @@ export class Lowerer {
5601
6435
  * else (including a DIFFERENT union) is left for requireExactShape, which
5602
6436
  * rejects union mismatches with SC2003. */
5603
6437
  coerceToExpected(expr, expected) {
6438
+ // A destination that holds undefined takes an unassigned deferred-init
6439
+ // field's slot as is (Node passes the undefined along).
6440
+ if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
6441
+ const slot = this.deferredFieldSlot(expr);
6442
+ if (slot)
6443
+ expr = slot;
6444
+ }
5604
6445
  if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
5605
6446
  return this.divergentValue(expr, expected);
5606
6447
  // Iterator-typed slots: native iterators and differently typed
@@ -5759,9 +6600,18 @@ export class Lowerer {
5759
6600
  };
5760
6601
  }
5761
6602
  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));
6603
+ // Records and arrays keep their identity as untyped values, including
6604
+ // when they arrive through a union, so `unknown` views, weak
6605
+ // collection keys and later typed reads all see the same object.
6606
+ const liveReference = (type) => {
6607
+ if (type.kind === "array")
6608
+ return true;
6609
+ const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
6610
+ return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
6611
+ };
6612
+ const liveRef = liveReference(expr.type) ||
6613
+ (expr.type.kind === "union" &&
6614
+ (this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
5765
6615
  return {
5766
6616
  kind: "dynFrom",
5767
6617
  value: expr,
@@ -5955,7 +6805,7 @@ export class Lowerer {
5955
6805
  this.armTag(expr.type.unionId, expected) >= 0) {
5956
6806
  const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
5957
6807
  if (helper) {
5958
- return { kind: "call", callee: helper, args: [expr], type: expected, loc: expr.loc };
6808
+ return this.checkedNarrowCall(helper, expr, expected, expr.loc);
5959
6809
  }
5960
6810
  }
5961
6811
  if (!typeEquals(expr.type, expected)) {
@@ -6206,6 +7056,9 @@ export class Lowerer {
6206
7056
  narrowedArmHelper(fromId, target, loc) {
6207
7057
  return narrowedArmHelper(this, fromId, target, loc);
6208
7058
  }
7059
+ checkedNarrowCall(helper, value, type, loc) {
7060
+ return checkedNarrowCall(this, helper, value, type, loc);
7061
+ }
6209
7062
  deferredReadHelper(fromId, target, loc) {
6210
7063
  return deferredReadHelper(this, fromId, target, loc);
6211
7064
  }
@@ -7024,15 +7877,47 @@ export class Lowerer {
7024
7877
  flushDeferred(symbol) {
7025
7878
  if (this.collecting)
7026
7879
  return;
7880
+ this.reportDeferred(symbol);
7881
+ }
7882
+ /** flushDeferred without the collection-pass guard: for declarations
7883
+ * whose diagnostics must report eagerly (class statements with
7884
+ * declaration-time code, extended bases — collectClassShapeDeferring). */
7885
+ reportDeferred(symbol) {
7027
7886
  const diags = this.deferredDiags.get(symbol);
7028
7887
  if (!diags)
7029
7888
  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)
7889
+ if (this.alreadyFlushed.has(symbol)) {
7890
+ this.deferredDiags.delete(symbol); // the emit pass reported these
7891
+ return;
7892
+ }
7893
+ if (this.diagSink === null) {
7894
+ this.deferredDiags.delete(symbol);
7895
+ this.flushedSymbols.add(symbol);
7896
+ for (const d of diags)
7897
+ this.pushDiag(d);
7898
+ return;
7899
+ }
7900
+ // A capture buffer is active (a probe, or another declaration's
7901
+ // deferred collection): the copies ride that buffer, and the
7902
+ // declaration stays deferred until one of them reaches the build.
7903
+ for (const d of diags) {
7904
+ this.pendingFlushes.set(d, { symbol, source: d });
7035
7905
  this.pushDiag(d);
7906
+ }
7907
+ }
7908
+ /** A flushed copy of a deferred diagnostic reached `diags`: that
7909
+ * diagnostic is reported, so it leaves the declaration's deferred list
7910
+ * (the declaration counts as flushed for the coverage remainder). */
7911
+ commitFlush(symbol, source) {
7912
+ this.flushedSymbols.add(symbol);
7913
+ const list = this.deferredDiags.get(symbol);
7914
+ if (!list)
7915
+ return;
7916
+ const index = list.indexOf(source);
7917
+ if (index >= 0)
7918
+ list.splice(index, 1);
7919
+ if (list.length === 0)
7920
+ this.deferredDiags.delete(symbol);
7036
7921
  }
7037
7922
  flushDeferredClass(className) {
7038
7923
  const symbol = this.deferredClassByName.get(className);
@@ -8111,13 +8996,7 @@ export class Lowerer {
8111
8996
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
8112
8997
  if (!helper)
8113
8998
  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));
8999
+ return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
8121
9000
  }
8122
9001
  lowerForStatement(stmt) {
8123
9002
  return lowerForStatement(this, stmt);
@@ -8362,21 +9241,21 @@ export class Lowerer {
8362
9241
  const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
8363
9242
  if (!helper)
8364
9243
  return lower();
8365
- right = {
8366
- kind: "call",
8367
- callee: helper,
8368
- args: [optionalRight.value],
8369
- type: optionalRight.present,
8370
- loc: locOf(expr.right),
8371
- };
9244
+ right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
8372
9245
  }
8373
9246
  else {
8374
9247
  right = this.lowerExpr(expr.right);
8375
9248
  }
8376
9249
  if (right.type.kind !== "classval")
8377
9250
  return lower();
8378
- const stableRight = this.declareHiddenLocal("%instanceofRight", right.type);
8379
- const rightValue = varRef(stableRight.id, right.type, locOf(expr.right));
9251
+ // A capture-free class reference is the immortal static class object:
9252
+ // re-reading it is pure, so it needs no owned temporary (whose retain
9253
+ // and release would be out-of-line calls on every test).
9254
+ const constantRight = right.kind === "classRef" && right.captures === undefined;
9255
+ const stableRight = constantRight
9256
+ ? null
9257
+ : this.declareHiddenLocal("%instanceofRight", right.type);
9258
+ const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
8380
9259
  const body = this.withExpressionOverride(expr.left, {
8381
9260
  kind: "unionNarrow",
8382
9261
  unionId: left.type.unionId,
@@ -8389,7 +9268,16 @@ export class Lowerer {
8389
9268
  kind: "seqExpr",
8390
9269
  stmts: [
8391
9270
  { kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
8392
- { kind: "varDecl", localId: stableRight.id, init: right, loc: locOf(expr.right) },
9271
+ ...(stableRight
9272
+ ? [
9273
+ {
9274
+ kind: "varDecl",
9275
+ localId: stableRight.id,
9276
+ init: right,
9277
+ loc: locOf(expr.right),
9278
+ },
9279
+ ]
9280
+ : []),
8393
9281
  ],
8394
9282
  result: {
8395
9283
  kind: "ternary",
@@ -8420,13 +9308,7 @@ export class Lowerer {
8420
9308
  const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
8421
9309
  if (!helper)
8422
9310
  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);
9311
+ return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
8430
9312
  }
8431
9313
  withExpressionOverride(node, value, lower) {
8432
9314
  const previous = this.chainRecvByNode.get(node);
@@ -8535,6 +9417,37 @@ export class Lowerer {
8535
9417
  fieldGetExpr(target, loc, blame) {
8536
9418
  return fieldGetExpr(this, target, loc, blame);
8537
9419
  }
9420
+ /** The undefined-armed slot read behind a checked deferred-init field
9421
+ * read, or null. Consumers that observe undefined itself (equality,
9422
+ * typeof, truthiness, nullish defaults) test the slot instead of the
9423
+ * checked extraction, which throws for an unassigned field. */
9424
+ deferredFieldSlot(value) {
9425
+ // The inline form: `slot is undefined ? throw : narrow(slot)`.
9426
+ if (value.kind === "ternary" &&
9427
+ value.cond.kind === "unionIsTag" &&
9428
+ value.else_.kind === "unionNarrow" &&
9429
+ value.else_.value === value.cond.value &&
9430
+ value.then.kind === "libCall" &&
9431
+ value.then.fn === "error.nodeThrow") {
9432
+ const read = value.cond.value;
9433
+ return read.kind === "fieldGet" &&
9434
+ read.type.kind === "union" &&
9435
+ this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
9436
+ ? read
9437
+ : null;
9438
+ }
9439
+ if (value.kind !== "call" || value.args.length !== 1)
9440
+ return null;
9441
+ const read = value.args[0];
9442
+ if (read.kind !== "fieldGet" || read.type.kind !== "union")
9443
+ return null;
9444
+ if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
9445
+ return null;
9446
+ return this.coercions.checkedNarrows.has(value.callee) ||
9447
+ value.callee.startsWith("%deferred.read.")
9448
+ ? read
9449
+ : null;
9450
+ }
8538
9451
  fieldSetStmt(target, value, loc, blame) {
8539
9452
  return fieldSetStmt(this, target, value, loc, blame);
8540
9453
  }
@@ -9218,8 +10131,8 @@ export class Lowerer {
9218
10131
  return lowerNumberStaticProperty(this, expr);
9219
10132
  }
9220
10133
  /* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
9221
- requireDynamicApi(feature, node) {
9222
- return requireDynamicApi(this, feature, node);
10134
+ requireDynamicApi(feature, node, hint) {
10135
+ return requireDynamicApi(this, feature, node, hint);
9223
10136
  }
9224
10137
  lowerMathProperty(expr) {
9225
10138
  return lowerMathProperty(this, expr);