@scriptc/compiler 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (588) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +7 -18
  2. package/dist/backend/cycle-analysis.d.ts +21 -9
  3. package/dist/backend/cycle-analysis.js +139 -67
  4. package/dist/backend/cycle-analysis.js.map +1 -1
  5. package/dist/backend/cycle-analysis.test.js +283 -3
  6. package/dist/backend/cycle-analysis.test.js.map +1 -1
  7. package/dist/backend/cycle-mutability.d.ts +39 -0
  8. package/dist/backend/cycle-mutability.js +205 -0
  9. package/dist/backend/cycle-mutability.js.map +1 -0
  10. package/dist/backend/link-plan-core.d.ts +2 -1
  11. package/dist/backend/link-plan-core.js +15 -3
  12. package/dist/backend/link-plan-core.js.map +1 -1
  13. package/dist/backend/link-plan.d.ts +2 -1
  14. package/dist/backend/link-plan.js.map +1 -1
  15. package/dist/backend/llvm/alloc.d.ts +43 -0
  16. package/dist/backend/llvm/alloc.js +246 -0
  17. package/dist/backend/llvm/alloc.js.map +1 -0
  18. package/dist/backend/llvm/alloc.test.d.ts +1 -0
  19. package/dist/backend/llvm/alloc.test.js +102 -0
  20. package/dist/backend/llvm/alloc.test.js.map +1 -0
  21. package/dist/backend/llvm/blocks.d.ts +9 -0
  22. package/dist/backend/llvm/blocks.js +9 -0
  23. package/dist/backend/llvm/blocks.js.map +1 -1
  24. package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
  25. package/dist/backend/llvm/borrowed-receivers.js +39 -0
  26. package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
  27. package/dist/backend/llvm/call-lifetimes.d.ts +18 -3
  28. package/dist/backend/llvm/call-lifetimes.js +54 -19
  29. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  30. package/dist/backend/llvm/call-lifetimes.test.js +178 -3
  31. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  32. package/dist/backend/llvm/checked-narrows.d.ts +24 -0
  33. package/dist/backend/llvm/checked-narrows.js +108 -0
  34. package/dist/backend/llvm/checked-narrows.js.map +1 -0
  35. package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
  36. package/dist/backend/llvm/checked-narrows.test.js +208 -0
  37. package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
  38. package/dist/backend/llvm/classes.d.ts +16 -1
  39. package/dist/backend/llvm/classes.js +60 -49
  40. package/dist/backend/llvm/classes.js.map +1 -1
  41. package/dist/backend/llvm/classes.test.js +68 -6
  42. package/dist/backend/llvm/classes.test.js.map +1 -1
  43. package/dist/backend/llvm/common.d.ts +13 -0
  44. package/dist/backend/llvm/common.js +30 -0
  45. package/dist/backend/llvm/common.js.map +1 -1
  46. package/dist/backend/llvm/constant-callbacks.d.ts +19 -3
  47. package/dist/backend/llvm/constant-callbacks.js +289 -52
  48. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  49. package/dist/backend/llvm/constant-callbacks.test.js +140 -3
  50. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
  51. package/dist/backend/llvm/debug-info.d.ts +6 -2
  52. package/dist/backend/llvm/debug-info.js +11 -1
  53. package/dist/backend/llvm/debug-info.js.map +1 -1
  54. package/dist/backend/llvm/dense-array-access.d.ts +29 -0
  55. package/dist/backend/llvm/dense-array-access.js +335 -0
  56. package/dist/backend/llvm/dense-array-access.js.map +1 -0
  57. package/dist/backend/llvm/dense-array-access.test.d.ts +1 -0
  58. package/dist/backend/llvm/dense-array-access.test.js +238 -0
  59. package/dist/backend/llvm/dense-array-access.test.js.map +1 -0
  60. package/dist/backend/llvm/dyn.d.ts +38 -0
  61. package/dist/backend/llvm/dyn.js +213 -14
  62. package/dist/backend/llvm/dyn.js.map +1 -1
  63. package/dist/backend/llvm/emitter.d.ts +308 -15
  64. package/dist/backend/llvm/emitter.js +1788 -206
  65. package/dist/backend/llvm/emitter.js.map +1 -1
  66. package/dist/backend/llvm/emitter.test.js +89 -7
  67. package/dist/backend/llvm/emitter.test.js.map +1 -1
  68. package/dist/backend/llvm/expr-async.js +62 -8
  69. package/dist/backend/llvm/expr-async.js.map +1 -1
  70. package/dist/backend/llvm/expr-async.test.d.ts +1 -0
  71. package/dist/backend/llvm/expr-async.test.js +62 -0
  72. package/dist/backend/llvm/expr-async.test.js.map +1 -0
  73. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  74. package/dist/backend/llvm/expr-bytes.js +85 -8
  75. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  76. package/dist/backend/llvm/expr-callbacks.js +29 -21
  77. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  78. package/dist/backend/llvm/expr-calls.d.ts +5 -0
  79. package/dist/backend/llvm/expr-calls.js +118 -15
  80. package/dist/backend/llvm/expr-calls.js.map +1 -1
  81. package/dist/backend/llvm/expr-containers.d.ts +7 -1
  82. package/dist/backend/llvm/expr-containers.js +246 -32
  83. package/dist/backend/llvm/expr-containers.js.map +1 -1
  84. package/dist/backend/llvm/expr-control.js +113 -8
  85. package/dist/backend/llvm/expr-control.js.map +1 -1
  86. package/dist/backend/llvm/expr-dispatch.js +2 -0
  87. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  88. package/dist/backend/llvm/expr-dynamic.js +244 -44
  89. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  90. package/dist/backend/llvm/expr-island.js +3 -8
  91. package/dist/backend/llvm/expr-island.js.map +1 -1
  92. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  93. package/dist/backend/llvm/expr-primitives.js +93 -36
  94. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  95. package/dist/backend/llvm/expr-primitives.test.js +51 -3
  96. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  97. package/dist/backend/llvm/expr-records.js +31 -17
  98. package/dist/backend/llvm/expr-records.js.map +1 -1
  99. package/dist/backend/llvm/expr-stream-bridges.js +96 -29
  100. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  101. package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
  102. package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
  103. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  104. package/dist/backend/llvm/integer-remainder.d.ts +11 -0
  105. package/dist/backend/llvm/integer-remainder.js +91 -0
  106. package/dist/backend/llvm/integer-remainder.js.map +1 -1
  107. package/dist/backend/llvm/layouts.d.ts +3 -1
  108. package/dist/backend/llvm/layouts.js +3 -2
  109. package/dist/backend/llvm/layouts.js.map +1 -1
  110. package/dist/backend/llvm/lazy-captures.d.ts +16 -0
  111. package/dist/backend/llvm/lazy-captures.js +96 -0
  112. package/dist/backend/llvm/lazy-captures.js.map +1 -0
  113. package/dist/backend/llvm/lazy-captures.test.d.ts +1 -0
  114. package/dist/backend/llvm/lazy-captures.test.js +78 -0
  115. package/dist/backend/llvm/lazy-captures.test.js.map +1 -0
  116. package/dist/backend/llvm/lib-abi.js +10 -0
  117. package/dist/backend/llvm/lib-abi.js.map +1 -1
  118. package/dist/backend/llvm/lib-filesystem.js +10 -15
  119. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  120. package/dist/backend/llvm/lib-process.js +8 -8
  121. package/dist/backend/llvm/lib-process.js.map +1 -1
  122. package/dist/backend/llvm/lib-shared.js +1 -0
  123. package/dist/backend/llvm/lib-shared.js.map +1 -1
  124. package/dist/backend/llvm/local-array-reads.d.ts +5 -1
  125. package/dist/backend/llvm/local-array-reads.js +147 -26
  126. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  127. package/dist/backend/llvm/local-array-reads.test.js +85 -4
  128. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  129. package/dist/backend/llvm/local-union-storage.d.ts +5 -1
  130. package/dist/backend/llvm/local-union-storage.js +54 -8
  131. package/dist/backend/llvm/local-union-storage.js.map +1 -1
  132. package/dist/backend/llvm/local-union-storage.test.js +94 -6
  133. package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
  134. package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
  135. package/dist/backend/llvm/map-number-lookup.js +62 -0
  136. package/dist/backend/llvm/map-number-lookup.js.map +1 -0
  137. package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
  138. package/dist/backend/llvm/map-read-lifetimes.js +24 -3
  139. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  140. package/dist/backend/llvm/map-read-lifetimes.test.js +39 -20
  141. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  142. package/dist/backend/llvm/nullable-fields.d.ts +28 -0
  143. package/dist/backend/llvm/nullable-fields.js +45 -0
  144. package/dist/backend/llvm/nullable-fields.js.map +1 -0
  145. package/dist/backend/llvm/nullable-fields.test.d.ts +1 -0
  146. package/dist/backend/llvm/nullable-fields.test.js +48 -0
  147. package/dist/backend/llvm/nullable-fields.test.js.map +1 -0
  148. package/dist/backend/llvm/nullable-unions.d.ts +68 -0
  149. package/dist/backend/llvm/nullable-unions.js +132 -0
  150. package/dist/backend/llvm/nullable-unions.js.map +1 -0
  151. package/dist/backend/llvm/publish.d.ts +58 -0
  152. package/dist/backend/llvm/publish.js +286 -0
  153. package/dist/backend/llvm/publish.js.map +1 -0
  154. package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
  155. package/dist/backend/llvm/rebound-parameters.js +72 -0
  156. package/dist/backend/llvm/rebound-parameters.js.map +1 -0
  157. package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
  158. package/dist/backend/llvm/rebound-parameters.test.js +67 -0
  159. package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
  160. package/dist/backend/llvm/reference-effects.d.ts +18 -2
  161. package/dist/backend/llvm/reference-effects.js +106 -16
  162. package/dist/backend/llvm/reference-effects.js.map +1 -1
  163. package/dist/backend/llvm/reference-effects.test.js +52 -1
  164. package/dist/backend/llvm/reference-effects.test.js.map +1 -1
  165. package/dist/backend/llvm/serialized-ir-emission.test.d.ts +1 -0
  166. package/dist/backend/llvm/serialized-ir-emission.test.js +52 -0
  167. package/dist/backend/llvm/serialized-ir-emission.test.js.map +1 -0
  168. package/dist/backend/llvm/shapes.d.ts +104 -9
  169. package/dist/backend/llvm/shapes.js +356 -46
  170. package/dist/backend/llvm/shapes.js.map +1 -1
  171. package/dist/backend/llvm/shapes.test.js +139 -5
  172. package/dist/backend/llvm/shapes.test.js.map +1 -1
  173. package/dist/backend/llvm/stack-callbacks.js +2 -3
  174. package/dist/backend/llvm/stack-callbacks.js.map +1 -1
  175. package/dist/backend/llvm/stack-captures.d.ts +3 -1
  176. package/dist/backend/llvm/stack-captures.js +5 -3
  177. package/dist/backend/llvm/stack-captures.js.map +1 -1
  178. package/dist/backend/llvm/stack-unions.test.js +43 -3
  179. package/dist/backend/llvm/stack-unions.test.js.map +1 -1
  180. package/dist/backend/llvm/string-construction.d.ts +10 -0
  181. package/dist/backend/llvm/string-construction.js +42 -0
  182. package/dist/backend/llvm/string-construction.js.map +1 -1
  183. package/dist/backend/llvm/string-construction.test.js +1 -1
  184. package/dist/backend/llvm/string-key-hash.d.ts +15 -0
  185. package/dist/backend/llvm/string-key-hash.js +76 -0
  186. package/dist/backend/llvm/string-key-hash.js.map +1 -0
  187. package/dist/backend/llvm/string-key-hash.test.d.ts +1 -0
  188. package/dist/backend/llvm/string-key-hash.test.js +45 -0
  189. package/dist/backend/llvm/string-key-hash.test.js.map +1 -0
  190. package/dist/backend/llvm/string-lifetimes.test.js +65 -15
  191. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  192. package/dist/backend/llvm/string-reads.d.ts +7 -0
  193. package/dist/backend/llvm/string-reads.js +76 -0
  194. package/dist/backend/llvm/string-reads.js.map +1 -0
  195. package/dist/backend/llvm/switch-dispatch.js +1 -1
  196. package/dist/backend/llvm/switch-dispatch.js.map +1 -1
  197. package/dist/backend/llvm/switch-dispatch.test.js +3 -3
  198. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  199. package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
  200. package/dist/backend/llvm/test-boxed-unions.js +13 -0
  201. package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
  202. package/dist/backend/llvm/union-repr.d.ts +15 -0
  203. package/dist/backend/llvm/union-repr.js +89 -0
  204. package/dist/backend/llvm/union-repr.js.map +1 -0
  205. package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
  206. package/dist/backend/llvm/virtual-borrows.js +82 -0
  207. package/dist/backend/llvm/virtual-borrows.js.map +1 -0
  208. package/dist/backend/llvm/walk-borrows.d.ts +43 -0
  209. package/dist/backend/llvm/walk-borrows.js +150 -0
  210. package/dist/backend/llvm/walk-borrows.js.map +1 -0
  211. package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
  212. package/dist/backend/llvm/walk-borrows.test.js +119 -0
  213. package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
  214. package/dist/backend/llvm/walkers.d.ts +5 -1
  215. package/dist/backend/llvm/walkers.js +24 -21
  216. package/dist/backend/llvm/walkers.js.map +1 -1
  217. package/dist/backend/may-throw.d.ts +29 -1
  218. package/dist/backend/may-throw.js +349 -3
  219. package/dist/backend/may-throw.js.map +1 -1
  220. package/dist/backend/may-throw.test.js +114 -2
  221. package/dist/backend/may-throw.test.js.map +1 -1
  222. package/dist/backend/native/cache-warm.d.ts +2 -1
  223. package/dist/backend/native/cache-warm.js.map +1 -1
  224. package/dist/backend/native/contracts.d.ts +3 -2
  225. package/dist/backend/native/contracts.js.map +1 -1
  226. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  227. package/dist/backend/native/runtime-inputs.js +2 -0
  228. package/dist/backend/native/runtime-inputs.js.map +1 -1
  229. package/dist/backend/native-codegen-core.d.ts +7 -4
  230. package/dist/backend/native-codegen-core.js +14 -9
  231. package/dist/backend/native-codegen-core.js.map +1 -1
  232. package/dist/backend/native-codegen.d.ts +9 -0
  233. package/dist/backend/native-codegen.js +53 -2
  234. package/dist/backend/native-codegen.js.map +1 -1
  235. package/dist/backend/native-codegen.test.js +64 -10
  236. package/dist/backend/native-codegen.test.js.map +1 -1
  237. package/dist/backend/native-link-info-core.d.ts +3 -2
  238. package/dist/backend/native-link-info-core.js.map +1 -1
  239. package/dist/backend/native-link-info.d.ts +2 -1
  240. package/dist/backend/native-link-info.js.map +1 -1
  241. package/dist/backend/native-link-info.test.js +2 -2
  242. package/dist/backend/native-link-info.test.js.map +1 -1
  243. package/dist/backend/native-tools.d.ts +5 -1
  244. package/dist/backend/native-tools.js +3 -1
  245. package/dist/backend/native-tools.js.map +1 -1
  246. package/dist/backend/optimization.d.ts +25 -0
  247. package/dist/backend/optimization.js +21 -0
  248. package/dist/backend/optimization.js.map +1 -0
  249. package/dist/backend/optimization.test.d.ts +1 -0
  250. package/dist/backend/optimization.test.js +24 -0
  251. package/dist/backend/optimization.test.js.map +1 -0
  252. package/dist/backend/publication.d.ts +28 -0
  253. package/dist/backend/publication.js +155 -0
  254. package/dist/backend/publication.js.map +1 -0
  255. package/dist/backend/publication.test.d.ts +1 -0
  256. package/dist/backend/publication.test.js +72 -0
  257. package/dist/backend/publication.test.js.map +1 -0
  258. package/dist/backend/runtime-abi.d.ts +2 -2
  259. package/dist/backend/runtime-abi.js +2 -2
  260. package/dist/backend/runtime-pack-core.d.ts +15 -4
  261. package/dist/backend/runtime-pack-core.js +23 -12
  262. package/dist/backend/runtime-pack-core.js.map +1 -1
  263. package/dist/backend/runtime-pack-native.d.ts +7 -3
  264. package/dist/backend/runtime-pack-native.js +0 -3
  265. package/dist/backend/runtime-pack-native.js.map +1 -1
  266. package/dist/backend/runtime-pack-targets.test.js +1 -1
  267. package/dist/backend/runtime-pack.d.ts +20 -2
  268. package/dist/backend/runtime-pack.js +23 -0
  269. package/dist/backend/runtime-pack.js.map +1 -1
  270. package/dist/backend/runtime-pack.test.js +115 -2
  271. package/dist/backend/runtime-pack.test.js.map +1 -1
  272. package/dist/backend/target-diagnostics.test.js +1 -1
  273. package/dist/backend/targets.d.ts +2 -1
  274. package/dist/backend/targets.js +1 -1
  275. package/dist/backend/targets.js.map +1 -1
  276. package/dist/cli/command.js +75 -11
  277. package/dist/cli/command.js.map +1 -1
  278. package/dist/cli/host.d.ts +2 -1
  279. package/dist/cli/usage.d.ts +4 -1
  280. package/dist/cli/usage.js +13 -3
  281. package/dist/cli/usage.js.map +1 -1
  282. package/dist/compile-types.d.ts +17 -5
  283. package/dist/coverage/report.d.ts +25 -2
  284. package/dist/coverage/report.js +161 -33
  285. package/dist/coverage/report.js.map +1 -1
  286. package/dist/coverage/verdict-cache.d.ts +63 -0
  287. package/dist/coverage/verdict-cache.js +193 -0
  288. package/dist/coverage/verdict-cache.js.map +1 -0
  289. package/dist/coverage/verdict-cache.test.d.ts +1 -0
  290. package/dist/coverage/verdict-cache.test.js +52 -0
  291. package/dist/coverage/verdict-cache.test.js.map +1 -0
  292. package/dist/coverage/verdict-semantic.d.ts +2 -0
  293. package/dist/coverage/verdict-semantic.js +24 -0
  294. package/dist/coverage/verdict-semantic.js.map +1 -0
  295. package/dist/coverage/verdict-startup.d.ts +15 -0
  296. package/dist/coverage/verdict-startup.js +66 -0
  297. package/dist/coverage/verdict-startup.js.map +1 -0
  298. package/dist/diagnostics/diagnostic.d.ts +29 -1
  299. package/dist/diagnostics/diagnostic.js +84 -12
  300. package/dist/diagnostics/diagnostic.js.map +1 -1
  301. package/dist/diagnostics/envelope.d.ts +130 -0
  302. package/dist/diagnostics/envelope.js +257 -0
  303. package/dist/diagnostics/envelope.js.map +1 -0
  304. package/dist/diagnostics/envelope.test.d.ts +1 -0
  305. package/dist/diagnostics/envelope.test.js +91 -0
  306. package/dist/diagnostics/envelope.test.js.map +1 -0
  307. package/dist/diagnostics/render.d.ts +3 -0
  308. package/dist/diagnostics/render.js +13 -2
  309. package/dist/diagnostics/render.js.map +1 -1
  310. package/dist/executable/executable-cache.d.ts +2 -2
  311. package/dist/executable/executable-cache.js +7 -4
  312. package/dist/executable/executable-cache.js.map +1 -1
  313. package/dist/executable/executable-cache.test.js +20 -0
  314. package/dist/executable/executable-cache.test.js.map +1 -1
  315. package/dist/executable/prepare.d.ts +4 -0
  316. package/dist/executable/prepare.js +7 -1
  317. package/dist/executable/prepare.js.map +1 -1
  318. package/dist/frontend/analysis.js +1 -0
  319. package/dist/frontend/analysis.js.map +1 -1
  320. package/dist/frontend/lowering/abstract-equality.js +5 -49
  321. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  322. package/dist/frontend/lowering/array-values.d.ts +30 -0
  323. package/dist/frontend/lowering/array-values.js +246 -1
  324. package/dist/frontend/lowering/array-values.js.map +1 -1
  325. package/dist/frontend/lowering/builtins/errors.js +17 -2
  326. package/dist/frontend/lowering/builtins/errors.js.map +1 -1
  327. package/dist/frontend/lowering/builtins/numbers.js +29 -1
  328. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  329. package/dist/frontend/lowering/builtins/process.d.ts +10 -17
  330. package/dist/frontend/lowering/builtins/process.js +66 -29
  331. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  332. package/dist/frontend/lowering/builtins/workers.js +4 -2
  333. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  334. package/dist/frontend/lowering/caught-value-dispatch.d.ts +11 -0
  335. package/dist/frontend/lowering/caught-value-dispatch.js +87 -0
  336. package/dist/frontend/lowering/caught-value-dispatch.js.map +1 -0
  337. package/dist/frontend/lowering/class-assertions.d.ts +12 -0
  338. package/dist/frontend/lowering/class-assertions.js +229 -5
  339. package/dist/frontend/lowering/class-assertions.js.map +1 -1
  340. package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
  341. package/dist/frontend/lowering/class-callbacks.js +18 -5
  342. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  343. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +1 -0
  344. package/dist/frontend/lowering/class-dynamic-dispatch.js +12 -6
  345. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  346. package/dist/frontend/lowering/class-method-mutations.d.ts +5 -0
  347. package/dist/frontend/lowering/class-method-mutations.js +472 -0
  348. package/dist/frontend/lowering/class-method-mutations.js.map +1 -0
  349. package/dist/frontend/lowering/class-method-values.js +25 -5
  350. package/dist/frontend/lowering/class-method-values.js.map +1 -1
  351. package/dist/frontend/lowering/class-prototypes.js +8 -2
  352. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  353. package/dist/frontend/lowering/class-unions.d.ts +16 -0
  354. package/dist/frontend/lowering/class-unions.js +127 -0
  355. package/dist/frontend/lowering/class-unions.js.map +1 -0
  356. package/dist/frontend/lowering/coercions/builders.js +8 -1
  357. package/dist/frontend/lowering/coercions/builders.js.map +1 -1
  358. package/dist/frontend/lowering/coercions/function-adapters.js +2 -0
  359. package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
  360. package/dist/frontend/lowering/coercions/identity.d.ts +7 -0
  361. package/dist/frontend/lowering/coercions/identity.js +26 -0
  362. package/dist/frontend/lowering/coercions/identity.js.map +1 -0
  363. package/dist/frontend/lowering/coercions/state.d.ts +6 -0
  364. package/dist/frontend/lowering/coercions/state.js +2 -0
  365. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  366. package/dist/frontend/lowering/coercions/structural.js +9 -6
  367. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  368. package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
  369. package/dist/frontend/lowering/coercions/unions.js +67 -2
  370. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  371. package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
  372. package/dist/frontend/lowering/collation-repertoire.js +19 -0
  373. package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
  374. package/dist/frontend/lowering/collection-narrowing.d.ts +6 -0
  375. package/dist/frontend/lowering/collection-narrowing.js +31 -0
  376. package/dist/frontend/lowering/collection-narrowing.js.map +1 -0
  377. package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
  378. package/dist/frontend/lowering/construction-escapes.js +146 -0
  379. package/dist/frontend/lowering/construction-escapes.js.map +1 -0
  380. package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
  381. package/dist/frontend/lowering/construction-escapes.test.js +150 -0
  382. package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
  383. package/dist/frontend/lowering/containers/array-callback-call.d.ts +7 -0
  384. package/dist/frontend/lowering/containers/array-callback-call.js +52 -0
  385. package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -0
  386. package/dist/frontend/lowering/containers/array-callbacks.d.ts +1 -1
  387. package/dist/frontend/lowering/containers/array-callbacks.js +416 -65
  388. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  389. package/dist/frontend/lowering/containers/array-concat.d.ts +7 -0
  390. package/dist/frontend/lowering/containers/array-concat.js +130 -0
  391. package/dist/frontend/lowering/containers/array-concat.js.map +1 -0
  392. package/dist/frontend/lowering/containers/array-construction.d.ts +2 -0
  393. package/dist/frontend/lowering/containers/array-construction.js +110 -5
  394. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  395. package/dist/frontend/lowering/containers/array-methods.d.ts +1 -11
  396. package/dist/frontend/lowering/containers/array-methods.js +274 -144
  397. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  398. package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
  399. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  400. package/dist/frontend/lowering/containers/collection-methods.js +10 -1
  401. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  402. package/dist/frontend/lowering/containers/indexed-objects.js +56 -70
  403. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  404. package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
  405. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  406. package/dist/frontend/lowering/cycle-initialization.d.ts +46 -0
  407. package/dist/frontend/lowering/cycle-initialization.js +303 -0
  408. package/dist/frontend/lowering/cycle-initialization.js.map +1 -0
  409. package/dist/frontend/lowering/divergences.d.ts +9 -0
  410. package/dist/frontend/lowering/divergences.js +630 -0
  411. package/dist/frontend/lowering/divergences.js.map +1 -0
  412. package/dist/frontend/lowering/expressions/evaluation-safety.js +4 -0
  413. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
  414. package/dist/frontend/lowering/expressions/object-literals.js +178 -34
  415. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  416. package/dist/frontend/lowering/expressions/union-field-write.d.ts +4 -4
  417. package/dist/frontend/lowering/expressions/union-field-write.js +54 -44
  418. package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
  419. package/dist/frontend/lowering/iterator-adapters.d.ts +20 -0
  420. package/dist/frontend/lowering/iterator-adapters.js +605 -0
  421. package/dist/frontend/lowering/iterator-adapters.js.map +1 -0
  422. package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
  423. package/dist/frontend/lowering/lower-array-sort.js +44 -5
  424. package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
  425. package/dist/frontend/lowering/lower-array-sort.test.d.ts +1 -0
  426. package/dist/frontend/lowering/lower-array-sort.test.js +31 -0
  427. package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -0
  428. package/dist/frontend/lowering/lower-assert.js +7 -0
  429. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  430. package/dist/frontend/lowering/lower-builtin-values.js +1 -0
  431. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  432. package/dist/frontend/lowering/lower-calls.d.ts +8 -3
  433. package/dist/frontend/lowering/lower-calls.js +388 -278
  434. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  435. package/dist/frontend/lowering/lower-classes.d.ts +6 -0
  436. package/dist/frontend/lowering/lower-classes.js +117 -65
  437. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  438. package/dist/frontend/lowering/lower-exprs.d.ts +22 -2
  439. package/dist/frontend/lowering/lower-exprs.js +811 -254
  440. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  441. package/dist/frontend/lowering/lower-functions.js +32 -19
  442. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  443. package/dist/frontend/lowering/lower-generators.d.ts +19 -13
  444. package/dist/frontend/lowering/lower-generators.js +67 -44
  445. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  446. package/dist/frontend/lowering/lower-inspect.js +29 -4
  447. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  448. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  449. package/dist/frontend/lowering/lower-island.js +24 -7
  450. package/dist/frontend/lowering/lower-island.js.map +1 -1
  451. package/dist/frontend/lowering/lower-modules.js +100 -3
  452. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  453. package/dist/frontend/lowering/lower-stmts.js +82 -64
  454. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  455. package/dist/frontend/lowering/lowerer.d.ts +115 -2
  456. package/dist/frontend/lowering/lowerer.js +1266 -88
  457. package/dist/frontend/lowering/lowerer.js.map +1 -1
  458. package/dist/frontend/lowering/native-ffi.js +3 -1
  459. package/dist/frontend/lowering/native-ffi.js.map +1 -1
  460. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
  461. package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
  462. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
  463. package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
  464. package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
  465. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
  466. package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
  467. package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
  468. package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
  469. package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
  470. package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
  471. package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
  472. package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
  473. package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
  474. package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
  475. package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
  476. package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
  477. package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
  478. package/dist/frontend/lowering/runtime-optional-provenance.d.ts +16 -0
  479. package/dist/frontend/lowering/runtime-optional-provenance.js +41 -0
  480. package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -0
  481. package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
  482. package/dist/frontend/lowering/strict-equality.d.ts +9 -0
  483. package/dist/frontend/lowering/strict-equality.js +116 -0
  484. package/dist/frontend/lowering/strict-equality.js.map +1 -0
  485. package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
  486. package/dist/frontend/lowering/string-index-bounds.js +537 -0
  487. package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
  488. package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
  489. package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
  490. package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
  491. package/dist/frontend/lowering/surfaces.js +3 -0
  492. package/dist/frontend/lowering/surfaces.js.map +1 -1
  493. package/dist/frontend/lowering/union-comparison.d.ts +5 -0
  494. package/dist/frontend/lowering/union-comparison.js +47 -0
  495. package/dist/frontend/lowering/union-comparison.js.map +1 -0
  496. package/dist/frontend/lowering/union-narrow.d.ts +2 -0
  497. package/dist/frontend/lowering/union-narrow.js +10 -6
  498. package/dist/frontend/lowering/union-narrow.js.map +1 -1
  499. package/dist/frontend/program-analysis.d.ts +10 -0
  500. package/dist/frontend/program-analysis.js +7 -0
  501. package/dist/frontend/program-analysis.js.map +1 -1
  502. package/dist/frontend/program.d.ts +63 -2
  503. package/dist/frontend/program.js +1038 -61
  504. package/dist/frontend/program.js.map +1 -1
  505. package/dist/frontend/program.test.js +122 -6
  506. package/dist/frontend/program.test.js.map +1 -1
  507. package/dist/frontend/resolve.d.ts +6 -0
  508. package/dist/frontend/resolve.js +14 -0
  509. package/dist/frontend/resolve.js.map +1 -1
  510. package/dist/frontend/threads-import.d.ts +5 -0
  511. package/dist/frontend/threads-import.js +21 -0
  512. package/dist/frontend/threads-import.js.map +1 -0
  513. package/dist/frontend/type-mapper.d.ts +4 -1
  514. package/dist/frontend/type-mapper.js +161 -20
  515. package/dist/frontend/type-mapper.js.map +1 -1
  516. package/dist/frontend/type-mapper.test.js +15 -3
  517. package/dist/frontend/type-mapper.test.js.map +1 -1
  518. package/dist/frontend/worker-target.d.ts +4 -0
  519. package/dist/frontend/worker-target.js +8 -1
  520. package/dist/frontend/worker-target.js.map +1 -1
  521. package/dist/index.d.ts +6 -1
  522. package/dist/index.js +70 -16
  523. package/dist/index.js.map +1 -1
  524. package/dist/ir/analysis.d.ts +3 -1
  525. package/dist/ir/analysis.js +4 -0
  526. package/dist/ir/analysis.js.map +1 -1
  527. package/dist/ir/build.d.ts +5 -0
  528. package/dist/ir/build.js +7 -0
  529. package/dist/ir/build.js.map +1 -1
  530. package/dist/ir/builtin-signatures.d.ts +1 -1
  531. package/dist/ir/builtin-signatures.js +5 -3
  532. package/dist/ir/builtin-signatures.js.map +1 -1
  533. package/dist/ir/constant-tables.test.js +1 -1
  534. package/dist/ir/initializer-bindings.test.js +1 -1
  535. package/dist/ir/int32-slots.d.ts +72 -0
  536. package/dist/ir/int32-slots.js +837 -0
  537. package/dist/ir/int32-slots.js.map +1 -0
  538. package/dist/ir/int32-slots.test.d.ts +1 -0
  539. package/dist/ir/int32-slots.test.js +222 -0
  540. package/dist/ir/int32-slots.test.js.map +1 -0
  541. package/dist/ir/integer-ranges.d.ts +19 -3
  542. package/dist/ir/integer-ranges.js +168 -23
  543. package/dist/ir/integer-ranges.js.map +1 -1
  544. package/dist/ir/integer-ranges.test.js +74 -2
  545. package/dist/ir/integer-ranges.test.js.map +1 -1
  546. package/dist/ir/ir.d.ts +101 -20
  547. package/dist/ir/ir.js +20 -6
  548. package/dist/ir/ir.js.map +1 -1
  549. package/dist/ir/scalar-records.test.js +1 -1
  550. package/dist/ir/serialize.d.ts +1 -1
  551. package/dist/ir/serialize.js +3 -3
  552. package/dist/ir/serialize.js.map +1 -1
  553. package/dist/ir/traverse.js +8 -0
  554. package/dist/ir/traverse.js.map +1 -1
  555. package/dist/ir/validate.js +139 -22
  556. package/dist/ir/validate.js.map +1 -1
  557. package/dist/ir/validate.test.js +308 -8
  558. package/dist/ir/validate.test.js.map +1 -1
  559. package/dist/library/fence-eval.test.js +1 -1
  560. package/dist/library/int-infer.test.js +4 -4
  561. package/dist/library/library-cache.test.js +5 -5
  562. package/dist/library/library-profile.d.ts +2 -1
  563. package/dist/library/library-profile.js +4 -3
  564. package/dist/library/library-profile.js.map +1 -1
  565. package/dist/native/cache.d.ts +3 -0
  566. package/dist/native/cache.js +49 -1
  567. package/dist/native/cache.js.map +1 -1
  568. package/dist/native/cache.test.js +21 -1
  569. package/dist/native/cache.test.js.map +1 -1
  570. package/dist/native/cli.js +1 -1
  571. package/dist/native/cli.js.map +1 -1
  572. package/dist/native/compiler.d.ts +5 -0
  573. package/dist/native/compiler.js +51 -4
  574. package/dist/native/compiler.js.map +1 -1
  575. package/dist/native/compiler.test.js +2 -2
  576. package/dist/native/library.d.ts +2 -1
  577. package/dist/native/library.js.map +1 -1
  578. package/dist/native/prepare.d.ts +4 -0
  579. package/dist/native/prepare.js +28 -3
  580. package/dist/native/prepare.js.map +1 -1
  581. package/dist/native/sanitizer.d.ts +3 -2
  582. package/dist/native/sanitizer.js +3 -13
  583. package/dist/native/sanitizer.js.map +1 -1
  584. package/dist/startup-cache.d.ts +2 -0
  585. package/dist/startup-cache.js +2 -0
  586. package/dist/startup-cache.js.map +1 -1
  587. package/package.json +20 -20
  588. package/surface-manifest.json +8 -2
@@ -1,5 +1,9 @@
1
+ import { generatorDrain } from "./iterator-adapters.js";
2
+ import { lowerUnionEquality, tagEqualityMayMissAlias } from "./strict-equality.js";
3
+ import { isOptionalProcessStreamProperty } from "./builtins/process.js";
1
4
  import { lowerWorkerMetadata } from "./builtins/workers.js";
2
- import { dynUndefinedExpr, nodeThrowExpr, countedFor, numLit, strLit, varRef, } from "../../ir/build.js";
5
+ import { threadsImportOf } from "../threads-import.js";
6
+ import { dynUndefinedExpr, nodeThrowExpr, numLit, strLit, varRef } from "../../ir/build.js";
3
7
  import { InternalCompilerError } from "../../errors.js";
4
8
  import { SYMBOL_T } from "../../ir/ir.js";
5
9
  import { literalValues } from "../literal-values.js";
@@ -11,9 +15,12 @@ import { literalUnionArm } from "../union-discriminants.js";
11
15
  * ToBoolean/ToString coercion helpers, and field/element reads and writes
12
16
  * (FieldTarget). */
13
17
  import * as ts from "../ts7/adapter.js";
18
+ import { constituentTypes } from "../ts7/checker.js";
14
19
  import { dirname } from "node:path";
15
20
  import * as posix from "node:path/posix";
16
- import { checkedClassAssertion } from "./class-assertions.js";
21
+ import { checkedClassAssertion, checkedClassUnionAssertion } from "./class-assertions.js";
22
+ import { narrowClassUnion, narrowStoredClassValue } from "./class-unions.js";
23
+ import { lowerUnionFieldWrite } from "./expressions/union-field-write.js";
17
24
  import { captureContextArguments } from "./function-context.js";
18
25
  import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
19
26
  import { wasiGuestPath } from "../../wasi-paths.js";
@@ -23,9 +30,8 @@ import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstO
23
30
  import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag, } from "../../diagnostics/diagnostic.js";
24
31
  import { PoisonError, dynFallbackType, jsFuncNameOf, neverTaintedJsType, own } from "./lowerer.js";
25
32
  import { lowerCollectionSpread } from "./containers/collection-methods.js";
26
- import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, tryLowerNumericIndexRead, } from "./containers/array-methods.js";
33
+ import { arrayValueRead, arrayValueStore, arrayValueType, lowerSafeIndexRead, tryLowerIndexTruthiness, tryLowerNumericIndexRead, lowerSafeBytesRead, } from "./array-values.js";
27
34
  import { strCharsCall } from "./containers/array-construction.js";
28
- import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
29
35
  import { lowerOptionalStringIndex } from "./string-index.js";
30
36
  import { tryLowerIndexedComparison } from "./indexed-comparison.js";
31
37
  import { npmStaticPackageOfPath } from "../npm-static.js";
@@ -98,8 +104,8 @@ export function templateRawTextOf(node) {
98
104
  const LOWER_EXPR_MAX_DEPTH = 200;
99
105
  let lowerExprDepth = 0;
100
106
  /** True when a property access names an ABSTRACT property declaration
101
- * (`abstract p: number` — a PropertyDeclaration, not an accessor): the
102
- * named-fence test for reads/writes through abstract-typed receivers. */
107
+ * (`abstract p: number` — a PropertyDeclaration, not an accessor).
108
+ * These uses need concrete property dispatch because the base has no slot. */
103
109
  export function abstractPropertyDeclOf(lowerer, expr) {
104
110
  const sym = lowerer.checker.getSymbolAtLocation(expr.name);
105
111
  if (!sym)
@@ -161,7 +167,10 @@ export function lowerExpr(lowerer, expr) {
161
167
  }
162
168
  lowerExprDepth++;
163
169
  try {
164
- return lowerExprInner(lowerer, expr);
170
+ const lowered = lowerExprInner(lowerer, expr);
171
+ if (lowered.type.kind === "void" && (lowerer.typeOf(expr).flags & ts.TypeFlags.Never) !== 0)
172
+ lowerer.neverValued.add(lowered);
173
+ return lowered;
165
174
  }
166
175
  finally {
167
176
  lowerExprDepth--;
@@ -286,16 +295,35 @@ function lowerExprInner(lowerer, expr) {
286
295
  // traps. Sub-union assertions and non-union inners keep their
287
296
  // historic erasure (the widening sites re-tag with traps already).
288
297
  if (ts.isNonNullExpression(expr)) {
298
+ // An asserted element read forwarded into a slot that holds undefined
299
+ // (see analyzeRuntimeOptionalArrayReads) reads without the bounds trap.
300
+ if (lowerer.runtimeOptionalAssertions.has(expr) &&
301
+ ts.isElementAccessExpression(expr.expression)) {
302
+ const probe = lowerAbsenceProbe(lowerer, expr.expression);
303
+ if (probe?.type.kind === "union")
304
+ return probe;
305
+ }
289
306
  const inner = lowerer.lowerExpr(expr.expression);
290
307
  if (inner.type.kind === "union") {
308
+ // `!` performs no runtime check: a missing array element stays
309
+ // undefined where its destination can hold it.
310
+ if (ts.isElementAccessExpression(expr.expression) &&
311
+ assertedElementKeepsUndefined(lowerer, expr))
312
+ return inner;
313
+ // A forwarded assertion whose destination was promoted to hold
314
+ // undefined (see analyzeRuntimeOptionalArrayReads), or an equality
315
+ // operand, keeps the value: `!` performs no runtime check.
316
+ const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
317
+ if (lowerer.runtimeOptionalAssertions.has(expr) ||
318
+ (target?.kind === "object" && isEqualityOperand(expr)))
319
+ return inner;
291
320
  const use = runtimeOptionalUseOf(expr);
292
321
  if (runtimeOptionalAssertionErases(lowerer, expr, inner, use))
293
322
  return inner;
294
- const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
295
323
  if (target && target.kind !== "union" && !typeEquals(target, inner.type)) {
296
324
  const helper = lowerer.narrowedArmHelper(inner.type.unionId, target, loc);
297
325
  if (helper) {
298
- return { kind: "call", callee: helper, args: [inner], type: target, loc };
326
+ return lowerer.checkedNarrowCall(helper, inner, target, loc);
299
327
  }
300
328
  }
301
329
  return inner;
@@ -717,7 +745,7 @@ function lowerExprInner(lowerer, expr) {
717
745
  // A known static result type fixes typeof's answer, but its producer
718
746
  // still evaluates: calls, getters, and checked reads can have effects
719
747
  // or throw. Only trivial operands may disappear.
720
- let operand = lowerer.lowerExpr(expr.expression);
748
+ let operand = deferredSlotOr(lowerer, runtimeOptionalStorageOperand(lowerer, expr.expression) ?? lowerer.lowerExpr(expr.expression));
721
749
  if (operand.type.kind === "jsval") {
722
750
  return { kind: "jsOp", op: "typeof", args: [operand], type: STRING, loc };
723
751
  }
@@ -936,13 +964,7 @@ function lowerExprInner(lowerer, expr) {
936
964
  ? lowerer.narrowedArmHelper(local.type.unionId, narrowed, loc)
937
965
  : null;
938
966
  if (helper) {
939
- return {
940
- kind: "call",
941
- callee: helper,
942
- args: [varRef(local.id, local.type, loc)],
943
- type: narrowed,
944
- loc,
945
- };
967
+ return lowerer.checkedNarrowCall(helper, varRef(local.id, local.type, loc), narrowed, loc);
946
968
  }
947
969
  }
948
970
  }
@@ -980,7 +1002,10 @@ function lowerExprInner(lowerer, expr) {
980
1002
  // that does not exist. The blocked-binding cascade names the use;
981
1003
  // without this the validator's registration check ICEs on the live
982
1004
  // reference (`@this {T}` inference — signature 07).
983
- if (lowerer.typeNamesUnregisteredClass(g.type)) {
1005
+ const unregistered = lowerer.unregisteredClassOf(g.type, new Set());
1006
+ if (unregistered !== null) {
1007
+ // The class's own rejection is the root cause the cascade names.
1008
+ lowerer.flushDeferredClass(unregistered);
984
1009
  lowerer.pushDiag(blockedBindingUseDiag(expr.text, loc));
985
1010
  throw new PoisonError();
986
1011
  }
@@ -1013,7 +1038,7 @@ function lowerExprInner(lowerer, expr) {
1013
1038
  lowerer.armTag(g.type.unionId, narrowed) >= 0) {
1014
1039
  const helper = lowerer.narrowedArmHelper(g.type.unionId, narrowed, loc);
1015
1040
  if (helper)
1016
- return { kind: "call", callee: helper, args: [ref], type: narrowed, loc };
1041
+ return lowerer.checkedNarrowCall(helper, ref, narrowed, loc);
1017
1042
  }
1018
1043
  const use = runtimeOptionalUseOf(expr);
1019
1044
  // The enclosing logical/conditional expression consumes the union
@@ -1111,6 +1136,14 @@ function lowerExprInner(lowerer, expr) {
1111
1136
  // as interned string literals; functions have no closure
1112
1137
  // representation (they lower to libCall at call sites only); members
1113
1138
  // with no lowering at all fence with the module-qualified name.
1139
+ {
1140
+ const member = threadsImportOf(lowerer.program, expr);
1141
+ // scriptc delivers published graphs by reference.
1142
+ if (member === "sharesPublishedGraphs")
1143
+ return { kind: "boolLit", value: true, type: BOOL, loc };
1144
+ if (member === "publish")
1145
+ lowerer.noLowering("publish (@scriptc/threads) as a value", expr, "call publish(value) directly");
1146
+ }
1114
1147
  {
1115
1148
  const bi = lowerer.builtinImportOf(expr);
1116
1149
  if (bi) {
@@ -3075,6 +3108,11 @@ export function maybeNarrow(lowerer, expr, node) {
3075
3108
  // same trust-the-checker contract as the union bridge below.
3076
3109
  if (expr.type.kind === "object") {
3077
3110
  const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
3111
+ if (narrowed?.kind === "union") {
3112
+ const union = narrowClassUnion(lowerer, expr, narrowed);
3113
+ if (union)
3114
+ return union;
3115
+ }
3078
3116
  if (narrowed?.kind === "object" &&
3079
3117
  narrowed.className !== expr.type.className &&
3080
3118
  lowerer.isSubclassOf(narrowed.className, expr.type.className)) {
@@ -3084,6 +3122,8 @@ export function maybeNarrow(lowerer, expr, node) {
3084
3122
  }
3085
3123
  if (expr.type.kind !== "union")
3086
3124
  return expr;
3125
+ if (ts.isPropertyAccessExpression(node) && isOptionalProcessStreamProperty(lowerer, node))
3126
+ return expr;
3087
3127
  // Represented fields and index values can include undefined even when
3088
3128
  // checker inference or lib.d.ts declares strings (RegExp captures).
3089
3129
  // Only a declaration that represents the optional arm can provide a
@@ -3228,6 +3268,9 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
3228
3268
  lowerer.unsupported("SC1090", expr, "a runtime-optional capture whose narrowed value type is not representable");
3229
3269
  }
3230
3270
  if (narrowed.kind === "union") {
3271
+ const classView = narrowStoredClassValue(lowerer, varRef(local.id, local.type, loc), narrowed);
3272
+ if (classView)
3273
+ return classView;
3231
3274
  // The checker may keep the receiver at the storage union unchanged
3232
3275
  // (`string | undefined` on a JS `.match()` call). There is no
3233
3276
  // sub-union to re-tag: preserve the value for the downstream
@@ -3254,6 +3297,13 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
3254
3297
  loc,
3255
3298
  };
3256
3299
  }
3300
+ if (narrowed.kind === "object") {
3301
+ const view = checkedClassAssertion(lowerer, varRef(local.id, local.type, loc), narrowed, loc);
3302
+ // Preserve the member-specific missing-value error on the two-arm
3303
+ // path below; mixed storage also needs to validate its concrete class.
3304
+ if (view && (lowerer.unions.get(local.type.unionId)?.arms.length ?? 0) > 2)
3305
+ return view;
3306
+ }
3257
3307
  const undefTag = lowerer.armTag(local.type.unionId, UNDEFINED_T);
3258
3308
  const def = lowerer.unions.get(local.type.unionId);
3259
3309
  if (!def || undefTag < 0)
@@ -3269,13 +3319,7 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
3269
3319
  if (!helper) {
3270
3320
  lowerer.unsupported("SC1090", expr, "a direct read from a runtime-optional capture with multiple value arms");
3271
3321
  }
3272
- return {
3273
- kind: "call",
3274
- callee: helper,
3275
- args: [varRef(local.id, local.type, loc)],
3276
- type: narrowed,
3277
- loc,
3278
- };
3322
+ return lowerer.checkedNarrowCall(helper, varRef(local.id, local.type, loc), narrowed, loc);
3279
3323
  }
3280
3324
  // A predicate can strengthen a record's fields or narrow a base class
3281
3325
  // to a subclass without changing the slot's stored value arm. Extract
@@ -3408,6 +3452,57 @@ function runtimeOptionalElementKey(expr) {
3408
3452
  return expr.text;
3409
3453
  return null;
3410
3454
  }
3455
+ /** An operand of `===`, `!==`, `==` or `!=` (through parentheses). */
3456
+ function isEqualityOperand(node) {
3457
+ let outer = node;
3458
+ while (ts.isParenthesizedExpression(outer.parent))
3459
+ outer = outer.parent;
3460
+ const parent = outer.parent;
3461
+ return (ts.isBinaryExpression(parent) &&
3462
+ (parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken ||
3463
+ parent.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
3464
+ parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsToken ||
3465
+ parent.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsToken));
3466
+ }
3467
+ /** Whether `xs[i]!` lands where an absent element's undefined is
3468
+ * representable: array storage (a literal element, a push/unshift
3469
+ * argument, an element assignment — arrays record the undefined state),
3470
+ * an equality operand, or a slot whose contextual type admits undefined.
3471
+ * TypeScript's `!` has no runtime effect, so such reads stay optional. */
3472
+ function assertedElementKeepsUndefined(lowerer, node) {
3473
+ let outer = node;
3474
+ while (ts.isParenthesizedExpression(outer.parent))
3475
+ outer = outer.parent;
3476
+ const parent = outer.parent;
3477
+ if (ts.isArrayLiteralExpression(parent))
3478
+ return true;
3479
+ // An equality test observes the missing element itself.
3480
+ if (ts.isBinaryExpression(parent) &&
3481
+ (parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken ||
3482
+ parent.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
3483
+ parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsToken ||
3484
+ parent.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsToken))
3485
+ return true;
3486
+ const isArray = (receiver) => lowerer.checker.isArrayType(lowerer.typeOf(receiver));
3487
+ if (ts.isCallExpression(parent) &&
3488
+ parent.arguments.includes(outer) &&
3489
+ ts.isPropertyAccessExpression(parent.expression) &&
3490
+ (parent.expression.name.text === "push" || parent.expression.name.text === "unshift") &&
3491
+ isArray(parent.expression.expression))
3492
+ return true;
3493
+ if (ts.isBinaryExpression(parent) &&
3494
+ parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
3495
+ parent.right === outer &&
3496
+ ts.isElementAccessExpression(parent.left) &&
3497
+ isArray(parent.left.expression))
3498
+ return true;
3499
+ const contextual = lowerer.checker.getContextualType(outer);
3500
+ if (!contextual)
3501
+ return false;
3502
+ const admits = (type) => (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Undefined)) !== 0;
3503
+ return (admits(contextual) ||
3504
+ ((contextual.flags & ts.TypeFlags.Union) !== 0 && constituentTypes(contextual).some(admits)));
3505
+ }
3411
3506
  function runtimeOptionalAssertionErases(lowerer, expr, inner, use) {
3412
3507
  if (inner.type.kind !== "union" ||
3413
3508
  !use?.optional ||
@@ -3436,6 +3531,17 @@ export function lowerUnitComparison(lowerer, left, right, negated, loc) {
3436
3531
  // Two unit literals (`undefined === undefined`): statically decided.
3437
3532
  return { kind: "boolLit", value: (other.unit === unit.unit) !== negated, type: BOOL, loc };
3438
3533
  }
3534
+ const slot = lowerer.deferredFieldSlot(other);
3535
+ if (slot)
3536
+ return lowerUnitComparison(lowerer, slot, unit, negated, loc);
3537
+ // A binding the checker narrowed past the unit can still hold it at
3538
+ // runtime when its value came from an unassigned deferred-init field;
3539
+ // the stored tag answers either way.
3540
+ if (other.kind === "unionNarrow" &&
3541
+ other.value.kind === "varRef" &&
3542
+ other.value.type.kind === "union" &&
3543
+ lowerer.armTag(other.value.type.unionId, unit.type) >= 0)
3544
+ return lowerUnitComparison(lowerer, other.value, unit, negated, loc);
3439
3545
  if (other.type.kind === "union") {
3440
3546
  const tag = lowerer.armTag(other.type.unionId, unit.type);
3441
3547
  if (tag < 0) {
@@ -3543,7 +3649,7 @@ function lowerLooseNullCompare(lowerer, expr, loc) {
3543
3649
  if (!leftIsNull && right.kind !== ts.SyntaxKind.NullKeyword)
3544
3650
  return null;
3545
3651
  const otherNode = leftIsNull ? right : left;
3546
- const other = lowerer.lowerExpr(otherNode);
3652
+ const other = lowerPresenceOperand(lowerer, otherNode);
3547
3653
  if (isUnitType(other.type)) {
3548
3654
  // `null == null`, `undefined == null`: units are mutually loose-equal.
3549
3655
  return { kind: "boolLit", value: !negated, type: BOOL, loc };
@@ -3648,13 +3754,110 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
3648
3754
  parents.push(first);
3649
3755
  first = first.left;
3650
3756
  }
3651
- let result = lowerNullishPair(lowerer, first, first === expr ? loc : locOf(first), lowerAbsenceProbe(lowerer, first.left) ?? lowerer.lowerExpr(first.left));
3757
+ let result = lowerNullishPair(lowerer, first, first === expr ? loc : locOf(first), assertedPresenceOperand(lowerer, first.left));
3652
3758
  for (let i = parents.length - 1; i >= 0; i--) {
3653
3759
  const parent = parents[i];
3654
3760
  result = lowerNullishPair(lowerer, parent, parent === expr ? loc : locOf(parent), result);
3655
3761
  }
3656
3762
  return result;
3657
3763
  }
3764
+ /** The left side of `??` (see lowerPresenceOperand). The absence probe
3765
+ * reads optional storage beneath erased assertions; a union assertion that
3766
+ * narrows a stored class arm still applies its checked class view, so
3767
+ * `(node.parent as Sub | undefined) ?? node` sees the asserted arm on both
3768
+ * sides of the default. */
3769
+ function assertedPresenceOperand(lowerer, node) {
3770
+ const probe = lowerAbsenceProbe(lowerer, node);
3771
+ if (probe === null)
3772
+ return runtimeOptionalStorageOperand(lowerer, node) ?? lowerer.lowerExpr(node);
3773
+ if (probe.type.kind !== "union" && probe.type.kind !== "object")
3774
+ return probe;
3775
+ let outer = node;
3776
+ while (ts.isParenthesizedExpression(outer))
3777
+ outer = outer.expression;
3778
+ if (!ts.isAsExpression(outer) && !ts.isTypeAssertion(outer))
3779
+ return probe;
3780
+ const target = lowerer.mapTypeOf(lowerer.checker.getTypeFromTypeNode(outer.type));
3781
+ if (target?.kind !== "union" || typeEquals(target, probe.type))
3782
+ return probe;
3783
+ return checkedClassUnionAssertion(lowerer, probe, target, locOf(outer)) ?? probe;
3784
+ }
3785
+ /** Read-modify-write updates read the member first: an unassigned
3786
+ * deferred-init receiver throws Node's member-read TypeError. */
3787
+ function checkDeferredMemberReceiver(lowerer, target, access) {
3788
+ if (target.container !== "class" ||
3789
+ !ts.isPropertyAccessExpression(access) ||
3790
+ target.obj.type.kind !== "object" ||
3791
+ lowerer.deferredFieldSlot(target.obj) === null)
3792
+ return;
3793
+ target.obj =
3794
+ lowerer.runtimeOptionalPropertyReceiver(access.expression, target.obj, target.obj.type, target.field) ?? target.obj;
3795
+ }
3796
+ /** Whether evaluating `expr` cannot store anything or run other code:
3797
+ * reads, arithmetic, tests and checked extractions (which at most throw). */
3798
+ function writeFree(expr) {
3799
+ switch (expr.kind) {
3800
+ case "numLit":
3801
+ case "strLit":
3802
+ case "boolLit":
3803
+ case "unitLit":
3804
+ case "varRef":
3805
+ return true;
3806
+ case "fieldGet":
3807
+ case "recordGet":
3808
+ return writeFree(expr.obj);
3809
+ case "bin":
3810
+ case "logical":
3811
+ return writeFree(expr.left) && writeFree(expr.right);
3812
+ case "unary":
3813
+ case "toBool":
3814
+ return writeFree(expr.operand);
3815
+ case "ternary":
3816
+ return writeFree(expr.cond) && writeFree(expr.then) && writeFree(expr.else_);
3817
+ case "unionIsTag":
3818
+ case "unionNarrow":
3819
+ case "unionWrap":
3820
+ return writeFree(expr.value);
3821
+ case "libCall":
3822
+ return expr.fn === "error.nodeThrow";
3823
+ default:
3824
+ return false;
3825
+ }
3826
+ }
3827
+ /** The TypeError of a deferred-init field read that finds the field
3828
+ * unassigned where its declared type cannot hold undefined. */
3829
+ export const DEFERRED_READ_MESSAGE = "undefined is not representable in the target union (a value narrowed or asserted past it still held it)";
3830
+ /** The undefined-armed slot behind a checked deferred-init field read, or
3831
+ * the value itself (Lowerer.deferredFieldSlot). */
3832
+ function deferredSlotOr(lowerer, value) {
3833
+ return lowerer.deferredFieldSlot(value) ?? value;
3834
+ }
3835
+ /** A plain value read that may be runtime-optional: an unchecked element
3836
+ * read, or a binding, member or call that carries one. Lowering these
3837
+ * without a destination type is the same as lowering them into one. */
3838
+ function isPlainValueRead(node) {
3839
+ while (ts.isParenthesizedExpression(node))
3840
+ node = node.expression;
3841
+ return (ts.isElementAccessExpression(node) ||
3842
+ ts.isIdentifier(node) ||
3843
+ ts.isPropertyAccessExpression(node) ||
3844
+ ts.isCallExpression(node));
3845
+ }
3846
+ /** A member access or call receiver needs the present value itself (JS
3847
+ * throws a TypeError reading a member of undefined). */
3848
+ function isMemberReceiver(node) {
3849
+ let child = node;
3850
+ let parent = node.parent;
3851
+ while (ts.isParenthesizedExpression(parent)) {
3852
+ child = parent;
3853
+ parent = parent.parent;
3854
+ }
3855
+ return (((ts.isPropertyAccessExpression(parent) ||
3856
+ ts.isElementAccessExpression(parent) ||
3857
+ ts.isCallExpression(parent)) &&
3858
+ parent.expression === child) ||
3859
+ (ts.isTaggedTemplateExpression(parent) && parent.tag === child));
3860
+ }
3658
3861
  function lowerNullishPair(lowerer, expr, loc, left) {
3659
3862
  if (left.type.kind === "dyn") {
3660
3863
  // `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
@@ -3691,7 +3894,24 @@ function lowerNullishPair(lowerer, expr, loc, left) {
3691
3894
  const fresh = rest.length === 1 ? lowerer.emptyCollectionFor(expr.right, rest[0]) : null;
3692
3895
  if (fresh)
3693
3896
  return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
3694
- const type = lowerer.irTypeOf(expr);
3897
+ let type = lowerer.irTypeOf(expr);
3898
+ // A runtime-optional read can reach the default even when declarations
3899
+ // call the left side present. Preserve a differently typed primitive
3900
+ // fallback rather than checking it against that incomplete result type.
3901
+ const rightType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
3902
+ const primitive = (t) => t.kind === "f64" || t.kind === "string" || t.kind === "bool";
3903
+ if (rightType &&
3904
+ primitive(type) &&
3905
+ primitive(rightType) &&
3906
+ !typeEquals(type, rightType) &&
3907
+ rest.every(primitive)) {
3908
+ const arms = [...rest];
3909
+ if (!arms.some((arm) => typeEquals(arm, rightType)))
3910
+ arms.push(rightType);
3911
+ arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
3912
+ type = { kind: "union", unionId: lowerer.unions.intern(arms) };
3913
+ lowerer.runtimeOptionalArithmeticTypes.set(expr, type);
3914
+ }
3695
3915
  if (type.kind === "dyn" && lowerer.dynConvertible(left.type)) {
3696
3916
  return {
3697
3917
  kind: "nullish",
@@ -3701,6 +3921,27 @@ function lowerNullishPair(lowerer, expr, loc, left) {
3701
3921
  loc,
3702
3922
  };
3703
3923
  }
3924
+ // A runtime-optional default (`name ?? names[0]`): the checker types an
3925
+ // unchecked element read as the bare element type, but an out-of-range
3926
+ // read is undefined at runtime. When the default shares the left's
3927
+ // optional union, that union is the result, so the undefined state
3928
+ // reaches the consumer (an optional return or parameter takes it as its
3929
+ // undefined arm; a plain slot still extracts the element with its
3930
+ // checked narrow).
3931
+ if (rest.length === 1 &&
3932
+ typeEquals(type, rest[0]) &&
3933
+ isPlainValueRead(expr.right) &&
3934
+ !isMemberReceiver(expr)) {
3935
+ const raw = lowerer.lowerExpr(expr.right);
3936
+ if (typeEquals(raw.type, left.type))
3937
+ return { kind: "nullish", left, right: raw, type: left.type, loc };
3938
+ // `names[0] ?? undefined`: an explicit unit default stays the left's arm.
3939
+ if (isUnitType(raw.type) && lowerer.armTag(left.type.unionId, raw.type) >= 0) {
3940
+ const right = lowerer.coerceInto(expr.right, raw, left.type);
3941
+ return { kind: "nullish", left, right, type: left.type, loc };
3942
+ }
3943
+ return { kind: "nullish", left, right: lowerer.coerceInto(expr.right, raw, type), type, loc };
3944
+ }
3704
3945
  if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
3705
3946
  const right = lowerer.lowerExprExpecting(expr.right, type);
3706
3947
  return { kind: "nullish", left, right, type, loc };
@@ -3711,6 +3952,24 @@ function lowerNullishPair(lowerer, expr, loc, left) {
3711
3952
  return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({ kind: "call", callee: helper, args: [stable], type, loc }));
3712
3953
  }
3713
3954
  }
3955
+ // A subclass value defaulted to a base-class value (`circle ?? shape`):
3956
+ // the present arm upcasts after the original nullish test, and the
3957
+ // default lowers lazily into the base-class result.
3958
+ const present = rest.length === 1 ? rest[0] : null;
3959
+ if (present?.kind === "object" &&
3960
+ type.kind === "object" &&
3961
+ present.className !== type.className &&
3962
+ lowerer.isSubclassOf(present.className, type.className)) {
3963
+ const helper = lowerer.narrowedArmHelper(left.type.unionId, present, loc);
3964
+ if (helper !== null) {
3965
+ return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({
3966
+ kind: "upcast",
3967
+ value: { kind: "call", callee: helper, args: [stable], type: present, loc },
3968
+ type,
3969
+ loc,
3970
+ }));
3971
+ }
3972
+ }
3714
3973
  // A default may change the union or widen its records. Test the
3715
3974
  // original tag before coercing: converting the whole left first can
3716
3975
  // lose nullish arms, and evaluating the default eagerly loses laziness.
@@ -3824,7 +4083,8 @@ export function lowerCondition(lowerer, expr) {
3824
4083
  };
3825
4084
  }
3826
4085
  }
3827
- return lowerer.ensureBool(lowerAbsenceProbe(lowerer, expr) ?? lowerer.lowerExpr(expr), expr);
4086
+ const value = lowerPresenceOperand(lowerer, expr);
4087
+ return (tryLowerIndexTruthiness(lowerer, value, expr, locOf(expr)) ?? lowerer.ensureBool(value, expr));
3828
4088
  }
3829
4089
  function lowerPromiseThenPresence(lowerer, access, result) {
3830
4090
  if (access.name.text !== "then")
@@ -3863,13 +4123,7 @@ function lowerPromiseThenPresence(lowerer, access, result) {
3863
4123
  const helper = lowerer.narrowedArmHelper(receiver.type.unionId, promiseType, receiver.loc);
3864
4124
  if (helper === null)
3865
4125
  return null;
3866
- receiver = {
3867
- kind: "call",
3868
- callee: helper,
3869
- args: [receiver],
3870
- type: promiseType,
3871
- loc: receiver.loc,
3872
- };
4126
+ receiver = lowerer.checkedNarrowCall(helper, receiver, promiseType, receiver.loc);
3873
4127
  }
3874
4128
  }
3875
4129
  if (receiver.type.kind === "dyn") {
@@ -4557,13 +4811,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
4557
4811
  ? lowerer.narrowedArmHelper(receiver.type.unionId, present, locOf(expr.expression))
4558
4812
  : null;
4559
4813
  receiver = helper
4560
- ? {
4561
- kind: "call",
4562
- callee: helper,
4563
- args: [receiver],
4564
- type: present,
4565
- loc: locOf(expr.expression),
4566
- }
4814
+ ? lowerer.checkedNarrowCall(helper, receiver, present, locOf(expr.expression))
4567
4815
  : lowerer.maybeNarrow(receiver, expr.expression);
4568
4816
  }
4569
4817
  // An island handle behind a typed-array .d.ts surface: the engine
@@ -4657,13 +4905,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
4657
4905
  ? lowerer.narrowedArmHelper(receiver.type.unionId, present, locOf(expr.expression))
4658
4906
  : null;
4659
4907
  receiver = helper
4660
- ? {
4661
- kind: "call",
4662
- callee: helper,
4663
- args: [receiver],
4664
- type: present,
4665
- loc: locOf(expr.expression),
4666
- }
4908
+ ? lowerer.checkedNarrowCall(helper, receiver, present, locOf(expr.expression))
4667
4909
  : lowerer.maybeNarrow(receiver, expr.expression);
4668
4910
  }
4669
4911
  // An island handle behind an array-typed .d.ts surface
@@ -4963,7 +5205,30 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
4963
5205
  }
4964
5206
  lowerer.badType(expr, tsType);
4965
5207
  }
4966
- const type = mapped;
5208
+ let type = mapped;
5209
+ // An inferred array of records takes the layout of an element literal
5210
+ // whose fields widened for missing-element reads; the other elements
5211
+ // build that layout directly. An explicit destination keeps its own.
5212
+ if (!expected && type.elem.kind === "record") {
5213
+ let variant = null;
5214
+ for (const candidate of expr.elements) {
5215
+ let x = candidate;
5216
+ while (ts.isParenthesizedExpression(x))
5217
+ x = x.expression;
5218
+ if (!ts.isObjectLiteralExpression(x))
5219
+ continue;
5220
+ const probe = tryLowerExpression(lowerer, x);
5221
+ if (!probe || !lowerer.isRuntimeOptionalRecordVariant(probe.type, type.elem))
5222
+ continue;
5223
+ if (variant !== null && !typeEquals(variant, probe.type)) {
5224
+ variant = null;
5225
+ break;
5226
+ }
5227
+ variant = probe.type;
5228
+ }
5229
+ if (variant !== null)
5230
+ type = arrayOf(variant);
5231
+ }
4967
5232
  // Keep the array payload type fixed. Optional reads are stored through
4968
5233
  // arrayValueStore, which records UNDEFINED in the state byte while the
4969
5234
  // payload remains number/string/etc.; widening the array element here
@@ -5031,6 +5296,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
5031
5296
  loc: locOf(el),
5032
5297
  };
5033
5298
  }
5299
+ // `[...it]` over a generator (or an iterator adapted into one):
5300
+ // drain it once, in order, into a fresh element array.
5301
+ if (src.type.kind === "generator") {
5302
+ const drained = generatorDrain(lowerer, src, type.elem, locOf(el));
5303
+ if (drained)
5304
+ src = drained;
5305
+ }
5034
5306
  // A native checked iterable can supply scalar elements even when
5035
5307
  // the checker inferred a typed array from its producer. Drain once
5036
5308
  // and validate the elements before the ordinary spread copy.
@@ -5121,8 +5393,21 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
5121
5393
  }
5122
5394
  const out = lowerer.declareHiddenLocal("%arrayLit", type);
5123
5395
  const outRef = varRef(out.id, type, loc);
5396
+ // Dense capacity for the elements the statements below store.
5397
+ const capacity = Math.min(elems.length - spreads.length, 1024);
5124
5398
  const body = [
5125
- { kind: "varDecl", localId: out.id, init: { kind: "arrayLit", elems: [], type, loc }, loc },
5399
+ {
5400
+ kind: "varDecl",
5401
+ localId: out.id,
5402
+ init: {
5403
+ kind: "arrayLit",
5404
+ elems: [],
5405
+ ...(capacity > 0 ? { capacity } : {}),
5406
+ type,
5407
+ loc,
5408
+ },
5409
+ loc,
5410
+ },
5126
5411
  ];
5127
5412
  const spreadSet = new Set(spreads);
5128
5413
  const currentLength = () => ({
@@ -5545,7 +5830,7 @@ export function lowerElementAccess(lowerer, expr) {
5545
5830
  ? lowerer.narrowedArmHelper(recv.type.unionId, present, locOf(expr.expression))
5546
5831
  : null;
5547
5832
  recv = helper
5548
- ? { kind: "call", callee: helper, args: [recv], type: present, loc: locOf(expr.expression) }
5833
+ ? lowerer.checkedNarrowCall(helper, recv, present, locOf(expr.expression))
5549
5834
  : lowerer.maybeNarrow(recv, expr.expression);
5550
5835
  }
5551
5836
  // A typed-array .d.ts surface whose VALUE is an island handle
@@ -5554,7 +5839,7 @@ export function lowerElementAccess(lowerer, expr) {
5554
5839
  // declared number type.
5555
5840
  if (recv.type.kind === "jsval")
5556
5841
  return islandElementRead(lowerer, expr, recv);
5557
- const index = lowerer.lowerExpr(expr.argumentExpression);
5842
+ const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
5558
5843
  if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "bytes") {
5559
5844
  return {
5560
5845
  kind: "dynKeyGet",
@@ -5578,14 +5863,21 @@ export function lowerElementAccess(lowerer, expr) {
5578
5863
  if (index.type.kind !== "f64") {
5579
5864
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
5580
5865
  }
5581
- return {
5582
- kind: "bytesIntrinsic",
5583
- method: "get",
5584
- receiver: recv,
5585
- args: [index],
5586
- type: F64,
5587
- loc: locOf(expr),
5588
- };
5866
+ // `t[i]!` asserts presence: the numeric element read (NaN when
5867
+ // invalid). Every other read answers undefined for an invalid index,
5868
+ // like Node.
5869
+ if (ts.isNonNullExpression(expr.parent) && expr.parent.expression === expr) {
5870
+ return {
5871
+ kind: "bytesIntrinsic",
5872
+ method: "get",
5873
+ receiver: recv,
5874
+ args: [index],
5875
+ type: F64,
5876
+ loc: locOf(expr),
5877
+ invalidNaN: true,
5878
+ };
5879
+ }
5880
+ return lowerSafeBytesRead(lowerer, recv, index, locOf(expr));
5589
5881
  }
5590
5882
  // Tuple element read `t[0]`: a positional-field read of the tuple's
5591
5883
  // record shape — LITERAL indices only (the checker's per-index types
@@ -5628,13 +5920,7 @@ export function lowerElementAccess(lowerer, expr) {
5628
5920
  ? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(expr.expression))
5629
5921
  : null;
5630
5922
  obj = helper
5631
- ? {
5632
- kind: "call",
5633
- callee: helper,
5634
- args: [obj],
5635
- type: present,
5636
- loc: locOf(expr.expression),
5637
- }
5923
+ ? lowerer.checkedNarrowCall(helper, obj, present, locOf(expr.expression))
5638
5924
  : lowerer.maybeNarrow(obj, expr.expression);
5639
5925
  }
5640
5926
  const idx = tupleLiteralIndex(expr.argumentExpression);
@@ -5811,19 +6097,14 @@ export function lowerElementAccess(lowerer, expr) {
5811
6097
  }
5812
6098
  }
5813
6099
  if (receiverIr?.kind === "string") {
5814
- // `s[i]` with a number index reads a UTF-16 code unit — charAt's
5815
- // exact job (the UTF-16-exact runtime). Without
5816
- // noUncheckedIndexedAccess the checker types the read `string`,
5817
- // and charAt's "" is the only string-typed answer for an
5818
- // out-of-range or fractional index where JS reads `undefined` —
5819
- // SEMANTICS.md documents the divergence; in-range integer reads
5820
- // (the loop pattern) are JS-exact. Optional results instead retain
5821
- // undefined for missing string properties.
6100
+ // An indexed string property is absent for non-integer and missing
6101
+ // indexes. charAt has different coercion and out-of-range semantics.
5822
6102
  const recv = lowerer.lowerExpr(expr.expression);
5823
- const index = lowerer.lowerExpr(expr.argumentExpression);
5824
- const resultType = lowerer.mapTypeOf(lowerer.typeOf(expr));
6103
+ const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
5825
6104
  if (index.type.kind === "f64" && recv.type.kind === "string") {
5826
- if (resultType?.kind === "string") {
6105
+ // A proven in-bounds integer index always names a code unit, where
6106
+ // charAt and the property read agree.
6107
+ if (lowerer.stringIndexBounds.inBounds(expr))
5827
6108
  return {
5828
6109
  kind: "strIntrinsic",
5829
6110
  method: "charAt",
@@ -5832,15 +6113,7 @@ export function lowerElementAccess(lowerer, expr) {
5832
6113
  type: STRING,
5833
6114
  loc: locOf(expr),
5834
6115
  };
5835
- }
5836
- if (resultType?.kind === "union") {
5837
- const arms = lowerer.unions.get(resultType.unionId)?.arms;
5838
- if (arms?.length === 2 &&
5839
- lowerer.armTag(resultType.unionId, STRING) >= 0 &&
5840
- lowerer.armTag(resultType.unionId, UNDEFINED_T) >= 0) {
5841
- return lowerOptionalStringIndex(lowerer, recv, index, resultType, locOf(expr));
5842
- }
5843
- }
6116
+ return lowerOptionalStringIndex(lowerer, recv, index, lowerer.withUndefinedArm(STRING), locOf(expr));
5844
6117
  }
5845
6118
  lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (expected a number index and a string or string | undefined result)");
5846
6119
  }
@@ -5903,7 +6176,7 @@ export function lowerElementAccess(lowerer, expr) {
5903
6176
  ? lowerer.narrowedArmHelper(arr.type.unionId, present, locOf(expr.expression))
5904
6177
  : null;
5905
6178
  arr = helper
5906
- ? { kind: "call", callee: helper, args: [arr], type: present, loc: locOf(expr.expression) }
6179
+ ? lowerer.checkedNarrowCall(helper, arr, present, locOf(expr.expression))
5907
6180
  : lowerer.maybeNarrow(arr, expr.expression);
5908
6181
  }
5909
6182
  // The checker sees an array but the VALUE is an island handle (an
@@ -5962,10 +6235,13 @@ export function lowerElementAccess(lowerer, expr) {
5962
6235
  // change the storage representation or the low-level getter itself.
5963
6236
  if (arr.type.kind === "array") {
5964
6237
  // A non-null assertion is the explicit proven-present form. Preserve
5965
- // the dense getter's established bounds trap here; turning `xs[i]!`
5966
- // into an optional union would change library ABI checks and the
5967
- // existing catchable RangeError contract for an out-of-bounds assert.
5968
- const provenPresent = ts.isNonNullExpression(expr.parent) && expr.parent.expression === expr;
6238
+ // the dense getter's established bounds trap where the asserted value
6239
+ // must be the element type (library ABI checks and the catchable
6240
+ // RangeError contract for an out-of-bounds assert). Where undefined is
6241
+ // representable, the assertion keeps JavaScript's unchecked read.
6242
+ const provenPresent = ts.isNonNullExpression(expr.parent) &&
6243
+ expr.parent.expression === expr &&
6244
+ !assertedElementKeepsUndefined(lowerer, expr.parent);
5969
6245
  if (provenPresent) {
5970
6246
  return lowerer.maybeNarrow({ kind: "arrayGet", arr, index, type: elemT, loc: locOf(expr) }, expr);
5971
6247
  }
@@ -5979,7 +6255,7 @@ export function lowerElementAccess(lowerer, expr) {
5979
6255
  arr.kind === "arrIntrinsic" &&
5980
6256
  arr.method === "slice" &&
5981
6257
  npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null) {
5982
- const safe = lowerNpmStaticSafeIndexRead(lowerer, arr, index, locOf(expr));
6258
+ const safe = lowerSafeIndexRead(lowerer, arr, index, locOf(expr));
5983
6259
  if (safe)
5984
6260
  return safe;
5985
6261
  }
@@ -6210,6 +6486,57 @@ function lowerRecordPropertyKey(lowerer, key, node) {
6210
6486
  }
6211
6487
  return key;
6212
6488
  }
6489
+ /** The stored value of an identifier whose storage holds an undefined arm
6490
+ * that the checker's type at this use has dropped. An assignment from an
6491
+ * unchecked indexed read narrows the declared type even though the value
6492
+ * can be absent at runtime, so presence tests (`=== undefined`, `== null`,
6493
+ * truthiness, `??`, `typeof`) read the storage tag instead of trusting
6494
+ * that narrowing. A guard-proven use answers the same way at runtime. */
6495
+ export function runtimeOptionalStorageOperand(lowerer, node) {
6496
+ let expr = node;
6497
+ while (ts.isParenthesizedExpression(expr))
6498
+ expr = expr.expression;
6499
+ if (!ts.isIdentifier(expr))
6500
+ return null;
6501
+ // Only storage that can actually receive an absent value without the
6502
+ // checker seeing it qualifies; other declared-optional bindings keep the
6503
+ // checker's assignment and guard narrowing.
6504
+ const symbol = lowerer.resolveValueSymbol(expr);
6505
+ const local = lowerer.resolveLocal(expr);
6506
+ const global = local ? undefined : lowerer.globalOf(expr);
6507
+ const root = local ? lowerer.runtimeOptionalRootOf(local) : null;
6508
+ if (root && lowerer.runtimeOptionalStorageLocals.has(root)) {
6509
+ // These locals track guards: a branch that proved presence removes
6510
+ // the binding from the active set, and its reads stay narrowed. A
6511
+ // captured binding can be reassigned between calls, so a capture
6512
+ // never inherits a guard (the identifier read's own rule).
6513
+ const captured = local.boxed === true ||
6514
+ (!!symbol && lowerer.ctx.captureBySymbol.get(symbol) === local) ||
6515
+ root !== local;
6516
+ if (!captured && !lowerer.runtimeOptionalLocals.has(root))
6517
+ return null;
6518
+ }
6519
+ else if (!(symbol && lowerer.runtimeOptionalAssignedSymbols.has(symbol)) &&
6520
+ !(symbol && lowerer.runtimeOptionalBindingTypes.has(symbol)) &&
6521
+ !(global && lowerer.isRuntimeOptionalGlobal(global)))
6522
+ return null;
6523
+ const optional = lowerer.runtimeOptionalIdentifierValue(expr);
6524
+ if (optional === null)
6525
+ return null;
6526
+ const checked = lowerer.mapTypeOf(lowerer.typeOf(expr));
6527
+ if (checked === null ||
6528
+ (checked.kind === "union" && lowerer.armTag(checked.unionId, UNDEFINED_T) >= 0))
6529
+ return null;
6530
+ return optional.value;
6531
+ }
6532
+ /** A presence-test operand: an absence-aware element or field read, an
6533
+ * identifier's runtime-optional storage, or the ordinary lowering. */
6534
+ export function lowerPresenceOperand(lowerer, node) {
6535
+ // An unassigned deferred-init field observes its undefined slot too.
6536
+ return deferredSlotOr(lowerer, lowerAbsenceProbe(lowerer, node) ??
6537
+ runtimeOptionalStorageOperand(lowerer, node) ??
6538
+ lowerer.lowerExpr(node));
6539
+ }
6213
6540
  /** A keyed read used only to observe absence (`if (map[k])`, or the left
6214
6541
  * side of `||`/`??`). JavaScript answers undefined for missing record keys
6215
6542
  * and array indices even when noUncheckedIndexedAccess is disabled. Keep
@@ -6225,6 +6552,10 @@ export function lowerAbsenceProbe(lowerer, node) {
6225
6552
  expr = expr.expression;
6226
6553
  }
6227
6554
  if (ts.isPropertyAccessExpression(expr)) {
6555
+ // Namespace members are bindings, not fields of a runtime namespace
6556
+ // object. Their ordinary read already preserves optional storage.
6557
+ if (!expr.questionDotToken && nsMemberIdentOf(lowerer, expr))
6558
+ return null;
6228
6559
  const target = lowerer.fieldTarget(expr);
6229
6560
  if (target?.container === "class" &&
6230
6561
  target.fieldType.kind === "union" &&
@@ -6418,13 +6749,7 @@ export function lowerElementCompound(lowerer, expr, op) {
6418
6749
  ? lowerer.narrowedArmHelper(receiver.type.unionId, present, locOf(target.expression))
6419
6750
  : null;
6420
6751
  if (helper)
6421
- receiver = {
6422
- kind: "call",
6423
- callee: helper,
6424
- args: [receiver],
6425
- type: present,
6426
- loc: locOf(target.expression),
6427
- };
6752
+ receiver = lowerer.checkedNarrowCall(helper, receiver, present, locOf(target.expression));
6428
6753
  }
6429
6754
  if (receiver.type.kind !== receiverType.kind) {
6430
6755
  lowerer.unsupported("SC1090", target.expression, "compound assignment through a non-native array or byte view");
@@ -6439,12 +6764,15 @@ export function lowerElementCompound(lowerer, expr, op) {
6439
6764
  const oldValue = receiver.type.kind === "array"
6440
6765
  ? arrayValueRead(lowerer, receiverRef(), indexRef(), receiver.type.elem, locOf(target))
6441
6766
  : {
6767
+ // An invalid index reads undefined in Node, which every numeric
6768
+ // compound operator turns into NaN; the write is then ignored.
6442
6769
  kind: "bytesIntrinsic",
6443
6770
  method: "get",
6444
6771
  receiver: receiverRef(),
6445
6772
  args: [indexRef()],
6446
6773
  type: F64,
6447
6774
  loc: locOf(target),
6775
+ invalidNaN: true,
6448
6776
  };
6449
6777
  const oldLocal = lowerer.declareHiddenLocal("%compoundOld", oldValue.type);
6450
6778
  const oldRef = () => varRef(oldLocal.id, oldValue.type, loc);
@@ -7060,13 +7388,7 @@ export function lowerElementWrite(lowerer, expr) {
7060
7388
  ? lowerer.narrowedArmHelper(arr.type.unionId, present, locOf(target.expression))
7061
7389
  : null;
7062
7390
  if (helper) {
7063
- arr = {
7064
- kind: "call",
7065
- callee: helper,
7066
- args: [arr],
7067
- type: present,
7068
- loc: locOf(target.expression),
7069
- };
7391
+ arr = lowerer.checkedNarrowCall(helper, arr, present, locOf(target.expression));
7070
7392
  }
7071
7393
  }
7072
7394
  if (arr.type.kind !== "array") {
@@ -7184,8 +7506,21 @@ export function ensureString(lowerer, e, node) {
7184
7506
  narrowed.kind === "bigint")) {
7185
7507
  const helper = lowerer.narrowedArmHelper(e.type.unionId, narrowed, e.loc);
7186
7508
  if (helper) {
7509
+ return ensureString(lowerer, lowerer.checkedNarrowCall(helper, e, narrowed, e.loc), node);
7510
+ }
7511
+ }
7512
+ if (narrowed?.kind === "union" &&
7513
+ !typeEquals(narrowed, e.type) &&
7514
+ lowerer.unions
7515
+ .get(narrowed.unionId)
7516
+ ?.arms.every((arm) => isUnitType(arm) ||
7517
+ arm.kind === "string" ||
7518
+ arm.kind === "f64" ||
7519
+ arm.kind === "bool" ||
7520
+ arm.kind === "bigint")) {
7521
+ const helper = lowerer.narrowedRetagHelper(node, e.type.unionId, narrowed.unionId, e.loc);
7522
+ if (helper)
7187
7523
  return ensureString(lowerer, { kind: "call", callee: helper, args: [e], type: narrowed, loc: e.loc }, node);
7188
- }
7189
7524
  }
7190
7525
  lowerer.unsupported("SC1090", node, `string conversions of unions with object arms (${NARROW_FIRST})`);
7191
7526
  }
@@ -7360,20 +7695,16 @@ export function lowerAsExpression(lowerer, expr) {
7360
7695
  };
7361
7696
  }
7362
7697
  }
7363
- // Narrowed reads (`err instanceof Error` proven) still pass below;
7364
- // other targets keep the narrowness fence with the checked-cast fix.
7365
- // In a JS FILE the AsExpression is tsgo's spelling of a JSDoc cast
7366
- // (`/** @type {T} */ (e)` — JS has no `as`); 5.9.3 parsed that as a
7367
- // plain parenthesized read, which lowered caught→dyn and met the
7368
- // target through the ordinary checked-cast coercion — so the dyn
7369
- // bridge stays open here exactly for that shape.
7698
+ // Structural assertions use the ordinary checked dynamic bridge. It
7699
+ // retains primitive values and native class identity before validating
7700
+ // the requested shape; an assertion does not bypass runtime checks.
7370
7701
  const narrowed = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
7371
7702
  const bridged = narrowed &&
7372
7703
  (narrowed.kind === "f64" ||
7373
7704
  narrowed.kind === "bool" ||
7374
7705
  narrowed.kind === "string" ||
7375
7706
  narrowed.kind === "object" ||
7376
- (narrowed.kind === "dyn" && isJsSourceFile(expr.getSourceFile())));
7707
+ narrowed.kind === "dyn");
7377
7708
  if (!bridged)
7378
7709
  lowerer.unsupported("SC1063", expr);
7379
7710
  }
@@ -7448,10 +7779,20 @@ export function lowerAsExpression(lowerer, expr) {
7448
7779
  if (target && target.kind !== "union" && !typeEquals(target, inner.type)) {
7449
7780
  const helper = lowerer.narrowedArmHelper(inner.type.unionId, target, locOf(expr));
7450
7781
  if (helper) {
7451
- return { kind: "call", callee: helper, args: [inner], type: target, loc: locOf(expr) };
7782
+ return lowerer.checkedNarrowCall(helper, inner, target, locOf(expr));
7452
7783
  }
7453
7784
  }
7454
7785
  }
7786
+ // `node.parent as Sub | undefined`: a union target whose class arm is
7787
+ // narrower than the stored class gets the same checked class view as
7788
+ // `node.parent as Sub`; the other arms keep their identical tags.
7789
+ if (target?.kind === "union" &&
7790
+ (inner.type.kind === "union" || inner.type.kind === "object") &&
7791
+ !typeEquals(target, inner.type)) {
7792
+ const asserted = checkedClassUnionAssertion(lowerer, inner, target, locOf(expr));
7793
+ if (asserted)
7794
+ return asserted;
7795
+ }
7455
7796
  return inner; // erasure, unchanged
7456
7797
  }
7457
7798
  if (inner.type.kind === "jsval") {
@@ -7578,7 +7919,7 @@ export function lowerPrefixUnary(lowerer, expr) {
7578
7919
  }
7579
7920
  case ts.SyntaxKind.ExclamationToken: {
7580
7921
  // `!x` is ToBoolean-then-negate: f64/string operands go through toBool.
7581
- const operand = lowerer.ensureBool(lowerer.lowerExpr(expr.operand), expr.operand);
7922
+ const operand = lowerer.ensureBool(deferredSlotOr(lowerer, runtimeOptionalStorageOperand(lowerer, expr.operand) ?? lowerer.lowerExpr(expr.operand)), expr.operand);
7582
7923
  return { kind: "unary", op: "!", operand, type: BOOL, loc };
7583
7924
  }
7584
7925
  case ts.SyntaxKind.TildeToken: {
@@ -7625,11 +7966,11 @@ export function lowerIncDec(lowerer, expr, prefix) {
7625
7966
  }
7626
7967
  fenceNodeModuleMutation(lowerer, access, "assignment");
7627
7968
  if (ts.isPropertyAccessExpression(access)) {
7628
- const target = staticFieldWriteTarget(lowerer, access);
7969
+ const target = staticFieldWriteTarget(lowerer, access) ??
7970
+ expandoWritableTarget(lowerer, access) ??
7971
+ nsWritableTarget(lowerer, access);
7629
7972
  if (target) {
7630
- if (target.type.kind !== "f64")
7631
- lowerer.unsupported("SC1043", expr);
7632
- return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
7973
+ return lowerIncDecTarget(lowerer, expr, target, prefix);
7633
7974
  }
7634
7975
  }
7635
7976
  const stmts = [];
@@ -7683,7 +8024,15 @@ export function lowerIncDec(lowerer, expr, prefix) {
7683
8024
  const type = raw.type;
7684
8025
  const read = type.kind === "array"
7685
8026
  ? arrayValueRead(lowerer, receiver, index, type.elem, loc)
7686
- : { kind: "bytesIntrinsic", method: "get", receiver, args: [index], type: F64, loc };
8027
+ : {
8028
+ kind: "bytesIntrinsic",
8029
+ method: "get",
8030
+ receiver,
8031
+ args: [index],
8032
+ type: F64,
8033
+ loc,
8034
+ invalidNaN: true,
8035
+ };
7687
8036
  return finish(read, (value) => type.kind === "array"
7688
8037
  ? arrayValueStore(lowerer, receiver, index, lowerer.coerceInto(access, value, type.elem), type.elem, loc)
7689
8038
  : {
@@ -7715,6 +8064,7 @@ export function lowerIncDec(lowerer, expr, prefix) {
7715
8064
  ? lowerer.fieldTarget(access)
7716
8065
  : symbolFieldTarget(lowerer, access);
7717
8066
  if (target && lowerer.dynConvertible(target.fieldType)) {
8067
+ checkDeferredMemberReceiver(lowerer, target, access);
7718
8068
  target.obj = save(target.obj, "%incrementReceiver");
7719
8069
  return finish(lowerer.fieldGetExpr(target, loc, access), (value) => lowerer.fieldSetStmt(target, lowerer.coerceInto(access, value, target.fieldType), loc, access));
7720
8070
  }
@@ -7741,10 +8091,18 @@ export function lowerIncDec(lowerer, expr, prefix) {
7741
8091
  if (!target) {
7742
8092
  lowerer.rejectUnresolved(expr.operand, `increment/decrement of '${expr.operand.text}' (not a writable local or module global)`);
7743
8093
  }
8094
+ return lowerIncDecTarget(lowerer, expr, target, prefix);
8095
+ }
8096
+ /** Numeric updates read the represented slot once and write the computed
8097
+ * value back in that same representation. In particular, an unchecked
8098
+ * indexed initializer can leave undefined in an otherwise numeric binding. */
8099
+ export function lowerIncDecTarget(lowerer, expr, target, prefix) {
8100
+ const loc = locOf(expr);
8101
+ const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
7744
8102
  if (target.type.kind === "dyn") {
7745
8103
  const old = lowerer.declareHiddenLocal("%numericOld", DYN);
7746
8104
  const next = lowerer.declareHiddenLocal("%numericNext", DYN);
7747
- const read = lowerer.lowerExpr(expr.operand);
8105
+ const read = varRef(target.id, target.type, loc);
7748
8106
  return {
7749
8107
  kind: "seqExpr",
7750
8108
  stmts: [
@@ -7776,6 +8134,42 @@ export function lowerIncDec(lowerer, expr, prefix) {
7776
8134
  loc,
7777
8135
  };
7778
8136
  }
8137
+ if (target.type.kind === "union") {
8138
+ const read = lowerOptionalNumber(lowerer, varRef(target.id, target.type, loc), loc, expr.operand);
8139
+ if (read.type.kind === "f64") {
8140
+ const old = lowerer.declareHiddenLocal("%numericOld", F64);
8141
+ const next = lowerer.declareHiddenLocal("%numericNext", F64);
8142
+ const result = varRef(prefix ? next.id : old.id, F64, loc);
8143
+ return {
8144
+ kind: "seqExpr",
8145
+ stmts: [
8146
+ { kind: "varDecl", localId: old.id, init: read, loc },
8147
+ {
8148
+ kind: "varDecl",
8149
+ localId: next.id,
8150
+ init: {
8151
+ kind: "bin",
8152
+ op,
8153
+ left: varRef(old.id, F64, loc),
8154
+ right: numLit(1, loc),
8155
+ type: F64,
8156
+ loc,
8157
+ },
8158
+ loc,
8159
+ },
8160
+ {
8161
+ kind: "assign",
8162
+ localId: target.id,
8163
+ value: lowerer.coerceInto(expr.operand, varRef(next.id, F64, loc), target.type),
8164
+ loc,
8165
+ },
8166
+ ],
8167
+ result,
8168
+ type: F64,
8169
+ loc,
8170
+ };
8171
+ }
8172
+ }
7779
8173
  if (target.type.kind !== "f64")
7780
8174
  lowerer.unsupported("SC1043", expr);
7781
8175
  return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
@@ -8136,6 +8530,9 @@ export function lowerBinary(lowerer, expr) {
8136
8530
  const recv = tryLowerExpression(lowerer, expr.left.expression);
8137
8531
  if (recv && recv.type.kind === "dyn")
8138
8532
  return lowerDynMemberAssignment(lowerer, expr, recv);
8533
+ const unionWrite = lowerUnionFieldWrite(lowerer, expr.left, expr.right);
8534
+ if (unionWrite)
8535
+ return unionWrite;
8139
8536
  const field = lowerer.fieldTarget(expr.left);
8140
8537
  if (field) {
8141
8538
  const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
@@ -8182,7 +8579,7 @@ export function lowerBinary(lowerer, expr) {
8182
8579
  const nullTest = lowerLooseNullCompare(lowerer, expr, loc);
8183
8580
  if (nullTest)
8184
8581
  return nullTest;
8185
- const loose = lowerAbstractEquality(lowerer, lowerer.lowerExpr(expr.left), lowerer.lowerExpr(expr.right), op === ts.SyntaxKind.ExclamationEqualsToken, loc);
8582
+ const loose = lowerAbstractEquality(lowerer, deferredSlotOr(lowerer, lowerer.lowerExpr(expr.left)), deferredSlotOr(lowerer, lowerer.lowerExpr(expr.right)), op === ts.SyntaxKind.ExclamationEqualsToken, loc);
8186
8583
  if (loose)
8187
8584
  return loose;
8188
8585
  lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
@@ -8229,12 +8626,26 @@ export function lowerBinary(lowerer, expr) {
8229
8626
  }
8230
8627
  const strictEquality = op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
8231
8628
  op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
8629
+ // An identifier whose storage is runtime-optional is compared by its
8630
+ // stored value: the checker's narrowing to the present arm is not a
8631
+ // runtime proof. Identifier reads are pure, so taking the stored value
8632
+ // after both operands lowered keeps the evaluation order. A union on
8633
+ // the other side keeps the narrowed read (union-to-union comparison
8634
+ // has no lowering).
8232
8635
  let left = strictEquality
8233
8636
  ? (lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left))
8234
8637
  : lowerer.lowerExpr(expr.left);
8235
8638
  let right = strictEquality
8236
8639
  ? (lowerAbsenceProbe(lowerer, expr.right) ?? lowerer.lowerExpr(expr.right))
8237
8640
  : lowerer.lowerExpr(expr.right);
8641
+ if (strictEquality) {
8642
+ const storedLeft = runtimeOptionalStorageOperand(lowerer, expr.left);
8643
+ const storedRight = runtimeOptionalStorageOperand(lowerer, expr.right);
8644
+ if (storedLeft !== null && right.type.kind !== "union")
8645
+ left = storedLeft;
8646
+ if (storedRight !== null && left.type.kind !== "union")
8647
+ right = storedRight;
8648
+ }
8238
8649
  if (strictEquality && (left.type.kind === "caught" || right.type.kind === "caught")) {
8239
8650
  if (left.type.kind === "caught")
8240
8651
  left = { kind: "caughtToDyn", value: left, type: DYN, loc: left.loc };
@@ -8721,7 +9132,7 @@ export function lowerBinary(lowerer, expr) {
8721
9132
  const optionalStringPlus = lowerOptionalStringPlus();
8722
9133
  if (optionalStringPlus)
8723
9134
  return optionalStringPlus;
8724
- if (isJsSourceFile(expr.getSourceFile())) {
9135
+ {
8725
9136
  const primitive = (type) => type.kind === "f64" ||
8726
9137
  type.kind === "string" ||
8727
9138
  type.kind === "bool" ||
@@ -8827,6 +9238,13 @@ export function lowerBinary(lowerer, expr) {
8827
9238
  if (unitTest)
8828
9239
  return unitTest;
8829
9240
  if (left.type.kind === "union" || right.type.kind === "union") {
9241
+ const compared = lowerUnionEquality(lowerer, left, right, negated, false, loc);
9242
+ if (compared)
9243
+ return compared;
9244
+ const aliasRefusal = (type) => {
9245
+ if (tagEqualityMayMissAlias(lowerer, type))
9246
+ lowerer.unsupported("SC1090", expr, `comparisons of related class values beside structural union members (${NARROW_FIRST})`);
9247
+ };
8830
9248
  // JS strict equality of the ARM values, per union tag (a
8831
9249
  // per-union helper): equal tags compare payloads (f64 ==, string
8832
9250
  // bytes, bool ==, ref-arm POINTER identity), different tags are
@@ -8862,6 +9280,7 @@ export function lowerBinary(lowerer, expr) {
8862
9280
  };
8863
9281
  }
8864
9282
  if ((sameUnion || !bothUnion) && lowerer.eqComparableUnion(ut.unionId)) {
9283
+ aliasRefusal(ut);
8865
9284
  return {
8866
9285
  kind: "unionEq",
8867
9286
  unionId: ut.unionId,
@@ -8892,6 +9311,7 @@ export function lowerBinary(lowerer, expr) {
8892
9311
  kind: "union",
8893
9312
  unionId: lowerer.unions.intern(arms),
8894
9313
  };
9314
+ aliasRefusal(common);
8895
9315
  return {
8896
9316
  kind: "unionEq",
8897
9317
  unionId: common.unionId,
@@ -9077,7 +9497,9 @@ function lowerLogicalChain(lowerer, expr) {
9077
9497
  current = left;
9078
9498
  continue;
9079
9499
  }
9080
- result = lowerLogicalPair(lowerer, current, lowerAbsenceProbe(lowerer, left) ?? lowerer.lowerExpr(left));
9500
+ result = lowerLogicalPair(lowerer, current, current.operatorToken.kind === ts.SyntaxKind.BarBarToken
9501
+ ? lowerPresenceOperand(lowerer, left)
9502
+ : (lowerAbsenceProbe(lowerer, left) ?? lowerer.lowerExpr(left)));
9081
9503
  break;
9082
9504
  }
9083
9505
  for (let i = parents.length - 1; i >= 0; i--) {
@@ -9732,11 +10154,12 @@ function lowerUnionTypeofTest(lowerer, expr, loc) {
9732
10154
  for (const [a, b] of operandPairs(expr)) {
9733
10155
  if (!ts.isTypeOfExpression(a))
9734
10156
  continue;
9735
- if (lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
10157
+ const stored = runtimeOptionalStorageOperand(lowerer, a.expression);
10158
+ if (stored === null && lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
9736
10159
  continue;
9737
10160
  if (!ts.isStringLiteral(b))
9738
10161
  continue; // bare-typeof + strEq handles pure operands
9739
- const value = lowerer.lowerExpr(a.expression);
10162
+ const value = stored ?? lowerer.lowerExpr(a.expression);
9740
10163
  if (value.type.kind !== "union")
9741
10164
  continue; // defensive: an already-narrowed use
9742
10165
  const unionId = value.type.unionId;
@@ -10037,17 +10460,30 @@ export function caughtRead(lowerer, node, local, loc) {
10037
10460
  }
10038
10461
  if (narrowed?.kind === "object") {
10039
10462
  const info = lowerer.classes.get(narrowed.className);
10040
- if (info && lowerer.inHierarchy(info)) {
10041
- return { kind: "caughtNarrow", value: ref, type: narrowed, loc };
10463
+ if (info) {
10464
+ const value = { kind: "caughtNarrow", value: ref, type: narrowed, loc };
10465
+ if (lowerer.inHierarchy(info))
10466
+ return value;
10467
+ return {
10468
+ kind: "ternary",
10469
+ cond: {
10470
+ kind: "caughtTest",
10471
+ value: ref,
10472
+ test: "instanceof",
10473
+ className: info.def.name,
10474
+ type: BOOL,
10475
+ loc,
10476
+ },
10477
+ then: value,
10478
+ else_: lowerer.coerceInto(node, { kind: "caughtToDyn", value: ref, type: DYN, loc }, narrowed),
10479
+ type: narrowed,
10480
+ loc,
10481
+ };
10042
10482
  }
10043
10483
  }
10044
- // An UN-narrowed use — the occurrence still types `unknown` — converts
10045
- // to a dyn value (`options.onError?.(error)` — the caught snapshot
10046
- // flowing into an unknown slot): the typed→unknown deep-copy stance
10047
- // extended to exception payloads. Error payloads keep their
10048
- // observability (instanceof Error / .message / String() — the checked-dynamic tree's
10049
- // error encoding); other object payloads are type-erased at runtime and
10050
- // convert to the "[object Object]" approximation — SEMANTICS.md 67.
10484
+ // Unnarrowed reads cross into unknown. The caught-value dispatch pass
10485
+ // retains known native class payloads through live identity capsules;
10486
+ // other payload kinds use the runtime's ordinary conversion.
10051
10487
  if (narrowed?.kind === "dyn") {
10052
10488
  return { kind: "caughtToDyn", value: ref, type: DYN, loc };
10053
10489
  }
@@ -10113,6 +10549,38 @@ export function lowerInstanceOf(lowerer, expr, loc) {
10113
10549
  (lowerer.isStdlibGlobal(expr.right, "Set") || lowerer.isStdlibGlobal(expr.right, "Map"))) {
10114
10550
  const collection = lowerer.isStdlibGlobal(expr.right, "Map") ? "map" : "set";
10115
10551
  const value = lowerer.lowerExpr(expr.left);
10552
+ if (value.type.kind === "union") {
10553
+ const union = lowerer.unions.get(value.type.unionId);
10554
+ if (union) {
10555
+ const local = lowerer.declareHiddenLocal("%collectionTest", value.type);
10556
+ const reference = varRef(local.id, value.type, loc);
10557
+ let result = { kind: "boolLit", value: false, type: BOOL, loc };
10558
+ union.arms.forEach((arm, tag) => {
10559
+ if (arm.kind !== collection)
10560
+ return;
10561
+ const test = {
10562
+ kind: "unionIsTag",
10563
+ unionId: union.id,
10564
+ value: reference,
10565
+ tag,
10566
+ negated: false,
10567
+ type: BOOL,
10568
+ loc,
10569
+ };
10570
+ result =
10571
+ result.kind === "boolLit"
10572
+ ? test
10573
+ : { kind: "logical", op: "||", left: result, right: test, type: BOOL, loc };
10574
+ });
10575
+ return {
10576
+ kind: "seqExpr",
10577
+ stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }],
10578
+ result,
10579
+ type: BOOL,
10580
+ loc,
10581
+ };
10582
+ }
10583
+ }
10116
10584
  if (value.type.kind === "dyn")
10117
10585
  return {
10118
10586
  kind: "libCall",
@@ -10390,8 +10858,22 @@ export function lowerInstanceOf(lowerer, expr, loc) {
10390
10858
  const caughtLocal = lowerer.caughtLocalOf(expr.left);
10391
10859
  if (caughtLocal) {
10392
10860
  if (!lowerer.inHierarchy(target)) {
10393
- lowerer.unsupported("SC1090", expr, `'instanceof' on a catch binding against the standalone class '${target.def.name}' ` +
10394
- `(only classes in extends hierarchies carry the vtable the payload test needs)`);
10861
+ const value = varRef(caughtLocal.id, CAUGHT, loc);
10862
+ return {
10863
+ kind: "ternary",
10864
+ cond: {
10865
+ kind: "caughtTest",
10866
+ value,
10867
+ test: "instanceof",
10868
+ className: target.def.name,
10869
+ type: BOOL,
10870
+ loc,
10871
+ },
10872
+ then: { kind: "boolLit", value: true, type: BOOL, loc },
10873
+ else_: classInstanceOf(lowerer, { kind: "caughtToDyn", value, type: DYN, loc }, target, loc),
10874
+ type: BOOL,
10875
+ loc,
10876
+ };
10395
10877
  }
10396
10878
  return {
10397
10879
  kind: "caughtTest",
@@ -10689,11 +11171,10 @@ function lowerPrivateIn(lowerer, expr, priv, loc) {
10689
11171
  * type-directed: a declared non-optional field is a compile-time `true`,
10690
11172
  * a missing field (no index signature) a compile-time `false` — both
10691
11173
  * folds limited to side-effect-free receivers, the lowerInstanceOf rule —
10692
- * and an OPTIONAL field (undefined-armed union) is a runtime tag test on
10693
- * the slot: present iff the arm is not undefined. That last case is the
10694
- * representation's honest answer — a field explicitly assigned
10695
- * `undefined` reads as absent (`"a" in {a: undefined}` is true in JS,
10696
- * false here; SEMANTICS.md 55). Union receivers (the `in`-narrowing
11174
+ * and an OPTIONAL field (undefined-armed union) is a runtime presence
11175
+ * test on the slot: an omitted or deleted property is absent, while a
11176
+ * property explicitly holding `undefined` is present, as in JS
11177
+ * (`"a" in {a: undefined}` is true). Union receivers (the `in`-narrowing
10697
11178
  * idiom over multiple shapes), index-signature keys, class instances,
10698
11179
  * and dyn/unknown stay fenced. Keys are literal strings — a computed key
10699
11180
  * over a shape would need the runtime key table. */
@@ -10737,13 +11218,42 @@ function lowerInExpression(lowerer, expr, loc) {
10737
11218
  }
10738
11219
  }
10739
11220
  }
11221
+ // A RUNTIME numeric key over an ARRAY receiver (`if (i in arr)` in a
11222
+ // loop) asks the same slot/property presence query as the literal fold
11223
+ // above and `Object.hasOwn`. The checked-dynamic fallback below would
11224
+ // box the receiver and materialize every element on each evaluation,
11225
+ // which makes such loops quadratic. A Number key never names an
11226
+ // inherited property: neither Array.prototype nor Object.prototype has
11227
+ // index-like or numeric-string keys. JS evaluates the key before the
11228
+ // receiver, so the key is stabilized in a hidden local first.
11229
+ if (lowerer.mapTypeOf(lowerer.typeOf(expr.left))?.kind === "f64" &&
11230
+ lowerer.mapTypeOf(lowerer.typeOf(expr.right))?.kind === "array") {
11231
+ const k = lowerer.lowerExpr(expr.left);
11232
+ const recvArr = lowerer.lowerExpr(expr.right);
11233
+ if (k.type.kind === "f64" && recvArr.type.kind === "array") {
11234
+ const keyLocal = lowerer.declareHiddenLocal("%inIndex", F64);
11235
+ return {
11236
+ kind: "seqExpr",
11237
+ stmts: [{ kind: "varDecl", localId: keyLocal.id, init: k, loc }],
11238
+ result: {
11239
+ kind: "arrayHas",
11240
+ arr: recvArr,
11241
+ index: varRef(keyLocal.id, F64, loc),
11242
+ type: BOOL,
11243
+ loc,
11244
+ },
11245
+ type: BOOL,
11246
+ loc,
11247
+ };
11248
+ }
11249
+ }
10740
11250
  const key = foldedStringKeyOf(lowerer, expr.left);
10741
11251
  if (key === null) {
10742
11252
  // A RUNTIME string key over an INDEX-SIGNATURE record receiver (the
10743
11253
  // `names.filter((k) => k in config)` idiom): the interned key-
10744
11254
  // presence helper — declared fields answer statically per name
10745
- // (optional slots per value: the undefined arm reads absent, stance
10746
- // 55), then the overflow map's live keys. Other receivers keep the
11255
+ // (optional slots by their runtime presence), then the overflow
11256
+ // map's live keys. Other receivers keep the
10747
11257
  // fence: fixed shapes want the literal folds above, and there is no
10748
11258
  // runtime key table to ask.
10749
11259
  const rIn = lowerRuntimeKeyIn(lowerer, expr, loc);
@@ -10986,24 +11496,9 @@ function lowerInExpression(lowerer, expr, loc) {
10986
11496
  const field = shape.fields.find((f) => f.name === key);
10987
11497
  if (field) {
10988
11498
  if (field.type.kind === "union" && lowerer.armTag(field.type.unionId, UNDEFINED_T) >= 0) {
10989
- // Optional slot: the key is present iff the arm is not undefined.
10990
- const read = {
10991
- kind: "recordGet",
10992
- obj: recv,
10993
- shapeId: recv.type.shapeId,
10994
- field: key,
10995
- type: field.type,
10996
- loc,
10997
- };
10998
- return {
10999
- kind: "unionIsTag",
11000
- unionId: field.type.unionId,
11001
- tag: lowerer.armTag(field.type.unionId, UNDEFINED_T),
11002
- negated: true,
11003
- value: read,
11004
- type: BOOL,
11005
- loc,
11006
- };
11499
+ // Optional slot: the key is present unless the field is absent
11500
+ // (omitted or deleted); an explicit undefined is present.
11501
+ return lowerer.recordFieldPresent(recv, recv.type.shapeId, key, loc);
11007
11502
  }
11008
11503
  // A declared non-optional field always exists on every value of the
11009
11504
  // shape — statically true, but folding may only drop a
@@ -11043,9 +11538,9 @@ function lowerInExpression(lowerer, expr, loc) {
11043
11538
  * runtime string and r an index-signature record — an interned
11044
11539
  * `%rec.haskey.<n>(k, r)` walks the declared names (a string-equality
11045
11540
  * chain: non-optional fields and accessor slots answer true, optional
11046
- * slots answer their per-value tag test) and then the overflow map's
11047
- * live keys. Null when the pair is outside that shape (the caller keeps
11048
- * its fence). */
11541
+ * slots answer their per-value tag test) and then probes the overflow
11542
+ * map's hashed key set. Null when the pair is outside that shape (the
11543
+ * caller keeps its fence). */
11049
11544
  function lowerRuntimeKeyIn(lowerer, expr, loc) {
11050
11545
  if (lowerer.mapTypeOf(lowerer.typeOf(expr.left))?.kind !== "string")
11051
11546
  return null;
@@ -11080,62 +11575,15 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
11080
11575
  type: BOOL,
11081
11576
  loc,
11082
11577
  };
11083
- const utag = !accessor && f.type.kind === "union" ? lowerer.armTag(f.type.unionId, UNDEFINED_T) : -1;
11084
- const answer = utag >= 0 && f.type.kind === "union"
11085
- ? {
11086
- kind: "unionIsTag",
11087
- unionId: f.type.unionId,
11088
- tag: utag,
11089
- negated: true,
11090
- value: {
11091
- kind: "recordGet",
11092
- obj: r,
11093
- shapeId: recvT.shapeId,
11094
- field: f.name,
11095
- type: f.type,
11096
- loc,
11097
- },
11098
- type: BOOL,
11099
- loc,
11100
- }
11101
- : { kind: "boolLit", value: true, type: BOOL, loc };
11578
+ const answer = accessor
11579
+ ? { kind: "boolLit", value: true, type: BOOL, loc }
11580
+ : lowerer.recordFieldPresent(r, recvT.shapeId, f.name, loc);
11102
11581
  body.push({ kind: "if", cond: eq, then: [ret(answer)], else_: null, loc });
11103
11582
  }
11104
- const ksT = arrayOf(STRING);
11105
- body.push({
11106
- kind: "varDecl",
11107
- localId: "ks.0",
11108
- init: { kind: "recordOvfKeys", obj: r, shapeId: recvT.shapeId, type: ksT, loc },
11109
- loc,
11110
- }, countedFor(loc, {
11111
- kind: "arrIntrinsic",
11112
- method: "length",
11113
- receiver: varRef("ks.0", ksT, loc),
11114
- args: [],
11115
- type: F64,
11116
- loc,
11117
- }, () => [
11118
- {
11119
- kind: "if",
11120
- cond: {
11121
- kind: "strEq",
11122
- negated: false,
11123
- left: k,
11124
- right: {
11125
- kind: "arrayGet",
11126
- arr: varRef("ks.0", ksT, loc),
11127
- index: varRef("i.0", F64, loc),
11128
- type: STRING,
11129
- loc,
11130
- },
11131
- type: BOOL,
11132
- loc,
11133
- },
11134
- then: [ret({ kind: "boolLit", value: true, type: BOOL, loc })],
11135
- else_: null,
11136
- loc,
11137
- },
11138
- ]), ret({ kind: "boolLit", value: false, type: BOOL, loc }));
11583
+ // Overflow keys answer through the map's hashed presence probe (a
11584
+ // stored undefined value is still present), not a scan over a fresh
11585
+ // key snapshot, which made `k in dict` loops quadratic.
11586
+ body.push(ret({ kind: "recordOvfHas", obj: r, shapeId: recvT.shapeId, key: k, type: BOOL, loc }));
11139
11587
  lowerer.liftedFns.push({
11140
11588
  name: helper,
11141
11589
  params: [
@@ -11146,8 +11594,6 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
11146
11594
  locals: [
11147
11595
  { id: "k.0", name: "k", type: STRING, mutable: true },
11148
11596
  { id: "r.0", name: "r", type: recT, mutable: true },
11149
- { id: "ks.0", name: "ks", type: ksT, mutable: false },
11150
- { id: "i.0", name: "i", type: F64, mutable: true },
11151
11597
  ],
11152
11598
  body,
11153
11599
  loc,
@@ -11835,7 +12281,7 @@ export function lowerUnionProperty(lowerer, expr) {
11835
12281
  return null;
11836
12282
  }
11837
12283
  if (value.type.kind !== "union") {
11838
- throw new InternalCompilerError("lowerer bug: union-typed receiver lowered to a non-union");
12284
+ lowerer.unsupported("SC1090", expr, "shared property access without a represented union receiver");
11839
12285
  }
11840
12286
  const def = lowerer.unions.get(value.type.unionId);
11841
12287
  if (!def)
@@ -11876,13 +12322,7 @@ export function lowerUnionProperty(lowerer, expr) {
11876
12322
  if (receiver === null) {
11877
12323
  const helper = lowerer.narrowedArmHelper(value.type.unionId, objectType, locOf(expr.expression));
11878
12324
  if (helper !== null)
11879
- receiver = {
11880
- kind: "call",
11881
- callee: helper,
11882
- args: [value],
11883
- type: objectType,
11884
- loc: locOf(expr.expression),
11885
- };
12325
+ receiver = lowerer.checkedNarrowCall(helper, value, objectType, locOf(expr.expression));
11886
12326
  }
11887
12327
  if (receiver !== null) {
11888
12328
  return {
@@ -12094,13 +12534,7 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
12094
12534
  if (obj.type.kind === "union") {
12095
12535
  const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
12096
12536
  obj = helper
12097
- ? {
12098
- kind: "call",
12099
- callee: helper,
12100
- args: [obj],
12101
- type: receiverType,
12102
- loc: locOf(receiverNode),
12103
- }
12537
+ ? lowerer.checkedNarrowCall(helper, obj, receiverType, locOf(receiverNode))
12104
12538
  : lowerer.maybeNarrow(obj, receiverNode);
12105
12539
  }
12106
12540
  return lowerer.coerceInto(receiverNode, obj, receiverType);
@@ -12212,11 +12646,32 @@ export function fieldTarget(lowerer, access) {
12212
12646
  const stored = ts.isIdentifier(access.expression)
12213
12647
  ? (lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type)
12214
12648
  : undefined;
12215
- const receiverIr = stored?.kind === "record" || stored?.kind === "object"
12649
+ const narrowed = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
12650
+ // Structural record views retain their storage layout, while a nominal
12651
+ // subclass guard proves a more specific layout of the same object.
12652
+ const receiverIr = stored?.kind === "record"
12216
12653
  ? stored
12217
- : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
12654
+ : stored?.kind === "object" &&
12655
+ !(narrowed?.kind === "object" &&
12656
+ lowerer.isSubclassOf(narrowed.className, stored.className))
12657
+ ? stored
12658
+ : narrowed;
12218
12659
  if (receiverIr?.kind === "object") {
12219
- return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
12660
+ const target = classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
12661
+ if (target)
12662
+ return target;
12663
+ // Abstract properties declare no base storage. Resolve the concrete
12664
+ // instance's live field or accessor through the existing class bridge;
12665
+ // inventing a shared field offset would misread subclass layouts.
12666
+ if (abstractPropertyDeclOf(lowerer, access)) {
12667
+ return {
12668
+ container: "dynamic",
12669
+ obj: lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), DYN),
12670
+ field: access.name.text,
12671
+ fieldType: lowerer.irTypeOf(access),
12672
+ };
12673
+ }
12674
+ return null;
12220
12675
  }
12221
12676
  if (receiverIr?.kind === "union" && hasClassPayload(lowerer, receiverIr)) {
12222
12677
  return representedClassFieldTarget(lowerer, access.expression, access.name.text, lowerer.lowerExpr(access.expression));
@@ -12243,13 +12698,7 @@ export function fieldTarget(lowerer, access) {
12243
12698
  ? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(access.expression))
12244
12699
  : null;
12245
12700
  obj = helper
12246
- ? {
12247
- kind: "call",
12248
- callee: helper,
12249
- args: [obj],
12250
- type: present,
12251
- loc: locOf(access.expression),
12252
- }
12701
+ ? lowerer.checkedNarrowCall(helper, obj, present, locOf(access.expression))
12253
12702
  : lowerer.maybeNarrow(obj, access.expression);
12254
12703
  }
12255
12704
  if (obj.type.kind !== "record")
@@ -12456,14 +12905,24 @@ function symbolFieldTarget(lowerer, expr) {
12456
12905
  * tsc-clean (the property types as the setter's param), but Node yields
12457
12906
  * undefined, which these property types cannot represent. */
12458
12907
  export function fieldGetExpr(lowerer, target, loc, blame) {
12908
+ // A member read through an unassigned deferred-init field throws Node's
12909
+ // member-read TypeError instead of the generic checked extraction.
12910
+ if (target.container === "class" &&
12911
+ ts.isPropertyAccessExpression(blame) &&
12912
+ target.obj.type.kind === "object" &&
12913
+ lowerer.deferredFieldSlot(target.obj) !== null) {
12914
+ const checked = lowerer.runtimeOptionalPropertyReceiver(blame.expression, target.obj, target.obj.type, target.field);
12915
+ if (checked)
12916
+ target = { ...target, obj: checked };
12917
+ }
12459
12918
  if (target.container === "dynamic")
12460
- return {
12919
+ return lowerer.coerceInto(blame, {
12461
12920
  kind: "dynKeyGet",
12462
12921
  value: target.obj,
12463
12922
  key: { kind: "strLit", value: target.field, type: STRING, loc },
12464
12923
  type: DYN,
12465
12924
  loc,
12466
- };
12925
+ }, target.fieldType);
12467
12926
  if (target.container === "class" &&
12468
12927
  (target.field === "message" || target.field === "name") &&
12469
12928
  (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
@@ -12579,6 +13038,34 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
12579
13038
  if (lowerer.classes.get(target.className)?.deferredInitFields?.has(target.field) === true &&
12580
13039
  target.fieldType.kind === "union") {
12581
13040
  const inner = lowerer.stripUndefinedArm(target.fieldType);
13041
+ // Reference arms over a stable receiver check inline: the present
13042
+ // arm is a plain projection of the slot, so ordinary reads keep their
13043
+ // borrowed form and only an unassigned slot reaches the throw.
13044
+ const unionId = target.fieldType.unionId;
13045
+ const presentTag = lowerer.armTag(unionId, inner);
13046
+ const undefTag = lowerer.armTag(unionId, UNDEFINED_T);
13047
+ if (inner.kind === "object" &&
13048
+ presentTag >= 0 &&
13049
+ undefTag >= 0 &&
13050
+ lowerer.unions.get(unionId)?.arms.length === 2 &&
13051
+ isSafeToRepeat(read)) {
13052
+ return {
13053
+ kind: "ternary",
13054
+ cond: {
13055
+ kind: "unionIsTag",
13056
+ unionId,
13057
+ tag: undefTag,
13058
+ negated: false,
13059
+ value: read,
13060
+ type: BOOL,
13061
+ loc,
13062
+ },
13063
+ then: nodeThrowExpr(1, "", DEFERRED_READ_MESSAGE, inner, loc),
13064
+ else_: { kind: "unionNarrow", unionId, tag: presentTag, value: read, type: inner, loc },
13065
+ type: inner,
13066
+ loc,
13067
+ };
13068
+ }
12582
13069
  const helper = lowerer.deferredReadHelper(target.fieldType.unionId, inner, loc);
12583
13070
  if (helper)
12584
13071
  return { kind: "call", callee: helper, args: [read], type: inner, loc };
@@ -12598,6 +13085,75 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
12598
13085
  * call). A write to a getter-only property never gets here in a clean
12599
13086
  * program (tsc's TS2540 is the fence); the rejection is the backstop. */
12600
13087
  export function fieldSetStmt(lowerer, target, value, loc, blame) {
13088
+ // A member write through an unassigned deferred-init field: the value
13089
+ // still evaluates first, then the write throws Node's TypeError.
13090
+ const deferredSlot = target.container === "class" &&
13091
+ !((target.field === "message" || target.field === "name") &&
13092
+ (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
13093
+ ? lowerer.deferredFieldSlot(target.obj)
13094
+ : null;
13095
+ if (target.container === "class" && deferredSlot?.type.kind === "union") {
13096
+ const unionId = deferredSlot.type.unionId;
13097
+ const undefTag = lowerer.armTag(unionId, UNDEFINED_T);
13098
+ const presentTag = lowerer.armTag(unionId, target.obj.type);
13099
+ if (undefTag >= 0 && presentTag >= 0) {
13100
+ // A value that cannot write anything leaves the field unchanged, so
13101
+ // the field is read after it (no snapshot of the slot).
13102
+ const late = isSafeToRepeat(deferredSlot) && writeFree(value);
13103
+ const receiver = late
13104
+ ? null
13105
+ : lowerer.declareHiddenLocal("%deferredWriteObject", deferredSlot.type);
13106
+ const stored = lowerer.declareHiddenLocal("%deferredWriteValue", value.type);
13107
+ const receiverRef = receiver ? varRef(receiver.id, receiver.type, loc) : deferredSlot;
13108
+ const result = varRef(stored.id, stored.type, loc);
13109
+ const write = {
13110
+ kind: "fieldSet",
13111
+ obj: {
13112
+ kind: "unionNarrow",
13113
+ unionId,
13114
+ tag: presentTag,
13115
+ value: receiverRef,
13116
+ type: target.obj.type,
13117
+ loc,
13118
+ },
13119
+ className: target.className,
13120
+ field: target.field,
13121
+ value: result,
13122
+ loc,
13123
+ };
13124
+ return {
13125
+ kind: "exprStmt",
13126
+ expr: {
13127
+ kind: "seqExpr",
13128
+ stmts: [
13129
+ ...(receiver
13130
+ ? [{ kind: "varDecl", localId: receiver.id, init: deferredSlot, loc }]
13131
+ : []),
13132
+ { kind: "varDecl", localId: stored.id, init: value, loc },
13133
+ ],
13134
+ result: {
13135
+ kind: "ternary",
13136
+ cond: {
13137
+ kind: "unionIsTag",
13138
+ unionId,
13139
+ tag: undefTag,
13140
+ value: receiverRef,
13141
+ negated: false,
13142
+ type: BOOL,
13143
+ loc,
13144
+ },
13145
+ then: nodeThrowExpr(1, "", `Cannot set properties of undefined (setting '${target.field}')`, result.type, loc),
13146
+ else_: { kind: "seqExpr", stmts: [write], result, type: result.type, loc },
13147
+ type: result.type,
13148
+ loc,
13149
+ },
13150
+ type: result.type,
13151
+ loc,
13152
+ },
13153
+ loc,
13154
+ };
13155
+ }
13156
+ }
12601
13157
  if (target.container === "dynamic")
12602
13158
  return {
12603
13159
  kind: "exprStmt",
@@ -12915,6 +13471,7 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
12915
13471
  const target = targetOf();
12916
13472
  if (!target)
12917
13473
  lowerer.unsupported("SC1090", access, "compound assignment to unsupported field targets");
13474
+ checkDeferredMemberReceiver(lowerer, target, access);
12918
13475
  target.obj = save(target.obj, "%compoundReceiver");
12919
13476
  // Accessors observe getter, RHS side effects, then setter, all through
12920
13477
  // the saved receiver.