mt-lang 0.3.8 → 0.3.10

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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -2,367 +2,364 @@
2
2
 
3
3
  module MilkTea
4
4
  module LowererBlock
5
- private
6
-
5
+ def lower_block(statements, env:, active_defers:, return_type:, loop_flow:, allow_return: true)
6
+ local_env = duplicate_env(env)
7
+ lowered = []
8
+ local_defers = []
9
+ local_env[:return_context] = {
10
+ return_type:,
11
+ active_defers:,
12
+ local_defers:,
13
+ allow_return:,
14
+ }
7
15
 
8
- def lower_block(statements, env:, active_defers:, return_type:, loop_flow:, allow_return: true)
9
- local_env = duplicate_env(env)
10
- lowered = []
11
- local_defers = []
12
- local_env[:return_context] = {
13
- return_type:,
14
- active_defers:,
15
- local_defers:,
16
- allow_return:,
17
- }
18
-
19
- statements.each do |statement|
20
- case statement
21
- when AST::DeferStmt
22
- local_defers << if statement.body
23
- lower_defer_cleanup_body(statement.body, env: local_env, return_type:)
24
- else
25
- lower_defer_cleanup_expression(statement.expression, env: local_env)
26
- end
27
- when AST::UnsafeStmt
28
- lower_block_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
29
- when AST::LocalDecl
30
- lower_block_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
31
- when AST::Assignment
32
- lower_block_assignment_stmt(statement, lowered:, local_defers:, local_env:)
33
- when AST::IfStmt
34
- lower_block_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
35
- when AST::MatchStmt
36
- lower_block_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
37
- when AST::StaticAssert
38
- lowered << lower_static_assert(statement, env: local_env)
39
- when AST::ForStmt
40
- if statement.inline
41
- lowered.concat(lower_inline_for_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
42
- else
43
- lowered << lower_for_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
44
- end
45
- when AST::ParallelBlockStmt
46
- lowered << lower_parallel_block_stmt(statement, env: local_env, active_defers: active_defers + local_defers)
47
- when AST::GatherStmt
48
- lowered << lower_gather_stmt(statement, env: local_env)
49
- when AST::WhileStmt
50
- if statement.inline
51
- lowered.concat(lower_inline_while_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
52
- else
53
- lowered << lower_while_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
54
- end
55
- when AST::PassStmt
56
- nil
57
- when AST::BreakStmt
58
- raise LoweringError.new("break must be inside a loop",
59
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
60
-
61
- lowered.concat(lower_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers]))
62
- when AST::ContinueStmt
63
- raise LoweringError.new("continue must be inside a loop",
64
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
65
-
66
- lowered.concat(lower_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers]))
67
- when AST::ReturnStmt
68
- lower_block_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
69
- when AST::ExpressionStmt
70
- lower_block_expression_stmt(statement, lowered:, local_defers:, local_env:)
71
- when AST::EmitStmt
72
- lowered.concat(lower_emit_stmt(statement, env: local_env))
73
- when AST::WhenStmt
74
- lower_block_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
16
+ statements.each do |statement|
17
+ case statement
18
+ when AST::DeferStmt
19
+ local_defers << if statement.body
20
+ lower_defer_cleanup_body(statement.body, env: local_env, return_type:)
21
+ else
22
+ lower_defer_cleanup_expression(statement.expression, env: local_env)
23
+ end
24
+ when AST::UnsafeStmt
25
+ lower_block_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
26
+ when AST::LocalDecl
27
+ lower_block_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
28
+ when AST::Assignment
29
+ lower_block_assignment_stmt(statement, lowered:, local_defers:, local_env:)
30
+ when AST::IfStmt
31
+ lower_block_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
32
+ when AST::MatchStmt
33
+ lower_block_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
34
+ when AST::StaticAssert
35
+ lowered << lower_static_assert(statement, env: local_env)
36
+ when AST::ForStmt
37
+ if statement.inline
38
+ lowered.concat(lower_inline_for_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
39
+ else
40
+ lowered << lower_for_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
41
+ end
42
+ when AST::ParallelBlockStmt
43
+ lowered << lower_parallel_block_stmt(statement, env: local_env, active_defers: active_defers + local_defers)
44
+ when AST::GatherStmt
45
+ lowered << lower_gather_stmt(statement, env: local_env)
46
+ when AST::WhileStmt
47
+ if statement.inline
48
+ lowered.concat(lower_inline_while_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
75
49
  else
76
- raise LoweringError.new("unsupported statement #{statement.class.name}",
77
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
50
+ lowered << lower_while_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
78
51
  end
52
+ when AST::PassStmt
53
+ nil
54
+ when AST::BreakStmt
55
+ raise LoweringError.new("break must be inside a loop",
56
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
57
+
58
+ lowered.concat(lower_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers]))
59
+ when AST::ContinueStmt
60
+ raise LoweringError.new("continue must be inside a loop",
61
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
62
+
63
+ lowered.concat(lower_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers]))
64
+ when AST::ReturnStmt
65
+ lower_block_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
66
+ when AST::ExpressionStmt
67
+ lower_block_expression_stmt(statement, lowered:, local_defers:, local_env:)
68
+ when AST::EmitStmt
69
+ lowered.concat(lower_emit_stmt(statement, env: local_env))
70
+ when AST::WhenStmt
71
+ lower_block_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
72
+ else
73
+ raise LoweringError.new("unsupported statement #{statement.class.name}",
74
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
79
75
  end
76
+ end
80
77
 
81
- unless terminating_ir_statement?(lowered.last)
82
- lowered.concat(cleanup_statements(local_defers, []))
83
- end
84
- lowered
78
+ unless terminating_ir_statement?(lowered.last)
79
+ lowered.concat(cleanup_statements(local_defers, []))
85
80
  end
81
+ lowered
82
+ end
86
83
 
87
- def lower_proc_expression_for_local(expression, env:, local_name:, proc_type:)
88
- captures = proc_capture_entries(expression, env)
89
- captures.each do |capture|
90
- if ref_type?(capture[:type]) || contains_ref_type?(capture[:type])
91
- raise LoweringError.new("proc capture #{capture[:name]} cannot use ref types",
92
- line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
93
- end
84
+ def lower_proc_expression_for_local(expression, env:, local_name:, proc_type:)
85
+ captures = proc_capture_entries(expression, env)
86
+ captures.each do |capture|
87
+ if ref_type?(capture[:type]) || contains_ref_type?(capture[:type])
88
+ raise LoweringError.new("proc capture #{capture[:name]} cannot use ref types",
89
+ line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
94
90
  end
95
-
96
- proc_id = fresh_proc_symbol
97
- invoke_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__invoke"
98
- release_linkage_name = "#{@ctx.module_prefix}__proc_#{proc_id}__release"
99
- retain_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__retain"
100
- env_struct_type = nil
101
- setup = []
102
-
103
- env_value = if captures.empty?
104
- IR::NullLiteral.new(type: proc_env_pointer_type)
105
- else
106
- env_struct_type = Types::Struct.new("#{@ctx.module_prefix}__proc_#{proc_id}__env").define_fields(
107
- { "__mt_ref_count" => @ctx.types.fetch("ptr_uint") }.merge(captures.each_with_object({}) { |capture, fields| fields[capture[:field_name]] = capture[:type] }),
108
- )
109
- @artifacts.synthetic_structs << IR::StructDecl.new(
110
- name: env_struct_type.name,
111
- linkage_name: env_struct_type.name,
112
- fields: [IR::Field.new(name: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")), *captures.map { |capture| IR::Field.new(name: capture[:field_name], type: capture[:type]) }],
113
- packed: false,
114
- alignment: nil,
115
- )
116
-
117
- env_pointer_type = pointer_to(env_struct_type)
118
- env_name = fresh_c_temp_name(env, "#{local_name}_env")
119
- raw_allocation = IR::Call.new(
120
- callee: "mt_async_alloc",
121
- arguments: [IR::SizeofExpr.new(target_type: env_struct_type, type: @ctx.types.fetch("ptr_uint"))],
122
- type: proc_env_pointer_type,
123
- )
124
- setup << IR::LocalDecl.new(
125
- name: env_name,
126
- linkage_name: env_name,
127
- type: env_pointer_type,
128
- value: IR::Cast.new(target_type: env_pointer_type, expression: raw_allocation, type: env_pointer_type),
129
- )
130
- env_pointer = IR::Name.new(name: env_name, type: env_pointer_type, pointer: false)
131
- setup << IR::Assignment.new(
132
- target: IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")),
133
- operator: "=",
134
- value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
135
- )
136
- captures.each do |capture|
137
- setup << IR::Assignment.new(
138
- target: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
139
- operator: "=",
140
- value: lower_expression(AST::Identifier.new(name: capture[:name]), env:, expected_type: capture[:type]),
141
- )
142
- end
143
- captures.each do |capture|
144
- next unless contains_proc_storage_type?(capture[:type])
145
-
146
- member = IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type])
147
- setup.concat(lower_proc_contained_retain_statements(member, capture[:type]))
148
- end
149
- IR::Cast.new(target_type: proc_env_pointer_type, expression: env_pointer, type: proc_env_pointer_type)
150
- end
151
-
152
- @artifacts.synthetic_functions << build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
153
- @artifacts.synthetic_functions << build_proc_release_function(release_linkage_name, env_struct_type)
154
- @artifacts.synthetic_functions << build_proc_retain_function(retain_c_name, env_struct_type)
155
-
156
- [
157
- setup,
158
- IR::AggregateLiteral.new(
159
- type: proc_type,
160
- fields: [
161
- IR::AggregateField.new(name: "env", value: env_value),
162
- IR::AggregateField.new(name: "invoke", value: IR::Name.new(name: invoke_c_name, type: proc_invoke_function_type(proc_type), pointer: false)),
163
- IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: proc_release_function_type, pointer: false)),
164
- IR::AggregateField.new(name: "retain", value: IR::Name.new(name: retain_c_name, type: proc_retain_function_type, pointer: false)),
165
- ],
166
- ),
167
- ]
168
91
  end
169
92
 
170
- def lower_inline_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
171
- iterable = compile_time_const_value(statement.iterables.first, env:)
172
- return [] unless iterable.is_a?(Array) && !iterable.empty?
173
-
174
- loop_var_name = statement.bindings.first.name
175
- loop_var_type = inline_loop_element_type(iterable.first)
176
- lowered = []
177
-
178
- iterable.each do |element|
179
- iter_env = duplicate_env(env)
180
- current_actual_scope(iter_env[:scopes])[loop_var_name] = local_binding(
181
- type: loop_var_type,
182
- linkage_name: c_local_name(loop_var_name),
183
- mutable: false,
184
- pointer: false,
185
- const_value: element,
93
+ proc_id = fresh_proc_symbol
94
+ invoke_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__invoke"
95
+ release_linkage_name = "#{@ctx.module_prefix}__proc_#{proc_id}__release"
96
+ retain_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__retain"
97
+ env_struct_type = nil
98
+ setup = []
99
+
100
+ env_value = if captures.empty?
101
+ IR::NullLiteral.new(type: proc_env_pointer_type)
102
+ else
103
+ env_struct_type = Types::Struct.new("#{@ctx.module_prefix}__proc_#{proc_id}__env").define_fields(
104
+ { "__mt_ref_count" => @ctx.types.fetch("ptr_uint") }.merge(captures.each_with_object({}) { |capture, fields| fields[capture[:field_name]] = capture[:type] }),
105
+ )
106
+ @artifacts.synthetic_structs << IR::StructDecl.new(
107
+ name: env_struct_type.name,
108
+ linkage_name: env_struct_type.name,
109
+ fields: [IR::Field.new(name: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")), *captures.map { |capture| IR::Field.new(name: capture[:field_name], type: capture[:type]) }],
110
+ packed: false,
111
+ alignment: nil,
112
+ )
113
+
114
+ env_pointer_type = pointer_to(env_struct_type)
115
+ env_name = fresh_c_temp_name(env, "#{local_name}_env")
116
+ raw_allocation = IR::Call.new(
117
+ callee: "mt_async_alloc",
118
+ arguments: [IR::SizeofExpr.new(target_type: env_struct_type, type: @ctx.types.fetch("ptr_uint"))],
119
+ type: proc_env_pointer_type,
120
+ )
121
+ setup << IR::LocalDecl.new(
122
+ name: env_name,
123
+ linkage_name: env_name,
124
+ type: env_pointer_type,
125
+ value: IR::Cast.new(target_type: env_pointer_type, expression: raw_allocation, type: env_pointer_type),
126
+ )
127
+ env_pointer = IR::Name.new(name: env_name, type: env_pointer_type, pointer: false)
128
+ setup << IR::Assignment.new(
129
+ target: IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")),
130
+ operator: "=",
131
+ value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
132
+ )
133
+ captures.each do |capture|
134
+ setup << IR::Assignment.new(
135
+ target: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
136
+ operator: "=",
137
+ value: lower_expression(AST::Identifier.new(name: capture[:name]), env:, expected_type: capture[:type]),
186
138
  )
187
- emit_stmts, other_stmts = statement.body.partition { |s| s.is_a?(AST::EmitStmt) }
188
- emit_stmts.each do |emit_stmt|
189
- lowered.concat(lower_emit_stmt(emit_stmt, env: iter_env))
190
- rescue LoweringError
191
- # emit for compile-time-only constructs — skip
192
- end
193
- unless other_stmts.empty?
194
- saved_type_env = @type_resolution_env
195
- saved_bypass = @bypass_sema_type_cache
196
- # Each unrolled iteration may bind the loop var to a different value
197
- # (e.g. a field whose `.type` differs per field), so expression types
198
- # must be re-inferred per iteration instead of reusing the single
199
- # sema-cached type computed for the representative first element.
200
- @type_resolution_env = iter_env
201
- @bypass_sema_type_cache = true
202
- begin
203
- body = lower_block(other_stmts, env: iter_env, active_defers:, return_type:, loop_flow: nil, allow_return:)
204
- ensure
205
- @type_resolution_env = saved_type_env
206
- @bypass_sema_type_cache = saved_bypass
207
- end
208
- lowered << IR::BlockStmt.new(body:) unless body.empty?
209
- end
210
139
  end
140
+ captures.each do |capture|
141
+ next unless contains_proc_storage_type?(capture[:type])
211
142
 
212
- lowered
143
+ member = IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type])
144
+ setup.concat(lower_proc_contained_retain_statements(member, capture[:type]))
145
+ end
146
+ IR::Cast.new(target_type: proc_env_pointer_type, expression: env_pointer, type: proc_env_pointer_type)
213
147
  end
214
148
 
215
- def lower_inline_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
216
- condition = compile_time_const_value(statement.condition, env:)
217
- return [] unless condition
149
+ @artifacts.synthetic_functions << build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
150
+ @artifacts.synthetic_functions << build_proc_release_function(release_linkage_name, env_struct_type)
151
+ @artifacts.synthetic_functions << build_proc_retain_function(retain_c_name, env_struct_type)
152
+
153
+ [
154
+ setup,
155
+ IR::AggregateLiteral.new(
156
+ type: proc_type,
157
+ fields: [
158
+ IR::AggregateField.new(name: "env", value: env_value),
159
+ IR::AggregateField.new(name: "invoke", value: IR::Name.new(name: invoke_c_name, type: proc_invoke_function_type(proc_type), pointer: false)),
160
+ IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: proc_release_function_type, pointer: false)),
161
+ IR::AggregateField.new(name: "retain", value: IR::Name.new(name: retain_c_name, type: proc_retain_function_type, pointer: false)),
162
+ ],
163
+ ),
164
+ ]
165
+ end
218
166
 
219
- iterations = 0
220
- max_iterations = 10_000
221
- lowered = []
167
+ def lower_inline_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
168
+ iterable = compile_time_const_value(statement.iterables.first, env:)
169
+ return [] unless iterable.is_a?(Array) && !iterable.empty?
222
170
 
223
- while compile_time_const_value(statement.condition, env:) && iterations < max_iterations
224
- body = lower_block(statement.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
171
+ loop_var_name = statement.bindings.first.name
172
+ loop_var_type = inline_loop_element_type(iterable.first)
173
+ lowered = []
174
+
175
+ iterable.each do |element|
176
+ iter_env = duplicate_env(env)
177
+ current_actual_scope(iter_env[:scopes])[loop_var_name] = local_binding(
178
+ type: loop_var_type,
179
+ linkage_name: c_local_name(loop_var_name),
180
+ mutable: false,
181
+ pointer: false,
182
+ const_value: element,
183
+ )
184
+ emit_stmts, other_stmts = statement.body.partition { |s| s.is_a?(AST::EmitStmt) }
185
+ emit_stmts.each do |emit_stmt|
186
+ lowered.concat(lower_emit_stmt(emit_stmt, env: iter_env))
187
+ rescue LoweringError
188
+ # emit for compile-time-only constructs — skip
189
+ end
190
+
191
+ unless other_stmts.empty?
192
+ saved_type_env = @type_resolution_env
193
+ saved_bypass = @bypass_sema_type_cache
194
+ # Each unrolled iteration may bind the loop var to a different value
195
+ # (e.g. a field whose `.type` differs per field), so expression types
196
+ # must be re-inferred per iteration instead of reusing the single
197
+ # sema-cached type computed for the representative first element.
198
+ @type_resolution_env = iter_env
199
+ @bypass_sema_type_cache = true
200
+ begin
201
+ body = lower_block(other_stmts, env: iter_env, active_defers:, return_type:, loop_flow: nil, allow_return:)
202
+ ensure
203
+ @type_resolution_env = saved_type_env
204
+ @bypass_sema_type_cache = saved_bypass
205
+ end
225
206
  lowered << IR::BlockStmt.new(body:) unless body.empty?
226
- iterations += 1
227
207
  end
208
+ end
209
+
210
+ lowered
211
+ end
212
+
213
+ def lower_inline_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
214
+ condition = compile_time_const_value(statement.condition, env:)
215
+ return [] unless condition
228
216
 
229
- lowered
217
+ iterations = 0
218
+ max_iterations = 10_000
219
+ lowered = []
220
+
221
+ while compile_time_const_value(statement.condition, env:) && iterations < max_iterations
222
+ body = lower_block(statement.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
223
+ lowered << IR::BlockStmt.new(body:) unless body.empty?
224
+ iterations += 1
230
225
  end
231
226
 
232
- def lower_inline_if_stmt(statement, env:, active_defers:, return_type:, allow_return:)
233
- chosen_branch = statement.branches.find do |branch|
234
- ct_value = compile_time_const_value(branch.condition, env:)
235
- ct_value == true
236
- end
227
+ lowered
228
+ end
237
229
 
238
- if chosen_branch
239
- lower_block(chosen_branch.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
240
- elsif statement.else_body
241
- lower_block(statement.else_body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
242
- else
243
- []
244
- end
230
+ def lower_inline_if_stmt(statement, env:, active_defers:, return_type:, allow_return:)
231
+ chosen_branch = statement.branches.find do |branch|
232
+ ct_value = compile_time_const_value(branch.condition, env:)
233
+ ct_value == true
234
+ end
235
+
236
+ if chosen_branch
237
+ lower_block(chosen_branch.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
238
+ elsif statement.else_body
239
+ lower_block(statement.else_body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
240
+ else
241
+ []
245
242
  end
243
+ end
246
244
 
247
- def lower_inline_match_stmt(statement, env:, active_defers:, return_type:, allow_return:)
248
- scrutinee = compile_time_const_value(statement.expression, env:)
249
- return [] unless scrutinee
245
+ def lower_inline_match_stmt(statement, env:, active_defers:, return_type:, allow_return:)
246
+ scrutinee = compile_time_const_value(statement.expression, env:)
247
+ return [] unless scrutinee
250
248
 
251
- chosen_arm = statement.arms.find do |arm|
252
- scrutinee == compile_time_const_value(arm.pattern, env:)
253
- end
249
+ chosen_arm = statement.arms.find do |arm|
250
+ scrutinee == compile_time_const_value(arm.pattern, env:)
251
+ end
252
+
253
+ return [] unless chosen_arm
254
254
 
255
- return [] unless chosen_arm
255
+ lower_block(chosen_arm.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
256
+ end
256
257
 
257
- lower_block(chosen_arm.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
258
+ def inline_loop_element_type(element)
259
+ if element.is_a?(Types::FieldHandle)
260
+ @ctx.types.fetch("field_handle")
261
+ elsif element.is_a?(Types::CallableHandle)
262
+ @ctx.types.fetch("callable_handle")
263
+ elsif element.is_a?(Types::AttributeHandle)
264
+ @ctx.types.fetch("attribute_handle")
265
+ elsif element.is_a?(Types::MemberHandle)
266
+ @ctx.types.fetch("member_handle")
267
+ elsif element.is_a?(Types::StructHandle)
268
+ @ctx.types.fetch("struct_handle")
269
+ else
270
+ @ctx.types.fetch("int")
258
271
  end
272
+ end
259
273
 
260
- def inline_loop_element_type(element)
261
- if element.is_a?(Types::FieldHandle)
262
- @ctx.types.fetch("field_handle")
263
- elsif element.is_a?(Types::CallableHandle)
264
- @ctx.types.fetch("callable_handle")
265
- elsif element.is_a?(Types::AttributeHandle)
266
- @ctx.types.fetch("attribute_handle")
267
- elsif element.is_a?(Types::MemberHandle)
268
- @ctx.types.fetch("member_handle")
269
- elsif element.is_a?(Types::StructHandle)
270
- @ctx.types.fetch("struct_handle")
271
- else
272
- @ctx.types.fetch("int")
273
- end
274
+ def lower_emit_stmt(statement, env:)
275
+ decl = statement.declaration
276
+ case decl
277
+ when AST::FunctionDef
278
+ lower_emitted_function(decl, env:)
279
+ when AST::StructDecl
280
+ lower_emitted_struct(decl)
281
+ when AST::ConstDecl
282
+ lower_emitted_const(decl, env:)
283
+ else
284
+ raise LoweringError.new("emit is not supported for #{decl.class.name}",
285
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
274
286
  end
287
+ end
275
288
 
276
- def lower_emit_stmt(statement, env:)
277
- decl = statement.declaration
278
- case decl
279
- when AST::FunctionDef
280
- lower_emitted_function(decl, env:)
281
- when AST::StructDecl
282
- lower_emitted_struct(decl)
283
- when AST::ConstDecl
284
- lower_emitted_const(decl, env:)
285
- else
286
- raise LoweringError.new("emit is not supported for #{decl.class.name}",
287
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
288
- end
289
+ def lower_emitted_function(decl, env:)
290
+ params = []
291
+ param_setup = []
292
+ return_type = decl.return_type ? resolve_type_ref(decl.return_type) : @ctx.types.fetch("void")
293
+ linkage_name = "#{@ctx.module_prefix}#{decl.name}"
294
+
295
+ decl.params.each do |param|
296
+ param_type = param.type ? resolve_type_ref(param.type) : @ctx.types.fetch("int")
297
+ param_linkage_name = c_local_name(param.name)
298
+ params << IR::Param.new(name: param.name, linkage_name: param_linkage_name, type: param_type, pointer: false)
299
+ env[:scopes].last[param.name] = local_binding(type: param_type, linkage_name: param_linkage_name, mutable: false, pointer: false)
289
300
  end
290
301
 
291
- def lower_emitted_function(decl, env:)
292
- params = []
293
- param_setup = []
294
- return_type = decl.return_type ? resolve_type_ref(decl.return_type) : @ctx.types.fetch("void")
295
- linkage_name = "#{@ctx.module_prefix}#{decl.name}"
296
-
297
- decl.params.each do |param|
298
- param_type = param.type ? resolve_type_ref(param.type) : @ctx.types.fetch("int")
299
- param_linkage_name = c_local_name(param.name)
300
- params << IR::Param.new(name: param.name, linkage_name: param_linkage_name, type: param_type, pointer: false)
301
- env[:scopes].last[param.name] = local_binding(type: param_type, linkage_name: param_linkage_name, mutable: false, pointer: false)
302
- end
302
+ body = lower_block(decl.body, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: true)
303
+ body = param_setup + body
304
+ func = IR::Function.new(name: decl.name, linkage_name:, params:, return_type:, body:, entry_point: false, method_receiver_param: false)
305
+ @artifacts.emitted_declarations << func
306
+ []
307
+ end
303
308
 
304
- body = lower_block(decl.body, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: true)
305
- body = param_setup + body
306
- func = IR::Function.new(name: decl.name, linkage_name:, params:, return_type:, body:, entry_point: false, method_receiver_param: false)
307
- @artifacts.emitted_declarations << func
308
- []
309
+ def lower_emitted_struct(decl)
310
+ fields = decl.fields.map do |field|
311
+ field_type = field.type ? resolve_type_ref(field.type) : @ctx.types.fetch("int")
312
+ IR::Field.new(name: field.name, type: field_type)
309
313
  end
314
+ linkage_name = "#{@ctx.module_prefix}#{decl.name}"
315
+ struct_decl = IR::StructDecl.new(name: decl.name, linkage_name:, fields:, packed: decl.respond_to?(:packed) ? decl.packed : false, alignment: nil)
316
+ @artifacts.emitted_declarations << struct_decl
317
+ []
318
+ end
310
319
 
311
- def lower_emitted_struct(decl)
312
- fields = decl.fields.map do |field|
313
- field_type = field.type ? resolve_type_ref(field.type) : @ctx.types.fetch("int")
314
- IR::Field.new(name: field.name, type: field_type)
320
+ def lower_destructure_decl(statement, env:, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
321
+ value_type = infer_expression_type(statement.value, env:)
322
+ setup, prepared_value, _cleanups = prepare_expression_with_cleanups(statement.value, env:, expected_type: value_type)
323
+ lowered.concat(setup)
324
+ value = lower_contextual_expression(prepared_value, env:, expected_type: value_type)
325
+
326
+ temp_name = fresh_c_temp_name(env, "destructure_val")
327
+ lowered << IR::LocalDecl.new(name: temp_name, linkage_name: temp_name, type: value_type, value:)
328
+
329
+ if statement.destructure_type_name
330
+ # Struct destructure: find fields by name in the struct type
331
+ destructure_type = value_type
332
+ fields = destructure_type.fields
333
+ statement.destructure_bindings.each do |name|
334
+ next if name == "_"
335
+ field_type = fields[name]
336
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: destructure_type, pointer: false), member: name, type: field_type)
337
+ decl_c_name = c_local_name(name)
338
+ lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
339
+ env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
315
340
  end
316
- linkage_name = "#{@ctx.module_prefix}#{decl.name}"
317
- struct_decl = IR::StructDecl.new(name: decl.name, linkage_name:, fields:, packed: decl.respond_to?(:packed) ? decl.packed : false, alignment: nil)
318
- @artifacts.emitted_declarations << struct_decl
319
- []
320
- end
321
-
322
- def lower_destructure_decl(statement, env:, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
323
- value_type = infer_expression_type(statement.value, env:)
324
- setup, prepared_value, _cleanups = prepare_expression_with_cleanups(statement.value, env:, expected_type: value_type)
325
- lowered.concat(setup)
326
- value = lower_contextual_expression(prepared_value, env:, expected_type: value_type)
327
-
328
- temp_name = fresh_c_temp_name(env, "destructure_val")
329
- lowered << IR::LocalDecl.new(name: temp_name, linkage_name: temp_name, type: value_type, value:)
330
-
331
- if statement.destructure_type_name
332
- # Struct destructure: find fields by name in the struct type
333
- destructure_type = value_type
334
- fields = destructure_type.fields
335
- statement.destructure_bindings.each do |name|
336
- next if name == "_"
337
- field_type = fields[name]
338
- field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: destructure_type, pointer: false), member: name, type: field_type)
339
- decl_c_name = c_local_name(name)
340
- lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
341
- env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
342
- end
343
- else
344
- # Tuple destructure: index by position
345
- statement.destructure_bindings.each_with_index do |name, index|
346
- next if name == "_"
347
- field_name = value_type.field_names[index]
348
- field_type = value_type.element_types[index]
349
- field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: value_type, pointer: false), member: field_name, type: field_type)
350
- decl_c_name = c_local_name(name)
351
- lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
352
- env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
353
- end
341
+ else
342
+ # Tuple destructure: index by position
343
+ statement.destructure_bindings.each_with_index do |name, index|
344
+ next if name == "_"
345
+ field_name = value_type.field_names[index]
346
+ field_type = value_type.element_types[index]
347
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: value_type, pointer: false), member: field_name, type: field_type)
348
+ decl_c_name = c_local_name(name)
349
+ lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
350
+ env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
354
351
  end
355
352
  end
353
+ end
356
354
 
357
- def lower_emitted_const(decl, env:)
358
- value = lower_static_storage_initializer(decl.value, env:, expected_type: decl.type ? resolve_type_ref(decl.type) : nil)
359
- type = decl.type ? resolve_type_ref(decl.type) : infer_expression_type(decl.value, env:)
360
- linkage_name = value_c_name(decl.name)
361
- constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:,
362
- line: decl.line, source_path: @ctx.current_analysis_path)
363
- @artifacts.emitted_declarations << constant
364
- []
365
- end
355
+ def lower_emitted_const(decl, env:)
356
+ value = lower_static_storage_initializer(decl.value, env:, expected_type: decl.type ? resolve_type_ref(decl.type) : nil)
357
+ type = decl.type ? resolve_type_ref(decl.type) : infer_expression_type(decl.value, env:)
358
+ linkage_name = value_c_name(decl.name)
359
+ constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:, line: decl.line, path: @ctx.current_analysis_path)
360
+ @artifacts.emitted_declarations << constant
361
+ []
362
+ end
366
363
 
367
364
  def variant_self_referencing_field?(field_type, scrutinee_type)
368
365
  return false unless field_type.is_a?(Types::Variant) || field_type.is_a?(Types::VariantInstance)
@@ -424,10 +421,10 @@ module MilkTea
424
421
  end
425
422
 
426
423
  expression_expected_type = if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?"
427
- nil
428
- else
429
- infer_expression_type(statement.expression, env: local_env)
430
- end
424
+ nil
425
+ else
426
+ infer_expression_type(statement.expression, env: local_env)
427
+ end
431
428
  prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
432
429
  statement.expression,
433
430
  env: local_env,
@@ -448,7 +445,7 @@ module MilkTea
448
445
  lowered.concat(prepared_cleanups.flat_map(&:itself))
449
446
  local_env[:scopes] = scopes_with_refinements(local_env[:scopes], consuming_foreign_call_refinements(foreign_call, local_env))
450
447
  elsif prepared_expression
451
- lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env: local_env), line: statement.line, source_path: @ctx.current_analysis_path)
448
+ lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env: local_env), line: statement.line, path: @ctx.current_analysis_path)
452
449
  lowered.concat(prepared_cleanups.flat_map(&:itself))
453
450
  else
454
451
  lowered.concat(prepared_cleanups.flat_map(&:itself))
@@ -501,7 +498,7 @@ module MilkTea
501
498
  end
502
499
  lowered.concat(lower_proc_contained_retain_statements(value, return_type)) if needs_proc_retain
503
500
  lowered.concat(cleanup)
504
- lowered << IR::ReturnStmt.new(value:, line: statement.line, source_path: @ctx.current_analysis_path)
501
+ lowered << IR::ReturnStmt.new(value:, line: statement.line, path: @ctx.current_analysis_path)
505
502
  end
506
503
 
507
504
  def lower_block_assignment_stmt(statement, lowered:, local_defers:, local_env:)
@@ -519,7 +516,7 @@ module MilkTea
519
516
  env: local_env,
520
517
  expected_type: target.type,
521
518
  allow_root_statement_foreign: true,
522
- materialize_array_calls: true,
519
+ materialize_array_calls: !array_type?(target.type),
523
520
  )
524
521
  lowered.concat(prepared_setup)
525
522
  if (foreign_call = foreign_call_info(prepared_value, local_env))
@@ -542,23 +539,23 @@ module MilkTea
542
539
  return
543
540
  else
544
541
  value = if statement.operator == "="
545
- lower_contextual_expression(
546
- prepared_value,
547
- env: local_env,
548
- expected_type: target.type,
549
- external_numeric: external_numeric_assignment_target?(statement.target, env: local_env),
550
- contextual_int_to_float: contextual_int_to_float_target?(target.type),
551
- )
552
- elsif ["+=", "-=", "*=", "/="].include?(statement.operator)
553
- lower_contextual_expression(
554
- prepared_value,
555
- env: local_env,
556
- expected_type: target.type,
557
- contextual_int_to_float: contextual_int_to_float_target?(target.type),
558
- )
559
- else
560
- lower_expression(prepared_value, env: local_env, expected_type: target.type)
561
- end
542
+ lower_contextual_expression(
543
+ prepared_value,
544
+ env: local_env,
545
+ expected_type: target.type,
546
+ external_numeric: external_numeric_assignment_target?(statement.target, env: local_env),
547
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
548
+ )
549
+ elsif ["+=", "-=", "*=", "/="].include?(statement.operator)
550
+ lower_contextual_expression(
551
+ prepared_value,
552
+ env: local_env,
553
+ expected_type: target.type,
554
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
555
+ )
556
+ else
557
+ lower_expression(prepared_value, env: local_env, expected_type: target.type)
558
+ end
562
559
  end
563
560
  if target.type.is_a?(Types::Nullable) && value && !value.type.is_a?(Types::Nullable) && !pointer_like_type?(target.type.base)
564
561
  value = nullable_some_literal(target.type, value)
@@ -686,17 +683,17 @@ module MilkTea
686
683
  end
687
684
 
688
685
  storage_type = if statement.else_body
689
- infer_expression_type(statement.value, env: local_env)
690
- elsif statement.type
691
- resolve_type_ref(statement.type)
692
- else
693
- infer_expression_type(statement.value, env: local_env)
694
- end
686
+ infer_expression_type(statement.value, env: local_env)
687
+ elsif statement.type
688
+ resolve_type_ref(statement.type)
689
+ else
690
+ infer_expression_type(statement.value, env: local_env)
691
+ end
695
692
  type = if statement.else_body
696
- statement.type ? resolve_type_ref(statement.type) : let_else_success_type(storage_type)
697
- else
698
- storage_type
699
- end
693
+ statement.type ? resolve_type_ref(statement.type) : let_else_success_type(storage_type)
694
+ else
695
+ storage_type
696
+ end
700
697
  linkage_name = let_else_storage_c_name(statement, local_env)
701
698
  decl_name = bind_let_else_local?(statement) ? statement.name : linkage_name
702
699
  prepared_setup = []
@@ -710,7 +707,7 @@ module MilkTea
710
707
  env: local_env,
711
708
  expected_type: storage_type,
712
709
  allow_root_statement_foreign: true,
713
- materialize_array_calls: true,
710
+ materialize_array_calls: !array_type?(storage_type),
714
711
  )
715
712
  lowered.concat(prepared_setup)
716
713
  end
@@ -727,7 +724,7 @@ module MilkTea
727
724
  raise LoweringError.new("consuming foreign calls must return void",
728
725
  line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
729
726
 
730
- lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, source_path: @ctx.current_analysis_path)
727
+ lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path)
731
728
  lowered.concat(cleanup_statements)
732
729
  emitted_decl = true
733
730
  elsif prepared_value.is_a?(AST::ProcExpr)
@@ -759,22 +756,22 @@ module MilkTea
759
756
  const_value: statement.else_body ? nil : statement.kind == :let && prepared_value ? compile_time_const_value(prepared_value, env: local_env) : nil,
760
757
  )
761
758
  end
762
- lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, source_path: @ctx.current_analysis_path) unless emitted_decl
759
+ lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path) unless emitted_decl
763
760
  if statement.else_body
764
761
  else_env = if statement.else_binding
765
- duplicate_env(local_env).tap do |env_with_error|
766
- current_actual_scope(env_with_error[:scopes])[statement.else_binding.name] = local_binding(
767
- type: let_else_error_type(storage_type),
768
- storage_type:,
769
- linkage_name:,
770
- mutable: false,
771
- pointer: false,
772
- projection: :result_failure_error,
773
- )
774
- end
775
- else
776
- local_env
777
- end
762
+ duplicate_env(local_env).tap do |env_with_error|
763
+ current_actual_scope(env_with_error[:scopes])[statement.else_binding.name] = local_binding(
764
+ type: let_else_error_type(storage_type),
765
+ storage_type:,
766
+ linkage_name:,
767
+ mutable: false,
768
+ pointer: false,
769
+ projection: :result_failure_error,
770
+ )
771
+ end
772
+ else
773
+ local_env
774
+ end
778
775
  else_body = lower_block(
779
776
  statement.else_body,
780
777
  env: else_env,
@@ -832,17 +829,17 @@ module MilkTea
832
829
  cases = statement.arms.map do |arm|
833
830
  arm_local_env = duplicate_env(local_env)
834
831
  binding_decl = if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
835
- arm_name = variant_match_arm_name_from_pattern(arm.pattern)
836
- if arm_name && scrutinee_type.has_payload?(arm_name)
837
- fields = scrutinee_type.arm(arm_name)
838
- payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
839
- data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
840
- arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
841
- binding_c = c_local_name(arm.binding_name)
842
- arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: true, pointer: false)
843
- IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
844
- end
845
- end
832
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
833
+ if arm_name && scrutinee_type.has_payload?(arm_name)
834
+ fields = scrutinee_type.arm(arm_name)
835
+ payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
836
+ data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
837
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
838
+ binding_c = c_local_name(arm.binding_name)
839
+ arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: true, pointer: false)
840
+ IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
841
+ end
842
+ end
846
843
  body = lower_block(
847
844
  arm.body,
848
845
  env: arm_local_env,
@@ -915,10 +912,10 @@ module MilkTea
915
912
  tag_value = IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false)
916
913
  tag_expr = IR::Member.new(receiver: expression, member: "kind", type: kind_type)
917
914
  goto_label = if arm_index < statement.arms.length - 1
918
- "__mt_match_#{m}_arm_#{arm_index + 1}"
919
- else
920
- arm_next_label
921
- end
915
+ "__mt_match_#{m}_arm_#{arm_index + 1}"
916
+ else
917
+ arm_next_label
918
+ end
922
919
  tag_check = IR::Binary.new(operator: "!=", left: tag_expr, right: tag_value, type: @ctx.types.fetch("bool"))
923
920
  lowered << IR::IfStmt.new(condition: tag_check, then_body: [IR::GotoStmt.new(label: goto_label)], else_body: [])
924
921
  end
@@ -1055,17 +1052,17 @@ module MilkTea
1055
1052
  non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1056
1053
  wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1057
1054
  else_body = if wildcard
1058
- lower_block(
1059
- wildcard.body,
1060
- env: local_env,
1061
- active_defers: active_defers + local_defers,
1062
- return_type:,
1063
- loop_flow: arm_loop_flow,
1064
- allow_return:,
1065
- )
1066
- else
1067
- []
1068
- end
1055
+ lower_block(
1056
+ wildcard.body,
1057
+ env: local_env,
1058
+ active_defers: active_defers + local_defers,
1059
+ return_type:,
1060
+ loop_flow: arm_loop_flow,
1061
+ allow_return:,
1062
+ )
1063
+ else
1064
+ []
1065
+ end
1069
1066
  non_wildcard.reverse_each do |arm|
1070
1067
  arm_body = lower_block(
1071
1068
  arm.body,
@@ -1088,17 +1085,17 @@ module MilkTea
1088
1085
  non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1089
1086
  wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1090
1087
  else_body = if wildcard
1091
- lower_block(
1092
- wildcard.body,
1093
- env: local_env,
1094
- active_defers: active_defers + local_defers,
1095
- return_type:,
1096
- loop_flow: arm_loop_flow,
1097
- allow_return:,
1098
- )
1099
- else
1100
- []
1101
- end
1088
+ lower_block(
1089
+ wildcard.body,
1090
+ env: local_env,
1091
+ active_defers: active_defers + local_defers,
1092
+ return_type:,
1093
+ loop_flow: arm_loop_flow,
1094
+ allow_return:,
1095
+ )
1096
+ else
1097
+ []
1098
+ end
1102
1099
  non_wildcard.reverse_each do |arm|
1103
1100
  arm_body = lower_block(
1104
1101
  arm.body,
@@ -1120,10 +1117,10 @@ module MilkTea
1120
1117
  non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1121
1118
  wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1122
1119
  else_body = if wildcard
1123
- lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
1124
- else
1125
- []
1126
- end
1120
+ lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
1121
+ else
1122
+ []
1123
+ end
1127
1124
  non_wildcard.reverse_each do |arm|
1128
1125
  arm_body = lower_block(
1129
1126
  arm.body,
@@ -1134,11 +1131,11 @@ module MilkTea
1134
1131
  allow_return:,
1135
1132
  )
1136
1133
  cond = if arm.pattern.is_a?(AST::RangeExpr)
1137
- lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
1138
- else
1139
- lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1140
- IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1141
- end
1134
+ lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
1135
+ else
1136
+ lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1137
+ IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1138
+ end
1142
1139
  else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1143
1140
  end
1144
1141
  lowered.concat(else_body)