mt-lang 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,1399 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LowererAsync
5
+ private
6
+ def lower_contained_task_release_statements(value_expr, type)
7
+ return [] unless contains_task_type?(type)
8
+
9
+ void_type = @ctx.types.fetch("void")
10
+
11
+ case type
12
+ when Types::Task
13
+ task_frame_expr = IR::Member.new(receiver: value_expr, member: "frame", type: type.field("frame"))
14
+ release_call = IR::ExpressionStmt.new(
15
+ expression: IR::Call.new(
16
+ callee: IR::Member.new(receiver: value_expr, member: "release", type: type.field("release")),
17
+ arguments: [task_frame_expr],
18
+ type: void_type,
19
+ ),
20
+ )
21
+ [IR::IfStmt.new(condition: task_frame_expr, then_body: [release_call], else_body: nil)]
22
+
23
+ when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
24
+ statements = []
25
+ type.fields.each do |field_name, field_type|
26
+ next unless contains_task_type?(field_type)
27
+
28
+ field_expr = IR::Member.new(receiver: value_expr, member: field_name, type: field_type)
29
+ statements.concat(lower_contained_task_release_statements(field_expr, field_type))
30
+ end
31
+ statements
32
+
33
+ when Types::VariantInstance
34
+ if type.definition.name == "Option" && type.arguments.any? { |a| contains_task_type?(a) }
35
+ lower_opt_task_value_release_statements(value_expr, type.arguments.first)
36
+ else
37
+ []
38
+ end
39
+
40
+ when Types::Variant, Types::GenericVariantDefinition
41
+ statements = []
42
+ type.arms.each do |arm_name, arm_fields|
43
+ arm_fields.each do |field_name, field_type|
44
+ next unless contains_task_type?(field_type)
45
+ data_expr = IR::Member.new(receiver: value_expr, member: "data", type: nil)
46
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: nil)
47
+ field_expr = IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
48
+ statements.concat(lower_contained_task_release_statements(field_expr, field_type))
49
+ end
50
+ end
51
+ statements
52
+
53
+ when Types::GenericInstance
54
+ if type.name == "Option" && type.arguments.any? { |a| contains_task_type?(a) }
55
+ lower_opt_task_value_release_statements(value_expr, type.arguments.first)
56
+ else
57
+ []
58
+ end
59
+
60
+ when Types::Nullable
61
+ lower_contained_task_release_statements(value_expr, type.base)
62
+
63
+ else
64
+ []
65
+ end
66
+ end
67
+
68
+ def lower_opt_task_value_release_statements(value_expr, task_type)
69
+ int_type = @ctx.types.fetch("int")
70
+ kind_expr = IR::Member.new(receiver: value_expr, member: "kind", type: int_type)
71
+ some_check = IR::Binary.new(
72
+ operator: "==",
73
+ left: kind_expr,
74
+ right: IR::IntegerLiteral.new(value: 0, type: int_type),
75
+ type: @ctx.types.fetch("bool"),
76
+ )
77
+ data_expr = IR::Member.new(receiver: value_expr, member: "data", type: nil)
78
+ some_payload_expr = IR::Member.new(receiver: data_expr, member: "some", type: nil)
79
+ task_value_expr = IR::Member.new(receiver: some_payload_expr, member: "value", type: task_type)
80
+ release_body = lower_contained_task_release_statements(task_value_expr, task_type)
81
+ none_assignment = IR::Assignment.new(
82
+ target: kind_expr,
83
+ operator: "=",
84
+ value: IR::IntegerLiteral.new(value: 1, type: int_type),
85
+ )
86
+ then_body = release_body + [none_assignment]
87
+ [IR::IfStmt.new(condition: some_check, then_body:, else_body: nil)]
88
+ end
89
+
90
+ def lower_async_local_decl_statement(statement, field_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: [], loop_flow: nil)
91
+ lowered = []
92
+ type = field_info[:type]
93
+ storage_type = field_info[:storage_type]
94
+ target = async_frame_field_expression(frame_expr, field_info[:field_name], storage_type)
95
+ prepared_setup = []
96
+ prepared_value = statement.value
97
+
98
+ if statement.value
99
+ cleanup_start = (env[:prepared_expression_cleanups] ||= []).length
100
+ prepared_setup, prepared_value = prepare_expression_for_inline_lowering(
101
+ statement.value,
102
+ env:,
103
+ expected_type: storage_type,
104
+ allow_root_statement_foreign: true,
105
+ )
106
+ cleanup_count = (env[:prepared_expression_cleanups] || []).length - cleanup_start
107
+ if cleanup_count.positive?
108
+ async_info[:format_str_fields][field_info[:field_name]] = storage_type
109
+ env[:prepared_expression_cleanups].slice!(cleanup_start, cleanup_count)
110
+ end
111
+ lowered.concat(prepared_setup)
112
+ end
113
+
114
+ if prepared_value && (foreign_call = foreign_call_info(prepared_value, env))
115
+ setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
116
+ foreign_call,
117
+ env:,
118
+ expected_type: storage_type,
119
+ statement_position: false,
120
+ )
121
+ lowered.concat(setup)
122
+ raise LoweringError, "foreign call used to initialize #{statement.name} must return a value" if call_type == @ctx.types.fetch("void")
123
+ raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
124
+
125
+ lowered << IR::Assignment.new(target:, operator: "=", value:)
126
+ lowered.concat(cleanup_statements)
127
+ else
128
+ value = if prepared_value
129
+ lower_contextual_expression(
130
+ prepared_value,
131
+ env:,
132
+ expected_type: storage_type,
133
+ contextual_int_to_float: statement.type && contextual_int_to_float_target?(type),
134
+ )
135
+ else
136
+ IR::ZeroInit.new(type: storage_type)
137
+ end
138
+ lowered << IR::Assignment.new(target:, operator: "=", value:)
139
+ end
140
+
141
+ if statement.else_body
142
+ else_env = duplicate_env(env)
143
+ if statement.else_binding
144
+ current_actual_scope(else_env[:scopes])[statement.else_binding.name] = local_binding(
145
+ type: let_else_error_type(storage_type),
146
+ storage_type:,
147
+ linkage_name: async_frame_field_c_name(field_info[:field_name]),
148
+ mutable: false,
149
+ pointer: false,
150
+ projection: :result_failure_error,
151
+ )
152
+ end
153
+ else_body = if statements_contain_await?(statement.else_body, async_info)
154
+ lower_async_cf_statements(
155
+ statement.else_body,
156
+ env: else_env,
157
+ frame_expr:,
158
+ raw_frame_expr:,
159
+ resume_linkage_name:,
160
+ async_info:,
161
+ active_defers:,
162
+ loop_flow:,
163
+ )
164
+ else
165
+ lower_async_non_await_statements(
166
+ statement.else_body,
167
+ env: else_env,
168
+ frame_expr:,
169
+ raw_frame_expr:,
170
+ async_info:,
171
+ active_defers:,
172
+ loop_flow:,
173
+ )
174
+ end
175
+ lowered << IR::IfStmt.new(
176
+ condition: let_else_failure_condition(target, storage_type),
177
+ then_body: else_body,
178
+ else_body: nil,
179
+ )
180
+ end
181
+
182
+ lowered
183
+ end
184
+ # Lowers a list of statements that contain no `await` anywhere, but live
185
+ # inside an async resume function. Return statements are lowered as async
186
+ # completions. All other control flow is lowered recursively.
187
+ def statements_contain_await?(statements, async_info)
188
+ statements.any? do |s|
189
+ case s
190
+ when AST::LocalDecl
191
+ async_info[:await_fields].key?(s.value&.object_id) || async_expression_contains_await?(s.value) || (s.else_body && statements_contain_await?(s.else_body, async_info))
192
+ when AST::Assignment
193
+ async_info[:await_fields].key?(s.value&.object_id) || async_expression_contains_await?(s.target) || async_expression_contains_await?(s.value)
194
+ when AST::ExpressionStmt
195
+ async_info[:await_fields].key?(s.expression&.object_id) || async_expression_contains_await?(s.expression)
196
+ when AST::ReturnStmt
197
+ async_info[:await_fields].key?(s.value&.object_id) || async_expression_contains_await?(s.value)
198
+ when AST::IfStmt
199
+ s.branches.any? { |b| statements_contain_await?(b.body, async_info) } ||
200
+ (s.else_body && statements_contain_await?(s.else_body, async_info))
201
+ when AST::WhileStmt
202
+ statements_contain_await?(s.body, async_info)
203
+ when AST::ForStmt
204
+ statements_contain_await?(s.body, async_info)
205
+ when AST::MatchStmt
206
+ s.arms.any? { |arm| statements_contain_await?(arm.body, async_info) }
207
+ when AST::UnsafeStmt
208
+ statements_contain_await?(s.body, async_info)
209
+ when AST::DeferStmt
210
+ (s.body && statements_contain_await?(s.body, async_info)) || (s.expression && async_expression_contains_await?(s.expression))
211
+ else
212
+ false
213
+ end
214
+ end
215
+ end
216
+
217
+ # Lower a list of statements that MAY contain await expressions inside nested control flow.
218
+ # CPS-via-goto: labels placed inside if/while/match bodies, reachable from top-level switch dispatch.
219
+ def lower_async_cf_statements(statements, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: [], loop_flow: nil)
220
+ lowered = []
221
+ local_defers = []
222
+ env[:return_context] = async_return_context(
223
+ return_type: async_info[:result_type],
224
+ active_defers:,
225
+ local_defers:,
226
+ frame_expr:,
227
+ raw_frame_expr:,
228
+ async_info:,
229
+ )
230
+
231
+ statements.each do |statement|
232
+ case statement
233
+ when AST::LocalDecl
234
+ field_info = async_info[:local_fields].fetch(async_local_decl_field_key(statement))
235
+ await_info = async_info[:await_fields][statement.value&.object_id]
236
+ if await_info
237
+ lowered.concat(lower_async_await_statement(statement, field_info:, await_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
238
+ else
239
+ lowered.concat(lower_async_local_decl_statement(statement, field_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
240
+ end
241
+ async_bind_local!(env, statement.name, field_info) if bind_let_else_local?(statement)
242
+ when AST::Assignment
243
+ await_info = async_info[:await_fields][statement.value&.object_id]
244
+ if await_info
245
+ lowered.concat(lower_async_await_statement(statement, await_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:))
246
+ else
247
+ lowered.concat(lower_async_assignment_statement(statement, env:))
248
+ end
249
+ when AST::ExpressionStmt
250
+ await_info = async_info[:await_fields][statement.expression&.object_id]
251
+ if await_info
252
+ lowered.concat(lower_async_await_statement(statement, await_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:))
253
+ else
254
+ lowered.concat(lower_async_expression_statement(statement, env:))
255
+ end
256
+ when AST::ReturnStmt
257
+ cleanup = lower_async_cleanup_entries(local_defers, active_defers, frame_expr:, raw_frame_expr:, async_info:)
258
+ await_info = async_info[:await_fields][statement.value&.object_id]
259
+ if await_info
260
+ lowered.concat(lower_async_await_statement(statement, await_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, cleanup:))
261
+ else
262
+ lowered.concat(lower_async_return_statement(statement, env:, frame_expr:, raw_frame_expr:, async_info:, cleanup:))
263
+ end
264
+ when AST::IfStmt
265
+ lowered.concat(lower_async_cf_if_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
266
+ when AST::WhileStmt
267
+ lowered.concat(lower_async_cf_while_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
268
+ when AST::ForStmt
269
+ lowered.concat(lower_async_cf_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers))
270
+ when AST::MatchStmt
271
+ lowered.concat(lower_async_cf_match_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
272
+ when AST::UnsafeStmt
273
+ lowered.concat(lower_async_cf_statements(statement.body, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)))
274
+ when AST::DeferStmt
275
+ local_defers << lower_async_defer_cleanup(statement, env:, async_info:)
276
+ when AST::PassStmt
277
+ nil
278
+ when AST::BreakStmt
279
+ if loop_flow
280
+ lowered.concat(lower_async_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers], frame_expr:, raw_frame_expr:, async_info:))
281
+ else
282
+ lowered << IR::BreakStmt.new
283
+ end
284
+ when AST::ContinueStmt
285
+ if loop_flow
286
+ lowered.concat(lower_async_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers], frame_expr:, raw_frame_expr:, async_info:))
287
+ else
288
+ lowered << IR::ContinueStmt.new
289
+ end
290
+ when AST::StaticAssert
291
+ lowered.concat(lower_static_assert(statement))
292
+ else
293
+ raise LoweringError, "unsupported async cf statement #{statement.class.name}"
294
+ end
295
+ end
296
+
297
+ unless cfg_block_always_terminates?(statements)
298
+ lowered.concat(lower_async_cleanup_entries(local_defers, [], frame_expr:, raw_frame_expr:, async_info:))
299
+ end
300
+ lowered
301
+ end
302
+
303
+ def lower_async_cf_if_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow:)
304
+ branch_entries = statement.branches.map do |branch|
305
+ condition_setup, prepared_cond = prepare_expression_for_inline_lowering(branch.condition, env:)
306
+ condition = lower_contextual_expression(prepared_cond, env:, expected_type: @ctx.types.fetch("bool"))
307
+ body = if statements_contain_await?(branch.body, async_info)
308
+ lower_async_cf_statements(branch.body, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow:)
309
+ else
310
+ lower_async_non_await_statements(branch.body, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow:)
311
+ end
312
+ { condition_setup:, condition:, body: }
313
+ end
314
+
315
+ else_body = if statement.else_body
316
+ if statements_contain_await?(statement.else_body, async_info)
317
+ lower_async_cf_statements(statement.else_body, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow:)
318
+ else
319
+ lower_async_non_await_statements(statement.else_body, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow:)
320
+ end
321
+ end
322
+
323
+ nested_else = else_body
324
+ branch_entries.reverse_each do |entry|
325
+ nested_else = [
326
+ *entry[:condition_setup],
327
+ IR::IfStmt.new(condition: entry[:condition], then_body: entry[:body], else_body: nested_else),
328
+ ]
329
+ end
330
+ nested_else || []
331
+ end
332
+
333
+ def lower_async_cf_while_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow:)
334
+ continue_label = fresh_c_temp_name(env, "loop_continue")
335
+ break_label = fresh_c_temp_name(env, "loop_break")
336
+ condition_setup, prepared_cond = prepare_expression_for_inline_lowering(statement.condition, env:)
337
+ condition = lower_contextual_expression(prepared_cond, env:, expected_type: @ctx.types.fetch("bool"))
338
+ inner_loop_flow = loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label))
339
+ body = if statements_contain_await?(statement.body, async_info)
340
+ lower_async_cf_statements(statement.body, env: duplicate_env(env), frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: inner_loop_flow)
341
+ else
342
+ lower_async_non_await_statements(statement.body, env: duplicate_env(env), frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: inner_loop_flow)
343
+ end
344
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
345
+
346
+ if condition_setup.empty?
347
+ stmts = [IR::WhileStmt.new(condition:, body:)]
348
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
349
+ return stmts
350
+ end
351
+
352
+ loop_body = [
353
+ *condition_setup,
354
+ IR::IfStmt.new(
355
+ condition: IR::Unary.new(operator: "not", operand: condition, type: @ctx.types.fetch("bool")),
356
+ then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
357
+ else_body: nil,
358
+ ),
359
+ *body,
360
+ ]
361
+ stmts = [IR::WhileStmt.new(condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")), body: loop_body)]
362
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(loop_body, break_label)
363
+ stmts
364
+ end
365
+
366
+ def lower_async_cf_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
367
+ return lower_async_cf_parallel_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:) if statement.parallel?
368
+
369
+ if range_iterable?(statement.iterable)
370
+ lower_async_cf_range_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
371
+ else
372
+ lower_async_cf_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
373
+ end
374
+ end
375
+
376
+ def lower_range_call(iterable, env:)
377
+ loop_type = infer_range_loop_type(iterable, env:)
378
+ start_expr_ast = range_start_of(iterable)
379
+ stop_expr_ast = range_end_of(iterable)
380
+ start_ir = lower_expression(start_expr_ast, env:, expected_type: loop_type)
381
+ stop_ir = lower_expression(stop_expr_ast, env:, expected_type: loop_type)
382
+ [start_ir, stop_ir, false]
383
+ end
384
+
385
+ def lower_async_cf_range_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
386
+ loop_var_name = statement.name
387
+ loop_var_type = infer_range_loop_type(statement.iterable, env:)
388
+ loop_var_field = async_info[:local_fields].fetch(loop_var_name)
389
+ loop_var_expr = async_frame_field_expression(frame_expr, loop_var_field[:field_name], loop_var_type)
390
+ continue_label = fresh_c_temp_name(env, "loop_continue")
391
+ break_label = fresh_c_temp_name(env, "loop_break")
392
+
393
+ start_expr, stop_expr, inclusive = lower_range_call(statement.iterable, env:)
394
+
395
+ # Store stop value in frame too so it survives suspension
396
+ stop_field_name = "#{loop_var_field[:field_name]}_stop"
397
+ async_info[:local_fields][stop_field_name] ||= { field_name: stop_field_name, type: loop_var_type, mutable: true }
398
+ stop_field_expr = async_frame_field_expression(frame_expr, stop_field_name, loop_var_type)
399
+
400
+ inner_env = duplicate_env(env)
401
+ inner_env[:scopes].last[loop_var_name] = local_binding(
402
+ type: loop_var_type,
403
+ linkage_name: async_frame_field_c_name(loop_var_field[:field_name]),
404
+ mutable: true, pointer: false
405
+ )
406
+ inner_loop_flow = loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label))
407
+
408
+ body = if statements_contain_await?(statement.body, async_info)
409
+ lower_async_cf_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: inner_loop_flow)
410
+ else
411
+ lower_async_non_await_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: inner_loop_flow)
412
+ end
413
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
414
+
415
+ cmp_op = inclusive ? "<=" : "<"
416
+ stmts = [
417
+ IR::Assignment.new(target: loop_var_expr, operator: "=", value: start_expr),
418
+ IR::Assignment.new(target: stop_field_expr, operator: "=", value: stop_expr),
419
+ IR::WhileStmt.new(
420
+ condition: IR::Binary.new(operator: cmp_op, left: loop_var_expr, right: stop_field_expr, type: @ctx.types.fetch("bool")),
421
+ body: body + [IR::Assignment.new(target: loop_var_expr, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: loop_var_type))],
422
+ ),
423
+ ]
424
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
425
+ stmts
426
+ end
427
+
428
+ def lower_async_cf_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
429
+ iterable_type = infer_expression_type(statement.iterable, env:)
430
+ element_type = collection_loop_type(iterable_type)
431
+ raise LoweringError, "for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}" unless element_type
432
+
433
+ iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
434
+ continue_label = fresh_c_temp_name(env, "loop_continue")
435
+ break_label = fresh_c_temp_name(env, "loop_break")
436
+ iterable_field = async_info[:local_fields].fetch(async_collection_iterable_field_key(statement))
437
+ index_field = async_info[:local_fields].fetch(async_collection_index_field_key(statement))
438
+ iterable_ref = async_frame_field_expression(frame_expr, iterable_field[:field_name], iterable_type)
439
+ index_ref = async_frame_field_expression(frame_expr, index_field[:field_name], @ctx.types.fetch("ptr_uint"))
440
+
441
+ # Loop variable stored in frame so it survives suspension
442
+ loop_var_field = async_info[:local_fields].fetch(statement.name)
443
+ loop_var_expr = async_frame_field_expression(frame_expr, loop_var_field[:field_name], element_type)
444
+
445
+ item_value = if array_type?(iterable_type)
446
+ IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
447
+ else
448
+ data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
449
+ IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
450
+ end
451
+ stop_value = if array_type?(iterable_type)
452
+ IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
453
+ else
454
+ IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
455
+ end
456
+
457
+ inner_env = duplicate_env(env)
458
+ inner_env[:scopes].last[statement.name] = local_binding(
459
+ type: element_type, linkage_name: async_frame_field_c_name(loop_var_field[:field_name]), mutable: true, pointer: false
460
+ )
461
+ inner_loop_flow = loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label))
462
+
463
+ assign_item = IR::Assignment.new(target: loop_var_expr, operator: "=", value: item_value)
464
+ body_stmts = if statements_contain_await?(statement.body, async_info)
465
+ lower_async_cf_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: inner_loop_flow)
466
+ else
467
+ lower_async_non_await_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: inner_loop_flow)
468
+ end
469
+ body_stmts << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body_stmts, continue_label)
470
+
471
+ stmts = [
472
+ *iterable_setup,
473
+ IR::Assignment.new(target: iterable_ref, operator: "=", value: lower_expression(prepared_iterable, env:, expected_type: iterable_type)),
474
+ IR::Assignment.new(target: index_ref, operator: "=", value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
475
+ IR::WhileStmt.new(
476
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
477
+ body: [assign_item] + body_stmts + [
478
+ IR::Assignment.new(target: index_ref, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
479
+ ],
480
+ ),
481
+ ]
482
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body_stmts, break_label)
483
+ stmts
484
+ end
485
+
486
+ def lower_async_cf_parallel_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
487
+ infos = statement.bindings.each_with_index.map do |binding, index|
488
+ iterable = statement.iterables[index]
489
+ iterable_type = infer_expression_type(iterable, env:)
490
+ element_type = collection_loop_type(iterable_type)
491
+ raise LoweringError, "parallel for loops expect arrays or spans for each iterable, got #{iterable_type}" unless element_type
492
+
493
+ {
494
+ binding:,
495
+ iterable:,
496
+ iterable_type:,
497
+ element_type:,
498
+ binding_type: collection_loop_binding_type(iterable_type, element_type) || element_type,
499
+ iterable_field: async_info[:local_fields].fetch(async_collection_iterable_field_key(statement, index)),
500
+ }
501
+ end
502
+
503
+ iterable_entries = infos.map do |info|
504
+ setup, prepared_iterable = prepare_expression_for_inline_lowering(info[:iterable], env:, expected_type: info[:iterable_type])
505
+ info.merge(setup:, prepared_iterable:)
506
+ end
507
+
508
+ continue_label = fresh_c_temp_name(env, "loop_continue")
509
+ break_label = fresh_c_temp_name(env, "loop_break")
510
+ index_field = async_info[:local_fields].fetch(async_collection_index_field_key(statement))
511
+ index_ref = async_frame_field_expression(frame_expr, index_field[:field_name], @ctx.types.fetch("ptr_uint"))
512
+ iterable_refs = iterable_entries.map do |entry|
513
+ async_frame_field_expression(frame_expr, entry[:iterable_field][:field_name], entry[:iterable_type])
514
+ end
515
+ stop_value = collection_loop_stop_value(iterable_refs.first, iterable_entries.first[:iterable_type])
516
+
517
+ inner_env = duplicate_env(env)
518
+ assign_items = iterable_entries.map.with_index do |entry, index|
519
+ item_value = collection_loop_item_value(iterable_refs[index], entry[:iterable_type], index_ref, entry[:element_type])
520
+ loop_item_value = if ref_type?(entry[:binding_type])
521
+ IR::AddressOf.new(expression: item_value, type: entry[:binding_type])
522
+ else
523
+ item_value
524
+ end
525
+ binding_field = async_info[:local_fields].fetch(entry[:binding].name)
526
+ binding_target = async_frame_field_expression(frame_expr, binding_field[:field_name], entry[:binding_type])
527
+ inner_env[:scopes].last[entry[:binding].name] = local_binding(
528
+ type: entry[:binding_type],
529
+ linkage_name: async_frame_field_c_name(binding_field[:field_name]),
530
+ mutable: true,
531
+ pointer: false,
532
+ )
533
+ IR::Assignment.new(target: binding_target, operator: "=", value: loop_item_value)
534
+ end
535
+ inner_loop_flow = loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label))
536
+ body_stmts = if statements_contain_await?(statement.body, async_info)
537
+ lower_async_cf_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: inner_loop_flow)
538
+ else
539
+ lower_async_non_await_statements(statement.body, env: inner_env, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: inner_loop_flow)
540
+ end
541
+ body_stmts << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body_stmts, continue_label)
542
+
543
+ length_checks = iterable_entries.drop(1).each_with_index.map do |entry, offset|
544
+ IR::IfStmt.new(
545
+ condition: IR::Binary.new(
546
+ operator: "!=",
547
+ left: collection_loop_stop_value(iterable_refs[offset + 1], entry[:iterable_type]),
548
+ right: stop_value,
549
+ type: @ctx.types.fetch("bool"),
550
+ ),
551
+ then_body: [lower_fatal_statement("parallel for iterables must have matching lengths", env:)],
552
+ else_body: nil,
553
+ )
554
+ end
555
+
556
+ stmts = [
557
+ *iterable_entries.flat_map { |entry| entry[:setup] },
558
+ *iterable_entries.each_with_index.map do |entry, index|
559
+ IR::Assignment.new(
560
+ target: iterable_refs[index],
561
+ operator: "=",
562
+ value: lower_expression(entry[:prepared_iterable], env:, expected_type: entry[:iterable_type]),
563
+ )
564
+ end,
565
+ *length_checks,
566
+ IR::Assignment.new(target: index_ref, operator: "=", value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
567
+ IR::WhileStmt.new(
568
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
569
+ body: assign_items + body_stmts + [
570
+ IR::Assignment.new(target: index_ref, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
571
+ ],
572
+ ),
573
+ ]
574
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body_stmts, break_label)
575
+ stmts
576
+ end
577
+
578
+ def lower_async_cf_match_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow:)
579
+ expr_setup, prepared_expr = prepare_expression_for_inline_lowering(statement.expression, env:)
580
+ match_expr = lower_contextual_expression(prepared_expr, env:, expected_type: nil)
581
+ match_type = infer_expression_type(statement.expression, env:)
582
+ arm_loop_flow = switch_loop_flow(loop_flow, [])
583
+
584
+ if match_type.is_a?(Types::Variant)
585
+ if statement.arms.any? { |arm| arm.binding_name && !wildcard_arm_pattern?(arm.pattern) } &&
586
+ !duplicable_foreign_argument_expression?(match_expr)
587
+ scrutinee_linkage_name = fresh_c_temp_name(env, "match_value")
588
+ expr_setup << IR::LocalDecl.new(name: scrutinee_linkage_name, linkage_name: scrutinee_linkage_name, type: match_type, value: match_expr)
589
+ match_expr = IR::Name.new(name: scrutinee_linkage_name, type: match_type, pointer: false)
590
+ end
591
+
592
+ kind_type = @ctx.types.fetch("int")
593
+ kind_expr = IR::Member.new(receiver: match_expr, member: "kind", type: kind_type)
594
+ cases = statement.arms.map do |arm|
595
+ arm_env, binding_decl = async_variant_match_arm_binding(arm, match_expr, match_type, env:, frame_expr:, local_fields: async_info[:local_fields])
596
+ arm_body = if statements_contain_await?(arm.body, async_info)
597
+ lower_async_cf_statements(arm.body, env: arm_env, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: arm_loop_flow)
598
+ else
599
+ lower_async_non_await_statements(arm.body, env: arm_env, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: arm_loop_flow)
600
+ end
601
+ body = [binding_decl, *arm_body].compact + [IR::BreakStmt.new]
602
+ if wildcard_arm_pattern?(arm.pattern)
603
+ IR::SwitchDefaultCase.new(body: body)
604
+ else
605
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
606
+ IR::SwitchCase.new(value: IR::Name.new(name: enum_member_c_name(match_type, "kind_#{arm_name}"), type: kind_type, pointer: false), body: body)
607
+ end
608
+ end
609
+
610
+ return expr_setup + [IR::SwitchStmt.new(expression: kind_expr, cases:, exhaustive: true)]
611
+ end
612
+
613
+ cases = statement.arms.map do |arm|
614
+ arm_body = if statements_contain_await?(arm.body, async_info)
615
+ lower_async_cf_statements(arm.body, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:, loop_flow: arm_loop_flow)
616
+ else
617
+ lower_async_non_await_statements(arm.body, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:, loop_flow: arm_loop_flow)
618
+ end
619
+ if wildcard_arm_pattern?(arm.pattern)
620
+ IR::SwitchDefaultCase.new(body: arm_body + [IR::BreakStmt.new])
621
+ else
622
+ IR::SwitchCase.new(value: lower_expression(arm.pattern, env:, expected_type: match_type), body: arm_body + [IR::BreakStmt.new])
623
+ end
624
+ end
625
+
626
+ expr_setup + [IR::SwitchStmt.new(expression: match_expr, cases:, exhaustive: true)]
627
+ end
628
+ def lower_async_non_await_statements(statements, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [], loop_flow: nil)
629
+ local_env = duplicate_env(env)
630
+ lowered = []
631
+ local_defers = []
632
+ local_env[:return_context] = async_return_context(
633
+ return_type: async_info[:result_type],
634
+ active_defers:,
635
+ local_defers:,
636
+ frame_expr:,
637
+ raw_frame_expr:,
638
+ async_info:,
639
+ )
640
+
641
+ statements.each do |statement|
642
+ case statement
643
+ when AST::LocalDecl
644
+ else_env = duplicate_env(local_env) if statement.else_body
645
+ type, storage_type = async_local_decl_types(statement, env: local_env)
646
+ linkage_name = c_local_name(statement.name)
647
+ if statement.value
648
+ prepared_setup, prepared_value = prepare_expression_for_inline_lowering(
649
+ statement.value, env: local_env, expected_type: storage_type, allow_root_statement_foreign: true
650
+ )
651
+ lowered.concat(prepared_setup)
652
+ value = lower_contextual_expression(
653
+ prepared_value, env: local_env, expected_type: storage_type,
654
+ contextual_int_to_float: statement.type && contextual_int_to_float_target?(type)
655
+ )
656
+ else
657
+ value = IR::ZeroInit.new(type: storage_type)
658
+ end
659
+ lowered << IR::LocalDecl.new(name: statement.name, linkage_name:, type: storage_type, value:)
660
+ current_actual_scope(local_env[:scopes])[statement.name] = local_binding(type:, storage_type:, linkage_name:, mutable: statement.kind == :var, pointer: false)
661
+ if statement.else_body
662
+ else_body = lower_async_non_await_statements(
663
+ statement.else_body,
664
+ env: else_env,
665
+ frame_expr:,
666
+ raw_frame_expr:,
667
+ async_info:,
668
+ active_defers: active_defers + local_defers,
669
+ loop_flow: nested_loop_flow(loop_flow, local_defers),
670
+ )
671
+ lowered << IR::IfStmt.new(
672
+ condition: IR::Binary.new(
673
+ operator: "==",
674
+ left: IR::Name.new(name: linkage_name, type: storage_type, pointer: false),
675
+ right: IR::NullLiteral.new(type: storage_type),
676
+ type: @ctx.types.fetch("bool"),
677
+ ),
678
+ then_body: else_body,
679
+ else_body: nil,
680
+ )
681
+ end
682
+ when AST::Assignment
683
+ lowered.concat(lower_async_assignment_statement(statement, env: local_env))
684
+ when AST::ExpressionStmt
685
+ lowered.concat(lower_async_expression_statement(statement, env: local_env))
686
+ when AST::ReturnStmt
687
+ lowered.concat(lower_async_return_statement(statement, env: local_env, frame_expr:, raw_frame_expr:, async_info:, cleanup: lower_async_cleanup_entries(local_defers, active_defers, frame_expr:, raw_frame_expr:, async_info:)))
688
+ when AST::IfStmt
689
+ branch_entries = statement.branches.map do |branch|
690
+ condition_setup, prepared_cond = prepare_expression_for_inline_lowering(
691
+ branch.condition, env: local_env, expected_type: @ctx.types.fetch("bool")
692
+ )
693
+ then_body = lower_async_non_await_statements(
694
+ branch.body, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)
695
+ )
696
+ [condition_setup, lower_expression(prepared_cond, env: local_env, expected_type: @ctx.types.fetch("bool")), then_body]
697
+ end
698
+ else_body = statement.else_body ? lower_async_non_await_statements(
699
+ statement.else_body, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)
700
+ ) : nil
701
+ nested = else_body || []
702
+ branch_entries.reverse_each do |cond_setup, cond, then_body|
703
+ nested = [*cond_setup, IR::IfStmt.new(condition: cond, then_body:, else_body: nested.empty? ? nil : nested)]
704
+ end
705
+ lowered.concat(nested)
706
+ when AST::MatchStmt
707
+ scrutinee_type = infer_expression_type(statement.expression, env: local_env)
708
+ expr_setup, prepared_expr = prepare_expression_for_inline_lowering(
709
+ statement.expression, env: local_env, expected_type: scrutinee_type
710
+ )
711
+ lowered.concat(expr_setup)
712
+ expr = lower_expression(prepared_expr, env: local_env, expected_type: scrutinee_type)
713
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
714
+ if scrutinee_type.is_a?(Types::Variant)
715
+ if statement.arms.any? { |arm| arm.binding_name && !wildcard_arm_pattern?(arm.pattern) } &&
716
+ !duplicable_foreign_argument_expression?(expr)
717
+ scrutinee_linkage_name = fresh_c_temp_name(local_env, "match_value")
718
+ lowered << IR::LocalDecl.new(name: scrutinee_linkage_name, linkage_name: scrutinee_linkage_name, type: scrutinee_type, value: expr)
719
+ expr = IR::Name.new(name: scrutinee_linkage_name, type: scrutinee_type, pointer: false)
720
+ end
721
+
722
+ kind_type = @ctx.types.fetch("int")
723
+ kind_expr = IR::Member.new(receiver: expr, member: "kind", type: kind_type)
724
+ cases = statement.arms.map do |arm|
725
+ arm_env, binding_decl = async_variant_match_arm_binding(arm, expr, scrutinee_type, env: local_env)
726
+ arm_body = lower_async_non_await_statements(
727
+ arm.body, env: arm_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers, loop_flow: arm_loop_flow
728
+ )
729
+ body = [binding_decl, *arm_body].compact
730
+ if wildcard_arm_pattern?(arm.pattern)
731
+ IR::SwitchDefaultCase.new(body: body)
732
+ else
733
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
734
+ IR::SwitchCase.new(value: IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false), body: body)
735
+ end
736
+ end
737
+ lowered << IR::SwitchStmt.new(expression: kind_expr, cases:, exhaustive: true)
738
+ else
739
+ cases = statement.arms.map do |arm|
740
+ arm_body = lower_async_non_await_statements(
741
+ arm.body, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers, loop_flow: arm_loop_flow
742
+ )
743
+ if wildcard_arm_pattern?(arm.pattern)
744
+ IR::SwitchDefaultCase.new(body: arm_body)
745
+ else
746
+ value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
747
+ IR::SwitchCase.new(value:, body: arm_body)
748
+ end
749
+ end
750
+ lowered << IR::SwitchStmt.new(expression: expr, cases:, exhaustive: true)
751
+ end
752
+ when AST::WhileStmt
753
+ lowered << lower_async_while_stmt(statement, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers)
754
+ when AST::ForStmt
755
+ lowered << lower_async_for_stmt(statement, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers)
756
+ when AST::DeferStmt
757
+ local_defers << lower_async_defer_cleanup(statement, env: local_env, async_info:)
758
+ when AST::PassStmt
759
+ nil
760
+ when AST::BreakStmt
761
+ if loop_flow
762
+ lowered.concat(lower_async_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers], frame_expr:, raw_frame_expr:, async_info:))
763
+ else
764
+ lowered << IR::BreakStmt.new
765
+ end
766
+ when AST::ContinueStmt
767
+ if loop_flow
768
+ lowered.concat(lower_async_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers], frame_expr:, raw_frame_expr:, async_info:))
769
+ else
770
+ lowered << IR::ContinueStmt.new
771
+ end
772
+ when AST::UnsafeStmt
773
+ lowered.concat(lower_async_non_await_statements(
774
+ statement.body, env: local_env, frame_expr:, raw_frame_expr:, async_info:, active_defers: active_defers + local_defers, loop_flow: nested_loop_flow(loop_flow, local_defers)
775
+ ))
776
+ when AST::StaticAssert
777
+ lowered << lower_static_assert(statement, env: local_env)
778
+ else
779
+ raise LoweringError, "unsupported async non-await statement #{statement.class.name}"
780
+ end
781
+ end
782
+
783
+ unless cfg_block_always_terminates?(statements)
784
+ lowered.concat(lower_async_cleanup_entries(local_defers, [], frame_expr:, raw_frame_expr:, async_info:))
785
+ end
786
+ lowered
787
+ end
788
+
789
+ def lower_async_while_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
790
+ continue_label = fresh_c_temp_name(env, "loop_continue")
791
+ break_label = fresh_c_temp_name(env, "loop_break")
792
+ condition_setup, prepared_cond = prepare_expression_for_inline_lowering(
793
+ statement.condition, env:, expected_type: @ctx.types.fetch("bool")
794
+ )
795
+ body = lower_async_non_await_statements(
796
+ statement.body,
797
+ env: duplicate_env(env),
798
+ frame_expr:,
799
+ raw_frame_expr:,
800
+ async_info:,
801
+ active_defers:,
802
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
803
+ )
804
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
805
+ cond = lower_expression(prepared_cond, env:, expected_type: @ctx.types.fetch("bool"))
806
+
807
+ if condition_setup.empty?
808
+ stmts = [IR::WhileStmt.new(condition: cond, body:)]
809
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
810
+ return IR::BlockStmt.new(body: stmts)
811
+ end
812
+
813
+ loop_body = [
814
+ *condition_setup,
815
+ IR::IfStmt.new(
816
+ condition: IR::Unary.new(operator: "not", operand: cond, type: @ctx.types.fetch("bool")),
817
+ then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
818
+ else_body: nil,
819
+ ),
820
+ *body,
821
+ ]
822
+ stmts = [IR::WhileStmt.new(condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")), body: loop_body)]
823
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(loop_body, break_label)
824
+ IR::BlockStmt.new(body: stmts)
825
+ end
826
+
827
+ def lower_async_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
828
+ return lower_async_parallel_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:) if statement.parallel?
829
+
830
+ return lower_async_range_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:) if range_iterable?(statement.iterable)
831
+
832
+ lower_async_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers:)
833
+ end
834
+
835
+ def lower_async_range_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
836
+ loop_type = infer_range_loop_type(statement.iterable, env:)
837
+ start_expr = range_start_of(statement.iterable)
838
+ stop_expr = range_end_of(statement.iterable)
839
+ start_setup, prepared_start = prepare_expression_for_inline_lowering(start_expr, env:, expected_type: loop_type)
840
+ stop_setup, prepared_stop = prepare_expression_for_inline_lowering(stop_expr, env:, expected_type: loop_type)
841
+ index_linkage_name = c_local_name(statement.name)
842
+ stop_linkage_name = fresh_c_temp_name(env, "for_stop")
843
+ continue_label = fresh_c_temp_name(env, "loop_continue")
844
+ break_label = fresh_c_temp_name(env, "loop_break")
845
+ index_ref = IR::Name.new(name: index_linkage_name, type: loop_type, pointer: false)
846
+ inline_stop = stop_setup.empty? && compile_time_numeric_const_expression?(prepared_stop)
847
+ stop_value = if inline_stop
848
+ lower_expression(prepared_stop, env:, expected_type: loop_type)
849
+ else
850
+ IR::Name.new(name: stop_linkage_name, type: loop_type, pointer: false)
851
+ end
852
+
853
+ while_env = duplicate_env(env)
854
+ current_actual_scope(while_env[:scopes])[statement.name] = local_binding(
855
+ type: loop_type, linkage_name: index_linkage_name, mutable: false, pointer: false
856
+ )
857
+ body = lower_async_non_await_statements(
858
+ statement.body,
859
+ env: while_env,
860
+ frame_expr:,
861
+ raw_frame_expr:,
862
+ async_info:,
863
+ active_defers:,
864
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
865
+ )
866
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
867
+
868
+ for_statement = IR::ForStmt.new(
869
+ init: IR::LocalDecl.new(name: statement.name, linkage_name: index_linkage_name, type: loop_type, value: lower_expression(prepared_start, env:, expected_type: loop_type)),
870
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
871
+ post: IR::Assignment.new(target: index_ref, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: loop_type)),
872
+ body:,
873
+ )
874
+
875
+ stmts = [
876
+ *start_setup,
877
+ *stop_setup,
878
+ *(inline_stop ? [] : [IR::LocalDecl.new(name: stop_linkage_name, linkage_name: stop_linkage_name, type: loop_type, value: lower_expression(prepared_stop, env:, expected_type: loop_type))]),
879
+ for_statement,
880
+ ]
881
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
882
+ IR::BlockStmt.new(body: stmts)
883
+ end
884
+
885
+ def lower_async_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
886
+ iterable_type = infer_expression_type(statement.iterable, env:)
887
+ element_type = collection_loop_type(iterable_type)
888
+ raise LoweringError, "for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}" unless element_type
889
+
890
+ iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
891
+ iterable_linkage_name = fresh_c_temp_name(env, "for_items")
892
+ index_linkage_name = fresh_c_temp_name(env, "for_index")
893
+ continue_label = fresh_c_temp_name(env, "loop_continue")
894
+ break_label = fresh_c_temp_name(env, "loop_break")
895
+ iterable_ref = IR::Name.new(name: iterable_linkage_name, type: iterable_type, pointer: false)
896
+ index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
897
+
898
+ item_value = if array_type?(iterable_type)
899
+ IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
900
+ else
901
+ data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
902
+ IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
903
+ end
904
+ stop_value = if array_type?(iterable_type)
905
+ IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
906
+ else
907
+ IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
908
+ end
909
+
910
+ while_env = duplicate_env(env)
911
+ current_actual_scope(while_env[:scopes])[statement.name] = local_binding(
912
+ type: element_type, linkage_name: c_local_name(statement.name), mutable: false, pointer: false
913
+ )
914
+ body = [IR::LocalDecl.new(name: statement.name, linkage_name: c_local_name(statement.name), type: element_type, value: item_value)]
915
+ body.concat(lower_async_non_await_statements(
916
+ statement.body,
917
+ env: while_env,
918
+ frame_expr:,
919
+ raw_frame_expr:,
920
+ async_info:,
921
+ active_defers:,
922
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
923
+ ))
924
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
925
+
926
+ for_statement = IR::ForStmt.new(
927
+ init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
928
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
929
+ post: IR::Assignment.new(target: index_ref, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
930
+ body:,
931
+ )
932
+
933
+ stmts = [
934
+ *iterable_setup,
935
+ IR::LocalDecl.new(name: iterable_linkage_name, linkage_name: iterable_linkage_name, type: iterable_type, value: lower_expression(prepared_iterable, env:, expected_type: iterable_type)),
936
+ for_statement,
937
+ ]
938
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
939
+ IR::BlockStmt.new(body: stmts)
940
+ end
941
+
942
+ def lower_async_parallel_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
943
+ infos = statement.bindings.each_with_index.map do |binding, index|
944
+ iterable = statement.iterables[index]
945
+ iterable_type = infer_expression_type(iterable, env:)
946
+ element_type = collection_loop_type(iterable_type)
947
+ raise LoweringError, "parallel for loops expect arrays or spans for each iterable, got #{iterable_type}" unless element_type
948
+
949
+ {
950
+ binding:,
951
+ iterable:,
952
+ iterable_type:,
953
+ element_type:,
954
+ binding_type: collection_loop_binding_type(iterable_type, element_type) || element_type,
955
+ }
956
+ end
957
+
958
+ iterable_entries = infos.map do |info|
959
+ setup, prepared_iterable = prepare_expression_for_inline_lowering(info[:iterable], env:, expected_type: info[:iterable_type])
960
+ linkage_name = fresh_c_temp_name(env, "for_items")
961
+ info.merge(
962
+ setup:,
963
+ prepared_iterable:,
964
+ iterable_linkage_name: linkage_name,
965
+ iterable_ref: IR::Name.new(name: linkage_name, type: info[:iterable_type], pointer: false),
966
+ )
967
+ end
968
+
969
+ index_linkage_name = fresh_c_temp_name(env, "for_index")
970
+ continue_label = fresh_c_temp_name(env, "loop_continue")
971
+ break_label = fresh_c_temp_name(env, "loop_break")
972
+ index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
973
+ stop_value = collection_loop_stop_value(iterable_entries.first[:iterable_ref], iterable_entries.first[:iterable_type])
974
+
975
+ while_env = duplicate_env(env)
976
+ body = iterable_entries.map do |entry|
977
+ item_value = collection_loop_item_value(entry[:iterable_ref], entry[:iterable_type], index_ref, entry[:element_type])
978
+ loop_item_value = if ref_type?(entry[:binding_type])
979
+ IR::AddressOf.new(expression: item_value, type: entry[:binding_type])
980
+ else
981
+ item_value
982
+ end
983
+ binding = entry[:binding]
984
+ current_actual_scope(while_env[:scopes])[binding.name] = local_binding(type: entry[:binding_type], linkage_name: c_local_name(binding.name), mutable: false, pointer: false)
985
+ IR::LocalDecl.new(name: binding.name, linkage_name: c_local_name(binding.name), type: entry[:binding_type], value: loop_item_value)
986
+ end
987
+ body.concat(lower_async_non_await_statements(
988
+ statement.body,
989
+ env: while_env,
990
+ frame_expr:,
991
+ raw_frame_expr:,
992
+ async_info:,
993
+ active_defers:,
994
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
995
+ ))
996
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
997
+
998
+ length_checks = iterable_entries.drop(1).map do |entry|
999
+ IR::IfStmt.new(
1000
+ condition: IR::Binary.new(
1001
+ operator: "!=",
1002
+ left: collection_loop_stop_value(entry[:iterable_ref], entry[:iterable_type]),
1003
+ right: stop_value,
1004
+ type: @ctx.types.fetch("bool"),
1005
+ ),
1006
+ then_body: [lower_fatal_statement("parallel for iterables must have matching lengths", env:)],
1007
+ else_body: nil,
1008
+ )
1009
+ end
1010
+
1011
+ for_statement = IR::ForStmt.new(
1012
+ init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
1013
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
1014
+ post: IR::Assignment.new(target: index_ref, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
1015
+ body:,
1016
+ )
1017
+
1018
+ stmts = [
1019
+ *iterable_entries.flat_map { |entry| entry[:setup] },
1020
+ *iterable_entries.map do |entry|
1021
+ IR::LocalDecl.new(name: entry[:iterable_linkage_name], linkage_name: entry[:iterable_linkage_name], type: entry[:iterable_type], value: lower_expression(entry[:prepared_iterable], env:, expected_type: entry[:iterable_type]))
1022
+ end,
1023
+ *length_checks,
1024
+ for_statement,
1025
+ ]
1026
+ stmts << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
1027
+ IR::BlockStmt.new(body: stmts)
1028
+ end
1029
+
1030
+ def lower_async_assignment_statement(statement, env:)
1031
+ lowered = []
1032
+ target = lower_assignment_target(statement.target, env:)
1033
+ prepared_setup, prepared_value = prepare_expression_for_inline_lowering(
1034
+ statement.value,
1035
+ env:,
1036
+ expected_type: target.type,
1037
+ allow_root_statement_foreign: true,
1038
+ )
1039
+ lowered.concat(prepared_setup)
1040
+
1041
+ if (foreign_call = foreign_call_info(prepared_value, env))
1042
+ setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
1043
+ foreign_call,
1044
+ env:,
1045
+ expected_type: target.type,
1046
+ statement_position: false,
1047
+ )
1048
+ lowered.concat(setup)
1049
+ raise LoweringError, "foreign call used in assignment must return a value" if call_type == @ctx.types.fetch("void")
1050
+ raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
1051
+
1052
+ lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
1053
+ lowered.concat(cleanup_statements)
1054
+ update_cstr_metadata_for_assignment!(statement, prepared_value, env)
1055
+ return lowered
1056
+ end
1057
+
1058
+ value = if statement.operator == "="
1059
+ lower_contextual_expression(
1060
+ prepared_value,
1061
+ env:,
1062
+ expected_type: target.type,
1063
+ external_numeric: external_numeric_assignment_target?(statement.target, env:),
1064
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
1065
+ )
1066
+ elsif ["+=", "-=", "*=", "/="].include?(statement.operator)
1067
+ lower_contextual_expression(
1068
+ prepared_value,
1069
+ env:,
1070
+ expected_type: target.type,
1071
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
1072
+ )
1073
+ else
1074
+ lower_expression(prepared_value, env:, expected_type: target.type)
1075
+ end
1076
+ update_cstr_metadata_for_assignment!(statement, prepared_value, env)
1077
+ if statement.operator == "=" && contains_proc_storage_type?(target.type)
1078
+ rhs_name = fresh_c_temp_name(env, "proc_assign")
1079
+ lowered << IR::LocalDecl.new(name: rhs_name, linkage_name: rhs_name, type: target.type, value:)
1080
+ rhs = IR::Name.new(name: rhs_name, type: target.type, pointer: false)
1081
+ lowered.concat(lower_proc_selective_retain_statements(rhs, statement.value, target.type))
1082
+ lowered.concat(lower_proc_contained_guarded_release_statements(target, target.type))
1083
+ lowered << IR::Assignment.new(target:, operator: "=", value: rhs)
1084
+ else
1085
+ lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
1086
+ end
1087
+ lowered
1088
+ end
1089
+
1090
+ def lower_async_expression_statement(statement, env:)
1091
+ lowered = []
1092
+ expression_expected_type = if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?"
1093
+ nil
1094
+ else
1095
+ infer_expression_type(statement.expression, env:)
1096
+ end
1097
+ prepared_setup, prepared_expression = prepare_expression_for_inline_lowering(
1098
+ statement.expression,
1099
+ env:,
1100
+ expected_type: expression_expected_type,
1101
+ allow_root_statement_foreign: true,
1102
+ allow_void_propagation: true,
1103
+ )
1104
+ lowered.concat(prepared_setup)
1105
+
1106
+ if prepared_expression && (foreign_call = foreign_call_info(prepared_expression, env))
1107
+ setup, = lower_foreign_call_statement(
1108
+ foreign_call,
1109
+ env:,
1110
+ expected_type: foreign_call[:binding].type.return_type,
1111
+ statement_position: true,
1112
+ discard_result: true,
1113
+ )
1114
+ lowered.concat(setup)
1115
+ elsif prepared_expression
1116
+ lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env:), line: statement.line, source_path: @ctx.current_analysis_path)
1117
+ end
1118
+
1119
+ lowered
1120
+ end
1121
+ def lower_async_return_statement(statement, env:, frame_expr:, raw_frame_expr:, async_info:, cleanup: [])
1122
+ lowered = []
1123
+ value = nil
1124
+ prepared_setup = []
1125
+ prepared_value = statement.value
1126
+
1127
+ if statement.value
1128
+ prepared_setup, prepared_value = prepare_expression_for_inline_lowering(
1129
+ statement.value,
1130
+ env:,
1131
+ expected_type: async_info[:result_type],
1132
+ allow_root_statement_foreign: true,
1133
+ )
1134
+ lowered.concat(prepared_setup)
1135
+ end
1136
+
1137
+ if prepared_value && (foreign_call = foreign_call_info(prepared_value, env))
1138
+ setup, value = lower_foreign_call_statement(foreign_call, env:, expected_type: async_info[:result_type], statement_position: false)
1139
+ lowered.concat(setup)
1140
+ elsif prepared_value
1141
+ value = lower_contextual_expression(
1142
+ prepared_value,
1143
+ env:,
1144
+ expected_type: async_info[:result_type],
1145
+ contextual_int_to_float: contextual_int_to_float_target?(async_info[:result_type]),
1146
+ )
1147
+ end
1148
+
1149
+ if async_info[:result_type] != @ctx.types.fetch("void") && value && cleanup.any? && !cleanup_safe_return_expression?(prepared_value)
1150
+ lowered << IR::Assignment.new(
1151
+ target: async_frame_field_expression(frame_expr, "result", async_info[:result_type]),
1152
+ operator: "=",
1153
+ value: value,
1154
+ )
1155
+ lowered.concat(cleanup)
1156
+ lowered.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value: nil, result_already_stored: true))
1157
+ else
1158
+ lowered.concat(cleanup)
1159
+ lowered.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value:))
1160
+ end
1161
+ lowered
1162
+ end
1163
+
1164
+ def lower_async_await_statement(statement, await_info:, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, field_info: nil, cleanup: [], active_defers: [], loop_flow: nil)
1165
+ lowered = []
1166
+ await_expression = case statement
1167
+ when AST::LocalDecl then statement.value
1168
+ when AST::Assignment then statement.value
1169
+ when AST::ExpressionStmt then statement.expression
1170
+ when AST::ReturnStmt then statement.value
1171
+ end
1172
+ prepared_setup, prepared_task = prepare_expression_for_inline_lowering(
1173
+ await_expression.expression,
1174
+ env:,
1175
+ expected_type: await_info[:task_type],
1176
+ )
1177
+ lowered.concat(prepared_setup)
1178
+ raise LoweringError, "await does not support foreign task expressions" if foreign_call_info(prepared_task, env)
1179
+
1180
+ task_expr = async_frame_field_expression(frame_expr, await_info[:field_name], await_info[:task_type])
1181
+ task_frame_expr = async_task_frame_expression(task_expr, await_info[:task_type])
1182
+ ready_call = async_task_call(task_expr, await_info[:task_type], "ready", [task_frame_expr], @ctx.types.fetch("bool"))
1183
+ set_waiter_call = async_task_call(
1184
+ task_expr,
1185
+ await_info[:task_type],
1186
+ "set_waiter",
1187
+ [
1188
+ task_frame_expr,
1189
+ raw_frame_expr,
1190
+ IR::Name.new(name: resume_linkage_name, type: async_info[:wake_type], pointer: false),
1191
+ ],
1192
+ @ctx.types.fetch("void"),
1193
+ )
1194
+ take_result_call = async_task_call(task_expr, await_info[:task_type], "take_result", [task_frame_expr], await_info[:result_type])
1195
+ release_call = async_task_call(task_expr, await_info[:task_type], "release", [task_frame_expr], @ctx.types.fetch("void"))
1196
+
1197
+ unless await_info[:reuse_existing_storage]
1198
+ lowered << IR::Assignment.new(
1199
+ target: task_expr,
1200
+ operator: "=",
1201
+ value: lower_contextual_expression(prepared_task, env:, expected_type: await_info[:task_type]),
1202
+ )
1203
+ end
1204
+ lowered << IR::IfStmt.new(
1205
+ condition: IR::Unary.new(operator: "not", operand: ready_call, type: @ctx.types.fetch("bool")),
1206
+ then_body: [
1207
+ IR::Assignment.new(
1208
+ target: async_frame_field_expression(frame_expr, "state", @ctx.types.fetch("int")),
1209
+ operator: "=",
1210
+ value: IR::IntegerLiteral.new(value: await_info[:state], type: @ctx.types.fetch("int")),
1211
+ ),
1212
+ IR::ExpressionStmt.new(expression: set_waiter_call),
1213
+ IR::ReturnStmt.new(value: nil),
1214
+ ],
1215
+ else_body: nil,
1216
+ )
1217
+ lowered << IR::LabelStmt.new(name: async_state_label(resume_linkage_name, await_info[:state]))
1218
+
1219
+ case statement
1220
+ when AST::LocalDecl
1221
+ storage_type = field_info[:storage_type]
1222
+ target = async_frame_field_expression(frame_expr, field_info[:field_name], storage_type)
1223
+ lowered << IR::Assignment.new(target:, operator: "=", value: take_result_call)
1224
+ lowered << IR::ExpressionStmt.new(expression: release_call)
1225
+ if statement.else_body
1226
+ else_env = duplicate_env(env)
1227
+ if statement.else_binding
1228
+ current_actual_scope(else_env[:scopes])[statement.else_binding.name] = local_binding(
1229
+ type: let_else_error_type(storage_type),
1230
+ storage_type:,
1231
+ linkage_name: async_frame_field_c_name(field_info[:field_name]),
1232
+ mutable: false,
1233
+ pointer: false,
1234
+ projection: :result_failure_error,
1235
+ )
1236
+ end
1237
+ else_body = if statements_contain_await?(statement.else_body, async_info)
1238
+ lower_async_cf_statements(
1239
+ statement.else_body,
1240
+ env: else_env,
1241
+ frame_expr:,
1242
+ raw_frame_expr:,
1243
+ resume_linkage_name:,
1244
+ async_info:,
1245
+ active_defers:,
1246
+ loop_flow:,
1247
+ )
1248
+ else
1249
+ lower_async_non_await_statements(
1250
+ statement.else_body,
1251
+ env: else_env,
1252
+ frame_expr:,
1253
+ raw_frame_expr:,
1254
+ async_info:,
1255
+ active_defers:,
1256
+ loop_flow:,
1257
+ )
1258
+ end
1259
+ lowered << IR::IfStmt.new(
1260
+ condition: let_else_failure_condition(target, storage_type),
1261
+ then_body: else_body,
1262
+ else_body: nil,
1263
+ )
1264
+ end
1265
+ when AST::Assignment
1266
+ lowered << IR::Assignment.new(target: lower_assignment_target(statement.target, env:), operator: statement.operator, value: take_result_call)
1267
+ lowered << IR::ExpressionStmt.new(expression: release_call)
1268
+ when AST::ExpressionStmt
1269
+ lowered << IR::ExpressionStmt.new(expression: take_result_call)
1270
+ lowered << IR::ExpressionStmt.new(expression: release_call)
1271
+ when AST::ReturnStmt
1272
+ if await_info[:result_type] == @ctx.types.fetch("void")
1273
+ lowered << IR::ExpressionStmt.new(expression: take_result_call)
1274
+ lowered << IR::ExpressionStmt.new(expression: release_call)
1275
+ lowered.concat(cleanup)
1276
+ lowered.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value: nil, result_already_stored: true))
1277
+ else
1278
+ lowered << IR::Assignment.new(
1279
+ target: async_frame_field_expression(frame_expr, "result", async_info[:result_type]),
1280
+ operator: "=",
1281
+ value: take_result_call,
1282
+ )
1283
+ lowered << IR::ExpressionStmt.new(expression: release_call)
1284
+ lowered.concat(cleanup)
1285
+ lowered.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value: nil, result_already_stored: true))
1286
+ end
1287
+ end
1288
+
1289
+ lowered
1290
+ end
1291
+
1292
+ def lower_async_defer_cleanup(statement, env:, async_info:)
1293
+ body = if statement.body
1294
+ statement.body
1295
+ elsif statement.expression
1296
+ [AST::ExpressionStmt.new(expression: statement.expression, line: statement.line)]
1297
+ else
1298
+ []
1299
+ end
1300
+
1301
+ { body:, env: snapshot_env(env) }
1302
+ end
1303
+
1304
+ def lower_async_cleanup_entries(local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
1305
+ cleanup_entries = local_defers.reverse + outer_defers.reverse
1306
+ cleanup_entries.flat_map do |cleanup_entry|
1307
+ next [] if cleanup_entry[:body].empty?
1308
+
1309
+ cleanup_env = duplicate_env(cleanup_entry[:env])
1310
+ if statements_contain_await?(cleanup_entry[:body], async_info)
1311
+ lower_async_cf_statements(
1312
+ cleanup_entry[:body],
1313
+ env: cleanup_env,
1314
+ frame_expr:,
1315
+ raw_frame_expr:,
1316
+ resume_linkage_name: async_info.fetch(:resume_linkage_name),
1317
+ async_info:,
1318
+ active_defers: [],
1319
+ loop_flow: nil,
1320
+ )
1321
+ else
1322
+ lower_async_non_await_statements(
1323
+ cleanup_entry[:body],
1324
+ env: cleanup_env,
1325
+ frame_expr:,
1326
+ raw_frame_expr:,
1327
+ async_info:,
1328
+ active_defers: [],
1329
+ loop_flow: nil,
1330
+ )
1331
+ end
1332
+ end
1333
+ end
1334
+
1335
+ def async_return_context(return_type:, active_defers:, local_defers:, frame_expr:, raw_frame_expr:, async_info:, allow_return: true)
1336
+ {
1337
+ return_type:,
1338
+ active_defers:,
1339
+ local_defers:,
1340
+ allow_return:,
1341
+ frame_expr:,
1342
+ raw_frame_expr:,
1343
+ async_info:,
1344
+ }
1345
+ end
1346
+
1347
+ def async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value:, result_already_stored: false)
1348
+ lowered = []
1349
+
1350
+ if async_info[:result_type] != @ctx.types.fetch("void") && !result_already_stored
1351
+ lowered << IR::Assignment.new(
1352
+ target: async_frame_field_expression(frame_expr, "result", async_info[:result_type]),
1353
+ operator: "=",
1354
+ value: value,
1355
+ )
1356
+ end
1357
+
1358
+ lowered << IR::Assignment.new(
1359
+ target: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")),
1360
+ operator: "=",
1361
+ value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
1362
+ )
1363
+
1364
+ waiter_frame_field = async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr])
1365
+ lowered << IR::IfStmt.new(
1366
+ condition: IR::Binary.new(
1367
+ operator: "!=",
1368
+ left: waiter_frame_field,
1369
+ right: IR::NullLiteral.new(type: async_info[:void_ptr]),
1370
+ type: @ctx.types.fetch("bool"),
1371
+ ),
1372
+ then_body: [
1373
+ IR::LocalDecl.new(
1374
+ name: "waiter_frame",
1375
+ linkage_name: "__mt_waiter_frame",
1376
+ type: async_info[:void_ptr],
1377
+ value: waiter_frame_field,
1378
+ ),
1379
+ IR::Assignment.new(
1380
+ target: waiter_frame_field,
1381
+ operator: "=",
1382
+ value: IR::NullLiteral.new(type: async_info[:void_ptr]),
1383
+ ),
1384
+ IR::ExpressionStmt.new(
1385
+ expression: IR::Call.new(
1386
+ callee: async_frame_field_expression(frame_expr, "waiter", async_info[:wake_type]),
1387
+ arguments: [IR::Name.new(name: "__mt_waiter_frame", type: async_info[:void_ptr], pointer: false)],
1388
+ type: @ctx.types.fetch("void"),
1389
+ ),
1390
+ ),
1391
+ IR::ReturnStmt.new(value: nil),
1392
+ ],
1393
+ else_body: nil,
1394
+ )
1395
+ lowered << IR::ReturnStmt.new(value: nil)
1396
+ lowered
1397
+ end
1398
+ end
1399
+ end