@scriptc/compiler 0.1.7 → 0.2.0

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 (1032) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +73 -24
  2. package/ambient/scriptc-overrides.d.ts +10 -6
  3. package/ambient/scriptc.d.ts +15 -0
  4. package/dist/backend/build-cache.d.ts +1 -3
  5. package/dist/backend/build-cache.js +3 -22
  6. package/dist/backend/build-cache.js.map +1 -1
  7. package/dist/backend/cache-root.d.ts +3 -0
  8. package/dist/backend/cache-root.js +23 -0
  9. package/dist/backend/cache-root.js.map +1 -0
  10. package/dist/backend/cycle-analysis.d.ts +15 -0
  11. package/dist/backend/cycle-analysis.js +107 -0
  12. package/dist/backend/cycle-analysis.js.map +1 -0
  13. package/dist/backend/cycle-analysis.test.js +124 -0
  14. package/dist/backend/cycle-analysis.test.js.map +1 -0
  15. package/dist/backend/executable-features.d.ts +4 -0
  16. package/dist/backend/executable-features.js +42 -0
  17. package/dist/backend/executable-features.js.map +1 -0
  18. package/dist/backend/external-c.d.ts +5 -27
  19. package/dist/backend/external-c.js +5 -40
  20. package/dist/backend/external-c.js.map +1 -1
  21. package/dist/backend/ffi-callbacks.d.ts +2 -3
  22. package/dist/backend/ffi-callbacks.js +2 -3
  23. package/dist/backend/ffi-callbacks.js.map +1 -1
  24. package/dist/backend/library-identity-markers.d.ts +4 -9
  25. package/dist/backend/library-identity-markers.js +11 -49
  26. package/dist/backend/library-identity-markers.js.map +1 -1
  27. package/dist/backend/library-pack.d.ts +5 -0
  28. package/dist/backend/library-pack.js +123 -0
  29. package/dist/backend/library-pack.js.map +1 -0
  30. package/dist/backend/link-plan-core.d.ts +20 -0
  31. package/dist/backend/link-plan-core.js +18 -0
  32. package/dist/backend/link-plan-core.js.map +1 -0
  33. package/dist/backend/link-plan.d.ts +1 -1
  34. package/dist/backend/link-plan.js +16 -17
  35. package/dist/backend/link-plan.js.map +1 -1
  36. package/dist/backend/link-trace.d.ts +2 -0
  37. package/dist/backend/link-trace.js +24 -0
  38. package/dist/backend/link-trace.js.map +1 -0
  39. package/dist/backend/linker.js +1 -1
  40. package/dist/backend/linker.js.map +1 -1
  41. package/dist/backend/literals.d.ts +9 -0
  42. package/dist/backend/literals.js +35 -0
  43. package/dist/backend/literals.js.map +1 -0
  44. package/dist/backend/literals.test.d.ts +1 -0
  45. package/dist/backend/literals.test.js +53 -0
  46. package/dist/backend/literals.test.js.map +1 -0
  47. package/dist/backend/llvm/blocks.d.ts +2 -0
  48. package/dist/backend/llvm/blocks.js +9 -4
  49. package/dist/backend/llvm/blocks.js.map +1 -1
  50. package/dist/backend/llvm/blocks.test.d.ts +1 -0
  51. package/dist/backend/llvm/blocks.test.js +91 -0
  52. package/dist/backend/llvm/blocks.test.js.map +1 -0
  53. package/dist/backend/llvm/classes.d.ts +14 -8
  54. package/dist/backend/llvm/classes.js +70 -28
  55. package/dist/backend/llvm/classes.js.map +1 -1
  56. package/dist/backend/llvm/common.d.ts +5 -1
  57. package/dist/backend/llvm/common.js +45 -4
  58. package/dist/backend/llvm/common.js.map +1 -1
  59. package/dist/backend/llvm/debug-info.js +2 -7
  60. package/dist/backend/llvm/debug-info.js.map +1 -1
  61. package/dist/backend/llvm/debug-info.test.d.ts +1 -0
  62. package/dist/backend/llvm/debug-info.test.js +110 -0
  63. package/dist/backend/llvm/debug-info.test.js.map +1 -0
  64. package/dist/backend/llvm/dyn.d.ts +16 -2
  65. package/dist/backend/llvm/dyn.js +494 -165
  66. package/dist/backend/llvm/dyn.js.map +1 -1
  67. package/dist/backend/llvm/emitter.d.ts +596 -1
  68. package/dist/backend/llvm/emitter.js +695 -410
  69. package/dist/backend/llvm/emitter.js.map +1 -1
  70. package/dist/backend/llvm/emitter.test.d.ts +1 -0
  71. package/dist/backend/llvm/emitter.test.js +179 -0
  72. package/dist/backend/llvm/emitter.test.js.map +1 -0
  73. package/dist/backend/llvm/expr-async.js +23 -14
  74. package/dist/backend/llvm/expr-async.js.map +1 -1
  75. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  76. package/dist/backend/llvm/expr-bytes.js +32 -14
  77. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  78. package/dist/backend/llvm/expr-callbacks.d.ts +1 -1
  79. package/dist/backend/llvm/expr-callbacks.js +25 -22
  80. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  81. package/dist/backend/llvm/expr-calls.js +136 -35
  82. package/dist/backend/llvm/expr-calls.js.map +1 -1
  83. package/dist/backend/llvm/expr-containers.d.ts +1 -1
  84. package/dist/backend/llvm/expr-containers.js +43 -14
  85. package/dist/backend/llvm/expr-containers.js.map +1 -1
  86. package/dist/backend/llvm/expr-context.d.ts +5 -260
  87. package/dist/backend/llvm/expr-control.js +9 -5
  88. package/dist/backend/llvm/expr-control.js.map +1 -1
  89. package/dist/backend/llvm/expr-dispatch.d.ts +1 -1
  90. package/dist/backend/llvm/expr-dispatch.js +2 -0
  91. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  92. package/dist/backend/llvm/expr-dynamic.js +62 -74
  93. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  94. package/dist/backend/llvm/expr-island.d.ts +1 -1
  95. package/dist/backend/llvm/expr-island.js +11 -7
  96. package/dist/backend/llvm/expr-island.js.map +1 -1
  97. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  98. package/dist/backend/llvm/expr-primitives.js +51 -30
  99. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  100. package/dist/backend/llvm/expr-primitives.test.js +1 -1
  101. package/dist/backend/llvm/expr-records.d.ts +5 -1
  102. package/dist/backend/llvm/expr-records.js +43 -4
  103. package/dist/backend/llvm/expr-records.js.map +1 -1
  104. package/dist/backend/llvm/expr-stream-bridges.d.ts +4 -4
  105. package/dist/backend/llvm/expr-stream-bridges.js +102 -121
  106. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  107. package/dist/backend/llvm/expr-stream-bridges.test.d.ts +1 -0
  108. package/dist/backend/llvm/expr-stream-bridges.test.js +55 -0
  109. package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -0
  110. package/dist/backend/llvm/expr-stream-callbacks.d.ts +1 -1
  111. package/dist/backend/llvm/expr-stream-callbacks.js +12 -12
  112. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  113. package/dist/backend/llvm/layouts.d.ts +19 -0
  114. package/dist/backend/llvm/layouts.js +12 -0
  115. package/dist/backend/llvm/layouts.js.map +1 -0
  116. package/dist/backend/llvm/lib-abi.d.ts +19 -0
  117. package/dist/backend/llvm/lib-abi.js +82 -0
  118. package/dist/backend/llvm/lib-abi.js.map +1 -0
  119. package/dist/backend/llvm/lib-connect.d.ts +2 -0
  120. package/dist/backend/llvm/lib-connect.js +51 -0
  121. package/dist/backend/llvm/lib-connect.js.map +1 -0
  122. package/dist/backend/llvm/lib-datagram.d.ts +2 -0
  123. package/dist/backend/llvm/lib-datagram.js +135 -0
  124. package/dist/backend/llvm/lib-datagram.js.map +1 -0
  125. package/dist/backend/llvm/lib-dispatch.js +50 -6
  126. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  127. package/dist/backend/llvm/lib-filesystem.js +30 -18
  128. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  129. package/dist/backend/llvm/lib-http2.d.ts +2 -0
  130. package/dist/backend/llvm/lib-http2.js +215 -0
  131. package/dist/backend/llvm/lib-http2.js.map +1 -0
  132. package/dist/backend/llvm/lib-lookup.d.ts +2 -0
  133. package/dist/backend/llvm/lib-lookup.js +39 -0
  134. package/dist/backend/llvm/lib-lookup.js.map +1 -0
  135. package/dist/backend/llvm/lib-network.js +25 -2
  136. package/dist/backend/llvm/lib-network.js.map +1 -1
  137. package/dist/backend/llvm/lib-process.js +21 -16
  138. package/dist/backend/llvm/lib-process.js.map +1 -1
  139. package/dist/backend/llvm/lib-shared.js +117 -1
  140. package/dist/backend/llvm/lib-shared.js.map +1 -1
  141. package/dist/backend/llvm/lib-stream.js +3 -2
  142. package/dist/backend/llvm/lib-stream.js.map +1 -1
  143. package/dist/backend/llvm/lib-test.d.ts +2 -0
  144. package/dist/backend/llvm/lib-test.js +34 -0
  145. package/dist/backend/llvm/lib-test.js.map +1 -0
  146. package/dist/backend/llvm/lib-tls.d.ts +2 -0
  147. package/dist/backend/llvm/lib-tls.js +82 -0
  148. package/dist/backend/llvm/lib-tls.js.map +1 -0
  149. package/dist/backend/llvm/shapes.d.ts +10 -9
  150. package/dist/backend/llvm/shapes.js +35 -130
  151. package/dist/backend/llvm/shapes.js.map +1 -1
  152. package/dist/backend/llvm/unsupported.d.ts +1 -0
  153. package/dist/backend/llvm/unsupported.js +1 -2
  154. package/dist/backend/llvm/unsupported.js.map +1 -1
  155. package/dist/backend/llvm/walkers.js +1 -7
  156. package/dist/backend/llvm/walkers.js.map +1 -1
  157. package/dist/backend/mangle.d.ts +2 -2
  158. package/dist/backend/mangle.js +16 -5
  159. package/dist/backend/mangle.js.map +1 -1
  160. package/dist/backend/{c/may-throw.d.ts → may-throw.d.ts} +1 -1
  161. package/dist/backend/{c/may-throw.js → may-throw.js} +51 -46
  162. package/dist/backend/may-throw.js.map +1 -0
  163. package/dist/backend/may-throw.test.d.ts +1 -0
  164. package/dist/backend/may-throw.test.js +58 -0
  165. package/dist/backend/may-throw.test.js.map +1 -0
  166. package/dist/backend/native-codegen-core.d.ts +22 -0
  167. package/dist/backend/native-codegen-core.js +44 -0
  168. package/dist/backend/native-codegen-core.js.map +1 -0
  169. package/dist/backend/native-codegen.d.ts +3 -19
  170. package/dist/backend/native-codegen.js +5 -44
  171. package/dist/backend/native-codegen.js.map +1 -1
  172. package/dist/backend/native-codegen.test.js +2 -2
  173. package/dist/backend/native-codegen.test.js.map +1 -1
  174. package/dist/backend/native-link-info-core.d.ts +94 -0
  175. package/dist/backend/native-link-info-core.js +62 -0
  176. package/dist/backend/native-link-info-core.js.map +1 -0
  177. package/dist/backend/native-link-info.d.ts +3 -88
  178. package/dist/backend/native-link-info.js +12 -243
  179. package/dist/backend/native-link-info.js.map +1 -1
  180. package/dist/backend/native-link-info.test.js +41 -146
  181. package/dist/backend/native-link-info.test.js.map +1 -1
  182. package/dist/backend/native-toolchain.d.ts +10 -25
  183. package/dist/backend/native-toolchain.js +31 -211
  184. package/dist/backend/native-toolchain.js.map +1 -1
  185. package/dist/backend/native-toolchain.test.js +5 -5
  186. package/dist/backend/native-toolchain.test.js.map +1 -1
  187. package/dist/backend/native-tools.d.ts +24 -0
  188. package/dist/backend/native-tools.js +33 -0
  189. package/dist/backend/native-tools.js.map +1 -0
  190. package/dist/backend/object-localize.js +19 -8
  191. package/dist/backend/object-localize.js.map +1 -1
  192. package/dist/backend/object-localize.test.js +22 -0
  193. package/dist/backend/object-localize.test.js.map +1 -1
  194. package/dist/backend/runtime-abi.d.ts +2 -2
  195. package/dist/backend/runtime-abi.js +2 -2
  196. package/dist/backend/runtime-pack-core.d.ts +101 -0
  197. package/dist/backend/runtime-pack-core.js +172 -0
  198. package/dist/backend/runtime-pack-core.js.map +1 -0
  199. package/dist/backend/runtime-pack-native.d.ts +21 -0
  200. package/dist/backend/runtime-pack-native.js +70 -0
  201. package/dist/backend/runtime-pack-native.js.map +1 -0
  202. package/dist/backend/runtime-pack-targets.test.js +1 -1
  203. package/dist/backend/runtime-pack.d.ts +3 -83
  204. package/dist/backend/runtime-pack.js +8 -151
  205. package/dist/backend/runtime-pack.js.map +1 -1
  206. package/dist/backend/runtime-pack.test.js +24 -1
  207. package/dist/backend/runtime-pack.test.js.map +1 -1
  208. package/dist/backend/target-diagnostics.d.ts +22 -0
  209. package/dist/backend/target-diagnostics.js +108 -0
  210. package/dist/backend/target-diagnostics.js.map +1 -0
  211. package/dist/backend/target-platform.d.ts +15 -0
  212. package/dist/backend/target-platform.js +72 -0
  213. package/dist/backend/target-platform.js.map +1 -0
  214. package/dist/backend/targets.d.ts +13 -4
  215. package/dist/backend/targets.js +95 -30
  216. package/dist/backend/targets.js.map +1 -1
  217. package/dist/backend/targets.test.js +65 -4
  218. package/dist/backend/targets.test.js.map +1 -1
  219. package/dist/backend/toolchain-environment.d.ts +6 -0
  220. package/dist/backend/toolchain-environment.js +99 -0
  221. package/dist/backend/toolchain-environment.js.map +1 -0
  222. package/dist/backend/vendor-archives.d.ts +1 -9
  223. package/dist/backend/vendor-archives.js +2 -9
  224. package/dist/backend/vendor-archives.js.map +1 -1
  225. package/dist/backend/vendor-inputs.d.ts +9 -0
  226. package/dist/backend/vendor-inputs.js +10 -0
  227. package/dist/backend/vendor-inputs.js.map +1 -0
  228. package/dist/backend/warm-cache.d.ts +3 -0
  229. package/dist/backend/warm-cache.js +41 -0
  230. package/dist/backend/warm-cache.js.map +1 -0
  231. package/dist/cli/command.d.ts +3 -0
  232. package/dist/cli/command.js +328 -0
  233. package/dist/cli/command.js.map +1 -0
  234. package/dist/cli/host.d.ts +24 -0
  235. package/dist/cli/host.js +2 -0
  236. package/dist/cli/host.js.map +1 -0
  237. package/dist/cli/output-options.d.ts +26 -0
  238. package/dist/cli/output-options.js +60 -0
  239. package/dist/cli/output-options.js.map +1 -0
  240. package/dist/cli/paths.d.ts +12 -0
  241. package/dist/cli/paths.js +42 -0
  242. package/dist/cli/paths.js.map +1 -0
  243. package/dist/cli/usage.d.ts +74 -0
  244. package/dist/cli/usage.js +86 -0
  245. package/dist/cli/usage.js.map +1 -0
  246. package/dist/cli/wasi-paths.d.ts +8 -0
  247. package/dist/cli/wasi-paths.js +41 -0
  248. package/dist/cli/wasi-paths.js.map +1 -0
  249. package/dist/cli/wasi-runner.d.ts +2 -0
  250. package/dist/cli/wasi-runner.js +28 -0
  251. package/dist/cli/wasi-runner.js.map +1 -0
  252. package/dist/compat/fetch-profile.js +3 -3
  253. package/dist/compat/fetch-profile.js.map +1 -1
  254. package/dist/compile-types.d.ts +162 -0
  255. package/dist/compile-types.js +2 -0
  256. package/dist/compile-types.js.map +1 -0
  257. package/dist/coverage/report.js +11 -5
  258. package/dist/coverage/report.js.map +1 -1
  259. package/dist/coverage/surface-manifest.js +5 -2
  260. package/dist/coverage/surface-manifest.js.map +1 -1
  261. package/dist/diagnostics/diagnostic.js +3 -3
  262. package/dist/diagnostics/diagnostic.js.map +1 -1
  263. package/dist/executable/executable-cache.d.ts +3 -4
  264. package/dist/executable/executable-cache.js +5 -6
  265. package/dist/executable/executable-cache.js.map +1 -1
  266. package/dist/executable/executable-cache.test.js +16 -17
  267. package/dist/executable/executable-cache.test.js.map +1 -1
  268. package/dist/executable/prepare.d.ts +14 -0
  269. package/dist/executable/prepare.js +66 -0
  270. package/dist/executable/prepare.js.map +1 -0
  271. package/dist/ffi/ffi-manifest.d.ts +11 -4
  272. package/dist/ffi/ffi-manifest.js +170 -58
  273. package/dist/ffi/ffi-manifest.js.map +1 -1
  274. package/dist/ffi/ffi-manifest.test.d.ts +1 -0
  275. package/dist/ffi/ffi-manifest.test.js +111 -0
  276. package/dist/ffi/ffi-manifest.test.js.map +1 -0
  277. package/dist/format-integer.d.ts +3 -0
  278. package/dist/format-integer.js +14 -0
  279. package/dist/format-integer.js.map +1 -0
  280. package/dist/frontend/analysis.d.ts +5 -0
  281. package/dist/frontend/analysis.js +88 -0
  282. package/dist/frontend/analysis.js.map +1 -0
  283. package/dist/frontend/arguments-usage.d.ts +1 -3
  284. package/dist/frontend/arguments-usage.js.map +1 -1
  285. package/dist/frontend/builtin-modules.d.ts +1 -1
  286. package/dist/frontend/builtin-modules.js +1 -1
  287. package/dist/frontend/builtin-modules.js.map +1 -1
  288. package/dist/frontend/cjs-lexer.d.ts +4 -25
  289. package/dist/frontend/cjs-lexer.js +6 -806
  290. package/dist/frontend/cjs-lexer.js.map +1 -1
  291. package/dist/frontend/cjs-resolve.d.ts +5 -0
  292. package/dist/frontend/cjs-resolve.js +429 -0
  293. package/dist/frontend/cjs-resolve.js.map +1 -0
  294. package/dist/frontend/cjs-syntax.d.ts +26 -0
  295. package/dist/frontend/cjs-syntax.js +806 -0
  296. package/dist/frontend/cjs-syntax.js.map +1 -0
  297. package/dist/frontend/comptime-node.d.ts +4 -0
  298. package/dist/frontend/comptime-node.js +16 -0
  299. package/dist/frontend/comptime-node.js.map +1 -0
  300. package/dist/frontend/comptime-node.test.d.ts +1 -0
  301. package/dist/frontend/comptime-node.test.js +37 -0
  302. package/dist/frontend/comptime-node.test.js.map +1 -0
  303. package/dist/frontend/dts-paths.d.ts +3 -4
  304. package/dist/frontend/dts-paths.js +15 -8
  305. package/dist/frontend/dts-paths.js.map +1 -1
  306. package/dist/frontend/fork-target.js +2 -2
  307. package/dist/frontend/fork-target.js.map +1 -1
  308. package/dist/frontend/helper-tokens.d.ts +1 -0
  309. package/dist/frontend/helper-tokens.js +147 -0
  310. package/dist/frontend/helper-tokens.js.map +1 -0
  311. package/dist/frontend/helper-tokens.test.d.ts +1 -0
  312. package/dist/frontend/helper-tokens.test.js +35 -0
  313. package/dist/frontend/helper-tokens.test.js.map +1 -0
  314. package/dist/frontend/inferred-js-diagnostics.d.ts +5 -0
  315. package/dist/frontend/inferred-js-diagnostics.js +69 -0
  316. package/dist/frontend/inferred-js-diagnostics.js.map +1 -0
  317. package/dist/frontend/inferred-js-diagnostics.test.d.ts +1 -0
  318. package/dist/frontend/inferred-js-diagnostics.test.js +58 -0
  319. package/dist/frontend/inferred-js-diagnostics.test.js.map +1 -0
  320. package/dist/frontend/input-tracker.d.ts +5 -2
  321. package/dist/frontend/input-tracker.js +24 -3
  322. package/dist/frontend/input-tracker.js.map +1 -1
  323. package/dist/frontend/input-tracker.test.js +23 -0
  324. package/dist/frontend/input-tracker.test.js.map +1 -1
  325. package/dist/frontend/lib-contract.js +32 -24
  326. package/dist/frontend/lib-contract.js.map +1 -1
  327. package/dist/frontend/literal-values.d.ts +4 -0
  328. package/dist/frontend/literal-values.js +21 -0
  329. package/dist/frontend/literal-values.js.map +1 -0
  330. package/dist/frontend/literal-values.test.d.ts +1 -0
  331. package/dist/frontend/literal-values.test.js +91 -0
  332. package/dist/frontend/literal-values.test.js.map +1 -0
  333. package/dist/frontend/lowering/abstract-equality.d.ts +1 -1
  334. package/dist/frontend/lowering/abstract-equality.js +3 -0
  335. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  336. package/dist/frontend/lowering/array-indexed-mutation.js +11 -6
  337. package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -1
  338. package/dist/frontend/lowering/array-values.d.ts +4 -1
  339. package/dist/frontend/lowering/array-values.js +43 -1
  340. package/dist/frontend/lowering/array-values.js.map +1 -1
  341. package/dist/frontend/lowering/callback-arg.js +1 -0
  342. package/dist/frontend/lowering/callback-arg.js.map +1 -1
  343. package/dist/frontend/lowering/class-callbacks.d.ts +12 -0
  344. package/dist/frontend/lowering/class-callbacks.js +63 -0
  345. package/dist/frontend/lowering/class-callbacks.js.map +1 -0
  346. package/dist/frontend/lowering/class-descriptors.d.ts +9 -0
  347. package/dist/frontend/lowering/class-descriptors.js +73 -0
  348. package/dist/frontend/lowering/class-descriptors.js.map +1 -0
  349. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +41 -0
  350. package/dist/frontend/lowering/class-dynamic-dispatch.js +803 -0
  351. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -0
  352. package/dist/frontend/lowering/class-dynamic-dispatch.test.d.ts +1 -0
  353. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +92 -0
  354. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -0
  355. package/dist/frontend/lowering/class-instance-constructor.d.ts +7 -0
  356. package/dist/frontend/lowering/class-instance-constructor.js +64 -0
  357. package/dist/frontend/lowering/class-instance-constructor.js.map +1 -0
  358. package/dist/frontend/lowering/class-method-values.d.ts +10 -0
  359. package/dist/frontend/lowering/class-method-values.js +170 -0
  360. package/dist/frontend/lowering/class-method-values.js.map +1 -0
  361. package/dist/frontend/lowering/class-prototypes.d.ts +16 -0
  362. package/dist/frontend/lowering/class-prototypes.js +134 -0
  363. package/dist/frontend/lowering/class-prototypes.js.map +1 -0
  364. package/dist/frontend/lowering/class-static-data.d.ts +6 -0
  365. package/dist/frontend/lowering/class-static-data.js +56 -0
  366. package/dist/frontend/lowering/class-static-data.js.map +1 -0
  367. package/dist/frontend/lowering/containers/buffer-numeric-methods.d.ts +4 -0
  368. package/dist/frontend/lowering/containers/buffer-numeric-methods.js +26 -0
  369. package/dist/frontend/lowering/containers/buffer-numeric-methods.js.map +1 -0
  370. package/dist/frontend/lowering/containers/bytes.d.ts +11 -7
  371. package/dist/frontend/lowering/containers/bytes.js +164 -155
  372. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  373. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +2 -2
  374. package/dist/frontend/lowering/containers/string-and-regexp.js +39 -16
  375. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  376. package/dist/frontend/lowering/containers/string-replacement.d.ts +7 -0
  377. package/dist/frontend/lowering/containers/string-replacement.js +217 -0
  378. package/dist/frontend/lowering/containers/string-replacement.js.map +1 -0
  379. package/dist/frontend/lowering/error-methods.d.ts +11 -0
  380. package/dist/frontend/lowering/error-methods.js +78 -0
  381. package/dist/frontend/lowering/error-methods.js.map +1 -0
  382. package/dist/frontend/lowering/expressions/global-symbols.d.ts +6 -0
  383. package/dist/frontend/lowering/expressions/global-symbols.js +11 -0
  384. package/dist/frontend/lowering/expressions/global-symbols.js.map +1 -0
  385. package/dist/frontend/lowering/expressions/native-proxy.d.ts +7 -0
  386. package/dist/frontend/lowering/expressions/native-proxy.js +49 -0
  387. package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -0
  388. package/dist/frontend/lowering/expressions/nullish-assignment.d.ts +6 -0
  389. package/dist/frontend/lowering/expressions/nullish-assignment.js +124 -0
  390. package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -0
  391. package/dist/frontend/lowering/expressions/object-literals.d.ts +8 -8
  392. package/dist/frontend/lowering/expressions/object-literals.js +473 -178
  393. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  394. package/dist/frontend/lowering/expressions/optional-chains.js +20 -8
  395. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  396. package/dist/frontend/lowering/expressions/try-lower-expression.js +1 -0
  397. package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -1
  398. package/dist/frontend/lowering/expressions/union-field-write.d.ts +8 -0
  399. package/dist/frontend/lowering/expressions/union-field-write.js +71 -0
  400. package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -0
  401. package/dist/frontend/lowering/function-context.d.ts +93 -0
  402. package/dist/frontend/lowering/function-context.js +103 -0
  403. package/dist/frontend/lowering/function-context.js.map +1 -0
  404. package/dist/frontend/lowering/generator-delegation.d.ts +7 -0
  405. package/dist/frontend/lowering/generator-delegation.js +85 -0
  406. package/dist/frontend/lowering/generator-delegation.js.map +1 -0
  407. package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +10 -0
  408. package/dist/frontend/lowering/generator-dynamic-dispatch.js +105 -0
  409. package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -0
  410. package/dist/frontend/lowering/lib-boundary.d.ts +2 -1
  411. package/dist/frontend/lowering/lib-boundary.js +59 -20
  412. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  413. package/dist/frontend/lowering/lib-boundary.test.d.ts +1 -0
  414. package/dist/frontend/lowering/lib-boundary.test.js +45 -0
  415. package/dist/frontend/lowering/lib-boundary.test.js.map +1 -0
  416. package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
  417. package/dist/frontend/lowering/lower-assert.d.ts +1 -1
  418. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  419. package/dist/frontend/lowering/lower-async-iteration.d.ts +1 -1
  420. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
  421. package/dist/frontend/lowering/lower-builtin-values.d.ts +12 -0
  422. package/dist/frontend/lowering/lower-builtin-values.js +139 -0
  423. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -0
  424. package/dist/frontend/lowering/lower-builtins.d.ts +27 -41
  425. package/dist/frontend/lowering/lower-builtins.js +539 -343
  426. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  427. package/dist/frontend/lowering/lower-calls.d.ts +20 -17
  428. package/dist/frontend/lowering/lower-calls.js +1236 -353
  429. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  430. package/dist/frontend/lowering/lower-classes.d.ts +83 -45
  431. package/dist/frontend/lowering/lower-classes.js +952 -339
  432. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  433. package/dist/frontend/lowering/lower-comptime.d.ts +1 -1
  434. package/dist/frontend/lowering/lower-comptime.js +23 -32
  435. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  436. package/dist/frontend/lowering/lower-containers.d.ts +27 -32
  437. package/dist/frontend/lowering/lower-containers.js +796 -151
  438. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  439. package/dist/frontend/lowering/lower-dgram.d.ts +1 -1
  440. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  441. package/dist/frontend/lowering/lower-enums.d.ts +1 -1
  442. package/dist/frontend/lowering/lower-enums.js.map +1 -1
  443. package/dist/frontend/lowering/lower-event-emitter.d.ts +30 -1
  444. package/dist/frontend/lowering/lower-event-emitter.js +58 -26
  445. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  446. package/dist/frontend/lowering/lower-expando.d.ts +7 -1
  447. package/dist/frontend/lowering/lower-expando.js +14 -6
  448. package/dist/frontend/lowering/lower-expando.js.map +1 -1
  449. package/dist/frontend/lowering/lower-exprs.d.ts +35 -11
  450. package/dist/frontend/lowering/lower-exprs.js +1674 -779
  451. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  452. package/dist/frontend/lowering/lower-generators.d.ts +2 -5
  453. package/dist/frontend/lowering/lower-generators.js +77 -46
  454. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  455. package/dist/frontend/lowering/lower-global-value.d.ts +7 -0
  456. package/dist/frontend/lowering/lower-global-value.js +17 -0
  457. package/dist/frontend/lowering/lower-global-value.js.map +1 -0
  458. package/dist/frontend/lowering/lower-inspect.d.ts +1 -1
  459. package/dist/frontend/lowering/lower-inspect.js +17 -8
  460. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  461. package/dist/frontend/lowering/lower-island.d.ts +5 -8
  462. package/dist/frontend/lowering/lower-island.js +34 -23
  463. package/dist/frontend/lowering/lower-island.js.map +1 -1
  464. package/dist/frontend/lowering/lower-mixins.d.ts +2 -2
  465. package/dist/frontend/lowering/lower-mixins.js +5 -3
  466. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  467. package/dist/frontend/lowering/lower-modules.d.ts +1 -1
  468. package/dist/frontend/lowering/lower-modules.js +233 -30
  469. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  470. package/dist/frontend/lowering/lower-namespaces.d.ts +1 -1
  471. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  472. package/dist/frontend/lowering/lower-node-module.d.ts +1 -1
  473. package/dist/frontend/lowering/lower-node-module.js +13 -10
  474. package/dist/frontend/lowering/lower-node-module.js.map +1 -1
  475. package/dist/frontend/lowering/lower-server.d.ts +1 -1
  476. package/dist/frontend/lowering/lower-server.js +6 -10
  477. package/dist/frontend/lowering/lower-server.js.map +1 -1
  478. package/dist/frontend/lowering/lower-stmts.d.ts +10 -14
  479. package/dist/frontend/lowering/lower-stmts.js +728 -496
  480. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  481. package/dist/frontend/lowering/lower-stream.d.ts +1 -1
  482. package/dist/frontend/lowering/lower-stream.js +19 -10
  483. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  484. package/dist/frontend/lowering/lower-test.d.ts +1 -1
  485. package/dist/frontend/lowering/lower-test.js.map +1 -1
  486. package/dist/frontend/lowering/lower-url.d.ts +6 -0
  487. package/dist/frontend/lowering/lower-url.js +77 -0
  488. package/dist/frontend/lowering/lower-url.js.map +1 -0
  489. package/dist/frontend/lowering/lowerer.d.ts +113 -189
  490. package/dist/frontend/lowering/lowerer.js +1031 -672
  491. package/dist/frontend/lowering/lowerer.js.map +1 -1
  492. package/dist/frontend/lowering/lowerer.test.d.ts +1 -0
  493. package/dist/frontend/lowering/lowerer.test.js +26 -0
  494. package/dist/frontend/lowering/lowerer.test.js.map +1 -0
  495. package/dist/frontend/lowering/native-ffi.d.ts +5 -0
  496. package/dist/frontend/lowering/native-ffi.js +40 -0
  497. package/dist/frontend/lowering/native-ffi.js.map +1 -0
  498. package/dist/frontend/lowering/object-enumeration-receiver.d.ts +7 -0
  499. package/dist/frontend/lowering/object-enumeration-receiver.js +33 -0
  500. package/dist/frontend/lowering/object-enumeration-receiver.js.map +1 -0
  501. package/dist/frontend/lowering/object-factory-new.d.ts +8 -0
  502. package/dist/frontend/lowering/object-factory-new.js +68 -0
  503. package/dist/frontend/lowering/object-factory-new.js.map +1 -0
  504. package/dist/frontend/lowering/optional-arguments.js +6 -1
  505. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  506. package/dist/frontend/lowering/pairs-snapshot.js +2 -1
  507. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  508. package/dist/frontend/lowering/runtime-optional-locals.d.ts +14 -0
  509. package/dist/frontend/lowering/runtime-optional-locals.js +41 -0
  510. package/dist/frontend/lowering/runtime-optional-locals.js.map +1 -0
  511. package/dist/frontend/lowering/runtime-optional-locals.test.d.ts +1 -0
  512. package/dist/frontend/lowering/runtime-optional-locals.test.js +85 -0
  513. package/dist/frontend/lowering/runtime-optional-locals.test.js.map +1 -0
  514. package/dist/frontend/lowering/sanitize-class-types.d.ts +8 -0
  515. package/dist/frontend/lowering/sanitize-class-types.js +79 -0
  516. package/dist/frontend/lowering/sanitize-class-types.js.map +1 -0
  517. package/dist/frontend/lowering/sanitize-class-types.test.d.ts +1 -0
  518. package/dist/frontend/lowering/sanitize-class-types.test.js +60 -0
  519. package/dist/frontend/lowering/sanitize-class-types.test.js.map +1 -0
  520. package/dist/frontend/lowering/static-this.d.ts +5 -1
  521. package/dist/frontend/lowering/static-this.js +28 -2
  522. package/dist/frontend/lowering/static-this.js.map +1 -1
  523. package/dist/frontend/lowering/string-index.d.ts +7 -0
  524. package/dist/frontend/lowering/string-index.js +39 -0
  525. package/dist/frontend/lowering/string-index.js.map +1 -0
  526. package/dist/frontend/lowering/surfaces.d.ts +8 -3
  527. package/dist/frontend/lowering/surfaces.js +61 -29
  528. package/dist/frontend/lowering/surfaces.js.map +1 -1
  529. package/dist/frontend/lowering/symbol-fields.d.ts +22 -0
  530. package/dist/frontend/lowering/symbol-fields.js +183 -0
  531. package/dist/frontend/lowering/symbol-fields.js.map +1 -0
  532. package/dist/frontend/lowering/symbol-methods.d.ts +7 -0
  533. package/dist/frontend/lowering/symbol-methods.js +30 -0
  534. package/dist/frontend/lowering/symbol-methods.js.map +1 -0
  535. package/dist/frontend/lowering/text-decoder-encoding.d.ts +10 -0
  536. package/dist/frontend/lowering/text-decoder-encoding.js +77 -0
  537. package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -0
  538. package/dist/frontend/lowering/text-decoder-encoding.test.d.ts +1 -0
  539. package/dist/frontend/lowering/text-decoder-encoding.test.js +38 -0
  540. package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -0
  541. package/dist/frontend/lowering/union-narrow.d.ts +9 -0
  542. package/dist/frontend/lowering/union-narrow.js +59 -0
  543. package/dist/frontend/lowering/union-narrow.js.map +1 -0
  544. package/dist/frontend/lowering/union-narrow.test.d.ts +1 -0
  545. package/dist/frontend/lowering/union-narrow.test.js +72 -0
  546. package/dist/frontend/lowering/union-narrow.test.js.map +1 -0
  547. package/dist/frontend/lowering/union-retag.d.ts +43 -0
  548. package/dist/frontend/lowering/union-retag.js +259 -0
  549. package/dist/frontend/lowering/union-retag.js.map +1 -0
  550. package/dist/frontend/lowering/union-retag.test.d.ts +1 -0
  551. package/dist/frontend/lowering/union-retag.test.js +405 -0
  552. package/dist/frontend/lowering/union-retag.test.js.map +1 -0
  553. package/dist/frontend/lowering/unregistered-class-types.d.ts +14 -0
  554. package/dist/frontend/lowering/unregistered-class-types.js +94 -0
  555. package/dist/frontend/lowering/unregistered-class-types.js.map +1 -0
  556. package/dist/frontend/lowering/unregistered-class-types.test.d.ts +1 -0
  557. package/dist/frontend/lowering/unregistered-class-types.test.js +65 -0
  558. package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -0
  559. package/dist/frontend/lowering/width-lift.d.ts +40 -0
  560. package/dist/frontend/lowering/width-lift.js +2 -0
  561. package/dist/frontend/lowering/width-lift.js.map +1 -0
  562. package/dist/frontend/module-syntax.d.ts +58 -0
  563. package/dist/frontend/module-syntax.js +123 -0
  564. package/dist/frontend/module-syntax.js.map +1 -0
  565. package/dist/frontend/module-syntax.test.d.ts +1 -0
  566. package/dist/frontend/module-syntax.test.js +78 -0
  567. package/dist/frontend/module-syntax.test.js.map +1 -0
  568. package/dist/frontend/npm-fetch-analysis.d.ts +17 -0
  569. package/dist/frontend/npm-fetch-analysis.js +100 -0
  570. package/dist/frontend/npm-fetch-analysis.js.map +1 -0
  571. package/dist/frontend/npm-fetch-analysis.test.d.ts +1 -0
  572. package/dist/frontend/npm-fetch-analysis.test.js +107 -0
  573. package/dist/frontend/npm-fetch-analysis.test.js.map +1 -0
  574. package/dist/frontend/npm-fetch-node.d.ts +2 -0
  575. package/dist/frontend/npm-fetch-node.js +12 -0
  576. package/dist/frontend/npm-fetch-node.js.map +1 -0
  577. package/dist/frontend/npm-fetch-syntax.d.ts +6 -0
  578. package/dist/frontend/npm-fetch-syntax.js +207 -0
  579. package/dist/frontend/npm-fetch-syntax.js.map +1 -0
  580. package/dist/frontend/npm-node.d.ts +10 -0
  581. package/dist/frontend/npm-node.js +14 -0
  582. package/dist/frontend/npm-node.js.map +1 -0
  583. package/dist/frontend/npm-static-bundled-cjs.d.ts +8 -0
  584. package/dist/frontend/npm-static-bundled-cjs.js +236 -0
  585. package/dist/frontend/npm-static-bundled-cjs.js.map +1 -0
  586. package/dist/frontend/npm-static-bundled-cjs.test.d.ts +1 -0
  587. package/dist/frontend/npm-static-bundled-cjs.test.js +69 -0
  588. package/dist/frontend/npm-static-bundled-cjs.test.js.map +1 -0
  589. package/dist/frontend/npm-static-declaration-syntax.d.ts +57 -0
  590. package/dist/frontend/npm-static-declaration-syntax.js +944 -0
  591. package/dist/frontend/npm-static-declaration-syntax.js.map +1 -0
  592. package/dist/frontend/npm-static-declarations.d.ts +11 -39
  593. package/dist/frontend/npm-static-declarations.js +22 -561
  594. package/dist/frontend/npm-static-declarations.js.map +1 -1
  595. package/dist/frontend/npm-static-declarations.test.js +245 -6
  596. package/dist/frontend/npm-static-declarations.test.js.map +1 -1
  597. package/dist/frontend/npm-static-prune.d.ts +1 -1
  598. package/dist/frontend/npm-static-prune.js +5 -4
  599. package/dist/frontend/npm-static-prune.js.map +1 -1
  600. package/dist/frontend/npm-static-prune.test.js +1 -1
  601. package/dist/frontend/npm-static-prune.test.js.map +1 -1
  602. package/dist/frontend/npm-static-rewrite-host.d.ts +4 -0
  603. package/dist/frontend/npm-static-rewrite-host.js +179 -0
  604. package/dist/frontend/npm-static-rewrite-host.js.map +1 -0
  605. package/dist/frontend/npm-static-rewrite-syntax.d.ts +16 -0
  606. package/dist/frontend/npm-static-rewrite-syntax.js +905 -0
  607. package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -0
  608. package/dist/frontend/npm-static-rewrite.d.ts +0 -5
  609. package/dist/frontend/npm-static-rewrite.js +2 -1079
  610. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  611. package/dist/frontend/npm-static-subtyping.d.ts +5 -0
  612. package/dist/frontend/npm-static-subtyping.js +55 -0
  613. package/dist/frontend/npm-static-subtyping.js.map +1 -0
  614. package/dist/frontend/npm-static-subtyping.test.d.ts +1 -0
  615. package/dist/frontend/npm-static-subtyping.test.js +58 -0
  616. package/dist/frontend/npm-static-subtyping.test.js.map +1 -0
  617. package/dist/frontend/npm-static.d.ts +7 -15
  618. package/dist/frontend/npm-static.js +19 -28
  619. package/dist/frontend/npm-static.js.map +1 -1
  620. package/dist/frontend/npm.d.ts +8 -69
  621. package/dist/frontend/npm.js +21 -361
  622. package/dist/frontend/npm.js.map +1 -1
  623. package/dist/frontend/npm.test.js +2 -2
  624. package/dist/frontend/npm.test.js.map +1 -1
  625. package/dist/frontend/pipeline-native.d.ts +6 -0
  626. package/dist/frontend/pipeline-native.js +32 -0
  627. package/dist/frontend/pipeline-native.js.map +1 -0
  628. package/dist/frontend/pipeline.d.ts +54 -0
  629. package/dist/frontend/pipeline.js +382 -0
  630. package/dist/frontend/pipeline.js.map +1 -0
  631. package/dist/frontend/pipeline.test.d.ts +1 -0
  632. package/dist/frontend/pipeline.test.js +77 -0
  633. package/dist/frontend/pipeline.test.js.map +1 -0
  634. package/dist/frontend/program-analysis.d.ts +10 -0
  635. package/dist/frontend/program-analysis.js +14 -0
  636. package/dist/frontend/program-analysis.js.map +1 -0
  637. package/dist/frontend/program-node.d.ts +9 -0
  638. package/dist/frontend/program-node.js +48 -0
  639. package/dist/frontend/program-node.js.map +1 -0
  640. package/dist/frontend/program.d.ts +38 -19
  641. package/dist/frontend/program.js +344 -169
  642. package/dist/frontend/program.js.map +1 -1
  643. package/dist/frontend/program.test.js +87 -2
  644. package/dist/frontend/program.test.js.map +1 -1
  645. package/dist/frontend/provenance-core.d.ts +9 -0
  646. package/dist/frontend/provenance-core.js +435 -0
  647. package/dist/frontend/provenance-core.js.map +1 -0
  648. package/dist/frontend/provenance.d.ts +0 -4
  649. package/dist/frontend/provenance.js +4 -459
  650. package/dist/frontend/provenance.js.map +1 -1
  651. package/dist/frontend/resolve.d.ts +3 -1
  652. package/dist/frontend/resolve.js +6 -3
  653. package/dist/frontend/resolve.js.map +1 -1
  654. package/dist/frontend/runtime-resolve.d.ts +4 -3
  655. package/dist/frontend/runtime-resolve.js +22 -18
  656. package/dist/frontend/runtime-resolve.js.map +1 -1
  657. package/dist/frontend/runtime-resolve.test.js +53 -0
  658. package/dist/frontend/runtime-resolve.test.js.map +1 -1
  659. package/dist/frontend/services-node.d.ts +5 -0
  660. package/dist/frontend/services-node.js +19 -0
  661. package/dist/frontend/services-node.js.map +1 -0
  662. package/dist/frontend/services-node.test.d.ts +1 -0
  663. package/dist/frontend/services-node.test.js +70 -0
  664. package/dist/frontend/services-node.test.js.map +1 -0
  665. package/dist/frontend/services.d.ts +44 -0
  666. package/dist/frontend/services.js +90 -0
  667. package/dist/frontend/services.js.map +1 -0
  668. package/dist/frontend/services.test.d.ts +1 -0
  669. package/dist/frontend/services.test.js +174 -0
  670. package/dist/frontend/services.test.js.map +1 -0
  671. package/dist/frontend/ts7/adapter.d.ts +3 -3
  672. package/dist/frontend/ts7/adapter.js +14 -31
  673. package/dist/frontend/ts7/adapter.js.map +1 -1
  674. package/dist/frontend/ts7/api.d.ts +16 -0
  675. package/dist/frontend/ts7/api.js +24 -0
  676. package/dist/frontend/ts7/api.js.map +1 -0
  677. package/dist/frontend/ts7/ast-bytes.d.ts +31 -0
  678. package/dist/frontend/ts7/ast-bytes.js +137 -0
  679. package/dist/frontend/ts7/ast-bytes.js.map +1 -0
  680. package/dist/frontend/ts7/ast-bytes.test.d.ts +1 -0
  681. package/dist/frontend/ts7/ast-bytes.test.js +73 -0
  682. package/dist/frontend/ts7/ast-bytes.test.js.map +1 -0
  683. package/dist/frontend/ts7/ast-guards.generated.d.ts +353 -0
  684. package/dist/frontend/ts7/ast-guards.generated.js +1951 -0
  685. package/dist/frontend/ts7/ast-guards.generated.js.map +1 -0
  686. package/dist/frontend/ts7/ast-guards.test.d.ts +1 -0
  687. package/dist/frontend/ts7/ast-guards.test.js +43 -0
  688. package/dist/frontend/ts7/ast-guards.test.js.map +1 -0
  689. package/dist/frontend/ts7/ast-node.d.ts +194 -0
  690. package/dist/frontend/ts7/ast-node.js +448 -0
  691. package/dist/frontend/ts7/ast-node.js.map +1 -0
  692. package/dist/frontend/ts7/ast-node.test.d.ts +1 -0
  693. package/dist/frontend/ts7/ast-node.test.js +266 -0
  694. package/dist/frontend/ts7/ast-node.test.js.map +1 -0
  695. package/dist/frontend/ts7/ast-schema.generated.d.ts +503 -0
  696. package/dist/frontend/ts7/ast-schema.generated.js +1446 -0
  697. package/dist/frontend/ts7/ast-schema.generated.js.map +1 -0
  698. package/dist/frontend/ts7/ast-text.d.ts +6 -0
  699. package/dist/frontend/ts7/ast-text.js +124 -0
  700. package/dist/frontend/ts7/ast-text.js.map +1 -0
  701. package/dist/frontend/ts7/ast-text.test.d.ts +1 -0
  702. package/dist/frontend/ts7/ast-text.test.js +38 -0
  703. package/dist/frontend/ts7/ast-text.test.js.map +1 -0
  704. package/dist/frontend/ts7/ast-tokens.generated.d.ts +2 -0
  705. package/dist/frontend/ts7/ast-tokens.generated.js +155 -0
  706. package/dist/frontend/ts7/ast-tokens.generated.js.map +1 -0
  707. package/dist/frontend/ts7/ast-types.d.ts +42 -0
  708. package/dist/frontend/ts7/ast-types.generated.d.ts +1615 -0
  709. package/dist/frontend/ts7/ast-types.generated.js +5 -0
  710. package/dist/frontend/ts7/ast-types.generated.js.map +1 -0
  711. package/dist/frontend/ts7/ast-types.js +3 -0
  712. package/dist/frontend/ts7/ast-types.js.map +1 -0
  713. package/dist/frontend/ts7/ast-wire.d.ts +58 -0
  714. package/dist/frontend/ts7/ast-wire.js +267 -0
  715. package/dist/frontend/ts7/ast-wire.js.map +1 -0
  716. package/dist/frontend/ts7/ast-wire.test.d.ts +1 -0
  717. package/dist/frontend/ts7/ast-wire.test.js +151 -0
  718. package/dist/frontend/ts7/ast-wire.test.js.map +1 -0
  719. package/dist/frontend/ts7/ast.d.ts +14 -14
  720. package/dist/frontend/ts7/ast.js +21 -15
  721. package/dist/frontend/ts7/ast.js.map +1 -1
  722. package/dist/frontend/ts7/census-check.js +1 -0
  723. package/dist/frontend/ts7/census-check.js.map +1 -1
  724. package/dist/frontend/ts7/checker-cache.d.ts +48 -0
  725. package/dist/frontend/ts7/checker-cache.js +92 -0
  726. package/dist/frontend/ts7/checker-cache.js.map +1 -0
  727. package/dist/frontend/ts7/checker.d.ts +19 -46
  728. package/dist/frontend/ts7/checker.js +231 -161
  729. package/dist/frontend/ts7/checker.js.map +1 -1
  730. package/dist/frontend/ts7/checker.test.d.ts +1 -0
  731. package/dist/frontend/ts7/checker.test.js +335 -0
  732. package/dist/frontend/ts7/checker.test.js.map +1 -0
  733. package/dist/frontend/ts7/enums.d.ts +4 -27
  734. package/dist/frontend/ts7/enums.generated.d.ts +878 -0
  735. package/dist/frontend/ts7/enums.generated.js +1313 -0
  736. package/dist/frontend/ts7/enums.generated.js.map +1 -0
  737. package/dist/frontend/ts7/enums.js +4 -45
  738. package/dist/frontend/ts7/enums.js.map +1 -1
  739. package/dist/frontend/ts7/enums.test.d.ts +1 -0
  740. package/dist/frontend/ts7/enums.test.js +48 -0
  741. package/dist/frontend/ts7/enums.test.js.map +1 -0
  742. package/dist/frontend/ts7/native-api.d.ts +10 -0
  743. package/dist/frontend/ts7/native-api.js +10 -0
  744. package/dist/frontend/ts7/native-api.js.map +1 -0
  745. package/dist/frontend/ts7/native-host.d.ts +2 -0
  746. package/dist/frontend/ts7/native-host.js +11 -0
  747. package/dist/frontend/ts7/native-host.js.map +1 -0
  748. package/dist/frontend/ts7/native-process.d.ts +14 -0
  749. package/dist/frontend/ts7/native-process.js +62 -0
  750. package/dist/frontend/ts7/native-process.js.map +1 -0
  751. package/dist/frontend/ts7/program-adapter.d.ts +10 -98
  752. package/dist/frontend/ts7/program-adapter.js +16 -310
  753. package/dist/frontend/ts7/program-adapter.js.map +1 -1
  754. package/dist/frontend/ts7/program-host.d.ts +115 -0
  755. package/dist/frontend/ts7/program-host.js +347 -0
  756. package/dist/frontend/ts7/program-host.js.map +1 -0
  757. package/dist/frontend/ts7/program-host.test.d.ts +1 -0
  758. package/dist/frontend/ts7/program-host.test.js +207 -0
  759. package/dist/frontend/ts7/program-host.test.js.map +1 -0
  760. package/dist/frontend/ts7/rpc-api.d.ts +12 -0
  761. package/dist/frontend/ts7/rpc-api.js +23 -0
  762. package/dist/frontend/ts7/rpc-api.js.map +1 -0
  763. package/dist/frontend/ts7/rpc-client.d.ts +27 -0
  764. package/dist/frontend/ts7/rpc-client.js +102 -0
  765. package/dist/frontend/ts7/rpc-client.js.map +1 -0
  766. package/dist/frontend/ts7/rpc-client.test.d.ts +1 -0
  767. package/dist/frontend/ts7/rpc-client.test.js +133 -0
  768. package/dist/frontend/ts7/rpc-client.test.js.map +1 -0
  769. package/dist/frontend/ts7/rpc-filesystem.d.ts +16 -0
  770. package/dist/frontend/ts7/rpc-filesystem.js +42 -0
  771. package/dist/frontend/ts7/rpc-filesystem.js.map +1 -0
  772. package/dist/frontend/ts7/rpc-filesystem.test.d.ts +1 -0
  773. package/dist/frontend/ts7/rpc-filesystem.test.js +89 -0
  774. package/dist/frontend/ts7/rpc-filesystem.test.js.map +1 -0
  775. package/dist/frontend/ts7/rpc-process.d.ts +2 -0
  776. package/dist/frontend/ts7/rpc-process.js +148 -0
  777. package/dist/frontend/ts7/rpc-process.js.map +1 -0
  778. package/dist/frontend/ts7/rpc-process.test.d.ts +1 -0
  779. package/dist/frontend/ts7/rpc-process.test.js +29 -0
  780. package/dist/frontend/ts7/rpc-process.test.js.map +1 -0
  781. package/dist/frontend/ts7/rpc-wire.d.ts +46 -0
  782. package/dist/frontend/ts7/rpc-wire.js +195 -0
  783. package/dist/frontend/ts7/rpc-wire.js.map +1 -0
  784. package/dist/frontend/ts7/rpc-wire.test.d.ts +1 -0
  785. package/dist/frontend/ts7/rpc-wire.test.js +168 -0
  786. package/dist/frontend/ts7/rpc-wire.test.js.map +1 -0
  787. package/dist/frontend/ts7/semantic-checker.d.ts +137 -0
  788. package/dist/frontend/ts7/semantic-checker.js +221 -0
  789. package/dist/frontend/ts7/semantic-checker.js.map +1 -0
  790. package/dist/frontend/ts7/semantic-checker.test.d.ts +1 -0
  791. package/dist/frontend/ts7/semantic-checker.test.js +119 -0
  792. package/dist/frontend/ts7/semantic-checker.test.js.map +1 -0
  793. package/dist/frontend/ts7/semantic-json.d.ts +5 -0
  794. package/dist/frontend/ts7/semantic-json.js +52 -0
  795. package/dist/frontend/ts7/semantic-json.js.map +1 -0
  796. package/dist/frontend/ts7/semantic-json.test.d.ts +1 -0
  797. package/dist/frontend/ts7/semantic-json.test.js +42 -0
  798. package/dist/frontend/ts7/semantic-json.test.js.map +1 -0
  799. package/dist/frontend/ts7/semantic-model.d.ts +222 -0
  800. package/dist/frontend/ts7/semantic-model.js +423 -0
  801. package/dist/frontend/ts7/semantic-model.js.map +1 -0
  802. package/dist/frontend/ts7/semantic-model.test.d.ts +1 -0
  803. package/dist/frontend/ts7/semantic-model.test.js +322 -0
  804. package/dist/frontend/ts7/semantic-model.test.js.map +1 -0
  805. package/dist/frontend/ts7/semantic-schema.generated.d.ts +283 -0
  806. package/dist/frontend/ts7/semantic-schema.generated.js +222 -0
  807. package/dist/frontend/ts7/semantic-schema.generated.js.map +1 -0
  808. package/dist/frontend/ts7/semantic-types.d.ts +5 -0
  809. package/dist/frontend/ts7/semantic-types.js +2 -0
  810. package/dist/frontend/ts7/semantic-types.js.map +1 -0
  811. package/dist/frontend/ts7/session-cache.d.ts +17 -0
  812. package/dist/frontend/ts7/session-cache.js +97 -0
  813. package/dist/frontend/ts7/session-cache.js.map +1 -0
  814. package/dist/frontend/ts7/session-cache.test.d.ts +1 -0
  815. package/dist/frontend/ts7/session-cache.test.js +87 -0
  816. package/dist/frontend/ts7/session-cache.test.js.map +1 -0
  817. package/dist/frontend/ts7/session-path.d.ts +18 -0
  818. package/dist/frontend/ts7/session-path.js +203 -0
  819. package/dist/frontend/ts7/session-path.js.map +1 -0
  820. package/dist/frontend/ts7/session-path.test.d.ts +1 -0
  821. package/dist/frontend/ts7/session-path.test.js +34 -0
  822. package/dist/frontend/ts7/session-path.test.js.map +1 -0
  823. package/dist/frontend/ts7/session-schema.generated.d.ts +167 -0
  824. package/dist/frontend/ts7/session-schema.generated.js +5 -0
  825. package/dist/frontend/ts7/session-schema.generated.js.map +1 -0
  826. package/dist/frontend/ts7/session-timing.d.ts +50 -0
  827. package/dist/frontend/ts7/session-timing.js +61 -0
  828. package/dist/frontend/ts7/session-timing.js.map +1 -0
  829. package/dist/frontend/ts7/session-timing.test.d.ts +1 -0
  830. package/dist/frontend/ts7/session-timing.test.js +52 -0
  831. package/dist/frontend/ts7/session-timing.test.js.map +1 -0
  832. package/dist/frontend/ts7/session.d.ts +105 -0
  833. package/dist/frontend/ts7/session.js +233 -0
  834. package/dist/frontend/ts7/session.js.map +1 -0
  835. package/dist/frontend/ts7/session.test.d.ts +1 -0
  836. package/dist/frontend/ts7/session.test.js +215 -0
  837. package/dist/frontend/ts7/session.test.js.map +1 -0
  838. package/dist/frontend/ts7/source-parser-node.d.ts +6 -0
  839. package/dist/frontend/ts7/source-parser-node.js +15 -0
  840. package/dist/frontend/ts7/source-parser-node.js.map +1 -0
  841. package/dist/frontend/ts7/source-parser.d.ts +25 -0
  842. package/dist/frontend/ts7/source-parser.js +96 -0
  843. package/dist/frontend/ts7/source-parser.js.map +1 -0
  844. package/dist/frontend/ts7/source-parser.test.d.ts +1 -0
  845. package/dist/frontend/ts7/source-parser.test.js +151 -0
  846. package/dist/frontend/ts7/source-parser.test.js.map +1 -0
  847. package/dist/frontend/ts7/syntax.d.ts +3 -0
  848. package/dist/frontend/ts7/syntax.js +4 -0
  849. package/dist/frontend/ts7/syntax.js.map +1 -0
  850. package/dist/frontend/tsc-codes.js +3 -0
  851. package/dist/frontend/tsc-codes.js.map +1 -1
  852. package/dist/frontend/type-mapper.d.ts +25 -17
  853. package/dist/frontend/type-mapper.js +465 -121
  854. package/dist/frontend/type-mapper.js.map +1 -1
  855. package/dist/frontend/type-mapper.test.js +123 -2
  856. package/dist/frontend/type-mapper.test.js.map +1 -1
  857. package/dist/frontend/union-discriminants.d.ts +16 -0
  858. package/dist/frontend/union-discriminants.js +98 -0
  859. package/dist/frontend/union-discriminants.js.map +1 -0
  860. package/dist/frontend/union-discriminants.test.d.ts +1 -0
  861. package/dist/frontend/union-discriminants.test.js +182 -0
  862. package/dist/frontend/union-discriminants.test.js.map +1 -0
  863. package/dist/index.d.ts +7 -198
  864. package/dist/index.js +369 -1572
  865. package/dist/index.js.map +1 -1
  866. package/dist/ir/analysis.d.ts +16 -4
  867. package/dist/ir/analysis.js +21 -4
  868. package/dist/ir/analysis.js.map +1 -1
  869. package/dist/ir/analysis.test.js +15 -2
  870. package/dist/ir/analysis.test.js.map +1 -1
  871. package/dist/ir/constant-tables.js +67 -49
  872. package/dist/ir/constant-tables.js.map +1 -1
  873. package/dist/ir/constant-tables.test.js +1 -1
  874. package/dist/ir/control-flow.d.ts +6 -0
  875. package/dist/ir/control-flow.js +142 -0
  876. package/dist/ir/control-flow.js.map +1 -0
  877. package/dist/ir/control-flow.test.d.ts +1 -0
  878. package/dist/ir/control-flow.test.js +113 -0
  879. package/dist/ir/control-flow.test.js.map +1 -0
  880. package/dist/ir/integer-loops.js +7 -18
  881. package/dist/ir/integer-loops.js.map +1 -1
  882. package/dist/ir/integer-ranges.js +53 -27
  883. package/dist/ir/integer-ranges.js.map +1 -1
  884. package/dist/ir/ir.d.ts +291 -152
  885. package/dist/ir/ir.js +588 -881
  886. package/dist/ir/ir.js.map +1 -1
  887. package/dist/ir/ir.test.js +127 -1
  888. package/dist/ir/ir.test.js.map +1 -1
  889. package/dist/ir/scalar-records.js +165 -134
  890. package/dist/ir/scalar-records.js.map +1 -1
  891. package/dist/ir/scalar-records.test.js +1 -1
  892. package/dist/ir/serialize.d.ts +4 -2
  893. package/dist/ir/serialize.js +19 -12
  894. package/dist/ir/serialize.js.map +1 -1
  895. package/dist/ir/serialize.test.d.ts +1 -0
  896. package/dist/ir/serialize.test.js +53 -0
  897. package/dist/ir/serialize.test.js.map +1 -0
  898. package/dist/ir/traverse.d.ts +41 -0
  899. package/dist/ir/traverse.js +660 -0
  900. package/dist/ir/traverse.js.map +1 -0
  901. package/dist/ir/traverse.test.d.ts +1 -0
  902. package/dist/ir/traverse.test.js +242 -0
  903. package/dist/ir/traverse.test.js.map +1 -0
  904. package/dist/ir/validate.js +1942 -1491
  905. package/dist/ir/validate.js.map +1 -1
  906. package/dist/ir/validate.test.js +476 -2
  907. package/dist/ir/validate.test.js.map +1 -1
  908. package/dist/library/cache-primitives.d.ts +2 -3
  909. package/dist/library/cache-primitives.js +4 -6
  910. package/dist/library/cache-primitives.js.map +1 -1
  911. package/dist/library/fence-eval.js +2 -2
  912. package/dist/library/fence-eval.js.map +1 -1
  913. package/dist/library/int-infer.js +7 -6
  914. package/dist/library/int-infer.js.map +1 -1
  915. package/dist/library/int-infer.test.js +4 -4
  916. package/dist/library/library-cache.d.ts +3 -2
  917. package/dist/library/library-cache.js +5 -17
  918. package/dist/library/library-cache.js.map +1 -1
  919. package/dist/library/library-cache.test.js +54 -41
  920. package/dist/library/library-cache.test.js.map +1 -1
  921. package/dist/library/library-profile.d.ts +3 -1
  922. package/dist/library/library-profile.js +15 -6
  923. package/dist/library/library-profile.js.map +1 -1
  924. package/dist/library/prepare.d.ts +15 -0
  925. package/dist/library/prepare.js +584 -0
  926. package/dist/library/prepare.js.map +1 -0
  927. package/dist/library/semantic-source.d.ts +1 -1
  928. package/dist/library/semantic-source.js +8 -6
  929. package/dist/library/semantic-source.js.map +1 -1
  930. package/dist/library/sidecar-validate.js +4 -4
  931. package/dist/library/sidecar-validate.js.map +1 -1
  932. package/dist/library/sidecar.d.ts +2 -0
  933. package/dist/library/sidecar.js +10 -3
  934. package/dist/library/sidecar.js.map +1 -1
  935. package/dist/native/cache.d.ts +11 -0
  936. package/dist/native/cache.js +113 -0
  937. package/dist/native/cache.js.map +1 -0
  938. package/dist/native/cache.test.d.ts +1 -0
  939. package/dist/native/cache.test.js +66 -0
  940. package/dist/native/cache.test.js.map +1 -0
  941. package/dist/native/cli.d.ts +1 -0
  942. package/dist/native/cli.js +112 -0
  943. package/dist/native/cli.js.map +1 -0
  944. package/dist/native/compiler.d.ts +15 -0
  945. package/dist/native/compiler.js +336 -0
  946. package/dist/native/compiler.js.map +1 -0
  947. package/dist/native/compiler.test.d.ts +1 -0
  948. package/dist/native/compiler.test.js +174 -0
  949. package/dist/native/compiler.test.js.map +1 -0
  950. package/dist/native/comptime.d.ts +5 -0
  951. package/dist/native/comptime.js +73 -0
  952. package/dist/native/comptime.js.map +1 -0
  953. package/dist/native/comptime.test.d.ts +1 -0
  954. package/dist/native/comptime.test.js +65 -0
  955. package/dist/native/comptime.test.js.map +1 -0
  956. package/dist/native/executable-cache.d.ts +26 -0
  957. package/dist/native/executable-cache.js +228 -0
  958. package/dist/native/executable-cache.js.map +1 -0
  959. package/dist/native/executable-cache.test.d.ts +1 -0
  960. package/dist/native/executable-cache.test.js +151 -0
  961. package/dist/native/executable-cache.test.js.map +1 -0
  962. package/dist/native/file-identity.d.ts +4 -0
  963. package/dist/native/file-identity.js +14 -0
  964. package/dist/native/file-identity.js.map +1 -0
  965. package/dist/native/file-identity.test.d.ts +1 -0
  966. package/dist/native/file-identity.test.js +43 -0
  967. package/dist/native/file-identity.test.js.map +1 -0
  968. package/dist/native/library.d.ts +22 -0
  969. package/dist/native/library.js +83 -0
  970. package/dist/native/library.js.map +1 -0
  971. package/dist/native/prepare.d.ts +21 -0
  972. package/dist/native/prepare.js +174 -0
  973. package/dist/native/prepare.js.map +1 -0
  974. package/dist/native/prepare.test.d.ts +1 -0
  975. package/dist/native/prepare.test.js +70 -0
  976. package/dist/native/prepare.test.js.map +1 -0
  977. package/dist/native/sanitizer.d.ts +11 -0
  978. package/dist/native/sanitizer.js +135 -0
  979. package/dist/native/sanitizer.js.map +1 -0
  980. package/dist/native/sanitizer.test.d.ts +1 -0
  981. package/dist/native/sanitizer.test.js +58 -0
  982. package/dist/native/sanitizer.test.js.map +1 -0
  983. package/dist/native/task.d.ts +4 -0
  984. package/dist/native/task.js +16 -0
  985. package/dist/native/task.js.map +1 -0
  986. package/dist/native/task.test.d.ts +1 -0
  987. package/dist/native/task.test.js +17 -0
  988. package/dist/native/task.test.js.map +1 -0
  989. package/dist/native/toolchain.d.ts +45 -0
  990. package/dist/native/toolchain.js +74 -0
  991. package/dist/native/toolchain.js.map +1 -0
  992. package/dist/native/toolchain.test.d.ts +1 -0
  993. package/dist/native/toolchain.test.js +33 -0
  994. package/dist/native/toolchain.test.js.map +1 -0
  995. package/dist/startup-cache.d.ts +1 -1
  996. package/dist/startup-cache.js +2 -5
  997. package/dist/startup-cache.js.map +1 -1
  998. package/dist/timing.d.ts +4 -0
  999. package/dist/timing.js +19 -0
  1000. package/dist/timing.js.map +1 -0
  1001. package/dist/wasi-paths.js +10 -7
  1002. package/dist/wasi-paths.js.map +1 -1
  1003. package/package.json +28 -19
  1004. package/surface-manifest.json +52 -18
  1005. package/dist/backend/c/async.d.ts +0 -171
  1006. package/dist/backend/c/async.js +0 -1317
  1007. package/dist/backend/c/async.js.map +0 -1
  1008. package/dist/backend/c/c-emitter.d.ts +0 -536
  1009. package/dist/backend/c/c-emitter.js +0 -1932
  1010. package/dist/backend/c/c-emitter.js.map +0 -1
  1011. package/dist/backend/c/exprs.d.ts +0 -15
  1012. package/dist/backend/c/exprs.js +0 -8847
  1013. package/dist/backend/c/exprs.js.map +0 -1
  1014. package/dist/backend/c/island.d.ts +0 -45
  1015. package/dist/backend/c/island.js +0 -281
  1016. package/dist/backend/c/island.js.map +0 -1
  1017. package/dist/backend/c/may-throw.js.map +0 -1
  1018. package/dist/backend/c/shapes.d.ts +0 -142
  1019. package/dist/backend/c/shapes.js +0 -603
  1020. package/dist/backend/c/shapes.js.map +0 -1
  1021. package/dist/backend/c/stmts.d.ts +0 -76
  1022. package/dist/backend/c/stmts.js +0 -1108
  1023. package/dist/backend/c/stmts.js.map +0 -1
  1024. package/dist/backend/c/types.d.ts +0 -71
  1025. package/dist/backend/c/types.js +0 -459
  1026. package/dist/backend/c/types.js.map +0 -1
  1027. package/dist/backend/c/types.test.js +0 -32
  1028. package/dist/backend/c/types.test.js.map +0 -1
  1029. package/dist/backend/c/walkers.d.ts +0 -134
  1030. package/dist/backend/c/walkers.js +0 -1772
  1031. package/dist/backend/c/walkers.js.map +0 -1
  1032. /package/dist/backend/{c/types.test.d.ts → cycle-analysis.test.d.ts} +0 -0
@@ -1,4 +1,6 @@
1
1
  import { InternalCompilerError } from "../../errors.js";
2
+ import { literalValues } from "../literal-values.js";
3
+ import { literalUnionArm } from "../union-discriminants.js";
2
4
  /* Expression lowering: the expression dispatch (lowerExpr), literals
3
5
  * (array/object/regex/template), operators (binary incl. compound targets,
4
6
  * unary, instanceof, caught-typeof tests), union narrowing and unit
@@ -6,16 +8,19 @@ import { InternalCompilerError } from "../../errors.js";
6
8
  * ToBoolean/ToString coercion helpers, and field/element reads and writes
7
9
  * (FieldTarget). */
8
10
  import * as ts from "../ts7/adapter.js";
9
- import { dirname, posix } from "node:path";
11
+ import { dirname } from "node:path";
12
+ import * as posix from "node:path/posix";
10
13
  import { pathToFileURL } from "node:url";
14
+ import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
11
15
  import { wasiGuestPath } from "../../wasi-paths.js";
12
- import { BIGINT_T, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isJsonSafeType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionFuncSetArmsOk } from "../../ir/ir.js";
16
+ import { BIGINT_T, BYTES_ELEMENT_NAME, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canDynCheckTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionContainerArmsOk } from "../../ir/ir.js";
13
17
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
14
18
  import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
15
- import { blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
19
+ import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
16
20
  import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
17
- import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
18
- import { arrayValueRead, arrayValueStore } from "./array-values.js";
21
+ import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
22
+ import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
23
+ import { lowerOptionalStringIndex } from "./string-index.js";
19
24
  import { tryLowerIndexedComparison } from "./indexed-comparison.js";
20
25
  import { npmStaticPackageOfPath } from "../npm-static.js";
21
26
  import { unsupportedModuleFeatureOf } from "../builtin-modules.js";
@@ -23,20 +28,32 @@ import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
23
28
  import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, contextualUndefReadType, fenceEarlyAliasUse, fenceEarlyNsMemberRef, lowerNsIdentifierValue, nsMemberIdentOf, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
24
29
  import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
25
30
  import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
26
- import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
31
+ import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
27
32
  import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
28
33
  import { mixinFnOfCallee } from "./lower-mixins.js";
29
- import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion } from "../type-mapper.js";
34
+ import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
30
35
  import { lowerYield } from "./lower-generators.js";
36
+ import { errorToStringCall } from "./error-methods.js";
31
37
  import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
32
38
  import { countedFor, numLit, varRef } from "../../ir/build.js";
39
+ import { unionWideningTags } from "../../ir/analysis.js";
33
40
  import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
41
+ import { globalSymbolKey } from "./expressions/global-symbols.js";
42
+ import { lowerShortCircuitAssignment } from "./expressions/nullish-assignment.js";
34
43
  import { hasOptionalChainGuard, isOptionalChainTail, isRequireMainFilename } from "./expressions/optional-chains.js";
35
44
  import { conditionalSpreadOf, foldedStringKeyOf } from "./expressions/object-literals.js";
36
45
  import { tryLowerExpression } from "./expressions/try-lower-expression.js";
37
46
  import { fenceNodeModuleMutation, isNodeModuleValue, lowerNodeModuleIdentifier, lowerNodeModuleProperty, lowerRequireCacheElement, lowerRequireCacheHas, lowerRequireMainProperty } from "./lower-node-module.js";
38
47
  import { lowerAbstractEquality } from "./abstract-equality.js";
39
48
  import { coerceStringSearchValue, defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js";
49
+ import { recordTextCodecClass } from "../../ir/ir.js";
50
+ import { classSymbolKeyOf } from "./symbol-fields.js";
51
+ import { lowerClassMethodValue } from "./class-method-values.js";
52
+ import { classInstanceOf } from "./class-dynamic-dispatch.js";
53
+ import { lowerGlobalValue } from "./lower-global-value.js";
54
+ import { lowerClassPrototypeData } from "./class-prototypes.js";
55
+ import { lowerArrayIsArrayValue } from "./lower-builtin-values.js";
56
+ import { lowerPerfHooksTypeof } from "./lower-builtins.js";
40
57
  /** A template piece's RAW text (String.raw's contract: escapes stay
41
58
  * characters). 7's client AST ships no rawText at runtime (the typing
42
59
  * declares it; the serialized node data omits it), so the raw span comes
@@ -313,6 +330,12 @@ function lowerExprInner(lowerer, expr) {
313
330
  return !sf.isDeclarationFile && !sf.fileName.includes("/node_modules/");
314
331
  });
315
332
  if (projectDeclared) {
333
+ // A spread already stored in the island must copy there too.
334
+ // Later overrides can make the literal's own inferred shape
335
+ // appear static without making that source a native record.
336
+ if (ts.isObjectLiteralExpression(expr) && expr.properties.some((p) => ts.isSpreadAssignment(p) && lowerer.mapTypeOf(lowerer.typeOf(p.expression))?.kind === "jsval")) {
337
+ return true;
338
+ }
316
339
  const own = lowerer.mapTypeOf(lowerer.typeOf(expr));
317
340
  if (own?.kind === "record" || own?.kind === "array")
318
341
  return false;
@@ -350,7 +373,7 @@ function lowerExprInner(lowerer, expr) {
350
373
  // null keeps the closure shape (function-shaped members, and the
351
374
  // conditional-spread arms where getters cannot combine).
352
375
  const islandMemberFence = (diagsBefore, err, valueNode, getterName = null) => {
353
- if (!(err instanceof PoisonError) || !isJsSourceFile(expr.getSourceFile()))
376
+ if (!isJsSourceFile(expr.getSourceFile()))
354
377
  throw err;
355
378
  const fence = lowerer.deferToRuntimeFence(diagsBefore, valueNode, {
356
379
  kind: "closure",
@@ -398,6 +421,8 @@ function lowerExprInner(lowerer, expr) {
398
421
  // instance): func-typed values keep the call-time closure;
399
422
  // data-shaped values fence at the read (the getter), unless a
400
423
  // conditional spread owns the literal (getters cannot combine).
424
+ if (!(err instanceof PoisonError))
425
+ throw err;
401
426
  const asGetter = value.type.kind !== "func" && spread === null;
402
427
  const fence = islandMemberFence(diagsBefore, err, valueNode, asGetter ? name.text : null);
403
428
  if (fence === null)
@@ -486,6 +511,8 @@ function lowerExprInner(lowerer, expr) {
486
511
  : null;
487
512
  }
488
513
  catch (err) {
514
+ if (!(err instanceof PoisonError))
515
+ throw err;
489
516
  const nameText = name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : null;
490
517
  const asGetter = nameText !== null && spread === null && !funcShapedMember(prop);
491
518
  value = islandMemberFence(valueDiagsBefore, err, prop, asGetter ? nameText : null);
@@ -551,6 +578,11 @@ function lowerExprInner(lowerer, expr) {
551
578
  return { kind: "jsOp", op: "arrLit", args, type: JSVAL, loc };
552
579
  }
553
580
  if (ts.isTypeOfExpression(expr)) {
581
+ if (lowerer.isStdlibGlobal(expr.expression, "process"))
582
+ return { kind: "strLit", value: "object", type: STRING, loc };
583
+ const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
584
+ if (performance)
585
+ return performance;
554
586
  if (isNodeModuleValue(lowerer, expr.expression)) {
555
587
  return { kind: "strLit", value: "object", type: STRING, loc };
556
588
  }
@@ -580,8 +612,14 @@ function lowerExprInner(lowerer, expr) {
580
612
  // every function and constructor global is "function" (the harness's
581
613
  // `typeof queueMicrotask === 'function'` probes). Shadowing locals
582
614
  // have non-stdlib symbols and keep the ordinary path.
583
- if (ts.isIdentifier(expr.expression)) {
584
- const sym = lowerer.checker.getSymbolAtLocation(expr.expression);
615
+ let tested = expr.expression;
616
+ while (ts.isParenthesizedExpression(tested))
617
+ tested = tested.expression;
618
+ if (ts.isIdentifier(tested)) {
619
+ const sym = lowerer.checker.getSymbolAtLocation(tested);
620
+ if (!sym && !lowerer.dynamic) {
621
+ return { kind: "strLit", value: "undefined", type: STRING, loc };
622
+ }
585
623
  if (lowerer.isStdlibSymbol(sym)) {
586
624
  const t = lowerer.typeOf(expr.expression);
587
625
  if (lowerer.checker.getCallSignatures(t).length > 0 ||
@@ -590,11 +628,9 @@ function lowerExprInner(lowerer, expr) {
590
628
  }
591
629
  }
592
630
  }
593
- // Island values ask the engine; static primitives constant-fold to
594
- // the JS answer (the operand still evaluates — JS evaluates typeof
595
- // operands too — but primitives here are side-effect-free varRefs/
596
- // literals after lowering, so folding away the value is safe only
597
- // when the operand is trivial; otherwise keep it simple and reject).
631
+ // A known static result type fixes typeof's answer, but its producer
632
+ // still evaluates: calls, getters, and checked reads can have effects
633
+ // or throw. Only trivial operands may disappear.
598
634
  let operand = lowerer.lowerExpr(expr.expression);
599
635
  if (operand.type.kind === "jsval") {
600
636
  return { kind: "jsOp", op: "typeof", args: [operand], type: STRING, loc };
@@ -612,13 +648,16 @@ function lowerExprInner(lowerer, expr) {
612
648
  array: "object", object: "object", record: "object",
613
649
  symbol: "symbol",
614
650
  map: "object", set: "object", promise: "object", bytes: "object",
615
- regexp: "object", generator: "object", classval: "function",
651
+ regex: "object", generator: "object", classval: "function",
616
652
  moduleNs: "object",
617
653
  undefinedT: "undefined", nullT: "object",
618
654
  };
619
655
  const folded = FOLD[operand.type.kind];
620
- if (folded && isSafeToDiscard(operand)) {
621
- return { kind: "strLit", value: folded, type: STRING, loc };
656
+ if (folded) {
657
+ const result = { kind: "strLit", value: folded, type: STRING, loc };
658
+ return isSafeToDiscard(operand) ? result : {
659
+ kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: operand, loc }], result, type: STRING, loc,
660
+ };
622
661
  }
623
662
  // A union operand: every arm's typeof answer is static, so the value
624
663
  // form is a ternary chain over runtime TAG tests (arms grouped by
@@ -653,6 +692,9 @@ function lowerExprInner(lowerer, expr) {
653
692
  lowerer.unsupported("SC1090", expr, "typeof expressions on statically-typed values");
654
693
  }
655
694
  if (ts.isIdentifier(expr)) {
695
+ if (lowerer.isStdlibGlobal(expr, "console")) {
696
+ return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
697
+ }
656
698
  const moduleValue = lowerNodeModuleIdentifier(lowerer, expr);
657
699
  if (moduleValue)
658
700
  return moduleValue;
@@ -676,7 +718,7 @@ function lowerExprInner(lowerer, expr) {
676
718
  }
677
719
  if (projection?.kind === "builtin-function") {
678
720
  if (!isJsSourceFile(expr.getSourceFile())) {
679
- const callable = lowerer.lowerBuiltinCallableValue(projection, loc);
721
+ const callable = lowerer.lowerBuiltinCallableValue({ module: projection.module, member: projection.member }, loc);
680
722
  if (callable)
681
723
  return callable;
682
724
  }
@@ -688,6 +730,21 @@ function lowerExprInner(lowerer, expr) {
688
730
  // bridges the tagged representation with a unionNarrow.
689
731
  const local = lowerer.resolveLocal(expr);
690
732
  if (local) {
733
+ // A switch can dispatch past a lexical declaration. Keep this
734
+ // previously refused codec form out until switch-scope TDZ boxes
735
+ // model that skipped initialization, including value aliases.
736
+ if (local.type.kind === "record" && recordTextCodecClass(lowerer.shapes.get(local.type.shapeId)) !== null) {
737
+ const symbol = lowerer.resolveValueSymbol(expr);
738
+ const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
739
+ if (decl && ts.isVariableDeclaration(decl) && ts.isVariableDeclarationList(decl.parent)
740
+ && (decl.parent.flags & ts.NodeFlags.BlockScoped) !== 0 && ts.isVariableStatement(decl.parent.parent)) {
741
+ const clause = decl.parent.parent.parent;
742
+ if ((ts.isCaseClause(clause) || ts.isDefaultClause(clause))
743
+ && !(expr.getStart() >= clause.getStart() && expr.end <= clause.end)) {
744
+ lowerer.unsupported("SC1090", expr, "codec bindings read from another switch clause before initialization is proven");
745
+ }
746
+ }
747
+ }
691
748
  if (local.type.kind === "caught")
692
749
  return lowerer.caughtRead(expr, local, loc);
693
750
  const symbol = lowerer.resolveValueSymbol(expr);
@@ -696,7 +753,7 @@ function lowerExprInner(lowerer, expr) {
696
753
  const arithmetic = lowerer.runtimeOptionalArithmeticLocals.has(runtimeOptionalRoot);
697
754
  const captured = !!symbol &&
698
755
  lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
699
- (lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
756
+ (local.boxed === true || lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
700
757
  if (arithmetic && !runtimeOptionalUseOf(expr)) {
701
758
  return { kind: "varRef", localId: local.id, type: local.type, loc };
702
759
  }
@@ -737,7 +794,24 @@ function lowerExprInner(lowerer, expr) {
737
794
  }
738
795
  return { kind: "varRef", localId: local.id, type: local.type, loc };
739
796
  }
740
- return lowerer.maybeNarrow({ kind: "varRef", localId: local.id, type: local.type, loc }, expr);
797
+ const narrowed = lowerer.maybeNarrow({ kind: "varRef", localId: local.id, type: local.type, loc }, expr);
798
+ if (narrowed.type.kind === "union" && lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
799
+ lowerer.mapTypeOf(lowerer.typeOf(expr))?.kind === "record") {
800
+ // A presence guard can be followed by a predicate strengthening
801
+ // the record's fields. Its refined shape is not a stored union
802
+ // arm: extract the original layout before reading a member.
803
+ const use = runtimeOptionalUseOf(expr);
804
+ if (use && !use.complex && !use.optional) {
805
+ if (use.kind === "property")
806
+ return runtimeOptionalReceiverRead(lowerer, expr, local, use.access.name.text, loc);
807
+ if (use.kind === "element") {
808
+ const key = runtimeOptionalElementKey(use.access.argumentExpression);
809
+ if (key !== null)
810
+ return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
811
+ }
812
+ }
813
+ }
814
+ return narrowed;
741
815
  }
742
816
  // `import x = N.y` aliases resolve transparently through globalOf/
743
817
  // fnSigOf below; their source-order guards live here (a no-op for
@@ -774,6 +848,7 @@ function lowerExprInner(lowerer, expr) {
774
848
  lowerer.unions.get(g.type.unionId).arms.some((a) => a.kind === "string");
775
849
  if (!arithmeticUnion && g.type.kind === "union" && narrowed && narrowed.kind !== "union" &&
776
850
  !isUnitType(narrowed) &&
851
+ !(narrowed.kind === "record" && recordTextCodecClass(lowerer.shapes.get(narrowed.shapeId)) !== null) &&
777
852
  narrowed.kind !== "f64" && narrowed.kind !== "string" && narrowed.kind !== "bool" &&
778
853
  lowerer.armTag(g.type.unionId, narrowed) >= 0) {
779
854
  const helper = lowerer.narrowedArmHelper(g.type.unionId, narrowed, loc);
@@ -817,7 +892,7 @@ function lowerExprInner(lowerer, expr) {
817
892
  if ((expr.text === "__dirname" || expr.text === "__filename") &&
818
893
  lowerer.isStdlibGlobal(expr, expr.text)) {
819
894
  const sf = expr.getSourceFile();
820
- if (isNodeEsmFile(sf)) {
895
+ if (isNodeEsmFile(sf, lowerer.program)) {
821
896
  lowerer.unsupported("SC1090", expr, `'${expr.text}' in an ES module (Node defines the CJS module globals only in CommonJS modules)`);
822
897
  }
823
898
  // The WASI runner mounts the host cwd at guest `/` and the host temp
@@ -899,17 +974,17 @@ function lowerExprInner(lowerer, expr) {
899
974
  if (roots)
900
975
  return roots;
901
976
  }
902
- // JavaScript sources: a builtin member taken as a bare VALUE is
903
- // the same identity-token story as stdlib globals above (the
904
- // harness adds worker_threads.Worker to its identity Set).
905
- if (isJsSourceFile(expr.getSourceFile())) {
906
- return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
907
- }
908
977
  {
909
978
  const callable = lowerer.lowerBuiltinCallableValue(bi, loc);
910
979
  if (callable)
911
980
  return callable;
912
981
  }
982
+ // JavaScript sources: an otherwise unsupported builtin VALUE is
983
+ // the same identity-token story as stdlib globals above (the
984
+ // harness adds worker_threads.Worker to its identity Set).
985
+ if (isJsSourceFile(expr.getSourceFile())) {
986
+ return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
987
+ }
913
988
  if (builtinModuleFnOf(lowerer, bi.module, bi.member)) {
914
989
  lowerer.unsupported("SC1090", expr, `library functions as values (call '${expr.text}' directly)`);
915
990
  }
@@ -999,6 +1074,9 @@ function lowerExprInner(lowerer, expr) {
999
1074
  // global, one token; what a token cannot do (be called, answer
1000
1075
  // typeof "function") meets per-site fences/divergences, and
1001
1076
  // SEMANTICS.md documents the stance.
1077
+ if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr) === "globalThis") {
1078
+ return lowerGlobalValue(lowerer, expr);
1079
+ }
1002
1080
  {
1003
1081
  const sym = lowerer.checker.getSymbolAtLocation(expr);
1004
1082
  if (lowerer.isStdlibSymbol(sym) || expr.text === "globalThis") {
@@ -1016,11 +1094,11 @@ function lowerExprInner(lowerer, expr) {
1016
1094
  // The families with a WHY: each hint states what makes the
1017
1095
  // surface genuinely non-static (or what to use instead).
1018
1096
  const globalHints = {
1019
- Proxy: "property-access metaprogramming has no static lowering (every property read must resolve at compile time)",
1097
+ Proxy: "Proxy constructor values have no native lowering; use a direct new Proxy with checked-native plain objects",
1020
1098
  Reflect: "reflective property access has no static lowering — read and call members directly",
1021
- Intl: "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat(\"en-US\").format(x) and x.toLocaleString(\"en-US\") with default options",
1099
+ Intl: "native Intl supports default Unicode grapheme segmentation and the composed new Intl.NumberFormat(\"en-US\").format(x); locale negotiation and other ICU-backed operations remain unsupported",
1022
1100
  SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
1023
- ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
1101
+ ArrayBuffer: "ArrayBuffer constructor values have no native lowering; use new ArrayBuffer(length) for fixed-length shared storage",
1024
1102
  WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
1025
1103
  FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
1026
1104
  eval: "runtime code evaluation cannot be compiled ahead of time",
@@ -1370,6 +1448,14 @@ function lowerExprInner(lowerer, expr) {
1370
1448
  if (ts.isConditionalExpression(expr))
1371
1449
  return lowerTernary(lowerer, expr);
1372
1450
  if (ts.isPropertyAccessExpression(expr)) {
1451
+ // Expando statics may still be absent even when JS inference sees
1452
+ // the eventual assignment's scalar type. Preserve the checked value.
1453
+ const staticField = lowerStaticFieldRead(lowerer, expr);
1454
+ if (staticField)
1455
+ return staticField;
1456
+ const prototypeData = lowerClassPrototypeData(lowerer, expr);
1457
+ if (prototypeData)
1458
+ return prototypeData;
1373
1459
  // `super.x`: the base chain's GETTER, called directly (super
1374
1460
  // dispatch is static in JS — never through the dynamic class).
1375
1461
  // super.method() calls are routed at the call site; a bare super
@@ -1411,6 +1497,25 @@ function lowerExprInner(lowerer, expr) {
1411
1497
  const moduleProperty = lowerNodeModuleProperty(lowerer, expr);
1412
1498
  if (moduleProperty)
1413
1499
  return moduleProperty;
1500
+ if (stdlibGlobalNameOf(lowerer, expr.expression) === "Symbol" &&
1501
+ ["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
1502
+ return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
1503
+ }
1504
+ if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
1505
+ return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
1506
+ }
1507
+ if (lowerer.stdlibGlobalMember(expr, "console") !== null) {
1508
+ return { kind: "dynKeyGet", value: { kind: "libCall", fn: "console.native", args: [], type: DYN, loc },
1509
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
1510
+ }
1511
+ // Missing host globals (Bun/Deno capability probes) have no binding
1512
+ // in a native program. Declared globals retain their own lowering;
1513
+ // arbitrary global-object mutation remains fenced. Preserve a dyn
1514
+ // undefined value so stored probes compose with optional calls.
1515
+ if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis" &&
1516
+ !lowerer.checker.getPropertyOfType(lowerer.typeOf(expr.expression), expr.name.text)) {
1517
+ return { kind: "dynFrom", value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }, type: DYN, loc };
1518
+ }
1414
1519
  if (expr.questionDotToken && expr.name.text === "electron") {
1415
1520
  const processProperty = lowerer.lowerProcessProperty(expr);
1416
1521
  if (processProperty)
@@ -1427,6 +1532,12 @@ function lowerExprInner(lowerer, expr) {
1427
1532
  if (!expr.questionDotToken && isOptionalChainTail(lowerer, expr)) {
1428
1533
  return lowerer.lowerOptionalChain(expr);
1429
1534
  }
1535
+ // Known Web handles retain their declared surface even when their
1536
+ // storage uses checked values. Apply this before generic reads.
1537
+ lowerer.fenceStaticAbortControllerMemberRead(expr);
1538
+ lowerer.fenceStaticResponseMember(expr, "read");
1539
+ lowerer.fenceStaticHeadersMember(expr, "read");
1540
+ lowerer.fenceStaticReadableStreamMember(expr, "read");
1430
1541
  // Island receiver: o.x is an engine property read (getProp may throw
1431
1542
  // — reading off null/undefined — bridged catchably like everything
1432
1543
  // at the boundary). `o?.x` arrives here too, through the chain
@@ -1566,11 +1677,11 @@ function lowerExprInner(lowerer, expr) {
1566
1677
  // resolve unconditionally — they run after the module evaluated,
1567
1678
  // the same approximation identifier exports already make. Every
1568
1679
  // other module.exports read keeps its existing story.
1569
- if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile())) {
1680
+ if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile(), lowerer.program)) {
1570
1681
  const whole = cjsClassExprWholeExportOf(expr.getSourceFile());
1571
1682
  if (whole) {
1572
1683
  let inBody = false;
1573
- for (let p = expr.parent; !ts.isSourceFile(p); p = p.parent) {
1684
+ for (let p = expr.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
1574
1685
  if (ts.isFunctionLike(p) || ts.isClassDeclaration(p) || ts.isClassExpression(p)) {
1575
1686
  inBody = true;
1576
1687
  break;
@@ -1598,7 +1709,7 @@ function lowerExprInner(lowerer, expr) {
1598
1709
  const writePos = ts.isBinaryExpression(expr.parent) &&
1599
1710
  expr.parent.left === expr &&
1600
1711
  expr.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
1601
- if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf)) {
1712
+ if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf, lowerer.program)) {
1602
1713
  const sym = lowerer.checker.getSymbolAtLocation(expr.name) ??
1603
1714
  lowerer.cjsModuleExportSymbol(sf, expr.name.text);
1604
1715
  const resolved = sym !== undefined && sym.flags & ts.SymbolFlags.Alias
@@ -1720,6 +1831,8 @@ function lowerExprInner(lowerer, expr) {
1720
1831
  isJsSourceFile(expr.getSourceFile())) {
1721
1832
  const canonical = stdlibGlobalNameOf(lowerer, expr);
1722
1833
  if (canonical !== null) {
1834
+ if (!lowerer.dynamic && canonical === "globalThis")
1835
+ return lowerGlobalValue(lowerer, expr);
1723
1836
  return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
1724
1837
  }
1725
1838
  }
@@ -1743,6 +1856,7 @@ function lowerExprInner(lowerer, expr) {
1743
1856
  // instead.
1744
1857
  if ((isJsSourceFile(expr.getSourceFile()) &&
1745
1858
  (lowerer.mapTypeOf(lowerer.typeOf(expr.expression)) === null ||
1859
+ (lowerer.typeOf(expr.expression).flags & ts.TypeFlags.Never) !== 0 ||
1746
1860
  // A never-tainted receiver type maps (never rides as f64) but
1747
1861
  // its VALUE lowered checked-dynamic — same dyn read.
1748
1862
  neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression)))) ||
@@ -1756,6 +1870,11 @@ function lowerExprInner(lowerer, expr) {
1756
1870
  const opt = hasOptionalChainGuard(expr.expression);
1757
1871
  return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recv, type: DYN, loc }, expr);
1758
1872
  }
1873
+ if (expr.name.text === "length" && (recv.type.kind === "array" || recv.type.kind === "string")) {
1874
+ return recv.type.kind === "array"
1875
+ ? { kind: "arrIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc }
1876
+ : { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
1877
+ }
1759
1878
  }
1760
1879
  // `f.name` / `f.length` / own properties on a FUNCTION-typed JS
1761
1880
  // value (the mustCall wrapper's function-instance members): read
@@ -1764,11 +1883,24 @@ function lowerExprInner(lowerer, expr) {
1764
1883
  // best-effort static name and the declared arity; anything else is
1765
1884
  // the own-property answer, undefined. Function.prototype METHOD
1766
1885
  // names stay fenced (a stored-member undefined would mis-answer
1767
- // `f.call` as a value). JS files only; TS keeps the fence.
1768
- if (!["apply", "bind", "call", "toString", "constructor", "prototype", "caller", "arguments"].includes(expr.name.text) &&
1769
- isJsSourceFile(expr.getSourceFile())) {
1886
+ // `f.call` as a value). Static callable values keep the existing TS fence; checked callable
1887
+ // storage can expose its own name and length in both source languages.
1888
+ if (!["apply", "bind", "call", "toString", "caller", "arguments"].includes(expr.name.text) &&
1889
+ (isJsSourceFile(expr.getSourceFile()) || expr.name.text === "name" || expr.name.text === "length" || expr.name.text === "constructor")) {
1770
1890
  const probed = tryLowerExpression(lowerer, expr.expression);
1771
- if (probed?.type.kind === "func" &&
1891
+ // Inferred callable returns can use checked storage even when the
1892
+ // checker still describes a function. Read its actual dyn value;
1893
+ // the ambient Function member must not hide its own properties.
1894
+ const receiverType = lowerer.typeOf(expr.expression);
1895
+ const receiverSymbol = receiverType.getSymbol();
1896
+ const checkedFunction = lowerer.checker.getCallSignatures(receiverType).length > 0 ||
1897
+ (receiverSymbol?.name === "Function" && lowerer.isStdlibSymbol(receiverSymbol));
1898
+ if (probed?.type.kind === "dyn" && checkedFunction) {
1899
+ const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
1900
+ const opt = hasOptionalChainGuard(expr.expression);
1901
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: probed, ...(opt ? { optional: true } : {}), type: DYN, loc }, expr);
1902
+ }
1903
+ if ((isJsSourceFile(expr.getSourceFile()) || expr.name.text === "constructor") && probed?.type.kind === "func" &&
1772
1904
  canBoxFuncIntoDyn(probed.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
1773
1905
  const fnName = jsFuncNameOf(expr.expression);
1774
1906
  const boxed = {
@@ -1823,7 +1955,10 @@ function lowerExprInner(lowerer, expr) {
1823
1955
  // the generic dyn receiver path below. Provenance is limited to the
1824
1956
  // node:util parseArgs family; every other stdlib member keeps its
1825
1957
  // ordinary surface fence.
1826
- if (isParseArgsDynProperty(lowerer, expr)) {
1958
+ const segmentDataSymbol = lowerer.typeOf(expr.expression).getSymbol();
1959
+ const segmentDataProperty = segmentDataSymbol?.name === "SegmentData" && lowerer.isStdlibSymbol(segmentDataSymbol) &&
1960
+ ["segment", "index", "input", "isWordLike"].includes(expr.name.text);
1961
+ if (isParseArgsDynProperty(lowerer, expr) || segmentDataProperty) {
1827
1962
  const recv = lowerer.lowerExpr(expr.expression);
1828
1963
  if (recv.type.kind === "dyn") {
1829
1964
  const key = {
@@ -1857,10 +1992,6 @@ function lowerExprInner(lowerer, expr) {
1857
1992
  // The lib fence's PROPERTY chokepoint: a stdlib-declared member that
1858
1993
  // no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
1859
1994
  // re.exec as a value, ...) reports SC2020 here.
1860
- lowerer.fenceStaticAbortControllerMemberRead(expr);
1861
- lowerer.fenceStaticResponseMember(expr, "read");
1862
- lowerer.fenceStaticHeadersMember(expr, "read");
1863
- lowerer.fenceStaticReadableStreamMember(expr, "read");
1864
1995
  lowerer.stdlibMemberFence(expr);
1865
1996
  // The npm chokepoint: a member on a package-typed receiver in a
1866
1997
  // static build — attributed to the package, like every other site.
@@ -1919,6 +2050,17 @@ function lowerExprInner(lowerer, expr) {
1919
2050
  loc,
1920
2051
  }, expr);
1921
2052
  }
2053
+ // An inferred package-JS call specializes a parameter to the
2054
+ // caller's own record shape. Optional option names omitted by that
2055
+ // caller read undefined; the missing field is not a failed type
2056
+ // assertion. Known prototype members were handled/fenced above.
2057
+ if (recvShape && !recvShape.tuple && lowerer.implicitParamTypes !== null &&
2058
+ isJsSourceFile(expr.getSourceFile()) && npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null) {
2059
+ return {
2060
+ kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: recvLowered, loc }],
2061
+ result: dynUndefinedExpr(loc), type: DYN, loc,
2062
+ };
2063
+ }
1922
2064
  }
1923
2065
  // An ABSTRACT property through an abstract-typed receiver: the
1924
2066
  // declaration is erased at runtime — Node defines no field for it,
@@ -1928,6 +2070,19 @@ function lowerExprInner(lowerer, expr) {
1928
2070
  if (recvLowered.type.kind === "object" && abstractPropertyDeclOf(lowerer, expr)) {
1929
2071
  lowerer.unsupported("SC1090", expr, `reading the abstract property '${expr.name.text}' through a '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'-typed receiver (abstract property declarations are erased at runtime, so no shared slot exists — type the receiver as the concrete class, or declare an abstract getter instead)`);
1930
2072
  }
2073
+ // Array reads may add an undefined arm even when the checker still
2074
+ // reports a class. Its property bag must remain reachable, while
2075
+ // an absent element takes the normal checked-value TypeError.
2076
+ const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
2077
+ if (isDynTypedRefType(nativeReceiver)) {
2078
+ const info = lowerer.classes.get(nativeReceiver.className);
2079
+ if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
2080
+ return lowerer.maybeNarrow({
2081
+ kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
2082
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc,
2083
+ }, expr);
2084
+ }
2085
+ }
1931
2086
  lowerer.unsupported("SC1090", expr, `reading '${expr.name.text}' from a value of type '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'`);
1932
2087
  }
1933
2088
  // Meta-properties, named: `new.target` reflects HOW a function was
@@ -1944,7 +2099,7 @@ function lowerExprInner(lowerer, expr) {
1944
2099
  const entry = UNSUPPORTED_EXPR[expr.kind];
1945
2100
  if (entry)
1946
2101
  lowerer.unsupported(entry.code, expr, entry.feature);
1947
- lowerer.unsupported("SC1090", expr, `syntax '${ts.SyntaxKind[expr.kind]}'`);
2102
+ lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
1948
2103
  }
1949
2104
  function moduleFileName(lowerer, sf) {
1950
2105
  return lowerer.targetPlatform === "wasi"
@@ -1983,11 +2138,10 @@ function importMetaProperty(lowerer, expr) {
1983
2138
  return null;
1984
2139
  }
1985
2140
  }
1986
- /** `c ? a : b` — see the inline comments. `expected` plays the contextual
1987
- * array type's role when the caller knows the slot's array type and tsc's
1988
- * API doesn't surface it (a ternary as a SPREAD source — lowerArrayLiteral
1989
- * threads the literal's own type in). */
1990
- function lowerTernary(lowerer, expr, expected) {
2141
+ /** `c ? a : b`. An explicit destination constructs each branch at that
2142
+ * layout before joining it. Without one, retain the expression's inferred
2143
+ * type and the contextual/sibling rules below. */
2144
+ export function lowerTernary(lowerer, expr, expected) {
1991
2145
  const loc = locOf(expr);
1992
2146
  // `Array.isArray(x) ? x : [x]` over a `T | readonly T[]` union: tsc
1993
2147
  // narrows the TRUE branch to `any[]` (maybeNarrow's isArray bridge
@@ -2041,7 +2195,16 @@ function lowerTernary(lowerer, expr, expected) {
2041
2195
  // literals) sit untaken in compiled JS. A boolLit carries no
2042
2196
  // effects, so dropping the condition read loses nothing.
2043
2197
  if (cond.kind === "boolLit") {
2044
- return lowerer.lowerExpr(cond.value ? expr.whenTrue : expr.whenFalse);
2198
+ return lowerer.lowerExprExpecting(cond.value ? expr.whenTrue : expr.whenFalse, expected);
2199
+ }
2200
+ if (expected) {
2201
+ // Construct fresh literals in their actual destination. Joining
2202
+ // inferred layouts first can erase the literal discriminator or
2203
+ // choose an empty array's uninhabited element representation.
2204
+ // Runtime-optional proofs apply only within the selected true arm.
2205
+ const then = withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () => lowerer.lowerExprExpecting(expr.whenTrue, expected));
2206
+ const else_ = lowerer.lowerExprExpecting(expr.whenFalse, expected);
2207
+ return { kind: "ternary", cond, then, else_, type: expected, loc };
2045
2208
  }
2046
2209
  const ctxTs = lowerer.checker.getContextualType(expr);
2047
2210
  const ctxMapped = ctxTs ? lowerer.mapTypeOf(ctxTs) : null;
@@ -2058,7 +2221,7 @@ function lowerTernary(lowerer, expr, expected) {
2058
2221
  // lowerArrayLiteral, and just as unambiguous — tsc already typed the
2059
2222
  // whole ternary by the filled arm alone. Both arms empty stays
2060
2223
  // fenced (no element type exists anywhere).
2061
- const ctxArray = expected ?? (ctxMapped?.kind === "array" ? ctxMapped : null);
2224
+ const ctxArray = ctxMapped?.kind === "array" ? ctxMapped : null;
2062
2225
  const armLiteral = (e) => {
2063
2226
  let x = e;
2064
2227
  while (ts.isParenthesizedExpression(x))
@@ -2082,7 +2245,21 @@ function lowerTernary(lowerer, expr, expected) {
2082
2245
  }
2083
2246
  return false;
2084
2247
  };
2248
+ // Inferred signature tables often join a populated argument array
2249
+ // with nested empty-array branches. Carry the whole record layout
2250
+ // through those branches before constructing their fresh fields.
2251
+ const ownJoin = lowerer.mapTypeOf(lowerer.typeOf(expr));
2252
+ const recordJoin = ctxMapped?.kind === "record" ? ctxMapped :
2253
+ ownJoin?.kind === "record" ? ownJoin : undefined;
2085
2254
  const lowerArm = (e, siblingType) => {
2255
+ let fresh = e;
2256
+ while (ts.isParenthesizedExpression(fresh))
2257
+ fresh = fresh.expression;
2258
+ if (recordJoin && ts.isConditionalExpression(fresh))
2259
+ return lowerTernary(lowerer, fresh, recordJoin);
2260
+ if (recordJoin && ts.isObjectLiteralExpression(fresh)) {
2261
+ return lowerer.lowerObjectLiteral(fresh, recordJoin);
2262
+ }
2086
2263
  const lit = armLiteral(e);
2087
2264
  if (lit && ctxArray)
2088
2265
  return lowerer.lowerArrayLiteral(lit, ctxArray);
@@ -2135,11 +2312,14 @@ function lowerTernary(lowerer, expr, expected) {
2135
2312
  const lowerTrueArm = () => withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, ownArrayJoin ?? undefined));
2136
2313
  if (!ctxArray && emptyUntypedArrayArm(expr.whenTrue) && !emptyUntypedArrayArm(expr.whenFalse)) {
2137
2314
  elseRaw = lowerer.lowerExpr(expr.whenFalse);
2138
- thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type));
2315
+ thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : elseRaw.type));
2139
2316
  }
2140
2317
  else {
2141
2318
  thenRaw = lowerTrueArm();
2142
- elseRaw = lowerArm(expr.whenFalse, thenRaw.type);
2319
+ // A checked-dynamic producer (Object.entries(...).filter(...))
2320
+ // can still have a precise checker array type. Empty literal arms
2321
+ // must adopt that element layout before the checked array boundary.
2322
+ elseRaw = lowerArm(expr.whenFalse, thenRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : thenRaw.type);
2143
2323
  }
2144
2324
  // The ternary's IR type is normally the checker's own: it collapses
2145
2325
  // same-kind literal unions ("a" | "b" → string) and forms tagged
@@ -2211,14 +2391,22 @@ function lowerTernary(lowerer, expr, expected) {
2211
2391
  }
2212
2392
  }
2213
2393
  }
2214
- const type = thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
2215
- ? thenRaw.type
2216
- : dynJoin
2217
- ? DYN
2218
- : (anyJoin ??
2219
- (useCtx && ctxMapped
2220
- ? ctxMapped
2221
- : lowerer.irTypeOf(expr)));
2394
+ // JavaScript function selection preserves the chosen closure's full
2395
+ // call ABI. A checker join can erase a rest/arguments pack even though
2396
+ // a later call supplies additional observable arguments.
2397
+ const callableJoin = isJsSourceFile(expr.getSourceFile()) &&
2398
+ thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
2399
+ !typeEquals(thenRaw.type, elseRaw.type) &&
2400
+ lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
2401
+ const type = callableJoin ? DYN :
2402
+ thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
2403
+ ? thenRaw.type
2404
+ : dynJoin
2405
+ ? DYN
2406
+ : (anyJoin ??
2407
+ (useCtx && ctxMapped
2408
+ ? ctxMapped
2409
+ : lowerer.irTypeOf(expr)));
2222
2410
  // Each arm flows into the ternary's type through the slot-coercion
2223
2411
  // path: union arms wrap, dyn slots reject the non-dyn arm with
2224
2412
  // SC1101, mismatched record shapes get SC2002; coerceInto is
@@ -2247,6 +2435,16 @@ function lowerTernary(lowerer, expr, expected) {
2247
2435
  * unrepresentable without a re-tag), or to nothing (`never` in an
2248
2436
  * exhaustive default) leaves the expression union-typed. */
2249
2437
  export function maybeNarrow(lowerer, expr, node) {
2438
+ if ((expr.type.kind === "union" || expr.type.kind === "dyn") &&
2439
+ isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node)) {
2440
+ const symbol = lowerer.checker.getSymbolAtLocation(node.name);
2441
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isPropertyDeclaration(decl) && !decl.initializer && !decl.type &&
2442
+ isJsSourceFile(decl.getSourceFile()))) {
2443
+ // Inference describes later writes, not the initial undefined.
2444
+ // A use that requires the inferred type must validate the value.
2445
+ return expr.type.kind === "dyn" ? expr : { kind: "dynFrom", value: expr, type: DYN, loc: expr.loc };
2446
+ }
2447
+ }
2250
2448
  // A dyn read tsc narrowed to a SCALAR (a typeof test proved the kind):
2251
2449
  // bridge with a VALIDATED extraction — dynCheck, the checked-cast
2252
2450
  // machinery — rather than a trusted one. After the guard the check
@@ -2254,15 +2452,40 @@ export function maybeNarrow(lowerer, expr, node) {
2254
2452
  // instead of misreading the payload (the dyn boundary's usual stance).
2255
2453
  // Object/array narrowings stay dyn-typed and keep their fences.
2256
2454
  if (expr.type.kind === "dyn") {
2455
+ if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
2456
+ const symbol = lowerer.resolveValueSymbol(node);
2457
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
2458
+ if (!ts.isParameter(decl) || decl.type)
2459
+ return false;
2460
+ const fn = decl.parent;
2461
+ if (!ts.isFunctionExpression(fn) && !ts.isArrowFunction(fn))
2462
+ return false;
2463
+ const assignment = fn.parent;
2464
+ return ts.isBinaryExpression(assignment) && assignment.right === fn &&
2465
+ ts.isPropertyAccessExpression(assignment.left) && assignment.left.name.text === "write" &&
2466
+ lowerer.isStdlibMember(assignment.left);
2467
+ }))
2468
+ return expr;
2469
+ }
2470
+ // Node's TTY declarations promise booleans/numbers even when a pipe
2471
+ // has no such property. Preserve the checked read for JS capability
2472
+ // probes instead of treating a declaration as a runtime type guard.
2473
+ if (isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node) &&
2474
+ ["isTTY", "columns", "rows"].includes(node.name.text) && lowerer.isStdlibMember(node))
2475
+ return expr;
2476
+ // A null-initialized JS field can later receive an object even when
2477
+ // checker flow calls its non-null branch never. The live checked
2478
+ // value remains authoritative; never is not a numeric guard.
2479
+ if (isJsSourceFile(node.getSourceFile()) && (lowerer.typeOf(node).flags & ts.TypeFlags.Never) !== 0)
2480
+ return expr;
2257
2481
  const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
2258
2482
  if (narrowed &&
2259
- (narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
2483
+ (narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string" || narrowed.kind === "bigint")) {
2260
2484
  return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
2261
2485
  }
2262
- // An `instanceof Uint8Array` narrow: the checked-dynamic tree's bytes kind, extracted
2263
- // with the same validated copy the checked cast uses (a Buffer that
2264
- // crossed in rides the kind too — it IS a Uint8Array in Node).
2265
- if (narrowed?.kind === "bytes" && narrowed.elem === "u8") {
2486
+ // An instanceof narrow retains the native view after validating its
2487
+ // exact element kind. Buffer also matches Uint8Array.
2488
+ if (narrowed?.kind === "bytes" || narrowed?.kind === "url") {
2266
2489
  return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
2267
2490
  }
2268
2491
  // An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
@@ -2289,6 +2512,11 @@ export function maybeNarrow(lowerer, expr, node) {
2289
2512
  }
2290
2513
  if (expr.type.kind !== "union")
2291
2514
  return expr;
2515
+ if (ts.isIdentifier(node)) {
2516
+ const symbol = lowerer.resolveValueSymbol(node);
2517
+ if (symbol && lowerer.unstableSwitchBindings.has(symbol))
2518
+ return expr;
2519
+ }
2292
2520
  const narrowedTs = lowerer.typeOf(node);
2293
2521
  const narrowed = lowerer.mapTypeOf(narrowedTs);
2294
2522
  // `Array.isArray(u)` on a union with one readonly array/tuple arm: the
@@ -2315,14 +2543,32 @@ export function maybeNarrow(lowerer, expr, node) {
2315
2543
  }
2316
2544
  const tag = lowerer.armTag(expr.type.unionId, narrowed);
2317
2545
  if (tag < 0) {
2318
- // A proven-present optional base class can narrow further to a
2319
- // subclass. The payload is still stored under the base class tag;
2320
- // unwrap it before applying the ordinary class downcast.
2546
+ // The checker may refine fields inside a discriminated record too.
2547
+ // That creates a different mapped shape, but does not change the
2548
+ // stored payload layout. A required literal discriminator proves
2549
+ // which original arm to read; retain all of that arm's field types.
2550
+ const def = lowerer.unions.get(expr.type.unionId);
2551
+ if (narrowed.kind === "record" && def?.discriminant) {
2552
+ const property = lowerer.checker.getPropertyOfType(narrowedTs, def.discriminant.field);
2553
+ if (property && !(property.flags & (ts.SymbolFlags.Optional | ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor))) {
2554
+ const values = literalValues(lowerer.checker.getTypeOfSymbol(property));
2555
+ const arm = values && literalUnionArm(def, values, (id) => lowerer.shapes.get(id));
2556
+ if (arm) {
2557
+ return { kind: "unionNarrow", unionId: expr.type.unionId,
2558
+ tag: lowerer.armTag(expr.type.unionId, arm), value: expr, type: arm, loc: expr.loc };
2559
+ }
2560
+ }
2561
+ }
2562
+ // A base-class arm can narrow to a subclass after instanceof. The
2563
+ // payload retains its base-class tag even when the union also has
2564
+ // records or other classes. Require one unambiguous containing arm.
2321
2565
  const arms = lowerer.unions.get(expr.type.unionId)?.arms ?? [];
2322
- const valueTag = arms.findIndex((arm) => !isUnitType(arm));
2566
+ const valueTag = narrowed.kind === "object"
2567
+ ? arms.findIndex((arm) => arm.kind === "object" && lowerer.isSubclassOf(narrowed.className, arm.className))
2568
+ : -1;
2323
2569
  const valueType = arms[valueTag];
2324
2570
  if (narrowed.kind === "object" && valueType?.kind === "object" &&
2325
- arms.every((arm, i) => i === valueTag || isUnitType(arm)) &&
2571
+ arms.every((arm, i) => i === valueTag || arm.kind !== "object" || !lowerer.isSubclassOf(narrowed.className, arm.className)) &&
2326
2572
  lowerer.isSubclassOf(narrowed.className, valueType.className)) {
2327
2573
  return {
2328
2574
  kind: "downcast",
@@ -2701,7 +2947,26 @@ function lowerLooseNullCompare(lowerer, expr, loc) {
2701
2947
  * results (several non-unit arms) and defaults that change the result
2702
2948
  * type are fenced with narrow-first hints. */
2703
2949
  export function lowerNullishCoalesce(lowerer, expr, loc) {
2704
- const left = lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left);
2950
+ // `a ?? b ?? c` is a left-nested AST. Lower its spine bottom-up so
2951
+ // long dispatch chains do not retain a full expression/binary lowering
2952
+ // frame per operand in a native compiler. The pair lowering still
2953
+ // decides each result layout and preserves the lazy right operand.
2954
+ const parents = [];
2955
+ let first = expr;
2956
+ while (ts.isBinaryExpression(first.left) &&
2957
+ first.left.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken &&
2958
+ !lowerer.chainRecvByNode.has(first.left)) {
2959
+ parents.push(first);
2960
+ first = first.left;
2961
+ }
2962
+ let result = lowerNullishPair(lowerer, first, first === expr ? loc : locOf(first), lowerAbsenceProbe(lowerer, first.left) ?? lowerer.lowerExpr(first.left));
2963
+ for (let i = parents.length - 1; i >= 0; i--) {
2964
+ const parent = parents[i];
2965
+ result = lowerNullishPair(lowerer, parent, parent === expr ? loc : locOf(parent), result);
2966
+ }
2967
+ return result;
2968
+ }
2969
+ function lowerNullishPair(lowerer, expr, loc, left) {
2705
2970
  if (left.type.kind === "dyn") {
2706
2971
  // `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
2707
2972
  // runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
@@ -2730,8 +2995,14 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
2730
2995
  lowerer.badType(expr.left, lowerer.typeOf(expr.left));
2731
2996
  if (!def.arms.some(isUnitType))
2732
2997
  return left;
2733
- const type = lowerer.irTypeOf(expr);
2734
2998
  const rest = def.arms.filter((a) => !isUnitType(a));
2999
+ // `optionalMap ?? new Map()` carries its element contract on the left;
3000
+ // the empty fallback may use it without constructing a Map<any, any>
3001
+ // arm that has no first-class union representation.
3002
+ const fresh = rest.length === 1 ? lowerer.emptyCollectionFor(expr.right, rest[0]) : null;
3003
+ if (fresh)
3004
+ return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
3005
+ const type = lowerer.irTypeOf(expr);
2735
3006
  if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
2736
3007
  const right = lowerer.lowerExprExpecting(expr.right, type);
2737
3008
  return { kind: "nullish", left, right, type, loc };
@@ -2739,180 +3010,49 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
2739
3010
  if (type.kind !== "union" && rest.length > 1 && rest.some((arm) => typeEquals(arm, type))) {
2740
3011
  const helper = lowerer.narrowedArmHelper(left.type.unionId, type, loc);
2741
3012
  if (helper !== null) {
2742
- const stmts = [];
2743
- let stable = left;
2744
- if (!isSafeToRepeat(left)) {
2745
- const local = lowerer.declareHiddenLocal("%nullish", left.type);
2746
- stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
2747
- stable = varRef(local.id, left.type, loc);
2748
- }
2749
- const unitTags = def.arms
2750
- .map((arm, tag) => ({ arm, tag }))
2751
- .filter(({ arm }) => isUnitType(arm))
2752
- .map(({ tag }) => tag);
2753
- let isNullish = { kind: "unionIsTag", unionId: left.type.unionId, tag: unitTags[0], negated: false, value: stable, type: BOOL, loc };
2754
- for (const tag of unitTags.slice(1)) {
2755
- isNullish = {
2756
- kind: "logical",
2757
- op: "||",
2758
- left: isNullish,
2759
- right: { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: stable, type: BOOL, loc },
2760
- type: BOOL,
2761
- loc,
2762
- };
2763
- }
2764
- const result = {
2765
- kind: "ternary",
2766
- cond: isNullish,
2767
- then: lowerer.lowerExprExpecting(expr.right, type),
2768
- else_: { kind: "call", callee: helper, args: [stable], type, loc },
2769
- type,
2770
- loc,
2771
- };
2772
- return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
3013
+ return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({ kind: "call", callee: helper, args: [stable], type, loc }));
2773
3014
  }
2774
3015
  }
2775
- // The RETAGGED shape — the default changes the result union (`s ??
2776
- // null` over `string | undefined` answers `null | string`): every
2777
- // non-unit arm of the left has a home in the result, so an interned
2778
- // helper tests the unit tags and re-wraps the payload per arm. The
2779
- // helper call evaluates the default EAGERLY where JS is lazy, so only
2780
- // effect-free defaults (the literal null/0/"" spellings) qualify —
2781
- // anything effectful keeps the fence.
3016
+ // A default may change the union or widen its records. Test the
3017
+ // original tag before coercing: converting the whole left first can
3018
+ // lose nullish arms, and evaluating the default eagerly loses laziness.
2782
3019
  if (type.kind === "union") {
2783
- const leftT = left.type;
2784
- const armPairs = rest.map((a) => ({ arm: a, src: lowerer.armTag(leftT.unionId, a), dst: lowerer.armTag(type.unionId, a) }));
2785
- if (rest.length > 0 && armPairs.every((pair) => pair.src >= 0 && pair.dst >= 0)) {
2786
- const stmts = [];
2787
- let stable = left;
2788
- if (!isSafeToRepeat(left)) {
2789
- const local = lowerer.declareHiddenLocal("%nullish", leftT);
2790
- stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
2791
- stable = varRef(local.id, leftT, loc);
2792
- }
2793
- const unitTags = def.arms
2794
- .map((arm, tag) => ({ arm, tag }))
2795
- .filter(({ arm }) => isUnitType(arm))
2796
- .map(({ tag }) => tag);
2797
- let isNullish = {
2798
- kind: "unionIsTag",
2799
- unionId: leftT.unionId,
2800
- tag: unitTags[0],
2801
- negated: false,
2802
- value: stable,
2803
- type: BOOL,
2804
- loc,
2805
- };
2806
- for (const tag of unitTags.slice(1)) {
2807
- isNullish = {
2808
- kind: "logical",
2809
- op: "||",
2810
- left: isNullish,
2811
- right: { kind: "unionIsTag", unionId: leftT.unionId, tag, negated: false, value: stable, type: BOOL, loc },
2812
- type: BOOL,
2813
- loc,
2814
- };
2815
- }
2816
- let present = null;
2817
- for (let i = armPairs.length - 1; i >= 0; i--) {
2818
- const pair = armPairs[i];
2819
- const wrapped = {
2820
- kind: "unionWrap",
2821
- unionId: type.unionId,
2822
- tag: pair.dst,
2823
- value: { kind: "unionNarrow", unionId: leftT.unionId, tag: pair.src, value: stable, type: pair.arm, loc },
2824
- type,
2825
- loc,
2826
- };
2827
- present = present === null
2828
- ? wrapped
2829
- : {
2830
- kind: "ternary",
2831
- cond: { kind: "unionIsTag", unionId: leftT.unionId, tag: pair.src, negated: false, value: stable, type: BOOL, loc },
2832
- then: wrapped,
2833
- else_: present,
2834
- type,
2835
- loc,
2836
- };
2837
- }
2838
- const result = {
2839
- kind: "ternary",
2840
- cond: isNullish,
2841
- then: lowerer.lowerExprExpecting(expr.right, type),
2842
- else_: present,
2843
- type,
2844
- loc,
2845
- };
2846
- return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
2847
- }
2848
- if (armPairs.every((p) => p.src >= 0 && p.dst >= 0)) {
2849
- const right = lowerer.lowerExprExpecting(expr.right, type);
2850
- if (isSafeToDiscard(right)) {
2851
- const helper = nullishRetagHelper(lowerer, leftT, type, loc);
2852
- return { kind: "call", callee: helper, args: [left, right], type, loc };
2853
- }
3020
+ const retag = lowerer.unionRetagHelper(left.type.unionId, type.unionId, loc) ??
3021
+ lowerer.narrowedRetagHelper(expr.left, left.type.unionId, type.unionId, loc);
3022
+ if (retag !== null) {
3023
+ // Narrowing may exclude a record/class arm while storage retains
3024
+ // it. The checked retag traps that impossible arm, but only AFTER
3025
+ // the original nullish test: null/undefined must reach the lazy
3026
+ // default even when neither is representable in the result.
3027
+ return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({ kind: "call", callee: retag, args: [stable], type, loc }));
2854
3028
  }
2855
3029
  }
2856
3030
  lowerer.unsupported("SC1090", expr, rest.length !== 1
2857
3031
  ? `'??' on '${lowerer.fmt(left.type)}' (the non-nullish result is a sub-union; check a discriminant field first)`
2858
3032
  : `'??' where the default changes the result type (left is '${lowerer.fmt(left.type)}' but the whole expression is '${lowerer.fmt(type)}' — give both sides one type)`);
2859
3033
  }
2860
- /** Interned `%nullish.retag.<n>(l, r)` — the retagged `??` (see
2861
- * lowerNullishCoalesce): unit-armed left answers the pre-evaluated
2862
- * default; every other arm narrows out of the left union and wraps into
2863
- * the result union's matching arm. */
2864
- function nullishRetagHelper(lowerer, leftT, resT, loc) {
2865
- const key = `nullish:${leftT.unionId}:${typeKey(resT)}`;
2866
- const existing = lowerer.widthHelpers.get(key);
2867
- if (existing)
2868
- return existing;
2869
- const name = `%nullish.retag.${lowerer.widthHelpers.size}`;
2870
- lowerer.widthHelpers.set(key, name);
2871
- const def = lowerer.unions.get(leftT.unionId);
2872
- const l = varRef("l.0", leftT, loc);
2873
- const r = varRef("r.0", resT, loc);
2874
- const body = [];
2875
- def.arms.forEach((arm, tag) => {
2876
- if (isUnitType(arm))
2877
- return;
2878
- const dst = lowerer.armTag(resT.unionId, arm);
2879
- body.push({
2880
- kind: "if",
2881
- cond: { kind: "unionIsTag", unionId: leftT.unionId, tag, negated: false, value: l, type: BOOL, loc },
2882
- then: [
2883
- {
2884
- kind: "return",
2885
- value: {
2886
- kind: "unionWrap",
2887
- unionId: resT.unionId,
2888
- tag: dst,
2889
- value: { kind: "unionNarrow", unionId: leftT.unionId, tag, value: l, type: arm, loc },
2890
- type: resT,
2891
- loc,
2892
- },
2893
- loc,
2894
- },
2895
- ],
2896
- else_: null,
2897
- loc,
2898
- });
2899
- });
2900
- body.push({ kind: "return", value: r, loc });
2901
- lowerer.liftedFns.push({
2902
- name,
2903
- params: [
2904
- { localId: "l.0", name: "l", type: leftT },
2905
- { localId: "r.0", name: "r", type: resT },
2906
- ],
2907
- returnType: resT,
2908
- locals: [
2909
- { id: "l.0", name: "l", type: leftT, mutable: true },
2910
- { id: "r.0", name: "r", type: resT, mutable: true },
2911
- ],
2912
- body,
2913
- loc,
2914
- });
2915
- return name;
3034
+ /** Test the original storage before projecting its present value. Both
3035
+ * branches are lazy, and an effectful receiver is evaluated exactly once.
3036
+ * Keeping the original tags matters when the checker has removed an arm
3037
+ * or the default changes the union's layout. */
3038
+ function nullishBranches(lowerer, left, leftType, arms, type, loc, absent, present) {
3039
+ const stmts = [];
3040
+ let stable = left;
3041
+ if (!isSafeToRepeat(left)) {
3042
+ const local = lowerer.declareHiddenLocal("%nullish", leftType);
3043
+ stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
3044
+ stable = varRef(local.id, leftType, loc);
3045
+ }
3046
+ const unitTags = arms.flatMap((arm, tag) => isUnitType(arm) ? [tag] : []);
3047
+ let test = null;
3048
+ for (const tag of unitTags) {
3049
+ const part = { kind: "unionIsTag", unionId: leftType.unionId, tag, negated: false, value: stable, type: BOOL, loc };
3050
+ test = test === null ? part : { kind: "logical", op: "||", left: test, right: part, type: BOOL, loc };
3051
+ }
3052
+ if (test === null)
3053
+ throw new InternalCompilerError("nullish branch requires an absent arm");
3054
+ const result = { kind: "ternary", cond: test, then: absent(), else_: present(stable), type, loc };
3055
+ return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
2916
3056
  }
2917
3057
  /** A CONDITION-position expression: the result is consumed as a bool
2918
3058
  * only, so `&&`/`||` descend recursively over ToBoolean'd operands —
@@ -2971,14 +3111,23 @@ function lowerPromiseThenPresence(lowerer, access, result) {
2971
3111
  ? (() => {
2972
3112
  const arms = lowerer.unions.get(mapped.unionId)?.arms ?? [];
2973
3113
  const promises = arms.filter((arm) => arm.kind === "promise");
2974
- return promises.length === 1 && arms.every((arm) => arm.kind === "promise" || isUnitType(arm))
2975
- ? promises[0]
3114
+ const promise = promises[0];
3115
+ return promises.length === 1 && promise?.kind === "promise" && arms.every((arm) => arm.kind === "promise" || isUnitType(arm))
3116
+ ? promise
2976
3117
  : null;
2977
3118
  })()
2978
3119
  : null;
2979
3120
  if (promiseType === null)
2980
3121
  return null;
2981
3122
  let receiver = lowerer.lowerExpr(access.expression);
3123
+ if (receiver.type.kind === "union" && access.questionDotToken) {
3124
+ const arms = lowerer.unions.get(receiver.type.unionId)?.arms ?? [];
3125
+ if (arms.every((arm) => arm.kind === "promise" || isUnitType(arm))) {
3126
+ return nullishBranches(lowerer, receiver, receiver.type, arms, result.type, locOf(access), () => result.type.kind === "bool"
3127
+ ? { kind: "boolLit", value: false, type: BOOL, loc: result.loc }
3128
+ : { kind: "strLit", value: "undefined", type: STRING, loc: result.loc }, () => result);
3129
+ }
3130
+ }
2982
3131
  if (receiver.type.kind === "union") {
2983
3132
  const optional = lowerer.runtimeOptionalPropertyReceiver(access.expression, receiver, promiseType, access.name.text);
2984
3133
  if (optional !== null)
@@ -2990,6 +3139,29 @@ function lowerPromiseThenPresence(lowerer, access, result) {
2990
3139
  receiver = { kind: "call", callee: helper, args: [receiver], type: promiseType, loc: receiver.loc };
2991
3140
  }
2992
3141
  }
3142
+ if (receiver.type.kind === "dyn") {
3143
+ // A specialized JS parameter can retain checked storage while JSDoc
3144
+ // describes a promise. Probe its actual value, preserving nullish
3145
+ // guards and own-property reads on ordinary thenable objects.
3146
+ const loc = locOf(access);
3147
+ const local = lowerer.declareHiddenLocal("%thenReceiver", DYN);
3148
+ const stable = varRef(local.id, DYN, loc);
3149
+ const member = {
3150
+ kind: "dynKeyGet", value: stable,
3151
+ key: { kind: "strLit", value: "then", type: STRING, loc },
3152
+ ...(access.questionDotToken ? { optional: true } : {}), type: DYN, loc,
3153
+ };
3154
+ const other = result.type.kind === "bool"
3155
+ ? { kind: "dynTest", test: "truthy", value: member, type: BOOL, loc }
3156
+ : { kind: "libCall", fn: "dyn.typeof", args: [member], type: STRING, loc };
3157
+ return {
3158
+ kind: "seqExpr", stmts: [{ kind: "varDecl", localId: local.id, init: receiver, loc }],
3159
+ result: {
3160
+ kind: "ternary", cond: { kind: "dynTest", test: "promise", value: stable, type: BOOL, loc },
3161
+ then: result, else_: other, type: result.type, loc,
3162
+ }, type: result.type, loc,
3163
+ };
3164
+ }
2993
3165
  if (receiver.type.kind !== "promise")
2994
3166
  return null;
2995
3167
  if (isSafeToDiscard(receiver))
@@ -3012,15 +3184,107 @@ function runtimeOptionalLocalOf(lowerer, node) {
3012
3184
  return local && lowerer.runtimeOptionalLocals.has(lowerer.runtimeOptionalRootOf(local)) ? local : null;
3013
3185
  }
3014
3186
  export function runtimeOptionalTrueIds(lowerer, node) {
3015
- let expr = node;
3016
- while (ts.isParenthesizedExpression(expr))
3017
- expr = expr.expression;
3018
- if (ts.isBinaryExpression(expr) &&
3019
- expr.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
3020
- return [...runtimeOptionalTrueIds(lowerer, expr.left), ...runtimeOptionalTrueIds(lowerer, expr.right)];
3021
- }
3022
- const local = runtimeOptionalLocalOf(lowerer, expr);
3023
- return local === null ? [] : [local];
3187
+ return runtimeOptionalGuardIds(lowerer, node, true);
3188
+ }
3189
+ /** Presence proved by one outcome of a boolean guard. Negation flips
3190
+ * the outcome; all operands of a true AND or false OR have that outcome.
3191
+ * Assignments invalidate a syntactic proof even when checker types hide
3192
+ * the undefined produced by an out-of-bounds read. */
3193
+ export function runtimeOptionalGuardIds(lowerer, node, truthy) {
3194
+ const candidates = [];
3195
+ const collect = (expr, outcome) => {
3196
+ if (ts.isParenthesizedExpression(expr))
3197
+ return collect(expr.expression, outcome);
3198
+ if (ts.isPrefixUnaryExpression(expr) && expr.operator === ts.SyntaxKind.ExclamationToken) {
3199
+ return collect(expr.operand, outcome === null ? null : !outcome);
3200
+ }
3201
+ if (ts.isBinaryExpression(expr) &&
3202
+ (outcome === true && expr.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
3203
+ outcome === false && expr.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
3204
+ collect(expr.left, outcome);
3205
+ collect(expr.right, outcome);
3206
+ }
3207
+ else if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) {
3208
+ // A completed ordinary member read checked its receiver even if
3209
+ // the property's value was falsy. Optional chains make no such
3210
+ // promise, and calls/captures retain their separate checked ABI.
3211
+ const local = runtimeOptionalLocalOf(lowerer, expr.expression);
3212
+ if (local !== null)
3213
+ candidates.push(local);
3214
+ else if (ts.isPropertyAccessExpression(expr.expression))
3215
+ collect(expr.expression, outcome);
3216
+ }
3217
+ else if (ts.isBinaryExpression(expr) &&
3218
+ expr.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken &&
3219
+ expr.operatorToken.kind !== ts.SyntaxKind.BarBarToken &&
3220
+ expr.operatorToken.kind !== ts.SyntaxKind.QuestionQuestionToken) {
3221
+ // Both operands of a comparison/arithmetic operator are evaluated.
3222
+ // Only member reads prove presence; bare identifiers need truth.
3223
+ const member = (operand) => {
3224
+ while (ts.isParenthesizedExpression(operand))
3225
+ operand = operand.expression;
3226
+ if (ts.isPropertyAccessExpression(operand))
3227
+ collect(operand, outcome);
3228
+ };
3229
+ member(expr.left);
3230
+ member(expr.right);
3231
+ const op = expr.operatorToken.kind;
3232
+ const equal = op === ts.SyntaxKind.EqualsEqualsEqualsToken || op === ts.SyntaxKind.EqualsEqualsToken;
3233
+ const unequal = op === ts.SyntaxKind.ExclamationEqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken;
3234
+ if ((equal && outcome === false) || (unequal && outcome === true)) {
3235
+ const present = (value, unit) => {
3236
+ while (ts.isParenthesizedExpression(unit))
3237
+ unit = unit.expression;
3238
+ const excludesUndefined = ts.isIdentifier(unit) && unit.text === "undefined" &&
3239
+ (lowerer.typeOf(unit).flags & ts.TypeFlags.Undefined) !== 0 ||
3240
+ unit.kind === ts.SyntaxKind.NullKeyword &&
3241
+ (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken);
3242
+ if (excludesUndefined) {
3243
+ const local = runtimeOptionalLocalOf(lowerer, value);
3244
+ if (local !== null)
3245
+ candidates.push(local);
3246
+ }
3247
+ };
3248
+ present(expr.left, expr.right);
3249
+ present(expr.right, expr.left);
3250
+ }
3251
+ }
3252
+ else if (outcome) {
3253
+ const local = runtimeOptionalLocalOf(lowerer, expr);
3254
+ if (local !== null)
3255
+ candidates.push(local);
3256
+ }
3257
+ };
3258
+ collect(node, truthy);
3259
+ if (candidates.length === 0)
3260
+ return candidates;
3261
+ const assigned = new Set();
3262
+ const visit = (part) => {
3263
+ if (ts.isFunctionLike(part))
3264
+ return;
3265
+ let target = null;
3266
+ if (ts.isBinaryExpression(part) && part.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
3267
+ part.operatorToken.kind <= ts.SyntaxKind.LastAssignment)
3268
+ target = part.left;
3269
+ if ((ts.isPrefixUnaryExpression(part) || ts.isPostfixUnaryExpression(part)) &&
3270
+ (part.operator === ts.SyntaxKind.PlusPlusToken || part.operator === ts.SyntaxKind.MinusMinusToken))
3271
+ target = part.operand;
3272
+ if (target !== null) {
3273
+ // Destructuring assignments can contain several identifiers.
3274
+ const mark = (name) => {
3275
+ if (ts.isIdentifier(name)) {
3276
+ const local = lowerer.resolveLocal(name);
3277
+ if (local)
3278
+ assigned.add(lowerer.runtimeOptionalRootOf(local));
3279
+ }
3280
+ ts.forEachChild(name, mark);
3281
+ };
3282
+ mark(target);
3283
+ }
3284
+ ts.forEachChild(part, visit);
3285
+ };
3286
+ visit(node);
3287
+ return candidates.filter((local) => !assigned.has(lowerer.runtimeOptionalRootOf(local)));
3024
3288
  }
3025
3289
  export function withRuntimeOptionalNarrowed(lowerer, locals, fn) {
3026
3290
  if (locals.length === 0)
@@ -3104,6 +3368,15 @@ export function eqComparableUnion(lowerer, unionId) {
3104
3368
  export function lowerIntrinsicProperty(lowerer, expr) {
3105
3369
  if (lowerer.chainBlocked(expr))
3106
3370
  return null;
3371
+ if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
3372
+ return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
3373
+ if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
3374
+ return lowerArrayIsArrayValue(lowerer, locOf(expr));
3375
+ if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
3376
+ const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
3377
+ if (fn)
3378
+ return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
3379
+ }
3107
3380
  // A never-tainted JS receiver type lowered checked-dynamic
3108
3381
  // (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
3109
3382
  // stand down so the dyn keyed read below the chain answers, instead
@@ -3139,6 +3412,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3139
3412
  const name = expr.name.text;
3140
3413
  const strictReceiver = (expected) => {
3141
3414
  const value = lowerer.lowerExpr(expr.expression);
3415
+ if (value.type.kind === "dyn")
3416
+ return lowerer.coerceInto(expr.expression, value, expected);
3142
3417
  return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
3143
3418
  };
3144
3419
  if (kind === "child") {
@@ -3359,15 +3634,14 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3359
3634
  // SEMANTICS.md notes the divergence from Node's Buffer pooling),
3360
3635
  // the view's real offset for a DataView. A runtime read, so the
3361
3636
  // receiver's evaluation is never discarded.
3362
- const receiver = lowerer.lowerExpr(expr.expression);
3637
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3638
+ const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3363
3639
  return { kind: "bytesIntrinsic", method: "byteOffset", receiver, args: [], type: F64, loc };
3364
3640
  }
3365
3641
  if (name === "buffer") {
3366
- // No ArrayBuffer VALUE exists — `.buffer` compiles only in the
3367
- // one composed position that consumes it, the DataView
3368
- // constructor's first argument (lowerDataViewNew peels it).
3369
- lowerer.unsupported("SC1090", expr, "'.buffer' outside new DataView(x.buffer, ...) / Buffer.from(x.buffer, ...) " +
3370
- "(no free-standing ArrayBuffer value exists; subarray() answers an aliasing view directly)");
3642
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3643
+ const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3644
+ return { kind: "bytesIntrinsic", method: "buffer", receiver, args: [], type: DYN, loc };
3371
3645
  }
3372
3646
  if (name === "slice" || name === "subarray" || name === "set" || name === "toString") {
3373
3647
  lowerer.unsupported("SC1090", expr, `typed-array methods as values (call '${name}' directly)`);
@@ -3376,7 +3650,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3376
3650
  }
3377
3651
  if (kind === "map") {
3378
3652
  if (name === "size") {
3379
- const receiver = lowerer.lowerExpr(expr.expression);
3653
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3654
+ const receiver = expected?.kind === "map" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3380
3655
  return { kind: "mapIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
3381
3656
  }
3382
3657
  if (MAP_METHODS.has(name) || name === "forEach") {
@@ -3386,7 +3661,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3386
3661
  }
3387
3662
  if (kind === "set") {
3388
3663
  if (name === "size") {
3389
- const receiver = lowerer.lowerExpr(expr.expression);
3664
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3665
+ const receiver = expected?.kind === "set" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3390
3666
  return { kind: "setIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
3391
3667
  }
3392
3668
  if (SET_METHODS.has(name)) {
@@ -3629,6 +3905,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3629
3905
  // to DYN wholesale now (mapType's dyn-element array rule), so the
3630
3906
  // literal IS the dyn array.
3631
3907
  if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
3908
+ if (expr.elements.some(ts.isSpreadElement)) {
3909
+ // Native collection drains return vectors of retained dyn
3910
+ // elements. Reuse the ordinary spread builder, then expose the
3911
+ // resulting array through the established unknown[] representation.
3912
+ const vector = lowerArrayLiteral(lowerer, expr, arrayOf(DYN));
3913
+ return lowerer.coerceInto(expr, vector, DYN);
3914
+ }
3632
3915
  const elems = expr.elements.map((el) => {
3633
3916
  if (ts.isSpreadElement(el)) {
3634
3917
  lowerer.unsupported("SC1090", el, "spread elements in a dynamic (unknown[]) array literal");
@@ -3679,19 +3962,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3679
3962
  (ts.isConditionalExpression(srcNode)
3680
3963
  ? lowerTernary(lowerer, srcNode, type)
3681
3964
  : lowerer.lowerExpr(el.expression));
3682
- // `[...someSet]`: a same-element Set drains into a fresh array in
3683
- // insertion order (setIntrinsic toArray); the spread machinery
3684
- // then copies like any array source.
3685
- if (src.type.kind === "set" && typeEquals(src.type.elem, type.elem)) {
3686
- src = {
3687
- kind: "setIntrinsic",
3688
- method: "toArray",
3689
- receiver: src,
3690
- args: [],
3691
- type: arrayOf(src.type.elem),
3692
- loc: locOf(el),
3693
- };
3694
- }
3965
+ src = lowerCollectionSpread(lowerer, src, el.expression) ?? src;
3695
3966
  // `[...new SymbolIterator]`: a CLASS ITERABLE drains through its
3696
3967
  // own protocol into a fresh element array (classIteratorDrainCall
3697
3968
  // — an infinite iterator loops forever, exactly Node), and the
@@ -3723,6 +3994,16 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3723
3994
  loc: locOf(el),
3724
3995
  };
3725
3996
  }
3997
+ // A native checked iterable can supply scalar elements even when
3998
+ // the checker inferred a typed array from its producer. Drain once
3999
+ // and validate the elements before the ordinary spread copy.
4000
+ if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
4001
+ src = lowerer.coerceInto(el.expression, {
4002
+ kind: "libCall", fn: "dyn.iterPack", args: [src,
4003
+ { kind: "strLit", value: el.expression.getText(), type: STRING, loc: locOf(el) }],
4004
+ type: DYN, loc: locOf(el),
4005
+ }, type);
4006
+ }
3726
4007
  // A same-family array whose ELEMENT lifts (string[] into a
3727
4008
  // (string | symbol)[] literal — per-element wrap/width copy):
3728
4009
  // the interned width helper reshapes before the spread copies.
@@ -3982,16 +4263,17 @@ export function lowerElementAccess(lowerer, expr) {
3982
4263
  lowerer.fenceStaticResponseMember(expr, "read");
3983
4264
  lowerer.fenceStaticHeadersMember(expr, "read");
3984
4265
  lowerer.fenceStaticReadableStreamMember(expr, "read");
3985
- // `globalThis[<expr>]` — the dynamic global probe (the harness's
3986
- // conditional-globals sweep): a compiled binary's globals are
3987
- // compile-time bindings, not runtime properties, and none of the
3988
- // probed web-platform objects exist here — the honest answer is
3989
- // undefined for every dynamic name (SEMANTICS.md documents the
3990
- // divergence: statically-known globals are not reachable this way;
3991
- // the PROPERTY spelling answers the identity token instead — see
3992
- // lowerPropertyAccess's globalThis rule).
4266
+ // Computed global reads share the stored global object's custom
4267
+ // properties. Symbol keys retain their identity-indexed store.
3993
4268
  if (!expr.questionDotToken && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis") {
3994
- return { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc: locOf(expr) };
4269
+ const symbol = globalSymbolKey(lowerer, expr.expression, expr.argumentExpression);
4270
+ if (symbol)
4271
+ return { kind: "libCall", fn: "dyn.globalSymbolGet", args: [symbol], type: DYN, loc: locOf(expr) };
4272
+ const key = lowerer.lowerExpr(expr.argumentExpression);
4273
+ const name = key.type.kind === "string" ? key : {
4274
+ kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(expr.argumentExpression, key, DYN)], type: STRING, loc: locOf(expr),
4275
+ };
4276
+ return { kind: "dynKeyGet", value: lowerGlobalValue(lowerer, expr.expression), key: name, type: DYN, loc: locOf(expr) };
3995
4277
  }
3996
4278
  // `req.headers["x-name"]` — the computed twin of `req.headers.host`
3997
4279
  // (the server spoke owns both; the envGet precedent).
@@ -4004,7 +4286,7 @@ export function lowerElementAccess(lowerer, expr) {
4004
4286
  // lower to the ONE process.envGet intrinsic. The read narrows like any
4005
4287
  // union-typed expression when the checker narrowed this occurrence.
4006
4288
  if (lowerer.isProcessEnv(expr.expression)) {
4007
- const key = lowerer.lowerExpr(expr.argumentExpression);
4289
+ const key = lowerEnvironmentKey(lowerer, expr.argumentExpression);
4008
4290
  if (key.type.kind !== "string") {
4009
4291
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing process.env with non-string keys");
4010
4292
  }
@@ -4027,17 +4309,27 @@ export function lowerElementAccess(lowerer, expr) {
4027
4309
  return lowerer.maybeNarrow(ex, expr);
4028
4310
  }
4029
4311
  // Symbol-keyed property READS (`this[kLimit]`): when the key is a
4030
- // statically-resolvable unique-symbol const declared as a field of the
4031
- // receiver's class (uniqueSymbolKeyOf — the countdown.js idiom), the
4312
+ // statically-resolvable symbol declared as a field of the receiver's
4313
+ // class (classSymbolKeyOf — the countdown.js/OpenTUI idioms), the
4032
4314
  // read IS an ordinary field read of the hidden slot. Every other
4033
4315
  // symbol key stays fenced: record shapes, runtime-identity keys
4034
- // (symbol parameters, Symbol.for consts), and keys no class declares
4316
+ // (symbol parameters, reassigned bindings), and keys no class declares
4035
4317
  // — the layouts are compile-time field lists.
4036
4318
  if (lowerer.mapTypeOf(lowerer.typeOf(expr.argumentExpression))?.kind === "symbol") {
4037
4319
  const target = symbolFieldTarget(lowerer, expr);
4038
4320
  if (target)
4039
4321
  return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4040
- lowerer.unsupported("SC1090", expr, "symbol-keyed property access outside class fields keyed by a module-level `const k = Symbol('desc')` (static shapes have no symbol-keyed storage)");
4322
+ let receiver = lowerer.lowerExpr(expr.expression);
4323
+ if (receiver.type.kind === "func") {
4324
+ const fnName = jsFuncNameOf(expr.expression);
4325
+ receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
4326
+ ...(fnName !== null ? { fnName } : {}) };
4327
+ }
4328
+ if (receiver.type.kind === "dyn") {
4329
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4330
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
4331
+ }
4332
+ lowerer.unsupported("SC1090", expr, "symbol-keyed property access outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
4041
4333
  }
4042
4334
  // A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
4043
4335
  // `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
@@ -4046,6 +4338,16 @@ export function lowerElementAccess(lowerer, expr) {
4046
4338
  let receiverIr = neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression))
4047
4339
  ? null
4048
4340
  : lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
4341
+ if (isJsSourceFile(expr.getSourceFile())) {
4342
+ const receiver = tryLowerExpression(lowerer, expr.expression);
4343
+ if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
4344
+ const loc = locOf(expr);
4345
+ const fnName = jsFuncNameOf(expr.expression);
4346
+ const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
4347
+ const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4348
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
4349
+ }
4350
+ }
4049
4351
  // A readonly tuple union under Array.isArray is checker-typed as an
4050
4352
  // intersection with any[], whose structural mapping is not the tuple
4051
4353
  // shape. maybeNarrow on the receiver uses the runtime tag proof to
@@ -4054,6 +4356,19 @@ export function lowerElementAccess(lowerer, expr) {
4054
4356
  const checkerArray = lowerer.checkerArrayValue(expr.expression);
4055
4357
  if (checkerArray)
4056
4358
  receiverIr = checkerArray.type;
4359
+ if (receiverIr?.kind === "object") {
4360
+ const key = recordKeyLiteralText(expr.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, expr.argumentExpression);
4361
+ if (key !== null) {
4362
+ const target = classFieldTarget(lowerer, expr.expression, receiverIr, key);
4363
+ if (target)
4364
+ return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4365
+ }
4366
+ if (isDynTypedRefType(receiverIr)) {
4367
+ const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
4368
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4369
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
4370
+ }
4371
+ }
4057
4372
  if (receiverIr?.kind === "jsval") {
4058
4373
  // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
4059
4374
  // receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
@@ -4079,6 +4394,8 @@ export function lowerElementAccess(lowerer, expr) {
4079
4394
  // read undefined — divergence 4's policy).
4080
4395
  if (receiverIr?.kind === "bytes") {
4081
4396
  let recv = lowerer.lowerExpr(expr.expression);
4397
+ if (recv.type.kind === "dyn")
4398
+ recv = lowerer.coerceInto(expr.expression, recv, receiverIr);
4082
4399
  if (recv.type.kind === "union" && lowerer.armTag(recv.type.unionId, UNDEFINED_T) >= 0) {
4083
4400
  const present = lowerer.stripUndefinedArm(recv.type);
4084
4401
  const helper = present.kind === "bytes"
@@ -4095,6 +4412,9 @@ export function lowerElementAccess(lowerer, expr) {
4095
4412
  if (recv.type.kind === "jsval")
4096
4413
  return islandElementRead(lowerer, expr, recv);
4097
4414
  const index = lowerer.lowerExpr(expr.argumentExpression);
4415
+ if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
4416
+ return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
4417
+ }
4098
4418
  if (index.type.kind !== "f64") {
4099
4419
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
4100
4420
  }
@@ -4116,6 +4436,17 @@ export function lowerElementAccess(lowerer, expr) {
4116
4436
  // like the array path.
4117
4437
  if (obj.type.kind === "jsval")
4118
4438
  return islandElementRead(lowerer, expr, obj);
4439
+ if (obj.type.kind === "dyn") {
4440
+ // Object.entries over a checked value stores each pair in the
4441
+ // dynamic tree even when the checker names a tuple type.
4442
+ const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4443
+ const optional = hasOptionalChainGuard(expr.expression);
4444
+ return lowerer.maybeNarrow({
4445
+ kind: "dynKeyGet", value: obj, key,
4446
+ ...(optional ? { optional: true } : {}),
4447
+ type: DYN, loc: locOf(expr),
4448
+ }, expr);
4449
+ }
4119
4450
  if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
4120
4451
  // The checker sees the outer `arrays[i]` as a tuple when
4121
4452
  // noUncheckedIndexedAccess is disabled, but the lowered read is
@@ -4171,21 +4502,28 @@ export function lowerElementAccess(lowerer, expr) {
4171
4502
  // (`pkg.workspaces.packages["0"]` — the lowering world types the
4172
4503
  // unknown-rooted chain `any`); a non-dyn lowering falls through to
4173
4504
  // the fences below (re-lowering is pure IR construction).
4174
- if (receiverIr?.kind === "dyn" || receiverIr === null) {
4505
+ if (receiverIr?.kind === "dyn" || receiverIr?.kind === "union" || receiverIr === null) {
4175
4506
  const obj = lowerer.lowerExpr(expr.expression);
4176
- if (obj.type.kind === "dyn") {
4507
+ // A generic mapped type can stay unresolved at this body use even
4508
+ // though its instantiated parameter has a concrete record ABI.
4509
+ // Dispatch from that stored shape, as the array fallback below does.
4510
+ if (receiverIr === null && obj.type.kind === "record") {
4511
+ const shape = lowerer.shapes.get(obj.type.shapeId);
4512
+ if (shape && !shape.tuple) {
4513
+ return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
4514
+ }
4515
+ }
4516
+ if (obj.type.kind === "dyn" || isDynTypedRefType(obj.type)) {
4177
4517
  const rawKey = lowerer.lowerExpr(expr.argumentExpression);
4178
4518
  // Number, bool, and DYN keys stringify (ToPropertyKey) — the
4179
4519
  // dyn-keyed read `catchWarning[warning.name]` where the property
4180
4520
  // chain itself lowered dyn.
4181
- const key = rawKey.type.kind === "string"
4182
- ? rawKey
4183
- : rawKey.type.kind === "f64" || rawKey.type.kind === "bool" || rawKey.type.kind === "dyn"
4184
- ? { kind: "toString", operand: rawKey, type: STRING, loc: rawKey.loc }
4185
- : null;
4186
- if (key) {
4521
+ const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol"
4522
+ ? lowerer.coerceToExpected(rawKey, DYN)
4523
+ : lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
4524
+ if (key.type.kind === "string" || key.type.kind === "dyn") {
4187
4525
  const opt = hasOptionalChainGuard(expr.expression);
4188
- return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: obj, type: DYN, loc: locOf(expr) }, expr);
4526
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(obj, DYN), type: DYN, loc: locOf(expr) }, expr);
4189
4527
  }
4190
4528
  }
4191
4529
  // A checker-`any` receiver that LOWERS to a static ARRAY
@@ -4195,6 +4533,9 @@ export function lowerElementAccess(lowerer, expr) {
4195
4533
  if (obj.type.kind === "array") {
4196
4534
  const index = lowerer.lowerExpr(expr.argumentExpression);
4197
4535
  if (index.type.kind === "f64") {
4536
+ const safe = lowerSafeIndexRead(lowerer, obj, index, locOf(expr));
4537
+ if (safe)
4538
+ return safe;
4198
4539
  return lowerer.maybeNarrow({ kind: "arrayGet", arr: obj, index, type: obj.type.elem, loc: locOf(expr) }, expr);
4199
4540
  }
4200
4541
  }
@@ -4217,7 +4558,7 @@ export function lowerElementAccess(lowerer, expr) {
4217
4558
  }
4218
4559
  }
4219
4560
  }
4220
- if (key.type.kind === "string") {
4561
+ if (key.type.kind === "string" || key.type.kind === "f64") {
4221
4562
  const lit = ts.isStringLiteral(expr.argumentExpression)
4222
4563
  ? expr.argumentExpression.text
4223
4564
  : null;
@@ -4225,33 +4566,20 @@ export function lowerElementAccess(lowerer, expr) {
4225
4566
  if (keyed)
4226
4567
  return lowerer.maybeNarrow(keyed, expr);
4227
4568
  }
4228
- // `u?.split(":")[0]` — a NUMBER index over an undefined-armed
4229
- // array union: the array arm answers its element (invalid indices
4230
- // trap — divergence 4's policy), unit arms answer undefined (the
4231
- // chain's short-circuit value). Every non-unit arm must be the
4232
- // same array type.
4233
- if (key.type.kind === "f64") {
4234
- const def = lowerer.unions.get(value.type.unionId);
4235
- const arrArms = def?.arms.filter((a) => a.kind === "array") ?? [];
4236
- if (def &&
4237
- arrArms.length > 0 &&
4238
- def.arms.every((a) => a.kind === "array" || isUnitType(a)) &&
4239
- arrArms.every((a) => typeEquals(a, arrArms[0]))) {
4240
- const elem = arrArms[0].elem;
4241
- const hasUnit = def.arms.some(isUnitType);
4242
- const t = !hasUnit
4243
- ? elem
4244
- : elem.kind === "union"
4245
- ? lowerer.withUndefinedArmOf(elem)
4246
- : lowerer.withUndefinedArm(elem);
4247
- if (t) {
4248
- return lowerer.maybeNarrow({ kind: "unionKeyGet", unionId: value.type.unionId, key, value, type: t, loc: locOf(expr) }, expr);
4249
- }
4250
- }
4251
- }
4252
4569
  }
4253
4570
  }
4254
4571
  if (receiverIr?.kind !== "array") {
4572
+ {
4573
+ const receiver = tryLowerExpression(lowerer, expr.expression);
4574
+ if (receiver?.type.kind === "func" &&
4575
+ canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
4576
+ const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4577
+ if (key.type.kind === "string") {
4578
+ const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr.expression) };
4579
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc: locOf(expr) }, expr);
4580
+ }
4581
+ }
4582
+ }
4255
4583
  if (receiverIr?.kind === "string") {
4256
4584
  // `s[i]` with a number index reads a UTF-16 code unit — charAt's
4257
4585
  // exact job (the UTF-16-exact runtime). Without
@@ -4259,16 +4587,23 @@ export function lowerElementAccess(lowerer, expr) {
4259
4587
  // and charAt's "" is the only string-typed answer for an
4260
4588
  // out-of-range or fractional index where JS reads `undefined` —
4261
4589
  // SEMANTICS.md documents the divergence; in-range integer reads
4262
- // (the loop pattern) are JS-exact. Under noUncheckedIndexedAccess
4263
- // the read types `string | undefined`, which charAt cannot honor —
4264
- // fenced.
4590
+ // (the loop pattern) are JS-exact. Optional results instead retain
4591
+ // undefined for missing string properties.
4592
+ const recv = lowerer.lowerExpr(expr.expression);
4265
4593
  const index = lowerer.lowerExpr(expr.argumentExpression);
4266
- if (index.type.kind === "f64" && lowerer.mapTypeOf(lowerer.typeOf(expr))?.kind === "string") {
4267
- const recv = lowerer.lowerExpr(expr.expression);
4268
- return { kind: "strIntrinsic", method: "charAt", receiver: recv, args: [index], type: STRING, loc: locOf(expr) };
4594
+ const resultType = lowerer.mapTypeOf(lowerer.typeOf(expr));
4595
+ if (index.type.kind === "f64" && recv.type.kind === "string") {
4596
+ if (resultType?.kind === "string") {
4597
+ return { kind: "strIntrinsic", method: "charAt", receiver: recv, args: [index], type: STRING, loc: locOf(expr) };
4598
+ }
4599
+ if (resultType?.kind === "union") {
4600
+ const arms = lowerer.unions.get(resultType.unionId)?.arms;
4601
+ if (arms?.length === 2 && lowerer.armTag(resultType.unionId, STRING) >= 0 && lowerer.armTag(resultType.unionId, UNDEFINED_T) >= 0) {
4602
+ return lowerOptionalStringIndex(lowerer, recv, index, resultType, locOf(expr));
4603
+ }
4604
+ }
4269
4605
  }
4270
- lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (a number index typed 'string' lowers to charAt; " +
4271
- "use .charAt(i) under noUncheckedIndexedAccess)");
4606
+ lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (expected a number index and a string or string | undefined result)");
4272
4607
  }
4273
4608
  lowerer.unsupported("SC1090", expr, "element access on non-array values");
4274
4609
  }
@@ -4346,14 +4681,17 @@ export function lowerElementAccess(lowerer, expr) {
4346
4681
  // can validate, and fences by name when it is not — an arrayGet over a
4347
4682
  // dyn receiver is never emitted (the validator ICE).
4348
4683
  if (arr.type.kind === "dyn") {
4349
- if (isJsonSafeType(receiverIr, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
4684
+ if (canDynCheckTo(receiverIr, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
4350
4685
  arr = { kind: "dynCheck", value: arr, type: receiverIr, loc: locOf(expr) };
4351
4686
  }
4352
4687
  else {
4353
4688
  lowerer.unsupported("SC1090", expr, `element access on checked-dynamic values with '${lowerer.fmt(receiverIr)}' elements`);
4354
4689
  }
4355
4690
  }
4356
- const index = lowerer.lowerExpr(expr.argumentExpression);
4691
+ // Array reads can themselves supply an index. A missing numeric value
4692
+ // is undefined, whose property lookup misses just like NaN; keep that
4693
+ // value instead of rejecting a checker-number index with optional IR.
4694
+ const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
4357
4695
  if (index.type.kind !== "f64") {
4358
4696
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
4359
4697
  }
@@ -4475,6 +4813,16 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4475
4813
  // to observe the missing value instead of throwing during that check.
4476
4814
  return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
4477
4815
  }
4816
+ if (litKey !== null) {
4817
+ const nominal = representedClassFieldTarget(lowerer, expr.expression, litKey, obj);
4818
+ if (nominal) {
4819
+ const read = lowerer.fieldGetExpr(nominal, loc, expr);
4820
+ return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
4821
+ }
4822
+ }
4823
+ if (hasClassPayload(lowerer, obj.type)) {
4824
+ lowerer.unsupported("SC1090", expr, "computed property reads through structural views of class instances (use a declared literal key)");
4825
+ }
4478
4826
  // Predicates can strengthen optional fields without changing the
4479
4827
  // receiver's stored layout. Use that layout for bracket reads just as
4480
4828
  // fieldTarget does for the corresponding dot reads.
@@ -4530,7 +4878,7 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4530
4878
  let declared = null;
4531
4879
  if (shape.indexValue) {
4532
4880
  declared = shape.indexValue;
4533
- if (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) {
4881
+ if (declared.kind !== "dyn" && (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess)) {
4534
4882
  declared = lowerer.withUndefinedArmOf(declared);
4535
4883
  if (!declared)
4536
4884
  lowerer.badType(expr, lowerer.typeOf(expr));
@@ -4540,6 +4888,8 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4540
4888
  shape.fields.every((f) => typeEquals(f.type, shape.fields[0].type))) {
4541
4889
  declared = shape.fields[0].type;
4542
4890
  }
4891
+ if (!declared && isJsSourceFile(expr.getSourceFile()) && recordKeyResultOk(lowerer, shape, DYN))
4892
+ declared = DYN;
4543
4893
  if (!declared) {
4544
4894
  lowerer.unsupported("SC1090", expr, `dynamic keyed reads of '${lowerer.fmt({ kind: "record", shapeId })}' (the declared fields have no one common type)`);
4545
4895
  }
@@ -4584,6 +4934,10 @@ export function lowerAbsenceProbe(lowerer, node) {
4584
4934
  }
4585
4935
  if (ts.isPropertyAccessExpression(expr)) {
4586
4936
  const target = lowerer.fieldTarget(expr);
4937
+ if (target?.container === "class" && target.fieldType.kind === "union" &&
4938
+ lowerer.unions.get(target.fieldType.unionId)?.arms.some(isUnitType)) {
4939
+ return lowerer.fieldGetExpr(target, locOf(expr), expr);
4940
+ }
4587
4941
  if (target?.container !== "recordOvf")
4588
4942
  return null;
4589
4943
  // A JS file-scope object-literal global is record-shaped to the
@@ -4685,6 +5039,7 @@ function recordKeyTypeLiteralText(lowerer, node) {
4685
5039
  function recordKeyResultOk(lowerer, shape, type) {
4686
5040
  const surfaces = (t) => typeEquals(t, type) ||
4687
5041
  (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
5042
+ exactUnionWidening(lowerer, t, type) ||
4688
5043
  (type.kind === "dyn" && lowerer.dynConvertible(t));
4689
5044
  if (!shape.fields.every((f) => surfaces(f.type)))
4690
5045
  return false;
@@ -4696,6 +5051,13 @@ function recordKeyResultOk(lowerer, shape, type) {
4696
5051
  }
4697
5052
  return true;
4698
5053
  }
5054
+ function exactUnionWidening(lowerer, from, to) {
5055
+ if (from.kind !== "union" || to.kind !== "union")
5056
+ return false;
5057
+ const source = lowerer.unions.get(from.unionId);
5058
+ const target = lowerer.unions.get(to.unionId);
5059
+ return !!source && !!target && unionWideningTags(source.arms, target.arms) !== null;
5060
+ }
4699
5061
  /** The literal index of a tuple access, or null when the expression isn't
4700
5062
  * a non-negative integer literal (parentheses tolerated — tsc's own
4701
5063
  * literal-index typing accepts them). */
@@ -4780,16 +5142,71 @@ export function lowerElementCompound(lowerer, expr, op) {
4780
5142
  loc,
4781
5143
  };
4782
5144
  }
4783
- /** `a[i] = v` in statement position → arraySet (element writes, like local
4784
- * assignment, produce no value in our subset). */
5145
+ /** Checked member assignment evaluates the reference before its RHS and
5146
+ * yields the original assigned value, including in a chained assignment. */
5147
+ function lowerDynMemberAssignment(lowerer, expr, recv) {
5148
+ const loc = locOf(expr);
5149
+ const target = expr.left;
5150
+ const receiver = lowerer.declareHiddenLocal("%setReceiver", DYN);
5151
+ const keyValue = ts.isElementAccessExpression(target)
5152
+ ? lowerer.lowerExprExpecting(target.argumentExpression, DYN)
5153
+ : lowerer.coerceToExpected({ kind: "strLit", value: target.name.text, type: STRING, loc }, DYN);
5154
+ const key = lowerer.declareHiddenLocal("%setKey", DYN);
5155
+ const raw = lowerer.lowerExpr(expr.right);
5156
+ const value = raw.type.kind === "void"
5157
+ ? {
5158
+ kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: raw, loc }],
5159
+ result: dynUndefinedExpr(loc), type: DYN, loc,
5160
+ }
5161
+ : isUnitType(raw.type) ? lowerer.coerceInto(expr.right, raw, DYN) : raw;
5162
+ const assigned = lowerer.declareHiddenLocal("%setValue", value.type);
5163
+ const result = varRef(assigned.id, value.type, loc);
5164
+ const stored = lowerer.coerceInto(expr.right, result, DYN);
5165
+ // Evaluate the reference before the RHS, but defer key coercion until
5166
+ // PutValue. Yield the RHS without rereading a setter-backed property.
5167
+ return {
5168
+ kind: "seqExpr", stmts: [
5169
+ { kind: "varDecl", localId: receiver.id, init: recv, loc },
5170
+ { kind: "varDecl", localId: key.id, init: keyValue, loc },
5171
+ { kind: "varDecl", localId: assigned.id, init: value, loc },
5172
+ { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [varRef(receiver.id, DYN, loc), varRef(key.id, DYN, loc), stored], type: VOID, loc }, loc },
5173
+ ], result, type: result.type, loc,
5174
+ };
5175
+ }
5176
+ /** JavaScript callable members live on the closure, so aliases and chained
5177
+ * writes share the same storage as Object.defineProperties. */
5178
+ export function lowerNativeFunctionAssignment(lowerer, expr) {
5179
+ const target = expr.left;
5180
+ if (!isJsSourceFile(expr.getSourceFile()) ||
5181
+ (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
5182
+ target.questionDotToken)
5183
+ return null;
5184
+ const receiver = tryLowerExpression(lowerer, target.expression);
5185
+ if (receiver?.type.kind !== "func" ||
5186
+ !canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
5187
+ return null;
5188
+ fenceNodeModuleMutation(lowerer, target, "assignment");
5189
+ const fnName = jsFuncNameOf(target.expression);
5190
+ const boxed = {
5191
+ kind: "dynFrom", value: receiver, type: DYN, loc: locOf(target.expression),
5192
+ ...(fnName !== null ? { fnName } : {}),
5193
+ };
5194
+ return lowerDynMemberAssignment(lowerer, expr, boxed);
5195
+ }
4785
5196
  export function lowerElementWrite(lowerer, expr) {
4786
5197
  const target = expr.left;
4787
5198
  fenceNodeModuleMutation(lowerer, target, "assignment");
5199
+ const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
5200
+ if (globalKey) {
5201
+ const value = lowerer.lowerExprExpecting(expr.right, DYN);
5202
+ const loc = locOf(expr);
5203
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.globalSymbolSet", args: [globalKey, value], type: VOID, loc }, loc };
5204
+ }
4788
5205
  // `process.env[key] = v` — the computed twin of the dotted env write:
4789
5206
  // setenv(3), string keys and string values only.
4790
5207
  if (lowerer.isProcessEnv(target.expression) && !target.questionDotToken) {
4791
5208
  const loc = locOf(expr);
4792
- const key = lowerer.lowerExpr(target.argumentExpression);
5209
+ const key = lowerEnvironmentKey(lowerer, target.argumentExpression);
4793
5210
  if (key.type.kind !== "string") {
4794
5211
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing process.env with non-string keys");
4795
5212
  }
@@ -4813,9 +5230,21 @@ export function lowerElementWrite(lowerer, expr) {
4813
5230
  const value = lowerer.lowerExprExpecting(expr.right, fieldT.fieldType);
4814
5231
  return lowerer.fieldSetStmt(fieldT, value, locOf(expr), target);
4815
5232
  }
4816
- lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a module-level `const k = Symbol('desc')` (static shapes have no symbol-keyed storage)");
5233
+ const receiver = lowerer.lowerExpr(target.expression);
5234
+ if (receiver.type.kind === "dyn")
5235
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
5236
+ lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
5237
+ }
5238
+ const checkedReceiver = tryLowerExpression(lowerer, target.expression);
5239
+ if (checkedReceiver?.type.kind === "dyn") {
5240
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
5241
+ }
5242
+ let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5243
+ if (receiverIr === null || receiverIr.kind === "dyn") {
5244
+ const receiver = tryLowerExpression(lowerer, target.expression);
5245
+ if (receiver?.type.kind === "array")
5246
+ receiverIr = receiver.type;
4817
5247
  }
4818
- const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
4819
5248
  if (receiverIr?.kind === "jsval") {
4820
5249
  const obj = lowerer.lowerExpr(target.expression);
4821
5250
  // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
@@ -4857,9 +5286,7 @@ export function lowerElementWrite(lowerer, expr) {
4857
5286
  const obj = lowerer.lowerExpr(target.expression);
4858
5287
  if (obj.type.kind === "dyn") {
4859
5288
  const loc = locOf(expr);
4860
- let key = lowerer.lowerExpr(target.argumentExpression);
4861
- if (key.type.kind === "f64")
4862
- key = lowerer.ensureString(key, target.argumentExpression);
5289
+ const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
4863
5290
  if (key.type.kind !== "string") {
4864
5291
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-string or non-number keys");
4865
5292
  }
@@ -4870,6 +5297,20 @@ export function lowerElementWrite(lowerer, expr) {
4870
5297
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
4871
5298
  }
4872
5299
  }
5300
+ if (receiverIr?.kind === "object" || receiverIr?.kind === "record") {
5301
+ const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
5302
+ if (key !== null) {
5303
+ const receiver = lowerer.lowerExpr(target.expression);
5304
+ const field = representedClassFieldTarget(lowerer, target.expression, key, receiver);
5305
+ if (field) {
5306
+ const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
5307
+ return lowerer.fieldSetStmt(field, value, locOf(expr), target);
5308
+ }
5309
+ if (hasClassPayload(lowerer, receiver.type)) {
5310
+ lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
5311
+ }
5312
+ }
5313
+ }
4873
5314
  // Typed-array element write `b[i] = v` — bytesSet: the value is an f64
4874
5315
  // the runtime coerces JS-exactly; invalid indices trap (no appends —
4875
5316
  // typed arrays are fixed-length; JS would ignore the write).
@@ -4884,6 +5325,9 @@ export function lowerElementWrite(lowerer, expr) {
4884
5325
  return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [recv, key, value], type: VOID, loc }, loc };
4885
5326
  }
4886
5327
  const index = lowerer.lowerExpr(target.argumentExpression);
5328
+ if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
5329
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, recv, DYN)), loc: locOf(expr) };
5330
+ }
4887
5331
  if (index.type.kind !== "f64") {
4888
5332
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
4889
5333
  }
@@ -4899,6 +5343,9 @@ export function lowerElementWrite(lowerer, expr) {
4899
5343
  // a recordSet/recordKeySet over a jsval.
4900
5344
  if (shape) {
4901
5345
  const obj = lowerer.lowerExpr(target.expression);
5346
+ if (hasClassPayload(lowerer, obj.type)) {
5347
+ lowerer.unsupported("SC1090", target, "computed property writes through structural views of class instances (use a declared literal key)");
5348
+ }
4902
5349
  if (obj.type.kind === "jsval") {
4903
5350
  const key = lowerer.jsvalIn(lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
4904
5351
  const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.right), expr.right);
@@ -5050,9 +5497,9 @@ export function lowerElementWrite(lowerer, expr) {
5050
5497
  // type (static `any` — mapTypeOf answers null without --dynamic)
5051
5498
  // probes the receiver's own lowered world: a dyn value takes the same
5052
5499
  // write, anything else falls through to the fences.
5053
- if (receiverIr?.kind === "dyn" || receiverIr === null) {
5500
+ if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
5054
5501
  const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
5055
- if (obj !== null && obj.type.kind === "dyn") {
5502
+ if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
5056
5503
  const loc = locOf(expr);
5057
5504
  const litKey = recordKeyLiteralText(target.argumentExpression);
5058
5505
  let key = litKey !== null
@@ -5066,7 +5513,7 @@ export function lowerElementWrite(lowerer, expr) {
5066
5513
  if (value.type.kind !== "dyn") {
5067
5514
  lowerer.unsupported("SC1101", expr.right, `storing '${lowerer.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
5068
5515
  }
5069
- return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5516
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [lowerer.coerceToExpected(obj, DYN), key, value], type: VOID, loc }, loc };
5070
5517
  }
5071
5518
  }
5072
5519
  if (receiverIr?.kind !== "array") {
@@ -5096,19 +5543,42 @@ export function lowerElementWrite(lowerer, expr) {
5096
5543
  if (arr.type.kind !== "array") {
5097
5544
  lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
5098
5545
  }
5099
- const index = lowerer.lowerExpr(target.argumentExpression);
5546
+ const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
5100
5547
  if (index.type.kind !== "f64") {
5101
5548
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
5102
5549
  }
5103
5550
  // Optional array reads carry the source array's payload type plus an
5104
5551
  // undefined arm. arrayValueStore turns that arm into the runtime's
5105
5552
  // UNDEFINED state while keeping number[]/string[] payload storage scalar.
5106
- const value = lowerer.lowerExpr(expr.right);
5107
- return arrayValueStore(lowerer, arr, index, value, receiverIr.elem, locOf(expr));
5553
+ let literal = expr.right;
5554
+ while (ts.isParenthesizedExpression(literal) || ts.isSatisfiesExpression(literal))
5555
+ literal = literal.expression;
5556
+ const value = ts.isObjectLiteralExpression(literal) || ts.isArrayLiteralExpression(literal)
5557
+ ? lowerer.lowerExprExpecting(literal, arr.type.elem) : lowerer.lowerExpr(expr.right);
5558
+ // Validate the payload before emitting a native store. A dormant JS
5559
+ // method can contradict its JSDoc element type; retain its call-time
5560
+ // refusal instead of leaving invalid IR in a newly reachable method.
5561
+ const expected = value.type.kind === "undefinedT" ||
5562
+ value.type.kind === "union" && lowerer.armTag(value.type.unionId, UNDEFINED_T) >= 0
5563
+ ? arrayValueType(lowerer, arr.type.elem) : arr.type.elem;
5564
+ return arrayValueStore(lowerer, arr, index, lowerer.coerceInto(expr.right, value, expected), arr.type.elem, locOf(expr));
5565
+ }
5566
+ /** Environment property names use ToPrimitive with the string hint, so
5567
+ * checked objects must execute their own conversion hooks. */
5568
+ export function lowerEnvironmentKey(lowerer, node) {
5569
+ const key = lowerer.lowerExpr(node);
5570
+ if (key.type.kind === "dyn")
5571
+ return { kind: "libCall", fn: "dyn.toStringCoerce", args: [key], type: STRING, loc: key.loc };
5572
+ return lowerRecordPropertyKey(lowerer, key, node);
5108
5573
  }
5109
5574
  export function ensureString(lowerer, e, node) {
5110
5575
  if (e.type.kind === "string")
5111
5576
  return e;
5577
+ if (e.type.kind === "url")
5578
+ return { kind: "libCall", fn: "url.href", args: [e], type: STRING, loc: e.loc };
5579
+ if (e.type.kind === "regex") {
5580
+ return { kind: "regexIntrinsic", method: "toString", receiver: e, args: [], type: STRING, loc: e.loc };
5581
+ }
5112
5582
  if (e.type.kind === "bigint") {
5113
5583
  return {
5114
5584
  kind: "libCall",
@@ -5124,18 +5594,21 @@ export function ensureString(lowerer, e, node) {
5124
5594
  // strings verbatim, arrays via join, objects as "[object Object]").
5125
5595
  return { kind: "toString", operand: e, type: STRING, loc: e.loc };
5126
5596
  }
5597
+ if (e.type.kind === "bytes") {
5598
+ // Preserve the runtime brand: typed arrays join numeric elements,
5599
+ // while a Buffer stored in a Uint8Array slot decodes its bytes.
5600
+ const boxed = { kind: "dynFrom", value: e, type: DYN, loc: e.loc };
5601
+ return { kind: "toString", operand: boxed, type: STRING, loc: e.loc };
5602
+ }
5127
5603
  if (e.type.kind === "jsval") {
5128
5604
  // String(v) in the engine — JS-exact (and Node-exact in templates).
5129
5605
  return { kind: "jsOp", op: "toStr", args: [e], type: STRING, loc: e.loc };
5130
5606
  }
5131
5607
  if (e.type.kind === "object") {
5132
- // String(err) / `${err}` over the Error hierarchy: Error.prototype
5133
- // .toString — the ONE runtime implementation (overriding it is
5134
- // fenced), "name: message" with the empty-side elisions, exactly
5135
- // Node's String(err), which carries no stack either.
5608
+ // String(err) / `${err}` uses the same dispatch as err.toString().
5136
5609
  for (let c = lowerer.classes.get(e.type.className) ?? null; c; c = c.base) {
5137
5610
  if (c.builtinError) {
5138
- return { kind: "libCall", fn: "error.toString", args: [lowerer.upcastTo(e, "%Error")], type: STRING, loc: e.loc };
5611
+ return errorToStringCall(lowerer, e);
5139
5612
  }
5140
5613
  }
5141
5614
  }
@@ -5326,6 +5799,38 @@ export function lowerAsExpression(lowerer, expr) {
5326
5799
  return lowerer.jsvalIn(inner, expr.expression);
5327
5800
  }
5328
5801
  const target = lowerer.mapTypeOf(targetTs0);
5802
+ // Unknown-only record views use the checked-dynamic representation.
5803
+ // A native record asserted to such a view still denotes the original
5804
+ // object. Box a live reference instead of erasing the assertion and
5805
+ // handing native storage to a checked-dynamic property operation.
5806
+ if (target?.kind === "dyn" && (targetTs0.flags & ts.TypeFlags.Object) !== 0) {
5807
+ const sourceShape = inner.type.kind === "record" ? lowerer.shapes.get(inner.type.shapeId) : undefined;
5808
+ if (hasClassPayload(lowerer, inner.type) || sourceShape && shapeHasAccessorSlots(sourceShape)) {
5809
+ lowerer.unsupported("SC1090", expr, "opaque structural views of classes or accessor records (name the property's concrete type to retain native accessor dispatch)");
5810
+ }
5811
+ const boxed = lowerer.coerceInto(expr, inner, DYN);
5812
+ return boxed.kind === "dynFrom" ? { ...boxed, liveRef: true } : boxed;
5813
+ }
5814
+ // Collection assertions are interface views only: their backing
5815
+ // value must already have the same native key/element/value layout.
5816
+ // Erasing a mock or a different instantiation here would let member
5817
+ // lowering interpret a record (or another ABI) as a native handle.
5818
+ if (target?.kind === "map" || target?.kind === "set") {
5819
+ return lowerer.coerceInto(expr, inner, target);
5820
+ }
5821
+ // A class-derived interface still names the class's native layout.
5822
+ // An assertion must not let a structural mock acquire that layout;
5823
+ // unions retain their checked extraction below, and real instances
5824
+ // keep the ordinary class narrowing path.
5825
+ if (target?.kind === "object" && inner.type.kind !== "object" && inner.type.kind !== "union") {
5826
+ return lowerer.coerceInto(expr, inner, target);
5827
+ }
5828
+ if (target?.kind === "object" && inner.type.kind === "object" &&
5829
+ !typeEquals(target, inner.type) &&
5830
+ !lowerer.isSubclassOf(inner.type.className, target.className) &&
5831
+ !lowerer.isSubclassOf(target.className, inner.type.className)) {
5832
+ return lowerer.coerceInto(expr, inner, target);
5833
+ }
5329
5834
  // Static assertions normally erase, but record layouts are
5330
5835
  // monomorphic: a consumer selected from the asserted shape must see
5331
5836
  // that shape physically. Reuse the ordinary slot coercion so plain
@@ -5378,7 +5883,21 @@ export function lowerAsExpression(lowerer, expr) {
5378
5883
  lowerer.badType(expr.type, targetTs);
5379
5884
  if (target.kind === "dyn")
5380
5885
  return inner; // `as unknown`: erasure
5381
- if (target.kind === "void" || !lowerer.jsonSafe(target)) {
5886
+ // An all-`unknown`-fields record target (`err as { code?: unknown }`
5887
+ // — the errno-probing idiom): there is nothing to validate (every
5888
+ // field is unknown, exactly what the dyn value already answers) and
5889
+ // nothing to build — the cast is pure typing, so it ERASES and the
5890
+ // reads ride the dyn keyed read.
5891
+ if (target.kind === "record") {
5892
+ const shape = lowerer.shapes.get(target.shapeId);
5893
+ if (shape &&
5894
+ !shape.tuple &&
5895
+ shape.fields.every((f) => f.type.kind === "dyn") &&
5896
+ (!shape.indexValue || shape.indexValue.kind === "dyn")) {
5897
+ return inner;
5898
+ }
5899
+ }
5900
+ if (target.kind === "void" || !canDynCheckTo(target, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
5382
5901
  // Bare undefined-armed targets pass when every OTHER arm is
5383
5902
  // JSON-safe: the checked-dynamic tree holds a first-class undefined value now
5384
5903
  // (index-signature overflow reads produce it for missing keys), and
@@ -5414,20 +5933,6 @@ export function lowerAsExpression(lowerer, expr) {
5414
5933
  if (DYN_HANDLE_KINDS.has(target.kind)) {
5415
5934
  return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
5416
5935
  }
5417
- // An all-`unknown`-fields record target (`err as { code?: unknown }`
5418
- // — the errno-probing idiom): there is nothing to validate (every
5419
- // field is unknown, exactly what the dyn value already answers) and
5420
- // nothing to build — the cast is pure typing, so it ERASES and the
5421
- // reads ride the dyn keyed read.
5422
- if (target.kind === "record") {
5423
- const shape = lowerer.shapes.get(target.shapeId);
5424
- if (shape &&
5425
- !shape.tuple &&
5426
- shape.fields.every((f) => f.type.kind === "dyn") &&
5427
- (!shape.indexValue || shape.indexValue.kind === "dyn")) {
5428
- return inner;
5429
- }
5430
- }
5431
5936
  lowerer.unsupported("SC1090", expr, `a checked cast of 'unknown' to '${target.kind === "void" ? "void" : lowerer.fmt(target)}' ` +
5432
5937
  `(a dynamic value can only be validated against JSON-representable types: ` +
5433
5938
  `number, string, boolean, records, arrays, and unions of those)`);
@@ -5445,7 +5950,10 @@ export function lowerPrefixUnary(lowerer, expr) {
5445
5950
  if (raw.type.kind === "bigint") {
5446
5951
  return { kind: "libCall", fn: "bigint.neg", args: [raw], type: BIGINT_T, loc };
5447
5952
  }
5448
- const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
5953
+ // Match the checked numeric boundary used by JS binary arithmetic.
5954
+ const operand = raw.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())
5955
+ ? { kind: "dynCheck", value: raw, type: F64, loc }
5956
+ : lowerOptionalNumber(lowerer, raw, loc, expr.operand);
5449
5957
  if (operand.type.kind !== "f64")
5450
5958
  lowerer.unsupported("SC1043", expr);
5451
5959
  if (operand.kind === "numLit")
@@ -5454,6 +5962,9 @@ export function lowerPrefixUnary(lowerer, expr) {
5454
5962
  }
5455
5963
  case ts.SyntaxKind.PlusToken: {
5456
5964
  const raw = lowerer.lowerExpr(expr.operand);
5965
+ if (raw.type.kind === "dyn") {
5966
+ return { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc };
5967
+ }
5457
5968
  // Unary + is ToNumber; on an already-number operand it's identity,
5458
5969
  // and a STRING operand runs the runtime's ECMA-exact StringToNumber
5459
5970
  // (num.fromString — Number(aString)'s lowering, scr_string.c).
@@ -5479,14 +5990,16 @@ export function lowerPrefixUnary(lowerer, expr) {
5479
5990
  if (raw.type.kind === "bigint") {
5480
5991
  return { kind: "libCall", fn: "bigint.not", args: [raw], type: BIGINT_T, loc };
5481
5992
  }
5993
+ if (raw.type.kind === "dyn")
5994
+ return {
5995
+ kind: "libCall", fn: "dyn.bitwise", args: [raw, dynUndefinedExpr(loc), { kind: "strLit", value: "~", type: STRING, loc }], type: DYN, loc,
5996
+ };
5482
5997
  // A checked-dynamic value retains enough JavaScript structure to
5483
5998
  // run exact ToNumber, including the object valueOf/toString
5484
5999
  // protocol. This is the static `any`/JS-lane answer for idioms
5485
6000
  // such as `while (~index)`, where inference may have widened an
5486
6001
  // otherwise numeric slot to the checked-dynamic tree.
5487
- const operand = raw.type.kind === "dyn"
5488
- ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc }
5489
- : lowerOptionalNumber(lowerer, raw, loc, expr.operand);
6002
+ const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
5490
6003
  if (operand.type.kind !== "f64")
5491
6004
  lowerer.unsupported("SC1043", expr);
5492
6005
  return { kind: "unary", op: "~", operand, type: F64, loc };
@@ -5496,7 +6009,7 @@ export function lowerPrefixUnary(lowerer, expr) {
5496
6009
  // `++x` / `--x` in expression position: yields the NEW value.
5497
6010
  return lowerIncDec(lowerer, expr, true);
5498
6011
  }
5499
- lowerer.unsupported("SC1090", expr, `syntax '${ts.SyntaxKind[expr.kind]}'`);
6012
+ lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
5500
6013
  }
5501
6014
  /** `x++`/`x--`/`++x`/`--x` in EXPRESSION position — the incDec node:
5502
6015
  * read, write ±1, yield old (postfix) or new (prefix) value. Receivers
@@ -5508,6 +6021,14 @@ function lowerIncDec(lowerer, expr, prefix) {
5508
6021
  const loc = locOf(expr);
5509
6022
  const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
5510
6023
  if (!ts.isIdentifier(expr.operand)) {
6024
+ if (ts.isPropertyAccessExpression(expr.operand)) {
6025
+ const target = staticFieldWriteTarget(lowerer, expr.operand);
6026
+ if (target) {
6027
+ if (target.type.kind !== "f64")
6028
+ lowerer.unsupported("SC1043", expr);
6029
+ return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
6030
+ }
6031
+ }
5511
6032
  // CLASS-FIELD receivers (`if (--this.limit === 0)` — countdown.js's
5512
6033
  // dec(); `--this[kLimit]` — its symbol-keyed spelling): a single-
5513
6034
  // evaluation read-modify-write over the instance, yielding old/new
@@ -5548,6 +6069,16 @@ function lowerIncDec(lowerer, expr, prefix) {
5548
6069
  if (!target) {
5549
6070
  lowerer.rejectUnresolved(expr.operand, `increment/decrement of '${expr.operand.text}' (not a writable local or module global)`);
5550
6071
  }
6072
+ if (target.type.kind === "dyn") {
6073
+ const old = lowerer.declareHiddenLocal("%numericOld", DYN);
6074
+ const next = lowerer.declareHiddenLocal("%numericNext", DYN);
6075
+ const read = lowerer.lowerExpr(expr.operand);
6076
+ return { kind: "seqExpr", stmts: [
6077
+ { kind: "varDecl", localId: old.id, init: { kind: "libCall", fn: "dyn.toNumeric", args: [read], type: DYN, loc }, loc },
6078
+ { kind: "varDecl", localId: next.id, init: { kind: "libCall", fn: "dyn.increment", args: [varRef(old.id, DYN, loc), { kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc }, loc },
6079
+ { kind: "assign", localId: target.id, value: varRef(next.id, DYN, loc), loc },
6080
+ ], result: varRef(prefix ? next.id : old.id, DYN, loc), type: DYN, loc };
6081
+ }
5551
6082
  if (target.type.kind !== "f64")
5552
6083
  lowerer.unsupported("SC1043", expr);
5553
6084
  return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
@@ -5599,7 +6130,7 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
5599
6130
  };
5600
6131
  const jop = JS_BIN[op];
5601
6132
  if (jop === undefined) {
5602
- lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.SyntaxKind[op]}' on 'any' values`);
6133
+ lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' on 'any' values`);
5603
6134
  }
5604
6135
  const type = jsOpResultKind(jop) === "bool" ? BOOL : JSVAL;
5605
6136
  return {
@@ -5608,6 +6139,39 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
5608
6139
  type, loc,
5609
6140
  };
5610
6141
  }
6142
+ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
6143
+ const loc = locOf(expr);
6144
+ const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
6145
+ const rhs = lowerer.lowerExpr(expr.right);
6146
+ const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
6147
+ if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
6148
+ const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
6149
+ const jop = JS_COMPOUND[compound];
6150
+ if (jop === undefined)
6151
+ lowerer.unsupported("SC1043", expr);
6152
+ const wrapped = {
6153
+ kind: "jsOp", op: jop,
6154
+ args: [lowerer.jsvalIn(read, expr.left), lowerer.jsvalIn(rhs, expr.right)],
6155
+ type: JSVAL, loc,
6156
+ };
6157
+ return lowerer.coerceInto(expr, wrapped, target.type);
6158
+ }
6159
+ if (compound === "+" && target.type.kind === "string") {
6160
+ return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
6161
+ }
6162
+ if (target.type.kind === "f64" && numericRhs.type.kind === "f64") {
6163
+ return { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
6164
+ }
6165
+ if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
6166
+ (target.type.kind === "dyn" || target.type.kind === "f64") &&
6167
+ (rhs.type.kind === "dyn" || rhs.type.kind === "f64") &&
6168
+ isJsSourceFile(expr.getSourceFile())) {
6169
+ const checkNum = (value) => value.type.kind === "dyn" ? { kind: "dynCheck", value, type: F64, loc: value.loc } : value;
6170
+ const computed = { kind: "bin", op: compound, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
6171
+ return target.type.kind === "dyn" ? { kind: "dynFrom", value: computed, type: DYN, loc } : computed;
6172
+ }
6173
+ lowerer.unsupported("SC1043", expr);
6174
+ }
5611
6175
  export function lowerBinary(lowerer, expr) {
5612
6176
  const loc = locOf(expr);
5613
6177
  const op = expr.operatorToken.kind;
@@ -5615,6 +6179,15 @@ export function lowerBinary(lowerer, expr) {
5615
6179
  if (cacheHas)
5616
6180
  return cacheHas;
5617
6181
  if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
6182
+ if (op === ts.SyntaxKind.EqualsToken) {
6183
+ const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
6184
+ if (callableWrite)
6185
+ return callableWrite;
6186
+ }
6187
+ if (op === ts.SyntaxKind.QuestionQuestionEqualsToken ||
6188
+ op === ts.SyntaxKind.AmpersandAmpersandEqualsToken || op === ts.SyntaxKind.BarBarEqualsToken) {
6189
+ return lowerShortCircuitAssignment(lowerer, expr);
6190
+ }
5618
6191
  if (ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) {
5619
6192
  fenceNodeModuleMutation(lowerer, expr.left, "assignment");
5620
6193
  }
@@ -5640,8 +6213,29 @@ export function lowerBinary(lowerer, expr) {
5640
6213
  }
5641
6214
  const indexedCompound = COMPOUND_ASSIGN_OPS[op];
5642
6215
  if (indexedCompound !== undefined && ts.isElementAccessExpression(expr.left)) {
6216
+ if (symbolFieldInfo(lowerer, expr.left))
6217
+ return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
5643
6218
  return lowerElementCompound(lowerer, expr, indexedCompound);
5644
6219
  }
6220
+ if (indexedCompound !== undefined && ts.isIdentifier(expr.left)) {
6221
+ const target = lowerer.resolveWritable(expr.left);
6222
+ if (!target) {
6223
+ lowerer.rejectUnresolved(expr.left, "assignment to an unresolved variable");
6224
+ }
6225
+ const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
6226
+ return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
6227
+ }
6228
+ if (indexedCompound !== undefined &&
6229
+ (ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) &&
6230
+ !(ts.isPropertyAccessExpression(expr.left) && expr.left.questionDotToken)) {
6231
+ const target = expandoWritableTarget(lowerer, expr.left) ??
6232
+ (ts.isPropertyAccessExpression(expr.left) ? nsWritableTarget(lowerer, expr.left) : null);
6233
+ if (target) {
6234
+ const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
6235
+ return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
6236
+ }
6237
+ return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
6238
+ }
5645
6239
  // `events.defaultMaxListeners = v` — the module-property write
5646
6240
  // Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
5647
6241
  // the value crosses into the checked-dynamic tree and the runtime ladder throws
@@ -5694,6 +6288,15 @@ export function lowerBinary(lowerer, expr) {
5694
6288
  const value = lowerer.lowerExprExpecting(expr.right, target.type);
5695
6289
  return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
5696
6290
  }
6291
+ // Bundled numeric enums use e[e["name"] = value] = "name".
6292
+ // Capture the receiver and key before the RHS. PutValue coerces the
6293
+ // key afterward, then yields the RHS without rereading the property.
6294
+ if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
6295
+ const recv = tryLowerExpression(lowerer, expr.left.expression);
6296
+ if (recv?.type.kind === "dyn") {
6297
+ return lowerDynMemberAssignment(lowerer, expr, recv);
6298
+ }
6299
+ }
5697
6300
  // `h.k = v` on an ISLAND receiver in VALUE position: the engine
5698
6301
  // property write (setProp throws the engine's TypeErrors on
5699
6302
  // nullish receivers, bridged catchably), the RHS value threaded
@@ -5760,6 +6363,22 @@ export function lowerBinary(lowerer, expr) {
5760
6363
  loc,
5761
6364
  };
5762
6365
  }
6366
+ const field = lowerer.fieldTarget(expr.left);
6367
+ if (field) {
6368
+ const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
6369
+ const receiver = field.obj;
6370
+ field.obj = varRef(recvTmp.id, receiver.type, loc);
6371
+ const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
6372
+ const valTmp = lowerer.declareHiddenLocal("%setValue", value.type);
6373
+ const result = varRef(valTmp.id, value.type, loc);
6374
+ return {
6375
+ kind: "seqExpr", stmts: [
6376
+ { kind: "varDecl", localId: recvTmp.id, init: receiver, loc },
6377
+ { kind: "varDecl", localId: valTmp.id, init: value, loc },
6378
+ lowerer.fieldSetStmt(field, result, loc, expr.left),
6379
+ ], result, type: result.type, loc,
6380
+ };
6381
+ }
5763
6382
  }
5764
6383
  }
5765
6384
  // Destructuring assignment in VALUE position (`(() => [i] = [i+1])()`,
@@ -5786,221 +6405,7 @@ export function lowerBinary(lowerer, expr) {
5786
6405
  lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
5787
6406
  }
5788
6407
  if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
5789
- if (op === ts.SyntaxKind.BarBarToken) {
5790
- const fallback = lowerAbsenceAwareOrChain(lowerer, expr);
5791
- if (fallback)
5792
- return fallback;
5793
- }
5794
- // JS value semantics: `a && b` is `toBool(a) ? b : a` — the result is
5795
- // an operand value, so both operands must share one IR kind (which is
5796
- // also what tsc's type says, modulo literal-union collapsing that
5797
- // mapType already handles). Mixed kinds (`n && s`) stay rejected.
5798
- const left = lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left);
5799
- const right = lowerer.lowerExpr(expr.right);
5800
- // A LITERAL-unit left operand (a compile-time undefined/null — the
5801
- // capability-probe members: `process.features.inspector ||
5802
- // !flag.startsWith('--inspect')` reads undefined in a compiled
5803
- // binary): statically falsy, so the operator folds JS-exactly —
5804
- // `unit || X` IS X, `unit && X` IS the unit (X's side effects never
5805
- // run in JS either; the lowered right is simply dropped). Literal
5806
- // units only — computed unit-typed values keep the fences below.
5807
- if (left.kind === "unitLit") {
5808
- return op === ts.SyntaxKind.BarBarToken ? right : left;
5809
- }
5810
- // JS objects and symbols are always truthy. In `ref || fallback`
5811
- // the fallback is therefore unreachable and the result is the
5812
- // already-evaluated left value — the browser-fallback idiom used by
5813
- // packages for `process.argv || []` and `process.env || {}`. The
5814
- // operand itself remains in the IR, preserving reads/calls that
5815
- // produce the reference; only the unreachable right lowering drops.
5816
- if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
5817
- return left;
5818
- }
5819
- if (left.type.kind === "dyn" || right.type.kind === "dyn") {
5820
- // A checked-dynamic operand (`fn.name || '<anonymous>'` —
5821
- // test/common's _mustCallInner): both sides live in the checked-dynamic tree and
5822
- // the deciding test is ToBoolean over the dyn kind
5823
- // (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
5824
- // non-dyn side converts through the usual boundary; a value with
5825
- // no dyn representation keeps the fence.
5826
- const l = lowerer.coerceToExpected(left, DYN);
5827
- const r = lowerer.coerceToExpected(right, DYN);
5828
- if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
5829
- lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
5830
- }
5831
- return {
5832
- kind: "logical",
5833
- op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
5834
- left: l,
5835
- right: r,
5836
- type: DYN, loc,
5837
- };
5838
- }
5839
- if (left.type.kind === "jsval" || right.type.kind === "jsval") {
5840
- return {
5841
- kind: "logical",
5842
- op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
5843
- left: lowerer.jsvalIn(left, expr.left),
5844
- right: lowerer.jsvalIn(right, expr.right),
5845
- type: JSVAL, loc,
5846
- };
5847
- }
5848
- if (left.type.kind === "union" || right.type.kind === "union") {
5849
- // JS value semantics over a union: the deciding test is the ARM
5850
- // value's ToBoolean (a per-union helper), and the result is the
5851
- // deciding operand. Supported when the checker's type of the whole
5852
- // expression maps to ONE union both operands coerce into (same
5853
- // union passes through; a plain arm wraps — `u || undefined`).
5854
- // Everything else (`u && flag`, whose value would need a wider
5855
- // re-tagged union) stays fenced — in CONDITION position those
5856
- // shapes lower through lowerCondition's bool descent instead.
5857
- let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
5858
- // A broad-JSDoc npm-static body can leave the CHECKER result `any`
5859
- // even after declaration-backed specialization has recovered both
5860
- // operands (`helpOption && args.find(...)` in commander). When the
5861
- // left consists only of always-truthy references and falsy units,
5862
- // the static result is exactly the right operand plus those units.
5863
- if (target === null &&
5864
- lowerer.implicitParamTypes !== null &&
5865
- npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
5866
- (lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
5867
- left.type.kind === "union") {
5868
- const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
5869
- const rightArms = right.type.kind === "union"
5870
- ? lowerer.unions.get(right.type.unionId)?.arms
5871
- : [right.type];
5872
- const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
5873
- if (leftArms && rightArms && rightIsStatic &&
5874
- leftArms.some(isUnitType) &&
5875
- leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
5876
- const byKey = new Map();
5877
- for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
5878
- byKey.set(typeKey(arm), arm);
5879
- }
5880
- const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
5881
- if (arms.length > 1) {
5882
- target = { kind: "union", unionId: lowerer.unions.intern(arms) };
5883
- }
5884
- }
5885
- }
5886
- if (target?.kind === "union") {
5887
- // `&&` whose left does NOT fit the result union: the checker
5888
- // built that result by DROPPING left arms which are always
5889
- // truthy (`Option | undefined && string | undefined` answers
5890
- // `string | undefined`, not `Option | string | undefined`).
5891
- // Evaluate the left once, test it in its own union, and retag it
5892
- // only on the falsy path. Any stranded reference arms are
5893
- // unreachable there because every JS object is truthy.
5894
- if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
5895
- const def = lowerer.unions.get(left.type.unionId);
5896
- const trappable = new Set();
5897
- def?.arms.forEach((arm, tag) => {
5898
- if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
5899
- trappable.add(tag);
5900
- }
5901
- });
5902
- const retag = trappable.size === 0
5903
- ? null
5904
- : lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
5905
- if (retag) {
5906
- lowerer.requireTruthyUnion(left.type.unionId, expr);
5907
- const stmts = [];
5908
- let stable = left;
5909
- if (!isSafeToRepeat(left)) {
5910
- const local = lowerer.declareHiddenLocal("%and", left.type);
5911
- stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
5912
- stable = varRef(local.id, left.type, loc);
5913
- }
5914
- const result = {
5915
- kind: "ternary",
5916
- cond: lowerer.ensureBool(stable, expr.left),
5917
- then: lowerer.coerceInto(expr.right, right, target),
5918
- else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
5919
- type: target,
5920
- loc,
5921
- };
5922
- return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
5923
- }
5924
- }
5925
- // `||` whose left does NOT fit the result union: the checker built
5926
- // that result by DROPPING the left's falsy arms (`process.env.X ||
5927
- // null` is `string | null`, `|| 3000` is `string | number` — the
5928
- // `undefined` is gone from both), so coercing the left eagerly, as
5929
- // the shared shape below does, retags an arm the test is about to
5930
- // rule out and throws where Node yields the default. Single-eval
5931
- // instead: test the left in its OWN union and retag only on the
5932
- // truthy side, where the dropped arms are unreachable.
5933
- if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
5934
- const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
5935
- if (retag) {
5936
- lowerer.requireTruthyUnion(left.type.unionId, expr);
5937
- return {
5938
- kind: "orDefault",
5939
- left,
5940
- right: lowerer.coerceInto(expr.right, right, target),
5941
- retag,
5942
- type: target,
5943
- loc,
5944
- };
5945
- }
5946
- }
5947
- lowerer.requireTruthyUnion(target.unionId, expr);
5948
- return {
5949
- kind: "logical",
5950
- op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
5951
- left: lowerer.coerceInto(expr.left, left, target),
5952
- right: lowerer.coerceInto(expr.right, right, target),
5953
- type: target, loc,
5954
- };
5955
- }
5956
- // `u || d` NARROWED by the default: the checker types the result
5957
- // as u's single non-unit arm (`marker() || "default"`) — evaluate
5958
- // u once, truthy extracts the arm, falsy takes d lazily (the
5959
- // orDefault node, nullish's truthiness sibling). An UNMAPPABLE
5960
- // checker result takes the same rule (`options.runner ||
5961
- // defaultRunner` — tsc's union of two structurally-compatible
5962
- // function types has no representation, while the left's one
5963
- // non-unit arm is the only representable answer): the default
5964
- // must coerce into the arm or fence on its own.
5965
- if (op === ts.SyntaxKind.BarBarToken &&
5966
- left.type.kind === "union") {
5967
- const def = lowerer.unions.get(left.type.unionId);
5968
- const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
5969
- const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
5970
- if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
5971
- lowerer.requireTruthyUnion(left.type.unionId, expr);
5972
- const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
5973
- return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
5974
- }
5975
- }
5976
- lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
5977
- }
5978
- const kind = left.type.kind;
5979
- if (kind !== right.type.kind ||
5980
- (kind !== "f64" && kind !== "string" && kind !== "bool")) {
5981
- // Mixed PLAIN operands (`value || null`, `flag || undefined`,
5982
- // `s || 0`): JS value semantics still compile when the checker
5983
- // types the RESULT as one union both operands coerce into — the
5984
- // same lift the union-operand path above takes, arriving here
5985
- // with two plain arm values instead.
5986
- const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
5987
- if (target?.kind === "union") {
5988
- lowerer.requireTruthyUnion(target.unionId, expr);
5989
- return {
5990
- kind: "logical",
5991
- op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
5992
- left: lowerer.coerceInto(expr.left, left, target),
5993
- right: lowerer.coerceInto(expr.right, right, target),
5994
- type: target, loc,
5995
- };
5996
- }
5997
- lowerer.unsupported("SC1042", expr);
5998
- }
5999
- return {
6000
- kind: "logical",
6001
- op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
6002
- left, right, type: left.type, loc,
6003
- };
6408
+ return lowerLogicalChain(lowerer, expr);
6004
6409
  }
6005
6410
  if (op === ts.SyntaxKind.QuestionQuestionToken) {
6006
6411
  return lowerer.lowerNullishCoalesce(expr, loc);
@@ -6011,7 +6416,7 @@ export function lowerBinary(lowerer, expr) {
6011
6416
  // runs for EFFECT exactly as its statement lowering (JS discards its
6012
6417
  // value), the right operand is the expression's value — a seqExpr.
6013
6418
  // The validator restricts seqExpr statements to straight-line writes
6014
- // (the C emission point is mid-expression), so a left operand whose
6419
+ // (the lowering point is mid-expression), so a left operand whose
6015
6420
  // statement lowering needs real control flow keeps a pointed fence
6016
6421
  // instead of tripping the validator downstream.
6017
6422
  const effect = lowerer.lowerExprStatement(expr.left);
@@ -6052,7 +6457,7 @@ export function lowerBinary(lowerer, expr) {
6052
6457
  op === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
6053
6458
  const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6054
6459
  const unit = left.kind === "unitLit" ? left : right.kind === "unitLit" ? right : null;
6055
- const other = unit === left ? right : left;
6460
+ const other = left.kind === "unitLit" ? right : left;
6056
6461
  if (unit && other.type.kind === "dyn") {
6057
6462
  return {
6058
6463
  kind: "dynTest",
@@ -6069,12 +6474,30 @@ export function lowerBinary(lowerer, expr) {
6069
6474
  // answers false). dyn vs dyn keeps the fence below.
6070
6475
  const dynSide = left.type.kind === "dyn" ? left : right;
6071
6476
  const scalarSide = dynSide === left ? right : left;
6477
+ const reference = scalarSide.type;
6478
+ if ((isDynTypedRefType(reference) || reference.kind === "record" || reference.kind === "array" ||
6479
+ reference.kind === "bytes" || reference.kind === "func" || reference.kind === "regex" || reference.kind === "url" ||
6480
+ reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
6481
+ lowerer.dynConvertible(reference)) {
6482
+ const boxed = {
6483
+ kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
6484
+ ...(reference.kind === "record" || reference.kind === "array" || reference.kind === "bytes" ? { liveRef: true } : {}),
6485
+ };
6486
+ return {
6487
+ kind: "dynScalarEq",
6488
+ left: scalarSide === left ? boxed : left,
6489
+ right: scalarSide === right ? boxed : right,
6490
+ ...(negated ? { negated: true } : {}),
6491
+ type: BOOL,
6492
+ loc,
6493
+ };
6494
+ }
6072
6495
  if (dynSide.type.kind === "dyn" &&
6073
6496
  (scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
6074
6497
  // dyn vs dyn (`context.actual !== context.exact` —
6075
6498
  // test/common's exit accounting): the runtime's whole-dyn
6076
6499
  // strict equality — scalars by value, units by kind,
6077
- // reference kinds by node identity (scr_dyn_strict_eq).
6500
+ // reference kinds by identity (scr_dyn_strict_eq).
6078
6501
  scalarSide.type.kind === "dyn")) {
6079
6502
  return {
6080
6503
  kind: "dynScalarEq",
@@ -6086,15 +6509,22 @@ export function lowerBinary(lowerer, expr) {
6086
6509
  };
6087
6510
  }
6088
6511
  }
6089
- // JS `any`-origin operands (the checked-dynamic declaration story):
6090
- // arithmetic and ordering CHECK the dyn side to the static side's
6091
- // scalar kind (dynCheck — a catchable TypeError on mismatch) and
6092
- // compute natively. Node would ToNumber-coerce instead — the honest
6093
- // divergence is loud (a throw), never a silent wrong answer
6094
- // (SEMANTICS.md). tsc rejects these forms on real `unknown`, so only
6095
- // any-typed JS reaches this; when BOTH sides are dyn a number
6096
- // context is the only honest guess for arithmetic — both check.
6097
- if (isJsSourceFile(expr.getSourceFile())) {
6512
+ const bitwise = {
6513
+ [ts.SyntaxKind.AmpersandToken]: "&", [ts.SyntaxKind.BarToken]: "|", [ts.SyntaxKind.CaretToken]: "^",
6514
+ [ts.SyntaxKind.LessThanLessThanToken]: "<<", [ts.SyntaxKind.GreaterThanGreaterThanToken]: ">>",
6515
+ [ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: ">>>",
6516
+ };
6517
+ if (bitwise[op]) {
6518
+ const l = lowerer.coerceToExpected(left, DYN);
6519
+ const r = lowerer.coerceToExpected(right, DYN);
6520
+ if (l.type.kind === "dyn" && r.type.kind === "dyn") {
6521
+ const value = { kind: "libCall", fn: "dyn.bitwise", args: [l, r, { kind: "strLit", value: bitwise[op], type: STRING, loc }], type: DYN, loc };
6522
+ return left.type.kind === "f64" || right.type.kind === "f64" ? { kind: "dynCheck", value, type: F64, loc } : value;
6523
+ }
6524
+ }
6525
+ // Numeric operators dispatch on ToNumeric's actual Number/BigInt
6526
+ // result, including values flowing through native callable properties.
6527
+ if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken) {
6098
6528
  const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
6099
6529
  const other = left.type.kind === "dyn" ? right : left;
6100
6530
  const NUM_BIN = {
@@ -6112,26 +6542,29 @@ export function lowerBinary(lowerer, expr) {
6112
6542
  };
6113
6543
  const arith = NUM_BIN[op];
6114
6544
  const cmp = NUM_CMP[op];
6115
- if ((arith || cmp) && (other.type.kind === "f64" || other.type.kind === "dyn")) {
6116
- const l = checkNum(left);
6117
- const r = checkNum(right);
6118
- if (arith)
6119
- return { kind: "bin", op: arith, left: l, right: r, type: F64, loc };
6120
- return { kind: "bin", op: cmp, left: l, right: r, type: BOOL, loc };
6121
- }
6122
- // `+`: number when the OTHER side is a number, string concat when
6123
- // it is a string — the two static homes; dyn+dyn stays a number.
6124
- if (op === ts.SyntaxKind.PlusToken) {
6125
- if (other.type.kind === "f64" || other.type.kind === "dyn") {
6126
- return { kind: "bin", op: "+", left: checkNum(left), right: checkNum(right), type: F64, loc };
6545
+ if (arith) {
6546
+ const l = lowerer.coerceToExpected(left, DYN);
6547
+ const r = lowerer.coerceToExpected(right, DYN);
6548
+ if (l.type.kind === "dyn" && r.type.kind === "dyn") {
6549
+ const value = { kind: "libCall", fn: "dyn.arithmetic",
6550
+ args: [l, r, { kind: "strLit", value: arith, type: STRING, loc }], type: DYN, loc };
6551
+ const numeric = left.type.kind === "bigint" || right.type.kind === "bigint" ? BIGINT_T
6552
+ : left.type.kind === "f64" || right.type.kind === "f64" ? F64 : null;
6553
+ return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
6127
6554
  }
6128
- if (other.type.kind === "string") {
6129
- // String-context `+`: JS's answer is String(unknown) — the
6130
- // JS-exact dyn walker (numbers format, arrays join, objects
6131
- // print [object Object], handles the same) — never a checked
6132
- // cast: `'status ' + res.statusCode` concatenates like Node.
6133
- const strOf = (e) => e.type.kind === "dyn" ? { kind: "toString", operand: e, type: STRING, loc: e.loc } : e;
6134
- return { kind: "strConcat", left: strOf(left), right: strOf(right), type: STRING, loc };
6555
+ }
6556
+ if (cmp && (other.type.kind === "f64" || other.type.kind === "dyn")) {
6557
+ return { kind: "bin", op: cmp, left: checkNum(left), right: checkNum(right), type: BOOL, loc };
6558
+ }
6559
+ // Untyped addition chooses concatenation or numeric addition only
6560
+ // after both operands have undergone ToPrimitive at runtime.
6561
+ if (op === ts.SyntaxKind.PlusToken) {
6562
+ const l = lowerer.coerceToExpected(left, DYN);
6563
+ const r = lowerer.coerceToExpected(right, DYN);
6564
+ if (l.type.kind === "dyn" && r.type.kind === "dyn") {
6565
+ const value = { kind: "libCall", fn: "dyn.add", args: [l, r], type: DYN, loc };
6566
+ // A statically known string operand guarantees a string result.
6567
+ return other.type.kind === "string" ? { kind: "dynCheck", value, type: STRING, loc } : value;
6135
6568
  }
6136
6569
  }
6137
6570
  }
@@ -6150,7 +6583,7 @@ export function lowerBinary(lowerer, expr) {
6150
6583
  // retain the SC2011 promise that --dynamic lifts this site.
6151
6584
  if (lowerer.dynamic)
6152
6585
  return lowerAnyBinaryInIsland(lowerer, expr, left, right, loc);
6153
- lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.SyntaxKind[op]}' operator`, expr);
6586
+ lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' operator`, expr);
6154
6587
  }
6155
6588
  // tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
6156
6589
  // itself); a dynamic equality would need a dyn walk — validate first.
@@ -6353,6 +6786,14 @@ export function lowerBinary(lowerer, expr) {
6353
6786
  const optionalStringPlus = lowerOptionalStringPlus();
6354
6787
  if (optionalStringPlus)
6355
6788
  return optionalStringPlus;
6789
+ if (isJsSourceFile(expr.getSourceFile())) {
6790
+ const primitive = (type) => type.kind === "f64" || type.kind === "string" || type.kind === "bool" || isUnitType(type) ||
6791
+ (type.kind === "union" && (lowerer.unions.get(type.unionId)?.arms.every(primitive) ?? false));
6792
+ if ((left.type.kind === "union" || right.type.kind === "union") && primitive(left.type) && primitive(right.type)) {
6793
+ lowerer.runtimeOptionalArithmeticTypes.set(expr, DYN);
6794
+ return { kind: "libCall", fn: "dyn.add", args: [lowerer.coerceToExpected(left, DYN), lowerer.coerceToExpected(right, DYN)], type: DYN, loc };
6795
+ }
6796
+ }
6356
6797
  lowerer.unsupported("SC1043", expr);
6357
6798
  break;
6358
6799
  case ts.SyntaxKind.MinusToken:
@@ -6395,8 +6836,11 @@ export function lowerBinary(lowerer, expr) {
6395
6836
  return indexed;
6396
6837
  if (plainBothNum)
6397
6838
  return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
6398
- if (bothStr)
6839
+ if (bothStr) {
6840
+ if (left.kind === "strLit" && right.kind === "strLit")
6841
+ return { kind: "boolLit", value: (left.value === right.value) !== negated, type: BOOL, loc };
6399
6842
  return { kind: "strEq", negated, left, right, type: BOOL, loc };
6843
+ }
6400
6844
  // bool === bool: a plain value compare (the config-drift checks'
6401
6845
  // `desired.lanMode !== actual.lanMode` shape).
6402
6846
  if (left.type.kind === "bool" && right.type.kind === "bool") {
@@ -6520,6 +6964,8 @@ export function lowerBinary(lowerer, expr) {
6520
6964
  if ((idLeft.type.kind === "array" ||
6521
6965
  idLeft.type.kind === "map" ||
6522
6966
  idLeft.type.kind === "set" ||
6967
+ idLeft.type.kind === "regex" ||
6968
+ idLeft.type.kind === "url" ||
6523
6969
  idLeft.type.kind === "object" ||
6524
6970
  idLeft.type.kind === "record" ||
6525
6971
  // Symbols ARE identity: `Symbol('a') === Symbol('a')` is false,
@@ -6560,21 +7006,283 @@ export function lowerBinary(lowerer, expr) {
6560
7006
  default:
6561
7007
  break;
6562
7008
  }
6563
- lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.SyntaxKind[op]}'`);
7009
+ lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}'`);
7010
+ }
7011
+ function lowerLogicalChain(lowerer, expr) {
7012
+ // Logical chains have a left-nested AST. Keep the native stack bounded
7013
+ // while retaining each pair's checker type and lazy operand evaluation.
7014
+ const parents = [];
7015
+ let current = expr;
7016
+ let result;
7017
+ while (true) {
7018
+ if (current.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
7019
+ const fallback = lowerAbsenceAwareOrChain(lowerer, current);
7020
+ if (fallback !== null) {
7021
+ result = fallback;
7022
+ break;
7023
+ }
7024
+ }
7025
+ const left = current.left;
7026
+ if (ts.isBinaryExpression(left) &&
7027
+ (left.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
7028
+ left.operatorToken.kind === ts.SyntaxKind.BarBarToken) &&
7029
+ !lowerer.chainRecvByNode.has(left)) {
7030
+ parents.push(current);
7031
+ current = left;
7032
+ continue;
7033
+ }
7034
+ result = lowerLogicalPair(lowerer, current, lowerAbsenceProbe(lowerer, left) ?? lowerer.lowerExpr(left));
7035
+ break;
7036
+ }
7037
+ for (let i = parents.length - 1; i >= 0; i--) {
7038
+ result = lowerLogicalPair(lowerer, parents[i], result);
7039
+ }
7040
+ return result;
7041
+ }
7042
+ function lowerLogicalPair(lowerer, expr, left) {
7043
+ const op = expr.operatorToken.kind;
7044
+ const loc = locOf(expr);
7045
+ const right = lowerer.lowerExpr(expr.right);
7046
+ // A LITERAL-unit left operand (a compile-time undefined/null — the
7047
+ // capability-probe members: `process.features.inspector ||
7048
+ // !flag.startsWith('--inspect')` reads undefined in a compiled
7049
+ // binary): statically falsy, so the operator folds JS-exactly —
7050
+ // `unit || X` IS X, `unit && X` IS the unit (X's side effects never
7051
+ // run in JS either; the lowered right is simply dropped). Literal
7052
+ // units only — computed unit-typed values keep the fences below.
7053
+ if (left.kind === "unitLit") {
7054
+ return op === ts.SyntaxKind.BarBarToken ? right : left;
7055
+ }
7056
+ // JS objects and symbols are always truthy. In `ref || fallback`
7057
+ // the fallback is therefore unreachable and the result is the
7058
+ // already-evaluated left value — the browser-fallback idiom used by
7059
+ // packages for `process.argv || []` and `process.env || {}`. The
7060
+ // operand itself remains in the IR, preserving reads/calls that
7061
+ // produce the reference; only the unreachable right lowering drops.
7062
+ if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
7063
+ return left;
7064
+ }
7065
+ if (left.type.kind === "dyn" || right.type.kind === "dyn") {
7066
+ // A checked-dynamic operand (`fn.name || '<anonymous>'` —
7067
+ // test/common's _mustCallInner): both sides live in the checked-dynamic tree and
7068
+ // the deciding test is ToBoolean over the dyn kind
7069
+ // (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
7070
+ // non-dyn side converts through the usual boundary; a value with
7071
+ // no dyn representation keeps the fence.
7072
+ const l = lowerer.coerceToExpected(left, DYN);
7073
+ const r = lowerer.coerceToExpected(right, DYN);
7074
+ if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
7075
+ lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
7076
+ }
7077
+ return {
7078
+ kind: "logical",
7079
+ op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7080
+ left: l,
7081
+ right: r,
7082
+ type: DYN, loc,
7083
+ };
7084
+ }
7085
+ if (left.type.kind === "jsval" || right.type.kind === "jsval") {
7086
+ return {
7087
+ kind: "logical",
7088
+ op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7089
+ left: lowerer.jsvalIn(left, expr.left),
7090
+ right: lowerer.jsvalIn(right, expr.right),
7091
+ type: JSVAL, loc,
7092
+ };
7093
+ }
7094
+ if (left.type.kind === "union" || right.type.kind === "union") {
7095
+ // Test the deciding operand in its own representation. The result
7096
+ // contains only the values that can survive short-circuit evaluation.
7097
+ let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
7098
+ // Index reads can be absent without noUncheckedIndexedAccess. Their
7099
+ // explicit nullish fallback still runs, even if the checker discarded it.
7100
+ if (target !== null) {
7101
+ const returned = [right.type, ...(op === ts.SyntaxKind.AmpersandAmpersandToken ? [left.type] : [])];
7102
+ for (const type of returned) {
7103
+ const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms ?? [] : [type];
7104
+ for (const arm of arms) {
7105
+ if (arm.kind === "undefinedT" && !isUnitType(target))
7106
+ target = lowerer.runtimeOptionalType(target);
7107
+ if (arm.kind === "nullT")
7108
+ target = withUnitArm(target, arm.kind, lowerer.unions) ?? target;
7109
+ }
7110
+ }
7111
+ }
7112
+ // A broad-JSDoc npm-static body can leave the CHECKER result `any`
7113
+ // even after declaration-backed specialization has recovered both
7114
+ // operands (`helpOption && args.find(...)` in commander). When the
7115
+ // left consists only of always-truthy references and falsy units,
7116
+ // the static result is exactly the right operand plus those units.
7117
+ if (op === ts.SyntaxKind.AmpersandAmpersandToken &&
7118
+ target === null &&
7119
+ lowerer.implicitParamTypes !== null &&
7120
+ npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
7121
+ (lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
7122
+ left.type.kind === "union") {
7123
+ const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
7124
+ const rightArms = right.type.kind === "union"
7125
+ ? lowerer.unions.get(right.type.unionId)?.arms
7126
+ : [right.type];
7127
+ const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
7128
+ if (leftArms && rightArms && rightIsStatic &&
7129
+ leftArms.some(isUnitType) &&
7130
+ leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
7131
+ const byKey = new Map();
7132
+ for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
7133
+ byKey.set(typeKey(arm), arm);
7134
+ }
7135
+ const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
7136
+ if (arms.length > 1) {
7137
+ const source = right.type.kind === "union" ? lowerer.unions.get(right.type.unionId) : undefined;
7138
+ target = { kind: "union", unionId: source
7139
+ ? lowerer.unions.transform(source, arms)
7140
+ : lowerer.unions.intern(arms) };
7141
+ }
7142
+ }
7143
+ }
7144
+ if (target?.kind === "union") {
7145
+ // `&&` whose left does NOT fit the result union: the checker
7146
+ // built that result by DROPPING left arms which are always
7147
+ // truthy (`Option | undefined && string | undefined` answers
7148
+ // `string | undefined`, not `Option | string | undefined`).
7149
+ // Evaluate the left once, test it in its own union, and retag it
7150
+ // only on the falsy path. Any stranded reference arms are
7151
+ // unreachable there because every JS object is truthy.
7152
+ if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
7153
+ const def = lowerer.unions.get(left.type.unionId);
7154
+ const trappable = new Set();
7155
+ def?.arms.forEach((arm, tag) => {
7156
+ if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
7157
+ trappable.add(tag);
7158
+ }
7159
+ });
7160
+ const retag = trappable.size === 0
7161
+ ? null
7162
+ : lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
7163
+ if (retag) {
7164
+ lowerer.requireTruthyUnion(left.type.unionId, expr);
7165
+ const stmts = [];
7166
+ let stable = left;
7167
+ if (!isSafeToRepeat(left)) {
7168
+ const local = lowerer.declareHiddenLocal("%and", left.type);
7169
+ stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
7170
+ stable = varRef(local.id, left.type, loc);
7171
+ }
7172
+ const result = {
7173
+ kind: "ternary",
7174
+ cond: lowerer.ensureBool(stable, expr.left),
7175
+ then: lowerer.coerceInto(expr.right, right, target),
7176
+ else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
7177
+ type: target,
7178
+ loc,
7179
+ };
7180
+ return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
7181
+ }
7182
+ }
7183
+ // `||` whose left does NOT fit the result union: the checker built
7184
+ // that result by DROPPING the left's falsy arms (`process.env.X ||
7185
+ // null` is `string | null`, `|| 3000` is `string | number` — the
7186
+ // `undefined` is gone from both), so coercing the left eagerly, as
7187
+ // the shared shape below does, retags an arm the test is about to
7188
+ // rule out and throws where Node yields the default. Single-eval
7189
+ // instead: test the left in its OWN union and retag only on the
7190
+ // truthy side, where the dropped arms are unreachable.
7191
+ if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
7192
+ const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
7193
+ if (retag) {
7194
+ lowerer.requireTruthyUnion(left.type.unionId, expr);
7195
+ return {
7196
+ kind: "orDefault",
7197
+ left,
7198
+ right: lowerer.coerceInto(expr.right, right, target),
7199
+ retag,
7200
+ type: target,
7201
+ loc,
7202
+ };
7203
+ }
7204
+ }
7205
+ lowerer.requireTruthyUnion(target.unionId, expr);
7206
+ return {
7207
+ kind: "logical",
7208
+ op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7209
+ left: lowerer.coerceInto(expr.left, left, target),
7210
+ right: lowerer.coerceInto(expr.right, right, target),
7211
+ type: target, loc,
7212
+ };
7213
+ }
7214
+ // `u || d` NARROWED by the default: the checker types the result
7215
+ // as u's single non-unit arm (`marker() || "default"`) — evaluate
7216
+ // u once, truthy extracts the arm, falsy takes d lazily (the
7217
+ // orDefault node, nullish's truthiness sibling). An UNMAPPABLE
7218
+ // checker result takes the same rule (`options.runner ||
7219
+ // defaultRunner` — tsc's union of two structurally-compatible
7220
+ // function types has no representation, while the left's one
7221
+ // non-unit arm is the only representable answer): the default
7222
+ // must coerce into the arm or fence on its own.
7223
+ if (op === ts.SyntaxKind.BarBarToken &&
7224
+ left.type.kind === "union") {
7225
+ const def = lowerer.unions.get(left.type.unionId);
7226
+ const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
7227
+ const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
7228
+ if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
7229
+ lowerer.requireTruthyUnion(left.type.unionId, expr);
7230
+ const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
7231
+ return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
7232
+ }
7233
+ }
7234
+ // JavaScript defaults can join differently inferred object layouts.
7235
+ // When both operands have native checked representations, preserve
7236
+ // the deciding value and short-circuit evaluation in that domain.
7237
+ if (isJsSourceFile(expr.getSourceFile()) &&
7238
+ lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
7239
+ return {
7240
+ kind: "logical", op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7241
+ left: lowerer.coerceInto(expr.left, left, DYN),
7242
+ right: lowerer.coerceInto(expr.right, right, DYN), type: DYN, loc,
7243
+ };
7244
+ }
7245
+ lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
7246
+ }
7247
+ const kind = left.type.kind;
7248
+ if (kind !== right.type.kind ||
7249
+ (kind !== "f64" && kind !== "string" && kind !== "bool")) {
7250
+ // Mixed PLAIN operands (`value || null`, `flag || undefined`,
7251
+ // `s || 0`): JS value semantics still compile when the checker
7252
+ // types the RESULT as one union both operands coerce into — the
7253
+ // same lift the union-operand path above takes, arriving here
7254
+ // with two plain arm values instead.
7255
+ const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
7256
+ if (target?.kind === "union") {
7257
+ lowerer.requireTruthyUnion(target.unionId, expr);
7258
+ return {
7259
+ kind: "logical",
7260
+ op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7261
+ left: lowerer.coerceInto(expr.left, left, target),
7262
+ right: lowerer.coerceInto(expr.right, right, target),
7263
+ type: target, loc,
7264
+ };
7265
+ }
7266
+ lowerer.unsupported("SC1042", expr);
7267
+ }
7268
+ return {
7269
+ kind: "logical",
7270
+ op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
7271
+ left, right, type: left.type, loc,
7272
+ };
6564
7273
  }
6565
7274
  function lowerAbsenceAwareOrChain(lowerer, expr) {
6566
7275
  const nodes = [];
6567
- const collect = (node) => {
6568
- if (ts.isBinaryExpression(node) &&
6569
- node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
6570
- collect(node.left);
6571
- collect(node.right);
7276
+ const pending = [expr];
7277
+ while (pending.length !== 0) {
7278
+ const node = pending.pop();
7279
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
7280
+ pending.push(node.right, node.left);
6572
7281
  }
6573
7282
  else {
6574
7283
  nodes.push(node);
6575
7284
  }
6576
- };
6577
- collect(expr);
7285
+ }
6578
7286
  if (nodes.length < 3)
6579
7287
  return null;
6580
7288
  const lowered = nodes.map((node) => lowerAbsenceProbe(lowerer, node) ?? lowerer.lowerExpr(node));
@@ -6645,10 +7353,7 @@ function errorRootedObjectOf(lowerer, t) {
6645
7353
  * shape, so the sibling typeof lowerings keep trying. */
6646
7354
  function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
6647
7355
  const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6648
- for (const [a, b] of [
6649
- [expr.left, expr.right],
6650
- [expr.right, expr.left],
6651
- ]) {
7356
+ for (const [a, b] of operandPairs(expr)) {
6652
7357
  if (!ts.isTypeOfExpression(a))
6653
7358
  continue;
6654
7359
  let prop = a.expression;
@@ -6708,10 +7413,7 @@ function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
6708
7413
  }
6709
7414
  export function lowerCaughtTypeofTest(lowerer, expr, loc) {
6710
7415
  const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6711
- for (const [a, b] of [
6712
- [expr.left, expr.right],
6713
- [expr.right, expr.left],
6714
- ]) {
7416
+ for (const [a, b] of operandPairs(expr)) {
6715
7417
  if (!ts.isTypeOfExpression(a))
6716
7418
  continue;
6717
7419
  const local = lowerer.caughtLocalOf(a.expression);
@@ -6834,12 +7536,13 @@ export function aliasTypeofNarrows(lowerer, cond, polarity) {
6834
7536
  walk(cond, polarity);
6835
7537
  return out;
6836
7538
  }
7539
+ /** Visit both operand orders while keeping the sequence an array of pairs. */
7540
+ function operandPairs(expr) {
7541
+ return [[expr.left, expr.right], [expr.right, expr.left]];
7542
+ }
6837
7543
  /** One ===/!== leaf of aliasTypeofNarrows — the qualification battery. */
6838
7544
  function aliasTypeofArmOf(lowerer, e, strip) {
6839
- for (const [a0, b] of [
6840
- [e.left, e.right],
6841
- [e.right, e.left],
6842
- ]) {
7545
+ for (const [a0, b] of operandPairs(e)) {
6843
7546
  if (!ts.isStringLiteral(b))
6844
7547
  continue;
6845
7548
  const a = strip(a0);
@@ -6868,7 +7571,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
6868
7571
  if (!bindingNeverReassigned(lowerer, valSym, valDecl))
6869
7572
  continue;
6870
7573
  const fn = valDecl.parent;
6871
- if (!(e.pos >= fn.pos && e.end <= fn.end))
7574
+ if (fn === undefined || !(e.pos >= fn.pos && e.end <= fn.end))
6872
7575
  continue;
6873
7576
  const valT = lowerer.typeOf(opnd); // override-aware: implicit bindings compose
6874
7577
  const mapped = lowerer.mapTypeOf(valT);
@@ -6910,10 +7613,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
6910
7613
  * bare-typeof value form then composes with strEq for pure operands). */
6911
7614
  function lowerUnionTypeofTest(lowerer, expr, loc) {
6912
7615
  const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6913
- for (const [a, b] of [
6914
- [expr.left, expr.right],
6915
- [expr.right, expr.left],
6916
- ]) {
7616
+ for (const [a, b] of operandPairs(expr)) {
6917
7617
  if (!ts.isTypeOfExpression(a))
6918
7618
  continue;
6919
7619
  if (lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
@@ -6961,12 +7661,13 @@ function lowerUnionTypeofTest(lowerer, expr, loc) {
6961
7661
  * side is a typeof over a dyn-typed operand (not this pattern). */
6962
7662
  function lowerDynTypeofTest(lowerer, expr, loc) {
6963
7663
  const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6964
- for (const [a, b] of [
6965
- [expr.left, expr.right],
6966
- [expr.right, expr.left],
6967
- ]) {
7664
+ for (const [a, b] of operandPairs(expr)) {
6968
7665
  if (!ts.isTypeOfExpression(a))
6969
7666
  continue;
7667
+ // Non-literal comparisons use the ordinary typeof string value,
7668
+ // preserving both operands' evaluation order without a narrowing test.
7669
+ if (!ts.isStringLiteral(b))
7670
+ continue;
6970
7671
  const declared = lowerer.mapTypeOf(lowerer.typeOf(a.expression));
6971
7672
  let value;
6972
7673
  if (declared?.kind === "dyn") {
@@ -7024,10 +7725,7 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
7024
7725
  value = probed;
7025
7726
  }
7026
7727
  }
7027
- if (!ts.isStringLiteral(b)) {
7028
- lowerer.unsupported("SC1090", expr, "'typeof' tests on 'unknown' values against non-literal strings");
7029
- }
7030
- if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "undefined") {
7728
+ if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "undefined" || b.text === "bigint" || b.text === "symbol") {
7031
7729
  return {
7032
7730
  kind: "dynTest",
7033
7731
  test: b.text,
@@ -7064,14 +7762,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
7064
7762
  loc,
7065
7763
  };
7066
7764
  }
7067
- // Kinds a dyn box can NEVER hold — no conversion into 'unknown'
7068
- // exists for bigints or symbols (dynFrom's domain is JSON-safe data
7069
- // + bytes + Error + functions), so the test's answer is a
7070
- // compile-time constant: false for ===, true for !==. The capability
7071
- // probes this settles (`typeof ms === 'bigint'` in dual-mode number
7072
- // helpers) then FOLD their impossible arm (lowerTernary), which is
7073
- // what lets the reachable arm compile statically.
7074
- if (b.text === "bigint" || b.text === "symbol") {
7765
+ // Native checked values do not yet carry symbols.
7766
+ if (b.text === "symbol") {
7075
7767
  return { kind: "boolLit", value: negated, type: BOOL, loc };
7076
7768
  }
7077
7769
  lowerer.unsupported("SC1090", b, `'typeof' tests against "${b.text}" on 'unknown' values (only "string"/"number"/` +
@@ -7093,7 +7785,7 @@ function lowerReplacedExportsRead(lowerer, expr) {
7093
7785
  if (!ts.isIdentifier(recv) || recv.text !== "exports")
7094
7786
  return null;
7095
7787
  const sf = expr.getSourceFile();
7096
- if (!isJsSourceFile(sf) || isNodeEsmFile(sf))
7788
+ if (!isJsSourceFile(sf) || isNodeEsmFile(sf, lowerer.program))
7097
7789
  return null;
7098
7790
  if (lowerer.peekLocal(recv) || lowerer.globalOf(recv))
7099
7791
  return null; // a user binding shadows
@@ -7270,6 +7962,34 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7270
7962
  const right = lowerer.lowerExpr(expr.right);
7271
7963
  return { kind: "jsOp", op: "instanceOf", args: [left, right], type: BOOL, loc };
7272
7964
  }
7965
+ if (!lowerer.dynamic && (lowerer.isStdlibGlobal(expr.right, "Set") || lowerer.isStdlibGlobal(expr.right, "Map"))) {
7966
+ const collection = lowerer.isStdlibGlobal(expr.right, "Map") ? "map" : "set";
7967
+ const value = lowerer.lowerExpr(expr.left);
7968
+ if (value.type.kind === "dyn")
7969
+ return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs", args: [value], type: BOOL, loc };
7970
+ if (value.type.kind === collection)
7971
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
7972
+ result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
7973
+ }
7974
+ if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
7975
+ if (lowerer.dynamic) {
7976
+ const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.left), expr.left);
7977
+ const ctor = { kind: "jsOp", op: "globalGet", name: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "WeakMap" : "WeakSet", args: [], type: JSVAL, loc };
7978
+ return { kind: "jsOp", op: "instanceOf", args: [value, ctor], type: BOOL, loc };
7979
+ }
7980
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7981
+ return { kind: "libCall", fn: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "weakMap.is" : "weakSet.is", args: [value], type: BOOL, loc };
7982
+ }
7983
+ const rhsBuiltin = ts.isIdentifier(expr.right) ? lowerer.builtinImportOf(expr.right) :
7984
+ ts.isPropertyAccessExpression(expr.right) ? lowerer.builtinMemberOf(expr.right) : null;
7985
+ if (lowerer.isStdlibGlobal(expr.right, "URL") || rhsBuiltin?.module === "url" && rhsBuiltin.member === "URL") {
7986
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7987
+ return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
7988
+ }
7989
+ if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
7990
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7991
+ return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
7992
+ }
7273
7993
  const rhsSymbol = ts.isIdentifier(expr.right) ? lowerer.resolveValueSymbol(expr.right) : null;
7274
7994
  // `x instanceof events.EventEmitter` — the namespace-member spelling
7275
7995
  // resolves to the same ambient class as the named import.
@@ -7303,29 +8023,41 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7303
8023
  // decoration result — fall to the class-VALUE path below, whose
7304
8024
  // instanceOfValue reads the interval off the bound class object.
7305
8025
  const directRhs = rhsSymbol ? lowerer.classBySymbol.get(rhsSymbol) : undefined;
7306
- const target = (directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
8026
+ const target = (directRhs?.localClass || directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
7307
8027
  rhsNsClass ??
7308
8028
  lowerer.builtinErrorInfoOf(rhsSymbol) ??
8029
+ lowerer.builtinErrorInfoOf(rhsMemberSymbol) ??
7309
8030
  lowerer.builtinEmitterInfoOf(rhsSymbol) ??
7310
8031
  lowerer.builtinEmitterInfoOf(rhsMemberSymbol) ??
7311
8032
  lowerer.builtinStreamInfoOf(rhsSymbol) ??
7312
8033
  lowerer.builtinStreamInfoOf(rhsMemberSymbol) ??
7313
8034
  undefined;
7314
8035
  if (!target) {
7315
- // `u instanceof Uint8Array` on an `unknown` value: the checked-dynamic tree carries a
7316
- // bytes kind — one runtime tag test, and tsc's narrowing types the
7317
- // true branch (reads bridge through maybeNarrow's validated
7318
- // extraction, like the typeof tests). Node's Buffer IS a Uint8Array
7319
- // subclass and rides the same bytes kind, so both worlds answer true
7320
- // for Buffer payloads — Node-exact (the bytes kind's other
7321
- // divergences are SEMANTICS.md 45). Catch bindings stay out (their
7322
- // payload is a typed snapshot, not a dyn).
7323
- if (ts.isIdentifier(expr.right) &&
7324
- lowerer.isStdlibGlobal(expr.right, "Uint8Array") &&
7325
- !lowerer.caughtLocalOf(expr.left)) {
7326
- const left = lowerer.lowerExpr(expr.left);
7327
- if (left.type.kind === "dyn") {
7328
- return { kind: "dynTest", test: "bytes", value: left, type: BOOL, loc };
8036
+ const date = lowerer.isStdlibGlobal(expr.right, "Date");
8037
+ const url = lowerer.isStdlibGlobal(expr.right, "URL");
8038
+ if ((date || url) && !lowerer.caughtLocalOf(expr.left)) {
8039
+ const value = lowerer.lowerExpr(expr.left);
8040
+ if (value.type.kind === "dyn")
8041
+ return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
8042
+ if (value.type.kind === "union")
8043
+ lowerer.unsupported("SC1090", expr, "Date or URL instanceof over a union");
8044
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
8045
+ result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
8046
+ }
8047
+ // Unknown storage retains each numeric typed array's exact native brand.
8048
+ if (ts.isIdentifier(expr.right) && !lowerer.caughtLocalOf(expr.left)) {
8049
+ const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
8050
+ if (elem !== undefined) {
8051
+ const left = lowerer.lowerExpr(expr.left);
8052
+ if (left.type.kind === "dyn") {
8053
+ return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
8054
+ }
8055
+ if (left.type.kind === "bytes") {
8056
+ return {
8057
+ kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
8058
+ result: { kind: "boolLit", value: left.type.elem === elem, type: BOOL, loc }, type: BOOL, loc,
8059
+ };
8060
+ }
7329
8061
  }
7330
8062
  }
7331
8063
  // `x instanceof RegExp` over a union with a regex arm (the
@@ -7338,6 +8070,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7338
8070
  lowerer.isStdlibGlobal(expr.right, "RegExp") &&
7339
8071
  !lowerer.caughtLocalOf(expr.left)) {
7340
8072
  const left = lowerer.lowerExpr(expr.left);
8073
+ if (left.type.kind === "dyn")
8074
+ return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [left], type: BOOL, loc };
7341
8075
  if (left.type.kind === "union") {
7342
8076
  const def = lowerer.unions.get(left.type.unionId);
7343
8077
  const tag = def ? def.arms.findIndex((a) => a.kind === "regex") : -1;
@@ -7355,7 +8089,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7355
8089
  // STANDALONE target class has exactly one possible runtime value
7356
8090
  // (itself — no descendants can flow into the slot), so the answer
7357
8091
  // folds statically exactly like the named-target folds below.
7358
- const rhsClassval = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
8092
+ const rhsClassval = storedClassValueType(lowerer, expr.right);
7359
8093
  if (rhsClassval?.kind === "classval" && !lowerer.caughtLocalOf(expr.left)) {
7360
8094
  const targetInfo = lowerer.classes.get(rhsClassval.className);
7361
8095
  if (!targetInfo) {
@@ -7372,7 +8106,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7372
8106
  const lhsInfo = lowerer.classes.get(left.type.className);
7373
8107
  if (!lhsInfo)
7374
8108
  throw new InternalCompilerError(`lowerer bug: unknown class ${left.type.className}`);
7375
- if (lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo)) {
8109
+ if ((lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo)) ||
8110
+ (targetInfo.localClass !== undefined && lhsInfo.localClass !== undefined)) {
7376
8111
  const classValue = lowerer.lowerExpr(expr.right);
7377
8112
  if (classValue.type.kind !== "classval")
7378
8113
  lowerer.badType(expr.right, lowerer.typeOf(expr.right));
@@ -7419,6 +8154,9 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7419
8154
  // (name strings are user-writable), so `u instanceof TypeError` keeps
7420
8155
  // the fence. Reads past the narrow bridge through maybeNarrow's
7421
8156
  // validated %Error extraction. SEMANTICS.md 67.
8157
+ if (left.type.kind === "dyn" && !target.def.runtime && !target.builtinError && !target.builtinEmitter && !target.builtinStream && !target.localClass) {
8158
+ return classInstanceOf(lowerer, left, target, loc);
8159
+ }
7422
8160
  if (left.type.kind === "dyn" && target.def.name === "%Error") {
7423
8161
  return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
7424
8162
  }
@@ -7441,6 +8179,44 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7441
8179
  if (left.type.kind === "dyn") {
7442
8180
  lowerer.unsupported("SC1090", expr, `'instanceof ${target.def.name.replace(/^%/, "")}' on 'unknown' values (only the Error classes answer — test 'instanceof Error' and read '.name')`);
7443
8181
  }
8182
+ if (left.type.kind === "union") {
8183
+ const unionId = left.type.unionId;
8184
+ const arms = lowerer.unions.get(unionId)?.arms;
8185
+ // Ordinary records and scalar values do not carry a class prototype.
8186
+ // Dynamic/opaque runtime objects keep their existing instanceof fences.
8187
+ if (arms?.every((arm) => arm.kind === "object" || arm.kind === "record" ||
8188
+ arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" ||
8189
+ arm.kind === "bigint" || arm.kind === "symbol" || isUnitType(arm))) {
8190
+ const key = `instanceof.union:${unionId}:${target.def.name}`;
8191
+ let helper = lowerer.widthHelpers.get(key);
8192
+ if (!helper) {
8193
+ helper = `%instanceof.union.${lowerer.widthHelpers.size}`;
8194
+ lowerer.widthHelpers.set(key, helper);
8195
+ const value = { kind: "varRef", localId: "value.0", type: left.type, loc };
8196
+ const body = [];
8197
+ arms.forEach((arm, tag) => {
8198
+ if (arm.kind !== "object")
8199
+ return;
8200
+ const lhsInfo = lowerer.classes.get(arm.className);
8201
+ if (!lhsInfo)
8202
+ throw new InternalCompilerError(`lowerer bug: unknown class ${arm.className}`);
8203
+ const result = lowerer.inHierarchy(lhsInfo) && lowerer.inHierarchy(target)
8204
+ ? { kind: "instanceOf", value: { kind: "unionNarrow", unionId, tag, value, type: arm, loc }, className: target.def.name, type: BOOL, loc }
8205
+ : { kind: "boolLit", value: arm.className === target.def.name || lowerer.isSubclassOf(arm.className, target.def.name), type: BOOL, loc };
8206
+ body.push({
8207
+ kind: "if", cond: { kind: "unionIsTag", unionId, tag, negated: false, value, type: BOOL, loc },
8208
+ then: [{ kind: "return", value: result, loc }], else_: null, loc,
8209
+ });
8210
+ });
8211
+ body.push({ kind: "return", value: { kind: "boolLit", value: false, type: BOOL, loc }, loc });
8212
+ lowerer.liftedFns.push({
8213
+ name: helper, params: [{ localId: "value.0", name: "value", type: left.type }], returnType: BOOL,
8214
+ locals: [{ id: "value.0", name: "value", type: left.type, mutable: false }], body, loc,
8215
+ });
8216
+ }
8217
+ return { kind: "call", callee: helper, args: [left], type: BOOL, loc };
8218
+ }
8219
+ }
7444
8220
  if (left.type.kind !== "object") {
7445
8221
  lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
7446
8222
  }
@@ -7613,6 +8389,12 @@ function lowerPrivateIn(lowerer, expr, priv, loc) {
7613
8389
  * and dyn/unknown stay fenced. Keys are literal strings — a computed key
7614
8390
  * over a shape would need the runtime key table. */
7615
8391
  function lowerInExpression(lowerer, expr, loc) {
8392
+ if (lowerer.isStdlibGlobal(expr.right, "process") && lowerer.foldedStringKeyOf(expr.left) === "hrtime") {
8393
+ return { kind: "boolLit", value: true, type: BOOL, loc };
8394
+ }
8395
+ const globalKey = globalSymbolKey(lowerer, expr.right, expr.left);
8396
+ if (globalKey)
8397
+ return { kind: "libCall", fn: "dyn.globalSymbolHas", args: [globalKey], type: BOOL, loc };
7616
8398
  // `#name in obj` — the ergonomic brand check (ES2022) — resolves
7617
8399
  // before any string-key machinery: the left operand is a private
7618
8400
  // NAME, not a value.
@@ -7664,14 +8446,13 @@ function lowerInExpression(lowerer, expr, loc) {
7664
8446
  // `in` shares the coercion).
7665
8447
  {
7666
8448
  const probed = tryLowerExpression(lowerer, expr.right);
7667
- if (probed?.type.kind === "dyn") {
7668
- let k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
7669
- if (k.type.kind === "f64" || k.type.kind === "string" || k.type.kind === "dyn") {
7670
- k = lowerer.ensureString(k, expr.left);
7671
- const recvD = lowerer.lowerExpr(expr.right);
7672
- if (recvD.type.kind === "dyn") {
7673
- return { kind: "libCall", fn: "dyn.hasKey", args: [recvD, k], type: BOOL, loc };
7674
- }
8449
+ if (probed && (probed.type.kind === "dyn" || isDynTypedRefType(probed.type))) {
8450
+ const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
8451
+ if (k.type.kind === "symbol" || k.type.kind === "dyn" || k.type.kind === "string" || k.type.kind === "f64") {
8452
+ const boxed = lowerer.coerceToExpected(k, DYN);
8453
+ const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
8454
+ return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
8455
+ result: { kind: "libCall", fn: "dyn.hasKeyComputed", args: [lowerer.coerceToExpected(lowerer.lowerExpr(expr.right), DYN), varRef(keyLocal.id, DYN, loc)], type: BOOL, loc }, type: BOOL, loc };
7675
8456
  }
7676
8457
  }
7677
8458
  }
@@ -7697,7 +8478,16 @@ function lowerInExpression(lowerer, expr, loc) {
7697
8478
  };
7698
8479
  return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
7699
8480
  }
7700
- const recv = lowerer.lowerExpr(expr.right);
8481
+ let recv = lowerer.lowerExpr(expr.right);
8482
+ if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type)) {
8483
+ recv = lowerer.coerceToExpected(recv, DYN);
8484
+ }
8485
+ const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
8486
+ if (recv.type.kind === "union" && siteType?.kind === "union" && !typeEquals(recv.type, siteType)) {
8487
+ const helper = lowerer.narrowedRetagHelper(expr.right, recv.type.unionId, siteType.unionId, loc);
8488
+ if (helper)
8489
+ recv = { kind: "call", callee: helper, args: [recv], type: siteType, loc };
8490
+ }
7701
8491
  // Error-rooted receivers (builtin or user subclass — the isErrnoException
7702
8492
  // predicate's `err instanceof Error && "code" in err` shape): `code`
7703
8493
  // answers from the runtime error's code slot (stamped by fs/system/
@@ -7722,6 +8512,9 @@ function lowerInExpression(lowerer, expr, loc) {
7722
8512
  while (info && info.base)
7723
8513
  info = info.base;
7724
8514
  if (info?.def.name === "%Error") {
8515
+ if (key === "cause") {
8516
+ return { kind: "libCall", fn: "error.hasCause", args: [recv], type: BOOL, loc };
8517
+ }
7725
8518
  if (key === "code") {
7726
8519
  const codeType = lowerer.envValueType();
7727
8520
  if (codeType.kind !== "union")
@@ -8366,12 +9159,24 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
8366
9159
  }
8367
9160
  return { kind: "call", callee: helper, args: [recv], type: recordT, loc };
8368
9161
  }
8369
- /** Field read `obj.f` on class-instance and record receivers, through the
8370
- * shared FieldTarget union (fieldGet / recordGet). Bound method references
8371
- * on classes are rejected specifically; func-typed record fields are
8372
- * ordinary closure values, so bare references to them work (unlike class
8373
- * methods, which have no bound-value form). */
9162
+ /** Field reads use the shared FieldTarget union. Native method reads select
9163
+ * an interned unbound callable; invocation supplies its receiver separately. */
8374
9164
  export function lowerFieldRead(lowerer, expr) {
9165
+ // A checked receiver already owns its property table. An ambient
9166
+ // generic method signature must not divert its value into native
9167
+ // object-literal specialization (Array.prototype.map is one example).
9168
+ if (ts.isIdentifier(expr.expression)) {
9169
+ const stored = lowerer.peekLocal(expr.expression) ?? lowerer.globalOf(expr.expression);
9170
+ if (stored?.type.kind === "dyn") {
9171
+ const value = lowerer.lowerExpr(expr.expression);
9172
+ if (value.type.kind === "dyn")
9173
+ return {
9174
+ kind: "dynKeyGet", value,
9175
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
9176
+ ...(expr.questionDotToken || hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr),
9177
+ };
9178
+ }
9179
+ }
8375
9180
  const target = lowerer.fieldTarget(expr);
8376
9181
  if (target)
8377
9182
  return lowerer.fieldGetExpr(target, locOf(expr), expr);
@@ -8388,7 +9193,9 @@ export function lowerFieldRead(lowerer, expr) {
8388
9193
  if (receiverIr?.kind === "object" &&
8389
9194
  (lowerer.findMethodOn(lowerer.classes.get(receiverIr.className) ?? null, expr.name.text) ||
8390
9195
  findGenericMethodOn(lowerer, lowerer.classes.get(receiverIr.className) ?? null, expr.name.text))) {
8391
- lowerer.unsupported("SC1090", expr, `bound method references (call '${expr.name.text}' directly)`);
9196
+ const info = lowerer.classes.get(receiverIr.className);
9197
+ if (info)
9198
+ return lowerClassMethodValue(lowerer, expr, info);
8392
9199
  }
8393
9200
  // An object-literal GENERIC method as a VALUE (`o.m` — the member is
8394
9201
  // excluded from the record shape): the pinned-value rule verbatim when
@@ -8448,16 +9255,22 @@ export function lowerFieldRead(lowerer, expr) {
8448
9255
  * and `switch (r.kind)` work without dedicated test nodes. Anything else
8449
9256
  * on a union receiver is rejected specifically (narrow first). */
8450
9257
  export function lowerUnionProperty(lowerer, expr) {
8451
- if (expr.questionDotToken)
9258
+ if (lowerer.chainBlocked(expr))
8452
9259
  return null;
8453
9260
  const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
8454
9261
  if (receiverIr?.kind !== "union")
8455
9262
  return null;
8456
- // Lower the receiver FIRST and read its actual IR union: a partially
8457
- // narrowed receiver (checker type = a SUB-union of the binding's union)
8458
- // stays the full union at runtime, and the tag switch must cover the
8459
- // full union's arms.
8460
- const value = lowerer.lowerExpr(expr.expression);
9263
+ // Lower the receiver first: its runtime value can still carry the full
9264
+ // binding union even when the checker has removed several variants.
9265
+ let value = lowerer.lowerExpr(expr.expression);
9266
+ // A guard can remove several variants without selecting one record.
9267
+ // Materialize that sub-union before looking for common fields; arms
9268
+ // ruled out by the guard must not veto a field shared by the survivors.
9269
+ if (value.type.kind === "union" && !typeEquals(value.type, receiverIr)) {
9270
+ const helper = lowerer.narrowedRetagHelper(expr.expression, value.type.unionId, receiverIr.unionId, locOf(expr));
9271
+ if (helper)
9272
+ value = { kind: "call", callee: helper, args: [value], type: receiverIr, loc: locOf(expr) };
9273
+ }
8461
9274
  // A checker-union receiver whose VALUE lowered to a plain RECORD (the
8462
9275
  // merged-signature fiction — `runner(cmd, args)` where runner joined
8463
9276
  // a structural runner type with spawnSync's, and the local adopted
@@ -8553,20 +9366,29 @@ export function lowerUnionProperty(lowerer, expr) {
8553
9366
  const keyed = lowerUnionKeyedRead(lowerer, expr, value.type.unionId, value, key, field);
8554
9367
  if (keyed)
8555
9368
  return keyed;
9369
+ // JavaScript overloads commonly inspect a class brand on a scalar-or-
9370
+ // instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
9371
+ // missing-property semantics and dispatch class data through its native
9372
+ // capsule instead of demanding a field shared by every union arm.
9373
+ if (isJsSourceFile(expr.getSourceFile()) && def.arms.some(isDynTypedRefType) && lowerer.dynConvertible(value.type)) {
9374
+ return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
9375
+ ...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
9376
+ }
8556
9377
  lowerer.unsupported("SC1090", expr, `reading '${field}' on a union-typed value (every arm must be an object/record ` +
8557
9378
  `with a same-typed field '${field}'; ` +
8558
9379
  `${NARROW_FIRST})`);
8559
9380
  }
8560
9381
  /** The unionDisc generalization: a keyed read on a union receiver whose
8561
9382
  * arms answer DIFFERENT (but joinable) types. Each arm contributes its
8562
- * declared answer — a declared field's type (literal keys), an
9383
+ * declared answer — an array element's type (number keys), a declared field's type (literal keys), an
8563
9384
  * index-signature arm's value type (plus its declared fields' types for
8564
9385
  * runtime keys, which reach them through the keyed-read helper), and
8565
9386
  * UNDEFINED for unit arms (reachable only through optional-chain tails,
8566
9387
  * where JS short-circuits to undefined; a unit arm the checker narrowed
8567
9388
  * away is simply unreachable). The result type is the JOIN of those
8568
- * answers; every arm's answer must be the join itself or one of its
8569
- * arms (sub-union RE-TAGGING between distinct unions stays fenced).
9389
+ * answers; every arm's answer must be the join itself, one of its
9390
+ * arms, or a sub-union whose payload representations are unchanged.
9391
+ * Array reads retain the ordinary invalid-index trap policy.
8570
9392
  * Returns null when any arm cannot answer — the caller owns the fence
8571
9393
  * message. The caller (the property/element dispatch) maybeNarrows. */
8572
9394
  function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
@@ -8602,6 +9424,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
8602
9424
  push(UNDEFINED_T);
8603
9425
  continue;
8604
9426
  }
9427
+ if (key.type.kind === "f64") {
9428
+ if (arm.kind !== "array" || !pushAnswer(arm.elem))
9429
+ return null;
9430
+ continue;
9431
+ }
8605
9432
  if (arm.kind !== "record")
8606
9433
  return null;
8607
9434
  const shape = lowerer.shapes.get(arm.shapeId);
@@ -8631,13 +9458,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
8631
9458
  if (joinArms.length === 0)
8632
9459
  return null;
8633
9460
  // The join must be a BUILDABLE union: arm kinds the union invariants
8634
- // admit (no map/dyn/jsval/generator arms; func arms only beside
8635
- // func/unit siblings — unionFuncSetArmsOk, the validator's rule). A
8636
- // join mixing, say, a func answer with data answers has no union to
8637
- // surface as; the caller owns the fence message.
9461
+ // admit (no dyn/jsval/generator arms; containers only beside unit
9462
+ // siblings). The caller owns the diagnostic for a refused join.
8638
9463
  if (joinArms.length > 1 &&
8639
- (!unionFuncSetArmsOk(joinArms) ||
8640
- joinArms.some((a) => a.kind === "map" || a.kind === "dyn" || a.kind === "jsval" || a.kind === "generator" || a.kind === "caught"))) {
9464
+ (!unionContainerArmsOk(joinArms) ||
9465
+ joinArms.some((a) => a.kind === "dyn" || a.kind === "jsval" || a.kind === "generator" || a.kind === "caught"))) {
8641
9466
  return null;
8642
9467
  }
8643
9468
  joinArms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
@@ -8645,13 +9470,19 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
8645
9470
  // Pass 2: every arm's answer must SURFACE as the join, and index arms
8646
9471
  // must pass the single-record keyed-read constraints (the helper is
8647
9472
  // shared with recordKeyGet, its missing-key policy included).
8648
- const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0);
9473
+ const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
9474
+ exactUnionWidening(lowerer, t, type);
8649
9475
  for (const arm of def.arms) {
8650
9476
  if (isUnitType(arm)) {
8651
9477
  if (!(type.kind === "union" && lowerer.armTag(type.unionId, UNDEFINED_T) >= 0))
8652
9478
  return null;
8653
9479
  continue;
8654
9480
  }
9481
+ if (key.type.kind === "f64") {
9482
+ if (arm.kind !== "array" || !surfaces(arm.elem))
9483
+ return null;
9484
+ continue;
9485
+ }
8655
9486
  if (arm.kind !== "record")
8656
9487
  return null;
8657
9488
  const shape = lowerer.shapes.get(arm.shapeId);
@@ -8669,6 +9500,75 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
8669
9500
  }
8670
9501
  return { kind: "unionKeyGet", unionId, key, value, type, loc: locOf(expr) };
8671
9502
  }
9503
+ /** A structural TypeScript view does not change a nominal object's layout
9504
+ * or accessor dispatch. Resolve the property from the represented class;
9505
+ * the optional checker view must not turn its getter into a record read. */
9506
+ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved) {
9507
+ const info = lowerer.classes.get(receiverType.className);
9508
+ if (!info) {
9509
+ // A receiver typed as a class whose collection deferred: the
9510
+ // deferred diagnostics are what explains the miss.
9511
+ lowerer.flushDeferredClass(receiverType.className);
9512
+ return null;
9513
+ }
9514
+ const fieldType = info.fields.get(fieldName);
9515
+ const lowerObjectReceiver = () => {
9516
+ let obj = saved ?? lowerer.lowerExpr(receiverNode);
9517
+ if (obj.type.kind === "union") {
9518
+ const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
9519
+ obj = helper
9520
+ ? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
9521
+ : lowerer.maybeNarrow(obj, receiverNode);
9522
+ }
9523
+ return obj.type.kind === "dyn" ? lowerer.coerceInto(receiverNode, obj, receiverType) : obj;
9524
+ };
9525
+ if (fieldName === "cause" && receiverType.className !== "%DOMException") {
9526
+ let root = info;
9527
+ while (root.base)
9528
+ root = root.base;
9529
+ if (root.def.name === "%Error") {
9530
+ return { container: "errorCause", obj: lowerObjectReceiver(), field: "cause", fieldType: DYN };
9531
+ }
9532
+ }
9533
+ if (fieldType) {
9534
+ const obj = lowerObjectReceiver();
9535
+ return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
9536
+ }
9537
+ // Accessor property: either half declared anywhere on the chain
9538
+ // makes the name an accessor target (fields and accessors share a
9539
+ // namespace — tsc rejects mixing them, so the halves agree on kind).
9540
+ const getF = lowerer.findMethodOn(info, `get:${fieldName}`);
9541
+ const setF = lowerer.findMethodOn(info, `set:${fieldName}`);
9542
+ if (getF || setF) {
9543
+ const obj = lowerObjectReceiver();
9544
+ return {
9545
+ container: "accessor",
9546
+ obj,
9547
+ className: receiverType.className,
9548
+ field: fieldName,
9549
+ fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
9550
+ };
9551
+ }
9552
+ return null;
9553
+ }
9554
+ /** A structural assertion can leave the value in its original class
9555
+ * representation, including an unchecked array read's undefined arm. Only
9556
+ * a single nominal payload has an unambiguous property layout. Validate
9557
+ * that payload before dispatching a field or accessor; an absent receiver
9558
+ * must never reach the backend as an object pointer. */
9559
+ function hasClassPayload(lowerer, type) {
9560
+ return type.kind === "object" || type.kind === "union" &&
9561
+ (lowerer.unions.get(type.unionId)?.arms.some((arm) => arm.kind === "object") ?? false);
9562
+ }
9563
+ function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
9564
+ const arms = value.type.kind === "union"
9565
+ ? lowerer.unions.get(value.type.unionId)?.arms.filter((arm) => !isUnitType(arm))
9566
+ : [value.type];
9567
+ const objectType = arms?.length === 1 ? arms[0] : null;
9568
+ return objectType?.kind === "object"
9569
+ ? classFieldTarget(lowerer, receiverNode, objectType, fieldName, value)
9570
+ : null;
9571
+ }
8672
9572
  /** Recognizes `obj.field` as an assignable field target: receiver is a
8673
9573
  * known class instance OR a record, and the member is a field or (class
8674
9574
  * receivers) a declared accessor property. Returns the pieces of a
@@ -8676,55 +9576,26 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
8676
9576
  export function fieldTarget(lowerer, access) {
8677
9577
  if (lowerer.chainBlocked(access))
8678
9578
  return null;
8679
- const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
9579
+ if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
9580
+ return { container: "errorStackLimit", obj: { kind: "numLit", value: 0, type: F64, loc: locOf(access) }, field: "stackTraceLimit", fieldType: F64 };
9581
+ }
9582
+ const stored = ts.isIdentifier(access.expression)
9583
+ ? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
9584
+ const receiverIr = stored?.kind === "record" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
8680
9585
  if (receiverIr?.kind === "object") {
8681
- const info = lowerer.classes.get(receiverIr.className);
8682
- if (!info) {
8683
- // A receiver typed as a class whose collection deferred: the
8684
- // deferred diagnostics are what explains the miss.
8685
- lowerer.flushDeferredClass(receiverIr.className);
8686
- return null;
8687
- }
8688
- const fieldType = info.fields.get(access.name.text);
8689
- const lowerObjectReceiver = () => {
8690
- let obj = lowerer.lowerExpr(access.expression);
8691
- if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
8692
- const present = lowerer.stripUndefinedArm(obj.type);
8693
- const helper = present.kind === "object"
8694
- ? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(access.expression))
8695
- : null;
8696
- obj = helper
8697
- ? { kind: "call", callee: helper, args: [obj], type: present, loc: locOf(access.expression) }
8698
- : lowerer.maybeNarrow(obj, access.expression);
8699
- }
8700
- return obj;
8701
- };
8702
- if (fieldType) {
8703
- const obj = lowerObjectReceiver();
8704
- return { container: "class", obj, className: receiverIr.className, field: access.name.text, fieldType };
8705
- }
8706
- // Accessor property: either half declared anywhere on the chain
8707
- // makes the name an accessor target (fields and accessors share a
8708
- // namespace — tsc rejects mixing them, so the halves agree on kind).
8709
- const getF = lowerer.findMethodOn(info, `get:${access.name.text}`);
8710
- const setF = lowerer.findMethodOn(info, `set:${access.name.text}`);
8711
- if (getF || setF) {
8712
- const obj = lowerObjectReceiver();
8713
- return {
8714
- container: "accessor",
8715
- obj,
8716
- className: receiverIr.className,
8717
- field: access.name.text,
8718
- fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
8719
- };
8720
- }
8721
- return null;
9586
+ return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
9587
+ }
9588
+ if (receiverIr?.kind === "union" && hasClassPayload(lowerer, receiverIr)) {
9589
+ return representedClassFieldTarget(lowerer, access.expression, access.name.text, lowerer.lowerExpr(access.expression));
8722
9590
  }
8723
9591
  if (receiverIr?.kind === "record") {
8724
9592
  const shape = lowerer.shapes.get(receiverIr.shapeId);
8725
9593
  const fieldType = shape?.fields.find((f) => f.name === access.name.text)?.type;
8726
9594
  if (fieldType) {
8727
9595
  let obj = lowerer.lowerExpr(access.expression);
9596
+ const nominal = representedClassFieldTarget(lowerer, access.expression, access.name.text, obj);
9597
+ if (nominal)
9598
+ return nominal;
8728
9599
  // A checker-record receiver whose VALUE stayed dyn (the erased
8729
9600
  // all-unknown-fields cast — `(err as { code?: unknown }).code`):
8730
9601
  // decline, and the dyn keyed-read fallback answers.
@@ -8858,7 +9729,7 @@ export function uniqueSymbolKeyOf(lowerer, key) {
8858
9729
  // Module level only: a unique-symbol const inside a FUNCTION is a
8859
9730
  // fresh runtime identity per call — one static slot would conflate
8860
9731
  // what JS keeps distinct.
8861
- if (!ts.isVariableStatement(decl.parent.parent) || !ts.isSourceFile(decl.parent.parent.parent))
9732
+ if (!ts.isVariableStatement(decl.parent?.parent) || !ts.isSourceFile(decl.parent?.parent?.parent))
8862
9733
  return null;
8863
9734
  const init = decl.initializer;
8864
9735
  if (!init || !ts.isCallExpression(init) || init.questionDotToken)
@@ -8891,10 +9762,10 @@ export function symbolFieldInfo(lowerer, expr) {
8891
9762
  lowerer.flushDeferredClass(receiverIr.className);
8892
9763
  return null;
8893
9764
  }
8894
- const key = uniqueSymbolKeyOf(lowerer, expr.argumentExpression);
9765
+ const key = classSymbolKeyOf(lowerer, expr.argumentExpression);
8895
9766
  if (!key)
8896
9767
  return null;
8897
- const field = info.symbolFields?.get(key.sym);
9768
+ const field = info.symbolFields?.get(key.identity);
8898
9769
  if (field === undefined)
8899
9770
  return null;
8900
9771
  const fieldType = info.fields.get(field);
@@ -8916,6 +9787,12 @@ function symbolFieldTarget(lowerer, expr) {
8916
9787
  * tsc-clean (the property types as the setter's param), but Node yields
8917
9788
  * undefined, which these property types cannot represent. */
8918
9789
  export function fieldGetExpr(lowerer, target, loc, blame) {
9790
+ if (target.container === "errorStackLimit") {
9791
+ return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
9792
+ }
9793
+ if (target.container === "errorCause") {
9794
+ return { kind: "libCall", fn: "error.cause", args: [target.obj], type: DYN, loc };
9795
+ }
8919
9796
  // A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
8920
9797
  // (a JS file-scope object-literal global): the checked-dynamic keyed
8921
9798
  // read — dynKeyGet (a missing key answers the dyn undefined, exactly
@@ -9012,6 +9889,12 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
9012
9889
  * call). A write to a getter-only property never gets here in a clean
9013
9890
  * program (tsc's TS2540 is the fence); the rejection is the backstop. */
9014
9891
  export function fieldSetStmt(lowerer, target, value, loc, blame) {
9892
+ if (target.container === "errorStackLimit") {
9893
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
9894
+ }
9895
+ if (target.container === "errorCause") {
9896
+ return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.setCause", args: [target.obj, value], type: VOID, loc }, loc };
9897
+ }
9015
9898
  // A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
9016
9899
  // the checked-dynamic keyed write — dyn.keySet (later writes win,
9017
9900
  // insertion order; Node's TypeErrors on non-object receivers), the
@@ -9180,61 +10063,73 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
9180
10063
  }
9181
10064
  /** `obj.f op= e` (and `obj.f++` with rhs null ≡ 1) — the element spelling
9182
10065
  * `obj[k] op= e` included when k is a declared symbol-keyed field.
9183
- * Restricted to side-effect-free receivers (identifier or `this`)
9184
- * because the desugar evaluates the receiver twice. */
10066
+ * Save the receiver and old value before the RHS, then write through
10067
+ * that same receiver even if the RHS replaces its original binding. */
9185
10068
  export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
10069
+ return { kind: "exprStmt", expr: lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc), loc };
10070
+ }
10071
+ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
9186
10072
  if (access.expression.kind === ts.SyntaxKind.SuperKeyword) {
9187
10073
  lowerer.unsupported("SC1090", access, "compound assignment through 'super' (read and write separately)");
9188
10074
  }
9189
- if (!ts.isIdentifier(access.expression) && access.expression.kind !== ts.SyntaxKind.ThisKeyword) {
9190
- lowerer.unsupported("SC1090", access, "compound assignment to fields of computed receivers");
9191
- }
10075
+ const body = [];
10076
+ const save = (value, name) => {
10077
+ const local = lowerer.declareHiddenLocal(name, value.type);
10078
+ body.push({ kind: "varDecl", localId: local.id, init: value, loc: value.loc });
10079
+ return varRef(local.id, value.type, value.loc);
10080
+ };
9192
10081
  // A CHECKED-DYNAMIC receiver (`context.actual++` — test/common's call
9193
10082
  // accounting; dot spelling only — symbol-keyed element targets are
9194
10083
  // static fields): read the member (dynKeyGet), VALIDATE it as a
9195
10084
  // number (dynCheck — a non-number member throws the catchable
9196
10085
  // TypeError where JS would ToNumber-coerce; loud, never a silent
9197
10086
  // NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
9198
- // receiver is an identifier (checked above), so evaluating it for
9199
- // read and write matches JS's once-evaluation observably.
10087
+ // receiver and member read are saved before evaluating the RHS.
9200
10088
  if (ts.isPropertyAccessExpression(access)) {
9201
10089
  const probed = tryLowerExpression(lowerer, access.expression);
9202
10090
  if (probed?.type.kind === "dyn") {
10091
+ const receiver = save(probed, "%compoundReceiver");
9203
10092
  const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
9204
- const read = { kind: "dynKeyGet", key, value: probed, type: DYN, loc };
10093
+ const read = save({ kind: "dynKeyGet", key, value: receiver, type: DYN, loc }, "%compoundOld");
9205
10094
  const cur = { kind: "dynCheck", value: read, type: F64, loc };
9206
- const rhs = rhsNode
10095
+ const rhs = save(rhsNode
9207
10096
  ? lowerer.lowerExpr(rhsNode)
9208
- : { kind: "numLit", value: 1, type: F64, loc };
9209
- if (rhs.type.kind !== "f64")
10097
+ : { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
10098
+ const numericRhs = rhs.type.kind === "dyn"
10099
+ ? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
10100
+ : lowerOptionalNumber(lowerer, rhs, loc);
10101
+ if (numericRhs.type.kind !== "f64")
9210
10102
  lowerer.unsupported("SC1043", access);
9211
- const value = { kind: "bin", op, left: cur, right: rhs, type: F64, loc };
9212
- const recv2 = lowerer.lowerExpr(access.expression);
10103
+ const value = save({ kind: "bin", op, left: cur, right: numericRhs, type: F64, loc }, "%compoundResult");
9213
10104
  const boxed = { kind: "dynFrom", value, type: DYN, loc };
9214
- return {
10105
+ body.push({
9215
10106
  kind: "exprStmt",
9216
- expr: { kind: "libCall", fn: "dyn.keySet", args: [recv2, { ...key }, boxed], type: VOID, loc },
10107
+ expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, { ...key }, boxed], type: VOID, loc },
9217
10108
  loc,
9218
- };
10109
+ });
10110
+ return { kind: "seqExpr", stmts: body, result: value, type: value.type, loc };
9219
10111
  }
9220
10112
  }
9221
10113
  const targetOf = () => ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
9222
10114
  const target = targetOf();
9223
10115
  if (!target)
9224
10116
  lowerer.unsupported("SC1090", access, "compound assignment to unsupported field targets");
9225
- // Through an accessor target this desugars to get, op, set — with the
9226
- // receiver an identifier/this, the observable order matches JS exactly:
9227
- // getter, rhs side effects, setter (verified against Node).
9228
- const read = lowerer.fieldGetExpr(target, locOf(access), access);
9229
- const rhs = rhsNode
10117
+ target.obj = save(target.obj, "%compoundReceiver");
10118
+ // Accessors observe getter, RHS side effects, then setter, all through
10119
+ // the saved receiver.
10120
+ const read = save(lowerer.fieldGetExpr(target, locOf(access), access), "%compoundOld");
10121
+ const rhs = save(rhsNode
9230
10122
  ? lowerer.lowerExpr(rhsNode)
9231
- : { kind: "numLit", value: 1, type: F64, loc };
10123
+ : { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
10124
+ const numericRhs = rhs.type.kind === "dyn" && isJsSourceFile(access.getSourceFile())
10125
+ ? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
10126
+ : lowerOptionalNumber(lowerer, rhs, loc);
9232
10127
  let value;
9233
10128
  if (op === "+" && target.fieldType.kind === "string") {
9234
10129
  value = { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, rhsNode ?? access), type: STRING, loc };
9235
10130
  }
9236
- else if (target.fieldType.kind === "f64" && rhs.type.kind === "f64") {
9237
- value = { kind: "bin", op, left: read, right: rhs, type: F64, loc };
10131
+ else if (target.fieldType.kind === "f64" && numericRhs.type.kind === "f64") {
10132
+ value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
9238
10133
  }
9239
10134
  else if (target.fieldType.kind === "dyn" &&
9240
10135
  isJsSourceFile(access.getSourceFile()) &&
@@ -9270,9 +10165,9 @@ export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
9270
10165
  else {
9271
10166
  lowerer.unsupported("SC1043", access);
9272
10167
  }
9273
- // Second, independent evaluation of the (side-effect-free) receiver.
9274
- const reevaluatedTarget = targetOf();
9275
- return lowerer.fieldSetStmt(reevaluatedTarget, value, loc, access);
10168
+ const result = save(value, "%compoundResult");
10169
+ body.push(lowerer.fieldSetStmt(target, result, loc, access));
10170
+ return { kind: "seqExpr", stmts: body, result, type: result.type, loc };
9276
10171
  }
9277
10172
  /** Stream-rooted receivers' property surface (readableEnded, destroyed,
9278
10173
  * ...): the receiver's mapped class dispatches into lower-stream.ts.