mt-lang 0.3.8 → 0.3.10

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