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