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,459 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LowererAsync
5
+ private
6
+
7
+ def normalize_async_body(binding, statements)
8
+ counter = { value: 0 }
9
+ env = empty_env
10
+ binding.body_params.each do |param_binding|
11
+ env[:scopes].last[param_binding.name] = local_binding(
12
+ type: param_binding.type,
13
+ linkage_name: param_binding.name,
14
+ mutable: param_binding.mutable,
15
+ pointer: false,
16
+ )
17
+ end
18
+ env[:return_context] = {
19
+ return_type: binding.body_return_type,
20
+ active_defers: [],
21
+ local_defers: [],
22
+ allow_return: true,
23
+ }
24
+ normalize_async_statements(statements, counter, env, return_type: binding.body_return_type)
25
+ end
26
+
27
+ def normalize_async_statements(statements, counter, env, return_type:)
28
+ statements.flat_map { |statement| normalize_async_statement(statement, counter, env, return_type:) }
29
+ end
30
+
31
+ def normalize_async_statement(statement, counter, env, return_type:)
32
+ case statement
33
+ when AST::LocalDecl
34
+ if statement.value
35
+ local_type, storage_type = async_local_decl_types(statement, env:)
36
+ expected_type = statement.else_body ? storage_type : (statement.type ? resolve_type_ref(statement.type) : nil)
37
+ setup, value = if statement.value.is_a?(AST::AwaitExpr)
38
+ inner_setup, inner_expr = normalize_async_expression(statement.value.expression, counter, env:, expected_type: expected_type)
39
+ [inner_setup, AST::AwaitExpr.new(expression: inner_expr)]
40
+ else
41
+ normalize_async_expression(statement.value, counter, env:, expected_type: expected_type)
42
+ end
43
+ else_body = if statement.else_body
44
+ else_env = duplicate_env(env)
45
+ normalize_async_statements(statement.else_body, counter, else_env, return_type:)
46
+ end
47
+ normalized = AST::LocalDecl.new(kind: statement.kind, name: statement.name, type: statement.type, value: value, else_binding: statement.else_binding, else_body:, line: statement.line)
48
+ if bind_let_else_local?(statement)
49
+ current_actual_scope(env[:scopes])[statement.name] = local_binding(
50
+ type: local_type,
51
+ storage_type:,
52
+ linkage_name: statement.name,
53
+ mutable: statement.kind == :var,
54
+ pointer: false,
55
+ projection: statement.else_body ? let_else_binding_projection(storage_type) : nil,
56
+ const_value: statement.else_body ? nil : statement.kind == :let ? compile_time_const_value(statement.value, env:) : nil,
57
+ )
58
+ end
59
+ return setup + [normalized]
60
+ end
61
+
62
+ local_type = resolve_type_ref(statement.type)
63
+ current_actual_scope(env[:scopes])[statement.name] = local_binding(
64
+ type: local_type,
65
+ storage_type: local_type,
66
+ linkage_name: statement.name,
67
+ mutable: statement.kind == :var,
68
+ pointer: false,
69
+ const_value: nil,
70
+ )
71
+ [statement]
72
+ when AST::Assignment
73
+ target_setup, target = normalize_async_assignment_target(statement.target, counter, env:)
74
+ return target_setup + [AST::Assignment.new(target:, operator: statement.operator, value: statement.value)] if statement.value.is_a?(AST::AwaitExpr)
75
+
76
+ target_type = infer_expression_type(statement.target, env:)
77
+ setup, value = normalize_async_expression(statement.value, counter, env:, expected_type: target_type)
78
+ target_setup + setup + [AST::Assignment.new(target:, operator: statement.operator, value: value)]
79
+ when AST::ExpressionStmt
80
+ return [statement] if statement.expression.is_a?(AST::AwaitExpr)
81
+
82
+ setup, expression = normalize_async_expression(statement.expression, counter, env:)
83
+ setup + [AST::ExpressionStmt.new(expression: expression, line: statement.line)]
84
+ when AST::ReturnStmt
85
+ return [statement] unless statement.value
86
+ return [statement] if statement.value.is_a?(AST::AwaitExpr)
87
+
88
+ setup, value = normalize_async_expression(statement.value, counter, env:, expected_type: return_type)
89
+ setup + [AST::ReturnStmt.new(value: value, line: statement.line)]
90
+ when AST::IfStmt
91
+ normalize_async_if_statement(statement, counter, env, return_type:)
92
+ when AST::MatchStmt
93
+ expr_setup, expression = normalize_async_expression(statement.expression, counter, env:)
94
+ scrutinee_type = infer_expression_type(statement.expression, env:)
95
+ arms = statement.arms.map do |arm|
96
+ arm_env = duplicate_env(env)
97
+ bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
98
+ AST::MatchArm.new(pattern: arm.pattern, binding_name: arm.binding_name, body: normalize_async_statements(arm.body, counter, arm_env, return_type:))
99
+ end
100
+ expr_setup + [AST::MatchStmt.new(expression:, arms:)]
101
+ when AST::WhileStmt
102
+ condition_setup, condition = normalize_async_expression(statement.condition, counter, env:, expected_type: @ctx.types.fetch("bool"))
103
+ body_env = duplicate_env(env)
104
+ body = normalize_async_statements(statement.body, counter, body_env, return_type:)
105
+ if condition_setup.empty?
106
+ [AST::WhileStmt.new(condition:, body:)]
107
+ else
108
+ cond_name = fresh_async_temp_name(counter)
109
+ condition_eval = condition_setup + [AST::LocalDecl.new(kind: :let, name: cond_name, type: ast_type_ref_for(@ctx.types.fetch("bool")), value: condition)]
110
+ [
111
+ AST::WhileStmt.new(
112
+ condition: AST::BooleanLiteral.new(value: true),
113
+ body: condition_eval + [
114
+ AST::IfStmt.new(
115
+ branches: [AST::IfBranch.new(condition: AST::UnaryOp.new(operator: "not", operand: AST::Identifier.new(name: cond_name)), body: [AST::BreakStmt.new])],
116
+ else_body: nil,
117
+ ),
118
+ *body,
119
+ ],
120
+ ),
121
+ ]
122
+ end
123
+ when AST::ForStmt
124
+ original_iterable = statement.iterable
125
+ loop_type = if range_iterable?(original_iterable)
126
+ infer_range_loop_type(original_iterable, env:)
127
+ else
128
+ iterable_type = infer_expression_type(original_iterable, env:)
129
+ collection_loop_type(iterable_type)
130
+ end
131
+ for_env = duplicate_env(env)
132
+ if statement.parallel?
133
+ iterable_setups = []
134
+ normalized_iterables = statement.iterables.map do |iterable|
135
+ setup, normalized_iterable = normalize_async_expression(iterable, counter, env:)
136
+ iterable_setups.concat(setup)
137
+ normalized_iterable
138
+ end
139
+ statement.bindings.each_with_index do |binding, index|
140
+ iterable_type = infer_expression_type(statement.iterables[index], env:)
141
+ element_type = collection_loop_type(iterable_type)
142
+ binding_type = collection_loop_binding_type(iterable_type, element_type) || element_type
143
+ current_actual_scope(for_env[:scopes])[binding.name] = local_binding(type: binding_type, linkage_name: binding.name, mutable: false, pointer: false)
144
+ end
145
+ body = normalize_async_statements(statement.body, counter, for_env, return_type:)
146
+ return iterable_setups + [AST::ForStmt.new(bindings: statement.bindings, iterables: normalized_iterables, body:, line: statement.line, column: statement.column)]
147
+ end
148
+
149
+ iterable_setup, iterable = normalize_async_expression(statement.iterable, counter, env:)
150
+ current_actual_scope(for_env[:scopes])[statement.name] = local_binding(type: loop_type, linkage_name: statement.name, mutable: false, pointer: false)
151
+ body = normalize_async_statements(statement.body, counter, for_env, return_type:)
152
+ iterable_setup + [AST::ForStmt.new(bindings: statement.bindings, iterables: [iterable], body:, line: statement.line, column: statement.column)]
153
+ when AST::UnsafeStmt
154
+ unsafe_env = duplicate_env(env)
155
+ [AST::UnsafeStmt.new(body: normalize_async_statements(statement.body, counter, unsafe_env, return_type:), line: statement.line, column: statement.column, length: statement.length)]
156
+ when AST::DeferStmt
157
+ cleanup_env = duplicate_env(env)
158
+ cleanup_env[:return_context] = cleanup_env[:return_context]&.merge(allow_return: false)
159
+ cleanup_body = if statement.body
160
+ normalize_async_statements(statement.body, counter, cleanup_env, return_type:)
161
+ else
162
+ expression_setup, expression = normalize_async_expression(statement.expression, counter, env: cleanup_env)
163
+ expression_setup + [AST::ExpressionStmt.new(expression:, line: statement.line)]
164
+ end
165
+ [AST::DeferStmt.new(expression: nil, body: cleanup_body, line: statement.line, column: statement.column, length: statement.length)]
166
+ when AST::BreakStmt, AST::ContinueStmt, AST::StaticAssert, AST::PassStmt
167
+ [statement]
168
+ else
169
+ raise LoweringError, "unsupported async statement #{statement.class.name}"
170
+ end
171
+ end
172
+
173
+ def normalize_async_if_statement(statement, counter, env, return_type:)
174
+ else_body = if statement.else_body
175
+ else_env = duplicate_env(env)
176
+ normalize_async_statements(statement.else_body, counter, else_env, return_type:)
177
+ end
178
+ normalize_async_if_branches(statement.branches, else_body, counter, env, return_type:)
179
+ end
180
+
181
+ def normalize_async_if_branches(branches, else_body, counter, env, return_type:)
182
+ return else_body || [] if branches.empty?
183
+
184
+ branch = branches.first
185
+ condition_setup, condition = normalize_async_expression(branch.condition, counter, env:, expected_type: @ctx.types.fetch("bool"))
186
+ then_env = duplicate_env(env)
187
+ then_body = normalize_async_statements(branch.body, counter, then_env, return_type:)
188
+ chained_else = normalize_async_if_branches(branches.drop(1), else_body, counter, env, return_type:)
189
+ condition_setup + [AST::IfStmt.new(branches: [AST::IfBranch.new(condition:, body: then_body)], else_body: chained_else)]
190
+ end
191
+
192
+ def normalize_async_assignment_target(target, counter, env:)
193
+ case target
194
+ when AST::Identifier
195
+ [[], target]
196
+ when AST::MemberAccess
197
+ receiver_setup, receiver = normalize_async_expression(target.receiver, counter, env:)
198
+ [receiver_setup, AST::MemberAccess.new(receiver:, member: target.member)]
199
+ when AST::IndexAccess
200
+ receiver_setup, receiver = normalize_async_expression(target.receiver, counter, env:)
201
+ index_setup, index = normalize_async_expression(target.index, counter, env:)
202
+ [receiver_setup + index_setup, AST::IndexAccess.new(receiver:, index:)]
203
+ when AST::Call
204
+ if read_call?(target)
205
+ setup = []
206
+ normalized_args = target.arguments.map do |arg|
207
+ arg_setup, value = normalize_async_expression(arg.value, counter, env:)
208
+ setup.concat(arg_setup)
209
+ AST::Argument.new(name: arg.name, value:)
210
+ end
211
+ [setup, AST::Call.new(callee: target.callee, arguments: normalized_args)]
212
+ else
213
+ raise LoweringError, "unsupported assignment target #{target.class.name}"
214
+ end
215
+ else
216
+ raise LoweringError, "unsupported assignment target #{target.class.name}"
217
+ end
218
+ end
219
+
220
+ def normalize_async_expression(expression, counter, env:, expected_type: nil)
221
+ case expression
222
+ when AST::AwaitExpr
223
+ inner_setup, inner = normalize_async_expression(expression.expression, counter, env:)
224
+ temp_name = fresh_async_temp_name(counter)
225
+ [
226
+ inner_setup + [AST::LocalDecl.new(kind: :let, name: temp_name, type: nil, value: AST::AwaitExpr.new(expression: inner))],
227
+ AST::Identifier.new(name: temp_name),
228
+ ]
229
+ when AST::Call
230
+ setup = []
231
+ callee_setup, callee = normalize_async_expression(expression.callee, counter, env:)
232
+ setup.concat(callee_setup)
233
+ arguments = expression.arguments.map do |argument|
234
+ argument_setup, value = normalize_async_expression(argument.value, counter, env:)
235
+ setup.concat(argument_setup)
236
+ AST::Argument.new(name: argument.name, value: value)
237
+ end
238
+ [setup, AST::Call.new(callee: callee, arguments: arguments)]
239
+ when AST::Specialization
240
+ setup = []
241
+ callee_setup, callee = normalize_async_expression(expression.callee, counter, env:)
242
+ setup.concat(callee_setup)
243
+ arguments = expression.arguments.map do |argument|
244
+ argument_setup, value = normalize_async_expression(argument.value, counter, env:)
245
+ setup.concat(argument_setup)
246
+ AST::TypeArgument.new(value: value)
247
+ end
248
+ [setup, AST::Specialization.new(callee: callee, arguments: arguments)]
249
+ when AST::PrefixCast
250
+ setup, expr = normalize_async_expression(expression.expression, counter, env:)
251
+ [setup, AST::PrefixCast.new(target_type: expression.target_type, expression: expr)]
252
+ when AST::UnaryOp
253
+ setup, operand = normalize_async_expression(expression.operand, counter, env:, expected_type: expected_type)
254
+ [setup, AST::UnaryOp.new(operator: expression.operator, operand: operand)]
255
+ when AST::BinaryOp
256
+ if %w[and or].include?(expression.operator)
257
+ left_setup, left = normalize_async_expression(expression.left, counter, env:, expected_type: @ctx.types.fetch("bool"))
258
+ right_setup, right = normalize_async_expression(expression.right, counter, env:, expected_type: @ctx.types.fetch("bool"))
259
+ temp_name = fresh_async_temp_name(counter)
260
+
261
+ temp_init = expression.operator == "and" ? AST::BooleanLiteral.new(value: false) : AST::BooleanLiteral.new(value: true)
262
+ short_circuit_value = expression.operator == "and" ? AST::BooleanLiteral.new(value: false) : AST::BooleanLiteral.new(value: true)
263
+
264
+ branch_body = right_setup + [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: right)]
265
+ else_body = [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: short_circuit_value)]
266
+
267
+ if expression.operator == "or"
268
+ branch_body, else_body = else_body, branch_body
269
+ end
270
+
271
+ setup = [AST::LocalDecl.new(kind: :var, name: temp_name, type: nil, value: temp_init)]
272
+ setup.concat(left_setup)
273
+ setup << AST::IfStmt.new(branches: [AST::IfBranch.new(condition: left, body: branch_body)], else_body: else_body)
274
+ return [setup, AST::Identifier.new(name: temp_name)]
275
+ end
276
+
277
+ left_setup, left = normalize_async_expression(expression.left, counter, env:)
278
+ right_setup, right = normalize_async_expression(expression.right, counter, env:)
279
+ [left_setup + right_setup, AST::BinaryOp.new(operator: expression.operator, left: left, right: right)]
280
+ when AST::IfExpr
281
+ condition_setup, condition = normalize_async_expression(expression.condition, counter, env:, expected_type: @ctx.types.fetch("bool"))
282
+ result_type = infer_expression_type(expression, env:, expected_type:)
283
+ then_setup, then_expression = normalize_async_expression(expression.then_expression, counter, env:, expected_type: result_type)
284
+ else_setup, else_expression = normalize_async_expression(expression.else_expression, counter, env:, expected_type: result_type)
285
+
286
+ return [[], AST::IfExpr.new(condition:, then_expression:, else_expression:)] if condition_setup.empty? && then_setup.empty? && else_setup.empty?
287
+
288
+ temp_name = fresh_async_temp_name(counter)
289
+ setup = condition_setup + [
290
+ AST::LocalDecl.new(kind: :var, name: temp_name, type: ast_type_ref_for(result_type), value: nil),
291
+ AST::IfStmt.new(
292
+ branches: [AST::IfBranch.new(condition:, body: then_setup + [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: then_expression)])],
293
+ else_body: else_setup + [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: else_expression)],
294
+ ),
295
+ ]
296
+ [setup, AST::Identifier.new(name: temp_name)]
297
+ when AST::MatchExpr
298
+ expression_setup, normalized_expression = normalize_async_expression(expression.expression, counter, env:)
299
+ result_type = infer_expression_type(expression, env:, expected_type:)
300
+ scrutinee_type = infer_expression_type(expression.expression, env:)
301
+ normalized_arms = expression.arms.map do |arm|
302
+ arm_env = duplicate_env(env)
303
+ bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
304
+ pattern_setup, normalized_pattern = normalize_async_expression(arm.pattern, counter, env:)
305
+ value_setup, normalized_value = normalize_async_expression(arm.value, counter, env: arm_env, expected_type: result_type)
306
+ [pattern_setup, value_setup, AST::MatchExprArm.new(
307
+ pattern: normalized_pattern,
308
+ binding_name: arm.binding_name,
309
+ binding_line: arm.binding_line,
310
+ binding_column: arm.binding_column,
311
+ value: normalized_value,
312
+ )]
313
+ end
314
+
315
+ if expression_setup.empty? && normalized_arms.all? { |pattern_setup, value_setup, _arm| pattern_setup.empty? && value_setup.empty? }
316
+ return [[], AST::MatchExpr.new(expression: normalized_expression, arms: normalized_arms.map(&:last), line: expression.line, column: expression.column, length: expression.length)]
317
+ end
318
+
319
+ temp_name = fresh_async_temp_name(counter)
320
+ setup = expression_setup + [
321
+ AST::LocalDecl.new(kind: :var, name: temp_name, type: ast_type_ref_for(result_type), value: nil),
322
+ AST::MatchStmt.new(
323
+ expression: normalized_expression,
324
+ arms: normalized_arms.map do |pattern_setup, value_setup, arm|
325
+ AST::MatchArm.new(
326
+ pattern: arm.pattern,
327
+ binding_name: arm.binding_name,
328
+ binding_line: arm.binding_line,
329
+ binding_column: arm.binding_column,
330
+ body: pattern_setup + value_setup + [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: arm.value)],
331
+ )
332
+ end,
333
+ line: expression.line,
334
+ column: expression.column,
335
+ length: expression.length,
336
+ ),
337
+ ]
338
+ [setup, AST::Identifier.new(name: temp_name)]
339
+ when AST::UnsafeExpr
340
+ normalize_async_expression(expression.expression, counter, env:, expected_type:)
341
+ when AST::MemberAccess
342
+ setup, receiver = normalize_async_expression(expression.receiver, counter, env:)
343
+ [setup, AST::MemberAccess.new(receiver: receiver, member: expression.member)]
344
+ when AST::IndexAccess
345
+ receiver_setup, receiver = normalize_async_expression(expression.receiver, counter, env:)
346
+ index_setup, index = normalize_async_expression(expression.index, counter, env:)
347
+ [receiver_setup + index_setup, AST::IndexAccess.new(receiver: receiver, index: index)]
348
+ when AST::RangeExpr
349
+ start_setup, start_expr = normalize_async_expression(expression.start_expr, counter, env:)
350
+ end_setup, end_expr = normalize_async_expression(expression.end_expr, counter, env:)
351
+ [start_setup + end_setup, AST::RangeExpr.new(start_expr:, end_expr:, line: expression.line, column: expression.column)]
352
+ when AST::FormatString
353
+ setup = []
354
+ parts = expression.parts.map do |part|
355
+ if part.is_a?(AST::FormatExprPart)
356
+ expression_setup, inner_expression = normalize_async_expression(part.expression, counter, env:)
357
+ setup.concat(expression_setup)
358
+ AST::FormatExprPart.new(expression: inner_expression, format_spec: part.format_spec)
359
+ else
360
+ part
361
+ end
362
+ end
363
+ [setup, AST::FormatString.new(parts: parts)]
364
+ else
365
+ [[], expression]
366
+ end
367
+ end
368
+
369
+ def ast_type_ref_for(type)
370
+ case type
371
+ when Types::Primitive
372
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
373
+ when Types::Nullable
374
+ inner = ast_type_ref_for(type.base)
375
+ raise LoweringError, "nullable annotation is only valid for named/generic types" unless inner.is_a?(AST::TypeRef)
376
+
377
+ AST::TypeRef.new(name: inner.name, arguments: inner.arguments, nullable: true)
378
+ when Types::GenericInstance
379
+ AST::TypeRef.new(
380
+ name: AST::QualifiedName.new(parts: type.name.split(".")),
381
+ arguments: type.arguments.map do |argument|
382
+ if argument.is_a?(Types::LiteralTypeArg)
383
+ AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
384
+ else
385
+ AST::TypeArgument.new(value: ast_type_ref_for(argument))
386
+ end
387
+ end,
388
+ nullable: false,
389
+ )
390
+ when Types::Span
391
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.element_type))], nullable: false)
392
+ when Types::Task
393
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["Task"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.result_type))], nullable: false)
394
+ when Types::TypeVar
395
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
396
+ when Types::StructInstance
397
+ base_parts = type.module_name ? type.module_name.split(".") + [type.name] : [type.name]
398
+ AST::TypeRef.new(
399
+ name: AST::QualifiedName.new(parts: base_parts),
400
+ arguments: type.arguments.map do |argument|
401
+ if argument.is_a?(Types::LiteralTypeArg)
402
+ AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
403
+ else
404
+ AST::TypeArgument.new(value: ast_type_ref_for(argument))
405
+ end
406
+ end,
407
+ nullable: false,
408
+ )
409
+ when Types::Struct, Types::Union, Types::Opaque, Types::Enum, Types::Flags
410
+ parts = type.module_name ? type.module_name.split(".") + [type.name] : [type.name]
411
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: parts), arguments: [], nullable: false)
412
+ when Types::Function
413
+ AST::FunctionType.new(
414
+ params: type.params.each_with_index.map { |param, i| AST::Param.new(name: param.name || "p#{i}", type: ast_type_ref_for(param.type)) },
415
+ return_type: ast_type_ref_for(type.return_type),
416
+ )
417
+ when Types::Proc
418
+ AST::ProcType.new(
419
+ params: type.params.each_with_index.map { |param, i| AST::Param.new(name: param.name || "p#{i}", type: ast_type_ref_for(param.type)) },
420
+ return_type: ast_type_ref_for(type.return_type),
421
+ )
422
+ else
423
+ raise LoweringError, "unsupported type for AST normalization #{type.class.name}"
424
+ end
425
+ end
426
+
427
+ def async_expression_contains_await?(expression)
428
+ case expression
429
+ when AST::AwaitExpr
430
+ true
431
+ when AST::Call, AST::Specialization
432
+ async_expression_contains_await?(expression.callee) || expression.arguments.any? { |argument| async_expression_contains_await?(argument.value) }
433
+ when AST::UnaryOp
434
+ async_expression_contains_await?(expression.operand)
435
+ when AST::BinaryOp
436
+ async_expression_contains_await?(expression.left) || async_expression_contains_await?(expression.right)
437
+ when AST::IfExpr
438
+ async_expression_contains_await?(expression.condition) || async_expression_contains_await?(expression.then_expression) || async_expression_contains_await?(expression.else_expression)
439
+ when AST::MatchExpr
440
+ async_expression_contains_await?(expression.expression) || expression.arms.any? { |arm| async_expression_contains_await?(arm.pattern) || async_expression_contains_await?(arm.value) }
441
+ when AST::UnsafeExpr
442
+ async_expression_contains_await?(expression.expression)
443
+ when AST::MemberAccess
444
+ async_expression_contains_await?(expression.receiver)
445
+ when AST::IndexAccess
446
+ async_expression_contains_await?(expression.receiver) || async_expression_contains_await?(expression.index)
447
+ when AST::FormatString
448
+ expression.parts.any? { |part| part.is_a?(AST::FormatExprPart) && async_expression_contains_await?(part.expression) }
449
+ else
450
+ false
451
+ end
452
+ end
453
+
454
+ def fresh_async_temp_name(counter)
455
+ counter[:value] += 1
456
+ "__mt_async_tmp_#{counter[:value]}"
457
+ end
458
+ end
459
+ end