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,715 +2,712 @@
2
2
 
3
3
  module MilkTea
4
4
  module LowererAsync
5
- private
6
-
7
-
8
- def build_async_main_entrypoint(binding, _constructor_c_name, async_info)
9
- task_type = async_info[:task_type]
10
- signature = root_main_entrypoint_signature(binding)
11
- raise LoweringError.new("async main entrypoint requires a supported signature",
12
- line: 0, column: 0, path: @ctx.current_analysis_path) unless signature
13
-
14
- params, setup_statements, call_arguments, cleanup_statements = build_root_main_entrypoint_bridge(signature)
15
- body = []
16
- env = empty_env
17
-
18
- root_proc_name = "__mt_async_main_root"
19
- result_name = "__mt_result"
20
-
21
- body.concat(setup_statements)
22
- argument_names = binding.type.params.each_index.map { |index| "__mt_async_main_arg_#{index + 1}" }
23
- binding.type.params.each_with_index do |param, index|
24
- name = argument_names.fetch(index)
25
- env[:scopes].last[name] = local_binding(type: param.type, linkage_name: name, mutable: false, pointer: false)
26
- body << IR::LocalDecl.new(
27
- name: name,
28
- linkage_name: name,
29
- type: param.type,
30
- value: call_arguments.fetch(index),
31
- )
32
- end
33
-
34
- proc_expression = AST::ProcExpr.new(
35
- params: [],
36
- return_type: ast_type_ref_for(task_type),
37
- body: [
38
- AST::ReturnStmt.new(
39
- value: AST::Call.new(
40
- callee: AST::Identifier.new(name: binding.name),
41
- arguments: argument_names.map { |name| AST::Argument.new(name: nil, value: AST::Identifier.new(name: name)) },
42
- ),
43
- ),
44
- ],
45
- )
46
- root_proc_type = Types::Registry.proc(params: [], return_type: task_type)
47
- proc_setup, proc_value = lower_proc_expression_for_local(proc_expression, env:, local_name: root_proc_name, proc_type: root_proc_type)
48
- body.concat(proc_setup)
5
+ def build_async_main_entrypoint(binding, _constructor_c_name, async_info)
6
+ task_type = async_info[:task_type]
7
+ signature = root_main_entrypoint_signature(binding)
8
+ raise LoweringError.new("async main entrypoint requires a supported signature",
9
+ line: 0, column: 0, path: @ctx.current_analysis_path) unless signature
10
+
11
+ params, setup_statements, call_arguments, cleanup_statements = build_root_main_entrypoint_bridge(signature)
12
+ body = []
13
+ env = empty_env
14
+
15
+ root_proc_name = "__mt_async_main_root"
16
+ result_name = "__mt_result"
17
+
18
+ body.concat(setup_statements)
19
+ argument_names = binding.type.params.each_index.map { |index| "__mt_async_main_arg_#{index + 1}" }
20
+ binding.type.params.each_with_index do |param, index|
21
+ name = argument_names.fetch(index)
22
+ env[:scopes].last[name] = local_binding(type: param.type, linkage_name: name, mutable: false, pointer: false)
49
23
  body << IR::LocalDecl.new(
50
- name: root_proc_name,
51
- linkage_name: root_proc_name,
52
- type: root_proc_type,
53
- value: proc_value,
54
- )
55
-
56
- root_proc_expr = IR::Name.new(name: root_proc_name, type: root_proc_type, pointer: false)
57
-
58
- if async_info[:result_type] == @ctx.types.fetch("int")
59
- wait_callee = async_main_runtime_callee_name("wait", type_arguments: [async_info[:result_type]])
60
- body << IR::LocalDecl.new(
61
- name: result_name,
62
- linkage_name: result_name,
63
- type: @ctx.types.fetch("int"),
64
- value: IR::Call.new(
65
- callee: wait_callee,
66
- arguments: [root_proc_expr],
67
- type: @ctx.types.fetch("int"),
68
- ),
69
- )
70
- else
71
- run_callee = async_main_runtime_callee_name("run")
72
- body << IR::ExpressionStmt.new(
73
- expression: IR::Call.new(
74
- callee: run_callee,
75
- arguments: [root_proc_expr],
76
- type: @ctx.types.fetch("void"),
77
- ),
78
- )
79
- end
80
-
81
- body << IR::ExpressionStmt.new(
82
- expression: lower_proc_release_expression(root_proc_expr, root_proc_type),
83
- )
84
- body.concat(cleanup_statements)
85
- body << IR::ReturnStmt.new(
86
- value: async_info[:result_type] == @ctx.types.fetch("int") ? IR::Name.new(name: result_name, type: @ctx.types.fetch("int"), pointer: false) : IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("int")),
24
+ name: name,
25
+ linkage_name: name,
26
+ type: param.type,
27
+ value: call_arguments.fetch(index),
87
28
  )
88
-
89
- IR::Function.new(
90
- name: binding.name,
91
- linkage_name: "main",
92
- params:,
93
- return_type: @ctx.types.fetch("int"),
94
- body: body,
95
- entry_point: true,
96
- )
97
- end
98
-
99
- def async_main_runtime_callee_name(function_name, type_arguments: [])
100
- binding = analysis_for_module("std.async").functions.fetch(function_name)
101
- binding = binding.owner.send(:instantiate_function_binding, binding, type_arguments) if type_arguments.any?
102
- function_binding_c_name(binding, module_name: binding.owner.module_name)
103
29
  end
104
30
 
105
- def build_root_main_entrypoint(binding)
106
- return nil if binding.async
107
-
108
- signature = root_main_entrypoint_signature(binding)
109
- return nil unless signature
110
-
111
- params, setup_statements, call_arguments, cleanup_statements = build_root_main_entrypoint_bridge(signature)
112
- return_type = binding.body_return_type
113
- body = []
114
- call = IR::Call.new(
115
- callee: function_binding_c_name(binding, module_name: @ctx.module_name),
116
- arguments: call_arguments,
117
- type: return_type,
118
- )
119
-
120
- body.concat(setup_statements)
121
- if return_type == @ctx.types.fetch("void")
122
- body << IR::ExpressionStmt.new(expression: call)
123
- body.concat(cleanup_statements)
124
- body << IR::ReturnStmt.new(value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("int")))
125
- elsif cleanup_statements.empty?
126
- body << IR::ReturnStmt.new(value: call)
127
- else
128
- result_name = "__mt_result"
129
- body << IR::LocalDecl.new(
130
- name: result_name,
131
- linkage_name: result_name,
132
- type: @ctx.types.fetch("int"),
133
- value: call,
134
- )
135
- body.concat(cleanup_statements)
136
- body << IR::ReturnStmt.new(value: IR::Name.new(name: result_name, type: @ctx.types.fetch("int"), pointer: false))
137
- end
138
-
139
- IR::Function.new(
140
- name: binding.name,
141
- linkage_name: "main",
142
- params:,
143
- return_type: @ctx.types.fetch("int"),
144
- body:,
145
- entry_point: true,
146
- )
147
- end
148
-
149
- def build_root_main_entrypoint_bridge(signature)
150
- argc_type = @ctx.types.fetch("int")
151
- raw_argv_type = pointer_to(pointer_to(@ctx.types.fetch("char")))
152
- argc_name = "argc"
153
- argv_name = "argv"
154
-
155
- case signature[:kind]
156
- when :none
157
- [[], [], [], []]
158
- when :raw_char_ptr_ptr
159
- argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
160
- argv_expr = IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false)
161
- [
162
- [
163
- IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
164
- IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
165
- ],
166
- [],
167
- [argc_expr, argv_expr],
168
- [],
169
- ]
170
- when :raw_cstr_ptr
171
- argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
172
- argv_expr = IR::Cast.new(
173
- target_type: signature[:argv_type],
174
- expression: IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false),
175
- type: signature[:argv_type],
176
- )
177
- [
178
- [
179
- IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
180
- IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
181
- ],
182
- [],
183
- [argc_expr, argv_expr],
184
- [],
185
- ]
186
- when :span_str
187
- items_type = pointer_to(@ctx.types.fetch("str"))
188
- items_name = "__mt_args_items"
189
- args_name = "__mt_args"
190
- items_expr = IR::Name.new(name: items_name, type: items_type, pointer: false)
191
- args_expr = IR::Name.new(name: args_name, type: signature[:args_type], pointer: false)
192
- argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
193
- argv_expr = IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false)
194
-
195
- setup = [
196
- IR::LocalDecl.new(
197
- name: items_name,
198
- linkage_name: items_name,
199
- type: items_type,
200
- value: IR::NullLiteral.new(type: items_type),
201
- ),
202
- IR::LocalDecl.new(
203
- name: args_name,
204
- linkage_name: args_name,
205
- type: signature[:args_type],
206
- value: IR::Call.new(
207
- callee: "mt_entry_argv_to_span_str",
208
- arguments: [
209
- argc_expr,
210
- argv_expr,
211
- IR::AddressOf.new(expression: items_expr, type: pointer_to(items_type)),
212
- ],
213
- type: signature[:args_type],
214
- ),
31
+ proc_expression = AST::ProcExpr.new(
32
+ params: [],
33
+ return_type: ast_type_ref_for(task_type),
34
+ body: [
35
+ AST::ReturnStmt.new(
36
+ value: AST::Call.new(
37
+ callee: AST::Identifier.new(name: binding.name),
38
+ arguments: argument_names.map { |name| AST::Argument.new(name: nil, value: AST::Identifier.new(name: name)) },
215
39
  ),
216
- ]
217
- cleanup = [
218
- IR::ExpressionStmt.new(
219
- expression: IR::Call.new(callee: "mt_free_entry_argv_strs", arguments: [items_expr], type: @ctx.types.fetch("void")),
220
- ),
221
- ]
222
-
223
- [
224
- [
225
- IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
226
- IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
227
- ],
228
- setup,
229
- [args_expr],
230
- cleanup,
231
- ]
232
- else
233
- raise LoweringError.new("unsupported root main entrypoint bridge #{signature[:kind]}",
234
- line: 0, column: 0, path: @ctx.current_analysis_path)
235
- end
236
- end
237
-
238
- def root_main_entrypoint_signature(binding)
239
- return nil unless @ctx.analysis == @program.root_analysis
240
- return nil unless binding.type.receiver_type.nil?
241
- return nil unless binding.name == "main"
242
- return nil unless binding.type_arguments.empty?
243
-
244
- return_type = binding.body_return_type
245
- return nil unless return_type == @ctx.types.fetch("int") || return_type == @ctx.types.fetch("void")
246
-
247
- params = binding.type.params
248
- return { kind: :none } if params.empty?
249
-
250
- if params.length == 1 && params.first.type.is_a?(Types::Span) && params.first.type.element_type == @ctx.types.fetch("str")
251
- return { kind: :span_str, args_type: params.first.type }
252
- end
253
-
254
- return nil unless params.length == 2
255
- return nil unless params[0].type == @ctx.types.fetch("int")
256
-
257
- argv_type = params[1].type
258
- return { kind: :raw_cstr_ptr, argv_type: } if argv_type == pointer_to(@ctx.types.fetch("cstr"))
259
- return { kind: :raw_char_ptr_ptr, argv_type: } if argv_type == pointer_to(pointer_to(@ctx.types.fetch("char")))
260
-
261
- nil
262
- end
263
-
264
-
265
- def build_async_constructor_function(binding, decl, frame_type, constructor_linkage_name, resume_linkage_name, ready_linkage_name, set_waiter_linkage_name, release_linkage_name, take_result_linkage_name, cancel_linkage_name, async_info)
266
- params = []
267
- body = []
268
- frame_pointer_type = pointer_to(frame_type)
269
- frame_expr = IR::Name.new(name: async_frame_local_name, type: frame_pointer_type, pointer: false)
270
- raw_frame_expr = IR::Cast.new(target_type: async_info[:void_ptr], expression: frame_expr, type: async_info[:void_ptr])
271
-
40
+ ),
41
+ ],
42
+ )
43
+ root_proc_type = Types::Registry.proc(params: [], return_type: task_type)
44
+ proc_setup, proc_value = lower_proc_expression_for_local(proc_expression, env:, local_name: root_proc_name, proc_type: root_proc_type)
45
+ body.concat(proc_setup)
46
+ body << IR::LocalDecl.new(
47
+ name: root_proc_name,
48
+ linkage_name: root_proc_name,
49
+ type: root_proc_type,
50
+ value: proc_value,
51
+ )
52
+
53
+ root_proc_expr = IR::Name.new(name: root_proc_name, type: root_proc_type, pointer: false)
54
+
55
+ if async_info[:result_type] == @ctx.types.fetch("int")
56
+ wait_callee = async_main_runtime_callee_name("wait", type_arguments: [async_info[:result_type]])
272
57
  body << IR::LocalDecl.new(
273
- name: async_frame_local_name,
274
- linkage_name: async_frame_local_name,
275
- type: frame_pointer_type,
276
- value: IR::Cast.new(
277
- target_type: frame_pointer_type,
278
- expression: IR::Call.new(
279
- callee: "mt_async_alloc",
280
- arguments: [IR::SizeofExpr.new(target_type: frame_type, type: @ctx.types.fetch("ptr_uint"))],
281
- type: async_info[:void_ptr],
282
- ),
283
- type: frame_pointer_type,
58
+ name: result_name,
59
+ linkage_name: result_name,
60
+ type: @ctx.types.fetch("int"),
61
+ value: IR::Call.new(
62
+ callee: wait_callee,
63
+ arguments: [root_proc_expr],
64
+ type: @ctx.types.fetch("int"),
284
65
  ),
285
66
  )
286
-
287
- binding.body_params.each do |param_binding|
288
- field_info = async_info[:param_fields].fetch(param_binding.name)
289
- field_type = field_info[:type]
290
- param_type = field_info[:param_type]
291
- linkage_name = c_local_name(param_binding.name)
292
- input_linkage_name = array_type?(param_type) && !field_info[:pointer] ? "#{linkage_name}_input" : linkage_name
293
- params << IR::Param.new(name: param_binding.name, linkage_name: input_linkage_name, type: param_type, pointer: field_info[:pointer])
294
- frame_field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_type)
295
- body << IR::Assignment.new(
296
- target: frame_field_expr,
297
- operator: "=",
298
- value: IR::Name.new(name: input_linkage_name, type: param_type, pointer: field_info[:pointer]),
299
- )
300
- # Retain proc-containing params: the frame outlives the constructor call stack,
301
- # so we must increment the env refcount so the caller releasing their copy is safe.
302
- if !field_info[:pointer] && contains_proc_storage_type?(param_type)
303
- body.concat(lower_proc_contained_retain_statements(frame_field_expr, param_type))
304
- end
305
- end
306
-
67
+ else
68
+ run_callee = async_main_runtime_callee_name("run")
307
69
  body << IR::ExpressionStmt.new(
308
- expression: IR::Call.new(callee: resume_linkage_name, arguments: [raw_frame_expr], type: @ctx.types.fetch("void")),
309
- )
310
- body << IR::ReturnStmt.new(
311
- value: IR::AggregateLiteral.new(
312
- type: async_info[:task_type],
313
- fields: [
314
- IR::AggregateField.new(name: "frame", value: raw_frame_expr),
315
- IR::AggregateField.new(name: "ready", value: IR::Name.new(name: ready_linkage_name, type: async_info[:task_type].field("ready"), pointer: false)),
316
- IR::AggregateField.new(name: "set_waiter", value: IR::Name.new(name: set_waiter_linkage_name, type: async_info[:task_type].field("set_waiter"), pointer: false)),
317
- IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: async_info[:task_type].field("release"), pointer: false)),
318
- IR::AggregateField.new(name: "take_result", value: IR::Name.new(name: take_result_linkage_name, type: async_info[:task_type].field("take_result"), pointer: false)),
319
- IR::AggregateField.new(name: "cancel", value: IR::Name.new(name: cancel_linkage_name, type: async_info[:task_type].field("cancel"), pointer: false)),
320
- ],
70
+ expression: IR::Call.new(
71
+ callee: run_callee,
72
+ arguments: [root_proc_expr],
73
+ type: @ctx.types.fetch("void"),
321
74
  ),
322
75
  )
323
-
324
- IR::Function.new(
325
- name: decl.name,
326
- linkage_name: constructor_linkage_name,
327
- params:,
328
- return_type: async_info[:task_type],
329
- body:,
330
- entry_point: false,
331
- method_receiver_param: !binding.type.receiver_type.nil?,
332
- )
333
76
  end
334
77
 
335
- def build_async_resume_function(binding, statements, frame_type, resume_linkage_name, async_info)
336
- async_info = async_info.merge(resume_linkage_name:)
337
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
338
- raw_frame_expr = IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)
339
- body = [async_frame_cast_declaration(frame_type, async_info)]
340
-
341
- env = async_resume_env_for(async_info)
342
- if async_info[:await_fields].empty?
343
- body.concat(lower_async_non_await_statements(statements, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: []))
344
- else
345
- cases = (0..async_info[:await_fields].length).map do |state|
346
- IR::SwitchCase.new(
347
- value: IR::IntegerLiteral.new(value: state, type: @ctx.types.fetch("int")),
348
- body: [IR::GotoStmt.new(label: async_state_label(resume_linkage_name, state))],
349
- )
350
- end
351
- body << IR::SwitchStmt.new(expression: async_frame_field_expression(frame_expr, "state", @ctx.types.fetch("int")), cases:)
352
- body << IR::ReturnStmt.new(value: nil)
353
- body << IR::LabelStmt.new(name: async_state_label(resume_linkage_name, 0))
354
- body.concat(lower_async_cf_statements(statements, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: []))
355
- end
356
-
357
- if async_info[:result_type] == @ctx.types.fetch("void") && !cfg_block_always_terminates?(statements)
358
- body.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value: nil, result_already_stored: true))
359
- end
360
-
361
- IR::Function.new(
362
- name: "#{binding.name}__resume",
363
- linkage_name: resume_linkage_name,
364
- params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
365
- return_type: @ctx.types.fetch("void"),
366
- body:,
367
- entry_point: false,
78
+ body << IR::ExpressionStmt.new(
79
+ expression: lower_proc_release_expression(root_proc_expr, root_proc_type),
80
+ )
81
+ body.concat(cleanup_statements)
82
+ body << IR::ReturnStmt.new(
83
+ value: async_info[:result_type] == @ctx.types.fetch("int") ? IR::Name.new(name: result_name, type: @ctx.types.fetch("int"), pointer: false) : IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("int")),
84
+ )
85
+
86
+ IR::Function.new(
87
+ name: binding.name,
88
+ linkage_name: "main",
89
+ params:,
90
+ return_type: @ctx.types.fetch("int"),
91
+ body: body,
92
+ entry_point: true,
93
+ )
94
+ end
95
+
96
+ def async_main_runtime_callee_name(function_name, type_arguments: [])
97
+ binding = analysis_for_module("std.async").functions.fetch(function_name)
98
+ binding = binding.owner.instantiate_function_binding(binding, type_arguments) if type_arguments.any?
99
+ function_binding_c_name(binding, module_name: binding.owner.module_name)
100
+ end
101
+
102
+ def build_root_main_entrypoint(binding)
103
+ return nil if binding.async
104
+
105
+ signature = root_main_entrypoint_signature(binding)
106
+ return nil unless signature
107
+
108
+ params, setup_statements, call_arguments, cleanup_statements = build_root_main_entrypoint_bridge(signature)
109
+ return_type = binding.body_return_type
110
+ body = []
111
+ call = IR::Call.new(
112
+ callee: function_binding_c_name(binding, module_name: @ctx.module_name),
113
+ arguments: call_arguments,
114
+ type: return_type,
115
+ )
116
+
117
+ body.concat(setup_statements)
118
+ if return_type == @ctx.types.fetch("void")
119
+ body << IR::ExpressionStmt.new(expression: call)
120
+ body.concat(cleanup_statements)
121
+ body << IR::ReturnStmt.new(value: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("int")))
122
+ elsif cleanup_statements.empty?
123
+ body << IR::ReturnStmt.new(value: call)
124
+ else
125
+ result_name = "__mt_result"
126
+ body << IR::LocalDecl.new(
127
+ name: result_name,
128
+ linkage_name: result_name,
129
+ type: @ctx.types.fetch("int"),
130
+ value: call,
368
131
  )
132
+ body.concat(cleanup_statements)
133
+ body << IR::ReturnStmt.new(value: IR::Name.new(name: result_name, type: @ctx.types.fetch("int"), pointer: false))
369
134
  end
370
135
 
371
- def build_async_ready_function(frame_type, ready_linkage_name, async_info)
372
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
373
-
374
- IR::Function.new(
375
- name: "#{ready_linkage_name}_fn",
376
- linkage_name: ready_linkage_name,
377
- params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
378
- return_type: @ctx.types.fetch("bool"),
379
- body: [
380
- async_frame_cast_declaration(frame_type, async_info),
381
- IR::ReturnStmt.new(value: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool"))),
136
+ IR::Function.new(
137
+ name: binding.name,
138
+ linkage_name: "main",
139
+ params:,
140
+ return_type: @ctx.types.fetch("int"),
141
+ body:,
142
+ entry_point: true,
143
+ )
144
+ end
145
+
146
+ def build_root_main_entrypoint_bridge(signature)
147
+ argc_type = @ctx.types.fetch("int")
148
+ raw_argv_type = pointer_to(pointer_to(@ctx.types.fetch("char")))
149
+ argc_name = "argc"
150
+ argv_name = "argv"
151
+
152
+ case signature[:kind]
153
+ when :none
154
+ [[], [], [], []]
155
+ when :raw_char_ptr_ptr
156
+ argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
157
+ argv_expr = IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false)
158
+ [
159
+ [
160
+ IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
161
+ IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
382
162
  ],
383
- entry_point: false,
163
+ [],
164
+ [argc_expr, argv_expr],
165
+ [],
166
+ ]
167
+ when :raw_cstr_ptr
168
+ argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
169
+ argv_expr = IR::Cast.new(
170
+ target_type: signature[:argv_type],
171
+ expression: IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false),
172
+ type: signature[:argv_type],
384
173
  )
385
- end
386
-
387
- def build_async_set_waiter_function(frame_type, set_waiter_linkage_name, async_info)
388
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
389
- waiter_frame_expr = IR::Name.new(name: "waiter_frame", type: async_info[:void_ptr], pointer: false)
390
- waiter_expr = IR::Name.new(name: "waiter", type: async_info[:wake_type], pointer: false)
391
-
392
- IR::Function.new(
393
- name: "#{set_waiter_linkage_name}_fn",
394
- linkage_name: set_waiter_linkage_name,
395
- params: [
396
- IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false),
397
- IR::Param.new(name: "waiter_frame", linkage_name: "waiter_frame", type: async_info[:void_ptr], pointer: false),
398
- IR::Param.new(name: "waiter", linkage_name: "waiter", type: async_info[:wake_type], pointer: false),
174
+ [
175
+ [
176
+ IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
177
+ IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
399
178
  ],
400
- return_type: @ctx.types.fetch("void"),
401
- body: [
402
- async_frame_cast_declaration(frame_type, async_info),
403
- IR::IfStmt.new(
404
- condition: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")),
405
- then_body: [
406
- IR::ExpressionStmt.new(expression: IR::Call.new(callee: waiter_expr, arguments: [waiter_frame_expr], type: @ctx.types.fetch("void"))),
407
- IR::ReturnStmt.new(value: nil),
179
+ [],
180
+ [argc_expr, argv_expr],
181
+ [],
182
+ ]
183
+ when :span_str
184
+ items_type = pointer_to(@ctx.types.fetch("str"))
185
+ items_name = "__mt_args_items"
186
+ args_name = "__mt_args"
187
+ items_expr = IR::Name.new(name: items_name, type: items_type, pointer: false)
188
+ args_expr = IR::Name.new(name: args_name, type: signature[:args_type], pointer: false)
189
+ argc_expr = IR::Name.new(name: argc_name, type: argc_type, pointer: false)
190
+ argv_expr = IR::Name.new(name: argv_name, type: raw_argv_type, pointer: false)
191
+
192
+ setup = [
193
+ IR::LocalDecl.new(
194
+ name: items_name,
195
+ linkage_name: items_name,
196
+ type: items_type,
197
+ value: IR::NullLiteral.new(type: items_type),
198
+ ),
199
+ IR::LocalDecl.new(
200
+ name: args_name,
201
+ linkage_name: args_name,
202
+ type: signature[:args_type],
203
+ value: IR::Call.new(
204
+ callee: "mt_entry_argv_to_span_str",
205
+ arguments: [
206
+ argc_expr,
207
+ argv_expr,
208
+ IR::AddressOf.new(expression: items_expr, type: pointer_to(items_type)),
408
209
  ],
409
- else_body: nil,
210
+ type: signature[:args_type],
410
211
  ),
411
- IR::Assignment.new(target: async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr]), operator: "=", value: waiter_frame_expr),
412
- IR::Assignment.new(target: async_frame_field_expression(frame_expr, "waiter", async_info[:wake_type]), operator: "=", value: waiter_expr),
413
- IR::ReturnStmt.new(value: nil),
212
+ ),
213
+ ]
214
+ cleanup = [
215
+ IR::ExpressionStmt.new(
216
+ expression: IR::Call.new(callee: "mt_free_entry_argv_strs", arguments: [items_expr], type: @ctx.types.fetch("void")),
217
+ ),
218
+ ]
219
+
220
+ [
221
+ [
222
+ IR::Param.new(name: argc_name, linkage_name: argc_name, type: argc_type, pointer: false),
223
+ IR::Param.new(name: argv_name, linkage_name: argv_name, type: raw_argv_type, pointer: false),
414
224
  ],
415
- entry_point: false,
416
- )
225
+ setup,
226
+ [args_expr],
227
+ cleanup,
228
+ ]
229
+ else
230
+ raise LoweringError.new("unsupported root main entrypoint bridge #{signature[:kind]}",
231
+ line: 0, column: 0, path: @ctx.current_analysis_path)
417
232
  end
233
+ end
418
234
 
419
- def build_async_release_function(frame_type, release_linkage_name, async_info)
420
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
421
- raw_frame_expr = IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)
422
-
423
- body = [async_frame_cast_declaration(frame_type, async_info)]
424
-
425
- not_ready_expr = IR::Unary.new(operator: "not", operand: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")), type: @ctx.types.fetch("bool"))
426
- not_ready_return = [IR::ReturnStmt.new(value: nil)]
427
-
428
- if async_info[:await_fields].any?
429
- await_release_stmts = []
430
- async_info[:await_fields].each_value do |field_info|
431
- task_field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:task_type])
432
- task_frame_expr = async_task_frame_expression(task_field_expr, field_info[:task_type])
433
- release_call = IR::ExpressionStmt.new(
434
- expression: async_task_call(task_field_expr, field_info[:task_type], "release", [task_frame_expr], @ctx.types.fetch("void")),
435
- )
436
- await_release_stmts << IR::IfStmt.new(
437
- condition: task_frame_expr,
438
- then_body: [release_call],
439
- else_body: nil,
440
- )
441
- end
442
- body << IR::IfStmt.new(
443
- condition: not_ready_expr,
444
- then_body: await_release_stmts + not_ready_return,
445
- else_body: nil,
446
- )
447
- else
448
- body << IR::IfStmt.new(
449
- condition: not_ready_expr,
450
- then_body: not_ready_return,
451
- else_body: nil,
452
- )
453
- end
454
-
455
- # Release proc-containing params (always initialized by constructor, but null-guard is safe).
456
- async_info[:param_fields].each_value do |field_info|
457
- next if field_info[:pointer]
458
- next unless contains_proc_storage_type?(field_info[:type])
235
+ def root_main_entrypoint_signature(binding)
236
+ return nil unless @ctx.analysis == @program.root_analysis
237
+ return nil unless binding.type.receiver_type.nil?
238
+ return nil unless binding.name == "main"
239
+ return nil unless binding.type_arguments.empty?
459
240
 
460
- field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
461
- body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:type]))
462
- end
241
+ return_type = binding.body_return_type
242
+ return nil unless return_type == @ctx.types.fetch("int") || return_type == @ctx.types.fetch("void")
463
243
 
464
- # Release proc-containing locals (may not be initialized if function returned early via branch,
465
- # so always null-guard via invoke pointer check on each proc).
466
- async_info[:local_fields].each_value do |field_info|
467
- next unless contains_proc_storage_type?(field_info[:storage_type])
244
+ params = binding.type.params
245
+ return { kind: :none } if params.empty?
468
246
 
469
- field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
470
- body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:storage_type]))
471
- end
247
+ if params.length == 1 && params.first.type.is_a?(Types::Span) && params.first.type.element_type == @ctx.types.fetch("str")
248
+ return { kind: :span_str, args_type: params.first.type }
249
+ end
472
250
 
473
- (async_info[:format_str_fields] || {}).each_key do |field_name|
474
- field_expr = async_frame_field_expression(frame_expr, field_name, @ctx.types.fetch("str"))
475
- body << IR::ExpressionStmt.new(
476
- expression: IR::Call.new(
477
- callee: "mt_format_str_release",
478
- arguments: [field_expr],
479
- type: @ctx.types.fetch("void"),
480
- ),
481
- )
251
+ return nil unless params.length == 2
252
+ return nil unless params[0].type == @ctx.types.fetch("int")
253
+
254
+ argv_type = params[1].type
255
+ return { kind: :raw_cstr_ptr, argv_type: } if argv_type == pointer_to(@ctx.types.fetch("cstr"))
256
+ return { kind: :raw_char_ptr_ptr, argv_type: } if argv_type == pointer_to(pointer_to(@ctx.types.fetch("char")))
257
+
258
+ nil
259
+ end
260
+
261
+
262
+ def build_async_constructor_function(binding, decl, frame_type, constructor_linkage_name, resume_linkage_name, ready_linkage_name, set_waiter_linkage_name, release_linkage_name, take_result_linkage_name, cancel_linkage_name, async_info)
263
+ params = []
264
+ body = []
265
+ frame_pointer_type = pointer_to(frame_type)
266
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: frame_pointer_type, pointer: false)
267
+ raw_frame_expr = IR::Cast.new(target_type: async_info[:void_ptr], expression: frame_expr, type: async_info[:void_ptr])
268
+
269
+ body << IR::LocalDecl.new(
270
+ name: async_frame_local_name,
271
+ linkage_name: async_frame_local_name,
272
+ type: frame_pointer_type,
273
+ value: IR::Cast.new(
274
+ target_type: frame_pointer_type,
275
+ expression: IR::Call.new(
276
+ callee: "mt_async_alloc",
277
+ arguments: [IR::SizeofExpr.new(target_type: frame_type, type: @ctx.types.fetch("ptr_uint"))],
278
+ type: async_info[:void_ptr],
279
+ ),
280
+ type: frame_pointer_type,
281
+ ),
282
+ )
283
+
284
+ binding.body_params.each do |param_binding|
285
+ field_info = async_info[:param_fields].fetch(param_binding.name)
286
+ field_type = field_info[:type]
287
+ param_type = field_info[:param_type]
288
+ linkage_name = c_local_name(param_binding.name)
289
+ input_linkage_name = array_type?(param_type) && !field_info[:pointer] ? "#{linkage_name}_input" : linkage_name
290
+ params << IR::Param.new(name: param_binding.name, linkage_name: input_linkage_name, type: param_type, pointer: field_info[:pointer])
291
+ frame_field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_type)
292
+ body << IR::Assignment.new(
293
+ target: frame_field_expr,
294
+ operator: "=",
295
+ value: IR::Name.new(name: input_linkage_name, type: param_type, pointer: field_info[:pointer]),
296
+ )
297
+ # Retain proc-containing params: the frame outlives the constructor call stack,
298
+ # so we must increment the env refcount so the caller releasing their copy is safe.
299
+ if !field_info[:pointer] && contains_proc_storage_type?(param_type)
300
+ body.concat(lower_proc_contained_retain_statements(frame_field_expr, param_type))
482
301
  end
302
+ end
483
303
 
484
- async_info[:param_fields].each_value do |field_info|
485
- next unless field_info[:pointer]
486
-
487
- param_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
488
- body << IR::ExpressionStmt.new(
489
- expression: IR::Call.new(callee: "mt_async_free", arguments: [param_expr], type: @ctx.types.fetch("void")),
304
+ body << IR::ExpressionStmt.new(
305
+ expression: IR::Call.new(callee: resume_linkage_name, arguments: [raw_frame_expr], type: @ctx.types.fetch("void")),
306
+ )
307
+ body << IR::ReturnStmt.new(
308
+ value: IR::AggregateLiteral.new(
309
+ type: async_info[:task_type],
310
+ fields: [
311
+ IR::AggregateField.new(name: "frame", value: raw_frame_expr),
312
+ IR::AggregateField.new(name: "ready", value: IR::Name.new(name: ready_linkage_name, type: async_info[:task_type].field("ready"), pointer: false)),
313
+ IR::AggregateField.new(name: "set_waiter", value: IR::Name.new(name: set_waiter_linkage_name, type: async_info[:task_type].field("set_waiter"), pointer: false)),
314
+ IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: async_info[:task_type].field("release"), pointer: false)),
315
+ IR::AggregateField.new(name: "take_result", value: IR::Name.new(name: take_result_linkage_name, type: async_info[:task_type].field("take_result"), pointer: false)),
316
+ IR::AggregateField.new(name: "cancel", value: IR::Name.new(name: cancel_linkage_name, type: async_info[:task_type].field("cancel"), pointer: false)),
317
+ ],
318
+ ),
319
+ )
320
+
321
+ IR::Function.new(
322
+ name: decl.name,
323
+ linkage_name: constructor_linkage_name,
324
+ params:,
325
+ return_type: async_info[:task_type],
326
+ body:,
327
+ entry_point: false,
328
+ method_receiver_param: !binding.type.receiver_type.nil?,
329
+ )
330
+ end
331
+
332
+ def build_async_resume_function(binding, statements, frame_type, resume_linkage_name, async_info)
333
+ async_info = async_info.merge(resume_linkage_name:)
334
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
335
+ raw_frame_expr = IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)
336
+ body = [async_frame_cast_declaration(frame_type, async_info)]
337
+
338
+ env = async_resume_env_for(async_info)
339
+ if async_info[:await_fields].empty?
340
+ body.concat(lower_async_non_await_statements(statements, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: []))
341
+ else
342
+ cases = (0..async_info[:await_fields].length).map do |state|
343
+ IR::SwitchCase.new(
344
+ value: IR::IntegerLiteral.new(value: state, type: @ctx.types.fetch("int")),
345
+ body: [IR::GotoStmt.new(label: async_state_label(resume_linkage_name, state))],
490
346
  )
491
347
  end
492
-
493
- body << IR::ExpressionStmt.new(expression: IR::Call.new(callee: "mt_async_free", arguments: [raw_frame_expr], type: @ctx.types.fetch("void")))
348
+ body << IR::SwitchStmt.new(expression: async_frame_field_expression(frame_expr, "state", @ctx.types.fetch("int")), cases:)
494
349
  body << IR::ReturnStmt.new(value: nil)
350
+ body << IR::LabelStmt.new(name: async_state_label(resume_linkage_name, 0))
351
+ body.concat(lower_async_cf_statements(statements, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers: []))
352
+ end
495
353
 
496
- IR::Function.new(
497
- name: "#{release_linkage_name}_fn",
498
- linkage_name: release_linkage_name,
499
- params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
500
- return_type: @ctx.types.fetch("void"),
501
- body:,
502
- entry_point: false,
503
- )
354
+ if async_info[:result_type] == @ctx.types.fetch("void") && !cfg_block_always_terminates?(statements)
355
+ body.concat(async_complete_statements(frame_expr:, raw_frame_expr:, async_info:, value: nil, result_already_stored: true))
504
356
  end
505
357
 
506
- def build_async_cancel_function(frame_type, cancel_linkage_name, async_info)
507
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
358
+ IR::Function.new(
359
+ name: "#{binding.name}__resume",
360
+ linkage_name: resume_linkage_name,
361
+ params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
362
+ return_type: @ctx.types.fetch("void"),
363
+ body:,
364
+ entry_point: false,
365
+ )
366
+ end
367
+
368
+ def build_async_ready_function(frame_type, ready_linkage_name, async_info)
369
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
370
+
371
+ IR::Function.new(
372
+ name: "#{ready_linkage_name}_fn",
373
+ linkage_name: ready_linkage_name,
374
+ params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
375
+ return_type: @ctx.types.fetch("bool"),
376
+ body: [
377
+ async_frame_cast_declaration(frame_type, async_info),
378
+ IR::ReturnStmt.new(value: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool"))),
379
+ ],
380
+ entry_point: false,
381
+ )
382
+ end
383
+
384
+ def build_async_set_waiter_function(frame_type, set_waiter_linkage_name, async_info)
385
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
386
+ waiter_frame_expr = IR::Name.new(name: "waiter_frame", type: async_info[:void_ptr], pointer: false)
387
+ waiter_expr = IR::Name.new(name: "waiter", type: async_info[:wake_type], pointer: false)
388
+
389
+ IR::Function.new(
390
+ name: "#{set_waiter_linkage_name}_fn",
391
+ linkage_name: set_waiter_linkage_name,
392
+ params: [
393
+ IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false),
394
+ IR::Param.new(name: "waiter_frame", linkage_name: "waiter_frame", type: async_info[:void_ptr], pointer: false),
395
+ IR::Param.new(name: "waiter", linkage_name: "waiter", type: async_info[:wake_type], pointer: false),
396
+ ],
397
+ return_type: @ctx.types.fetch("void"),
398
+ body: [
399
+ async_frame_cast_declaration(frame_type, async_info),
400
+ IR::IfStmt.new(
401
+ condition: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")),
402
+ then_body: [
403
+ IR::ExpressionStmt.new(expression: IR::Call.new(callee: waiter_expr, arguments: [waiter_frame_expr], type: @ctx.types.fetch("void"))),
404
+ IR::ReturnStmt.new(value: nil),
405
+ ],
406
+ else_body: nil,
407
+ ),
408
+ IR::Assignment.new(target: async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr]), operator: "=", value: waiter_frame_expr),
409
+ IR::Assignment.new(target: async_frame_field_expression(frame_expr, "waiter", async_info[:wake_type]), operator: "=", value: waiter_expr),
410
+ IR::ReturnStmt.new(value: nil),
411
+ ],
412
+ entry_point: false,
413
+ )
414
+ end
508
415
 
509
- body = [async_frame_cast_declaration(frame_type, async_info)]
416
+ def build_async_release_function(frame_type, release_linkage_name, async_info)
417
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
418
+ raw_frame_expr = IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)
510
419
 
511
- ready_expr = async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool"))
512
- if_ready_return = IR::IfStmt.new(
513
- condition: ready_expr,
514
- then_body: [IR::ReturnStmt.new(value: nil)],
515
- else_body: nil,
516
- )
517
- body << if_ready_return
420
+ body = [async_frame_cast_declaration(frame_type, async_info)]
518
421
 
519
- cancelled_assign = IR::Assignment.new(
520
- target: async_frame_field_expression(frame_expr, "cancelled", @ctx.types.fetch("bool")),
521
- operator: "=",
522
- value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
523
- )
524
- body << IR::ExpressionStmt.new(expression: cancelled_assign)
422
+ not_ready_expr = IR::Unary.new(operator: "not", operand: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")), type: @ctx.types.fetch("bool"))
423
+ not_ready_return = [IR::ReturnStmt.new(value: nil)]
525
424
 
526
- cancel_stmts = []
425
+ if async_info[:await_fields].any?
426
+ await_release_stmts = []
527
427
  async_info[:await_fields].each_value do |field_info|
528
428
  task_field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:task_type])
529
429
  task_frame_expr = async_task_frame_expression(task_field_expr, field_info[:task_type])
530
- cancel_call = IR::ExpressionStmt.new(
531
- expression: async_task_call(task_field_expr, field_info[:task_type], "cancel", [task_frame_expr], @ctx.types.fetch("void")),
532
- )
533
430
  release_call = IR::ExpressionStmt.new(
534
431
  expression: async_task_call(task_field_expr, field_info[:task_type], "release", [task_frame_expr], @ctx.types.fetch("void")),
535
432
  )
536
- cancel_member_expr = IR::Member.new(receiver: task_field_expr, member: "cancel", type: async_info[:task_type].field("cancel"))
537
- cancel_stmts << IR::IfStmt.new(
433
+ await_release_stmts << IR::IfStmt.new(
538
434
  condition: task_frame_expr,
539
- then_body: [
540
- IR::IfStmt.new(
541
- condition: cancel_member_expr,
542
- then_body: [cancel_call],
543
- else_body: nil,
544
- ),
545
- release_call,
546
- ],
435
+ then_body: [release_call],
547
436
  else_body: nil,
548
437
  )
549
438
  end
550
- body.concat(cancel_stmts) if cancel_stmts.any?
551
-
552
- async_info[:param_fields].each_value do |field_info|
553
- next if field_info[:pointer]
554
- next unless contains_proc_storage_type?(field_info[:type])
555
-
556
- field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
557
- body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:type]))
558
- end
559
-
560
- async_info[:local_fields].each_value do |field_info|
561
- next unless contains_proc_storage_type?(field_info[:storage_type])
562
-
563
- field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
564
- body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:storage_type]))
565
- end
566
-
567
- (async_info[:format_str_fields] || {}).each_key do |field_name|
568
- field_expr = async_frame_field_expression(frame_expr, field_name, @ctx.types.fetch("str"))
569
- body << IR::ExpressionStmt.new(
570
- expression: IR::Call.new(
571
- callee: "mt_format_str_release",
572
- arguments: [field_expr],
573
- type: @ctx.types.fetch("void"),
574
- ),
575
- )
576
- end
577
-
578
- async_info[:param_fields].each_value do |field_info|
579
- next unless field_info[:pointer]
580
-
581
- param_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
582
- body << IR::ExpressionStmt.new(
583
- expression: IR::Call.new(callee: "mt_async_free", arguments: [param_expr], type: @ctx.types.fetch("void")),
584
- )
585
- end
586
-
587
- ready_assign = IR::Assignment.new(
588
- target: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")),
589
- operator: "=",
590
- value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
439
+ body << IR::IfStmt.new(
440
+ condition: not_ready_expr,
441
+ then_body: await_release_stmts + not_ready_return,
442
+ else_body: nil,
591
443
  )
592
- body << IR::ExpressionStmt.new(expression: ready_assign)
593
-
594
- waiter_frame_expr = async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr])
595
- wake_stmts = [
596
- IR::ExpressionStmt.new(
597
- expression: IR::Assignment.new(
598
- target: async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr]),
599
- operator: "=",
600
- value: IR::NullLiteral.new(type: async_info[:void_ptr]),
601
- ),
602
- ),
603
- IR::ExpressionStmt.new(
604
- expression: IR::Call.new(
605
- callee: IR::Name.new(name: async_frame_field_c_name("waiter"), type: async_info[:wake_type], pointer: false),
606
- arguments: [waiter_frame_expr],
607
- type: @ctx.types.fetch("void"),
608
- ),
609
- ),
610
- ]
444
+ else
611
445
  body << IR::IfStmt.new(
612
- condition: waiter_frame_expr,
613
- then_body: wake_stmts,
446
+ condition: not_ready_expr,
447
+ then_body: not_ready_return,
614
448
  else_body: nil,
615
449
  )
450
+ end
616
451
 
617
- body << IR::ReturnStmt.new(value: nil)
452
+ # Release proc-containing params (always initialized by constructor, but null-guard is safe).
453
+ async_info[:param_fields].each_value do |field_info|
454
+ next if field_info[:pointer]
455
+ next unless contains_proc_storage_type?(field_info[:type])
618
456
 
619
- IR::Function.new(
620
- name: "#{cancel_linkage_name}_fn",
621
- linkage_name: cancel_linkage_name,
622
- params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
623
- return_type: @ctx.types.fetch("void"),
624
- body:,
625
- entry_point: false,
626
- )
457
+ field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
458
+ body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:type]))
627
459
  end
628
460
 
629
- def build_async_take_result_function(frame_type, take_result_linkage_name, async_info)
630
- frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
631
- body = if async_info[:result_type] == @ctx.types.fetch("void")
632
- [IR::ReturnStmt.new(value: nil)]
633
- else
634
- [async_frame_cast_declaration(frame_type, async_info),
635
- IR::ReturnStmt.new(value: async_frame_field_expression(frame_expr, "result", async_info[:result_type]))]
636
- end
637
-
638
- IR::Function.new(
639
- name: "#{take_result_linkage_name}_fn",
640
- linkage_name: take_result_linkage_name,
641
- params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
642
- return_type: async_info[:result_type],
643
- body:,
644
- entry_point: false,
645
- )
646
- end
461
+ # Release proc-containing locals (may not be initialized if function returned early via branch,
462
+ # so always null-guard via invoke pointer check on each proc).
463
+ async_info[:local_fields].each_value do |field_info|
464
+ next unless contains_proc_storage_type?(field_info[:storage_type])
647
465
 
648
- def async_resume_env_for(async_info)
649
- env = empty_env
650
- async_info[:param_fields].each do |name, field_info|
651
- env[:scopes].last[name] = local_binding(
652
- type: field_info[:pointer] ? pointee_type(field_info[:type]) : field_info[:type],
653
- linkage_name: async_frame_field_c_name(field_info[:field_name]),
654
- mutable: field_info[:mutable],
655
- pointer: field_info[:pointer],
656
- )
657
- end
658
- env
466
+ field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
467
+ body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:storage_type]))
659
468
  end
660
469
 
661
- def async_bind_local!(env, name, field_info)
662
- current_actual_scope(env[:scopes])[name] = local_binding(
663
- type: field_info[:type],
664
- storage_type: field_info[:storage_type],
665
- linkage_name: async_frame_field_c_name(field_info[:field_name]),
666
- mutable: field_info[:mutable],
667
- pointer: false,
470
+ (async_info[:format_str_fields] || {}).each_key do |field_name|
471
+ field_expr = async_frame_field_expression(frame_expr, field_name, @ctx.types.fetch("str"))
472
+ body << IR::ExpressionStmt.new(
473
+ expression: IR::Call.new(
474
+ callee: "mt_format_str_release",
475
+ arguments: [field_expr],
476
+ type: @ctx.types.fetch("void"),
477
+ ),
668
478
  )
669
479
  end
670
480
 
671
- def async_frame_cast_declaration(frame_type, async_info)
672
- IR::LocalDecl.new(
673
- name: async_frame_local_name,
674
- linkage_name: async_frame_local_name,
675
- type: pointer_to(frame_type),
676
- value: IR::Cast.new(
677
- target_type: pointer_to(frame_type),
678
- expression: IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false),
679
- type: pointer_to(frame_type),
680
- ),
481
+ async_info[:param_fields].each_value do |field_info|
482
+ next unless field_info[:pointer]
483
+
484
+ param_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
485
+ body << IR::ExpressionStmt.new(
486
+ expression: IR::Call.new(callee: "mt_async_free", arguments: [param_expr], type: @ctx.types.fetch("void")),
681
487
  )
682
488
  end
683
489
 
684
- def async_frame_local_name
685
- "__mt_frame"
490
+ body << IR::ExpressionStmt.new(expression: IR::Call.new(callee: "mt_async_free", arguments: [raw_frame_expr], type: @ctx.types.fetch("void")))
491
+ body << IR::ReturnStmt.new(value: nil)
492
+
493
+ IR::Function.new(
494
+ name: "#{release_linkage_name}_fn",
495
+ linkage_name: release_linkage_name,
496
+ params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
497
+ return_type: @ctx.types.fetch("void"),
498
+ body:,
499
+ entry_point: false,
500
+ )
501
+ end
502
+
503
+ def build_async_cancel_function(frame_type, cancel_linkage_name, async_info)
504
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
505
+
506
+ body = [async_frame_cast_declaration(frame_type, async_info)]
507
+
508
+ ready_expr = async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool"))
509
+ if_ready_return = IR::IfStmt.new(
510
+ condition: ready_expr,
511
+ then_body: [IR::ReturnStmt.new(value: nil)],
512
+ else_body: nil,
513
+ )
514
+ body << if_ready_return
515
+
516
+ cancelled_assign = IR::Assignment.new(
517
+ target: async_frame_field_expression(frame_expr, "cancelled", @ctx.types.fetch("bool")),
518
+ operator: "=",
519
+ value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
520
+ )
521
+ body << IR::ExpressionStmt.new(expression: cancelled_assign)
522
+
523
+ cancel_stmts = []
524
+ async_info[:await_fields].each_value do |field_info|
525
+ task_field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:task_type])
526
+ task_frame_expr = async_task_frame_expression(task_field_expr, field_info[:task_type])
527
+ cancel_call = IR::ExpressionStmt.new(
528
+ expression: async_task_call(task_field_expr, field_info[:task_type], "cancel", [task_frame_expr], @ctx.types.fetch("void")),
529
+ )
530
+ release_call = IR::ExpressionStmt.new(
531
+ expression: async_task_call(task_field_expr, field_info[:task_type], "release", [task_frame_expr], @ctx.types.fetch("void")),
532
+ )
533
+ cancel_member_expr = IR::Member.new(receiver: task_field_expr, member: "cancel", type: async_info[:task_type].field("cancel"))
534
+ cancel_stmts << IR::IfStmt.new(
535
+ condition: task_frame_expr,
536
+ then_body: [
537
+ IR::IfStmt.new(
538
+ condition: cancel_member_expr,
539
+ then_body: [cancel_call],
540
+ else_body: nil,
541
+ ),
542
+ release_call,
543
+ ],
544
+ else_body: nil,
545
+ )
686
546
  end
547
+ body.concat(cancel_stmts) if cancel_stmts.any?
687
548
 
688
- def async_frame_raw_name
689
- "__mt_frame_raw"
690
- end
549
+ async_info[:param_fields].each_value do |field_info|
550
+ next if field_info[:pointer]
551
+ next unless contains_proc_storage_type?(field_info[:type])
691
552
 
692
- def async_frame_field_c_name(field_name)
693
- "#{async_frame_local_name}->#{field_name}"
553
+ field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
554
+ body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:type]))
694
555
  end
695
556
 
696
- def async_state_label(resume_linkage_name, state)
697
- "#{resume_linkage_name}_state_#{state}"
557
+ async_info[:local_fields].each_value do |field_info|
558
+ next unless contains_proc_storage_type?(field_info[:storage_type])
559
+
560
+ field_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
561
+ body.concat(lower_async_frame_proc_release_statements(field_expr, field_info[:storage_type]))
698
562
  end
699
563
 
700
- def async_frame_field_expression(frame_expr, field_name, field_type)
701
- IR::Member.new(receiver: frame_expr, member: field_name, type: field_type)
564
+ (async_info[:format_str_fields] || {}).each_key do |field_name|
565
+ field_expr = async_frame_field_expression(frame_expr, field_name, @ctx.types.fetch("str"))
566
+ body << IR::ExpressionStmt.new(
567
+ expression: IR::Call.new(
568
+ callee: "mt_format_str_release",
569
+ arguments: [field_expr],
570
+ type: @ctx.types.fetch("void"),
571
+ ),
572
+ )
702
573
  end
703
574
 
704
- def async_task_frame_expression(task_expr, task_type)
705
- IR::Member.new(receiver: task_expr, member: "frame", type: task_type.field("frame"))
575
+ async_info[:param_fields].each_value do |field_info|
576
+ next unless field_info[:pointer]
577
+
578
+ param_expr = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:type])
579
+ body << IR::ExpressionStmt.new(
580
+ expression: IR::Call.new(callee: "mt_async_free", arguments: [param_expr], type: @ctx.types.fetch("void")),
581
+ )
706
582
  end
707
583
 
708
- def async_task_call(task_expr, task_type, member, arguments, return_type)
709
- IR::Call.new(
710
- callee: IR::Member.new(receiver: task_expr, member:, type: task_type.field(member)),
711
- arguments:,
712
- type: return_type,
584
+ ready_assign = IR::Assignment.new(
585
+ target: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")),
586
+ operator: "=",
587
+ value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool")),
588
+ )
589
+ body << IR::ExpressionStmt.new(expression: ready_assign)
590
+
591
+ waiter_frame_expr = async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr])
592
+ wake_stmts = [
593
+ IR::ExpressionStmt.new(
594
+ expression: IR::Assignment.new(
595
+ target: async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr]),
596
+ operator: "=",
597
+ value: IR::NullLiteral.new(type: async_info[:void_ptr]),
598
+ ),
599
+ ),
600
+ IR::ExpressionStmt.new(
601
+ expression: IR::Call.new(
602
+ callee: IR::Name.new(name: async_frame_field_c_name("waiter"), type: async_info[:wake_type], pointer: false),
603
+ arguments: [waiter_frame_expr],
604
+ type: @ctx.types.fetch("void"),
605
+ ),
606
+ ),
607
+ ]
608
+ body << IR::IfStmt.new(
609
+ condition: waiter_frame_expr,
610
+ then_body: wake_stmts,
611
+ else_body: nil,
612
+ )
613
+
614
+ body << IR::ReturnStmt.new(value: nil)
615
+
616
+ IR::Function.new(
617
+ name: "#{cancel_linkage_name}_fn",
618
+ linkage_name: cancel_linkage_name,
619
+ params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
620
+ return_type: @ctx.types.fetch("void"),
621
+ body:,
622
+ entry_point: false,
623
+ )
624
+ end
625
+
626
+ def build_async_take_result_function(frame_type, take_result_linkage_name, async_info)
627
+ frame_expr = IR::Name.new(name: async_frame_local_name, type: pointer_to(frame_type), pointer: false)
628
+ body = if async_info[:result_type] == @ctx.types.fetch("void")
629
+ [IR::ReturnStmt.new(value: nil)]
630
+ else
631
+ [async_frame_cast_declaration(frame_type, async_info),
632
+ IR::ReturnStmt.new(value: async_frame_field_expression(frame_expr, "result", async_info[:result_type]))]
633
+ end
634
+
635
+ IR::Function.new(
636
+ name: "#{take_result_linkage_name}_fn",
637
+ linkage_name: take_result_linkage_name,
638
+ params: [IR::Param.new(name: "frame", linkage_name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false)],
639
+ return_type: async_info[:result_type],
640
+ body:,
641
+ entry_point: false,
642
+ )
643
+ end
644
+
645
+ def async_resume_env_for(async_info)
646
+ env = empty_env
647
+ async_info[:param_fields].each do |name, field_info|
648
+ env[:scopes].last[name] = local_binding(
649
+ type: field_info[:pointer] ? pointee_type(field_info[:type]) : field_info[:type],
650
+ linkage_name: async_frame_field_c_name(field_info[:field_name]),
651
+ mutable: field_info[:mutable],
652
+ pointer: field_info[:pointer],
713
653
  )
714
654
  end
655
+ env
656
+ end
657
+
658
+ def async_bind_local!(env, name, field_info)
659
+ current_actual_scope(env[:scopes])[name] = local_binding(
660
+ type: field_info[:type],
661
+ storage_type: field_info[:storage_type],
662
+ linkage_name: async_frame_field_c_name(field_info[:field_name]),
663
+ mutable: field_info[:mutable],
664
+ pointer: false,
665
+ )
666
+ end
667
+
668
+ def async_frame_cast_declaration(frame_type, async_info)
669
+ IR::LocalDecl.new(
670
+ name: async_frame_local_name,
671
+ linkage_name: async_frame_local_name,
672
+ type: pointer_to(frame_type),
673
+ value: IR::Cast.new(
674
+ target_type: pointer_to(frame_type),
675
+ expression: IR::Name.new(name: async_frame_raw_name, type: async_info[:void_ptr], pointer: false),
676
+ type: pointer_to(frame_type),
677
+ ),
678
+ )
679
+ end
680
+
681
+ def async_frame_local_name
682
+ "__mt_frame"
683
+ end
684
+
685
+ def async_frame_raw_name
686
+ "__mt_frame_raw"
687
+ end
688
+
689
+ def async_frame_field_c_name(field_name)
690
+ "#{async_frame_local_name}->#{field_name}"
691
+ end
692
+
693
+ def async_state_label(resume_linkage_name, state)
694
+ "#{resume_linkage_name}_state_#{state}"
695
+ end
696
+
697
+ def async_frame_field_expression(frame_expr, field_name, field_type)
698
+ IR::Member.new(receiver: frame_expr, member: field_name, type: field_type)
699
+ end
700
+
701
+ def async_task_frame_expression(task_expr, task_type)
702
+ IR::Member.new(receiver: task_expr, member: "frame", type: task_type.field("frame"))
703
+ end
704
+
705
+ def async_task_call(task_expr, task_type, member, arguments, return_type)
706
+ IR::Call.new(
707
+ callee: IR::Member.new(receiver: task_expr, member:, type: task_type.field(member)),
708
+ arguments:,
709
+ type: return_type,
710
+ )
711
+ end
715
712
  end
716
713
  end