@scriptc/compiler 0.2.10 → 0.3.1

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 (584) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +7 -38
  2. package/ambient/scriptc-overrides.d.ts +1 -25
  3. package/ambient/scriptc.d.ts +1 -9
  4. package/dist/backend/cycle-analysis.test.js +1 -1
  5. package/dist/backend/cycle-mutability.d.ts +1 -1
  6. package/dist/backend/cycle-mutability.js +1 -18
  7. package/dist/backend/cycle-mutability.js.map +1 -1
  8. package/dist/backend/executable-features.d.ts +1 -1
  9. package/dist/backend/executable-features.js +3 -8
  10. package/dist/backend/executable-features.js.map +1 -1
  11. package/dist/backend/immutability.d.ts +1 -1
  12. package/dist/backend/immutability.js +1 -1
  13. package/dist/backend/immutability.test.js +1 -1
  14. package/dist/backend/library-pack.js +0 -2
  15. package/dist/backend/library-pack.js.map +1 -1
  16. package/dist/backend/linker.js +1 -1
  17. package/dist/backend/linker.js.map +1 -1
  18. package/dist/backend/llvm/bigint-wrap.d.ts +4 -0
  19. package/dist/backend/llvm/bigint-wrap.js +590 -0
  20. package/dist/backend/llvm/bigint-wrap.js.map +1 -0
  21. package/dist/backend/llvm/blocks.d.ts +2 -0
  22. package/dist/backend/llvm/blocks.js +4 -0
  23. package/dist/backend/llvm/blocks.js.map +1 -1
  24. package/dist/backend/llvm/call-lifetimes.js +15 -4
  25. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  26. package/dist/backend/llvm/call-lifetimes.test.js +14 -3
  27. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  28. package/dist/backend/llvm/checked-narrows.test.js +2 -2
  29. package/dist/backend/llvm/checked-narrows.test.js.map +1 -1
  30. package/dist/backend/llvm/classes.js +60 -21
  31. package/dist/backend/llvm/classes.js.map +1 -1
  32. package/dist/backend/llvm/classes.test.js +11 -11
  33. package/dist/backend/llvm/classes.test.js.map +1 -1
  34. package/dist/backend/llvm/common.d.ts +7 -5
  35. package/dist/backend/llvm/common.js +7 -5
  36. package/dist/backend/llvm/common.js.map +1 -1
  37. package/dist/backend/llvm/constant-callbacks.d.ts +1 -1
  38. package/dist/backend/llvm/constant-callbacks.js +13 -31
  39. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  40. package/dist/backend/llvm/constant-callbacks.test.js +1 -1
  41. package/dist/backend/llvm/dense-array-access.js +133 -45
  42. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  43. package/dist/backend/llvm/dense-array-access.test.js +54 -2
  44. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  45. package/dist/backend/llvm/dyn.d.ts +2 -1
  46. package/dist/backend/llvm/dyn.js +4 -73
  47. package/dist/backend/llvm/dyn.js.map +1 -1
  48. package/dist/backend/llvm/emitter.d.ts +123 -23
  49. package/dist/backend/llvm/emitter.js +729 -258
  50. package/dist/backend/llvm/emitter.js.map +1 -1
  51. package/dist/backend/llvm/emitter.test.js +17 -10
  52. package/dist/backend/llvm/emitter.test.js.map +1 -1
  53. package/dist/backend/llvm/expr-async.js +120 -43
  54. package/dist/backend/llvm/expr-async.js.map +1 -1
  55. package/dist/backend/llvm/expr-async.test.js +2 -2
  56. package/dist/backend/llvm/expr-async.test.js.map +1 -1
  57. package/dist/backend/llvm/expr-bytes.js +51 -36
  58. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  59. package/dist/backend/llvm/expr-callbacks.js +72 -3
  60. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  61. package/dist/backend/llvm/expr-calls.d.ts +1 -1
  62. package/dist/backend/llvm/expr-calls.js +159 -30
  63. package/dist/backend/llvm/expr-calls.js.map +1 -1
  64. package/dist/backend/llvm/expr-calls.test.js +132 -0
  65. package/dist/backend/llvm/expr-calls.test.js.map +1 -0
  66. package/dist/backend/llvm/expr-containers.js +25 -8
  67. package/dist/backend/llvm/expr-containers.js.map +1 -1
  68. package/dist/backend/llvm/expr-context.d.ts +2 -0
  69. package/dist/backend/llvm/expr-control.js +4 -107
  70. package/dist/backend/llvm/expr-control.js.map +1 -1
  71. package/dist/backend/llvm/expr-dispatch.d.ts +1 -2
  72. package/dist/backend/llvm/expr-dispatch.js +3 -41
  73. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  74. package/dist/backend/llvm/expr-dynamic.d.ts +1 -1
  75. package/dist/backend/llvm/expr-dynamic.js +39 -31
  76. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  77. package/dist/backend/llvm/expr-primitives.d.ts +7 -0
  78. package/dist/backend/llvm/expr-primitives.js +80 -22
  79. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  80. package/dist/backend/llvm/expr-primitives.test.js +97 -7
  81. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  82. package/dist/backend/llvm/expr-records.d.ts +9 -0
  83. package/dist/backend/llvm/expr-records.js +134 -16
  84. package/dist/backend/llvm/expr-records.js.map +1 -1
  85. package/dist/backend/llvm/expr-stream-bridges.d.ts +1 -1
  86. package/dist/backend/llvm/expr-stream-bridges.js +80 -1
  87. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  88. package/dist/backend/llvm/expr-stream-bridges.test.js +51 -2
  89. package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -1
  90. package/dist/backend/llvm/int32-abi.d.ts +31 -0
  91. package/dist/backend/llvm/int32-abi.js +62 -0
  92. package/dist/backend/llvm/int32-abi.js.map +1 -0
  93. package/dist/backend/llvm/int32-abi.test.js +107 -0
  94. package/dist/backend/llvm/int32-abi.test.js.map +1 -0
  95. package/dist/backend/llvm/int32-locals.d.ts +30 -0
  96. package/dist/backend/llvm/int32-locals.js +95 -0
  97. package/dist/backend/llvm/int32-locals.js.map +1 -0
  98. package/dist/backend/llvm/integer-values.d.ts +4 -0
  99. package/dist/backend/llvm/integer-values.js +30 -1
  100. package/dist/backend/llvm/integer-values.js.map +1 -1
  101. package/dist/backend/llvm/integer-views.d.ts +8 -1
  102. package/dist/backend/llvm/integer-views.js +47 -2
  103. package/dist/backend/llvm/integer-views.js.map +1 -1
  104. package/dist/backend/llvm/lazy-captures.d.ts +16 -0
  105. package/dist/backend/llvm/lazy-captures.js +88 -2
  106. package/dist/backend/llvm/lazy-captures.js.map +1 -1
  107. package/dist/backend/llvm/lazy-captures.test.js +48 -0
  108. package/dist/backend/llvm/lazy-captures.test.js.map +1 -1
  109. package/dist/backend/llvm/lib-dispatch.js +14 -1
  110. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  111. package/dist/backend/llvm/lib-filesystem.js +3 -13
  112. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  113. package/dist/backend/llvm/lib-shared.js +7 -8
  114. package/dist/backend/llvm/lib-shared.js.map +1 -1
  115. package/dist/backend/llvm/local-array-reads.js +21 -2
  116. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  117. package/dist/backend/llvm/local-array-reads.test.js +4 -3
  118. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  119. package/dist/backend/llvm/local-union-storage.test.js +3 -3
  120. package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
  121. package/dist/backend/llvm/map-number-lookup.d.ts +7 -0
  122. package/dist/backend/llvm/map-number-lookup.js +16 -2
  123. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  124. package/dist/backend/llvm/map-read-lifetimes.js +7 -1
  125. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  126. package/dist/backend/llvm/map-read-lifetimes.test.js +25 -8
  127. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  128. package/dist/backend/llvm/reference-effects.js +2 -0
  129. package/dist/backend/llvm/reference-effects.js.map +1 -1
  130. package/dist/backend/llvm/reference-writes.d.ts +3 -0
  131. package/dist/backend/llvm/reference-writes.js +10 -2
  132. package/dist/backend/llvm/reference-writes.js.map +1 -1
  133. package/dist/backend/llvm/regex-matchers.d.ts +39 -0
  134. package/dist/backend/llvm/regex-matchers.js +1649 -0
  135. package/dist/backend/llvm/regex-matchers.js.map +1 -0
  136. package/dist/backend/llvm/regex-matchers.test.js +96 -0
  137. package/dist/backend/llvm/regex-matchers.test.js.map +1 -0
  138. package/dist/backend/llvm/shapes.d.ts +36 -1
  139. package/dist/backend/llvm/shapes.js +128 -16
  140. package/dist/backend/llvm/shapes.js.map +1 -1
  141. package/dist/backend/llvm/shapes.test.js +3 -3
  142. package/dist/backend/llvm/stack-unions.test.js +2 -2
  143. package/dist/backend/llvm/stack-unions.test.js.map +1 -1
  144. package/dist/backend/llvm/string-construction.test.js +3 -3
  145. package/dist/backend/llvm/string-construction.test.js.map +1 -1
  146. package/dist/backend/llvm/string-lifetimes.test.js +6 -6
  147. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  148. package/dist/backend/llvm/string-literal-equality.d.ts +22 -0
  149. package/dist/backend/llvm/string-literal-equality.js +78 -0
  150. package/dist/backend/llvm/string-literal-equality.js.map +1 -0
  151. package/dist/backend/llvm/switch-dispatch.js +59 -13
  152. package/dist/backend/llvm/switch-dispatch.js.map +1 -1
  153. package/dist/backend/llvm/switch-dispatch.test.js +72 -7
  154. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  155. package/dist/backend/llvm/union-retype.test.d.ts +1 -0
  156. package/dist/backend/llvm/union-retype.test.js +229 -0
  157. package/dist/backend/llvm/union-retype.test.js.map +1 -0
  158. package/dist/backend/llvm/wrapping-sums.d.ts +17 -0
  159. package/dist/backend/llvm/wrapping-sums.js +113 -0
  160. package/dist/backend/llvm/wrapping-sums.js.map +1 -0
  161. package/dist/backend/loop-polls.js +1 -0
  162. package/dist/backend/loop-polls.js.map +1 -1
  163. package/dist/backend/mangle.d.ts +9 -0
  164. package/dist/backend/mangle.js +15 -0
  165. package/dist/backend/mangle.js.map +1 -1
  166. package/dist/backend/may-throw.js +6 -12
  167. package/dist/backend/may-throw.js.map +1 -1
  168. package/dist/backend/may-throw.test.js +1 -1
  169. package/dist/backend/native/artifact-stamps.js +0 -2
  170. package/dist/backend/native/artifact-stamps.js.map +1 -1
  171. package/dist/backend/native/cache-warm.d.ts +1 -1
  172. package/dist/backend/native/cache-warm.js +2 -3
  173. package/dist/backend/native/cache-warm.js.map +1 -1
  174. package/dist/backend/native/compiler-fingerprint.d.ts +1 -1
  175. package/dist/backend/native/compiler-fingerprint.js +4 -7
  176. package/dist/backend/native/compiler-fingerprint.js.map +1 -1
  177. package/dist/backend/native/contracts.d.ts +3 -28
  178. package/dist/backend/native/driver.js +1 -3
  179. package/dist/backend/native/driver.js.map +1 -1
  180. package/dist/backend/native/executable.d.ts +0 -15
  181. package/dist/backend/native/executable.js +17 -172
  182. package/dist/backend/native/executable.js.map +1 -1
  183. package/dist/backend/native/tool-identity.js +0 -1
  184. package/dist/backend/native/tool-identity.js.map +1 -1
  185. package/dist/backend/native/vendor.d.ts +1 -1
  186. package/dist/backend/native/vendor.js +1 -1
  187. package/dist/backend/native/vendor.js.map +1 -1
  188. package/dist/backend/native-link-info-core.d.ts +0 -2
  189. package/dist/backend/native-link-info-core.js.map +1 -1
  190. package/dist/backend/native-toolchain.test.js +23 -33
  191. package/dist/backend/native-toolchain.test.js.map +1 -1
  192. package/dist/backend/publication.test.js +1 -1
  193. package/dist/backend/runtime-abi.d.ts +2 -2
  194. package/dist/backend/runtime-abi.js +2 -2
  195. package/dist/backend/runtime-pack-core.d.ts +1 -2
  196. package/dist/backend/runtime-pack-core.js +5 -14
  197. package/dist/backend/runtime-pack-core.js.map +1 -1
  198. package/dist/backend/runtime-pack-targets.test.js +3 -8
  199. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  200. package/dist/backend/runtime-pack.test.js +22 -19
  201. package/dist/backend/runtime-pack.test.js.map +1 -1
  202. package/dist/backend/target-diagnostics.d.ts +0 -5
  203. package/dist/backend/target-diagnostics.js +1 -11
  204. package/dist/backend/target-diagnostics.js.map +1 -1
  205. package/dist/backend/target-diagnostics.test.js +1 -1
  206. package/dist/backend/vendor-archives.d.ts +1 -7
  207. package/dist/backend/vendor-archives.js +9 -189
  208. package/dist/backend/vendor-archives.js.map +1 -1
  209. package/dist/backend/vendor-inputs.d.ts +0 -1
  210. package/dist/backend/vendor-inputs.js +0 -1
  211. package/dist/backend/vendor-inputs.js.map +1 -1
  212. package/dist/backend/warm-cache.js +3 -5
  213. package/dist/backend/warm-cache.js.map +1 -1
  214. package/dist/cli/command.js +4 -8
  215. package/dist/cli/command.js.map +1 -1
  216. package/dist/cli/host.d.ts +1 -1
  217. package/dist/cli/usage.d.ts +1 -5
  218. package/dist/cli/usage.js +4 -9
  219. package/dist/cli/usage.js.map +1 -1
  220. package/dist/compat/fetch-profile.d.ts +2 -2
  221. package/dist/compat/fetch-profile.js +16 -25
  222. package/dist/compat/fetch-profile.js.map +1 -1
  223. package/dist/compile-types.d.ts +2 -17
  224. package/dist/contracts/check.js +6 -6
  225. package/dist/contracts/check.js.map +1 -1
  226. package/dist/coverage/report.d.ts +5 -22
  227. package/dist/coverage/report.js +12 -76
  228. package/dist/coverage/report.js.map +1 -1
  229. package/dist/coverage/surface-manifest.d.ts +3 -3
  230. package/dist/coverage/surface-manifest.js +22 -27
  231. package/dist/coverage/surface-manifest.js.map +1 -1
  232. package/dist/coverage/verdict-cache.d.ts +1 -3
  233. package/dist/coverage/verdict-cache.js +2 -7
  234. package/dist/coverage/verdict-cache.js.map +1 -1
  235. package/dist/diagnostics/diagnostic.d.ts +11 -60
  236. package/dist/diagnostics/diagnostic.js +39 -118
  237. package/dist/diagnostics/diagnostic.js.map +1 -1
  238. package/dist/diagnostics/envelope.d.ts +3 -4
  239. package/dist/diagnostics/envelope.js +0 -9
  240. package/dist/diagnostics/envelope.js.map +1 -1
  241. package/dist/diagnostics/envelope.test.js +1 -1
  242. package/dist/diagnostics/envelope.test.js.map +1 -1
  243. package/dist/executable/executable-cache.d.ts +0 -3
  244. package/dist/executable/executable-cache.js +2 -4
  245. package/dist/executable/executable-cache.js.map +1 -1
  246. package/dist/executable/executable-cache.test.js +0 -4
  247. package/dist/executable/executable-cache.test.js.map +1 -1
  248. package/dist/executable/prepare.js +5 -2
  249. package/dist/executable/prepare.js.map +1 -1
  250. package/dist/frontend/analysis.js +10 -10
  251. package/dist/frontend/analysis.js.map +1 -1
  252. package/dist/frontend/cjs-syntax.js +0 -88
  253. package/dist/frontend/cjs-syntax.js.map +1 -1
  254. package/dist/frontend/dts-paths.d.ts +1 -1
  255. package/dist/frontend/dts-paths.js +1 -1
  256. package/dist/frontend/lowering/abstract-equality.js +1 -4
  257. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  258. package/dist/frontend/lowering/array-values.d.ts +14 -0
  259. package/dist/frontend/lowering/array-values.js +33 -11
  260. package/dist/frontend/lowering/array-values.js.map +1 -1
  261. package/dist/frontend/lowering/builtins/arguments.js +0 -13
  262. package/dist/frontend/lowering/builtins/arguments.js.map +1 -1
  263. package/dist/frontend/lowering/builtins/crypto.d.ts +0 -4
  264. package/dist/frontend/lowering/builtins/crypto.js +0 -4
  265. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  266. package/dist/frontend/lowering/builtins/json.js +1 -39
  267. package/dist/frontend/lowering/builtins/json.js.map +1 -1
  268. package/dist/frontend/lowering/builtins/module-resolution.d.ts +0 -15
  269. package/dist/frontend/lowering/builtins/module-resolution.js +5 -44
  270. package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -1
  271. package/dist/frontend/lowering/builtins/numbers.d.ts +0 -6
  272. package/dist/frontend/lowering/builtins/numbers.js +6 -57
  273. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  274. package/dist/frontend/lowering/builtins/promises.js +4 -67
  275. package/dist/frontend/lowering/builtins/promises.js.map +1 -1
  276. package/dist/frontend/lowering/builtins/workers.js +1 -1
  277. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  278. package/dist/frontend/lowering/call-arguments.d.ts +1 -37
  279. package/dist/frontend/lowering/call-arguments.js +11 -94
  280. package/dist/frontend/lowering/call-arguments.js.map +1 -1
  281. package/dist/frontend/lowering/call-signatures.d.ts +1 -6
  282. package/dist/frontend/lowering/call-signatures.js +16 -68
  283. package/dist/frontend/lowering/call-signatures.js.map +1 -1
  284. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +19 -4
  285. package/dist/frontend/lowering/class-dynamic-dispatch.js +168 -98
  286. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  287. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +44 -4
  288. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  289. package/dist/frontend/lowering/class-method-mutations.js +0 -2
  290. package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
  291. package/dist/frontend/lowering/class-protocols.js +1 -1
  292. package/dist/frontend/lowering/class-protocols.js.map +1 -1
  293. package/dist/frontend/lowering/coercions/function-adapters.js +5 -35
  294. package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
  295. package/dist/frontend/lowering/coercions/state.d.ts +0 -1
  296. package/dist/frontend/lowering/coercions/state.js +0 -1
  297. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  298. package/dist/frontend/lowering/coercions/structural-plans.js +2 -4
  299. package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
  300. package/dist/frontend/lowering/coercions/structural.js +2 -12
  301. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  302. package/dist/frontend/lowering/coercions/unions.js +0 -1
  303. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  304. package/dist/frontend/lowering/containers/array-callbacks.js +49 -28
  305. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  306. package/dist/frontend/lowering/containers/array-construction.js +2 -3
  307. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  308. package/dist/frontend/lowering/containers/array-methods.js +10 -54
  309. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  310. package/dist/frontend/lowering/containers/array-pipelines.d.ts +54 -0
  311. package/dist/frontend/lowering/containers/array-pipelines.js +1827 -0
  312. package/dist/frontend/lowering/containers/array-pipelines.js.map +1 -0
  313. package/dist/frontend/lowering/containers/bytes.js +0 -1
  314. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  315. package/dist/frontend/lowering/containers/callback-arguments.js +2 -10
  316. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  317. package/dist/frontend/lowering/containers/collection-methods.js +1 -6
  318. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  319. package/dist/frontend/lowering/containers/exec-result-uses.d.ts +10 -0
  320. package/dist/frontend/lowering/containers/exec-result-uses.js +88 -0
  321. package/dist/frontend/lowering/containers/exec-result-uses.js.map +1 -0
  322. package/dist/frontend/lowering/containers/indexed-objects.js +0 -2
  323. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  324. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +0 -6
  325. package/dist/frontend/lowering/containers/string-and-regexp.js +17 -17
  326. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  327. package/dist/frontend/lowering/containers/string-replacement.d.ts +2 -1
  328. package/dist/frontend/lowering/containers/string-replacement.js +32 -136
  329. package/dist/frontend/lowering/containers/string-replacement.js.map +1 -1
  330. package/dist/frontend/lowering/devirtualize.d.ts +33 -0
  331. package/dist/frontend/lowering/devirtualize.js +199 -0
  332. package/dist/frontend/lowering/devirtualize.js.map +1 -0
  333. package/dist/frontend/lowering/expressions/evaluation-safety.js +0 -3
  334. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
  335. package/dist/frontend/lowering/expressions/native-proxy.js +1 -1
  336. package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
  337. package/dist/frontend/lowering/expressions/nullish-assignment.js +55 -8
  338. package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
  339. package/dist/frontend/lowering/expressions/object-literals.d.ts +0 -8
  340. package/dist/frontend/lowering/expressions/object-literals.js +8 -35
  341. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  342. package/dist/frontend/lowering/expressions/optional-chains.d.ts +0 -4
  343. package/dist/frontend/lowering/expressions/optional-chains.js +3 -66
  344. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  345. package/dist/frontend/lowering/function-context.js +0 -1
  346. package/dist/frontend/lowering/function-context.js.map +1 -1
  347. package/dist/frontend/lowering/generic-functions.js +0 -1
  348. package/dist/frontend/lowering/generic-functions.js.map +1 -1
  349. package/dist/frontend/lowering/identifier-uses.d.ts +7 -0
  350. package/dist/frontend/lowering/identifier-uses.js +23 -0
  351. package/dist/frontend/lowering/identifier-uses.js.map +1 -0
  352. package/dist/frontend/lowering/iterator-adapters.js +51 -3
  353. package/dist/frontend/lowering/iterator-adapters.js.map +1 -1
  354. package/dist/frontend/lowering/lib-boundary.js +10 -77
  355. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  356. package/dist/frontend/lowering/lower-array-sort.js +14 -10
  357. package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
  358. package/dist/frontend/lowering/lower-array-sort.test.js +2 -2
  359. package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -1
  360. package/dist/frontend/lowering/lower-assert.js +2 -3
  361. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  362. package/dist/frontend/lowering/lower-async-iteration.js +1 -1
  363. package/dist/frontend/lowering/lower-calls.d.ts +2 -27
  364. package/dist/frontend/lowering/lower-calls.js +111 -493
  365. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  366. package/dist/frontend/lowering/lower-classes.js +12 -60
  367. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  368. package/dist/frontend/lowering/lower-comptime.js +2 -3
  369. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  370. package/dist/frontend/lowering/lower-console.d.ts +14 -0
  371. package/dist/frontend/lowering/lower-console.js +195 -0
  372. package/dist/frontend/lowering/lower-console.js.map +1 -0
  373. package/dist/frontend/lowering/lower-enums.js +5 -8
  374. package/dist/frontend/lowering/lower-enums.js.map +1 -1
  375. package/dist/frontend/lowering/lower-event-emitter.js +3 -4
  376. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  377. package/dist/frontend/lowering/lower-expando.js +3 -5
  378. package/dist/frontend/lowering/lower-expando.js.map +1 -1
  379. package/dist/frontend/lowering/lower-exprs.d.ts +0 -8
  380. package/dist/frontend/lowering/lower-exprs.js +180 -905
  381. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  382. package/dist/frontend/lowering/lower-functions.js +9 -8
  383. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  384. package/dist/frontend/lowering/lower-generators.d.ts +10 -0
  385. package/dist/frontend/lowering/lower-generators.js +108 -41
  386. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  387. package/dist/frontend/lowering/lower-inspect.js +1 -50
  388. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  389. package/dist/frontend/lowering/lower-modules.d.ts +0 -43
  390. package/dist/frontend/lowering/lower-modules.js +29 -329
  391. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  392. package/dist/frontend/lowering/lower-modules.test.js +5 -5
  393. package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
  394. package/dist/frontend/lowering/lower-server.js +0 -30
  395. package/dist/frontend/lowering/lower-server.js.map +1 -1
  396. package/dist/frontend/lowering/lower-stdlib.d.ts +98 -0
  397. package/dist/frontend/lowering/{lower-island.js → lower-stdlib.js} +97 -1160
  398. package/dist/frontend/lowering/lower-stdlib.js.map +1 -0
  399. package/dist/frontend/lowering/lower-stmts.d.ts +0 -16
  400. package/dist/frontend/lowering/lower-stmts.js +58 -1016
  401. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  402. package/dist/frontend/lowering/lower-stmts.test.js +1 -1
  403. package/dist/frontend/lowering/lower-stmts.test.js.map +1 -1
  404. package/dist/frontend/lowering/lower-stream.js +14 -13
  405. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  406. package/dist/frontend/lowering/lowerer.d.ts +42 -155
  407. package/dist/frontend/lowering/lowerer.js +192 -816
  408. package/dist/frontend/lowering/lowerer.js.map +1 -1
  409. package/dist/frontend/lowering/lowerer.test.js +4 -51
  410. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  411. package/dist/frontend/lowering/nan-coded-reads.d.ts +16 -0
  412. package/dist/frontend/lowering/nan-coded-reads.js +108 -1
  413. package/dist/frontend/lowering/nan-coded-reads.js.map +1 -1
  414. package/dist/frontend/lowering/narrowing-stability.js +1 -1
  415. package/dist/frontend/lowering/narrowing-stability.js.map +1 -1
  416. package/dist/frontend/lowering/optional-arguments.js +0 -2
  417. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  418. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +4 -0
  419. package/dist/frontend/lowering/runtime-optional-bounds.js +2 -0
  420. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
  421. package/dist/frontend/lowering/runtime-optional-bounds.test.js +27 -0
  422. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
  423. package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
  424. package/dist/frontend/lowering/split-consumers.js +2 -18
  425. package/dist/frontend/lowering/split-consumers.js.map +1 -1
  426. package/dist/frontend/lowering/strict-equality.js +1 -1
  427. package/dist/frontend/lowering/strict-equality.js.map +1 -1
  428. package/dist/frontend/lowering/surfaces.d.ts +6 -45
  429. package/dist/frontend/lowering/surfaces.js +8 -73
  430. package/dist/frontend/lowering/surfaces.js.map +1 -1
  431. package/dist/frontend/lowering/union-retag.d.ts +6 -1
  432. package/dist/frontend/lowering/union-retag.js +64 -1
  433. package/dist/frontend/lowering/union-retag.js.map +1 -1
  434. package/dist/frontend/lowering/union-retag.test.js +46 -0
  435. package/dist/frontend/lowering/union-retag.test.js.map +1 -1
  436. package/dist/frontend/module-syntax.d.ts +0 -3
  437. package/dist/frontend/module-syntax.js.map +1 -1
  438. package/dist/frontend/npm-node.d.ts +0 -7
  439. package/dist/frontend/npm-node.js +0 -7
  440. package/dist/frontend/npm-node.js.map +1 -1
  441. package/dist/frontend/npm-static-rewrite-syntax.d.ts +1 -1
  442. package/dist/frontend/npm-static-rewrite-syntax.js +6 -82
  443. package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -1
  444. package/dist/frontend/npm-static.d.ts +1 -1
  445. package/dist/frontend/npm-static.js +6 -71
  446. package/dist/frontend/npm-static.js.map +1 -1
  447. package/dist/frontend/npm.d.ts +3 -319
  448. package/dist/frontend/npm.js +6 -1007
  449. package/dist/frontend/npm.js.map +1 -1
  450. package/dist/frontend/npm.test.js +2 -197
  451. package/dist/frontend/npm.test.js.map +1 -1
  452. package/dist/frontend/pipeline-native.js +1 -2
  453. package/dist/frontend/pipeline-native.js.map +1 -1
  454. package/dist/frontend/pipeline.d.ts +3 -11
  455. package/dist/frontend/pipeline.js +37 -56
  456. package/dist/frontend/pipeline.js.map +1 -1
  457. package/dist/frontend/pipeline.test.js +12 -6
  458. package/dist/frontend/pipeline.test.js.map +1 -1
  459. package/dist/frontend/program.d.ts +0 -34
  460. package/dist/frontend/program.js +12 -97
  461. package/dist/frontend/program.js.map +1 -1
  462. package/dist/frontend/provenance-core.d.ts +0 -4
  463. package/dist/frontend/provenance-core.js +4 -37
  464. package/dist/frontend/provenance-core.js.map +1 -1
  465. package/dist/frontend/provenance-registry.d.ts +0 -4
  466. package/dist/frontend/provenance-registry.js +0 -20
  467. package/dist/frontend/provenance-registry.js.map +1 -1
  468. package/dist/frontend/resolve.d.ts +0 -12
  469. package/dist/frontend/resolve.js +0 -34
  470. package/dist/frontend/resolve.js.map +1 -1
  471. package/dist/frontend/runtime-resolve.js +2 -2
  472. package/dist/frontend/runtime-resolve.js.map +1 -1
  473. package/dist/frontend/services-node.js +2 -3
  474. package/dist/frontend/services-node.js.map +1 -1
  475. package/dist/frontend/services.d.ts +1 -8
  476. package/dist/frontend/services.js +2 -22
  477. package/dist/frontend/services.js.map +1 -1
  478. package/dist/frontend/services.test.js +0 -38
  479. package/dist/frontend/services.test.js.map +1 -1
  480. package/dist/frontend/tsc-codes.js +0 -11
  481. package/dist/frontend/tsc-codes.js.map +1 -1
  482. package/dist/frontend/type-mapper.d.ts +2 -27
  483. package/dist/frontend/type-mapper.js +58 -156
  484. package/dist/frontend/type-mapper.js.map +1 -1
  485. package/dist/index.d.ts +1 -1
  486. package/dist/index.js +7 -10
  487. package/dist/index.js.map +1 -1
  488. package/dist/ir/api.d.ts +1 -1
  489. package/dist/ir/api.js +1 -1
  490. package/dist/ir/api.js.map +1 -1
  491. package/dist/ir/builtin-effects.js +2 -5
  492. package/dist/ir/builtin-effects.js.map +1 -1
  493. package/dist/ir/builtin-signatures.d.ts +1 -1
  494. package/dist/ir/builtin-signatures.js +13 -56
  495. package/dist/ir/builtin-signatures.js.map +1 -1
  496. package/dist/ir/constant-globals.test.js +1 -1
  497. package/dist/ir/constant-tables.test.js +1 -1
  498. package/dist/ir/final-fields.test.js +1 -1
  499. package/dist/ir/function-references.d.ts +12 -0
  500. package/dist/ir/function-references.js +29 -0
  501. package/dist/ir/function-references.js.map +1 -0
  502. package/dist/ir/function-references.test.d.ts +1 -0
  503. package/dist/ir/function-references.test.js +276 -0
  504. package/dist/ir/function-references.test.js.map +1 -0
  505. package/dist/ir/initializer-bindings.test.js +1 -1
  506. package/dist/ir/int32-slots.d.ts +10 -7
  507. package/dist/ir/int32-slots.js +219 -68
  508. package/dist/ir/int32-slots.js.map +1 -1
  509. package/dist/ir/int32-slots.test.js +39 -2
  510. package/dist/ir/int32-slots.test.js.map +1 -1
  511. package/dist/ir/integer-ranges.d.ts +4 -4
  512. package/dist/ir/integer-ranges.js +89 -64
  513. package/dist/ir/integer-ranges.js.map +1 -1
  514. package/dist/ir/integer-ranges.test.js +71 -9
  515. package/dist/ir/integer-ranges.test.js.map +1 -1
  516. package/dist/ir/intrinsic-signatures.js +1 -0
  517. package/dist/ir/intrinsic-signatures.js.map +1 -1
  518. package/dist/ir/ir.d.ts +64 -290
  519. package/dist/ir/ir.js +11 -259
  520. package/dist/ir/ir.js.map +1 -1
  521. package/dist/ir/ir.test.js +2 -7
  522. package/dist/ir/ir.test.js.map +1 -1
  523. package/dist/ir/runtime-features.d.ts +3 -30
  524. package/dist/ir/runtime-features.js +8 -43
  525. package/dist/ir/runtime-features.js.map +1 -1
  526. package/dist/ir/scalar-records.test.js +1 -1
  527. package/dist/ir/serialize.d.ts +1 -1
  528. package/dist/ir/serialize.js +3 -3
  529. package/dist/ir/serialize.js.map +1 -1
  530. package/dist/ir/traverse.js +4 -21
  531. package/dist/ir/traverse.js.map +1 -1
  532. package/dist/ir/unshare.test.js +1 -1
  533. package/dist/ir/validate.js +66 -213
  534. package/dist/ir/validate.js.map +1 -1
  535. package/dist/ir/validate.test.js +31 -13
  536. package/dist/ir/validate.test.js.map +1 -1
  537. package/dist/library/fence-eval.test.js +1 -1
  538. package/dist/library/int-infer.test.js +4 -4
  539. package/dist/library/library-cache.test.js +5 -5
  540. package/dist/library/prepare.js +5 -20
  541. package/dist/library/prepare.js.map +1 -1
  542. package/dist/native/cli.js +0 -2
  543. package/dist/native/cli.js.map +1 -1
  544. package/dist/native/compiler.test.js +2 -4
  545. package/dist/native/compiler.test.js.map +1 -1
  546. package/dist/native/prepare.js +2 -4
  547. package/dist/native/prepare.js.map +1 -1
  548. package/dist/native/sanitizer.js +2 -16
  549. package/dist/native/sanitizer.js.map +1 -1
  550. package/dist/native/sanitizer.test.js +0 -3
  551. package/dist/native/sanitizer.test.js.map +1 -1
  552. package/dist/startup-cache.js +0 -2
  553. package/dist/startup-cache.js.map +1 -1
  554. package/package.json +20 -20
  555. package/surface-manifest.json +80 -104
  556. package/dist/backend/llvm/expr-island.d.ts +0 -15
  557. package/dist/backend/llvm/expr-island.js +0 -709
  558. package/dist/backend/llvm/expr-island.js.map +0 -1
  559. package/dist/frontend/lowering/coercions/island.d.ts +0 -37
  560. package/dist/frontend/lowering/coercions/island.js +0 -350
  561. package/dist/frontend/lowering/coercions/island.js.map +0 -1
  562. package/dist/frontend/lowering/lower-island.d.ts +0 -229
  563. package/dist/frontend/lowering/lower-island.js.map +0 -1
  564. package/dist/frontend/lowering/lower-island.test.js +0 -56
  565. package/dist/frontend/lowering/lower-island.test.js.map +0 -1
  566. package/dist/frontend/npm-fetch-analysis.d.ts +0 -17
  567. package/dist/frontend/npm-fetch-analysis.js +0 -109
  568. package/dist/frontend/npm-fetch-analysis.js.map +0 -1
  569. package/dist/frontend/npm-fetch-analysis.test.js +0 -130
  570. package/dist/frontend/npm-fetch-analysis.test.js.map +0 -1
  571. package/dist/frontend/npm-fetch-node.d.ts +0 -2
  572. package/dist/frontend/npm-fetch-node.js +0 -12
  573. package/dist/frontend/npm-fetch-node.js.map +0 -1
  574. package/dist/frontend/npm-fetch-syntax.d.ts +0 -6
  575. package/dist/frontend/npm-fetch-syntax.js +0 -202
  576. package/dist/frontend/npm-fetch-syntax.js.map +0 -1
  577. package/dist/native/runtime-typescript.d.ts +0 -4
  578. package/dist/native/runtime-typescript.js +0 -74
  579. package/dist/native/runtime-typescript.js.map +0 -1
  580. package/dist/native/runtime-typescript.test.js +0 -41
  581. package/dist/native/runtime-typescript.test.js.map +0 -1
  582. /package/dist/{frontend/lowering/lower-island.test.d.ts → backend/llvm/expr-calls.test.d.ts} +0 -0
  583. /package/dist/{frontend/npm-fetch-analysis.test.d.ts → backend/llvm/int32-abi.test.d.ts} +0 -0
  584. /package/dist/{native/runtime-typescript.test.d.ts → backend/llvm/regex-matchers.test.d.ts} +0 -0
@@ -0,0 +1,1827 @@
1
+ import { boolLit, countedFor, numLit, varRef } from "../../../ir/build.js";
2
+ import { BOOL, F64, VOID, typeEquals, typeKey, } from "../../../ir/ir.js";
3
+ import { everyExprChild, everyStmtChild, everyStmtList, transformStmtList, } from "../../../ir/traverse.js";
4
+ import { arrayIndexPresent, arrayValueRead, arrayValueStore, arrayValueType, } from "../array-values.js";
5
+ const PRODUCERS = new Set(["map", "filter", "flatMap", "flatMapArray"]);
6
+ const TERMINALS = new Set([
7
+ "reduce",
8
+ "some",
9
+ "every",
10
+ "findIndex",
11
+ "forEach",
12
+ ]);
13
+ function emptyEffects() {
14
+ return { throws: false, loops: false, reads: new Set(), writes: new Set() };
15
+ }
16
+ function mergeEffects(into, from) {
17
+ into.throws ||= from.throws;
18
+ into.loops ||= from.loops;
19
+ for (const id of from.reads)
20
+ into.reads.add(id);
21
+ for (const id of from.writes)
22
+ into.writes.add(id);
23
+ }
24
+ const PURE_MATH = new Set([
25
+ "math.abs",
26
+ "math.acos",
27
+ "math.acosh",
28
+ "math.asin",
29
+ "math.asinh",
30
+ "math.atan",
31
+ "math.atan2",
32
+ "math.atanh",
33
+ "math.cbrt",
34
+ "math.ceil",
35
+ "math.clz32",
36
+ "math.cos",
37
+ "math.cosh",
38
+ "math.exp",
39
+ "math.expm1",
40
+ "math.floor",
41
+ "math.fround",
42
+ "math.imul",
43
+ "math.log",
44
+ "math.log10",
45
+ "math.log1p",
46
+ "math.log2",
47
+ "math.max",
48
+ "math.min",
49
+ "math.pow",
50
+ "math.round",
51
+ "math.sign",
52
+ "math.sin",
53
+ "math.sinh",
54
+ "math.sqrt",
55
+ "math.tan",
56
+ "math.tanh",
57
+ "math.trunc",
58
+ ]);
59
+ /** String methods that cannot throw or call user code on string/number
60
+ * arguments. */
61
+ const PURE_STRING = new Set([
62
+ "length",
63
+ "charCodeAt",
64
+ "charAt",
65
+ "indexOf",
66
+ "includes",
67
+ "startsWith",
68
+ "endsWith",
69
+ "slice",
70
+ "substring",
71
+ "trim",
72
+ "trimStart",
73
+ "trimEnd",
74
+ "toLowerCase",
75
+ "toUpperCase",
76
+ "isWellFormed",
77
+ "toWellFormed",
78
+ "cpAt",
79
+ ]);
80
+ function primitive(t) {
81
+ return t.kind === "f64" || t.kind === "string" || t.kind === "bool";
82
+ }
83
+ class CallbackEffects {
84
+ fns;
85
+ /** The unique function that creates each closure, or null when several do. */
86
+ creators = new Map();
87
+ memo = new Map();
88
+ active = new Set();
89
+ globals;
90
+ constructor(functions, globals) {
91
+ this.fns = new Map(functions.map((fn) => [fn.name, fn]));
92
+ this.globals = new Map(globals.map((g) => [g.id, g]));
93
+ for (const fn of functions) {
94
+ everyStmtList(fn.body, {
95
+ expr: (e) => {
96
+ if (e.kind === "closure") {
97
+ const prior = this.creators.get(e.fnName);
98
+ this.creators.set(e.fnName, prior === undefined || prior === fn ? fn : null);
99
+ }
100
+ return true;
101
+ },
102
+ stmt: () => true,
103
+ });
104
+ }
105
+ }
106
+ fn(name) {
107
+ return this.fns.get(name);
108
+ }
109
+ scope(fn, all) {
110
+ return {
111
+ fn,
112
+ locals: new Map(fn.locals.map((l) => [l.id, l])),
113
+ captures: new Set((fn.captures ?? []).map((c) => c.localId)),
114
+ all,
115
+ };
116
+ }
117
+ /** Effects of calling `fn`, or null when they are not modeled. */
118
+ ofFunction(fn) {
119
+ const cached = this.memo.get(fn.name);
120
+ if (cached !== undefined)
121
+ return cached;
122
+ if (this.active.has(fn.name))
123
+ return null; // recursion
124
+ if (fn.async || fn.generator || fn.classCaptures?.length) {
125
+ this.memo.set(fn.name, null);
126
+ return null;
127
+ }
128
+ this.active.add(fn.name);
129
+ const effects = emptyEffects();
130
+ const scope = this.scope(fn, false);
131
+ const ok = fn.body.every((s) => this.stmt(s, scope, effects));
132
+ this.active.delete(fn.name);
133
+ const result = ok ? effects : null;
134
+ this.memo.set(fn.name, result);
135
+ return result;
136
+ }
137
+ /** Effects of evaluating `e` in `scope`; false when not modeled. */
138
+ expr(e, scope, out) {
139
+ const child = (c) => this.expr(c, scope, out);
140
+ const stmt = (s) => this.stmt(s, scope, out);
141
+ switch (e.kind) {
142
+ case "numLit":
143
+ case "strLit":
144
+ case "boolLit":
145
+ case "unitLit":
146
+ return true;
147
+ case "varRef":
148
+ return this.read(e.localId, scope, out);
149
+ case "assignExpr":
150
+ return this.write(e.localId, scope, out) && child(e.value);
151
+ case "incDec":
152
+ return this.read(e.localId, scope, out) && this.write(e.localId, scope, out);
153
+ case "bin":
154
+ return (e.left.type.kind !== "bigint" &&
155
+ e.right.type.kind !== "bigint" &&
156
+ child(e.left) &&
157
+ child(e.right));
158
+ case "logical":
159
+ case "ternary":
160
+ case "nullish":
161
+ case "strConcat":
162
+ case "strEq":
163
+ case "strCmp":
164
+ case "toBool":
165
+ case "recordGet":
166
+ case "recordLit":
167
+ case "recordClone":
168
+ case "fieldGet":
169
+ case "unionWrap":
170
+ case "unionNarrow":
171
+ case "unionIsTag":
172
+ case "unionDisc":
173
+ case "upcast":
174
+ case "seqExpr":
175
+ return everyExprChild(e, child, stmt);
176
+ case "unary":
177
+ return primitive(e.operand.type) && child(e.operand);
178
+ case "toString":
179
+ return (primitive(e.operand.type) || e.operand.type.kind === "union") && child(e.operand);
180
+ case "arrayLit":
181
+ return !e.spreads?.length && everyExprChild(e, child, stmt);
182
+ case "arrIntrinsic":
183
+ return e.method === "length" && e.receiver.type.kind === "array" && child(e.receiver);
184
+ case "strIntrinsic":
185
+ return (PURE_STRING.has(e.method) &&
186
+ e.args.every((a) => a.type.kind === "f64" || a.type.kind === "string") &&
187
+ everyExprChild(e, child, stmt));
188
+ case "libCall":
189
+ if (e.fn === "error.nodeThrow") {
190
+ out.throws = true;
191
+ return everyExprChild(e, child, stmt);
192
+ }
193
+ if (e.fn === "error.new")
194
+ return everyExprChild(e, child, stmt);
195
+ return PURE_MATH.has(e.fn) && everyExprChild(e, child, stmt);
196
+ case "closure":
197
+ return !e.adapts;
198
+ case "call": {
199
+ const callee = this.fns.get(e.callee);
200
+ if (!callee || callee.captures?.length)
201
+ return false;
202
+ const effects = this.ofFunction(callee);
203
+ if (!effects)
204
+ return false;
205
+ mergeEffects(out, effects);
206
+ return e.args.every(child);
207
+ }
208
+ case "callValue": {
209
+ if (e.receiver)
210
+ return false;
211
+ const callee = this.resolve(e.callee, scope.fn, 0);
212
+ if (!callee)
213
+ return false;
214
+ const effects = this.ofFunction(callee);
215
+ if (!effects)
216
+ return false;
217
+ mergeEffects(out, effects);
218
+ return child(e.callee) && e.args.every(child);
219
+ }
220
+ default:
221
+ return false;
222
+ }
223
+ }
224
+ stmt(s, scope, out) {
225
+ const child = (c) => this.expr(c, scope, out);
226
+ const stmt = (c) => this.stmt(c, scope, out);
227
+ switch (s.kind) {
228
+ case "varDecl":
229
+ return s.init === null || child(s.init);
230
+ case "assign":
231
+ return this.write(s.localId, scope, out) && child(s.value);
232
+ case "throw":
233
+ out.throws = true;
234
+ return child(s.value);
235
+ case "while":
236
+ case "doWhile":
237
+ case "for":
238
+ out.loops = true;
239
+ return everyStmtChild(s, child, stmt);
240
+ case "exprStmt":
241
+ case "return":
242
+ case "if":
243
+ case "block":
244
+ case "break":
245
+ case "continue":
246
+ return everyStmtChild(s, child, stmt);
247
+ default:
248
+ return false;
249
+ }
250
+ }
251
+ read(id, scope, out) {
252
+ const local = scope.locals.get(id);
253
+ if (local) {
254
+ if (local.tdz)
255
+ out.throws = true;
256
+ if (scope.all || scope.captures.has(id))
257
+ out.reads.add(id);
258
+ return true;
259
+ }
260
+ const global = this.globals.get(id);
261
+ if (!global)
262
+ return false;
263
+ if (global.tdz || global.initFlag)
264
+ out.throws = true;
265
+ out.reads.add(id);
266
+ return true;
267
+ }
268
+ write(id, scope, out) {
269
+ const local = scope.locals.get(id);
270
+ if (local) {
271
+ if (local.tdz)
272
+ out.throws = true;
273
+ if (scope.all || scope.captures.has(id))
274
+ out.writes.add(id);
275
+ return true;
276
+ }
277
+ const global = this.globals.get(id);
278
+ if (!global)
279
+ return false;
280
+ if (global.tdz || global.initFlag)
281
+ out.throws = true;
282
+ out.writes.add(id);
283
+ return true;
284
+ }
285
+ /** The one function a function-valued expression evaluated in `fn` can
286
+ * hold: a closure literal, or a never-reassigned binding initialized by
287
+ * one (directly, or through a call whose every return is the same
288
+ * closure literal). Null when unknown. */
289
+ resolve(e, fn, depth) {
290
+ if (depth > 4)
291
+ return null;
292
+ if (e.kind === "closure") {
293
+ const target = this.fns.get(e.fnName);
294
+ return target && !e.adapts ? target : null;
295
+ }
296
+ if (e.kind !== "varRef")
297
+ return null;
298
+ const id = e.localId;
299
+ if (!fn.locals.some((l) => l.id === id))
300
+ return null;
301
+ if (fn.params.some((p) => p.localId === id))
302
+ return null;
303
+ if ((fn.captures ?? []).some((c) => c.localId === id)) {
304
+ const creator = this.creators.get(fn.name);
305
+ return creator ? this.resolveBinding(id, creator, depth + 1) : null;
306
+ }
307
+ return this.resolveBinding(id, fn, depth + 1);
308
+ }
309
+ resolveBinding(id, fn, depth) {
310
+ const local = fn.locals.find((l) => l.id === id);
311
+ if (!local || local.mutable || local.tdz)
312
+ return null;
313
+ if (fn.params.some((p) => p.localId === id))
314
+ return null;
315
+ let init = null;
316
+ let definitions = 0;
317
+ everyStmtList(fn.body, {
318
+ expr: (node) => {
319
+ if ((node.kind === "assignExpr" || node.kind === "incDec") && node.localId === id)
320
+ definitions += 2;
321
+ return true;
322
+ },
323
+ stmt: (node) => {
324
+ if (node.kind === "varDecl" && node.localId === id) {
325
+ definitions++;
326
+ init = node.init;
327
+ }
328
+ else if ((node.kind === "assign" || node.kind === "forOf") && node.localId === id) {
329
+ definitions += 2;
330
+ }
331
+ return true;
332
+ },
333
+ });
334
+ const value = init;
335
+ if (definitions !== 1 || value === null)
336
+ return null;
337
+ return this.resolveValue(value, fn, depth);
338
+ }
339
+ resolveValue(e, fn, depth) {
340
+ if (e.kind === "closure" || e.kind === "varRef")
341
+ return this.resolve(e, fn, depth);
342
+ if (e.kind !== "call")
343
+ return null;
344
+ const factory = this.fns.get(e.callee);
345
+ if (!factory || factory.captures?.length || factory.async || factory.generator)
346
+ return null;
347
+ let target = null;
348
+ let returns = 0;
349
+ const ok = everyStmtList(factory.body, {
350
+ expr: () => true,
351
+ stmt: (node) => {
352
+ if (node.kind !== "return")
353
+ return true;
354
+ returns++;
355
+ const value = node.value;
356
+ if (value?.kind !== "closure" || value.adapts)
357
+ return false;
358
+ if (target !== null && target !== value.fnName)
359
+ return false;
360
+ target = value.fnName;
361
+ return true;
362
+ },
363
+ });
364
+ const name = target;
365
+ if (!ok || returns === 0 || name === null)
366
+ return null;
367
+ return this.fns.get(name) ?? null;
368
+ }
369
+ }
370
+ function isFunc(e) {
371
+ return e !== undefined && e.type.kind === "func";
372
+ }
373
+ function stageOf(lowerer, e) {
374
+ if (e.kind !== "call")
375
+ return null;
376
+ const info = lowerer.arrHofInfo.get(e.callee);
377
+ if (!info)
378
+ return null;
379
+ const expected = 2 + (info.method === "reduce" && info.hasInit ? 1 : 0) + (info.split ? 1 : 0);
380
+ if (e.args.length !== expected)
381
+ return null;
382
+ const receiver = e.args[0];
383
+ const slow = e.args[1];
384
+ const fast = info.split ? e.args[e.args.length - 1] : undefined;
385
+ if (!isFunc(slow) || (info.split && !isFunc(fast)))
386
+ return null;
387
+ if (receiver.type.kind !== "array" || !typeEquals(receiver.type.elem, info.elem))
388
+ return null;
389
+ return {
390
+ stage: { info, slow, fast: fast && isFunc(fast) ? fast : null, loc: e.loc },
391
+ receiver,
392
+ };
393
+ }
394
+ function matchChain(lowerer, e) {
395
+ let terminal;
396
+ let receiver;
397
+ const stages = [];
398
+ if (e.kind === "arrIntrinsic" && e.method === "length" && e.args.length === 0) {
399
+ if (e.receiver.type.kind !== "array")
400
+ return null;
401
+ terminal = { kind: "length" };
402
+ receiver = e.receiver;
403
+ }
404
+ else {
405
+ const top = stageOf(lowerer, e);
406
+ if (!top)
407
+ return null;
408
+ receiver = top.receiver;
409
+ if (TERMINALS.has(top.stage.info.method)) {
410
+ const init = top.stage.info.method === "reduce" && top.stage.info.hasInit && e.kind === "call"
411
+ ? e.args[2]
412
+ : null;
413
+ terminal = { kind: "call", stage: top.stage, init };
414
+ }
415
+ else if (PRODUCERS.has(top.stage.info.method)) {
416
+ terminal = { kind: "collect" };
417
+ stages.push(top.stage);
418
+ }
419
+ else
420
+ return null;
421
+ }
422
+ for (;;) {
423
+ const inner = stageOf(lowerer, receiver);
424
+ if (!inner || !PRODUCERS.has(inner.stage.info.method))
425
+ break;
426
+ stages.unshift(inner.stage);
427
+ receiver = inner.receiver;
428
+ }
429
+ let slice = null;
430
+ if (receiver.kind === "arrIntrinsic" &&
431
+ receiver.method === "slice" &&
432
+ receiver.args.length <= 2 &&
433
+ receiver.receiver.type.kind === "array" &&
434
+ receiver.type.kind === "array" &&
435
+ typeEquals(receiver.type, receiver.receiver.type) &&
436
+ receiver.args.every((a) => a.type.kind === "f64")) {
437
+ slice = { start: receiver.args[0] ?? null, end: receiver.args[1] ?? null };
438
+ receiver = receiver.receiver;
439
+ }
440
+ if (receiver.type.kind !== "array")
441
+ return null;
442
+ const removed = stages.length + (slice ? 1 : 0) - (terminal.kind === "collect" ? 1 : 0);
443
+ if (removed < 1)
444
+ return null;
445
+ return {
446
+ source: receiver,
447
+ sourceType: receiver.type,
448
+ slice,
449
+ stages,
450
+ terminal,
451
+ type: e.type,
452
+ loc: e.loc,
453
+ };
454
+ }
455
+ /** Fuse chains of array-HOF helper calls into one loop.
456
+ *
457
+ * `xs.filter(f).map(g).reduce(h, z)` lowers to nested helper calls, each
458
+ * materializing a full intermediate array. When every callback's effects
459
+ * are modeled (see CallbackEffects) and reordering them cannot be
460
+ * observed, the chain becomes one synthetic helper that runs each element
461
+ * through all stages before reading the next, and never builds the
462
+ * intermediate arrays. The rules, from strictest to weakest need:
463
+ *
464
+ * - No callback writes the heap, calls host code or an unknown function,
465
+ * or recurses. Allocation and reads are fine: with no heap writes, every
466
+ * read sees the same value in either order.
467
+ * - Callbacks may assign shared bindings (captured locals, globals), but
468
+ * no other callback in the chain reads or assigns those bindings.
469
+ * - At most one callback may throw (or contain a loop, which might not
470
+ * terminate); if one does, no other callback assigns anything. A throw
471
+ * then leaves the same observable state in either order.
472
+ * - some/every/findIndex stop early, so the stages before them must not
473
+ * throw, loop or assign at all: the unfused chain would have run them on
474
+ * every element.
475
+ * - Index parameters receive the position in the array the unfused
476
+ * stage would have visited: the source index while only maps (which
477
+ * keep holes in place) precede it, else a count of the values that
478
+ * reached the stage. Only the first stage over an unsliced source may
479
+ * take the array parameter; any other would name an intermediate array.
480
+ * - Holes: map and slice preserve them, filter/flatMap drop them; every
481
+ * consumer here skips them except findIndex, which is not fused when a
482
+ * hole can reach it. Lengths are read once, before any callback, and no
483
+ * callback can write an array.
484
+ *
485
+ * A `const` holding a chain that is read once, as the receiver of the next
486
+ * statement's consumer, is folded into that consumer when the rest of that
487
+ * statement is free of effects. */
488
+ export function fuseArrayPipelines(lowerer, functions) {
489
+ if (lowerer.arrHofInfo.size === 0)
490
+ return;
491
+ const pass = new PipelineFusion(lowerer, functions);
492
+ for (const fn of functions.slice())
493
+ pass.run(fn);
494
+ functions.push(...pass.added);
495
+ }
496
+ class PipelineFusion {
497
+ lowerer;
498
+ added = [];
499
+ effects;
500
+ helpers = new Map();
501
+ constructor(lowerer, functions) {
502
+ this.lowerer = lowerer;
503
+ this.effects = new CallbackEffects(functions, lowerer.globalsList);
504
+ }
505
+ run(fn) {
506
+ const infos = this.lowerer.arrHofInfo;
507
+ const candidate = !everyStmtList(fn.body, {
508
+ expr: (e) => !(e.kind === "call" && infos.has(e.callee)),
509
+ stmt: () => true,
510
+ });
511
+ if (!candidate)
512
+ return;
513
+ const removed = new Set();
514
+ let body = this.lists(fn.body, fn, removed);
515
+ body = transformStmtList(body, {
516
+ stmt: (s) => s,
517
+ expr: (e) => this.fuse(e, fn) ?? e,
518
+ });
519
+ fn.body = body;
520
+ if (removed.size > 0)
521
+ fn.locals = fn.locals.filter((l) => !removed.has(l.id));
522
+ }
523
+ /* ── statement lists: fold single-use chain bindings ── */
524
+ lists(list, fn, removed) {
525
+ const nested = list.map((s) => this.nested(s, fn, removed));
526
+ const out = [];
527
+ for (let i = 0; i < nested.length; i++) {
528
+ let current = nested[i];
529
+ while (i + 1 < nested.length) {
530
+ const merged = this.fold(current, nested[i + 1], fn);
531
+ if (!merged)
532
+ break;
533
+ removed.add(current.localId);
534
+ current = merged;
535
+ i++;
536
+ }
537
+ out.push(current);
538
+ }
539
+ return out;
540
+ }
541
+ nested(s, fn, removed) {
542
+ const l = (body) => this.lists(body, fn, removed);
543
+ switch (s.kind) {
544
+ case "if":
545
+ return { ...s, then: l(s.then), else_: s.else_ === null ? null : l(s.else_) };
546
+ case "while":
547
+ case "doWhile":
548
+ case "for":
549
+ case "forOf":
550
+ case "block":
551
+ return { ...s, body: l(s.body) };
552
+ case "switch":
553
+ return { ...s, cases: s.cases.map((c) => ({ ...c, body: l(c.body) })) };
554
+ case "tryCatch":
555
+ return {
556
+ ...s,
557
+ tryBody: l(s.tryBody),
558
+ catchBody: s.catchBody === null ? null : l(s.catchBody),
559
+ finallyBody: s.finallyBody === null ? null : l(s.finallyBody),
560
+ };
561
+ default:
562
+ return s;
563
+ }
564
+ }
565
+ /** `const L = <chain>; <stmt using L once as a consumer receiver>`. */
566
+ fold(decl, next, fn) {
567
+ if (decl.kind !== "varDecl" || decl.init === null)
568
+ return null;
569
+ const init = decl.init;
570
+ const id = decl.localId;
571
+ const local = fn.locals.find((l) => l.id === id);
572
+ if (!local || local.boxed || local.tdz || !typeEquals(local.type, init.type))
573
+ return null;
574
+ if (!matchChain(this.lowerer, init) && !stageOf(this.lowerer, init)) {
575
+ if (!(init.kind === "arrIntrinsic" && init.method === "slice"))
576
+ return null;
577
+ }
578
+ if (useCount(fn, id) !== 1)
579
+ return null;
580
+ // Only expression statements whose value is computed before any store.
581
+ let root;
582
+ switch (next.kind) {
583
+ case "varDecl":
584
+ if (next.init === null)
585
+ return null;
586
+ root = next.init;
587
+ break;
588
+ case "assign":
589
+ root = next.value;
590
+ break;
591
+ case "exprStmt":
592
+ root = next.expr;
593
+ break;
594
+ case "return":
595
+ if (next.value === null)
596
+ return null;
597
+ root = next.value;
598
+ break;
599
+ default:
600
+ return null;
601
+ }
602
+ const site = locateUse(this.lowerer, root, id);
603
+ if (!site)
604
+ return null;
605
+ const substituted = substituteReceiver(site.node, init);
606
+ // The chain now runs after the rest of `next`'s operands are evaluated;
607
+ // those must not observe or change anything the chain touches.
608
+ const plan = this.plan(substituted, fn);
609
+ const chainArgs = matchChain(this.lowerer, substituted);
610
+ if (!plan || !chainArgs)
611
+ return null;
612
+ if (site.conditional && (plan.risky || plan.writes.size > 0))
613
+ return null;
614
+ const scope = this.effects.scope(fn, true);
615
+ const rest = emptyEffects();
616
+ const placeholder = { kind: "numLit", value: 0, type: F64, loc: site.node.loc };
617
+ if (!this.effects.expr(replaceNode(root, site.node, placeholder), scope, rest))
618
+ return null;
619
+ if (rest.throws || rest.writes.size > 0 || intersects(rest.reads, plan.writes))
620
+ return null;
621
+ const moved = emptyEffects();
622
+ const movedArgs = [chainArgs.source];
623
+ if (chainArgs.slice?.start)
624
+ movedArgs.push(chainArgs.slice.start);
625
+ if (chainArgs.slice?.end)
626
+ movedArgs.push(chainArgs.slice.end);
627
+ if (!movedArgs.every((a) => this.effects.expr(a, scope, moved)))
628
+ return null;
629
+ if (moved.throws || moved.writes.size > 0 || moved.loops)
630
+ return null;
631
+ const value = replaceNode(root, site.node, substituted);
632
+ switch (next.kind) {
633
+ case "varDecl":
634
+ return { ...next, init: value };
635
+ case "assign":
636
+ return { ...next, value };
637
+ case "exprStmt":
638
+ return { ...next, expr: value };
639
+ case "return":
640
+ return { ...next, value };
641
+ }
642
+ return null;
643
+ }
644
+ /* ── expressions ── */
645
+ fuse(e, fn) {
646
+ const plan = this.plan(e, fn);
647
+ return plan ? this.emit(plan) : null;
648
+ }
649
+ /** Decide whether `e` is a fusable chain; null when it is not. */
650
+ plan(e, fn) {
651
+ const chain = matchChain(this.lowerer, e);
652
+ if (!chain)
653
+ return null;
654
+ const { stages, terminal } = chain;
655
+ const terminalStage = terminal.kind === "call" ? terminal.stage : null;
656
+ const callbacks = terminalStage ? [...stages, terminalStage] : stages;
657
+ // Element types must line up stage to stage.
658
+ let elem = chain.sourceType.elem;
659
+ for (const stage of callbacks) {
660
+ if (!typeEquals(stage.info.elem, elem))
661
+ return null;
662
+ if (stage.info.method === "filter" && !typeEquals(stage.info.outElem, stage.info.elem))
663
+ return null;
664
+ if ((stage.info.method === "map" || stage.info.method === "flatMap") &&
665
+ !typeEquals(stage.info.outElem, stage.info.fnRet))
666
+ return null;
667
+ elem = stage.info.outElem;
668
+ }
669
+ // Index arguments are reproduced; an array argument only names the source.
670
+ for (let k = 0; k < callbacks.length; k++) {
671
+ const stage = callbacks[k];
672
+ const lead = stage.info.method === "reduce" ? 1 : 0;
673
+ if (stage.info.arity > lead + 3)
674
+ return null;
675
+ if (stage.info.arity === lead + 3 && (k !== 0 || chain.slice))
676
+ return null;
677
+ }
678
+ if (terminalStage?.info.method === "reduce" && !terminalStage.info.hasInit)
679
+ return null;
680
+ const holes = holeCarrying(chain);
681
+ if (terminalStage?.info.method === "findIndex" && holes)
682
+ return null;
683
+ // Effects of each callback, in chain order.
684
+ const scope = this.effects.scope(fn, true);
685
+ const summaries = [];
686
+ const argReads = emptyEffects();
687
+ for (const stage of callbacks) {
688
+ for (const arg of [stage.slow, stage.fast]) {
689
+ if (!arg)
690
+ continue;
691
+ if (!this.effects.expr(arg, scope, argReads))
692
+ return null;
693
+ }
694
+ const target = this.effects.resolve(stage.fast ?? stage.slow, fn, 0);
695
+ const summary = target ? this.effects.ofFunction(target) : null;
696
+ if (!summary)
697
+ return null;
698
+ if (stage.fast) {
699
+ const slow = this.effects.resolve(stage.slow, fn, 0);
700
+ const slowSummary = slow ? this.effects.ofFunction(slow) : null;
701
+ if (!slowSummary)
702
+ return null;
703
+ mergeEffects(summary, slowSummary);
704
+ }
705
+ summaries.push(summary);
706
+ }
707
+ if (argReads.throws || argReads.writes.size > 0)
708
+ return null;
709
+ const init = terminal.kind === "call" ? terminal.init : null;
710
+ if (init) {
711
+ if (!this.effects.expr(init, scope, argReads))
712
+ return null;
713
+ if (argReads.throws || argReads.writes.size > 0 || argReads.loops)
714
+ return null;
715
+ }
716
+ const writes = new Set();
717
+ let riskyCount = 0;
718
+ for (const s of summaries) {
719
+ for (const id of s.writes)
720
+ writes.add(id);
721
+ if (s.throws || s.loops)
722
+ riskyCount++;
723
+ }
724
+ if (riskyCount > 1)
725
+ return null;
726
+ for (let j = 0; j < summaries.length; j++) {
727
+ const sj = summaries[j];
728
+ if (riskyCount === 1 && !(sj.throws || sj.loops) && sj.writes.size > 0)
729
+ return null;
730
+ for (let k = 0; k < summaries.length; k++) {
731
+ if (j === k)
732
+ continue;
733
+ const sk = summaries[k];
734
+ if (intersects(sj.writes, sk.reads) || intersects(sj.writes, sk.writes))
735
+ return null;
736
+ }
737
+ }
738
+ if (intersects(argReads.reads, writes))
739
+ return null;
740
+ const early = terminalStage !== null &&
741
+ (terminalStage.info.method === "some" ||
742
+ terminalStage.info.method === "every" ||
743
+ terminalStage.info.method === "findIndex");
744
+ if (early) {
745
+ for (let k = 0; k < stages.length; k++) {
746
+ const s = summaries[k];
747
+ if (s.throws || s.loops || s.writes.size > 0)
748
+ return null;
749
+ }
750
+ }
751
+ return { chain, callbacks, writes, risky: riskyCount > 0 };
752
+ }
753
+ /* ── helper construction ── */
754
+ /** A closure-literal callback whose body can be copied into the loop:
755
+ * small, no nested closures, and its captured bindings only read (they
756
+ * are passed in by value, read when the fused call starts; no callback
757
+ * in the chain assigns a binding another one reads). */
758
+ inlineTarget = (arg) => {
759
+ if (arg.kind !== "closure" || arg.adapts)
760
+ return null;
761
+ const fn = this.effects.fn(arg.fnName);
762
+ if (!fn || fn.async || fn.generator || fn.classCaptures?.length)
763
+ return null;
764
+ const captures = new Set((fn.captures ?? []).map((c) => c.localId));
765
+ if (captures.size !== arg.captures.length)
766
+ return null;
767
+ const effects = this.effects.ofFunction(fn);
768
+ if (!effects || intersects(effects.writes, captures))
769
+ return null;
770
+ if (fn.locals.some((l) => l.tdz || (l.boxed && !captures.has(l.id))))
771
+ return null;
772
+ let size = 0;
773
+ const small = everyStmtList(fn.body, {
774
+ expr: (e) => ++size <= MAX_INLINE_NODES && e.kind !== "closure" && e.kind !== "selfRef",
775
+ stmt: (s) => ++size <= MAX_INLINE_NODES && s.kind !== "tryCatch",
776
+ });
777
+ return small ? fn : null;
778
+ };
779
+ emit(plan) {
780
+ const built = new FusedLoop(this.lowerer, plan.chain, this.inlineTarget).build();
781
+ if (!built)
782
+ return null;
783
+ let name = this.helpers.get(built.key);
784
+ if (!name) {
785
+ name = `%arr.fused.${this.helpers.size}`;
786
+ this.helpers.set(built.key, name);
787
+ this.added.push({ ...built.fn, name });
788
+ }
789
+ return {
790
+ kind: "call",
791
+ callee: name,
792
+ args: built.args,
793
+ type: plan.chain.type,
794
+ loc: plan.chain.loc,
795
+ };
796
+ }
797
+ }
798
+ function intersects(a, b) {
799
+ for (const id of a)
800
+ if (b.has(id))
801
+ return true;
802
+ return false;
803
+ }
804
+ /** Map and slice keep holes in place; filter and flatMap drop them. */
805
+ function holeCarrying(chain) {
806
+ return chain.stages.every((s) => s.info.method === "map");
807
+ }
808
+ function useCount(fn, id) {
809
+ let uses = 0;
810
+ everyStmtList(fn.body, {
811
+ expr: (e) => {
812
+ switch (e.kind) {
813
+ case "varRef":
814
+ case "assignExpr":
815
+ case "incDec":
816
+ if (e.localId === id)
817
+ uses++;
818
+ break;
819
+ case "closure":
820
+ if (e.captures.includes(id))
821
+ uses += 2;
822
+ break;
823
+ }
824
+ return true;
825
+ },
826
+ stmt: (s) => {
827
+ if ((s.kind === "assign" || s.kind === "forOf") && s.localId === id)
828
+ uses += 2;
829
+ if (s.kind === "varDecl" && s.localId === id && s.init === null)
830
+ uses += 2;
831
+ return true;
832
+ },
833
+ });
834
+ return uses;
835
+ }
836
+ /** The consumer node in `root` whose receiver is `varRef id`, and whether
837
+ * it is evaluated only conditionally. */
838
+ function locateUse(lowerer, root, id) {
839
+ let found = null;
840
+ const isUse = (e) => {
841
+ const receiver = e.kind === "arrIntrinsic" && e.method === "length"
842
+ ? e.receiver
843
+ : e.kind === "call" && lowerer.arrHofInfo.has(e.callee)
844
+ ? e.args[0]
845
+ : undefined;
846
+ return receiver?.kind === "varRef" && receiver.localId === id;
847
+ };
848
+ const walk = (e, conditional) => {
849
+ if (isUse(e)) {
850
+ found = { node: e, conditional };
851
+ return false;
852
+ }
853
+ const branching = conditional ||
854
+ e.kind === "ternary" ||
855
+ e.kind === "logical" ||
856
+ e.kind === "nullish" ||
857
+ e.kind === "orDefault" ||
858
+ e.kind === "optChain" ||
859
+ e.kind === "seqExpr" ||
860
+ e.kind === "closure";
861
+ return everyExprChild(e, (c) => walk(c, branching && !(e.kind === "ternary" && c === e.cond)), () => true);
862
+ };
863
+ walk(root, false);
864
+ return found;
865
+ }
866
+ function substituteReceiver(node, receiver) {
867
+ if (node.kind === "arrIntrinsic")
868
+ return { ...node, receiver };
869
+ if (node.kind === "call")
870
+ return { ...node, args: [receiver, ...node.args.slice(1)] };
871
+ return node;
872
+ }
873
+ function replaceNode(root, target, replacement) {
874
+ if (root === target)
875
+ return replacement;
876
+ return transformExprOnce(root, target, replacement);
877
+ }
878
+ function transformExprOnce(root, target, replacement) {
879
+ const [stmt] = transformStmtList([{ kind: "exprStmt", expr: root, loc: root.loc }], {
880
+ stmt: (s) => s,
881
+ expr: (e) => (e === target ? replacement : e),
882
+ });
883
+ return stmt.expr;
884
+ }
885
+ const MAX_INLINE_NODES = 400;
886
+ class FusedLoop {
887
+ lowerer;
888
+ chain;
889
+ inlineTarget;
890
+ params = [];
891
+ locals = [];
892
+ args = [];
893
+ keyParts = [];
894
+ callees = [];
895
+ loc;
896
+ next = 0;
897
+ failed = false;
898
+ constructor(lowerer, chain, inlineTarget) {
899
+ this.lowerer = lowerer;
900
+ this.chain = chain;
901
+ this.inlineTarget = inlineTarget;
902
+ this.loc = chain.loc;
903
+ }
904
+ fresh(name) {
905
+ return `${name}.${this.next++}`;
906
+ }
907
+ local(name, type, mutable = false) {
908
+ const id = this.fresh(name);
909
+ this.locals.push({ id, name, type, mutable });
910
+ return id;
911
+ }
912
+ param(name, value, key) {
913
+ const id = this.fresh(name);
914
+ this.params.push({ localId: id, name, type: value.type });
915
+ this.locals.push({ id, name, type: value.type, mutable: false });
916
+ this.args.push(value);
917
+ this.keyParts.push(`${key}:${typeKey(value.type)}`);
918
+ return varRef(id, value.type, this.loc);
919
+ }
920
+ /** How the loop reaches one callback argument; decided at first use. */
921
+ callee(arg) {
922
+ const known = this.callees.find((c) => c.arg === arg);
923
+ if (known)
924
+ return known;
925
+ const target = this.inlineTarget(arg);
926
+ let callee;
927
+ if (target && arg.kind === "closure") {
928
+ const captures = new Map();
929
+ (target.captures ?? []).forEach((capture, index) => {
930
+ const source = arg.captures[index];
931
+ const ref = this.param("cap", varRef(source, capture.type, this.loc), `cap@${target.name}`);
932
+ captures.set(capture.localId, ref.localId);
933
+ });
934
+ this.keyParts.push(`inline@${target.name}`);
935
+ callee = { arg, ref: null, inline: target, captures };
936
+ }
937
+ else {
938
+ const site = arg.kind === "closure" ? `@${arg.fnName}` : "";
939
+ callee = { arg, ref: this.param("f", arg, `cb${site}`), inline: null, captures: null };
940
+ }
941
+ this.callees.push(callee);
942
+ return callee;
943
+ }
944
+ /** Statements and the value of calling `callee` with `args`. */
945
+ invoke(callee, args, ret) {
946
+ const loc = this.loc;
947
+ if (!callee.inline) {
948
+ return {
949
+ pre: [],
950
+ value: { kind: "callValue", callee: callee.ref, args, type: ret, loc },
951
+ };
952
+ }
953
+ const fn = callee.inline;
954
+ if (fn.params.length !== args.length) {
955
+ this.failed = true;
956
+ return { pre: [], value: { kind: "numLit", value: 0, type: F64, loc } };
957
+ }
958
+ const renamed = new Map(callee.captures);
959
+ // A parameter the body never assigns can name its argument's local
960
+ // directly: the loop's value locals are not assigned while the
961
+ // inlined body runs.
962
+ const assigned = new Set();
963
+ everyStmtList(fn.body, {
964
+ expr: (e) => {
965
+ if (e.kind === "assignExpr" || e.kind === "incDec")
966
+ assigned.add(e.localId);
967
+ return true;
968
+ },
969
+ stmt: (st) => {
970
+ if (st.kind === "assign" || st.kind === "forOf")
971
+ assigned.add(st.localId);
972
+ return true;
973
+ },
974
+ });
975
+ const aliased = new Set();
976
+ fn.params.forEach((p, index) => {
977
+ const arg = args[index];
978
+ if (arg.kind === "varRef" && !assigned.has(p.localId) && typeEquals(arg.type, p.type)) {
979
+ renamed.set(p.localId, arg.localId);
980
+ aliased.add(p.localId);
981
+ }
982
+ });
983
+ for (const l of fn.locals) {
984
+ if (renamed.has(l.id))
985
+ continue;
986
+ const id = this.local(l.name, l.type, true);
987
+ renamed.set(l.id, id);
988
+ }
989
+ const labels = new Map();
990
+ const exit = this.fresh("%fused.exit");
991
+ const label = (name) => {
992
+ let id = labels.get(name);
993
+ if (!id) {
994
+ id = this.fresh("%fused.label");
995
+ labels.set(name, id);
996
+ }
997
+ return id;
998
+ };
999
+ const result = fn.returnType.kind === "void" ? null : this.local("ret", fn.returnType, true);
1000
+ const rename = (id) => renamed.get(id) ?? id;
1001
+ const body = transformStmtList(fn.body, {
1002
+ expr: (e) => {
1003
+ switch (e.kind) {
1004
+ case "varRef":
1005
+ case "incDec":
1006
+ case "assignExpr":
1007
+ return { ...e, localId: rename(e.localId) };
1008
+ default:
1009
+ return e;
1010
+ }
1011
+ },
1012
+ stmt: (s) => {
1013
+ switch (s.kind) {
1014
+ case "return": {
1015
+ const exitStmt = { kind: "break", label: exit, loc: s.loc };
1016
+ if (s.value === null || result === null) {
1017
+ return s.value === null
1018
+ ? exitStmt
1019
+ : {
1020
+ kind: "block",
1021
+ body: [{ kind: "exprStmt", expr: s.value, loc: s.loc }, exitStmt],
1022
+ loc: s.loc,
1023
+ };
1024
+ }
1025
+ return {
1026
+ kind: "block",
1027
+ body: [{ kind: "assign", localId: result, value: s.value, loc: s.loc }, exitStmt],
1028
+ loc: s.loc,
1029
+ };
1030
+ }
1031
+ case "varDecl":
1032
+ case "assign":
1033
+ return { ...s, localId: rename(s.localId) };
1034
+ case "break":
1035
+ case "continue":
1036
+ return s.label && s.label !== exit ? { ...s, label: label(s.label) } : s;
1037
+ case "while":
1038
+ case "doWhile":
1039
+ case "for":
1040
+ case "block":
1041
+ return s.labels ? { ...s, labels: s.labels.map(label) } : s;
1042
+ default:
1043
+ return s;
1044
+ }
1045
+ },
1046
+ });
1047
+ const bind = fn.params.flatMap((p, index) => aliased.has(p.localId)
1048
+ ? []
1049
+ : [{ kind: "varDecl", localId: rename(p.localId), init: args[index], loc }]);
1050
+ const pre = [];
1051
+ if (result !== null)
1052
+ pre.push({ kind: "varDecl", localId: result, init: null, loc });
1053
+ pre.push({ kind: "block", labels: [exit], body: [...bind, ...body], loc });
1054
+ return {
1055
+ pre,
1056
+ value: result === null
1057
+ ? { kind: "numLit", value: 0, type: F64, loc }
1058
+ : varRef(result, fn.returnType, loc),
1059
+ };
1060
+ }
1061
+ build() {
1062
+ const { chain, loc } = this;
1063
+ const arrT = chain.sourceType;
1064
+ const a = this.param("a", chain.source, "src");
1065
+ const n = this.local("n", F64);
1066
+ const body = [
1067
+ {
1068
+ kind: "varDecl",
1069
+ localId: n,
1070
+ init: { kind: "arrIntrinsic", method: "length", receiver: a, args: [], type: F64, loc },
1071
+ loc,
1072
+ },
1073
+ ];
1074
+ const nRef = varRef(n, F64, loc);
1075
+ // Slice bounds: ToIntegerOrInfinity, then relative to the length.
1076
+ let lo = numLit(0, loc);
1077
+ let hi = nRef;
1078
+ if (chain.slice) {
1079
+ const bound = (value, name, fallback) => {
1080
+ if (!value)
1081
+ return fallback;
1082
+ const raw = this.param(name, value, name);
1083
+ const t = this.local(`${name}Int`, F64);
1084
+ const at = this.local(`${name}At`, F64);
1085
+ const tRef = varRef(t, F64, loc);
1086
+ const sum = { kind: "bin", op: "+", left: nRef, right: tRef, type: F64, loc };
1087
+ const lt = (left, right) => ({
1088
+ kind: "bin",
1089
+ op: "<",
1090
+ left,
1091
+ right,
1092
+ type: BOOL,
1093
+ loc,
1094
+ });
1095
+ const pick = (cond, then, else_) => ({
1096
+ kind: "ternary",
1097
+ cond,
1098
+ then,
1099
+ else_,
1100
+ type: F64,
1101
+ loc,
1102
+ });
1103
+ body.push({
1104
+ kind: "varDecl",
1105
+ localId: t,
1106
+ init: pick({ kind: "bin", op: "!==", left: raw, right: raw, type: BOOL, loc }, numLit(0, loc), { kind: "libCall", fn: "math.trunc", args: [raw], type: F64, loc }),
1107
+ loc,
1108
+ }, {
1109
+ kind: "varDecl",
1110
+ localId: at,
1111
+ init: pick(lt(tRef, numLit(0, loc)), pick(lt(sum, numLit(0, loc)), numLit(0, loc), sum), pick(lt(nRef, tRef), nRef, tRef)),
1112
+ loc,
1113
+ });
1114
+ return varRef(at, F64, loc);
1115
+ };
1116
+ lo = bound(chain.slice.start, "start", numLit(0, loc));
1117
+ hi = bound(chain.slice.end, "end", nRef);
1118
+ this.keyParts.push(`slice:${chain.slice.start ? 1 : 0}${chain.slice.end ? 1 : 0}`);
1119
+ }
1120
+ const terminalStage = chain.terminal.kind === "call" ? chain.terminal.stage : null;
1121
+ for (const stage of terminalStage ? [...chain.stages, terminalStage] : chain.stages) {
1122
+ this.keyParts.push(`${stage.info.method}:${typeKey(stage.info.elem)}:${typeKey(stage.info.fnRet)}:${stage.info.arity}:${stage.info.present ? "p" : ""}`);
1123
+ }
1124
+ const holes = holeCarrying(chain);
1125
+ const resultT = chain.type;
1126
+ const terminal = chain.terminal;
1127
+ const epilogue = [];
1128
+ // Terminal state. The reduce seed is a parameter evaluated after the
1129
+ // callbacks, like the unfused call's argument.
1130
+ let acc = null;
1131
+ let count = null;
1132
+ let out = null;
1133
+ const stateDecls = [];
1134
+ if (terminal.kind === "call" && terminal.stage.info.method === "reduce") {
1135
+ acc = this.local("acc", terminal.stage.info.outElem, true);
1136
+ epilogue.push({ kind: "return", value: varRef(acc, terminal.stage.info.outElem, loc), loc });
1137
+ }
1138
+ else if (terminal.kind === "length" ||
1139
+ (terminal.kind === "call" && terminal.stage.info.method === "findIndex")) {
1140
+ count = this.local("count", F64, true);
1141
+ stateDecls.push({ kind: "varDecl", localId: count, init: numLit(0, loc), loc });
1142
+ epilogue.push({
1143
+ kind: "return",
1144
+ value: terminal.kind === "length" ? varRef(count, F64, loc) : numLit(-1, loc),
1145
+ loc,
1146
+ });
1147
+ }
1148
+ else if (terminal.kind === "collect") {
1149
+ out = this.local("out", resultT);
1150
+ stateDecls.push({
1151
+ kind: "varDecl",
1152
+ localId: out,
1153
+ init: { kind: "arrayLit", elems: [], type: resultT, loc },
1154
+ loc,
1155
+ });
1156
+ if (holes) {
1157
+ stateDecls.push({
1158
+ kind: "arraySetLength",
1159
+ arr: varRef(out, resultT, loc),
1160
+ length: {
1161
+ kind: "ternary",
1162
+ cond: { kind: "bin", op: "<", left: lo, right: hi, type: BOOL, loc },
1163
+ then: { kind: "bin", op: "-", left: hi, right: lo, type: F64, loc },
1164
+ else_: numLit(0, loc),
1165
+ type: F64,
1166
+ loc,
1167
+ },
1168
+ loc,
1169
+ });
1170
+ }
1171
+ epilogue.push({ kind: "return", value: varRef(out, resultT, loc), loc });
1172
+ }
1173
+ else if (terminal.kind === "call") {
1174
+ const method = terminal.stage.info.method;
1175
+ if (method === "some" || method === "every")
1176
+ epilogue.push({ kind: "return", value: boolLit(method === "every", loc), loc });
1177
+ }
1178
+ const i = varRef("i.0", F64, loc);
1179
+ this.locals.push({ id: "i.0", name: "i", type: F64, mutable: true });
1180
+ const slotRead = () => ({ kind: "arrayGet", arr: a, index: i, type: arrT.elem, loc });
1181
+ const outIndex = () => chain.slice ? { kind: "bin", op: "-", left: i, right: lo, type: F64, loc } : i;
1182
+ const counters = new Map();
1183
+ /** The index argument for the value now reaching callback `k`. */
1184
+ const indexArg = (k, pre) => {
1185
+ if (chain.stages.slice(0, k).every((s) => s.info.method === "map"))
1186
+ return outIndex();
1187
+ let counter = counters.get(k);
1188
+ if (counter === undefined) {
1189
+ counter = this.local("index", F64, true);
1190
+ counters.set(k, counter);
1191
+ stateDecls.push({ kind: "varDecl", localId: counter, init: numLit(0, loc), loc });
1192
+ }
1193
+ const ix = this.local("ix", F64);
1194
+ pre.push({ kind: "varDecl", localId: ix, init: varRef(counter, F64, loc), loc }, increment(counter, loc));
1195
+ return varRef(ix, F64, loc);
1196
+ };
1197
+ /** Call callback `k` with `lead` arguments, a value, and the index and
1198
+ * array arguments it declares. */
1199
+ const deliver = (stage, k, lead, flow) => {
1200
+ const arity = stage.info.arity;
1201
+ const value = varRef(flow.id, flow.type, loc);
1202
+ const at = lead.length;
1203
+ const pick = () => {
1204
+ if (arity <= at)
1205
+ return { arg: stage.slow, value: null };
1206
+ const fast = stage.fast;
1207
+ if (fast && typeEquals(fast.type.params[at], flow.type))
1208
+ return { arg: fast, value };
1209
+ const paramT = stage.slow.type.params[at];
1210
+ if (typeEquals(paramT, flow.type))
1211
+ return { arg: stage.slow, value };
1212
+ if (paramT.kind === "union" && flow.type.kind !== "union") {
1213
+ const tag = this.lowerer.armTag(paramT.unionId, flow.type);
1214
+ if (tag >= 0)
1215
+ return {
1216
+ arg: stage.slow,
1217
+ value: { kind: "unionWrap", unionId: paramT.unionId, tag, value, type: paramT, loc },
1218
+ };
1219
+ }
1220
+ return null;
1221
+ };
1222
+ const chosen = pick();
1223
+ if (!chosen)
1224
+ return null;
1225
+ const before = [];
1226
+ const args = chosen.value ? [...lead, chosen.value] : lead.slice();
1227
+ if (chosen.value && arity > args.length)
1228
+ args.push(indexArg(k, before));
1229
+ if (chosen.value && arity > args.length)
1230
+ args.push(a);
1231
+ const called = this.invoke(this.callee(chosen.arg), args.slice(0, arity), chosen.arg.type.ret);
1232
+ return { pre: [...before, ...called.pre], value: called.value };
1233
+ };
1234
+ const fail = () => {
1235
+ this.failed = true;
1236
+ return [];
1237
+ };
1238
+ /** Turn the source slot into a value for the callback of `stage`. */
1239
+ const read = (stage, rest) => {
1240
+ if (stage.info.present) {
1241
+ const v = this.local("v", arrT.elem);
1242
+ return [
1243
+ { kind: "varDecl", localId: v, init: slotRead(), loc },
1244
+ ...rest({ kind: "value", id: v, type: arrT.elem }),
1245
+ ];
1246
+ }
1247
+ const valueT = arrayValueType(this.lowerer, arrT.elem);
1248
+ if (stage.fast) {
1249
+ // The split visit: a value goes on as the plain element, a present
1250
+ // undefined as the undefined arm.
1251
+ const missing = this.lowerer.wrappedUndefined(valueT, loc);
1252
+ if (!missing)
1253
+ return fail();
1254
+ const v = this.local("v", arrT.elem);
1255
+ const u = this.local("u", valueT);
1256
+ return [
1257
+ {
1258
+ kind: "if",
1259
+ cond: {
1260
+ kind: "bin",
1261
+ op: "===",
1262
+ left: { kind: "arrayState", arr: a, index: i, type: F64, loc },
1263
+ right: numLit(1, loc),
1264
+ type: BOOL,
1265
+ loc,
1266
+ },
1267
+ then: [
1268
+ { kind: "varDecl", localId: v, init: slotRead(), loc },
1269
+ ...rest({ kind: "value", id: v, type: arrT.elem }),
1270
+ ],
1271
+ else_: [
1272
+ { kind: "varDecl", localId: u, init: missing, loc },
1273
+ ...rest({ kind: "value", id: u, type: valueT }),
1274
+ ],
1275
+ loc,
1276
+ },
1277
+ ];
1278
+ }
1279
+ const u = this.local("u", valueT);
1280
+ return [
1281
+ {
1282
+ kind: "varDecl",
1283
+ localId: u,
1284
+ init: arrayValueRead(this.lowerer, a, i, arrT.elem, loc),
1285
+ loc,
1286
+ },
1287
+ ...rest({ kind: "value", id: u, type: valueT }),
1288
+ ];
1289
+ };
1290
+ /** The statements that run one value through stages k.. and the terminal. */
1291
+ const visit = (k, flow) => {
1292
+ if (k === chain.stages.length)
1293
+ return finish(flow);
1294
+ const stage = chain.stages[k];
1295
+ const info = stage.info;
1296
+ if (flow.kind === "slot")
1297
+ return read(stage, (value) => visit(k, value));
1298
+ const called = deliver(stage, k, [], flow);
1299
+ if (!called)
1300
+ return fail();
1301
+ const { pre, value: result } = called;
1302
+ switch (info.method) {
1303
+ case "map":
1304
+ case "flatMap": {
1305
+ const id = this.local("m", info.fnRet);
1306
+ return [
1307
+ ...pre,
1308
+ { kind: "varDecl", localId: id, init: result, loc },
1309
+ ...visit(k + 1, { kind: "value", id, type: info.fnRet }),
1310
+ ];
1311
+ }
1312
+ case "filter":
1313
+ return [
1314
+ ...pre,
1315
+ {
1316
+ kind: "if",
1317
+ cond: result.type.kind === "bool"
1318
+ ? result
1319
+ : { kind: "toBool", operand: result, type: BOOL, loc },
1320
+ then: visit(k + 1, flow),
1321
+ else_: null,
1322
+ loc,
1323
+ },
1324
+ ];
1325
+ case "flatMapArray": {
1326
+ const r = this.local("r", info.fnRet);
1327
+ const m = this.local("len", F64);
1328
+ const j = this.local("j", F64, true);
1329
+ const inner = info.fnRet.elem;
1330
+ const rRef = varRef(r, info.fnRet, loc);
1331
+ const jRef = varRef(j, F64, loc);
1332
+ let each;
1333
+ if (k + 1 === chain.stages.length && terminal.kind === "length") {
1334
+ each = finish({ kind: "slot" });
1335
+ }
1336
+ else {
1337
+ const w = this.local("w", arrayValueType(this.lowerer, inner));
1338
+ const element = arrayValueRead(this.lowerer, rRef, jRef, inner, loc);
1339
+ each = [
1340
+ { kind: "varDecl", localId: w, init: element, loc },
1341
+ ...visit(k + 1, { kind: "value", id: w, type: element.type }),
1342
+ ];
1343
+ }
1344
+ return [
1345
+ ...pre,
1346
+ { kind: "varDecl", localId: r, init: result, loc },
1347
+ {
1348
+ kind: "varDecl",
1349
+ localId: m,
1350
+ init: {
1351
+ kind: "arrIntrinsic",
1352
+ method: "length",
1353
+ receiver: rRef,
1354
+ args: [],
1355
+ type: F64,
1356
+ loc,
1357
+ },
1358
+ loc,
1359
+ },
1360
+ {
1361
+ kind: "for",
1362
+ init: { kind: "varDecl", localId: j, init: numLit(0, loc), loc },
1363
+ cond: {
1364
+ kind: "bin",
1365
+ op: "<",
1366
+ left: jRef,
1367
+ right: varRef(m, F64, loc),
1368
+ type: BOOL,
1369
+ loc,
1370
+ },
1371
+ update: {
1372
+ kind: "assign",
1373
+ localId: j,
1374
+ value: { kind: "bin", op: "+", left: jRef, right: numLit(1, loc), type: F64, loc },
1375
+ loc,
1376
+ },
1377
+ body: [
1378
+ {
1379
+ kind: "if",
1380
+ cond: arrayIndexPresent(rRef, jRef, loc),
1381
+ then: each,
1382
+ else_: null,
1383
+ loc,
1384
+ },
1385
+ ],
1386
+ loc,
1387
+ },
1388
+ ];
1389
+ }
1390
+ default:
1391
+ return fail();
1392
+ }
1393
+ };
1394
+ /** The terminal's action for one value. */
1395
+ const finish = (flow) => {
1396
+ if (terminal.kind === "length")
1397
+ return [increment(count, loc)];
1398
+ if (terminal.kind === "collect") {
1399
+ if (flow.kind !== "value")
1400
+ return fail();
1401
+ const last = chain.stages[chain.stages.length - 1];
1402
+ const outT = resultT;
1403
+ if (!typeEquals(outT.elem, last.info.outElem))
1404
+ return fail();
1405
+ const outRef = varRef(out, resultT, loc);
1406
+ const index = holes
1407
+ ? outIndex()
1408
+ : { kind: "arrIntrinsic", method: "length", receiver: outRef, args: [], type: F64, loc };
1409
+ return [
1410
+ arrayValueStore(this.lowerer, outRef, index, varRef(flow.id, flow.type, loc), outT.elem, loc),
1411
+ ];
1412
+ }
1413
+ const stage = terminal.stage;
1414
+ const method = stage.info.method;
1415
+ if (flow.kind === "slot")
1416
+ return read(stage, (value) => finish(value));
1417
+ if (method === "reduce") {
1418
+ const accT = stage.info.outElem;
1419
+ const called = deliver(stage, chain.stages.length, [varRef(acc, accT, loc)], flow);
1420
+ if (!called || !typeEquals(called.value.type, accT))
1421
+ return fail();
1422
+ return [...called.pre, { kind: "assign", localId: acc, value: called.value, loc }];
1423
+ }
1424
+ const called = deliver(stage, chain.stages.length, [], flow);
1425
+ if (!called)
1426
+ return fail();
1427
+ const { pre, value: result } = called;
1428
+ if (method === "forEach") {
1429
+ return stage.slow.type.ret.kind === "void" && result.kind === "numLit"
1430
+ ? pre
1431
+ : [...pre, { kind: "exprStmt", expr: result, loc }];
1432
+ }
1433
+ const test = predicateToBoolean(result);
1434
+ if (method === "some")
1435
+ return [
1436
+ ...pre,
1437
+ {
1438
+ kind: "if",
1439
+ cond: test,
1440
+ then: [{ kind: "return", value: boolLit(true, loc), loc }],
1441
+ else_: null,
1442
+ loc,
1443
+ },
1444
+ ];
1445
+ if (method === "every")
1446
+ return [
1447
+ ...pre,
1448
+ {
1449
+ kind: "if",
1450
+ cond: { kind: "unary", op: "!", operand: test, type: BOOL, loc },
1451
+ then: [{ kind: "return", value: boolLit(false, loc), loc }],
1452
+ else_: null,
1453
+ loc,
1454
+ },
1455
+ ];
1456
+ if (method === "findIndex")
1457
+ return [
1458
+ ...pre,
1459
+ {
1460
+ kind: "if",
1461
+ cond: test,
1462
+ then: [{ kind: "return", value: varRef(count, F64, loc), loc }],
1463
+ else_: null,
1464
+ loc,
1465
+ },
1466
+ increment(count, loc),
1467
+ ];
1468
+ return fail();
1469
+ };
1470
+ // Callbacks are parameters in stage order; the reduce seed follows them.
1471
+ for (const stage of terminalStage ? [...chain.stages, terminalStage] : chain.stages) {
1472
+ void stage;
1473
+ }
1474
+ const present = visit(0, { kind: "slot" });
1475
+ if (this.failed)
1476
+ return null;
1477
+ if (acc !== null && terminal.kind === "call") {
1478
+ const z = this.param("z", terminal.init, "init");
1479
+ stateDecls.unshift({ kind: "varDecl", localId: acc, init: z, loc });
1480
+ }
1481
+ body.push(...stateDecls);
1482
+ const hole = holes && terminal.kind === "length" ? [increment(count, loc)] : null;
1483
+ const loopBody = [
1484
+ { kind: "if", cond: arrayIndexPresent(a, i, loc), then: present, else_: hole, loc },
1485
+ ];
1486
+ const loop = chain.slice
1487
+ ? {
1488
+ kind: "for",
1489
+ init: { kind: "varDecl", localId: "i.0", init: lo, loc },
1490
+ cond: { kind: "bin", op: "<", left: i, right: hi, type: BOOL, loc },
1491
+ update: {
1492
+ kind: "assign",
1493
+ localId: "i.0",
1494
+ value: { kind: "bin", op: "+", left: i, right: numLit(1, loc), type: F64, loc },
1495
+ loc,
1496
+ },
1497
+ body: loopBody,
1498
+ loc,
1499
+ }
1500
+ : countedFor(loc, nRef, () => loopBody);
1501
+ body.push(loop, ...epilogue);
1502
+ this.keyParts.push(`${terminal.kind}:${typeKey(resultT)}`);
1503
+ const returnType = terminal.kind === "call" && terminal.stage.info.method === "forEach" ? VOID : resultT;
1504
+ const fn = {
1505
+ params: this.params,
1506
+ returnType,
1507
+ locals: this.locals,
1508
+ body,
1509
+ loc,
1510
+ };
1511
+ foldKnownUnions(fn);
1512
+ scalarizeRecordTemporaries(this.lowerer, fn);
1513
+ return { fn, args: this.args, key: this.keyParts.join("|") };
1514
+ }
1515
+ }
1516
+ function increment(id, loc) {
1517
+ return {
1518
+ kind: "assign",
1519
+ localId: id,
1520
+ value: {
1521
+ kind: "bin",
1522
+ op: "+",
1523
+ left: varRef(id, F64, loc),
1524
+ right: numLit(1, loc),
1525
+ type: F64,
1526
+ loc,
1527
+ },
1528
+ loc,
1529
+ };
1530
+ }
1531
+ /** ToBoolean of a predicate result, as the some/every/findIndex helpers apply it. */
1532
+ function predicateToBoolean(e) {
1533
+ if (e.type.kind === "bool")
1534
+ return e;
1535
+ if (e.type.kind === "dyn")
1536
+ return { kind: "dynTest", test: "truthy", value: e, type: BOOL, loc: e.loc };
1537
+ return { kind: "toBool", operand: e, type: BOOL, loc: e.loc };
1538
+ }
1539
+ /** A union local the fused loop wrapped from a plain value has a known
1540
+ * tag: its tag tests fold, its narrowing is the plain value, and the wrap
1541
+ * itself goes away once nothing reads it. */
1542
+ function foldKnownUnions(fn) {
1543
+ const writes = new Map();
1544
+ const count = (id) => {
1545
+ writes.set(id, (writes.get(id) ?? 0) + 1);
1546
+ };
1547
+ everyStmtList(fn.body, {
1548
+ expr: (e) => {
1549
+ if (e.kind === "assignExpr" || e.kind === "incDec")
1550
+ count(e.localId);
1551
+ return true;
1552
+ },
1553
+ stmt: (s) => {
1554
+ if (s.kind === "varDecl" || s.kind === "assign" || s.kind === "forOf")
1555
+ count(s.localId);
1556
+ return true;
1557
+ },
1558
+ });
1559
+ const known = new Map();
1560
+ everyStmtList(fn.body, {
1561
+ expr: () => true,
1562
+ stmt: (s) => {
1563
+ if (s.kind === "varDecl" &&
1564
+ s.init?.kind === "unionWrap" &&
1565
+ s.init.value.kind === "varRef" &&
1566
+ writes.get(s.localId) === 1 &&
1567
+ writes.get(s.init.value.localId) === 1)
1568
+ known.set(s.localId, { tag: s.init.tag, value: s.init.value });
1569
+ return true;
1570
+ },
1571
+ });
1572
+ if (known.size === 0)
1573
+ return;
1574
+ const fold = (e) => {
1575
+ if (e.kind === "unionIsTag" && e.value.kind === "varRef") {
1576
+ const k = known.get(e.value.localId);
1577
+ if (k)
1578
+ return boolLit((k.tag === e.tag) !== e.negated, e.loc);
1579
+ }
1580
+ if (e.kind === "unionNarrow" && e.value.kind === "varRef") {
1581
+ const k = known.get(e.value.localId);
1582
+ if (k && k.tag === e.tag && typeEquals(k.value.type, e.type))
1583
+ return { ...k.value, loc: e.loc };
1584
+ }
1585
+ if (e.kind === "ternary") {
1586
+ const cond = fold(e.cond);
1587
+ if (cond.kind === "boolLit")
1588
+ return fold(cond.value ? e.then : e.else_);
1589
+ }
1590
+ return e;
1591
+ };
1592
+ let body = transformStmtList(fn.body, { stmt: (s) => s, expr: fold });
1593
+ const reads = new Set();
1594
+ everyStmtList(body, {
1595
+ expr: (e) => {
1596
+ if (e.kind === "varRef")
1597
+ reads.add(e.localId);
1598
+ return true;
1599
+ },
1600
+ stmt: () => true,
1601
+ });
1602
+ const dead = new Set([...known.keys()].filter((id) => !reads.has(id)));
1603
+ if (dead.size > 0) {
1604
+ const prune = (list) => list
1605
+ .filter((s) => !(s.kind === "varDecl" && dead.has(s.localId)))
1606
+ .map((s) => {
1607
+ switch (s.kind) {
1608
+ case "if":
1609
+ return { ...s, then: prune(s.then), else_: s.else_ === null ? null : prune(s.else_) };
1610
+ case "for":
1611
+ case "while":
1612
+ case "doWhile":
1613
+ case "block":
1614
+ return { ...s, body: prune(s.body) };
1615
+ default:
1616
+ return s;
1617
+ }
1618
+ });
1619
+ body = prune(body);
1620
+ fn.locals = fn.locals.filter((l) => !dead.has(l.id));
1621
+ }
1622
+ fn.body = body;
1623
+ }
1624
+ /* ── per-element record temporaries ──────────────────────────────────── */
1625
+ /** Inside a fused loop, a record one inlined stage builds and the next
1626
+ * only reads field by field never needs to exist. A group of record locals
1627
+ * connected by copies, each defined only by a literal of their shape (or a
1628
+ * copy within the group) and used only through field reads, becomes one
1629
+ * scalar local per field. Literal fields still evaluate in source order
1630
+ * (through temporaries, so a literal may read the fields it replaces).
1631
+ * Identity cannot be observed: no use besides a field read survives. */
1632
+ function scalarizeRecordTemporaries(lowerer, fn) {
1633
+ const params = new Set(fn.params.map((p) => p.localId));
1634
+ const candidates = new Map();
1635
+ for (const l of fn.locals) {
1636
+ if (l.type.kind !== "record" || l.boxed || l.tdz || params.has(l.id))
1637
+ continue;
1638
+ const shape = lowerer.shapes.get(l.type.shapeId);
1639
+ if (!shape ||
1640
+ shape.tuple ||
1641
+ shape.indexValue ||
1642
+ shape.fields.length === 0 ||
1643
+ shape.fields.length > 8 ||
1644
+ !shape.fields.every((f) => f.type.kind === "f64" || f.type.kind === "bool"))
1645
+ continue;
1646
+ candidates.set(l.id, l);
1647
+ }
1648
+ if (candidates.size === 0)
1649
+ return;
1650
+ const parent = new Map();
1651
+ const find = (id) => {
1652
+ let root = id;
1653
+ while (parent.has(root))
1654
+ root = parent.get(root);
1655
+ return root;
1656
+ };
1657
+ const union = (x, y) => {
1658
+ const rx = find(x);
1659
+ const ry = find(y);
1660
+ if (rx !== ry)
1661
+ parent.set(rx, ry);
1662
+ };
1663
+ const bad = new Set();
1664
+ const sameShape = (x, y) => candidates.get(x).type.shapeId === candidates.get(y).type.shapeId;
1665
+ const literalOk = (e, shapeId) => e.kind === "recordLit" &&
1666
+ e.type.kind === "record" &&
1667
+ e.type.shapeId === shapeId &&
1668
+ e.fields.every((f) => !f.overflow && !f.drop);
1669
+ const expr = (e) => {
1670
+ if (e.kind === "recordGet" && e.obj.kind === "varRef" && candidates.has(e.obj.localId))
1671
+ return true;
1672
+ if ((e.kind === "varRef" || e.kind === "assignExpr" || e.kind === "incDec") &&
1673
+ candidates.has(e.localId))
1674
+ bad.add(e.localId);
1675
+ if (e.kind === "closure")
1676
+ for (const id of e.captures)
1677
+ if (candidates.has(id))
1678
+ bad.add(id);
1679
+ return everyExprChild(e, expr, stmt);
1680
+ };
1681
+ const definition = (id, value) => {
1682
+ const target = candidates.get(id);
1683
+ if (value === null)
1684
+ return;
1685
+ if (value.kind === "varRef" && candidates.has(value.localId)) {
1686
+ if (sameShape(id, value.localId))
1687
+ union(id, value.localId);
1688
+ else
1689
+ bad.add(id);
1690
+ return;
1691
+ }
1692
+ if (literalOk(value, target.type.shapeId)) {
1693
+ for (const f of value.fields)
1694
+ expr(f.value);
1695
+ return;
1696
+ }
1697
+ bad.add(id);
1698
+ expr(value);
1699
+ };
1700
+ const stmt = (s) => {
1701
+ if ((s.kind === "varDecl" || s.kind === "assign") && candidates.has(s.localId)) {
1702
+ definition(s.localId, s.kind === "varDecl" ? s.init : s.value);
1703
+ return true;
1704
+ }
1705
+ if (s.kind === "forOf" && candidates.has(s.localId))
1706
+ bad.add(s.localId);
1707
+ return everyStmtChild(s, expr, stmt);
1708
+ };
1709
+ fn.body.forEach(stmt);
1710
+ const badRoots = new Set([...bad].map(find));
1711
+ const chosen = [...candidates.keys()].filter((id) => !badRoots.has(find(id)));
1712
+ if (chosen.length === 0)
1713
+ return;
1714
+ const fields = new Map();
1715
+ const added = [];
1716
+ let next = 0;
1717
+ const used = new Set(fn.locals.map((l) => l.id));
1718
+ const fresh = (name) => {
1719
+ let id;
1720
+ do
1721
+ id = `${name}.s${next++}`;
1722
+ while (used.has(id));
1723
+ used.add(id);
1724
+ return id;
1725
+ };
1726
+ for (const id of chosen) {
1727
+ const local = candidates.get(id);
1728
+ const shape = lowerer.shapes.get(local.type.shapeId);
1729
+ const map = new Map();
1730
+ for (const f of shape.fields) {
1731
+ const fid = fresh(`${local.name}.${f.name}`);
1732
+ map.set(f.name, fid);
1733
+ added.push({ id: fid, name: `${local.name}.${f.name}`, type: f.type, mutable: true });
1734
+ }
1735
+ fields.set(id, map);
1736
+ }
1737
+ const fieldType = (id, field) => lowerer.shapes.get(candidates.get(id).type.shapeId).fields.find((f) => f.name === field)
1738
+ .type;
1739
+ const rewriteExpr = (e) => {
1740
+ const [s] = transformStmtList([{ kind: "exprStmt", expr: e, loc: e.loc }], {
1741
+ stmt: (x) => x,
1742
+ expr: (x) => {
1743
+ if (x.kind === "recordGet" && x.obj.kind === "varRef") {
1744
+ const id = fields.get(x.obj.localId)?.get(x.field);
1745
+ if (id)
1746
+ return { kind: "varRef", localId: id, type: x.type, loc: x.loc };
1747
+ }
1748
+ return x;
1749
+ },
1750
+ });
1751
+ return s.expr;
1752
+ };
1753
+ const rewriteList = (list) => list.flatMap(rewriteStmt);
1754
+ const rewriteStmt = (s) => {
1755
+ if ((s.kind === "varDecl" || s.kind === "assign") && fields.has(s.localId)) {
1756
+ const map = fields.get(s.localId);
1757
+ const value = s.kind === "varDecl" ? s.init : s.value;
1758
+ const store = (fid, v) => s.kind === "varDecl"
1759
+ ? { kind: "varDecl", localId: fid, init: v, loc: s.loc }
1760
+ : { kind: "assign", localId: fid, value: v, loc: s.loc };
1761
+ if (value === null)
1762
+ return [...map.values()].map((fid) => store(fid, null));
1763
+ if (value.kind === "varRef") {
1764
+ const from = fields.get(value.localId);
1765
+ return [...map].map(([name, fid]) => store(fid, varRef(from.get(name), fieldType(s.localId, name), s.loc)));
1766
+ }
1767
+ const literal = value;
1768
+ const temps = [];
1769
+ const values = new Map();
1770
+ for (const f of literal.fields) {
1771
+ const t = fresh(`${candidates.get(s.localId).name}.${f.name}.t`);
1772
+ const type = fieldType(s.localId, f.name);
1773
+ added.push({ id: t, name: f.name, type, mutable: false });
1774
+ temps.push({ kind: "varDecl", localId: t, init: rewriteExpr(f.value), loc: s.loc });
1775
+ values.set(f.name, varRef(t, type, s.loc));
1776
+ }
1777
+ return [...temps, ...[...map].map(([name, fid]) => store(fid, values.get(name)))];
1778
+ }
1779
+ return rewriteNested(s);
1780
+ };
1781
+ const rewriteNested = (s) => {
1782
+ switch (s.kind) {
1783
+ case "if":
1784
+ return [
1785
+ {
1786
+ ...s,
1787
+ cond: rewriteExpr(s.cond),
1788
+ then: rewriteList(s.then),
1789
+ else_: s.else_ === null ? null : rewriteList(s.else_),
1790
+ },
1791
+ ];
1792
+ case "while":
1793
+ return [{ ...s, cond: rewriteExpr(s.cond), body: rewriteList(s.body) }];
1794
+ case "doWhile":
1795
+ return [{ ...s, cond: rewriteExpr(s.cond), body: rewriteList(s.body) }];
1796
+ case "for":
1797
+ return [
1798
+ {
1799
+ ...s,
1800
+ init: s.init === null ? null : (rewriteStmt(s.init)[0] ?? null),
1801
+ cond: s.cond === null ? null : rewriteExpr(s.cond),
1802
+ update: s.update === null ? null : (rewriteStmt(s.update)[0] ?? null),
1803
+ body: rewriteList(s.body),
1804
+ },
1805
+ ];
1806
+ case "block":
1807
+ return [{ ...s, body: rewriteList(s.body) }];
1808
+ default: {
1809
+ const [x] = transformStmtList([s], {
1810
+ stmt: (y) => y,
1811
+ expr: (y) => {
1812
+ if (y.kind === "recordGet" && y.obj.kind === "varRef") {
1813
+ const id = fields.get(y.obj.localId)?.get(y.field);
1814
+ if (id)
1815
+ return { kind: "varRef", localId: id, type: y.type, loc: y.loc };
1816
+ }
1817
+ return y;
1818
+ },
1819
+ });
1820
+ return [x];
1821
+ }
1822
+ }
1823
+ };
1824
+ fn.body = rewriteList(fn.body);
1825
+ fn.locals = [...fn.locals.filter((l) => !fields.has(l.id)), ...added];
1826
+ }
1827
+ //# sourceMappingURL=array-pipelines.js.map