redscript-mc 2.6.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (636) hide show
  1. package/.github/workflows/ci.yml +11 -0
  2. package/CHANGELOG.md +18 -9
  3. package/README-benchmarks.md +48 -0
  4. package/README-vscode-test.md +251 -0
  5. package/RELEASE_NOTES.md +74 -0
  6. package/ROADMAP.md +131 -167
  7. package/benchmarks/_shared.ts +468 -0
  8. package/benchmarks/baseline.json +2816 -0
  9. package/benchmarks/baseline.md +13 -0
  10. package/benchmarks/compiler-perf.report.json +207 -0
  11. package/benchmarks/compiler-perf.ts +76 -0
  12. package/benchmarks/results.md +13 -0
  13. package/benchmarks/stdlib-complexity.report.json +2606 -0
  14. package/benchmarks/stdlib-complexity.ts +54 -0
  15. package/benchmarks/stdlib-size.md +57 -0
  16. package/benchmarks/stdlib-size.ts +91 -0
  17. package/coverage-report.md +177 -0
  18. package/dist/src/__tests__/budget.test.js +4 -0
  19. package/dist/src/__tests__/cache/cache-behavior.test.d.ts +10 -0
  20. package/dist/src/__tests__/cache/cache-behavior.test.js +425 -0
  21. package/dist/src/__tests__/cache-extra.test.d.ts +5 -0
  22. package/dist/src/__tests__/cache-extra.test.js +211 -0
  23. package/dist/src/__tests__/cli-init.test.d.ts +1 -0
  24. package/dist/src/__tests__/cli-init.test.js +97 -0
  25. package/dist/src/__tests__/cli-publish.test.d.ts +9 -0
  26. package/dist/src/__tests__/cli-publish.test.js +189 -0
  27. package/dist/src/__tests__/cli.test.js +76 -0
  28. package/dist/src/__tests__/compile-preprocess.test.d.ts +11 -0
  29. package/dist/src/__tests__/compile-preprocess.test.js +328 -0
  30. package/dist/src/__tests__/compiler/break-stmt.test.d.ts +1 -0
  31. package/dist/src/__tests__/compiler/break-stmt.test.js +58 -0
  32. package/dist/src/__tests__/compiler/const-decl.test.d.ts +1 -0
  33. package/dist/src/__tests__/compiler/const-decl.test.js +123 -0
  34. package/dist/src/__tests__/compiler/continue-stmt.test.d.ts +1 -0
  35. package/dist/src/__tests__/compiler/continue-stmt.test.js +67 -0
  36. package/dist/src/__tests__/compiler/coroutine-extended.test.d.ts +17 -0
  37. package/dist/src/__tests__/compiler/coroutine-extended.test.js +565 -0
  38. package/dist/src/__tests__/compiler/deprecated.test.d.ts +4 -0
  39. package/dist/src/__tests__/compiler/deprecated.test.js +285 -0
  40. package/dist/src/__tests__/compiler/do-while.test.d.ts +1 -0
  41. package/dist/src/__tests__/compiler/do-while.test.js +120 -0
  42. package/dist/src/__tests__/compiler/enum-payload.test.d.ts +9 -0
  43. package/dist/src/__tests__/compiler/enum-payload.test.js +272 -0
  44. package/dist/src/__tests__/compiler/interface.test.d.ts +10 -0
  45. package/dist/src/__tests__/compiler/interface.test.js +258 -0
  46. package/dist/src/__tests__/compiler/labeled-loops.test.d.ts +1 -0
  47. package/dist/src/__tests__/compiler/labeled-loops.test.js +263 -0
  48. package/dist/src/__tests__/compiler/match-string.test.d.ts +1 -0
  49. package/dist/src/__tests__/compiler/match-string.test.js +43 -0
  50. package/dist/src/__tests__/compiler/memoize.test.d.ts +1 -0
  51. package/dist/src/__tests__/compiler/memoize.test.js +113 -0
  52. package/dist/src/__tests__/compiler/method-chain.test.d.ts +5 -0
  53. package/dist/src/__tests__/compiler/method-chain.test.js +115 -0
  54. package/dist/src/__tests__/compiler/module-import.test.d.ts +12 -0
  55. package/dist/src/__tests__/compiler/module-import.test.js +261 -0
  56. package/dist/src/__tests__/compiler/option-extensions.test.d.ts +6 -0
  57. package/dist/src/__tests__/compiler/option-extensions.test.js +191 -0
  58. package/dist/src/__tests__/compiler/profile-decorator.test.d.ts +1 -0
  59. package/dist/src/__tests__/compiler/profile-decorator.test.js +69 -0
  60. package/dist/src/__tests__/compiler/string-advanced.test.d.ts +7 -0
  61. package/dist/src/__tests__/compiler/string-advanced.test.js +281 -0
  62. package/dist/src/__tests__/compiler/struct-extends.test.d.ts +1 -0
  63. package/dist/src/__tests__/compiler/struct-extends.test.js +95 -0
  64. package/dist/src/__tests__/compiler/throttle-retry.test.d.ts +1 -0
  65. package/dist/src/__tests__/compiler/throttle-retry.test.js +166 -0
  66. package/dist/src/__tests__/compiler/tuple-type.test.d.ts +10 -0
  67. package/dist/src/__tests__/compiler/tuple-type.test.js +229 -0
  68. package/dist/src/__tests__/compiler/watch-decorator.test.d.ts +1 -0
  69. package/dist/src/__tests__/compiler/watch-decorator.test.js +65 -0
  70. package/dist/src/__tests__/config/project-config.test.d.ts +1 -0
  71. package/dist/src/__tests__/config/project-config.test.js +199 -0
  72. package/dist/src/__tests__/config-decorator.test.d.ts +8 -0
  73. package/dist/src/__tests__/config-decorator.test.js +142 -0
  74. package/dist/src/__tests__/diagnostics-extra.test.d.ts +6 -0
  75. package/dist/src/__tests__/diagnostics-extra.test.js +132 -0
  76. package/dist/src/__tests__/emit/compile-branches.test.d.ts +1 -0
  77. package/dist/src/__tests__/emit/compile-branches.test.js +123 -0
  78. package/dist/src/__tests__/emit/compile-coverage.test.d.ts +25 -0
  79. package/dist/src/__tests__/emit/compile-coverage.test.js +617 -0
  80. package/dist/src/__tests__/emit/compile-extra-branches.test.d.ts +12 -0
  81. package/dist/src/__tests__/emit/compile-extra-branches.test.js +225 -0
  82. package/dist/src/__tests__/emit/compile-mocked-branches.test.d.ts +0 -0
  83. package/dist/src/__tests__/emit/compile-mocked-branches.test.js +238 -0
  84. package/dist/src/__tests__/emit/execute-chain.test.d.ts +10 -0
  85. package/dist/src/__tests__/emit/execute-chain.test.js +94 -0
  86. package/dist/src/__tests__/emit/index.test.js +2 -1
  87. package/dist/src/__tests__/emit/modules-branches.test.d.ts +1 -0
  88. package/dist/src/__tests__/emit/modules-branches.test.js +88 -0
  89. package/dist/src/__tests__/emit/modules-coverage.test.d.ts +15 -0
  90. package/dist/src/__tests__/emit/modules-coverage.test.js +221 -0
  91. package/dist/src/__tests__/emit/modules-errors.test.d.ts +12 -0
  92. package/dist/src/__tests__/emit/modules-errors.test.js +169 -0
  93. package/dist/src/__tests__/emit/modules-rewrite.test.d.ts +17 -0
  94. package/dist/src/__tests__/emit/modules-rewrite.test.js +204 -0
  95. package/dist/src/__tests__/emit/source-map.test.d.ts +1 -0
  96. package/dist/src/__tests__/emit/source-map.test.js +167 -0
  97. package/dist/src/__tests__/enum.test.js +9 -4
  98. package/dist/src/__tests__/error-recovery.test.d.ts +7 -0
  99. package/dist/src/__tests__/error-recovery.test.js +217 -0
  100. package/dist/src/__tests__/events-types-extra.test.d.ts +10 -0
  101. package/dist/src/__tests__/events-types-extra.test.js +91 -0
  102. package/dist/src/__tests__/events-types.test.d.ts +4 -0
  103. package/dist/src/__tests__/events-types.test.js +56 -0
  104. package/dist/src/__tests__/formatter.test.js +13 -5
  105. package/dist/src/__tests__/hir/lower-extra.test.d.ts +9 -0
  106. package/dist/src/__tests__/hir/lower-extra.test.js +140 -0
  107. package/dist/src/__tests__/hir/monomorphize-extra.test.d.ts +15 -0
  108. package/dist/src/__tests__/hir/monomorphize-extra.test.js +200 -0
  109. package/dist/src/__tests__/hir/monomorphize-extra2.test.d.ts +16 -0
  110. package/dist/src/__tests__/hir/monomorphize-extra2.test.js +316 -0
  111. package/dist/src/__tests__/incremental.test.js +10 -2
  112. package/dist/src/__tests__/index-extra.test.d.ts +10 -0
  113. package/dist/src/__tests__/index-extra.test.js +71 -0
  114. package/dist/src/__tests__/lexer.test.js +2 -2
  115. package/dist/src/__tests__/lint/rules.test.d.ts +5 -0
  116. package/dist/src/__tests__/lint/rules.test.js +208 -0
  117. package/dist/src/__tests__/lir/lower.test.js +29 -0
  118. package/dist/src/__tests__/lir/verify.test.js +30 -0
  119. package/dist/src/__tests__/lsp/completion.test.d.ts +7 -0
  120. package/dist/src/__tests__/lsp/completion.test.js +583 -0
  121. package/dist/src/__tests__/lsp/definition.test.d.ts +7 -0
  122. package/dist/src/__tests__/lsp/definition.test.js +454 -0
  123. package/dist/src/__tests__/lsp/diagnostics.test.d.ts +10 -0
  124. package/dist/src/__tests__/lsp/diagnostics.test.js +98 -0
  125. package/dist/src/__tests__/lsp/hover-docs.test.d.ts +10 -0
  126. package/dist/src/__tests__/lsp/hover-docs.test.js +210 -0
  127. package/dist/src/__tests__/lsp.test.js +4 -1
  128. package/dist/src/__tests__/mc-integration/item-entity-events.test.js +4 -0
  129. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +4 -0
  130. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.d.ts +13 -0
  131. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1227 -0
  132. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.d.ts +13 -0
  133. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1509 -0
  134. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.d.ts +14 -0
  135. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1374 -0
  136. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.d.ts +10 -0
  137. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +759 -0
  138. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.d.ts +13 -0
  139. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +855 -0
  140. package/dist/src/__tests__/mc-integration/stdlib-coverage.test.js +4 -0
  141. package/dist/src/__tests__/mc-integration/syntax-coverage.test.js +4 -0
  142. package/dist/src/__tests__/mc-validator-coverage.test.d.ts +13 -0
  143. package/dist/src/__tests__/mc-validator-coverage.test.js +296 -0
  144. package/dist/src/__tests__/mc-validator-extra.test.d.ts +13 -0
  145. package/dist/src/__tests__/mc-validator-extra.test.js +245 -0
  146. package/dist/src/__tests__/mir/lower-extra.test.d.ts +20 -0
  147. package/dist/src/__tests__/mir/lower-extra.test.js +361 -0
  148. package/dist/src/__tests__/mir/lower-extra2.test.d.ts +17 -0
  149. package/dist/src/__tests__/mir/lower-extra2.test.js +317 -0
  150. package/dist/src/__tests__/mir/lower-extra3.test.d.ts +19 -0
  151. package/dist/src/__tests__/mir/lower-extra3.test.js +249 -0
  152. package/dist/src/__tests__/mir/lower-extra4.test.d.ts +23 -0
  153. package/dist/src/__tests__/mir/lower-extra4.test.js +606 -0
  154. package/dist/src/__tests__/mir/lower-extra5.test.d.ts +25 -0
  155. package/dist/src/__tests__/mir/lower-extra5.test.js +543 -0
  156. package/dist/src/__tests__/mir/lower-extra6.test.d.ts +16 -0
  157. package/dist/src/__tests__/mir/lower-extra6.test.js +471 -0
  158. package/dist/src/__tests__/mir/lower-extra7.test.d.ts +35 -0
  159. package/dist/src/__tests__/mir/lower-extra7.test.js +921 -0
  160. package/dist/src/__tests__/mir/lower-extra8.test.d.ts +19 -0
  161. package/dist/src/__tests__/mir/lower-extra8.test.js +626 -0
  162. package/dist/src/__tests__/mir/lower-extra9.test.d.ts +14 -0
  163. package/dist/src/__tests__/mir/lower-extra9.test.js +717 -0
  164. package/dist/src/__tests__/optimizer/auto-inline.test.d.ts +1 -0
  165. package/dist/src/__tests__/optimizer/auto-inline.test.js +176 -0
  166. package/dist/src/__tests__/optimizer/cse.test.d.ts +4 -0
  167. package/dist/src/__tests__/optimizer/cse.test.js +178 -0
  168. package/dist/src/__tests__/optimizer/inline_fn.test.d.ts +1 -0
  169. package/dist/src/__tests__/optimizer/inline_fn.test.js +221 -0
  170. package/dist/src/__tests__/optimizer/licm.test.d.ts +1 -0
  171. package/dist/src/__tests__/optimizer/licm.test.js +244 -0
  172. package/dist/src/__tests__/optimizer/optimizer-extended.test.d.ts +12 -0
  173. package/dist/src/__tests__/optimizer/optimizer-extended.test.js +993 -0
  174. package/dist/src/__tests__/optimizer/strength-reduction.test.d.ts +1 -0
  175. package/dist/src/__tests__/optimizer/strength-reduction.test.js +86 -0
  176. package/dist/src/__tests__/optimizer/tco.test.d.ts +14 -0
  177. package/dist/src/__tests__/optimizer/tco.test.js +203 -0
  178. package/dist/src/__tests__/parser-coverage.test.d.ts +25 -0
  179. package/dist/src/__tests__/parser-coverage.test.js +491 -0
  180. package/dist/src/__tests__/parser-extra.test.d.ts +6 -0
  181. package/dist/src/__tests__/parser-extra.test.js +451 -0
  182. package/dist/src/__tests__/parser.test.js +12 -0
  183. package/dist/src/__tests__/repl-extra.test.d.ts +13 -0
  184. package/dist/src/__tests__/repl-extra.test.js +174 -0
  185. package/dist/src/__tests__/repl-server-extra.test.d.ts +10 -0
  186. package/dist/src/__tests__/repl-server-extra.test.js +161 -0
  187. package/dist/src/__tests__/repl-server.test.d.ts +6 -0
  188. package/dist/src/__tests__/repl-server.test.js +146 -0
  189. package/dist/src/__tests__/runtime-extra.test.d.ts +15 -0
  190. package/dist/src/__tests__/runtime-extra.test.js +732 -0
  191. package/dist/src/__tests__/singleton-decorator.test.d.ts +11 -0
  192. package/dist/src/__tests__/singleton-decorator.test.js +260 -0
  193. package/dist/src/__tests__/sourcemap.test.js +1 -1
  194. package/dist/src/__tests__/stdlib/advanced.test.d.ts +5 -0
  195. package/dist/src/__tests__/stdlib/advanced.test.js +301 -0
  196. package/dist/src/__tests__/stdlib/bigint.test.d.ts +4 -0
  197. package/dist/src/__tests__/stdlib/bigint.test.js +83 -0
  198. package/dist/src/__tests__/stdlib/bits.test.d.ts +4 -0
  199. package/dist/src/__tests__/stdlib/bits.test.js +96 -0
  200. package/dist/src/__tests__/stdlib/bossbar.test.d.ts +4 -0
  201. package/dist/src/__tests__/stdlib/bossbar.test.js +72 -0
  202. package/dist/src/__tests__/stdlib/color.test.d.ts +4 -0
  203. package/dist/src/__tests__/stdlib/color.test.js +84 -0
  204. package/dist/src/__tests__/stdlib/combat.test.d.ts +4 -0
  205. package/dist/src/__tests__/stdlib/combat.test.js +64 -0
  206. package/dist/src/__tests__/stdlib/cooldown.test.d.ts +4 -0
  207. package/dist/src/__tests__/stdlib/cooldown.test.js +64 -0
  208. package/dist/src/__tests__/stdlib/dialog.test.js +15 -7
  209. package/dist/src/__tests__/stdlib/ecs.test.d.ts +4 -0
  210. package/dist/src/__tests__/stdlib/ecs.test.js +81 -0
  211. package/dist/src/__tests__/stdlib/effects.test.d.ts +4 -0
  212. package/dist/src/__tests__/stdlib/effects.test.js +72 -0
  213. package/dist/src/__tests__/stdlib/events.test.d.ts +4 -0
  214. package/dist/src/__tests__/stdlib/events.test.js +55 -0
  215. package/dist/src/__tests__/stdlib/expr.test.d.ts +4 -0
  216. package/dist/src/__tests__/stdlib/expr.test.js +77 -0
  217. package/dist/src/__tests__/stdlib/fft.test.d.ts +4 -0
  218. package/dist/src/__tests__/stdlib/fft.test.js +82 -0
  219. package/dist/src/__tests__/stdlib/graph.test.d.ts +4 -0
  220. package/dist/src/__tests__/stdlib/graph.test.js +102 -0
  221. package/dist/src/__tests__/stdlib/interactions.test.d.ts +4 -0
  222. package/dist/src/__tests__/stdlib/interactions.test.js +60 -0
  223. package/dist/src/__tests__/stdlib/inventory.test.d.ts +4 -0
  224. package/dist/src/__tests__/stdlib/inventory.test.js +68 -0
  225. package/dist/src/__tests__/stdlib/linalg.test.d.ts +5 -0
  226. package/dist/src/__tests__/stdlib/linalg.test.js +78 -0
  227. package/dist/src/__tests__/stdlib/map.test.d.ts +1 -0
  228. package/dist/src/__tests__/stdlib/map.test.js +84 -0
  229. package/dist/src/__tests__/stdlib/math.test.js +19 -6
  230. package/dist/src/__tests__/stdlib/math_hp.test.d.ts +4 -0
  231. package/dist/src/__tests__/stdlib/math_hp.test.js +80 -0
  232. package/dist/src/__tests__/stdlib/mobs.test.d.ts +4 -0
  233. package/dist/src/__tests__/stdlib/mobs.test.js +61 -0
  234. package/dist/src/__tests__/stdlib/noise.test.d.ts +4 -0
  235. package/dist/src/__tests__/stdlib/noise.test.js +73 -0
  236. package/dist/src/__tests__/stdlib/ode.test.d.ts +4 -0
  237. package/dist/src/__tests__/stdlib/ode.test.js +68 -0
  238. package/dist/src/__tests__/stdlib/parabola.test.d.ts +4 -0
  239. package/dist/src/__tests__/stdlib/parabola.test.js +77 -0
  240. package/dist/src/__tests__/stdlib/particles.test.d.ts +4 -0
  241. package/dist/src/__tests__/stdlib/particles.test.js +68 -0
  242. package/dist/src/__tests__/stdlib/physics.test.d.ts +4 -0
  243. package/dist/src/__tests__/stdlib/physics.test.js +76 -0
  244. package/dist/src/__tests__/stdlib/player.test.d.ts +4 -0
  245. package/dist/src/__tests__/stdlib/player.test.js +64 -0
  246. package/dist/src/__tests__/stdlib/quaternion.test.d.ts +4 -0
  247. package/dist/src/__tests__/stdlib/quaternion.test.js +73 -0
  248. package/dist/src/__tests__/stdlib/queue.test.d.ts +1 -0
  249. package/dist/src/__tests__/stdlib/queue.test.js +97 -0
  250. package/dist/src/__tests__/stdlib/random.test.d.ts +4 -0
  251. package/dist/src/__tests__/stdlib/random.test.js +76 -0
  252. package/dist/src/__tests__/stdlib/result.test.d.ts +12 -0
  253. package/dist/src/__tests__/stdlib/result.test.js +329 -0
  254. package/dist/src/__tests__/stdlib/scheduler.test.js +19 -8
  255. package/dist/src/__tests__/stdlib/set_int.test.d.ts +1 -0
  256. package/dist/src/__tests__/stdlib/set_int.test.js +88 -0
  257. package/dist/src/__tests__/stdlib/sets.test.d.ts +6 -0
  258. package/dist/src/__tests__/stdlib/sets.test.js +60 -0
  259. package/dist/src/__tests__/stdlib/signal.test.d.ts +4 -0
  260. package/dist/src/__tests__/stdlib/signal.test.js +84 -0
  261. package/dist/src/__tests__/stdlib/spawn.test.d.ts +4 -0
  262. package/dist/src/__tests__/stdlib/spawn.test.js +68 -0
  263. package/dist/src/__tests__/stdlib/string.test.d.ts +12 -0
  264. package/dist/src/__tests__/stdlib/string.test.js +231 -0
  265. package/dist/src/__tests__/stdlib/strings.test.d.ts +4 -0
  266. package/dist/src/__tests__/stdlib/strings.test.js +83 -0
  267. package/dist/src/__tests__/stdlib/tags.test.d.ts +4 -0
  268. package/dist/src/__tests__/stdlib/tags.test.js +57 -0
  269. package/dist/src/__tests__/stdlib/teams.test.d.ts +4 -0
  270. package/dist/src/__tests__/stdlib/teams.test.js +72 -0
  271. package/dist/src/__tests__/stdlib/timer.test.d.ts +4 -0
  272. package/dist/src/__tests__/stdlib/timer.test.js +79 -0
  273. package/dist/src/__tests__/stdlib/vec.test.d.ts +5 -0
  274. package/dist/src/__tests__/stdlib/vec.test.js +94 -0
  275. package/dist/src/__tests__/stdlib/world.test.d.ts +4 -0
  276. package/dist/src/__tests__/stdlib/world.test.js +72 -0
  277. package/dist/src/__tests__/struct-display.test.d.ts +1 -0
  278. package/dist/src/__tests__/struct-display.test.js +64 -0
  279. package/dist/src/__tests__/test-framework/runner.test.d.ts +10 -0
  280. package/dist/src/__tests__/test-framework/runner.test.js +193 -0
  281. package/dist/src/__tests__/tuner/adapters.test.d.ts +14 -0
  282. package/dist/src/__tests__/tuner/adapters.test.js +194 -0
  283. package/dist/src/__tests__/tuner/simulator-extra.test.d.ts +4 -0
  284. package/dist/src/__tests__/tuner/simulator-extra.test.js +193 -0
  285. package/dist/src/__tests__/typechecker-coverage.test.d.ts +30 -0
  286. package/dist/src/__tests__/typechecker-coverage.test.js +627 -0
  287. package/dist/src/__tests__/typechecker.test.js +3 -3
  288. package/dist/src/__tests__/watch-decorator.test.d.ts +1 -0
  289. package/dist/src/__tests__/watch-decorator.test.js +54 -0
  290. package/dist/src/ast/types.d.ts +102 -3
  291. package/dist/src/cache/incremental.d.ts +13 -14
  292. package/dist/src/cache/incremental.js +106 -89
  293. package/dist/src/cache/index.d.ts +8 -2
  294. package/dist/src/cache/index.js +18 -6
  295. package/dist/src/cli.d.ts +1 -0
  296. package/dist/src/cli.js +466 -17
  297. package/dist/src/config/project-config.d.ts +29 -0
  298. package/dist/src/config/project-config.js +180 -0
  299. package/dist/src/diagnostics/index.d.ts +9 -0
  300. package/dist/src/diagnostics/index.js +18 -1
  301. package/dist/src/emit/compile.d.ts +10 -0
  302. package/dist/src/emit/compile.js +395 -50
  303. package/dist/src/emit/index.d.ts +40 -0
  304. package/dist/src/emit/index.js +307 -14
  305. package/dist/src/emit/modules.js +21 -3
  306. package/dist/src/emit/sourcemap.d.ts +23 -27
  307. package/dist/src/emit/sourcemap.js +52 -30
  308. package/dist/src/formatter/index.js +33 -8
  309. package/dist/src/hir/deprecated.d.ts +13 -0
  310. package/dist/src/hir/deprecated.js +218 -0
  311. package/dist/src/hir/lower.js +114 -8
  312. package/dist/src/hir/monomorphize.js +22 -2
  313. package/dist/src/hir/types.d.ts +65 -1
  314. package/dist/src/index.d.ts +6 -0
  315. package/dist/src/index.js +18 -3
  316. package/dist/src/lexer/index.d.ts +2 -1
  317. package/dist/src/lexer/index.js +39 -3
  318. package/dist/src/lint/index.d.ts +45 -0
  319. package/dist/src/lint/index.js +930 -0
  320. package/dist/src/lir/lower.js +29 -2
  321. package/dist/src/lir/types.d.ts +2 -0
  322. package/dist/src/lsp/server.js +92 -5
  323. package/dist/src/mir/lower.js +775 -34
  324. package/dist/src/mir/macro.js +36 -2
  325. package/dist/src/mir/types.d.ts +12 -0
  326. package/dist/src/mir/verify.js +9 -0
  327. package/dist/src/optimizer/auto-inline.d.ts +2 -0
  328. package/dist/src/optimizer/auto-inline.js +67 -0
  329. package/dist/src/optimizer/cse.d.ts +20 -0
  330. package/dist/src/optimizer/cse.js +234 -0
  331. package/dist/src/optimizer/inline.d.ts +26 -0
  332. package/dist/src/optimizer/inline.js +286 -0
  333. package/dist/src/optimizer/interprocedural.js +4 -0
  334. package/dist/src/optimizer/licm.d.ts +32 -0
  335. package/dist/src/optimizer/licm.js +371 -0
  336. package/dist/src/optimizer/pipeline.js +12 -2
  337. package/dist/src/optimizer/strength_reduction.d.ts +15 -0
  338. package/dist/src/optimizer/strength_reduction.js +90 -0
  339. package/dist/src/optimizer/tco.d.ts +53 -0
  340. package/dist/src/optimizer/tco.js +238 -0
  341. package/dist/src/parser/index.d.ts +32 -0
  342. package/dist/src/parser/index.js +421 -59
  343. package/dist/src/repl-server.d.ts +13 -0
  344. package/dist/src/repl-server.js +127 -0
  345. package/dist/src/structs/expand.d.ts +15 -0
  346. package/dist/src/structs/expand.js +46 -0
  347. package/dist/src/testing/runner.d.ts +40 -0
  348. package/dist/src/testing/runner.js +237 -0
  349. package/dist/src/typechecker/index.d.ts +3 -0
  350. package/dist/src/typechecker/index.js +254 -9
  351. package/dist/tsconfig.tsbuildinfo +1 -1
  352. package/doc-drafts/redscript-docs/docs/en/stdlib/graph.md +104 -0
  353. package/doc-drafts/redscript-docs/docs/en/stdlib/parabola.md +113 -0
  354. package/doc-drafts/redscript-docs/docs/en/stdlib/pathfind.md +104 -0
  355. package/doc-drafts/redscript-docs/docs/en/stdlib/physics.md +134 -0
  356. package/doc-drafts/redscript-docs/docs/en/stdlib/quaternion.md +135 -0
  357. package/doc-drafts/redscript-docs/docs/zh/stdlib/graph.md +104 -0
  358. package/doc-drafts/redscript-docs/docs/zh/stdlib/parabola.md +113 -0
  359. package/doc-drafts/redscript-docs/docs/zh/stdlib/pathfind.md +104 -0
  360. package/doc-drafts/redscript-docs/docs/zh/stdlib/physics.md +134 -0
  361. package/doc-drafts/redscript-docs/docs/zh/stdlib/quaternion.md +135 -0
  362. package/docs/stdlib/result.md +156 -0
  363. package/docs/stdlib/result.zh.md +156 -0
  364. package/editors/vscode/fixtures/test.mcrs +7 -0
  365. package/editors/vscode/out/extension.js +2095 -225
  366. package/editors/vscode/out/lsp-server.js +519 -51
  367. package/editors/vscode/package-lock.json +9 -4
  368. package/editors/vscode/package.json +1 -1
  369. package/examples/display-demo.mcrs +64 -0
  370. package/examples/game/racing.mcrs +301 -0
  371. package/examples/game/tower_defense.mcrs +311 -0
  372. package/examples/math/physics_sim.mcrs +322 -0
  373. package/examples/rpg/boss_fight.mcrs +313 -0
  374. package/examples/rpg/health_system.mcrs +237 -0
  375. package/examples/rpg/inventory.mcrs +265 -0
  376. package/examples/util/debug_hud.mcrs +279 -0
  377. package/jest.config.js +10 -0
  378. package/package.json +12 -3
  379. package/playground/index.html +823 -0
  380. package/scripts/gen-docs.ts +533 -0
  381. package/scripts/update-redscript-docs-stdlib.sh +770 -0
  382. package/src/__tests__/budget.test.ts +5 -0
  383. package/src/__tests__/cache/cache-behavior.test.ts +480 -0
  384. package/src/__tests__/cache-extra.test.ts +199 -0
  385. package/src/__tests__/cli-docs.test.ts +77 -0
  386. package/src/__tests__/cli-init.test.ts +91 -0
  387. package/src/__tests__/cli-publish.test.ts +190 -0
  388. package/src/__tests__/cli.test.ts +117 -1
  389. package/src/__tests__/compile-preprocess.test.ts +366 -0
  390. package/src/__tests__/compiler/break-stmt.test.ts +66 -0
  391. package/src/__tests__/compiler/const-decl.test.ts +141 -0
  392. package/src/__tests__/compiler/continue-stmt.test.ts +81 -0
  393. package/src/__tests__/compiler/coroutine-extended.test.ts +723 -0
  394. package/src/__tests__/compiler/deprecated.test.ts +305 -0
  395. package/src/__tests__/compiler/do-while.test.ts +130 -0
  396. package/src/__tests__/compiler/enum-payload.test.ts +299 -0
  397. package/src/__tests__/compiler/interface.test.ts +287 -0
  398. package/src/__tests__/compiler/labeled-loops.test.ts +279 -0
  399. package/src/__tests__/compiler/match-string.test.ts +45 -0
  400. package/src/__tests__/compiler/memoize.test.ts +126 -0
  401. package/src/__tests__/compiler/method-chain.test.ts +121 -0
  402. package/src/__tests__/compiler/module-import.test.ts +240 -0
  403. package/src/__tests__/compiler/option-extensions.test.ts +207 -0
  404. package/src/__tests__/compiler/profile-decorator.test.ts +79 -0
  405. package/src/__tests__/compiler/string-advanced.test.ts +310 -0
  406. package/src/__tests__/compiler/struct-extends.test.ts +109 -0
  407. package/src/__tests__/compiler/throttle-retry.test.ts +191 -0
  408. package/src/__tests__/compiler/tuple-type.test.ts +263 -0
  409. package/src/__tests__/compiler/watch-decorator.test.ts +72 -0
  410. package/src/__tests__/config/project-config.test.ts +181 -0
  411. package/src/__tests__/config-decorator.test.ts +157 -0
  412. package/src/__tests__/diagnostics-extra.test.ts +155 -0
  413. package/src/__tests__/emit/compile-branches.test.ts +135 -0
  414. package/src/__tests__/emit/compile-coverage.test.ts +696 -0
  415. package/src/__tests__/emit/compile-extra-branches.test.ts +228 -0
  416. package/src/__tests__/emit/compile-mocked-branches.test.ts +249 -0
  417. package/src/__tests__/emit/compile.test.ts +6 -1
  418. package/src/__tests__/emit/execute-chain.test.ts +114 -0
  419. package/src/__tests__/emit/index.test.ts +2 -1
  420. package/src/__tests__/emit/modules-branches.test.ts +90 -0
  421. package/src/__tests__/emit/modules-coverage.test.ts +241 -0
  422. package/src/__tests__/emit/modules-errors.test.ts +192 -0
  423. package/src/__tests__/emit/modules-rewrite.test.ts +232 -0
  424. package/src/__tests__/emit/source-map.test.ts +152 -0
  425. package/src/__tests__/enum.test.ts +9 -4
  426. package/src/__tests__/error-recovery.test.ts +226 -0
  427. package/src/__tests__/events-types-extra.test.ts +110 -0
  428. package/src/__tests__/events-types.test.ts +66 -0
  429. package/src/__tests__/formatter.test.ts +15 -5
  430. package/src/__tests__/generics.test.ts +16 -9
  431. package/src/__tests__/hir/lower-extra.test.ts +151 -0
  432. package/src/__tests__/hir/monomorphize-coverage.test.ts +432 -0
  433. package/src/__tests__/hir/monomorphize-extra.test.ts +220 -0
  434. package/src/__tests__/hir/monomorphize-extra2.test.ts +350 -0
  435. package/src/__tests__/impl.test.ts +12 -8
  436. package/src/__tests__/incremental.test.ts +10 -2
  437. package/src/__tests__/index-extra.test.ts +79 -0
  438. package/src/__tests__/lexer.test.ts +2 -2
  439. package/src/__tests__/lint/hir-coverage.test.ts +1716 -0
  440. package/src/__tests__/lint/rules-coverage.test.ts +598 -0
  441. package/src/__tests__/lint/rules.test.ts +230 -0
  442. package/src/__tests__/lir/lower.test.ts +33 -0
  443. package/src/__tests__/lir/verify.test.ts +33 -0
  444. package/src/__tests__/lsp/completion.test.ts +687 -0
  445. package/src/__tests__/lsp/definition.test.ts +499 -0
  446. package/src/__tests__/lsp/diagnostics.test.ts +108 -0
  447. package/src/__tests__/lsp/hover-docs.test.ts +222 -0
  448. package/src/__tests__/lsp.test.ts +4 -1
  449. package/src/__tests__/mc-integration/item-entity-events.test.ts +5 -0
  450. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +5 -0
  451. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1105 -0
  452. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1366 -0
  453. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1245 -0
  454. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +755 -0
  455. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +771 -0
  456. package/src/__tests__/mc-integration/stdlib-coverage.test.ts +5 -0
  457. package/src/__tests__/mc-integration/syntax-coverage.test.ts +5 -0
  458. package/src/__tests__/mc-validator-coverage.test.ts +325 -0
  459. package/src/__tests__/mc-validator-extra.test.ts +252 -0
  460. package/src/__tests__/mir/lower-extra.test.ts +402 -0
  461. package/src/__tests__/mir/lower-extra2.test.ts +348 -0
  462. package/src/__tests__/mir/lower-extra3.test.ts +277 -0
  463. package/src/__tests__/mir/lower-extra4.test.ts +636 -0
  464. package/src/__tests__/mir/lower-extra5.test.ts +612 -0
  465. package/src/__tests__/mir/lower-extra6.test.ts +520 -0
  466. package/src/__tests__/mir/lower-extra7.test.ts +1045 -0
  467. package/src/__tests__/mir/lower-extra8.test.ts +704 -0
  468. package/src/__tests__/mir/lower-extra9.test.ts +821 -0
  469. package/src/__tests__/optimizer/auto-inline.test.ts +206 -0
  470. package/src/__tests__/optimizer/cse.test.ts +195 -0
  471. package/src/__tests__/optimizer/inline_fn.test.ts +263 -0
  472. package/src/__tests__/optimizer/licm.test.ts +358 -0
  473. package/src/__tests__/optimizer/nbt-coalesce.test.ts +147 -0
  474. package/src/__tests__/optimizer/optimizer-extended.test.ts +1081 -0
  475. package/src/__tests__/optimizer/scoreboard-batch.test.ts +141 -0
  476. package/src/__tests__/optimizer/strength-reduction.test.ts +111 -0
  477. package/src/__tests__/optimizer/tco-coverage.test.ts +309 -0
  478. package/src/__tests__/optimizer/tco.test.ts +238 -0
  479. package/src/__tests__/option.test.ts +14 -7
  480. package/src/__tests__/parser-coverage.test.ts +576 -0
  481. package/src/__tests__/parser-extra.test.ts +531 -0
  482. package/src/__tests__/parser.test.ts +14 -0
  483. package/src/__tests__/repl-extra.test.ts +195 -0
  484. package/src/__tests__/repl-server-extra.test.ts +150 -0
  485. package/src/__tests__/repl-server.test.ts +122 -0
  486. package/src/__tests__/runtime-extra.test.ts +862 -0
  487. package/src/__tests__/singleton-decorator.test.ts +285 -0
  488. package/src/__tests__/sourcemap.test.ts +1 -1
  489. package/src/__tests__/stdlib/advanced.test.ts +312 -0
  490. package/src/__tests__/stdlib/bigint.test.ts +57 -0
  491. package/src/__tests__/stdlib/bits.test.ts +75 -0
  492. package/src/__tests__/stdlib/bossbar.test.ts +45 -0
  493. package/src/__tests__/stdlib/color.test.ts +60 -0
  494. package/src/__tests__/stdlib/combat.test.ts +35 -0
  495. package/src/__tests__/stdlib/cooldown.test.ts +35 -0
  496. package/src/__tests__/stdlib/dialog.test.ts +14 -6
  497. package/src/__tests__/stdlib/ecs.test.ts +54 -0
  498. package/src/__tests__/stdlib/effects.test.ts +45 -0
  499. package/src/__tests__/stdlib/events.test.ts +23 -0
  500. package/src/__tests__/stdlib/expr.test.ts +48 -0
  501. package/src/__tests__/stdlib/fft.test.ts +54 -0
  502. package/src/__tests__/stdlib/graph.test.ts +77 -0
  503. package/src/__tests__/stdlib/interactions.test.ts +30 -0
  504. package/src/__tests__/stdlib/inventory.test.ts +40 -0
  505. package/src/__tests__/stdlib/linalg.test.ts +52 -0
  506. package/src/__tests__/stdlib/map.test.ts +55 -0
  507. package/src/__tests__/stdlib/math.test.ts +19 -5
  508. package/src/__tests__/stdlib/math_hp.test.ts +55 -0
  509. package/src/__tests__/stdlib/mobs.test.ts +40 -0
  510. package/src/__tests__/stdlib/noise.test.ts +46 -0
  511. package/src/__tests__/stdlib/ode.test.ts +40 -0
  512. package/src/__tests__/stdlib/parabola.test.ts +51 -0
  513. package/src/__tests__/stdlib/particles.test.ts +40 -0
  514. package/src/__tests__/stdlib/physics.test.ts +50 -0
  515. package/src/__tests__/stdlib/player.test.ts +35 -0
  516. package/src/__tests__/stdlib/quaternion.test.ts +46 -0
  517. package/src/__tests__/stdlib/queue.test.ts +73 -0
  518. package/src/__tests__/stdlib/random.test.ts +50 -0
  519. package/src/__tests__/stdlib/result.test.ts +326 -0
  520. package/src/__tests__/stdlib/scheduler.test.ts +18 -7
  521. package/src/__tests__/stdlib/set_int.test.ts +62 -0
  522. package/src/__tests__/stdlib/sets.test.ts +28 -0
  523. package/src/__tests__/stdlib/signal.test.ts +60 -0
  524. package/src/__tests__/stdlib/spawn.test.ts +40 -0
  525. package/src/__tests__/stdlib/string.test.ts +224 -0
  526. package/src/__tests__/stdlib/strings.test.ts +55 -0
  527. package/src/__tests__/stdlib/tags.test.ts +32 -0
  528. package/src/__tests__/stdlib/teams.test.ts +45 -0
  529. package/src/__tests__/stdlib/timer.test.ts +53 -0
  530. package/src/__tests__/stdlib/vec.test.ts +72 -0
  531. package/src/__tests__/stdlib/world.test.ts +45 -0
  532. package/src/__tests__/struct-display.test.ts +69 -0
  533. package/src/__tests__/test-framework/runner.test.ts +208 -0
  534. package/src/__tests__/tuner/adapters.test.ts +232 -0
  535. package/src/__tests__/tuner/simulator-extra.test.ts +222 -0
  536. package/src/__tests__/tuple.test.ts +11 -4
  537. package/src/__tests__/typechecker-coverage.test.ts +671 -0
  538. package/src/__tests__/typechecker.test.ts +4 -3
  539. package/src/__tests__/watch-decorator.test.ts +59 -0
  540. package/src/ast/types.ts +65 -3
  541. package/src/cache/incremental.ts +128 -99
  542. package/src/cache/index.ts +35 -8
  543. package/src/cli.ts +538 -29
  544. package/src/config/project-config.ts +176 -0
  545. package/src/diagnostics/index.ts +22 -0
  546. package/src/docs.ts +98 -0
  547. package/src/emit/compile.ts +408 -51
  548. package/src/emit/index.ts +366 -18
  549. package/src/emit/modules.ts +19 -3
  550. package/src/emit/sourcemap.ts +64 -43
  551. package/src/formatter/index.ts +35 -8
  552. package/src/hir/deprecated.ts +212 -0
  553. package/src/hir/lower.ts +128 -8
  554. package/src/hir/monomorphize.ts +24 -2
  555. package/src/hir/types.ts +26 -1
  556. package/src/index.ts +23 -3
  557. package/src/lexer/index.ts +45 -6
  558. package/src/lint/index.ts +922 -0
  559. package/src/lir/lower.ts +30 -2
  560. package/src/lir/types.ts +4 -0
  561. package/src/lsp/server.ts +100 -1
  562. package/src/mir/lower.ts +785 -40
  563. package/src/mir/macro.ts +30 -2
  564. package/src/mir/types.ts +13 -0
  565. package/src/mir/verify.ts +10 -2
  566. package/src/optimizer/auto-inline.ts +86 -0
  567. package/src/optimizer/copy_prop.ts +2 -2
  568. package/src/optimizer/coroutine.ts +3 -3
  569. package/src/optimizer/cse.ts +205 -0
  570. package/src/optimizer/dce.ts +2 -2
  571. package/src/optimizer/inline.ts +335 -0
  572. package/src/optimizer/interprocedural.ts +5 -1
  573. package/src/optimizer/licm.ts +454 -0
  574. package/src/optimizer/nbt-coalesce.ts +109 -0
  575. package/src/optimizer/pipeline.ts +16 -2
  576. package/src/optimizer/scoreboard-batch.ts +52 -0
  577. package/src/optimizer/strength_reduction.ts +95 -0
  578. package/src/optimizer/tco.ts +267 -0
  579. package/src/optimizer/unroll.ts +2 -2
  580. package/src/parser/index.ts +426 -53
  581. package/src/repl-server.ts +102 -0
  582. package/src/stdlib/advanced.mcrs +271 -101
  583. package/src/stdlib/bigint.mcrs +97 -11
  584. package/src/stdlib/bits.mcrs +75 -12
  585. package/src/stdlib/bossbar.mcrs +37 -8
  586. package/src/stdlib/calculus.mcrs +82 -26
  587. package/src/stdlib/color.mcrs +98 -16
  588. package/src/stdlib/combat.mcrs +23 -5
  589. package/src/stdlib/cooldown.mcrs +19 -0
  590. package/src/stdlib/dialog.mcrs +45 -7
  591. package/src/stdlib/easing.mcrs +132 -12
  592. package/src/stdlib/ecs.mcrs +142 -25
  593. package/src/stdlib/effects.mcrs +88 -12
  594. package/src/stdlib/events.mcrs +21 -2
  595. package/src/stdlib/expr.mcrs +18 -3
  596. package/src/stdlib/fft.mcrs +66 -56
  597. package/src/stdlib/geometry.mcrs +137 -39
  598. package/src/stdlib/graph.mcrs +73 -0
  599. package/src/stdlib/heap.mcrs +49 -8
  600. package/src/stdlib/i18n/zh.yaml +2891 -0
  601. package/src/stdlib/interactions.mcrs +43 -20
  602. package/src/stdlib/inventory.mcrs +14 -3
  603. package/src/stdlib/linalg.mcrs +185 -30
  604. package/src/stdlib/list.mcrs +168 -18
  605. package/src/stdlib/map.mcrs +112 -0
  606. package/src/stdlib/math.mcrs +68 -18
  607. package/src/stdlib/math_hp.mcrs +124 -33
  608. package/src/stdlib/matrix.mcrs +133 -20
  609. package/src/stdlib/mobs.mcrs +87 -0
  610. package/src/stdlib/noise.mcrs +65 -21
  611. package/src/stdlib/ode.mcrs +96 -0
  612. package/src/stdlib/parabola.mcrs +104 -29
  613. package/src/stdlib/particles.mcrs +78 -21
  614. package/src/stdlib/pathfind.mcrs +89 -35
  615. package/src/stdlib/physics.mcrs +134 -26
  616. package/src/stdlib/player.mcrs +18 -0
  617. package/src/stdlib/quaternion.mcrs +213 -9
  618. package/src/stdlib/queue.mcrs +123 -0
  619. package/src/stdlib/random.mcrs +63 -18
  620. package/src/stdlib/result.mcrs +111 -0
  621. package/src/stdlib/scheduler.mcrs +59 -10
  622. package/src/stdlib/set_int.mcrs +240 -0
  623. package/src/stdlib/sets.mcrs +49 -19
  624. package/src/stdlib/signal.mcrs +151 -79
  625. package/src/stdlib/sort.mcrs +44 -24
  626. package/src/stdlib/spawn.mcrs +30 -7
  627. package/src/stdlib/state.mcrs +40 -5
  628. package/src/stdlib/strings.mcrs +131 -3
  629. package/src/stdlib/tags.mcrs +2 -2
  630. package/src/stdlib/teams.mcrs +22 -10
  631. package/src/stdlib/timer.mcrs +36 -6
  632. package/src/stdlib/vec.mcrs +44 -9
  633. package/src/stdlib/world.mcrs +57 -25
  634. package/src/structs/expand.ts +64 -0
  635. package/src/testing/runner.ts +271 -0
  636. package/src/typechecker/index.ts +273 -9
@@ -0,0 +1,993 @@
1
+ "use strict";
2
+ /**
3
+ * Extended optimizer coverage tests.
4
+ *
5
+ * Targets uncovered branches in:
6
+ * - coroutine.ts (fnContainsMacroCalls paths, macro-skip warning)
7
+ * - unroll.ts (two-step latch pattern, initializesTo0 copy path, copy-init path)
8
+ * - interprocedural.ts (call_macro isSelfRecursive, param mismatch guard, rewriteCallSites guards)
9
+ * - lir/const_imm.ts (score_swap / store_nbt_to_score / macro_line read slots, extractSlotsFromRaw)
10
+ * - block_merge.ts (branch terminator — no merge, target === fn.entry guard)
11
+ * - constant_fold.ts (evalCmp default branch)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ // ---------------------------------------------------------------------------
15
+ // Imports
16
+ // ---------------------------------------------------------------------------
17
+ const coroutine_1 = require("../../optimizer/coroutine");
18
+ const unroll_1 = require("../../optimizer/unroll");
19
+ const interprocedural_1 = require("../../optimizer/interprocedural");
20
+ const const_imm_1 = require("../../optimizer/lir/const_imm");
21
+ const block_merge_1 = require("../../optimizer/block_merge");
22
+ const constant_fold_1 = require("../../optimizer/constant_fold");
23
+ // ---------------------------------------------------------------------------
24
+ // MIR helpers
25
+ // ---------------------------------------------------------------------------
26
+ const c = (v) => ({ kind: 'const', value: v });
27
+ const t = (n) => ({ kind: 'temp', name: n });
28
+ function mirBlock(id, instrs, term, preds = []) {
29
+ return { id, instrs, term, preds };
30
+ }
31
+ function mirFn(name, params, blocks, isMacro = false) {
32
+ return {
33
+ name,
34
+ params: params.map(p => ({ name: p, isMacroParam: false })),
35
+ blocks,
36
+ entry: 'entry',
37
+ isMacro,
38
+ };
39
+ }
40
+ function mirMod(functions) {
41
+ return { functions, namespace: 'test', objective: '__test' };
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // LIR helpers
45
+ // ---------------------------------------------------------------------------
46
+ const obj = '__test';
47
+ function slot(player) { return { player, obj }; }
48
+ function lirFn(instructions) {
49
+ return { name: 'test', instructions, isMacro: false, macroParams: [] };
50
+ }
51
+ // ===========================================================================
52
+ // 1. coroutine.ts — uncovered branches
53
+ // ===========================================================================
54
+ describe('coroutine — fnContainsMacroCalls skip path', () => {
55
+ function mkBlock(id, instrs, term) {
56
+ return { id, instrs, term, preds: [] };
57
+ }
58
+ test('skips transform and emits warning when function has call_macro', () => {
59
+ const fn = {
60
+ name: 'test:worker',
61
+ params: [],
62
+ blocks: [
63
+ mkBlock('entry', [
64
+ // call_macro instruction — should trigger the skip path
65
+ { kind: 'call_macro', dst: null, fn: 'test:mfn', args: [] },
66
+ ], { kind: 'return', value: null }),
67
+ ],
68
+ entry: 'entry',
69
+ isMacro: false,
70
+ };
71
+ const mod = mirMod([fn]);
72
+ const info = { fnName: 'test:worker', batch: 10 };
73
+ const result = (0, coroutine_1.coroutineTransform)(mod, [info]);
74
+ // Function should be kept unchanged (not transformed)
75
+ expect(result.module.functions).toHaveLength(1);
76
+ expect(result.module.functions[0].name).toBe('test:worker');
77
+ // No tick functions generated
78
+ expect(result.generatedTickFunctions).toHaveLength(0);
79
+ // Warning should be emitted
80
+ expect(result.warnings).toHaveLength(1);
81
+ expect(result.warnings[0]).toContain('test:worker');
82
+ expect(result.warnings[0]).toContain('@coroutine');
83
+ });
84
+ test('skips transform when function has __raw: call with ${interpolation}', () => {
85
+ const fn = {
86
+ name: 'test:interpolated',
87
+ params: [],
88
+ blocks: [
89
+ mkBlock('entry', [
90
+ // A raw call with variable interpolation
91
+ { kind: 'call', dst: null, fn: '__raw:say ${name}', args: [] },
92
+ ], { kind: 'return', value: null }),
93
+ ],
94
+ entry: 'entry',
95
+ isMacro: false,
96
+ };
97
+ const mod = mirMod([fn]);
98
+ const info = { fnName: 'test:interpolated', batch: 10 };
99
+ const result = (0, coroutine_1.coroutineTransform)(mod, [info]);
100
+ expect(result.module.functions).toHaveLength(1);
101
+ expect(result.generatedTickFunctions).toHaveLength(0);
102
+ expect(result.warnings).toHaveLength(1);
103
+ });
104
+ test('skips transform when function has __raw:\\x01 (builtin with macro)', () => {
105
+ const fn = {
106
+ name: 'test:builtin_macro',
107
+ params: [],
108
+ blocks: [
109
+ mkBlock('entry', [
110
+ { kind: 'call', dst: null, fn: '__raw:\x01particle dust', args: [] },
111
+ ], { kind: 'return', value: null }),
112
+ ],
113
+ entry: 'entry',
114
+ isMacro: false,
115
+ };
116
+ const mod = mirMod([fn]);
117
+ const info = { fnName: 'test:builtin_macro', batch: 5 };
118
+ const result = (0, coroutine_1.coroutineTransform)(mod, [info]);
119
+ expect(result.generatedTickFunctions).toHaveLength(0);
120
+ expect(result.warnings).toHaveLength(1);
121
+ });
122
+ test('transforms when __raw: call has no interpolation', () => {
123
+ // __raw: without ${ or \x01 should NOT block the transform
124
+ const fn = {
125
+ name: 'test:plain_raw',
126
+ params: [],
127
+ blocks: [
128
+ mkBlock('entry', [
129
+ { kind: 'call', dst: null, fn: '__raw:say hello', args: [] },
130
+ ], { kind: 'return', value: null }),
131
+ ],
132
+ entry: 'entry',
133
+ isMacro: false,
134
+ };
135
+ const mod = mirMod([fn]);
136
+ const info = { fnName: 'test:plain_raw', batch: 5 };
137
+ const result = (0, coroutine_1.coroutineTransform)(mod, [info]);
138
+ // Should be transformed (no warning)
139
+ expect(result.warnings).toHaveLength(0);
140
+ expect(result.generatedTickFunctions).toHaveLength(1);
141
+ });
142
+ test('call_macro in terminator position also triggers skip', () => {
143
+ // The loop in fnContainsMacroCalls iterates [...block.instrs, block.term]
144
+ // so a call_macro as the terminator should also trigger the skip
145
+ const fn = {
146
+ name: 'test:macro_term',
147
+ params: [],
148
+ blocks: [
149
+ {
150
+ id: 'entry',
151
+ instrs: [],
152
+ // call_macro as terminator
153
+ term: { kind: 'call_macro', dst: null, fn: 'test:m', args: [] },
154
+ preds: [],
155
+ },
156
+ ],
157
+ entry: 'entry',
158
+ isMacro: false,
159
+ };
160
+ const mod = mirMod([fn]);
161
+ const info = { fnName: 'test:macro_term', batch: 5 };
162
+ const result = (0, coroutine_1.coroutineTransform)(mod, [info]);
163
+ expect(result.warnings).toHaveLength(1);
164
+ expect(result.generatedTickFunctions).toHaveLength(0);
165
+ });
166
+ });
167
+ // ===========================================================================
168
+ // 2. unroll.ts — uncovered branches
169
+ // ===========================================================================
170
+ describe('loopUnroll — uncovered branches', () => {
171
+ function buildLoopWithBlocks(blocks) {
172
+ return { name: 'test', params: [], blocks, entry: 'entry', isMacro: false };
173
+ }
174
+ test('two-step latch pattern: add t_tmp i 1; copy i t_tmp', () => {
175
+ // Pattern 2 in latchIncrementsBy1
176
+ const fn = buildLoopWithBlocks([
177
+ mirBlock('entry', [
178
+ { kind: 'const', dst: 'i', value: 0 },
179
+ ], { kind: 'jump', target: 'loop_header_0' }),
180
+ mirBlock('loop_header_0', [
181
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
182
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
183
+ mirBlock('loop_body_0', [
184
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
185
+ ], { kind: 'jump', target: 'loop_latch_0' }),
186
+ mirBlock('loop_latch_0', [
187
+ // Two-step increment: t_tmp = i + 1; i = t_tmp
188
+ { kind: 'add', dst: 't_tmp', a: t('i'), b: c(1) },
189
+ { kind: 'copy', dst: 'i', src: t('t_tmp') },
190
+ ], { kind: 'jump', target: 'loop_header_0' }),
191
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
192
+ ]);
193
+ const result = (0, unroll_1.loopUnroll)(fn);
194
+ // Should have unrolled — loop_header gone
195
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
196
+ const entry = result.blocks.find(b => b.id === 'entry');
197
+ const calls = entry.instrs.filter(i => i.kind === 'call');
198
+ expect(calls).toHaveLength(3);
199
+ // i substituted as 0, 1, 2
200
+ const callInstrs = calls;
201
+ expect(callInstrs[0].args[0]).toEqual(c(0));
202
+ expect(callInstrs[1].args[0]).toEqual(c(1));
203
+ expect(callInstrs[2].args[0]).toEqual(c(2));
204
+ });
205
+ test('initializesTo0 via copy instruction (copy i, const 0)', () => {
206
+ // Tests the `copy` branch in initializesTo0
207
+ const fn = buildLoopWithBlocks([
208
+ mirBlock('entry', [
209
+ // Initialize via copy of const 0 instead of direct const
210
+ { kind: 'copy', dst: 'i', src: c(0) },
211
+ ], { kind: 'jump', target: 'loop_header_0' }),
212
+ mirBlock('loop_header_0', [
213
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(2) },
214
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
215
+ mirBlock('loop_body_0', [
216
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
217
+ ], { kind: 'jump', target: 'loop_latch_0' }),
218
+ mirBlock('loop_latch_0', [
219
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
220
+ ], { kind: 'jump', target: 'loop_header_0' }),
221
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
222
+ ]);
223
+ const result = (0, unroll_1.loopUnroll)(fn);
224
+ // Should unroll since copy i, 0 is recognized as init-to-0
225
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
226
+ const entry = result.blocks.find(b => b.id === 'entry');
227
+ const calls = entry.instrs.filter(i => i.kind === 'call');
228
+ expect(calls).toHaveLength(2);
229
+ });
230
+ test('initializesTo0 copy with non-zero const does not unroll', () => {
231
+ // copy i, const 5 — not init to 0
232
+ const fn = buildLoopWithBlocks([
233
+ mirBlock('entry', [
234
+ { kind: 'copy', dst: 'i', src: c(5) },
235
+ ], { kind: 'jump', target: 'loop_header_0' }),
236
+ mirBlock('loop_header_0', [
237
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(8) },
238
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
239
+ mirBlock('loop_body_0', [
240
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
241
+ ], { kind: 'jump', target: 'loop_latch_0' }),
242
+ mirBlock('loop_latch_0', [
243
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
244
+ ], { kind: 'jump', target: 'loop_header_0' }),
245
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
246
+ ]);
247
+ const result = (0, unroll_1.loopUnroll)(fn);
248
+ expect(result).toBe(fn); // unchanged
249
+ });
250
+ test('latch with wrong temp in two-step does not unroll', () => {
251
+ // Pattern 2 but copy is from a different temp than the add destination
252
+ const fn = buildLoopWithBlocks([
253
+ mirBlock('entry', [
254
+ { kind: 'const', dst: 'i', value: 0 },
255
+ ], { kind: 'jump', target: 'loop_header_0' }),
256
+ mirBlock('loop_header_0', [
257
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
258
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
259
+ mirBlock('loop_body_0', [
260
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
261
+ ], { kind: 'jump', target: 'loop_latch_0' }),
262
+ mirBlock('loop_latch_0', [
263
+ // Two-step but the copy sources from a different unrelated temp
264
+ { kind: 'add', dst: 't_tmp', a: t('i'), b: c(1) },
265
+ { kind: 'copy', dst: 'i', src: t('other_tmp') }, // wrong source
266
+ ], { kind: 'jump', target: 'loop_header_0' }),
267
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
268
+ ]);
269
+ const result = (0, unroll_1.loopUnroll)(fn);
270
+ // Should NOT unroll — two-step mismatch
271
+ expect(result).toBe(fn);
272
+ });
273
+ test('multiple loops in function: unrolls both iteratively', () => {
274
+ // Two sequential loops, both N=2 — fixpoint iteration handles both
275
+ const fn = buildLoopWithBlocks([
276
+ mirBlock('entry', [
277
+ { kind: 'const', dst: 'i', value: 0 },
278
+ ], { kind: 'jump', target: 'loop_header_0' }),
279
+ mirBlock('loop_header_0', [
280
+ { kind: 'cmp', dst: 't0', op: 'lt', a: t('i'), b: c(2) },
281
+ ], { kind: 'branch', cond: t('t0'), then: 'loop_body_0', else: 'mid' }),
282
+ mirBlock('loop_body_0', [
283
+ { kind: 'call', dst: null, fn: 'test:a', args: [t('i')] },
284
+ ], { kind: 'jump', target: 'loop_latch_0' }),
285
+ mirBlock('loop_latch_0', [
286
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
287
+ ], { kind: 'jump', target: 'loop_header_0' }),
288
+ mirBlock('mid', [
289
+ { kind: 'const', dst: 'j', value: 0 },
290
+ ], { kind: 'jump', target: 'loop_header_1' }),
291
+ mirBlock('loop_header_1', [
292
+ { kind: 'cmp', dst: 't1', op: 'lt', a: t('j'), b: c(2) },
293
+ ], { kind: 'branch', cond: t('t1'), then: 'loop_body_1', else: 'loop_exit_1' }),
294
+ mirBlock('loop_body_1', [
295
+ { kind: 'call', dst: null, fn: 'test:b', args: [t('j')] },
296
+ ], { kind: 'jump', target: 'loop_latch_1' }),
297
+ mirBlock('loop_latch_1', [
298
+ { kind: 'add', dst: 'j', a: t('j'), b: c(1) },
299
+ ], { kind: 'jump', target: 'loop_header_1' }),
300
+ mirBlock('loop_exit_1', [], { kind: 'return', value: null }),
301
+ ]);
302
+ const result = (0, unroll_1.loopUnroll)(fn);
303
+ // Both loops should be unrolled
304
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
305
+ });
306
+ });
307
+ // ===========================================================================
308
+ // 3. interprocedural.ts — uncovered branches
309
+ // ===========================================================================
310
+ describe('interproceduralConstProp — uncovered branches', () => {
311
+ test('isSelfRecursive: function calling itself via call_macro is not specialized', () => {
312
+ // A function that is self-recursive via call_macro should be skipped
313
+ const selfRecursiveFn = mirFn('test:recur', ['n'], [
314
+ mirBlock('entry', [
315
+ // call_macro to itself
316
+ { kind: 'call_macro', dst: null, fn: 'test:recur', args: [] },
317
+ ], { kind: 'return', value: null }),
318
+ ]);
319
+ const callerFn = mirFn('test:main', [], [
320
+ mirBlock('entry', [
321
+ { kind: 'call', dst: null, fn: 'test:recur', args: [c(5)] },
322
+ ], { kind: 'return', value: null }),
323
+ ]);
324
+ const mod = mirMod([callerFn, selfRecursiveFn]);
325
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
326
+ // Should NOT create specialized version — self-recursive via call_macro
327
+ expect(result.functions.some(f => f.name.includes('__const_'))).toBe(false);
328
+ });
329
+ test('param count mismatch: callee.params.length !== instr.args.length skips', () => {
330
+ // Callee expects 2 params but call site provides 1 — should not specialize
331
+ const addFn = mirFn('test:add', ['a', 'b'], [
332
+ mirBlock('entry', [
333
+ { kind: 'add', dst: 'r', a: t('a'), b: t('b') },
334
+ ], { kind: 'return', value: t('r') }),
335
+ ]);
336
+ const callerFn = mirFn('test:main', [], [
337
+ mirBlock('entry', [
338
+ // Only 1 arg but callee expects 2
339
+ { kind: 'call', dst: null, fn: 'test:add', args: [c(3)] },
340
+ ], { kind: 'return', value: null }),
341
+ ]);
342
+ const mod = mirMod([callerFn, addFn]);
343
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
344
+ expect(result.functions.some(f => f.name.includes('__const_'))).toBe(false);
345
+ });
346
+ test('rewriteCallSites skips call_macro instructions (not rewritten)', () => {
347
+ // rewriteCallSites only rewrites 'call' not 'call_macro'
348
+ const helperFn = mirFn('test:helper', ['x'], [
349
+ mirBlock('entry', [], { kind: 'return', value: null }),
350
+ ]);
351
+ const callerFn = mirFn('test:main', [], [
352
+ mirBlock('entry', [
353
+ // call_macro — should not be rewritten even if specialized fn exists
354
+ { kind: 'call_macro', dst: null, fn: 'test:helper', args: [] },
355
+ ], { kind: 'return', value: null }),
356
+ ]);
357
+ const mod = mirMod([callerFn, helperFn]);
358
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
359
+ const main = result.functions.find(f => f.name === 'test:main');
360
+ const macroCall = main.blocks[0].instrs.find(i => i.kind === 'call_macro');
361
+ // Should remain unchanged
362
+ expect(macroCall).toBeDefined();
363
+ });
364
+ test('rewriteCallSites: call with self-same name is not rewritten', () => {
365
+ // When callee.name === fn.name (self-call) in rewriteCallSites, skip
366
+ const selfCallFn = mirFn('test:fib', ['n'], [
367
+ mirBlock('entry', [
368
+ // direct self-call with constant arg — rewriteCallSites should skip
369
+ { kind: 'call', dst: null, fn: 'test:fib', args: [c(5)] },
370
+ ], { kind: 'return', value: null }),
371
+ ]);
372
+ const mod = mirMod([selfCallFn]);
373
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
374
+ const fib = result.functions.find(f => f.name === 'test:fib');
375
+ const call = fib.blocks[0].instrs.find(i => i.kind === 'call');
376
+ // Self call should remain unchanged
377
+ expect(call.fn).toBe('test:fib');
378
+ });
379
+ test('rewriteCallSites: call with non-all-const args is not rewritten', () => {
380
+ const addFn = mirFn('test:add', ['a', 'b'], [
381
+ mirBlock('entry', [
382
+ { kind: 'add', dst: 'r', a: t('a'), b: t('b') },
383
+ ], { kind: 'return', value: t('r') }),
384
+ ]);
385
+ const callerFn = mirFn('test:main', [], [
386
+ mirBlock('entry', [
387
+ // Mixed args — not all const
388
+ { kind: 'call', dst: null, fn: 'test:add', args: [t('x'), c(4)] },
389
+ ], { kind: 'return', value: null }),
390
+ ]);
391
+ const mod = mirMod([callerFn, addFn]);
392
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
393
+ const main = result.functions.find(f => f.name === 'test:main');
394
+ const call = main.blocks[0].instrs.find(i => i.kind === 'call');
395
+ // Should remain as 'test:add' (not rewritten)
396
+ expect(call.fn).toBe('test:add');
397
+ });
398
+ test('callee with multiple blocks is not specialized', () => {
399
+ // Only single-block callees are specialized
400
+ const multiFn = mirFn('test:multi', ['x'], [
401
+ mirBlock('entry', [
402
+ { kind: 'cmp', dst: 'cond', op: 'gt', a: t('x'), b: c(0) },
403
+ ], { kind: 'branch', cond: t('cond'), then: 'pos', else: 'neg' }),
404
+ mirBlock('pos', [], { kind: 'return', value: c(1) }),
405
+ mirBlock('neg', [], { kind: 'return', value: c(0) }),
406
+ ]);
407
+ const callerFn = mirFn('test:main', [], [
408
+ mirBlock('entry', [
409
+ { kind: 'call', dst: null, fn: 'test:multi', args: [c(5)] },
410
+ ], { kind: 'return', value: null }),
411
+ ]);
412
+ const mod = mirMod([callerFn, multiFn]);
413
+ const result = (0, interprocedural_1.interproceduralConstProp)(mod);
414
+ expect(result.functions.some(f => f.name.includes('__const_'))).toBe(false);
415
+ });
416
+ });
417
+ // ===========================================================================
418
+ // 4. lir/const_imm.ts — uncovered branches
419
+ // ===========================================================================
420
+ describe('constImmFold — getReadSlots uncovered branches', () => {
421
+ test('score_swap: both slots a and b are read slots', () => {
422
+ // score_swap uses both a and b — getReadSlots returns [a, b]
423
+ // If one of them is the $__const_ slot, use count is > 1 → no fold
424
+ const constSlot = slot('$__const_5');
425
+ const fn = lirFn([
426
+ { kind: 'score_set', dst: constSlot, value: 5 },
427
+ { kind: 'score_swap', a: constSlot, b: slot('$x') },
428
+ { kind: 'score_add', dst: slot('$y'), src: constSlot },
429
+ ]);
430
+ const result = (0, const_imm_1.constImmFold)(fn);
431
+ // $__const_5 is used twice (swap + add) → no fold
432
+ expect(result.instructions).toHaveLength(3);
433
+ expect(result).toBe(fn);
434
+ });
435
+ test('store_nbt_to_score: does not count as reading a slot', () => {
436
+ // store_nbt_to_score reads from NBT, not a score slot — getReadSlots returns []
437
+ // So $__const_5 with only score_add as user → folds
438
+ const constSlot = slot('$__const_5');
439
+ const fn = lirFn([
440
+ { kind: 'score_set', dst: constSlot, value: 5 },
441
+ // store_nbt_to_score does NOT read constSlot
442
+ { kind: 'store_nbt_to_score', dst: slot('$z'), ns: 'test', path: 'data', scale: 1 },
443
+ { kind: 'score_add', dst: slot('$x'), src: constSlot },
444
+ ]);
445
+ // Not adjacent (store_nbt_to_score is between) — no fold
446
+ const result = (0, const_imm_1.constImmFold)(fn);
447
+ expect(result.instructions).toHaveLength(3);
448
+ });
449
+ test('macro_line: slots referenced in template are counted', () => {
450
+ // macro_line uses extractSlotsFromRaw on the template field
451
+ const constSlot = slot('$__const_5');
452
+ const fn = lirFn([
453
+ { kind: 'score_set', dst: constSlot, value: 5 },
454
+ // macro_line that references constSlot in template
455
+ { kind: 'macro_line', template: `$__const_5 ${obj}` },
456
+ { kind: 'score_add', dst: slot('$x'), src: constSlot },
457
+ ]);
458
+ const result = (0, const_imm_1.constImmFold)(fn);
459
+ // $__const_5 is used in macro_line template + score_add → 2 uses → no fold
460
+ expect(result).toBe(fn);
461
+ expect(result.instructions).toHaveLength(3);
462
+ });
463
+ test('extractSlotsFromRaw: raw cmd with player-obj pattern extracts slot', () => {
464
+ // raw instruction — slots extracted via regex /(\$[\w.:]+)\s+(\S+)/g
465
+ const constSlot = slot('$__const_7');
466
+ const fn = lirFn([
467
+ { kind: 'score_set', dst: constSlot, value: 7 },
468
+ // raw command that references constSlot
469
+ { kind: 'raw', cmd: `scoreboard players get $__const_7 ${obj}` },
470
+ { kind: 'score_add', dst: slot('$a'), src: constSlot },
471
+ ]);
472
+ const result = (0, const_imm_1.constImmFold)(fn);
473
+ // constSlot used in raw + score_add → 2 uses → no fold
474
+ expect(result).toBe(fn);
475
+ });
476
+ test('raw cmd with no matching slots: slot is not counted', () => {
477
+ // A raw cmd with no $-prefixed player references
478
+ const constSlot = slot('$__const_3');
479
+ const fn = lirFn([
480
+ { kind: 'score_set', dst: constSlot, value: 3 },
481
+ { kind: 'raw', cmd: 'say hello world' }, // no slot references
482
+ { kind: 'score_add', dst: slot('$x'), src: constSlot },
483
+ ]);
484
+ // constSlot used only once (score_add), but not adjacent to score_set
485
+ const result = (0, const_imm_1.constImmFold)(fn);
486
+ expect(result.instructions).toHaveLength(3); // not adjacent → no fold
487
+ });
488
+ test('call_if_score: both a and b slots are counted as reads', () => {
489
+ // call_if_score reads both a and b — if constSlot appears as either, use count++
490
+ const constSlot = slot('$__const_4');
491
+ const fn = lirFn([
492
+ { kind: 'score_set', dst: constSlot, value: 4 },
493
+ { kind: 'call_if_score', fn: 'test:f', a: constSlot, op: 'gt', b: slot('$x') },
494
+ { kind: 'score_add', dst: slot('$y'), src: constSlot },
495
+ ]);
496
+ const result = (0, const_imm_1.constImmFold)(fn);
497
+ // constSlot used in call_if_score (a) + score_add → 2 uses → no fold
498
+ expect(result).toBe(fn);
499
+ });
500
+ test('return_value: slot is counted as a read', () => {
501
+ // return_value reads the slot — if constSlot is returned, use count++
502
+ const constSlot = slot('$__const_9');
503
+ const fn = lirFn([
504
+ { kind: 'score_set', dst: constSlot, value: 9 },
505
+ { kind: 'score_add', dst: slot('$x'), src: constSlot },
506
+ { kind: 'return_value', slot: constSlot },
507
+ ]);
508
+ // Two uses of constSlot (score_add + return_value) — no fold for the score_add pair
509
+ const result = (0, const_imm_1.constImmFold)(fn);
510
+ expect(result).toBe(fn);
511
+ });
512
+ test('instructions length < 2: returns fn unchanged', () => {
513
+ const fn = lirFn([
514
+ { kind: 'score_set', dst: slot('$x'), value: 1 },
515
+ ]);
516
+ const result = (0, const_imm_1.constImmFold)(fn);
517
+ expect(result).toBe(fn);
518
+ });
519
+ });
520
+ // ===========================================================================
521
+ // 5. block_merge.ts — uncovered branch
522
+ // ===========================================================================
523
+ describe('blockMerge — uncovered branches', () => {
524
+ test('branch terminator: block with branch does not trigger merge', () => {
525
+ // The merge condition requires term.kind === 'jump'; a branch should not merge
526
+ const fn = {
527
+ name: 'test',
528
+ params: [],
529
+ blocks: [
530
+ mirBlock('entry', [], {
531
+ kind: 'branch', cond: t('c'), then: 'b1', else: 'b2',
532
+ }),
533
+ mirBlock('b1', [], { kind: 'return', value: null }, ['entry']),
534
+ mirBlock('b2', [], { kind: 'return', value: null }, ['entry']),
535
+ ],
536
+ entry: 'entry',
537
+ isMacro: false,
538
+ };
539
+ const result = (0, block_merge_1.blockMerge)(fn);
540
+ // Should have all 3 blocks — no merge possible
541
+ expect(result.blocks).toHaveLength(3);
542
+ });
543
+ test('jump to entry block: not merged (entry guard)', () => {
544
+ // A block jumping to 'entry' should not cause entry to be merged away
545
+ // (the `targetId !== fn.entry` guard)
546
+ const fn = {
547
+ name: 'test',
548
+ params: [],
549
+ blocks: [
550
+ mirBlock('entry', [
551
+ { kind: 'const', dst: 'x', value: 1 },
552
+ ], { kind: 'return', value: t('x') }),
553
+ // A block that jumps to entry (unusual but valid for testing)
554
+ mirBlock('before', [], { kind: 'jump', target: 'entry' }),
555
+ ],
556
+ entry: 'before',
557
+ isMacro: false,
558
+ };
559
+ const result = (0, block_merge_1.blockMerge)(fn);
560
+ // 'entry' has 1 pred (before) but is the fn.entry — guard prevents merge?
561
+ // Actually fn.entry = 'before', so 'entry' is NOT fn.entry here — it should merge
562
+ // Let's verify: before → entry (single pred), entry is not fn.entry ('before' is)
563
+ // So entry gets merged into before
564
+ expect(result.blocks.some(b => b.id === 'before')).toBe(true);
565
+ });
566
+ test('target not in blockMap: merge is skipped gracefully', () => {
567
+ // If target block doesn't exist in blockMap, blockMap.get returns undefined
568
+ // The `if (target && ...)` guard handles this
569
+ const fn = {
570
+ name: 'test',
571
+ params: [],
572
+ blocks: [
573
+ mirBlock('entry', [], { kind: 'jump', target: 'nonexistent' }),
574
+ ],
575
+ entry: 'entry',
576
+ isMacro: false,
577
+ };
578
+ // Should not throw — target not found means no merge
579
+ const result = (0, block_merge_1.blockMerge)(fn);
580
+ expect(result.blocks).toHaveLength(1);
581
+ });
582
+ test('already-removed block is skipped in iteration', () => {
583
+ // When A merges B, B is added to `removed`. If blocks array processes B again, it skips.
584
+ // A → B → C (all single-pred). B merges into A in first iteration;
585
+ // then A+B → C merges in second iteration.
586
+ const fn = {
587
+ name: 'test',
588
+ params: [],
589
+ blocks: [
590
+ mirBlock('entry', [{ kind: 'const', dst: 'a', value: 1 }], { kind: 'jump', target: 'b1' }),
591
+ mirBlock('b1', [{ kind: 'const', dst: 'b', value: 2 }], { kind: 'jump', target: 'b2' }, ['entry']),
592
+ mirBlock('b2', [{ kind: 'const', dst: 'c', value: 3 }], { kind: 'return', value: null }, ['b1']),
593
+ ],
594
+ entry: 'entry',
595
+ isMacro: false,
596
+ };
597
+ const result = (0, block_merge_1.blockMerge)(fn);
598
+ expect(result.blocks).toHaveLength(1);
599
+ expect(result.blocks[0].instrs).toHaveLength(3);
600
+ });
601
+ });
602
+ // ===========================================================================
603
+ // 2b. unroll.ts — additional uncovered branches
604
+ // ===========================================================================
605
+ describe('loopUnroll — additional edge cases', () => {
606
+ test('initializesTo0: another instr defines loopVar before const → returns false', () => {
607
+ // In initializesTo0, if getInstrDst returns loopVar for a non-const, non-copy instr → false
608
+ const fn = {
609
+ name: 'test', params: [], entry: 'entry', isMacro: false,
610
+ blocks: [
611
+ mirBlock('entry', [
612
+ // Another instr defines 'i' — then const i 0 never reached in reverse scan
613
+ { kind: 'add', dst: 'i', a: c(1), b: c(1) }, // defines i = 2
614
+ { kind: 'const', dst: 'i', value: 0 }, // defines i = 0 (last def wins in reverse)
615
+ ], { kind: 'jump', target: 'loop_header_0' }),
616
+ mirBlock('loop_header_0', [
617
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
618
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
619
+ mirBlock('loop_body_0', [
620
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
621
+ ], { kind: 'jump', target: 'loop_latch_0' }),
622
+ mirBlock('loop_latch_0', [
623
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
624
+ ], { kind: 'jump', target: 'loop_header_0' }),
625
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
626
+ ],
627
+ };
628
+ // Reverse scan hits const i 0 first → should unroll
629
+ const result = (0, unroll_1.loopUnroll)(fn);
630
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
631
+ });
632
+ test('initializesTo0: loopVar defined by non-zero non-copy instr → false', () => {
633
+ // In reverse scan: first hit is add dst=i → getInstrDst returns 'i' → return false
634
+ const fn = {
635
+ name: 'test', params: [], entry: 'entry', isMacro: false,
636
+ blocks: [
637
+ mirBlock('entry', [
638
+ // Only a non-zero add defines i — no const 0 before it in reverse scan
639
+ { kind: 'add', dst: 'i', a: c(5), b: c(3) }, // defines i (not 0)
640
+ ], { kind: 'jump', target: 'loop_header_0' }),
641
+ mirBlock('loop_header_0', [
642
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
643
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
644
+ mirBlock('loop_body_0', [
645
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
646
+ ], { kind: 'jump', target: 'loop_latch_0' }),
647
+ mirBlock('loop_latch_0', [
648
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
649
+ ], { kind: 'jump', target: 'loop_header_0' }),
650
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
651
+ ],
652
+ };
653
+ const result = (0, unroll_1.loopUnroll)(fn);
654
+ expect(result).toBe(fn); // not unrolled — not initialized to 0
655
+ });
656
+ test('findPreHeader: multiple predecessors to header → no unroll', () => {
657
+ // If loop_header has more than one non-latch predecessor, findPreHeader returns null
658
+ const fn = {
659
+ name: 'test', params: [], entry: 'entry', isMacro: false,
660
+ blocks: [
661
+ mirBlock('entry', [
662
+ { kind: 'const', dst: 'i', value: 0 },
663
+ ], { kind: 'branch', cond: t('some_cond'), then: 'loop_header_0', else: 'alt' }),
664
+ mirBlock('alt', [
665
+ { kind: 'const', dst: 'i', value: 0 },
666
+ ], { kind: 'jump', target: 'loop_header_0' }),
667
+ // loop_header has two non-latch preds: entry and alt
668
+ mirBlock('loop_header_0', [
669
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
670
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
671
+ mirBlock('loop_body_0', [
672
+ { kind: 'call', dst: null, fn: 'test:body', args: [t('i')] },
673
+ ], { kind: 'jump', target: 'loop_latch_0' }),
674
+ mirBlock('loop_latch_0', [
675
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
676
+ ], { kind: 'jump', target: 'loop_header_0' }),
677
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
678
+ ],
679
+ };
680
+ const result = (0, unroll_1.loopUnroll)(fn);
681
+ expect(result).toBe(fn); // not unrolled — ambiguous pre-header
682
+ });
683
+ test('body with nbt_write_dynamic substitutes both indexSrc and valueSrc', () => {
684
+ // Covers the nbt_write_dynamic case in substituteInstr
685
+ const fn = {
686
+ name: 'test', params: [], entry: 'entry', isMacro: false,
687
+ blocks: [
688
+ mirBlock('entry', [
689
+ { kind: 'const', dst: 'i', value: 0 },
690
+ ], { kind: 'jump', target: 'loop_header_0' }),
691
+ mirBlock('loop_header_0', [
692
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(2) },
693
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
694
+ mirBlock('loop_body_0', [
695
+ // nbt_write_dynamic with i as both indexSrc and valueSrc
696
+ {
697
+ kind: 'nbt_write_dynamic',
698
+ ns: 'test', pathPrefix: 'arr',
699
+ indexSrc: t('i'),
700
+ valueSrc: t('i'),
701
+ },
702
+ ], { kind: 'jump', target: 'loop_latch_0' }),
703
+ mirBlock('loop_latch_0', [
704
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
705
+ ], { kind: 'jump', target: 'loop_header_0' }),
706
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
707
+ ],
708
+ };
709
+ const result = (0, unroll_1.loopUnroll)(fn);
710
+ // Should unroll — 2 copies
711
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
712
+ const entry = result.blocks.find(b => b.id === 'entry');
713
+ const writes = entry.instrs.filter(i => i.kind === 'nbt_write_dynamic');
714
+ expect(writes).toHaveLength(2);
715
+ // Both indexSrc and valueSrc should be substituted
716
+ const w0 = writes[0];
717
+ expect(w0.indexSrc).toEqual(c(0));
718
+ expect(w0.valueSrc).toEqual(c(0));
719
+ });
720
+ test('body with nbt_write substitutes src', () => {
721
+ const fn = {
722
+ name: 'test', params: [], entry: 'entry', isMacro: false,
723
+ blocks: [
724
+ mirBlock('entry', [
725
+ { kind: 'const', dst: 'i', value: 0 },
726
+ ], { kind: 'jump', target: 'loop_header_0' }),
727
+ mirBlock('loop_header_0', [
728
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(2) },
729
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
730
+ mirBlock('loop_body_0', [
731
+ { kind: 'nbt_write', ns: 'test', path: 'val', type: 'int', scale: 1, src: t('i') },
732
+ ], { kind: 'jump', target: 'loop_latch_0' }),
733
+ mirBlock('loop_latch_0', [
734
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
735
+ ], { kind: 'jump', target: 'loop_header_0' }),
736
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
737
+ ],
738
+ };
739
+ const result = (0, unroll_1.loopUnroll)(fn);
740
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
741
+ const entry = result.blocks.find(b => b.id === 'entry');
742
+ const writes = entry.instrs.filter(i => i.kind === 'nbt_write');
743
+ expect(writes).toHaveLength(2);
744
+ expect(writes[0].src).toEqual(c(0));
745
+ expect(writes[1].src).toEqual(c(1));
746
+ });
747
+ test('body with call_macro substitutes arg values', () => {
748
+ // Covers the call_macro case in substituteInstr
749
+ const fn = {
750
+ name: 'test', params: [], entry: 'entry', isMacro: false,
751
+ blocks: [
752
+ mirBlock('entry', [
753
+ { kind: 'const', dst: 'i', value: 0 },
754
+ ], { kind: 'jump', target: 'loop_header_0' }),
755
+ mirBlock('loop_header_0', [
756
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(2) },
757
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
758
+ mirBlock('loop_body_0', [
759
+ {
760
+ kind: 'call_macro',
761
+ dst: null,
762
+ fn: 'test:macro_fn',
763
+ args: [{ name: 'x', value: t('i'), type: 'int', scale: 1 }],
764
+ },
765
+ ], { kind: 'jump', target: 'loop_latch_0' }),
766
+ mirBlock('loop_latch_0', [
767
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
768
+ ], { kind: 'jump', target: 'loop_header_0' }),
769
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
770
+ ],
771
+ };
772
+ const result = (0, unroll_1.loopUnroll)(fn);
773
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
774
+ const entry = result.blocks.find(b => b.id === 'entry');
775
+ const macroCalls = entry.instrs.filter(i => i.kind === 'call_macro');
776
+ expect(macroCalls).toHaveLength(2);
777
+ expect(macroCalls[0].args[0].value).toEqual(c(0));
778
+ expect(macroCalls[1].args[0].value).toEqual(c(1));
779
+ });
780
+ test('body with return instr: substitutes value', () => {
781
+ // return with value — substituteInstr covers the return case
782
+ const fn = {
783
+ name: 'test', params: [], entry: 'entry', isMacro: false,
784
+ blocks: [
785
+ mirBlock('entry', [
786
+ { kind: 'const', dst: 'i', value: 0 },
787
+ ], { kind: 'jump', target: 'loop_header_0' }),
788
+ mirBlock('loop_header_0', [
789
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(2) },
790
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
791
+ // Body ending with a terminator substitution test via branch (cond=i)
792
+ mirBlock('loop_body_0', [
793
+ { kind: 'call', dst: null, fn: 'test:f', args: [] },
794
+ ], { kind: 'jump', target: 'loop_latch_0' }),
795
+ mirBlock('loop_latch_0', [
796
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
797
+ ], { kind: 'jump', target: 'loop_header_0' }),
798
+ mirBlock('loop_exit_0', [], { kind: 'return', value: t('i') }),
799
+ ],
800
+ };
801
+ const result = (0, unroll_1.loopUnroll)(fn);
802
+ // Unrolled — exit is kept with return value
803
+ expect(result.blocks.some(b => b.id.startsWith('loop_header'))).toBe(false);
804
+ });
805
+ test('loop with N=0: does not unroll (N <= 0 guard)', () => {
806
+ const fn = {
807
+ name: 'test', params: [], entry: 'entry', isMacro: false,
808
+ blocks: [
809
+ mirBlock('entry', [
810
+ { kind: 'const', dst: 'i', value: 0 },
811
+ ], { kind: 'jump', target: 'loop_header_0' }),
812
+ mirBlock('loop_header_0', [
813
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(0) },
814
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
815
+ mirBlock('loop_body_0', [
816
+ { kind: 'call', dst: null, fn: 'test:body', args: [] },
817
+ ], { kind: 'jump', target: 'loop_latch_0' }),
818
+ mirBlock('loop_latch_0', [
819
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
820
+ ], { kind: 'jump', target: 'loop_header_0' }),
821
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
822
+ ],
823
+ };
824
+ const result = (0, unroll_1.loopUnroll)(fn);
825
+ expect(result).toBe(fn); // N=0 is rejected
826
+ });
827
+ test('loop_header with no cmp instr: not unrolled', () => {
828
+ // header has branch but no cmp instruction for condName
829
+ const fn = {
830
+ name: 'test', params: [], entry: 'entry', isMacro: false,
831
+ blocks: [
832
+ mirBlock('entry', [
833
+ { kind: 'const', dst: 'i', value: 0 },
834
+ ], { kind: 'jump', target: 'loop_header_0' }),
835
+ mirBlock('loop_header_0', [
836
+ // No cmp here — branch uses t_cmp but nothing defines it
837
+ ], { kind: 'branch', cond: t('t_cmp'), then: 'loop_body_0', else: 'loop_exit_0' }),
838
+ mirBlock('loop_body_0', [
839
+ { kind: 'call', dst: null, fn: 'test:body', args: [] },
840
+ ], { kind: 'jump', target: 'loop_latch_0' }),
841
+ mirBlock('loop_latch_0', [
842
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
843
+ ], { kind: 'jump', target: 'loop_header_0' }),
844
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
845
+ ],
846
+ };
847
+ const result = (0, unroll_1.loopUnroll)(fn);
848
+ expect(result).toBe(fn);
849
+ });
850
+ test('loop_header with const cond (not temp): not unrolled', () => {
851
+ // branch.cond must be temp — if it's const, skip
852
+ const fn = {
853
+ name: 'test', params: [], entry: 'entry', isMacro: false,
854
+ blocks: [
855
+ mirBlock('entry', [
856
+ { kind: 'const', dst: 'i', value: 0 },
857
+ ], { kind: 'jump', target: 'loop_header_0' }),
858
+ mirBlock('loop_header_0', [
859
+ { kind: 'cmp', dst: 't_cmp', op: 'lt', a: t('i'), b: c(3) },
860
+ ], { kind: 'branch', cond: c(1), then: 'loop_body_0', else: 'loop_exit_0' }),
861
+ mirBlock('loop_body_0', [
862
+ { kind: 'call', dst: null, fn: 'test:body', args: [] },
863
+ ], { kind: 'jump', target: 'loop_latch_0' }),
864
+ mirBlock('loop_latch_0', [
865
+ { kind: 'add', dst: 'i', a: t('i'), b: c(1) },
866
+ ], { kind: 'jump', target: 'loop_header_0' }),
867
+ mirBlock('loop_exit_0', [], { kind: 'return', value: null }),
868
+ ],
869
+ };
870
+ const result = (0, unroll_1.loopUnroll)(fn);
871
+ expect(result).toBe(fn);
872
+ });
873
+ });
874
+ // ===========================================================================
875
+ // 6. constant_fold.ts — uncovered branch (evalCmp default)
876
+ // ===========================================================================
877
+ describe('constantFold — evalCmp default branch', () => {
878
+ test('unknown cmp op falls through to default (returns 0)', () => {
879
+ // The default case in evalCmp — an op that doesn't match any known op
880
+ // We test through constantFold by constructing a cmp with unknown op
881
+ const fn = {
882
+ name: 'test',
883
+ params: [],
884
+ blocks: [
885
+ mirBlock('entry', [
886
+ // Use an unknown cmp op — TypeScript won't normally allow this
887
+ // but we cast to test the runtime default branch
888
+ {
889
+ kind: 'cmp',
890
+ dst: 'result',
891
+ op: 'unknown_op',
892
+ a: c(5),
893
+ b: c(3),
894
+ },
895
+ ], { kind: 'return', value: t('result') }),
896
+ ],
897
+ entry: 'entry',
898
+ isMacro: false,
899
+ };
900
+ const result = (0, constant_fold_1.constantFold)(fn);
901
+ // Should have folded cmp(unknown_op, 5, 3) → const 0 (default case)
902
+ const instr = result.blocks[0].instrs[0];
903
+ expect(instr.kind).toBe('const');
904
+ if (instr.kind === 'const') {
905
+ expect(instr.value).toBe(0);
906
+ expect(instr.dst).toBe('result');
907
+ }
908
+ });
909
+ test('all six cmp ops fold correctly', () => {
910
+ // Regression: verify all known ops work (these are covered but good to have)
911
+ const ops = [
912
+ { op: 'eq', a: 5, b: 5, expected: 1 },
913
+ { op: 'eq', a: 5, b: 3, expected: 0 },
914
+ { op: 'ne', a: 5, b: 3, expected: 1 },
915
+ { op: 'ne', a: 5, b: 5, expected: 0 },
916
+ { op: 'lt', a: 3, b: 5, expected: 1 },
917
+ { op: 'lt', a: 5, b: 3, expected: 0 },
918
+ { op: 'le', a: 5, b: 5, expected: 1 },
919
+ { op: 'le', a: 6, b: 5, expected: 0 },
920
+ { op: 'gt', a: 5, b: 3, expected: 1 },
921
+ { op: 'gt', a: 3, b: 5, expected: 0 },
922
+ { op: 'ge', a: 5, b: 5, expected: 1 },
923
+ { op: 'ge', a: 4, b: 5, expected: 0 },
924
+ ];
925
+ for (const { op, a: av, b: bv, expected } of ops) {
926
+ const fn = {
927
+ name: 'test',
928
+ params: [],
929
+ blocks: [
930
+ mirBlock('entry', [
931
+ { kind: 'cmp', dst: 'r', op, a: c(av), b: c(bv) },
932
+ ], { kind: 'return', value: t('r') }),
933
+ ],
934
+ entry: 'entry',
935
+ isMacro: false,
936
+ };
937
+ const result = (0, constant_fold_1.constantFold)(fn);
938
+ const instr = result.blocks[0].instrs[0];
939
+ expect(instr.kind).toBe('const');
940
+ if (instr.kind === 'const') {
941
+ expect(instr.value).toBe(expected);
942
+ }
943
+ }
944
+ });
945
+ test('cmp with non-const operands is not folded', () => {
946
+ const fn = {
947
+ name: 'test',
948
+ params: [],
949
+ blocks: [
950
+ mirBlock('entry', [
951
+ { kind: 'cmp', dst: 'r', op: 'lt', a: t('x'), b: c(5) },
952
+ ], { kind: 'return', value: t('r') }),
953
+ ],
954
+ entry: 'entry',
955
+ isMacro: false,
956
+ };
957
+ const result = (0, constant_fold_1.constantFold)(fn);
958
+ // Not folded — a is temp
959
+ expect(result.blocks[0].instrs[0].kind).toBe('cmp');
960
+ });
961
+ test('div by zero is not folded', () => {
962
+ const fn = {
963
+ name: 'test',
964
+ params: [],
965
+ blocks: [
966
+ mirBlock('entry', [
967
+ { kind: 'div', dst: 'r', a: c(10), b: c(0) },
968
+ ], { kind: 'return', value: t('r') }),
969
+ ],
970
+ entry: 'entry',
971
+ isMacro: false,
972
+ };
973
+ const result = (0, constant_fold_1.constantFold)(fn);
974
+ // div by 0 should not be folded
975
+ expect(result.blocks[0].instrs[0].kind).toBe('div');
976
+ });
977
+ test('mod by zero is not folded', () => {
978
+ const fn = {
979
+ name: 'test',
980
+ params: [],
981
+ blocks: [
982
+ mirBlock('entry', [
983
+ { kind: 'mod', dst: 'r', a: c(10), b: c(0) },
984
+ ], { kind: 'return', value: t('r') }),
985
+ ],
986
+ entry: 'entry',
987
+ isMacro: false,
988
+ };
989
+ const result = (0, constant_fold_1.constantFold)(fn);
990
+ expect(result.blocks[0].instrs[0].kind).toBe('mod');
991
+ });
992
+ });
993
+ //# sourceMappingURL=optimizer-extended.test.js.map