mt-lang 0.3.23 → 0.3.25

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