mt-lang 0.3.24 → 0.3.26

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