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,799 +2,616 @@
2
2
 
3
3
  module MilkTea
4
4
  module LowererLoops
5
- private
6
-
7
-
8
- def lower_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
9
- return lower_threaded_for_stmt(statement, env:, active_defers:) if statement.threaded
10
- return lower_parallel_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if statement.parallel?
11
- return lower_range_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if range_iterable?(statement.iterable)
12
-
13
- iterable_type = infer_expression_type(statement.iterable, env:)
14
- return lower_iterator_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if collection_loop_type(iterable_type).nil?
15
-
16
- lower_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
5
+ def lower_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
6
+ return lower_threaded_for_stmt(statement, env:, active_defers:) if statement.threaded
7
+ return lower_parallel_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if statement.parallel?
8
+ return lower_range_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if range_iterable?(statement.iterable)
9
+
10
+ iterable_type = infer_expression_type(statement.iterable, env:)
11
+ return lower_iterator_for_stmt(statement, env:, active_defers:, return_type:, allow_return:) if collection_loop_type(iterable_type).nil?
12
+
13
+ lower_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
14
+ end
15
+
16
+ def lower_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
17
+ continue_label = fresh_c_temp_name(env, "loop_continue")
18
+ break_label = fresh_c_temp_name(env, "loop_break")
19
+ condition_setup, prepared_condition, condition_cleanups = prepare_expression_with_cleanups(
20
+ statement.condition,
21
+ env:,
22
+ expected_type: @ctx.types.fetch("bool"),
23
+ )
24
+
25
+ body = lower_block(
26
+ statement.body,
27
+ env: env_with_refinements(duplicate_env(env), flow_refinements(statement.condition, truthy: true, env: env)),
28
+ active_defers:,
29
+ return_type:,
30
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
31
+ allow_return:,
32
+ )
33
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
34
+
35
+ condition = lower_expression(prepared_condition, env:, expected_type: @ctx.types.fetch("bool"))
36
+
37
+ if condition_setup.empty? && condition_cleanups.empty?
38
+ statements = [
39
+ IR::WhileStmt.new(
40
+ condition:,
41
+ body:,
42
+ ),
43
+ ]
44
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
45
+ return IR::BlockStmt.new(body: statements)
17
46
  end
18
47
 
19
- def lower_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
20
- continue_label = fresh_c_temp_name(env, "loop_continue")
21
- break_label = fresh_c_temp_name(env, "loop_break")
22
- condition_setup, prepared_condition, condition_cleanups = prepare_expression_with_cleanups(
23
- statement.condition,
24
- env:,
25
- expected_type: @ctx.types.fetch("bool"),
26
- )
27
-
28
- body = lower_block(
48
+ loop_body = [
49
+ *condition_setup,
50
+ IR::IfStmt.new(
51
+ condition: IR::Unary.new(operator: "not", operand: condition, type: @ctx.types.fetch("bool")),
52
+ then_body: condition_cleanups.flat_map(&:itself) + [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
53
+ else_body: condition_cleanups.flat_map(&:itself),
54
+ ),
55
+ *body,
56
+ ]
57
+
58
+ statements = [
59
+ IR::WhileStmt.new(
60
+ condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
61
+ body: loop_body,
62
+ ),
63
+ ]
64
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(loop_body, break_label)
65
+
66
+ IR::BlockStmt.new(body: statements)
67
+ end
68
+
69
+ def lower_range_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
70
+ loop_type = infer_range_loop_type(statement.iterable, env:)
71
+ start_expr = range_start_of(statement.iterable)
72
+ stop_expr = range_end_of(statement.iterable)
73
+ start_setup, prepared_start = prepare_expression_for_inline_lowering(start_expr, env:, expected_type: loop_type)
74
+ stop_setup, prepared_stop = prepare_expression_for_inline_lowering(stop_expr, env:, expected_type: loop_type)
75
+ index_linkage_name = c_local_name(statement.name)
76
+ stop_linkage_name = fresh_c_temp_name(env, "for_stop")
77
+ continue_label = fresh_c_temp_name(env, "loop_continue")
78
+ break_label = fresh_c_temp_name(env, "loop_break")
79
+ index_ref = IR::Name.new(name: index_linkage_name, type: loop_type, pointer: false)
80
+ inline_stop = stop_setup.empty? && compile_time_numeric_const_expression?(prepared_stop)
81
+ stop_value = if inline_stop
82
+ lower_expression(prepared_stop, env:, expected_type: loop_type)
83
+ else
84
+ IR::Name.new(name: stop_linkage_name, type: loop_type, pointer: false)
85
+ end
86
+
87
+ while_env = duplicate_env(env)
88
+ current_actual_scope(while_env[:scopes])[statement.name] = local_binding(type: loop_type, linkage_name: c_local_name(statement.name), mutable: false, pointer: false)
89
+
90
+ body = []
91
+ body.concat(
92
+ lower_block(
29
93
  statement.body,
30
- env: env_with_refinements(duplicate_env(env), flow_refinements(statement.condition, truthy: true, env: env)),
94
+ env: while_env,
31
95
  active_defers:,
32
96
  return_type:,
33
97
  loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
34
98
  allow_return:,
99
+ ),
100
+ )
101
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
102
+
103
+ for_statement = IR::ForStmt.new(
104
+ init: IR::LocalDecl.new(name: statement.name, linkage_name: index_linkage_name, type: loop_type, value: lower_expression(prepared_start, env:, expected_type: loop_type)),
105
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
106
+ post: IR::Assignment.new(
107
+ target: index_ref,
108
+ operator: "+=",
109
+ value: IR::IntegerLiteral.new(value: 1, type: loop_type),
110
+ ),
111
+ body:,
112
+ )
113
+
114
+ statements = [
115
+ *start_setup,
116
+ *stop_setup,
117
+ for_statement,
118
+ ]
119
+ unless inline_stop
120
+ statements.insert(
121
+ statements.length - 1,
122
+ IR::LocalDecl.new(name: stop_linkage_name, linkage_name: stop_linkage_name, type: loop_type, value: lower_expression(prepared_stop, env:, expected_type: loop_type)),
35
123
  )
36
- body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
37
-
38
- condition = lower_expression(prepared_condition, env:, expected_type: @ctx.types.fetch("bool"))
39
-
40
- if condition_setup.empty? && condition_cleanups.empty?
41
- statements = [
42
- IR::WhileStmt.new(
43
- condition:,
44
- body:,
45
- ),
46
- ]
47
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
48
- return IR::BlockStmt.new(body: statements)
49
- end
50
-
51
- loop_body = [
52
- *condition_setup,
53
- IR::IfStmt.new(
54
- condition: IR::Unary.new(operator: "not", operand: condition, type: @ctx.types.fetch("bool")),
55
- then_body: condition_cleanups.flat_map(&:itself) + [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
56
- else_body: condition_cleanups.flat_map(&:itself),
57
- ),
58
- *body,
59
- ]
124
+ end
125
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
126
+
127
+ IR::BlockStmt.new(body: statements)
128
+ end
129
+
130
+ def lower_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
131
+ iterable_type = infer_expression_type(statement.iterable, env:)
132
+ element_type = collection_loop_type(iterable_type)
133
+ raise LoweringError.new("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next(), got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
134
+ iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
135
+ binding_type = collection_loop_binding_type(iterable_type, element_type) || element_type
136
+
137
+ iterable_linkage_name = fresh_c_temp_name(env, "for_items")
138
+ index_linkage_name = fresh_c_temp_name(env, "for_index")
139
+ continue_label = fresh_c_temp_name(env, "loop_continue")
140
+ break_label = fresh_c_temp_name(env, "loop_break")
141
+ iterable_ref = IR::Name.new(name: iterable_linkage_name, type: iterable_type, pointer: false)
142
+ index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
143
+
144
+ item_value = if array_type?(iterable_type)
145
+ IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
146
+ else
147
+ data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
148
+ IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
149
+ end
150
+
151
+ stop_value = if array_type?(iterable_type)
152
+ IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
153
+ else
154
+ IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
155
+ end
156
+
157
+ loop_item_value = if ref_type?(binding_type)
158
+ IR::AddressOf.new(expression: item_value, type: binding_type)
159
+ else
160
+ item_value
161
+ end
60
162
 
61
- statements = [
62
- IR::WhileStmt.new(
63
- condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
64
- body: loop_body,
65
- ),
66
- ]
67
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(loop_body, break_label)
163
+ while_env = duplicate_env(env)
164
+ current_actual_scope(while_env[:scopes])[statement.name] = local_binding(type: binding_type, linkage_name: c_local_name(statement.name), mutable: false, pointer: false)
68
165
 
69
- IR::BlockStmt.new(body: statements)
166
+ body = [
167
+ IR::LocalDecl.new(name: statement.name, linkage_name: c_local_name(statement.name), type: binding_type, value: loop_item_value),
168
+ ]
169
+ body.concat(
170
+ lower_block(
171
+ statement.body,
172
+ env: while_env,
173
+ active_defers:,
174
+ return_type:,
175
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
176
+ allow_return:,
177
+ ),
178
+ )
179
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
180
+
181
+ for_statement = IR::ForStmt.new(
182
+ init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
183
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
184
+ post: IR::Assignment.new(
185
+ target: index_ref,
186
+ operator: "+=",
187
+ value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
188
+ ),
189
+ body:,
190
+ )
191
+
192
+ statements = [
193
+ *iterable_setup,
194
+ IR::LocalDecl.new(name: iterable_linkage_name, linkage_name: iterable_linkage_name, type: iterable_type, value: lower_expression(prepared_iterable, env:, expected_type: iterable_type)),
195
+ for_statement,
196
+ ]
197
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
198
+
199
+ IR::BlockStmt.new(body: statements)
200
+ end
201
+
202
+ def lower_parallel_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
203
+ infos = statement.bindings.each_with_index.map do |binding, index|
204
+ iterable = statement.iterables[index]
205
+ iterable_type = infer_expression_type(iterable, env:)
206
+ element_type = collection_loop_type(iterable_type)
207
+ raise LoweringError.new("parallel for loops expect arrays or spans for each iterable, got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
208
+
209
+ {
210
+ binding:,
211
+ iterable:,
212
+ iterable_type:,
213
+ element_type:,
214
+ binding_type: collection_loop_binding_type(iterable_type, element_type) || element_type,
215
+ }
70
216
  end
71
217
 
72
- def lower_range_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
73
- loop_type = infer_range_loop_type(statement.iterable, env:)
74
- start_expr = range_start_of(statement.iterable)
75
- stop_expr = range_end_of(statement.iterable)
76
- start_setup, prepared_start = prepare_expression_for_inline_lowering(start_expr, env:, expected_type: loop_type)
77
- stop_setup, prepared_stop = prepare_expression_for_inline_lowering(stop_expr, env:, expected_type: loop_type)
78
- index_linkage_name = c_local_name(statement.name)
79
- stop_linkage_name = fresh_c_temp_name(env, "for_stop")
80
- continue_label = fresh_c_temp_name(env, "loop_continue")
81
- break_label = fresh_c_temp_name(env, "loop_break")
82
- index_ref = IR::Name.new(name: index_linkage_name, type: loop_type, pointer: false)
83
- inline_stop = stop_setup.empty? && compile_time_numeric_const_expression?(prepared_stop)
84
- stop_value = if inline_stop
85
- lower_expression(prepared_stop, env:, expected_type: loop_type)
86
- else
87
- IR::Name.new(name: stop_linkage_name, type: loop_type, pointer: false)
88
- end
89
-
90
- while_env = duplicate_env(env)
91
- current_actual_scope(while_env[:scopes])[statement.name] = local_binding(type: loop_type, linkage_name: c_local_name(statement.name), mutable: false, pointer: false)
92
-
93
- body = []
94
- body.concat(
95
- lower_block(
96
- statement.body,
97
- env: while_env,
98
- active_defers:,
99
- return_type:,
100
- loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
101
- allow_return:,
102
- ),
218
+ iterable_entries = infos.map do |info|
219
+ setup, prepared_iterable = prepare_expression_for_inline_lowering(info[:iterable], env:, expected_type: info[:iterable_type])
220
+ linkage_name = fresh_c_temp_name(env, "for_items")
221
+ info.merge(
222
+ setup:,
223
+ prepared_iterable:,
224
+ iterable_linkage_name: linkage_name,
225
+ iterable_ref: IR::Name.new(name: linkage_name, type: info[:iterable_type], pointer: false),
103
226
  )
104
- body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
105
-
106
- for_statement = IR::ForStmt.new(
107
- init: IR::LocalDecl.new(name: statement.name, linkage_name: index_linkage_name, type: loop_type, value: lower_expression(prepared_start, env:, expected_type: loop_type)),
108
- condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
109
- post: IR::Assignment.new(
110
- target: index_ref,
111
- operator: "+=",
112
- value: IR::IntegerLiteral.new(value: 1, type: loop_type),
113
- ),
114
- body:,
115
- )
116
-
117
- statements = [
118
- *start_setup,
119
- *stop_setup,
120
- for_statement,
121
- ]
122
- unless inline_stop
123
- statements.insert(
124
- statements.length - 1,
125
- IR::LocalDecl.new(name: stop_linkage_name, linkage_name: stop_linkage_name, type: loop_type, value: lower_expression(prepared_stop, env:, expected_type: loop_type)),
126
- )
127
- end
128
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
129
-
130
- IR::BlockStmt.new(body: statements)
131
227
  end
132
228
 
133
- def lower_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
134
- iterable_type = infer_expression_type(statement.iterable, env:)
135
- element_type = collection_loop_type(iterable_type)
136
- raise LoweringError.new("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next(), got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
137
- iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
138
- binding_type = collection_loop_binding_type(iterable_type, element_type) || element_type
139
-
140
- iterable_linkage_name = fresh_c_temp_name(env, "for_items")
141
- index_linkage_name = fresh_c_temp_name(env, "for_index")
142
- continue_label = fresh_c_temp_name(env, "loop_continue")
143
- break_label = fresh_c_temp_name(env, "loop_break")
144
- iterable_ref = IR::Name.new(name: iterable_linkage_name, type: iterable_type, pointer: false)
145
- index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
146
-
147
- item_value = if array_type?(iterable_type)
148
- IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
149
- else
150
- data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
151
- IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
152
- end
153
-
154
- stop_value = if array_type?(iterable_type)
155
- IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
156
- else
157
- IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
158
- end
159
-
160
- loop_item_value = if ref_type?(binding_type)
161
- IR::AddressOf.new(expression: item_value, type: binding_type)
229
+ index_linkage_name = fresh_c_temp_name(env, "for_index")
230
+ continue_label = fresh_c_temp_name(env, "loop_continue")
231
+ break_label = fresh_c_temp_name(env, "loop_break")
232
+ index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
233
+ stop_value = collection_loop_stop_value(iterable_entries.first[:iterable_ref], iterable_entries.first[:iterable_type])
234
+
235
+ while_env = duplicate_env(env)
236
+ body = iterable_entries.map do |entry|
237
+ item_value = collection_loop_item_value(entry[:iterable_ref], entry[:iterable_type], index_ref, entry[:element_type])
238
+ loop_item_value = if ref_type?(entry[:binding_type])
239
+ IR::AddressOf.new(expression: item_value, type: entry[:binding_type])
162
240
  else
163
241
  item_value
164
242
  end
165
-
166
- while_env = duplicate_env(env)
167
- current_actual_scope(while_env[:scopes])[statement.name] = local_binding(type: binding_type, linkage_name: c_local_name(statement.name), mutable: false, pointer: false)
168
-
169
- body = [
170
- IR::LocalDecl.new(name: statement.name, linkage_name: c_local_name(statement.name), type: binding_type, value: loop_item_value),
171
- ]
172
- body.concat(
173
- lower_block(
174
- statement.body,
175
- env: while_env,
176
- active_defers:,
177
- return_type:,
178
- loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
179
- allow_return:,
180
- ),
181
- )
182
- body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
183
-
184
- for_statement = IR::ForStmt.new(
185
- init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
186
- condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
187
- post: IR::Assignment.new(
188
- target: index_ref,
189
- operator: "+=",
190
- value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
191
- ),
192
- body:,
193
- )
194
-
195
- statements = [
196
- *iterable_setup,
197
- IR::LocalDecl.new(name: iterable_linkage_name, linkage_name: iterable_linkage_name, type: iterable_type, value: lower_expression(prepared_iterable, env:, expected_type: iterable_type)),
198
- for_statement,
199
- ]
200
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
201
-
202
- IR::BlockStmt.new(body: statements)
243
+ binding = entry[:binding]
244
+ current_actual_scope(while_env[:scopes])[binding.name] = local_binding(type: entry[:binding_type], linkage_name: c_local_name(binding.name), mutable: false, pointer: false)
245
+ IR::LocalDecl.new(name: binding.name, linkage_name: c_local_name(binding.name), type: entry[:binding_type], value: loop_item_value)
203
246
  end
247
+ body.concat(
248
+ lower_block(
249
+ statement.body,
250
+ env: while_env,
251
+ active_defers:,
252
+ return_type:,
253
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
254
+ allow_return:,
255
+ ),
256
+ )
257
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
204
258
 
205
- def lower_parallel_collection_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
206
- infos = statement.bindings.each_with_index.map do |binding, index|
207
- iterable = statement.iterables[index]
208
- iterable_type = infer_expression_type(iterable, env:)
209
- element_type = collection_loop_type(iterable_type)
210
- raise LoweringError.new("parallel for loops expect arrays or spans for each iterable, got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
211
-
212
- {
213
- binding:,
214
- iterable:,
215
- iterable_type:,
216
- element_type:,
217
- binding_type: collection_loop_binding_type(iterable_type, element_type) || element_type,
218
- }
219
- end
220
-
221
- iterable_entries = infos.map do |info|
222
- setup, prepared_iterable = prepare_expression_for_inline_lowering(info[:iterable], env:, expected_type: info[:iterable_type])
223
- linkage_name = fresh_c_temp_name(env, "for_items")
224
- info.merge(
225
- setup:,
226
- prepared_iterable:,
227
- iterable_linkage_name: linkage_name,
228
- iterable_ref: IR::Name.new(name: linkage_name, type: info[:iterable_type], pointer: false),
229
- )
230
- end
231
-
232
- index_linkage_name = fresh_c_temp_name(env, "for_index")
233
- continue_label = fresh_c_temp_name(env, "loop_continue")
234
- break_label = fresh_c_temp_name(env, "loop_break")
235
- index_ref = IR::Name.new(name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), pointer: false)
236
- stop_value = collection_loop_stop_value(iterable_entries.first[:iterable_ref], iterable_entries.first[:iterable_type])
237
-
238
- while_env = duplicate_env(env)
239
- body = iterable_entries.map do |entry|
240
- item_value = collection_loop_item_value(entry[:iterable_ref], entry[:iterable_type], index_ref, entry[:element_type])
241
- loop_item_value = if ref_type?(entry[:binding_type])
242
- IR::AddressOf.new(expression: item_value, type: entry[:binding_type])
243
- else
244
- item_value
245
- end
246
- binding = entry[:binding]
247
- current_actual_scope(while_env[:scopes])[binding.name] = local_binding(type: entry[:binding_type], linkage_name: c_local_name(binding.name), mutable: false, pointer: false)
248
- IR::LocalDecl.new(name: binding.name, linkage_name: c_local_name(binding.name), type: entry[:binding_type], value: loop_item_value)
249
- end
250
- body.concat(
251
- lower_block(
252
- statement.body,
253
- env: while_env,
254
- active_defers:,
255
- return_type:,
256
- loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
257
- allow_return:,
258
- ),
259
- )
260
- body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
261
-
262
- length_checks = iterable_entries.drop(1).map do |entry|
263
- IR::IfStmt.new(
264
- condition: IR::Binary.new(
265
- operator: "!=",
266
- left: collection_loop_stop_value(entry[:iterable_ref], entry[:iterable_type]),
267
- right: stop_value,
268
- type: @ctx.types.fetch("bool"),
269
- ),
270
- then_body: [lower_fatal_statement("parallel for iterables must have matching lengths", env:)],
271
- else_body: nil,
272
- )
273
- end
274
-
275
- for_statement = IR::ForStmt.new(
276
- init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
277
- condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
278
- post: IR::Assignment.new(
279
- target: index_ref,
280
- operator: "+=",
281
- value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
259
+ length_checks = iterable_entries.drop(1).map do |entry|
260
+ IR::IfStmt.new(
261
+ condition: IR::Binary.new(
262
+ operator: "!=",
263
+ left: collection_loop_stop_value(entry[:iterable_ref], entry[:iterable_type]),
264
+ right: stop_value,
265
+ type: @ctx.types.fetch("bool"),
282
266
  ),
283
- body:,
267
+ then_body: [lower_fatal_statement("parallel for iterables must have matching lengths", env:)],
268
+ else_body: nil,
284
269
  )
285
-
286
- statements = [
287
- *iterable_entries.flat_map { |entry| entry[:setup] },
288
- *iterable_entries.map do |entry|
289
- IR::LocalDecl.new(
290
- name: entry[:iterable_linkage_name],
291
- linkage_name: entry[:iterable_linkage_name],
292
- type: entry[:iterable_type],
293
- value: lower_expression(entry[:prepared_iterable], env:, expected_type: entry[:iterable_type]),
294
- )
295
- end,
296
- *length_checks,
297
- for_statement,
298
- ]
299
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
300
-
301
- IR::BlockStmt.new(body: statements)
302
270
  end
303
271
 
304
- def lower_iterator_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
305
- iterable_type = infer_expression_type(statement.iterable, env:)
306
- iterator_info = iterator_loop_info(iterable_type, env:)
307
- raise LoweringError.new("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next(), got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless iterator_info
308
-
309
- iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
310
- iterator_c_name = fresh_c_temp_name(env, "for_iterator")
311
- iterator_name = iterator_c_name
312
- continue_label = fresh_c_temp_name(env, "loop_continue")
313
- break_label = fresh_c_temp_name(env, "loop_break")
314
-
315
- iter_call = AST::Call.new(
316
- callee: AST::MemberAccess.new(receiver: prepared_iterable, member: "iter"),
317
- arguments: [],
318
- )
319
-
320
- iterator_env = duplicate_env(env)
321
- current_actual_scope(iterator_env[:scopes])[iterator_name] = local_binding(
322
- type: iterator_info[:iterator_type],
323
- linkage_name: iterator_c_name,
324
- mutable: true,
325
- pointer: false,
326
- )
327
-
328
- loop_env = duplicate_env(iterator_env)
329
- current_actual_scope(loop_env[:scopes])[statement.name] = local_binding(
330
- type: iterator_info[:item_type],
331
- storage_type: iterator_info[:item_storage_type],
332
- linkage_name: c_local_name(statement.name),
333
- mutable: false,
334
- pointer: false,
335
- )
336
-
337
- next_call = AST::Call.new(
338
- callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
339
- arguments: [],
340
- )
341
-
342
- body = if iterator_info[:kind] == :nullable_item
343
- item_ref = IR::Name.new(name: c_local_name(statement.name), type: iterator_info[:item_storage_type], pointer: false)
344
- [
345
- IR::LocalDecl.new(
346
- name: statement.name,
347
- linkage_name: c_local_name(statement.name),
348
- type: iterator_info[:item_storage_type],
349
- value: lower_expression(next_call, env: iterator_env, expected_type: iterator_info[:item_storage_type]),
350
- ),
351
- IR::IfStmt.new(
352
- condition: IR::Binary.new(
353
- operator: "==",
354
- left: item_ref,
355
- right: IR::NullLiteral.new(type: iterator_info[:item_storage_type]),
356
- type: @ctx.types.fetch("bool"),
357
- ),
358
- then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
359
- else_body: nil,
360
- ),
361
- ]
362
- else
363
- ready_linkage_name = fresh_c_temp_name(env, "for_ready")
364
- ready_ref = IR::Name.new(name: ready_linkage_name, type: @ctx.types.fetch("bool"), pointer: false)
365
- current_call = AST::Call.new(
366
- callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
367
- arguments: [],
368
- )
369
- [
370
- IR::LocalDecl.new(
371
- name: ready_linkage_name,
372
- linkage_name: ready_linkage_name,
373
- type: @ctx.types.fetch("bool"),
374
- value: lower_expression(next_call, env: iterator_env, expected_type: @ctx.types.fetch("bool")),
375
- ),
376
- IR::IfStmt.new(
377
- condition: IR::Unary.new(operator: "not", operand: ready_ref, type: @ctx.types.fetch("bool")),
378
- then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
379
- else_body: nil,
380
- ),
381
- IR::LocalDecl.new(
382
- name: statement.name,
383
- linkage_name: c_local_name(statement.name),
384
- type: iterator_info[:item_storage_type],
385
- value: lower_expression(current_call, env: iterator_env, expected_type: iterator_info[:item_storage_type]),
386
- ),
387
- ]
388
- end
389
- body.concat(
390
- lower_block(
391
- statement.body,
392
- env: loop_env,
393
- active_defers:,
394
- return_type:,
395
- loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
396
- allow_return:,
397
- ),
398
- )
399
- body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
400
-
401
- statements = [
402
- *iterable_setup,
272
+ for_statement = IR::ForStmt.new(
273
+ init: IR::LocalDecl.new(name: index_linkage_name, linkage_name: index_linkage_name, type: @ctx.types.fetch("ptr_uint"), value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint"))),
274
+ condition: IR::Binary.new(operator: "<", left: index_ref, right: stop_value, type: @ctx.types.fetch("bool")),
275
+ post: IR::Assignment.new(
276
+ target: index_ref,
277
+ operator: "+=",
278
+ value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
279
+ ),
280
+ body:,
281
+ )
282
+
283
+ statements = [
284
+ *iterable_entries.flat_map { |entry| entry[:setup] },
285
+ *iterable_entries.map do |entry|
403
286
  IR::LocalDecl.new(
404
- name: iterator_name,
405
- linkage_name: iterator_c_name,
406
- type: iterator_info[:iterator_type],
407
- value: lower_expression(iter_call, env:, expected_type: iterator_info[:iterator_type]),
408
- ),
409
- IR::WhileStmt.new(
410
- condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
411
- body:,
412
- ),
413
- ]
414
- statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
415
-
416
- IR::BlockStmt.new(body: statements)
417
- end
418
-
419
- def lower_range_index_assignment(statement, env:)
420
- range = statement.target.index
421
- start_val = range.start_expr.value
422
- receiver_type = infer_expression_type(statement.target.receiver, env:)
423
- element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint"))
424
-
425
- receiver_setup, prepared_receiver = prepare_expression_for_inline_lowering(statement.target.receiver, env:, expected_type: receiver_type)
426
- statements = receiver_setup.dup
427
-
428
- statement.value.elements.each_with_index do |elem, i|
429
- index_ir = IR::IntegerLiteral.new(value: start_val + i, type: @ctx.types.fetch("ptr_uint"))
430
- target_ir = IR::Index.new(
431
- receiver: lower_expression(prepared_receiver, env:, expected_type: receiver_type),
432
- index: index_ir,
433
- type: element_type,
287
+ name: entry[:iterable_linkage_name],
288
+ linkage_name: entry[:iterable_linkage_name],
289
+ type: entry[:iterable_type],
290
+ value: lower_expression(entry[:prepared_iterable], env:, expected_type: entry[:iterable_type]),
434
291
  )
435
- value_ir = lower_contextual_expression(
436
- elem,
437
- env:,
438
- expected_type: element_type,
439
- contextual_int_to_float: contextual_int_to_float_target?(element_type),
440
- )
441
- statements << IR::Assignment.new(target: target_ir, operator: "=", value: value_ir)
442
- end
443
-
444
- statements
445
- end
446
-
447
- def lower_threaded_for_stmt(statement, env:, active_defers:)
448
- loop_type = infer_range_loop_type(statement.iterable, env:)
449
- start_expr_ast = range_start_of(statement.iterable)
450
- stop_expr_ast = range_end_of(statement.iterable)
451
- start_setup, _ = prepare_expression_for_inline_lowering(start_expr_ast, env:, expected_type: loop_type)
452
- stop_setup, prepared_stop = prepare_expression_for_inline_lowering(stop_expr_ast, env:, expected_type: loop_type)
453
-
454
- lowered_stop = lower_expression(prepared_stop, env:, expected_type: loop_type)
455
- index_linkage_name = c_local_name(statement.name)
456
-
457
- body_env = duplicate_env(env)
458
- current_actual_scope(body_env[:scopes])[statement.name] = local_binding(
459
- type: loop_type, linkage_name: index_linkage_name, mutable: false, pointer: false,
460
- )
461
-
462
- body = lower_block(
292
+ end,
293
+ *length_checks,
294
+ for_statement,
295
+ ]
296
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
297
+
298
+ IR::BlockStmt.new(body: statements)
299
+ end
300
+
301
+ def lower_iterator_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
302
+ iterable_type = infer_expression_type(statement.iterable, env:)
303
+ iterator_info = iterator_loop_info(iterable_type, env:)
304
+ raise LoweringError.new("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next(), got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless iterator_info
305
+
306
+ iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
307
+ iterator_c_name = fresh_c_temp_name(env, "for_iterator")
308
+ iterator_name = iterator_c_name
309
+ continue_label = fresh_c_temp_name(env, "loop_continue")
310
+ break_label = fresh_c_temp_name(env, "loop_break")
311
+
312
+ iter_call = AST::Call.new(
313
+ callee: AST::MemberAccess.new(receiver: prepared_iterable, member: "iter"),
314
+ arguments: [],
315
+ )
316
+
317
+ iterator_env = duplicate_env(env)
318
+ current_actual_scope(iterator_env[:scopes])[iterator_name] = local_binding(
319
+ type: iterator_info[:iterator_type],
320
+ linkage_name: iterator_c_name,
321
+ mutable: true,
322
+ pointer: false,
323
+ )
324
+
325
+ loop_env = duplicate_env(iterator_env)
326
+ current_actual_scope(loop_env[:scopes])[statement.name] = local_binding(
327
+ type: iterator_info[:item_type],
328
+ storage_type: iterator_info[:item_storage_type],
329
+ linkage_name: c_local_name(statement.name),
330
+ mutable: false,
331
+ pointer: false,
332
+ )
333
+
334
+ next_call = AST::Call.new(
335
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
336
+ arguments: [],
337
+ )
338
+
339
+ body = if iterator_info[:kind] == :nullable_item
340
+ item_ref = IR::Name.new(name: c_local_name(statement.name), type: iterator_info[:item_storage_type], pointer: false)
341
+ [
342
+ IR::LocalDecl.new(
343
+ name: statement.name,
344
+ linkage_name: c_local_name(statement.name),
345
+ type: iterator_info[:item_storage_type],
346
+ value: lower_expression(next_call, env: iterator_env, expected_type: iterator_info[:item_storage_type]),
347
+ ),
348
+ IR::IfStmt.new(
349
+ condition: IR::Binary.new(
350
+ operator: "==",
351
+ left: item_ref,
352
+ right: IR::NullLiteral.new(type: iterator_info[:item_storage_type]),
353
+ type: @ctx.types.fetch("bool"),
354
+ ),
355
+ then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
356
+ else_body: nil,
357
+ ),
358
+ ]
359
+ else
360
+ ready_linkage_name = fresh_c_temp_name(env, "for_ready")
361
+ ready_ref = IR::Name.new(name: ready_linkage_name, type: @ctx.types.fetch("bool"), pointer: false)
362
+ current_call = AST::Call.new(
363
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
364
+ arguments: [],
365
+ )
366
+ [
367
+ IR::LocalDecl.new(
368
+ name: ready_linkage_name,
369
+ linkage_name: ready_linkage_name,
370
+ type: @ctx.types.fetch("bool"),
371
+ value: lower_expression(next_call, env: iterator_env, expected_type: @ctx.types.fetch("bool")),
372
+ ),
373
+ IR::IfStmt.new(
374
+ condition: IR::Unary.new(operator: "not", operand: ready_ref, type: @ctx.types.fetch("bool")),
375
+ then_body: [loop_exit_statement(loop_exit_break(break_label), local_defers: [], outer_defers: [])],
376
+ else_body: nil,
377
+ ),
378
+ IR::LocalDecl.new(
379
+ name: statement.name,
380
+ linkage_name: c_local_name(statement.name),
381
+ type: iterator_info[:item_storage_type],
382
+ value: lower_expression(current_call, env: iterator_env, expected_type: iterator_info[:item_storage_type]),
383
+ ),
384
+ ]
385
+ end
386
+ body.concat(
387
+ lower_block(
463
388
  statement.body,
464
- env: body_env,
389
+ env: loop_env,
465
390
  active_defers:,
466
- return_type: @ctx.types.fetch("void"),
467
- loop_flow: nil,
468
- allow_return: false,
469
- )
470
-
471
- @parallel_for_counter += 1
472
- uid = "#{@ctx.module_prefix}_pfor_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
473
- cap_struct_c_name = "mt_pfor_cap_#{uid}"
474
- worker_c_name = "mt_pfor_work_#{uid}"
475
-
476
- all_names = {}
477
- body.each { |s| collect_pfor_ir_names_stmt(s, all_names) }
478
- local_decls = Set.new
479
- body.each { |s| collect_pfor_local_decls(s, local_decls) }
480
- excluded = Set.new(local_decls.to_a + [index_linkage_name])
481
- captures = all_names.values.reject { |n| excluded.include?(n.name) }
482
-
483
- validate_pfor_no_ref_captures!(captures)
484
-
485
- void_type = @ctx.types.fetch("void")
486
- void_ptr_type = Types::Registry.generic_instance("ptr", [void_type])
487
- long_type = @ctx.types.fetch("long")
488
-
489
- array_capture_names = Set.new
490
- cap_fields = captures.map do |c|
491
- if array_type?(c.type)
492
- array_capture_names << c.name
493
- elem = array_element_type(c.type)
494
- IR::Field.new(name: c.name, type: Types::Registry.generic_instance("ptr", [elem]))
495
- else
496
- IR::Field.new(name: c.name, type: c.type)
497
- end
498
- end
499
- @artifacts.synthetic_structs << IR::StructDecl.new(
500
- name: cap_struct_c_name, linkage_name: cap_struct_c_name,
501
- fields: cap_fields, packed: false, alignment: nil,
502
- )
503
-
504
- cap_ptr_type = Types::Registry.generic_instance("ptr", [Types::Struct.new(cap_struct_c_name)])
505
- cap_name_ir = IR::Name.new(name: "mt_cap", type: cap_ptr_type, pointer: true)
506
- worker_body = [
507
- IR::LocalDecl.new(
508
- name: "mt_cap", linkage_name: "mt_cap", type: cap_ptr_type,
509
- value: IR::Cast.new(
510
- target_type: cap_ptr_type,
511
- expression: IR::Name.new(name: "mt_pfor_data", type: void_ptr_type, pointer: false),
512
- type: cap_ptr_type,
513
- ),
514
- ),
515
- ]
516
- captures.each do |c|
517
- alias_type = if array_capture_names.include?(c.name)
518
- Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
519
- else
520
- c.type
521
- end
522
- worker_body << IR::LocalDecl.new(
523
- name: c.name, linkage_name: c.name, type: alias_type,
524
- value: IR::Member.new(receiver: cap_name_ir, member: c.name, type: alias_type),
525
- )
526
- end
527
-
528
- body = rewrite_pfor_array_captures(body, array_capture_names) unless array_capture_names.empty?
529
-
530
- loop_var_ref = IR::Name.new(name: index_linkage_name, type: loop_type, pointer: false)
531
- worker_body << IR::ForStmt.new(
532
- init: IR::LocalDecl.new(
533
- name: index_linkage_name, linkage_name: index_linkage_name, type: loop_type,
534
- value: IR::Name.new(name: "mt_pfor_start", type: long_type, pointer: false),
535
- ),
536
- condition: IR::Binary.new(
537
- operator: "<",
538
- left: loop_var_ref,
539
- right: IR::Name.new(name: "mt_pfor_end", type: long_type, pointer: false),
540
- type: @ctx.types.fetch("bool"),
541
- ),
542
- post: IR::Assignment.new(
543
- target: loop_var_ref,
544
- operator: "+=",
545
- value: IR::IntegerLiteral.new(value: 1, type: loop_type),
546
- ),
391
+ return_type:,
392
+ loop_flow: loop_flow(break_target: loop_exit_break(break_label), continue_target: loop_exit_continue(continue_label)),
393
+ allow_return:,
394
+ ),
395
+ )
396
+ body << IR::LabelStmt.new(name: continue_label) if contains_label_target?(body, continue_label)
397
+
398
+ statements = [
399
+ *iterable_setup,
400
+ IR::LocalDecl.new(
401
+ name: iterator_name,
402
+ linkage_name: iterator_c_name,
403
+ type: iterator_info[:iterator_type],
404
+ value: lower_expression(iter_call, env:, expected_type: iterator_info[:iterator_type]),
405
+ ),
406
+ IR::WhileStmt.new(
407
+ condition: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
547
408
  body:,
409
+ ),
410
+ ]
411
+ statements << IR::LabelStmt.new(name: break_label) if contains_label_target?(body, break_label)
412
+
413
+ IR::BlockStmt.new(body: statements)
414
+ end
415
+
416
+ def lower_range_index_assignment(statement, env:)
417
+ range = statement.target.index
418
+ start_val = range.start_expr.value
419
+ receiver_type = infer_expression_type(statement.target.receiver, env:)
420
+ element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint"))
421
+
422
+ receiver_setup, prepared_receiver = prepare_expression_for_inline_lowering(statement.target.receiver, env:, expected_type: receiver_type)
423
+ statements = receiver_setup.dup
424
+
425
+ statement.value.elements.each_with_index do |elem, i|
426
+ index_ir = IR::IntegerLiteral.new(value: start_val + i, type: @ctx.types.fetch("ptr_uint"))
427
+ target_ir = IR::Index.new(
428
+ receiver: lower_expression(prepared_receiver, env:, expected_type: receiver_type),
429
+ index: index_ir,
430
+ type: element_type,
548
431
  )
549
-
550
- @artifacts.synthetic_functions << IR::Function.new(
551
- name: worker_c_name, linkage_name: worker_c_name,
552
- params: [
553
- IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: void_ptr_type, pointer: false),
554
- IR::Param.new(name: "mt_pfor_start", linkage_name: "mt_pfor_start", type: long_type, pointer: false),
555
- IR::Param.new(name: "mt_pfor_end", linkage_name: "mt_pfor_end", type: long_type, pointer: false),
556
- ],
557
- return_type: void_type,
558
- body: worker_body,
559
- entry_point: false,
432
+ value_ir = lower_contextual_expression(
433
+ elem,
434
+ env:,
435
+ expected_type: element_type,
436
+ contextual_int_to_float: contextual_int_to_float_target?(element_type),
560
437
  )
438
+ statements << IR::Assignment.new(target: target_ir, operator: "=", value: value_ir)
439
+ end
561
440
 
562
- cap_struct_type = Types::Struct.new(cap_struct_c_name, linkage_name: cap_struct_c_name).tap do |s|
563
- s.define_fields(captures.each_with_object({}) do |c, h|
564
- h[c.name] = if array_capture_names.include?(c.name)
565
- Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
566
- else
567
- c.type
568
- end
569
- end)
441
+ statements
442
+ end
443
+
444
+ def lower_threaded_for_stmt(statement, env:, active_defers:)
445
+ loop_type = infer_range_loop_type(statement.iterable, env:)
446
+ start_expr_ast = range_start_of(statement.iterable)
447
+ stop_expr_ast = range_end_of(statement.iterable)
448
+ start_setup, _ = prepare_expression_for_inline_lowering(start_expr_ast, env:, expected_type: loop_type)
449
+ stop_setup, prepared_stop = prepare_expression_for_inline_lowering(stop_expr_ast, env:, expected_type: loop_type)
450
+
451
+ lowered_stop = lower_expression(prepared_stop, env:, expected_type: loop_type)
452
+ index_linkage_name = c_local_name(statement.name)
453
+
454
+ body_env = duplicate_env(env)
455
+ current_actual_scope(body_env[:scopes])[statement.name] = local_binding(
456
+ type: loop_type, linkage_name: index_linkage_name, mutable: false, pointer: false,
457
+ )
458
+
459
+ body = lower_block(
460
+ statement.body,
461
+ env: body_env,
462
+ active_defers:,
463
+ return_type: @ctx.types.fetch("void"),
464
+ loop_flow: nil,
465
+ allow_return: false,
466
+ )
467
+
468
+ @parallel_for_counter += 1
469
+ uid = "#{@ctx.module_prefix}_pfor_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
470
+ cap_struct_c_name = "mt_pfor_cap_#{uid}"
471
+ worker_c_name = "mt_pfor_work_#{uid}"
472
+
473
+ all_names = {}
474
+ body.each { |s| collect_pfor_ir_names_stmt(s, all_names) }
475
+ local_decls = Set.new
476
+ body.each { |s| collect_pfor_local_decls(s, local_decls) }
477
+ excluded = Set.new(local_decls.to_a + [index_linkage_name])
478
+ captures = all_names.values.reject { |n| excluded.include?(n.name) }
479
+
480
+ validate_pfor_no_ref_captures!(captures)
481
+
482
+ void_type = @ctx.types.fetch("void")
483
+ void_ptr_type = Types::Registry.generic_instance("ptr", [void_type])
484
+ long_type = @ctx.types.fetch("long")
485
+
486
+ array_capture_names = Set.new
487
+ cap_fields = captures.map do |c|
488
+ if array_type?(c.type)
489
+ array_capture_names << c.name
490
+ elem = array_element_type(c.type)
491
+ IR::Field.new(name: c.name, type: Types::Registry.generic_instance("ptr", [elem]))
492
+ else
493
+ IR::Field.new(name: c.name, type: c.type)
570
494
  end
571
- cap_local_name = "mt_pfor_cap"
572
- cap_init = IR::AggregateLiteral.new(
573
- type: cap_struct_type,
574
- fields: captures.map do |c|
575
- if array_capture_names.include?(c.name)
576
- elem = array_element_type(c.type)
577
- ptr_type = Types::Registry.generic_instance("ptr", [elem])
578
- IR::AggregateField.new(name: c.name, value: IR::Name.new(name: c.name, type: ptr_type, pointer: false))
579
- else
580
- IR::AggregateField.new(name: c.name, value: c)
581
- end
582
- end,
495
+ end
496
+ @artifacts.synthetic_structs << IR::StructDecl.new(
497
+ name: cap_struct_c_name, linkage_name: cap_struct_c_name,
498
+ fields: cap_fields, packed: false, alignment: nil,
499
+ )
500
+
501
+ cap_ptr_type = Types::Registry.generic_instance("ptr", [Types::Struct.new(cap_struct_c_name)])
502
+ cap_name_ir = IR::Name.new(name: "mt_cap", type: cap_ptr_type, pointer: true)
503
+ worker_body = [
504
+ IR::LocalDecl.new(
505
+ name: "mt_cap", linkage_name: "mt_cap", type: cap_ptr_type,
506
+ value: IR::Cast.new(
507
+ target_type: cap_ptr_type,
508
+ expression: IR::Name.new(name: "mt_pfor_data", type: void_ptr_type, pointer: false),
509
+ type: cap_ptr_type,
510
+ ),
511
+ ),
512
+ ]
513
+ captures.each do |c|
514
+ alias_type = if array_capture_names.include?(c.name)
515
+ Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
516
+ else
517
+ c.type
518
+ end
519
+ worker_body << IR::LocalDecl.new(
520
+ name: c.name, linkage_name: c.name, type: alias_type,
521
+ value: IR::Member.new(receiver: cap_name_ir, member: c.name, type: alias_type),
583
522
  )
584
-
585
- worker_fn_type = Types::Registry.function(nil, params: [], return_type: void_type)
586
- call_site = [
587
- IR::LocalDecl.new(name: cap_local_name, linkage_name: cap_local_name, type: cap_struct_type, value: cap_init),
588
- IR::ExpressionStmt.new(expression: IR::Call.new(
589
- callee: "mt_parallel_for",
590
- arguments: [
591
- IR::Name.new(name: worker_c_name, type: worker_fn_type, pointer: false),
592
- IR::AddressOf.new(
593
- expression: IR::Name.new(name: cap_local_name, type: cap_struct_type, pointer: false),
594
- type: void_ptr_type,
595
- ),
596
- lowered_stop,
597
- ],
598
- type: void_type,
599
- )),
600
- ]
601
-
602
- IR::BlockStmt.new(body: [*start_setup, *stop_setup, *call_site])
603
523
  end
604
524
 
605
- def lower_parallel_block_stmt(statement, env:, active_defers:)
606
- void_type = @ctx.types.fetch("void")
607
- void_ptr_type = Types::Registry.generic_instance("ptr", [void_type])
608
-
609
- @parallel_for_counter += 1
610
- uid_base = "#{@ctx.module_prefix}_spawn_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
611
-
612
- block_infos = statement.bodies.each_with_index.map do |body, idx|
613
- block_body = lower_block(
614
- body,
615
- env: duplicate_env(env),
616
- active_defers:,
617
- return_type: void_type,
618
- loop_flow: nil,
619
- allow_return: false,
620
- )
621
-
622
- all_names = {}
623
- block_body.each { |s| collect_pfor_ir_names_stmt(s, all_names) }
624
- local_decls = Set.new
625
- block_body.each { |s| collect_pfor_local_decls(s, local_decls) }
626
- captures = all_names.values.reject { |n| local_decls.include?(n.name) }
627
-
628
- validate_pfor_no_ref_captures!(captures)
629
- written_names = collect_pfor_written_names(block_body, captures)
630
- captureless = captures.empty?
631
-
632
- cap_struct_c_name = "mt_spawn_cap_#{uid_base}_#{idx}"
633
- worker_c_name = "mt_spawn_work_#{uid_base}_#{idx}"
634
-
635
- if captureless
636
- worker_body = block_body.dup
525
+ body = rewrite_pfor_array_captures(body, array_capture_names) unless array_capture_names.empty?
526
+
527
+ loop_var_ref = IR::Name.new(name: index_linkage_name, type: loop_type, pointer: false)
528
+ worker_body << IR::ForStmt.new(
529
+ init: IR::LocalDecl.new(
530
+ name: index_linkage_name, linkage_name: index_linkage_name, type: loop_type,
531
+ value: IR::Name.new(name: "mt_pfor_start", type: long_type, pointer: false),
532
+ ),
533
+ condition: IR::Binary.new(
534
+ operator: "<",
535
+ left: loop_var_ref,
536
+ right: IR::Name.new(name: "mt_pfor_end", type: long_type, pointer: false),
537
+ type: @ctx.types.fetch("bool"),
538
+ ),
539
+ post: IR::Assignment.new(
540
+ target: loop_var_ref,
541
+ operator: "+=",
542
+ value: IR::IntegerLiteral.new(value: 1, type: loop_type),
543
+ ),
544
+ body:,
545
+ )
546
+
547
+ @artifacts.synthetic_functions << IR::Function.new(
548
+ name: worker_c_name, linkage_name: worker_c_name,
549
+ params: [
550
+ IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: void_ptr_type, pointer: false),
551
+ IR::Param.new(name: "mt_pfor_start", linkage_name: "mt_pfor_start", type: long_type, pointer: false),
552
+ IR::Param.new(name: "mt_pfor_end", linkage_name: "mt_pfor_end", type: long_type, pointer: false),
553
+ ],
554
+ return_type: void_type,
555
+ body: worker_body,
556
+ entry_point: false,
557
+ )
558
+
559
+ cap_struct_type = Types::Struct.new(cap_struct_c_name, linkage_name: cap_struct_c_name).tap do |s|
560
+ s.define_fields(captures.each_with_object({}) do |c, h|
561
+ h[c.name] = if array_capture_names.include?(c.name)
562
+ Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
563
+ else
564
+ c.type
565
+ end
566
+ end)
567
+ end
568
+ cap_local_name = "mt_pfor_cap"
569
+ cap_init = IR::AggregateLiteral.new(
570
+ type: cap_struct_type,
571
+ fields: captures.map do |c|
572
+ if array_capture_names.include?(c.name)
573
+ elem = array_element_type(c.type)
574
+ ptr_type = Types::Registry.generic_instance("ptr", [elem])
575
+ IR::AggregateField.new(name: c.name, value: IR::Name.new(name: c.name, type: ptr_type, pointer: false))
637
576
  else
638
- array_capture_names = Set.new
639
- cap_fields = captures.map do |c|
640
- if array_type?(c.type)
641
- array_capture_names << c.name
642
- IR::Field.new(name: c.name, type: Types::Registry.generic_instance("ptr", [array_element_type(c.type)]))
643
- else
644
- IR::Field.new(name: c.name, type: c.type)
645
- end
646
- end
647
-
648
- @artifacts.synthetic_structs << IR::StructDecl.new(
649
- name: cap_struct_c_name, linkage_name: cap_struct_c_name,
650
- fields: cap_fields, packed: false, alignment: nil,
651
- )
652
-
653
- cap_ptr_type = Types::Registry.generic_instance("ptr", [Types::Struct.new(cap_struct_c_name)])
654
- cap_name_ir = IR::Name.new(name: "mt_cap", type: cap_ptr_type, pointer: true)
655
- worker_body = [
656
- IR::LocalDecl.new(
657
- name: "mt_cap", linkage_name: "mt_cap", type: cap_ptr_type,
658
- value: IR::Cast.new(
659
- target_type: cap_ptr_type,
660
- expression: IR::Name.new(name: "mt_pfor_data", type: void_ptr_type, pointer: false),
661
- type: cap_ptr_type,
662
- ),
663
- ),
664
- ]
665
- captures.each do |c|
666
- alias_type = if array_capture_names.include?(c.name)
667
- Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
668
- else
669
- c.type
670
- end
671
- worker_body << IR::LocalDecl.new(
672
- name: c.name, linkage_name: c.name, type: alias_type,
673
- value: IR::Member.new(receiver: cap_name_ir, member: c.name, type: alias_type),
674
- )
675
- end
676
-
677
- rewritten_body = array_capture_names.empty? ? block_body : rewrite_pfor_array_captures(block_body, array_capture_names)
678
- worker_body.concat(rewritten_body)
577
+ IR::AggregateField.new(name: c.name, value: c)
679
578
  end
680
-
681
- @artifacts.synthetic_functions << IR::Function.new(
682
- name: worker_c_name, linkage_name: worker_c_name,
683
- params: [IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: void_ptr_type, pointer: false)],
684
- return_type: void_type,
685
- body: worker_body,
686
- entry_point: false,
687
- )
688
-
689
- cap_struct_type = if captureless
690
- void_ptr_type
691
- else
692
- Types::Struct.new(cap_struct_c_name, linkage_name: cap_struct_c_name).tap do |s|
693
- s.define_fields(captures.each_with_object({}) do |c, h|
694
- h[c.name] = if array_capture_names.include?(c.name)
695
- Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
696
- else
697
- c.type
698
- end
699
- end)
700
- end
701
- end
702
- cap_local_name = "mt_spawn_cap_#{idx}"
703
- cap_init = if captureless
704
- IR::IntegerLiteral.new(value: 0, type: void_ptr_type)
705
- else
706
- IR::AggregateLiteral.new(
707
- type: cap_struct_type,
708
- fields: captures.map do |c|
709
- if array_capture_names.include?(c.name)
710
- elem = array_element_type(c.type)
711
- ptr_type = Types::Registry.generic_instance("ptr", [elem])
712
- IR::AggregateField.new(name: c.name, value: IR::Name.new(name: c.name, type: ptr_type, pointer: false))
713
- else
714
- IR::AggregateField.new(name: c.name, value: c)
715
- end
716
- end,
717
- )
718
- end
719
-
720
- { worker_c_name:, cap_local_name:, cap_struct_type:, cap_init:, capture_names: Set.new(captures.map(&:name)), written_names:, captureless: }
721
- end
722
-
723
- validate_pfor_write_conflicts!(block_infos)
724
-
725
- fn_type = Types::Registry.function(nil, params: [], return_type: void_type)
726
- spawn_item_type = Types::Struct.new("mt_spawn_item", linkage_name: "mt_spawn_item").tap do |s|
727
- s.define_fields({ "work" => fn_type, "data" => void_ptr_type })
728
- end
729
-
730
- call_site = []
731
- block_infos.each do |info|
732
- unless info[:captureless]
733
- call_site << IR::LocalDecl.new(
734
- name: info[:cap_local_name], linkage_name: info[:cap_local_name],
735
- type: info[:cap_struct_type], value: info[:cap_init],
736
- )
737
- end
738
- end
739
-
740
- tasks_local = "mt_spawn_tasks"
741
- tasks_count = block_infos.length
742
- tasks_array_type = Types::Registry.generic_instance("array", [spawn_item_type, Types::LiteralTypeArg.new(tasks_count)])
743
- tasks_init = IR::ArrayLiteral.new(
744
- type: tasks_array_type,
745
- elements: block_infos.map { |info|
746
- data_expr = if info[:captureless]
747
- IR::IntegerLiteral.new(value: 0, type: void_ptr_type)
748
- else
749
- IR::AddressOf.new(
750
- expression: IR::Name.new(name: info[:cap_local_name], type: info[:cap_struct_type], pointer: false),
751
- type: void_ptr_type,
752
- )
753
- end
754
- IR::AggregateLiteral.new(
755
- type: spawn_item_type,
756
- fields: [
757
- IR::AggregateField.new(name: "work", value: IR::Name.new(name: info[:worker_c_name], type: fn_type, pointer: false)),
758
- IR::AggregateField.new(name: "data", value: data_expr),
759
- ],
760
- )
761
- },
762
- )
763
- call_site << IR::LocalDecl.new(name: tasks_local, linkage_name: tasks_local, type: tasks_array_type, value: tasks_init)
764
- call_site << IR::ExpressionStmt.new(expression: IR::Call.new(
765
- callee: "mt_spawn_all",
579
+ end,
580
+ )
581
+
582
+ worker_fn_type = Types::Registry.function(nil, params: [], return_type: void_type)
583
+ call_site = [
584
+ IR::LocalDecl.new(name: cap_local_name, linkage_name: cap_local_name, type: cap_struct_type, value: cap_init),
585
+ IR::ExpressionStmt.new(expression: IR::Call.new(
586
+ callee: "mt_parallel_for",
766
587
  arguments: [
767
- IR::Name.new(name: tasks_local, type: tasks_array_type, pointer: false),
768
- IR::IntegerLiteral.new(value: tasks_count, type: @ctx.types.fetch("int")),
588
+ IR::Name.new(name: worker_c_name, type: worker_fn_type, pointer: false),
589
+ IR::AddressOf.new(
590
+ expression: IR::Name.new(name: cap_local_name, type: cap_struct_type, pointer: false),
591
+ type: void_ptr_type,
592
+ ),
593
+ lowered_stop,
769
594
  ],
770
595
  type: void_type,
771
- ))
596
+ )),
597
+ ]
772
598
 
773
- IR::BlockStmt.new(body: call_site)
774
- end
599
+ IR::BlockStmt.new(body: [*start_setup, *stop_setup, *call_site])
600
+ end
775
601
 
776
- def lower_gather_stmt(statement, env:)
777
- call_site = statement.handles.map do |handle_expr|
778
- handle_ir = lower_expression(handle_expr, env:)
779
- IR::ExpressionStmt.new(expression: IR::Call.new(
780
- callee: "mt_detach_join",
781
- arguments: [handle_ir],
782
- type: @ctx.types.fetch("void"),
783
- ))
784
- end
785
- IR::BlockStmt.new(body: call_site)
786
- end
602
+ def lower_parallel_block_stmt(statement, env:, active_defers:)
603
+ void_type = @ctx.types.fetch("void")
604
+ void_ptr_type = Types::Registry.generic_instance("ptr", [void_type])
787
605
 
788
- def lower_detach_expr(expression, env:)
789
- @parallel_for_counter += 1
790
- uid = "#{@ctx.module_prefix}_detach_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
791
- worker_c_name = "mt_detach_work_#{uid}"
606
+ @parallel_for_counter += 1
607
+ uid_base = "#{@ctx.module_prefix}_spawn_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
792
608
 
609
+ block_infos = statement.bodies.each_with_index.map do |body, idx|
793
610
  block_body = lower_block(
794
- expression.body,
611
+ body,
795
612
  env: duplicate_env(env),
796
- active_defers: nil,
797
- return_type: @ctx.types.fetch("void"),
613
+ active_defers:,
614
+ return_type: void_type,
798
615
  loop_flow: nil,
799
616
  allow_return: false,
800
617
  )
@@ -804,333 +621,513 @@ module MilkTea
804
621
  local_decls = Set.new
805
622
  block_body.each { |s| collect_pfor_local_decls(s, local_decls) }
806
623
  captures = all_names.values.reject { |n| local_decls.include?(n.name) }
624
+
807
625
  validate_pfor_no_ref_captures!(captures)
626
+ written_names = collect_pfor_written_names(block_body, captures)
627
+ captureless = captures.empty?
808
628
 
809
- if captures.empty?
629
+ cap_struct_c_name = "mt_spawn_cap_#{uid_base}_#{idx}"
630
+ worker_c_name = "mt_spawn_work_#{uid_base}_#{idx}"
631
+
632
+ if captureless
810
633
  worker_body = block_body.dup
811
634
  else
812
- raise LoweringError.new("detach with captured variables is not yet supported; use a global function call or module-level variables", line: 0, column: 0, path: @ctx.current_analysis_path)
635
+ array_capture_names = Set.new
636
+ cap_fields = captures.map do |c|
637
+ if array_type?(c.type)
638
+ array_capture_names << c.name
639
+ IR::Field.new(name: c.name, type: Types::Registry.generic_instance("ptr", [array_element_type(c.type)]))
640
+ else
641
+ IR::Field.new(name: c.name, type: c.type)
642
+ end
643
+ end
644
+
645
+ @artifacts.synthetic_structs << IR::StructDecl.new(
646
+ name: cap_struct_c_name, linkage_name: cap_struct_c_name,
647
+ fields: cap_fields, packed: false, alignment: nil,
648
+ )
649
+
650
+ cap_ptr_type = Types::Registry.generic_instance("ptr", [Types::Struct.new(cap_struct_c_name)])
651
+ cap_name_ir = IR::Name.new(name: "mt_cap", type: cap_ptr_type, pointer: true)
652
+ worker_body = [
653
+ IR::LocalDecl.new(
654
+ name: "mt_cap", linkage_name: "mt_cap", type: cap_ptr_type,
655
+ value: IR::Cast.new(
656
+ target_type: cap_ptr_type,
657
+ expression: IR::Name.new(name: "mt_pfor_data", type: void_ptr_type, pointer: false),
658
+ type: cap_ptr_type,
659
+ ),
660
+ ),
661
+ ]
662
+ captures.each do |c|
663
+ alias_type = if array_capture_names.include?(c.name)
664
+ Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
665
+ else
666
+ c.type
667
+ end
668
+ worker_body << IR::LocalDecl.new(
669
+ name: c.name, linkage_name: c.name, type: alias_type,
670
+ value: IR::Member.new(receiver: cap_name_ir, member: c.name, type: alias_type),
671
+ )
672
+ end
673
+
674
+ rewritten_body = array_capture_names.empty? ? block_body : rewrite_pfor_array_captures(block_body, array_capture_names)
675
+ worker_body.concat(rewritten_body)
813
676
  end
814
677
 
815
678
  @artifacts.synthetic_functions << IR::Function.new(
816
679
  name: worker_c_name, linkage_name: worker_c_name,
817
- params: [IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: @ctx.types.fetch("void").then { |v| Types::Registry.generic_instance("ptr", [v]) }, pointer: false)],
818
- return_type: @ctx.types.fetch("void"),
680
+ params: [IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: void_ptr_type, pointer: false)],
681
+ return_type: void_type,
819
682
  body: worker_body,
820
683
  entry_point: false,
821
684
  )
822
685
 
823
- void_ptr_type = Types::Registry.generic_instance("ptr", [@ctx.types.fetch("void")])
824
- IR::Call.new(
825
- callee: "mt_detach_run",
826
- arguments: [
827
- IR::Name.new(name: worker_c_name, type: Types::Registry.function(nil, params: [], return_type: @ctx.types.fetch("void")), pointer: false),
828
- IR::IntegerLiteral.new(value: 0, type: void_ptr_type),
829
- ],
830
- type: void_ptr_type,
831
- )
832
- end
686
+ cap_struct_type = if captureless
687
+ void_ptr_type
688
+ else
689
+ Types::Struct.new(cap_struct_c_name, linkage_name: cap_struct_c_name).tap do |s|
690
+ s.define_fields(captures.each_with_object({}) do |c, h|
691
+ h[c.name] = if array_capture_names.include?(c.name)
692
+ Types::Registry.generic_instance("ptr", [array_element_type(c.type)])
693
+ else
694
+ c.type
695
+ end
696
+ end)
697
+ end
698
+ end
699
+ cap_local_name = "mt_spawn_cap_#{idx}"
700
+ cap_init = if captureless
701
+ IR::IntegerLiteral.new(value: 0, type: void_ptr_type)
702
+ else
703
+ IR::AggregateLiteral.new(
704
+ type: cap_struct_type,
705
+ fields: captures.map do |c|
706
+ if array_capture_names.include?(c.name)
707
+ elem = array_element_type(c.type)
708
+ ptr_type = Types::Registry.generic_instance("ptr", [elem])
709
+ IR::AggregateField.new(name: c.name, value: IR::Name.new(name: c.name, type: ptr_type, pointer: false))
710
+ else
711
+ IR::AggregateField.new(name: c.name, value: c)
712
+ end
713
+ end,
714
+ )
715
+ end
833
716
 
834
- def validate_pfor_no_ref_captures!(captures)
835
- captures.each do |c|
836
- raise LoweringError.new("cannot capture '#{c.name}' of type ref across thread boundary — ref values are not safe to share across threads", line: 0, column: 0, path: @ctx.current_analysis_path) if ref_type?(c.type)
837
- end
717
+ { worker_c_name:, cap_local_name:, cap_struct_type:, cap_init:, capture_names: Set.new(captures.map(&:name)), written_names:, captureless: }
838
718
  end
839
719
 
840
- def validate_pfor_write_conflicts!(block_infos)
841
- block_infos.each_with_index do |info, idx|
842
- info[:written_names].each do |name|
843
- block_infos.each_with_index do |other, other_idx|
844
- next if idx == other_idx
845
- next unless other[:capture_names].include?(name)
720
+ validate_pfor_write_conflicts!(block_infos)
846
721
 
847
- raise LoweringError.new("write conflict in parallel block: '#{name}' is written in spawn block #{idx + 1} and accessed in spawn block #{other_idx + 1}", line: 0, column: 0, path: @ctx.current_analysis_path)
848
- end
849
- end
850
- end
722
+ fn_type = Types::Registry.function(nil, params: [], return_type: void_type)
723
+ spawn_item_type = Types::Struct.new("mt_spawn_item", linkage_name: "mt_spawn_item").tap do |s|
724
+ s.define_fields({ "work" => fn_type, "data" => void_ptr_type })
851
725
  end
852
726
 
853
- def collect_pfor_written_names(stmts, captures)
854
- capture_names = Set.new(captures.map(&:name))
855
- written = Set.new
856
- stmts.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
857
- written
858
- end
859
-
860
- def collect_pfor_written_stmt(stmt, capture_names, written)
861
- case stmt
862
- when IR::Assignment
863
- base = pfor_assignment_base_name(stmt.target)
864
- written << base if base && capture_names.include?(base)
865
- when IR::IfStmt
866
- stmt.then_body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
867
- stmt.else_body&.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
868
- when IR::WhileStmt
869
- stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
870
- when IR::ForStmt
871
- stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
872
- when IR::BlockStmt
873
- stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
874
- when IR::SwitchStmt
875
- stmt.cases.each { |c| c.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) } }
727
+ call_site = []
728
+ block_infos.each do |info|
729
+ unless info[:captureless]
730
+ call_site << IR::LocalDecl.new(
731
+ name: info[:cap_local_name], linkage_name: info[:cap_local_name],
732
+ type: info[:cap_struct_type], value: info[:cap_init],
733
+ )
876
734
  end
877
735
  end
878
736
 
879
- def pfor_assignment_base_name(expr)
880
- case expr
881
- when IR::Name
882
- expr.name
883
- when IR::Member
884
- pfor_assignment_base_name(expr.receiver)
885
- when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
886
- pfor_assignment_base_name(expr.receiver)
887
- when IR::AddressOf
888
- pfor_assignment_base_name(expr.expression)
889
- else
890
- nil
891
- end
737
+ tasks_local = "mt_spawn_tasks"
738
+ tasks_count = block_infos.length
739
+ tasks_array_type = Types::Registry.generic_instance("array", [spawn_item_type, Types::LiteralTypeArg.new(tasks_count)])
740
+ tasks_init = IR::ArrayLiteral.new(
741
+ type: tasks_array_type,
742
+ elements: block_infos.map { |info|
743
+ data_expr = if info[:captureless]
744
+ IR::IntegerLiteral.new(value: 0, type: void_ptr_type)
745
+ else
746
+ IR::AddressOf.new(
747
+ expression: IR::Name.new(name: info[:cap_local_name], type: info[:cap_struct_type], pointer: false),
748
+ type: void_ptr_type,
749
+ )
750
+ end
751
+ IR::AggregateLiteral.new(
752
+ type: spawn_item_type,
753
+ fields: [
754
+ IR::AggregateField.new(name: "work", value: IR::Name.new(name: info[:worker_c_name], type: fn_type, pointer: false)),
755
+ IR::AggregateField.new(name: "data", value: data_expr),
756
+ ],
757
+ )
758
+ },
759
+ )
760
+ call_site << IR::LocalDecl.new(name: tasks_local, linkage_name: tasks_local, type: tasks_array_type, value: tasks_init)
761
+ call_site << IR::ExpressionStmt.new(expression: IR::Call.new(
762
+ callee: "mt_spawn_all",
763
+ arguments: [
764
+ IR::Name.new(name: tasks_local, type: tasks_array_type, pointer: false),
765
+ IR::IntegerLiteral.new(value: tasks_count, type: @ctx.types.fetch("int")),
766
+ ],
767
+ type: void_type,
768
+ ))
769
+
770
+ IR::BlockStmt.new(body: call_site)
771
+ end
772
+
773
+ def lower_gather_stmt(statement, env:)
774
+ call_site = statement.handles.map do |handle_expr|
775
+ handle_ir = lower_expression(handle_expr, env:)
776
+ IR::ExpressionStmt.new(expression: IR::Call.new(
777
+ callee: "mt_detach_join",
778
+ arguments: [handle_ir],
779
+ type: @ctx.types.fetch("void"),
780
+ ))
892
781
  end
893
-
894
- def rewrite_pfor_array_captures(stmts, array_names)
895
- stmts.map { |s| rewrite_pfor_stmt(s, array_names) }
782
+ IR::BlockStmt.new(body: call_site)
783
+ end
784
+
785
+ def lower_detach_expr(expression, env:)
786
+ @parallel_for_counter += 1
787
+ uid = "#{@ctx.module_prefix}_detach_#{@parallel_for_counter}".gsub(/[^A-Za-z0-9_]/, "_")
788
+ worker_c_name = "mt_detach_work_#{uid}"
789
+
790
+ block_body = lower_block(
791
+ expression.body,
792
+ env: duplicate_env(env),
793
+ active_defers: nil,
794
+ return_type: @ctx.types.fetch("void"),
795
+ loop_flow: nil,
796
+ allow_return: false,
797
+ )
798
+
799
+ all_names = {}
800
+ block_body.each { |s| collect_pfor_ir_names_stmt(s, all_names) }
801
+ local_decls = Set.new
802
+ block_body.each { |s| collect_pfor_local_decls(s, local_decls) }
803
+ captures = all_names.values.reject { |n| local_decls.include?(n.name) }
804
+ validate_pfor_no_ref_captures!(captures)
805
+
806
+ if captures.empty?
807
+ worker_body = block_body.dup
808
+ else
809
+ raise LoweringError.new("detach with captured variables is not yet supported; use a global function call or module-level variables", line: 0, column: 0, path: @ctx.current_analysis_path)
896
810
  end
897
811
 
898
- def rewrite_pfor_stmt(stmt, array_names)
899
- case stmt
900
- when IR::Assignment
901
- IR::Assignment.new(
902
- target: rewrite_pfor_expr(stmt.target, array_names),
903
- operator: stmt.operator,
904
- value: rewrite_pfor_expr(stmt.value, array_names),
905
- )
906
- when IR::LocalDecl
907
- stmt.value ? IR::LocalDecl.new(name: stmt.name, linkage_name: stmt.linkage_name, type: stmt.type, value: rewrite_pfor_expr(stmt.value, array_names), line: stmt.line, source_path: stmt.source_path) : stmt
908
- when IR::ExpressionStmt
909
- IR::ExpressionStmt.new(expression: rewrite_pfor_expr(stmt.expression, array_names), line: stmt.line, source_path: stmt.source_path)
910
- when IR::IfStmt
911
- IR::IfStmt.new(
912
- condition: rewrite_pfor_expr(stmt.condition, array_names),
913
- then_body: rewrite_pfor_array_captures(stmt.then_body, array_names),
914
- else_body: stmt.else_body ? rewrite_pfor_array_captures(stmt.else_body, array_names) : nil,
915
- )
916
- when IR::ForStmt
917
- IR::ForStmt.new(
918
- init: stmt.init ? rewrite_pfor_stmt(stmt.init, array_names) : nil,
919
- condition: stmt.condition ? rewrite_pfor_expr(stmt.condition, array_names) : nil,
920
- post: stmt.post ? rewrite_pfor_stmt(stmt.post, array_names) : nil,
921
- body: rewrite_pfor_array_captures(stmt.body, array_names),
922
- )
923
- when IR::WhileStmt
924
- IR::WhileStmt.new(
925
- condition: rewrite_pfor_expr(stmt.condition, array_names),
926
- body: rewrite_pfor_array_captures(stmt.body, array_names),
927
- )
928
- when IR::BlockStmt
929
- IR::BlockStmt.new(body: rewrite_pfor_array_captures(stmt.body, array_names))
930
- when IR::SwitchStmt
931
- IR::SwitchStmt.new(
932
- expression: rewrite_pfor_expr(stmt.expression, array_names),
933
- cases: stmt.cases.map { |c| IR::SwitchCase.new(value: c.value, body: rewrite_pfor_array_captures(c.body, array_names)) },
934
- exhaustive: stmt.exhaustive,
935
- )
936
- when IR::ReturnStmt
937
- stmt.value ? IR::ReturnStmt.new(value: rewrite_pfor_expr(stmt.value, array_names), line: stmt.line, source_path: stmt.source_path) : stmt
938
- else
939
- stmt
940
- end
812
+ @artifacts.synthetic_functions << IR::Function.new(
813
+ name: worker_c_name, linkage_name: worker_c_name,
814
+ params: [IR::Param.new(name: "mt_pfor_data", linkage_name: "mt_pfor_data", type: @ctx.types.fetch("void").then { |v| Types::Registry.generic_instance("ptr", [v]) }, pointer: false)],
815
+ return_type: @ctx.types.fetch("void"),
816
+ body: worker_body,
817
+ entry_point: false,
818
+ )
819
+
820
+ void_ptr_type = Types::Registry.generic_instance("ptr", [@ctx.types.fetch("void")])
821
+ IR::Call.new(
822
+ callee: "mt_detach_run",
823
+ arguments: [
824
+ IR::Name.new(name: worker_c_name, type: Types::Registry.function(nil, params: [], return_type: @ctx.types.fetch("void")), pointer: false),
825
+ IR::IntegerLiteral.new(value: 0, type: void_ptr_type),
826
+ ],
827
+ type: void_ptr_type,
828
+ )
829
+ end
830
+
831
+ def validate_pfor_no_ref_captures!(captures)
832
+ captures.each do |c|
833
+ raise LoweringError.new("cannot capture '#{c.name}' of type ref across thread boundary — ref values are not safe to share across threads", line: 0, column: 0, path: @ctx.current_analysis_path) if ref_type?(c.type)
941
834
  end
835
+ end
942
836
 
943
- def rewrite_pfor_expr(expr, array_names)
944
- case expr
945
- when IR::CheckedIndex
946
- if expr.receiver.is_a?(IR::AddressOf) &&
947
- expr.receiver.expression.is_a?(IR::Name) &&
948
- array_names.include?(expr.receiver.expression.name)
949
- name_node = expr.receiver.expression
950
- elem = array_element_type(name_node.type) if array_type?(name_node.type)
951
- ptr_type = elem ? Types::Registry.generic_instance("ptr", [elem]) : name_node.type
952
- IR::Index.new(
953
- receiver: IR::Name.new(name: name_node.name, type: ptr_type, pointer: false),
954
- index: rewrite_pfor_expr(expr.index, array_names),
955
- type: expr.type,
956
- )
957
- elsif expr.receiver.is_a?(IR::Name) &&
958
- array_names.include?(expr.receiver.name)
959
- name_node = expr.receiver
960
- elem = array_element_type(expr.receiver_type)
961
- ptr_type = elem ? Types::Registry.generic_instance("ptr", [elem]) : expr.receiver_type
962
- IR::Index.new(
963
- receiver: IR::Name.new(name: name_node.name, type: ptr_type, pointer: false),
964
- index: rewrite_pfor_expr(expr.index, array_names),
965
- type: expr.type,
966
- )
967
- else
968
- expr
837
+ def validate_pfor_write_conflicts!(block_infos)
838
+ block_infos.each_with_index do |info, idx|
839
+ info[:written_names].each do |name|
840
+ block_infos.each_with_index do |other, other_idx|
841
+ next if idx == other_idx
842
+ next unless other[:capture_names].include?(name)
843
+
844
+ raise LoweringError.new("write conflict in parallel block: '#{name}' is written in spawn block #{idx + 1} and accessed in spawn block #{other_idx + 1}", line: 0, column: 0, path: @ctx.current_analysis_path)
969
845
  end
970
- when IR::Binary
971
- IR::Binary.new(operator: expr.operator, left: rewrite_pfor_expr(expr.left, array_names), right: rewrite_pfor_expr(expr.right, array_names), type: expr.type)
972
- when IR::Unary
973
- IR::Unary.new(operator: expr.operator, operand: rewrite_pfor_expr(expr.operand, array_names), type: expr.type)
974
- when IR::Cast
975
- IR::Cast.new(target_type: expr.target_type, expression: rewrite_pfor_expr(expr.expression, array_names), type: expr.type)
976
- when IR::Call
977
- IR::Call.new(
978
- callee: expr.callee.is_a?(String) ? expr.callee : rewrite_pfor_expr(expr.callee, array_names),
979
- arguments: expr.arguments.map { |a| rewrite_pfor_expr(a, array_names) },
846
+ end
847
+ end
848
+ end
849
+
850
+ def collect_pfor_written_names(stmts, captures)
851
+ capture_names = Set.new(captures.map(&:name))
852
+ written = Set.new
853
+ stmts.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
854
+ written
855
+ end
856
+
857
+ def collect_pfor_written_stmt(stmt, capture_names, written)
858
+ case stmt
859
+ when IR::Assignment
860
+ base = pfor_assignment_base_name(stmt.target)
861
+ written << base if base && capture_names.include?(base)
862
+ when IR::IfStmt
863
+ stmt.then_body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
864
+ stmt.else_body&.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
865
+ when IR::WhileStmt
866
+ stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
867
+ when IR::ForStmt
868
+ stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
869
+ when IR::BlockStmt
870
+ stmt.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) }
871
+ when IR::SwitchStmt
872
+ stmt.cases.each { |c| c.body.each { |s| collect_pfor_written_stmt(s, capture_names, written) } }
873
+ end
874
+ end
875
+
876
+ def pfor_assignment_base_name(expr)
877
+ case expr
878
+ when IR::Name
879
+ expr.name
880
+ when IR::Member
881
+ pfor_assignment_base_name(expr.receiver)
882
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
883
+ pfor_assignment_base_name(expr.receiver)
884
+ when IR::AddressOf
885
+ pfor_assignment_base_name(expr.expression)
886
+ else
887
+ nil
888
+ end
889
+ end
890
+
891
+ def rewrite_pfor_array_captures(stmts, array_names)
892
+ stmts.map { |s| rewrite_pfor_stmt(s, array_names) }
893
+ end
894
+
895
+ def rewrite_pfor_stmt(stmt, array_names)
896
+ case stmt
897
+ when IR::Assignment
898
+ IR::Assignment.new(
899
+ target: rewrite_pfor_expr(stmt.target, array_names),
900
+ operator: stmt.operator,
901
+ value: rewrite_pfor_expr(stmt.value, array_names),
902
+ )
903
+ when IR::LocalDecl
904
+ stmt.value ? IR::LocalDecl.new(name: stmt.name, linkage_name: stmt.linkage_name, type: stmt.type, value: rewrite_pfor_expr(stmt.value, array_names), line: stmt.line, path: stmt.path) : stmt
905
+ when IR::ExpressionStmt
906
+ IR::ExpressionStmt.new(expression: rewrite_pfor_expr(stmt.expression, array_names), line: stmt.line, path: stmt.path)
907
+ when IR::IfStmt
908
+ IR::IfStmt.new(
909
+ condition: rewrite_pfor_expr(stmt.condition, array_names),
910
+ then_body: rewrite_pfor_array_captures(stmt.then_body, array_names),
911
+ else_body: stmt.else_body ? rewrite_pfor_array_captures(stmt.else_body, array_names) : nil,
912
+ )
913
+ when IR::ForStmt
914
+ IR::ForStmt.new(
915
+ init: stmt.init ? rewrite_pfor_stmt(stmt.init, array_names) : nil,
916
+ condition: stmt.condition ? rewrite_pfor_expr(stmt.condition, array_names) : nil,
917
+ post: stmt.post ? rewrite_pfor_stmt(stmt.post, array_names) : nil,
918
+ body: rewrite_pfor_array_captures(stmt.body, array_names),
919
+ )
920
+ when IR::WhileStmt
921
+ IR::WhileStmt.new(
922
+ condition: rewrite_pfor_expr(stmt.condition, array_names),
923
+ body: rewrite_pfor_array_captures(stmt.body, array_names),
924
+ )
925
+ when IR::BlockStmt
926
+ IR::BlockStmt.new(body: rewrite_pfor_array_captures(stmt.body, array_names))
927
+ when IR::SwitchStmt
928
+ IR::SwitchStmt.new(
929
+ expression: rewrite_pfor_expr(stmt.expression, array_names),
930
+ cases: stmt.cases.map { |c| IR::SwitchCase.new(value: c.value, body: rewrite_pfor_array_captures(c.body, array_names)) },
931
+ exhaustive: stmt.exhaustive,
932
+ )
933
+ when IR::ReturnStmt
934
+ stmt.value ? IR::ReturnStmt.new(value: rewrite_pfor_expr(stmt.value, array_names), line: stmt.line, path: stmt.path) : stmt
935
+ else
936
+ stmt
937
+ end
938
+ end
939
+
940
+ def rewrite_pfor_expr(expr, array_names)
941
+ case expr
942
+ when IR::CheckedIndex
943
+ if expr.receiver.is_a?(IR::AddressOf) &&
944
+ expr.receiver.expression.is_a?(IR::Name) &&
945
+ array_names.include?(expr.receiver.expression.name)
946
+ name_node = expr.receiver.expression
947
+ elem = array_element_type(name_node.type) if array_type?(name_node.type)
948
+ ptr_type = elem ? Types::Registry.generic_instance("ptr", [elem]) : name_node.type
949
+ IR::Index.new(
950
+ receiver: IR::Name.new(name: name_node.name, type: ptr_type, pointer: false),
951
+ index: rewrite_pfor_expr(expr.index, array_names),
980
952
  type: expr.type,
981
953
  )
982
- when IR::AddressOf
983
- IR::AddressOf.new(expression: rewrite_pfor_expr(expr.expression, array_names), type: expr.type)
984
- when IR::Conditional
985
- IR::Conditional.new(
986
- condition: rewrite_pfor_expr(expr.condition, array_names),
987
- then_expression: rewrite_pfor_expr(expr.then_expression, array_names),
988
- else_expression: rewrite_pfor_expr(expr.else_expression, array_names),
954
+ elsif expr.receiver.is_a?(IR::Name) &&
955
+ array_names.include?(expr.receiver.name)
956
+ name_node = expr.receiver
957
+ elem = array_element_type(expr.receiver_type)
958
+ ptr_type = elem ? Types::Registry.generic_instance("ptr", [elem]) : expr.receiver_type
959
+ IR::Index.new(
960
+ receiver: IR::Name.new(name: name_node.name, type: ptr_type, pointer: false),
961
+ index: rewrite_pfor_expr(expr.index, array_names),
989
962
  type: expr.type,
990
963
  )
991
- when IR::Member
992
- IR::Member.new(receiver: rewrite_pfor_expr(expr.receiver, array_names), member: expr.member, type: expr.type)
993
- when IR::Index
994
- IR::Index.new(receiver: rewrite_pfor_expr(expr.receiver, array_names), index: rewrite_pfor_expr(expr.index, array_names), type: expr.type)
995
- when IR::AggregateLiteral
996
- IR::AggregateLiteral.new(type: expr.type, fields: expr.fields.map { |f| IR::AggregateField.new(name: f.name, value: rewrite_pfor_expr(f.value, array_names)) })
997
964
  else
998
965
  expr
999
966
  end
967
+ when IR::Binary
968
+ IR::Binary.new(operator: expr.operator, left: rewrite_pfor_expr(expr.left, array_names), right: rewrite_pfor_expr(expr.right, array_names), type: expr.type)
969
+ when IR::Unary
970
+ IR::Unary.new(operator: expr.operator, operand: rewrite_pfor_expr(expr.operand, array_names), type: expr.type)
971
+ when IR::Cast
972
+ IR::Cast.new(target_type: expr.target_type, expression: rewrite_pfor_expr(expr.expression, array_names), type: expr.type)
973
+ when IR::Call
974
+ IR::Call.new(
975
+ callee: expr.callee.is_a?(String) ? expr.callee : rewrite_pfor_expr(expr.callee, array_names),
976
+ arguments: expr.arguments.map { |a| rewrite_pfor_expr(a, array_names) },
977
+ type: expr.type,
978
+ )
979
+ when IR::AddressOf
980
+ IR::AddressOf.new(expression: rewrite_pfor_expr(expr.expression, array_names), type: expr.type)
981
+ when IR::Conditional
982
+ IR::Conditional.new(
983
+ condition: rewrite_pfor_expr(expr.condition, array_names),
984
+ then_expression: rewrite_pfor_expr(expr.then_expression, array_names),
985
+ else_expression: rewrite_pfor_expr(expr.else_expression, array_names),
986
+ type: expr.type,
987
+ )
988
+ when IR::Member
989
+ IR::Member.new(receiver: rewrite_pfor_expr(expr.receiver, array_names), member: expr.member, type: expr.type)
990
+ when IR::Index
991
+ IR::Index.new(receiver: rewrite_pfor_expr(expr.receiver, array_names), index: rewrite_pfor_expr(expr.index, array_names), type: expr.type)
992
+ when IR::AggregateLiteral
993
+ IR::AggregateLiteral.new(type: expr.type, fields: expr.fields.map { |f| IR::AggregateField.new(name: f.name, value: rewrite_pfor_expr(f.value, array_names)) })
994
+ else
995
+ expr
1000
996
  end
1001
-
1002
- def collect_pfor_ir_names_stmt(stmt, result)
1003
- case stmt
1004
- when IR::LocalDecl
1005
- collect_pfor_ir_names_expr(stmt.value, result) if stmt.value
1006
- when IR::Assignment
1007
- collect_pfor_ir_names_expr(stmt.target, result)
1008
- collect_pfor_ir_names_expr(stmt.value, result)
1009
- when IR::ExpressionStmt
1010
- collect_pfor_ir_names_expr(stmt.expression, result)
1011
- when IR::ReturnStmt
1012
- collect_pfor_ir_names_expr(stmt.value, result) if stmt.value
1013
- when IR::IfStmt
1014
- collect_pfor_ir_names_expr(stmt.condition, result)
1015
- stmt.then_body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1016
- stmt.else_body&.each { |s| collect_pfor_ir_names_stmt(s, result) }
1017
- when IR::WhileStmt
1018
- collect_pfor_ir_names_expr(stmt.condition, result)
1019
- stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1020
- when IR::ForStmt
1021
- collect_pfor_ir_names_stmt(stmt.init, result) if stmt.init
1022
- collect_pfor_ir_names_expr(stmt.condition, result) if stmt.condition
1023
- collect_pfor_ir_names_stmt(stmt.post, result) if stmt.post.is_a?(IR::Assignment) || stmt.post.is_a?(IR::ExpressionStmt)
1024
- stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1025
- when IR::BlockStmt
1026
- stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1027
- when IR::SwitchStmt
1028
- collect_pfor_ir_names_expr(stmt.expression, result)
1029
- stmt.cases.each { |c| c.body.each { |s| collect_pfor_ir_names_stmt(s, result) } }
1030
- end
997
+ end
998
+
999
+ def collect_pfor_ir_names_stmt(stmt, result)
1000
+ case stmt
1001
+ when IR::LocalDecl
1002
+ collect_pfor_ir_names_expr(stmt.value, result) if stmt.value
1003
+ when IR::Assignment
1004
+ collect_pfor_ir_names_expr(stmt.target, result)
1005
+ collect_pfor_ir_names_expr(stmt.value, result)
1006
+ when IR::ExpressionStmt
1007
+ collect_pfor_ir_names_expr(stmt.expression, result)
1008
+ when IR::ReturnStmt
1009
+ collect_pfor_ir_names_expr(stmt.value, result) if stmt.value
1010
+ when IR::IfStmt
1011
+ collect_pfor_ir_names_expr(stmt.condition, result)
1012
+ stmt.then_body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1013
+ stmt.else_body&.each { |s| collect_pfor_ir_names_stmt(s, result) }
1014
+ when IR::WhileStmt
1015
+ collect_pfor_ir_names_expr(stmt.condition, result)
1016
+ stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1017
+ when IR::ForStmt
1018
+ collect_pfor_ir_names_stmt(stmt.init, result) if stmt.init
1019
+ collect_pfor_ir_names_expr(stmt.condition, result) if stmt.condition
1020
+ collect_pfor_ir_names_stmt(stmt.post, result) if stmt.post.is_a?(IR::Assignment) || stmt.post.is_a?(IR::ExpressionStmt)
1021
+ stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1022
+ when IR::BlockStmt
1023
+ stmt.body.each { |s| collect_pfor_ir_names_stmt(s, result) }
1024
+ when IR::SwitchStmt
1025
+ collect_pfor_ir_names_expr(stmt.expression, result)
1026
+ stmt.cases.each { |c| c.body.each { |s| collect_pfor_ir_names_stmt(s, result) } }
1031
1027
  end
1032
-
1033
- def collect_pfor_ir_names_expr(expr, result)
1034
- case expr
1035
- when IR::Name
1036
- result[expr.name] = expr unless result.key?(expr.name)
1037
- when IR::Member
1038
- collect_pfor_ir_names_expr(expr.receiver, result)
1039
- when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
1040
- collect_pfor_ir_names_expr(expr.receiver, result)
1041
- collect_pfor_ir_names_expr(expr.index, result)
1042
- when IR::Call
1043
- collect_pfor_ir_names_expr(expr.callee, result)
1044
- expr.arguments.each { |a| collect_pfor_ir_names_expr(a, result) }
1045
- when IR::Binary
1046
- collect_pfor_ir_names_expr(expr.left, result)
1047
- collect_pfor_ir_names_expr(expr.right, result)
1048
- when IR::Unary
1049
- collect_pfor_ir_names_expr(expr.operand, result)
1050
- when IR::Cast
1051
- collect_pfor_ir_names_expr(expr.expression, result)
1052
- when IR::AddressOf
1053
- collect_pfor_ir_names_expr(expr.expression, result)
1054
- when IR::Conditional
1055
- collect_pfor_ir_names_expr(expr.condition, result)
1056
- collect_pfor_ir_names_expr(expr.then_expression, result)
1057
- collect_pfor_ir_names_expr(expr.else_expression, result)
1058
- when IR::AggregateLiteral
1059
- expr.fields.each { |f| collect_pfor_ir_names_expr(f.value, result) }
1060
- when IR::ArrayLiteral
1061
- expr.elements.each { |e| collect_pfor_ir_names_expr(e, result) }
1062
- when IR::ReinterpretExpr
1063
- collect_pfor_ir_names_expr(expr.expression, result)
1064
- end
1028
+ end
1029
+
1030
+ def collect_pfor_ir_names_expr(expr, result)
1031
+ case expr
1032
+ when IR::Name
1033
+ result[expr.name] = expr unless result.key?(expr.name)
1034
+ when IR::Member
1035
+ collect_pfor_ir_names_expr(expr.receiver, result)
1036
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
1037
+ collect_pfor_ir_names_expr(expr.receiver, result)
1038
+ collect_pfor_ir_names_expr(expr.index, result)
1039
+ when IR::Call
1040
+ collect_pfor_ir_names_expr(expr.callee, result)
1041
+ expr.arguments.each { |a| collect_pfor_ir_names_expr(a, result) }
1042
+ when IR::Binary
1043
+ collect_pfor_ir_names_expr(expr.left, result)
1044
+ collect_pfor_ir_names_expr(expr.right, result)
1045
+ when IR::Unary
1046
+ collect_pfor_ir_names_expr(expr.operand, result)
1047
+ when IR::Cast
1048
+ collect_pfor_ir_names_expr(expr.expression, result)
1049
+ when IR::AddressOf
1050
+ collect_pfor_ir_names_expr(expr.expression, result)
1051
+ when IR::Conditional
1052
+ collect_pfor_ir_names_expr(expr.condition, result)
1053
+ collect_pfor_ir_names_expr(expr.then_expression, result)
1054
+ collect_pfor_ir_names_expr(expr.else_expression, result)
1055
+ when IR::AggregateLiteral
1056
+ expr.fields.each { |f| collect_pfor_ir_names_expr(f.value, result) }
1057
+ when IR::ArrayLiteral
1058
+ expr.elements.each { |e| collect_pfor_ir_names_expr(e, result) }
1059
+ when IR::ReinterpretExpr
1060
+ collect_pfor_ir_names_expr(expr.expression, result)
1065
1061
  end
1066
-
1067
- def collect_pfor_local_decls(stmt, decls)
1068
- case stmt
1069
- when IR::LocalDecl
1070
- decls << stmt.linkage_name
1071
- when IR::ForStmt
1072
- decls << stmt.init.linkage_name if stmt.init.is_a?(IR::LocalDecl)
1073
- stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1074
- when IR::BlockStmt
1075
- stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1076
- when IR::IfStmt
1077
- stmt.then_body.each { |s| collect_pfor_local_decls(s, decls) }
1078
- stmt.else_body&.each { |s| collect_pfor_local_decls(s, decls) }
1079
- when IR::WhileStmt
1080
- stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1081
- when IR::SwitchStmt
1082
- stmt.cases.each { |c| c.body.each { |s| collect_pfor_local_decls(s, decls) } }
1083
- end
1062
+ end
1063
+
1064
+ def collect_pfor_local_decls(stmt, decls)
1065
+ case stmt
1066
+ when IR::LocalDecl
1067
+ decls << stmt.linkage_name
1068
+ when IR::ForStmt
1069
+ decls << stmt.init.linkage_name if stmt.init.is_a?(IR::LocalDecl)
1070
+ stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1071
+ when IR::BlockStmt
1072
+ stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1073
+ when IR::IfStmt
1074
+ stmt.then_body.each { |s| collect_pfor_local_decls(s, decls) }
1075
+ stmt.else_body&.each { |s| collect_pfor_local_decls(s, decls) }
1076
+ when IR::WhileStmt
1077
+ stmt.body.each { |s| collect_pfor_local_decls(s, decls) }
1078
+ when IR::SwitchStmt
1079
+ stmt.cases.each { |c| c.body.each { |s| collect_pfor_local_decls(s, decls) } }
1080
+ end
1081
+ end
1082
+
1083
+ def iterator_loop_info(type, env:)
1084
+ iter_name = "__mt_for_iterable__"
1085
+ iterator_name = "__mt_for_iterator__"
1086
+ probe_env = duplicate_env(env)
1087
+ current_actual_scope(probe_env[:scopes])[iter_name] = local_binding(type:, linkage_name: iter_name, mutable: false, pointer: false)
1088
+
1089
+ iter_call = AST::Call.new(
1090
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iter_name), member: "iter"),
1091
+ arguments: [],
1092
+ )
1093
+ iterator_type = infer_expression_type(iter_call, env: probe_env)
1094
+
1095
+ current_actual_scope(probe_env[:scopes])[iterator_name] = local_binding(type: iterator_type, linkage_name: iterator_name, mutable: true, pointer: false)
1096
+ next_call = AST::Call.new(
1097
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
1098
+ arguments: [],
1099
+ )
1100
+ item_storage_type = infer_expression_type(next_call, env: probe_env)
1101
+ if item_storage_type.is_a?(Types::Nullable) && nullable_iterator_item_type?(item_storage_type.base)
1102
+ return {
1103
+ kind: :nullable_item,
1104
+ iterator_type:,
1105
+ item_storage_type:,
1106
+ item_type: item_storage_type.base,
1107
+ }
1084
1108
  end
1085
1109
 
1086
- def iterator_loop_info(type, env:)
1087
- iter_name = "__mt_for_iterable__"
1088
- iterator_name = "__mt_for_iterator__"
1089
- probe_env = duplicate_env(env)
1090
- current_actual_scope(probe_env[:scopes])[iter_name] = local_binding(type:, linkage_name: iter_name, mutable: false, pointer: false)
1091
-
1092
- iter_call = AST::Call.new(
1093
- callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iter_name), member: "iter"),
1094
- arguments: [],
1095
- )
1096
- iterator_type = infer_expression_type(iter_call, env: probe_env)
1097
-
1098
- current_actual_scope(probe_env[:scopes])[iterator_name] = local_binding(type: iterator_type, linkage_name: iterator_name, mutable: true, pointer: false)
1099
- next_call = AST::Call.new(
1100
- callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
1110
+ if item_storage_type == @ctx.types.fetch("bool")
1111
+ current_call = AST::Call.new(
1112
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
1101
1113
  arguments: [],
1102
1114
  )
1103
- item_storage_type = infer_expression_type(next_call, env: probe_env)
1104
- if item_storage_type.is_a?(Types::Nullable) && nullable_iterator_item_type?(item_storage_type.base)
1105
- return {
1106
- kind: :nullable_item,
1107
- iterator_type:,
1108
- item_storage_type:,
1109
- item_type: item_storage_type.base,
1110
- }
1111
- end
1112
-
1113
- if item_storage_type == @ctx.types.fetch("bool")
1114
- current_call = AST::Call.new(
1115
- callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
1116
- arguments: [],
1117
- )
1118
- current_type = infer_expression_type(current_call, env: probe_env)
1119
- return {
1120
- kind: :current_item,
1121
- iterator_type:,
1122
- item_storage_type: current_type,
1123
- item_type: current_type,
1124
- }
1125
- end
1126
-
1127
- nil
1128
- rescue LoweringError
1129
- nil
1115
+ current_type = infer_expression_type(current_call, env: probe_env)
1116
+ return {
1117
+ kind: :current_item,
1118
+ iterator_type:,
1119
+ item_storage_type: current_type,
1120
+ item_type: current_type,
1121
+ }
1130
1122
  end
1131
1123
 
1132
- def nullable_iterator_item_type?(type)
1133
- type == @ctx.types.fetch("cstr") || pointer_type?(type)
1134
- end
1124
+ nil
1125
+ rescue LoweringError
1126
+ nil
1127
+ end
1128
+
1129
+ def nullable_iterator_item_type?(type)
1130
+ type == @ctx.types.fetch("cstr") || pointer_type?(type)
1131
+ end
1135
1132
  end
1136
1133
  end