mt-lang 0.3.24 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -1
  3. data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
  4. data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
  5. data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
  6. data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
  7. data/lib/milk_tea/core/bindings.rb +4 -9
  8. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
  9. data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
  10. data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
  11. data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
  12. data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
  13. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
  14. data/lib/milk_tea/core/c_backend/statements.rb +20 -20
  15. data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
  16. data/lib/milk_tea/core/c_backend/type_system.rb +1 -1
  17. data/lib/milk_tea/core/c_backend.rb +3 -3
  18. data/lib/milk_tea/core/compile_time.rb +41 -70
  19. data/lib/milk_tea/core/control_flow/builder.rb +3 -3
  20. data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
  21. data/lib/milk_tea/core/control_flow/graph.rb +1 -1
  22. data/lib/milk_tea/core/control_flow/termination.rb +1 -1
  23. data/lib/milk_tea/core/cst.rb +16 -0
  24. data/lib/milk_tea/core/flow_scope.rb +13 -0
  25. data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
  26. data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
  27. data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
  28. data/lib/milk_tea/core/lexer/indentation.rb +4 -4
  29. data/lib/milk_tea/core/lexer/numbers.rb +1 -1
  30. data/lib/milk_tea/core/lexer/strings.rb +4 -4
  31. data/lib/milk_tea/core/lexer/symbols.rb +3 -3
  32. data/lib/milk_tea/core/lexer/trivia.rb +4 -4
  33. data/lib/milk_tea/core/lexer.rb +17 -17
  34. data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
  35. data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
  36. data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
  37. data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
  38. data/lib/milk_tea/core/lowering/block.rb +982 -980
  39. data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
  40. data/lib/milk_tea/core/lowering/declarations.rb +175 -173
  41. data/lib/milk_tea/core/lowering/dyn.rb +171 -169
  42. data/lib/milk_tea/core/lowering/events.rb +946 -944
  43. data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
  44. data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
  45. data/lib/milk_tea/core/lowering/functions.rb +186 -184
  46. data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
  47. data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
  48. data/lib/milk_tea/core/lowering/proc.rb +355 -353
  49. data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
  50. data/lib/milk_tea/core/lowering/scans.rb +162 -168
  51. data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
  52. data/lib/milk_tea/core/lowering/utils.rb +964 -963
  53. data/lib/milk_tea/core/lowering.rb +22 -22
  54. data/lib/milk_tea/core/module_binder.rb +1 -1
  55. data/lib/milk_tea/core/module_loader.rb +2 -2
  56. data/lib/milk_tea/core/parser/attributes.rb +1 -1
  57. data/lib/milk_tea/core/parser/blocks.rb +2 -2
  58. data/lib/milk_tea/core/parser/declarations.rb +15 -15
  59. data/lib/milk_tea/core/parser/expressions.rb +20 -20
  60. data/lib/milk_tea/core/parser/recovery.rb +1 -1
  61. data/lib/milk_tea/core/parser/statements.rb +3 -3
  62. data/lib/milk_tea/core/parser/types.rb +4 -4
  63. data/lib/milk_tea/core/parser.rb +39 -21
  64. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
  65. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
  66. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
  67. data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
  68. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
  69. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
  70. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
  71. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
  72. data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
  73. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
  74. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
  75. data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
  76. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
  77. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
  78. data/lib/milk_tea/core/types/layout.rb +3 -1
  79. data/lib/milk_tea/core/types/visitor.rb +351 -349
  80. data/lib/milk_tea/core.rb +0 -2
  81. data/lib/milk_tea/tooling/cli/commands/bindgen.rb +1 -1
  82. data/lib/milk_tea/tooling/cli/commands/check.rb +5 -5
  83. data/lib/milk_tea/tooling/cli/commands/debug.rb +3 -3
  84. data/lib/milk_tea/tooling/cli/commands/deps.rb +1 -1
  85. data/lib/milk_tea/tooling/cli/commands/emit_c.rb +2 -2
  86. data/lib/milk_tea/tooling/cli/commands/format.rb +6 -6
  87. data/lib/milk_tea/tooling/cli/commands/lint.rb +3 -3
  88. data/lib/milk_tea/tooling/cli/commands/lower.rb +3 -3
  89. data/lib/milk_tea/tooling/cli/commands/parse.rb +3 -3
  90. data/lib/milk_tea/tooling/cli/commands/test.rb +3 -3
  91. data/lib/milk_tea/tooling/cli/commands/toolchain.rb +1 -1
  92. data/lib/milk_tea/tooling/cli.rb +22 -34
  93. data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
  94. metadata +4 -5
  95. data/lib/milk_tea/core/cst_builder.rb +0 -19
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
  97. data/lib/milk_tea/core/token_stream.rb +0 -30
@@ -1,412 +1,414 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MilkTea
4
- module LowererProc
5
- def build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
6
- env = empty_env
7
- params = [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)]
8
- parameter_setup = []
9
-
10
- if env_struct_type
11
- env_pointer_type = pointer_to(env_struct_type)
12
- env_pointer_name = "__mt_proc_env_ptr"
13
- env[:scopes].last[env_pointer_name] = local_binding(type: env_pointer_type, linkage_name: env_pointer_name, mutable: false, pointer: false)
14
- parameter_setup << IR::LocalDecl.new(
15
- name: env_pointer_name,
16
- linkage_name: env_pointer_name,
17
- type: env_pointer_type,
18
- value: IR::Cast.new(
19
- target_type: env_pointer_type,
20
- expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false),
21
- type: env_pointer_type,
22
- ),
23
- )
24
-
25
- env_pointer = IR::Name.new(name: env_pointer_name, type: env_pointer_type, pointer: false)
26
- captures.each do |capture|
27
- capture_c_name = "__mt_capture_#{capture[:name]}"
28
- env[:scopes].last[capture[:name]] = local_binding(type: capture[:type], linkage_name: capture_c_name, mutable: false, pointer: false)
4
+ module Lowering
5
+ module Proc
6
+ def build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
7
+ env = empty_env
8
+ params = [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)]
9
+ parameter_setup = []
10
+
11
+ if env_struct_type
12
+ env_pointer_type = pointer_to(env_struct_type)
13
+ env_pointer_name = "__mt_proc_env_ptr"
14
+ env[:scopes].last[env_pointer_name] = local_binding(type: env_pointer_type, linkage_name: env_pointer_name, mutable: false, pointer: false)
29
15
  parameter_setup << IR::LocalDecl.new(
30
- name: capture[:name],
31
- linkage_name: capture_c_name,
32
- type: capture[:type],
33
- value: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
16
+ name: env_pointer_name,
17
+ linkage_name: env_pointer_name,
18
+ type: env_pointer_type,
19
+ value: IR::Cast.new(
20
+ target_type: env_pointer_type,
21
+ expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false),
22
+ type: env_pointer_type,
23
+ ),
34
24
  )
25
+
26
+ env_pointer = IR::Name.new(name: env_pointer_name, type: env_pointer_type, pointer: false)
27
+ captures.each do |capture|
28
+ capture_c_name = "__mt_capture_#{capture[:name]}"
29
+ env[:scopes].last[capture[:name]] = local_binding(type: capture[:type], linkage_name: capture_c_name, mutable: false, pointer: false)
30
+ parameter_setup << IR::LocalDecl.new(
31
+ name: capture[:name],
32
+ linkage_name: capture_c_name,
33
+ type: capture[:type],
34
+ value: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
35
+ )
36
+ end
35
37
  end
36
- end
37
38
 
38
- expression.params.each_with_index do |param, index|
39
- type = proc_type.params.fetch(index).type
40
- linkage_name = c_local_name(param.name)
41
- if array_type?(type)
42
- input_linkage_name = "#{linkage_name}_input"
43
- params << IR::Param.new(name: param.name, linkage_name: input_linkage_name, type:, pointer: false)
44
- env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
45
- parameter_setup << IR::LocalDecl.new(
46
- name: param.name,
47
- linkage_name:,
48
- type:,
49
- value: IR::Name.new(name: input_linkage_name, type:, pointer: false),
50
- )
51
- else
52
- env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
53
- params << IR::Param.new(name: param.name, linkage_name:, type:, pointer: false)
39
+ expression.params.each_with_index do |param, index|
40
+ type = proc_type.params.fetch(index).type
41
+ linkage_name = c_local_name(param.name)
42
+ if array_type?(type)
43
+ input_linkage_name = "#{linkage_name}_input"
44
+ params << IR::Param.new(name: param.name, linkage_name: input_linkage_name, type:, pointer: false)
45
+ env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
46
+ parameter_setup << IR::LocalDecl.new(
47
+ name: param.name,
48
+ linkage_name:,
49
+ type:,
50
+ value: IR::Name.new(name: input_linkage_name, type:, pointer: false),
51
+ )
52
+ else
53
+ env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
54
+ params << IR::Param.new(name: param.name, linkage_name:, type:, pointer: false)
55
+ end
54
56
  end
55
- end
56
57
 
57
- body = parameter_setup + lower_block(expression.body, env:, active_defers: [], return_type: proc_type.return_type, loop_flow: nil, allow_return: true)
58
- IR::Function.new(name: invoke_c_name, linkage_name: invoke_c_name, params:, return_type: proc_type.return_type, body:, entry_point: false)
59
- end
58
+ body = parameter_setup + lower_block(expression.body, env:, active_defers: [], return_type: proc_type.return_type, loop_flow: nil, allow_return: true)
59
+ IR::Function.new(name: invoke_c_name, linkage_name: invoke_c_name, params:, return_type: proc_type.return_type, body:, entry_point: false)
60
+ end
60
61
 
61
- def build_proc_release_function(release_linkage_name, env_struct_type)
62
- return build_proc_noop_release_function(release_linkage_name) unless env_struct_type
62
+ def build_proc_release_function(release_linkage_name, env_struct_type)
63
+ return build_proc_noop_release_function(release_linkage_name) unless env_struct_type
63
64
 
64
- env_pointer_type = pointer_to(env_struct_type)
65
- env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
66
- ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
65
+ env_pointer_type = pointer_to(env_struct_type)
66
+ env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
67
+ ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
67
68
 
68
- free_body = []
69
+ free_body = []
69
70
 
70
- if contains_proc_storage_type?(env_struct_type)
71
- env_struct_type.fields.reject { |name, _| name == "__mt_ref_count" }.each do |field_name, field_type|
72
- next unless contains_proc_storage_type?(field_type)
71
+ if contains_proc_storage_type?(env_struct_type)
72
+ env_struct_type.fields.reject { |name, _| name == "__mt_ref_count" }.each do |field_name, field_type|
73
+ next unless contains_proc_storage_type?(field_type)
73
74
 
74
- member = IR::Member.new(receiver: env_pointer, member: field_name, type: field_type)
75
- free_body.concat(lower_proc_contained_release_statements(member, field_type))
75
+ member = IR::Member.new(receiver: env_pointer, member: field_name, type: field_type)
76
+ free_body.concat(lower_proc_contained_release_statements(member, field_type))
77
+ end
76
78
  end
77
- end
78
79
 
79
- free_body << IR::ExpressionStmt.new(
80
- expression: IR::Call.new(
81
- callee: "mt_async_free",
82
- arguments: [IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
83
- type: @ctx.types.fetch("void"),
84
- ),
85
- )
86
-
87
- IR::Function.new(
88
- name: release_linkage_name,
89
- linkage_name: release_linkage_name,
90
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
91
- return_type: @ctx.types.fetch("void"),
92
- body: [
93
- IR::LocalDecl.new(
94
- name: "__mt_proc_env_ptr",
95
- linkage_name: "__mt_proc_env_ptr",
96
- type: env_pointer_type,
97
- value: IR::Cast.new(target_type: env_pointer_type, expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false), type: env_pointer_type),
98
- ),
99
- IR::Assignment.new(target: ref_count, operator: "-=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
100
- IR::IfStmt.new(
101
- condition: IR::Binary.new(operator: "==", left: ref_count, right: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint")), type: @ctx.types.fetch("bool")),
102
- then_body: free_body,
103
- else_body: nil,
80
+ free_body << IR::ExpressionStmt.new(
81
+ expression: IR::Call.new(
82
+ callee: "mt_async_free",
83
+ arguments: [IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
84
+ type: @ctx.types.fetch("void"),
104
85
  ),
105
- IR::ReturnStmt.new(value: nil),
106
- ],
107
- entry_point: false,
108
- )
109
- end
86
+ )
110
87
 
111
- def build_proc_retain_function(retain_c_name, env_struct_type)
112
- return build_proc_noop_retain_function(retain_c_name) unless env_struct_type
113
-
114
- env_pointer_type = pointer_to(env_struct_type)
115
- env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
116
- ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
117
- IR::Function.new(
118
- name: retain_c_name,
119
- linkage_name: retain_c_name,
120
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
121
- return_type: @ctx.types.fetch("void"),
122
- body: [
123
- IR::LocalDecl.new(
124
- name: "__mt_proc_env_ptr",
125
- linkage_name: "__mt_proc_env_ptr",
126
- type: env_pointer_type,
127
- value: IR::Cast.new(target_type: env_pointer_type, expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false), type: env_pointer_type),
128
- ),
129
- IR::Assignment.new(target: ref_count, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
130
- IR::ReturnStmt.new(value: nil),
131
- ],
132
- entry_point: false,
133
- )
134
- end
88
+ IR::Function.new(
89
+ name: release_linkage_name,
90
+ linkage_name: release_linkage_name,
91
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
92
+ return_type: @ctx.types.fetch("void"),
93
+ body: [
94
+ IR::LocalDecl.new(
95
+ name: "__mt_proc_env_ptr",
96
+ linkage_name: "__mt_proc_env_ptr",
97
+ type: env_pointer_type,
98
+ value: IR::Cast.new(target_type: env_pointer_type, expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false), type: env_pointer_type),
99
+ ),
100
+ IR::Assignment.new(target: ref_count, operator: "-=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
101
+ IR::IfStmt.new(
102
+ condition: IR::Binary.new(operator: "==", left: ref_count, right: IR::IntegerLiteral.new(value: 0, type: @ctx.types.fetch("ptr_uint")), type: @ctx.types.fetch("bool")),
103
+ then_body: free_body,
104
+ else_body: nil,
105
+ ),
106
+ IR::ReturnStmt.new(value: nil),
107
+ ],
108
+ entry_point: false,
109
+ )
110
+ end
135
111
 
136
- def proc_capture_entries(expression, env)
137
- local_scopes = [expression.params.each_with_object({}) { |param, names| names[param.name] = true }]
138
- captures = {}
139
- collect_proc_captures_from_statements(expression.body, env, local_scopes, captures)
140
- captures.values
141
- end
112
+ def build_proc_retain_function(retain_c_name, env_struct_type)
113
+ return build_proc_noop_retain_function(retain_c_name) unless env_struct_type
142
114
 
143
- def collect_proc_captures_from_statements(statements, env, local_scopes, captures)
144
- statements.each do |statement|
145
- collect_proc_captures_from_statement(statement, env, local_scopes, captures)
115
+ env_pointer_type = pointer_to(env_struct_type)
116
+ env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
117
+ ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
118
+ IR::Function.new(
119
+ name: retain_c_name,
120
+ linkage_name: retain_c_name,
121
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
122
+ return_type: @ctx.types.fetch("void"),
123
+ body: [
124
+ IR::LocalDecl.new(
125
+ name: "__mt_proc_env_ptr",
126
+ linkage_name: "__mt_proc_env_ptr",
127
+ type: env_pointer_type,
128
+ value: IR::Cast.new(target_type: env_pointer_type, expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false), type: env_pointer_type),
129
+ ),
130
+ IR::Assignment.new(target: ref_count, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
131
+ IR::ReturnStmt.new(value: nil),
132
+ ],
133
+ entry_point: false,
134
+ )
146
135
  end
147
- end
148
136
 
149
- def collect_proc_captures_from_statement(statement, env, local_scopes, captures)
150
- case statement
151
- when AST::LocalDecl
152
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
153
- local_scopes.last[statement.name] = true
154
- when AST::Assignment
155
- collect_proc_captures_from_expression(statement.target, env, local_scopes, captures)
156
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures)
157
- when AST::IfStmt
158
- statement.branches.each do |branch|
159
- collect_proc_captures_from_expression(branch.condition, env, local_scopes, captures)
160
- collect_proc_captures_from_statements(branch.body, env, local_scopes + [{}], captures)
137
+ def proc_capture_entries(expression, env)
138
+ local_scopes = [expression.params.each_with_object({}) { |param, names| names[param.name] = true }]
139
+ captures = {}
140
+ collect_proc_captures_from_statements(expression.body, env, local_scopes, captures)
141
+ captures.values
142
+ end
143
+
144
+ def collect_proc_captures_from_statements(statements, env, local_scopes, captures)
145
+ statements.each do |statement|
146
+ collect_proc_captures_from_statement(statement, env, local_scopes, captures)
161
147
  end
162
- collect_proc_captures_from_statements(statement.else_body, env, local_scopes + [{}], captures) if statement.else_body
163
- when AST::MatchStmt
164
- collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
165
- statement.arms.each do |arm|
166
- collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
167
- collect_proc_captures_from_statements(arm.body, env, local_scopes + [{}], captures)
148
+ end
149
+
150
+ def collect_proc_captures_from_statement(statement, env, local_scopes, captures)
151
+ case statement
152
+ when AST::LocalDecl
153
+ collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
154
+ local_scopes.last[statement.name] = true
155
+ when AST::Assignment
156
+ collect_proc_captures_from_expression(statement.target, env, local_scopes, captures)
157
+ collect_proc_captures_from_expression(statement.value, env, local_scopes, captures)
158
+ when AST::IfStmt
159
+ statement.branches.each do |branch|
160
+ collect_proc_captures_from_expression(branch.condition, env, local_scopes, captures)
161
+ collect_proc_captures_from_statements(branch.body, env, local_scopes + [{}], captures)
162
+ end
163
+ collect_proc_captures_from_statements(statement.else_body, env, local_scopes + [{}], captures) if statement.else_body
164
+ when AST::MatchStmt
165
+ collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
166
+ statement.arms.each do |arm|
167
+ collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
168
+ collect_proc_captures_from_statements(arm.body, env, local_scopes + [{}], captures)
169
+ end
170
+ when AST::UnsafeStmt
171
+ collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
172
+ when AST::StaticAssert
173
+ collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
174
+ collect_proc_captures_from_expression(statement.message, env, local_scopes, captures)
175
+ when AST::ForStmt
176
+ statement.iterables.each { |iterable| collect_proc_captures_from_expression(iterable, env, local_scopes, captures) }
177
+ collect_proc_captures_from_statements(statement.body, env, local_scopes + [statement.names.each_with_object({}) { |name, scope| scope[name] = true }], captures)
178
+ when AST::WhileStmt
179
+ collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
180
+ collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
181
+ when AST::ReturnStmt
182
+ collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
183
+ when AST::DeferStmt
184
+ collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
185
+ when AST::ExpressionStmt
186
+ collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
187
+ when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt
188
+ nil
189
+ else
190
+ raise LoweringError.new("unsupported proc capture statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
168
191
  end
169
- when AST::UnsafeStmt
170
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
171
- when AST::StaticAssert
172
- collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
173
- collect_proc_captures_from_expression(statement.message, env, local_scopes, captures)
174
- when AST::ForStmt
175
- statement.iterables.each { |iterable| collect_proc_captures_from_expression(iterable, env, local_scopes, captures) }
176
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [statement.names.each_with_object({}) { |name, scope| scope[name] = true }], captures)
177
- when AST::WhileStmt
178
- collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
179
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
180
- when AST::ReturnStmt
181
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
182
- when AST::DeferStmt
183
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
184
- when AST::ExpressionStmt
185
- collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
186
- when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt
187
- nil
188
- else
189
- raise LoweringError.new("unsupported proc capture statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
190
192
  end
191
- end
192
193
 
193
- def collect_proc_captures_from_expression(expression, env, local_scopes, captures)
194
- return unless expression
194
+ def collect_proc_captures_from_expression(expression, env, local_scopes, captures)
195
+ return unless expression
195
196
 
196
- case expression
197
- when AST::Identifier
198
- return if local_scopes.any? { |scope| scope.key?(expression.name) }
197
+ case expression
198
+ when AST::Identifier
199
+ return if local_scopes.any? { |scope| scope.key?(expression.name) }
199
200
 
200
- if (binding = proc_capture_binding(expression.name, env))
201
- captures[expression.name] ||= { name: expression.name, field_name: expression.name, type: binding[:type] }
202
- end
203
- when AST::MemberAccess
204
- collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
205
- when AST::IndexAccess
206
- collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
207
- collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
208
- when AST::Specialization
209
- collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
210
- expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
211
- when AST::Call
212
- collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
213
- expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
214
- when AST::UnaryOp
215
- collect_proc_captures_from_expression(expression.operand, env, local_scopes, captures)
216
- when AST::BinaryOp
217
- collect_proc_captures_from_expression(expression.left, env, local_scopes, captures)
218
- collect_proc_captures_from_expression(expression.right, env, local_scopes, captures)
219
- when AST::IfExpr
220
- collect_proc_captures_from_expression(expression.condition, env, local_scopes, captures)
221
- collect_proc_captures_from_expression(expression.then_expression, env, local_scopes, captures)
222
- collect_proc_captures_from_expression(expression.else_expression, env, local_scopes, captures)
223
- when AST::MatchExpr
224
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
225
- expression.arms.each do |arm|
226
- collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
227
- arm_scopes = arm.binding_name ? local_scopes + [{ arm.binding_name => true }] : local_scopes
228
- collect_proc_captures_from_expression(arm.value, env, arm_scopes, captures)
201
+ if (binding = proc_capture_binding(expression.name, env))
202
+ captures[expression.name] ||= { name: expression.name, field_name: expression.name, type: binding[:type] }
203
+ end
204
+ when AST::MemberAccess
205
+ collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
206
+ when AST::IndexAccess
207
+ collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
208
+ collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
209
+ when AST::Specialization
210
+ collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
211
+ expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
212
+ when AST::Call
213
+ collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
214
+ expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
215
+ when AST::UnaryOp
216
+ collect_proc_captures_from_expression(expression.operand, env, local_scopes, captures)
217
+ when AST::BinaryOp
218
+ collect_proc_captures_from_expression(expression.left, env, local_scopes, captures)
219
+ collect_proc_captures_from_expression(expression.right, env, local_scopes, captures)
220
+ when AST::IfExpr
221
+ collect_proc_captures_from_expression(expression.condition, env, local_scopes, captures)
222
+ collect_proc_captures_from_expression(expression.then_expression, env, local_scopes, captures)
223
+ collect_proc_captures_from_expression(expression.else_expression, env, local_scopes, captures)
224
+ when AST::MatchExpr
225
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
226
+ expression.arms.each do |arm|
227
+ collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
228
+ arm_scopes = arm.binding_name ? local_scopes + [{ arm.binding_name => true }] : local_scopes
229
+ collect_proc_captures_from_expression(arm.value, env, arm_scopes, captures)
230
+ end
231
+ when AST::UnsafeExpr
232
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
233
+ when AST::AwaitExpr
234
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
235
+ when AST::FormatString
236
+ expression.parts.each do |part|
237
+ collect_proc_captures_from_expression(part.expression, env, local_scopes, captures) if part.is_a?(AST::FormatExprPart)
238
+ end
239
+ when AST::ProcExpr, AST::TypeRef, AST::FunctionType, AST::ProcType,
240
+ AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr,
241
+ AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
242
+ AST::BooleanLiteral, AST::NullLiteral
243
+ nil
244
+ else
245
+ raise LoweringError.new("unsupported proc capture expression #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
229
246
  end
230
- when AST::UnsafeExpr
231
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
232
- when AST::AwaitExpr
233
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
234
- when AST::FormatString
235
- expression.parts.each do |part|
236
- collect_proc_captures_from_expression(part.expression, env, local_scopes, captures) if part.is_a?(AST::FormatExprPart)
247
+ end
248
+
249
+ def proc_capture_binding(name, env)
250
+ env[:scopes].reverse_each do |scope|
251
+ return scope[name] if scope.key?(name)
237
252
  end
238
- when AST::ProcExpr, AST::TypeRef, AST::FunctionType, AST::ProcType,
239
- AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr,
240
- AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
241
- AST::BooleanLiteral, AST::NullLiteral
253
+
242
254
  nil
243
- else
244
- raise LoweringError.new("unsupported proc capture expression #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
245
255
  end
246
- end
247
256
 
248
- def proc_capture_binding(name, env)
249
- env[:scopes].reverse_each do |scope|
250
- return scope[name] if scope.key?(name)
257
+ def lower_proc_release_expression(proc_expression, _proc_type)
258
+ IR::Call.new(
259
+ callee: IR::Member.new(receiver: proc_expression, member: "release", type: proc_release_function_type),
260
+ arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
261
+ type: @ctx.types.fetch("void"),
262
+ )
251
263
  end
252
264
 
253
- nil
254
- end
255
-
256
- def lower_proc_release_expression(proc_expression, _proc_type)
257
- IR::Call.new(
258
- callee: IR::Member.new(receiver: proc_expression, member: "release", type: proc_release_function_type),
259
- arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
260
- type: @ctx.types.fetch("void"),
261
- )
262
- end
263
-
264
- def lower_proc_retain_expression(proc_expression, _proc_type)
265
- IR::Call.new(
266
- callee: IR::Member.new(receiver: proc_expression, member: "retain", type: proc_retain_function_type),
267
- arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
268
- type: @ctx.types.fetch("void"),
269
- )
270
- end
265
+ def lower_proc_retain_expression(proc_expression, _proc_type)
266
+ IR::Call.new(
267
+ callee: IR::Member.new(receiver: proc_expression, member: "retain", type: proc_retain_function_type),
268
+ arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
269
+ type: @ctx.types.fetch("void"),
270
+ )
271
+ end
271
272
 
272
- def lower_proc_contained_release_statements(value_expression, type)
273
- lower_proc_contained_lifecycle_statements(value_expression, type, :release)
274
- end
273
+ def lower_proc_contained_release_statements(value_expression, type)
274
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release)
275
+ end
275
276
 
276
- # Null-guarded release: safe when value may be zero-initialized (var locals, async frame fields).
277
- # Wraps each proc release in `if (proc.invoke) { proc.release(proc.env); }`.
278
- def lower_proc_contained_guarded_release_statements(value_expression, type)
279
- lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
280
- end
277
+ # Null-guarded release: safe when value may be zero-initialized (var locals, async frame fields).
278
+ # Wraps each proc release in `if (proc.invoke) { proc.release(proc.env); }`.
279
+ def lower_proc_nullable_release_statements(value_expression, type)
280
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
281
+ end
281
282
 
282
- # Alias used for async frame fields (always guarded).
283
- def lower_async_frame_proc_release_statements(value_expression, type)
284
- lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
285
- end
283
+ # Alias used for async frame fields (always guarded).
284
+ def lower_async_frame_proc_nullable_release_statements(value_expression, type)
285
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
286
+ end
286
287
 
287
- def lower_proc_contained_retain_statements(value_expression, type)
288
- lower_proc_contained_lifecycle_statements(value_expression, type, :retain)
289
- end
288
+ def lower_proc_contained_retain_statements(value_expression, type)
289
+ lower_proc_contained_lifecycle_statements(value_expression, type, :retain)
290
+ end
290
291
 
291
- def lower_proc_contained_lifecycle_statements(value_expression, type, mode, guarded: false)
292
- return [] unless contains_proc_storage_type?(type)
292
+ def lower_proc_contained_lifecycle_statements(value_expression, type, mode, guarded: false)
293
+ return [] unless contains_proc_storage_type?(type)
293
294
 
294
- if proc_type?(type)
295
- if mode == :release && guarded
296
- invoke_member = IR::Member.new(receiver: value_expression, member: "invoke", type: proc_invoke_function_type(type))
297
- release_stmt = IR::ExpressionStmt.new(expression: lower_proc_release_expression(value_expression, type))
298
- return [IR::IfStmt.new(condition: invoke_member, then_body: [release_stmt], else_body: nil)]
295
+ if proc_type?(type)
296
+ if mode == :release && guarded
297
+ invoke_member = IR::Member.new(receiver: value_expression, member: "invoke", type: proc_invoke_function_type(type))
298
+ release_stmt = IR::ExpressionStmt.new(expression: lower_proc_release_expression(value_expression, type))
299
+ return [IR::IfStmt.new(condition: invoke_member, then_body: [release_stmt], else_body: nil)]
300
+ end
301
+ expression = mode == :retain ? lower_proc_retain_expression(value_expression, type) : lower_proc_release_expression(value_expression, type)
302
+ return [IR::ExpressionStmt.new(expression:)]
299
303
  end
300
- expression = mode == :retain ? lower_proc_retain_expression(value_expression, type) : lower_proc_release_expression(value_expression, type)
301
- return [IR::ExpressionStmt.new(expression:)]
302
- end
303
304
 
304
- case type
305
- when Types::Struct, Types::StructInstance
306
- statements = []
307
- type.fields.each do |field_name, field_type|
308
- next unless contains_proc_storage_type?(field_type)
305
+ case type
306
+ when Types::Struct, Types::StructInstance
307
+ statements = []
308
+ type.fields.each do |field_name, field_type|
309
+ next unless contains_proc_storage_type?(field_type)
309
310
 
310
- member = IR::Member.new(receiver: value_expression, member: field_name, type: field_type)
311
- statements.concat(lower_proc_contained_lifecycle_statements(member, field_type, mode, guarded:))
311
+ member = IR::Member.new(receiver: value_expression, member: field_name, type: field_type)
312
+ statements.concat(lower_proc_contained_lifecycle_statements(member, field_type, mode, guarded:))
313
+ end
314
+ statements
315
+ when Types::Nullable
316
+ []
317
+ else
318
+ raise LoweringError.new("unsupported proc lifecycle container #{type.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
312
319
  end
313
- statements
314
- when Types::Nullable
315
- []
316
- else
317
- raise LoweringError.new("unsupported proc lifecycle container #{type.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
318
320
  end
319
- end
320
321
 
321
- # Retain only proc fields that did NOT originate from a fresh proc expression in `original_ast`.
322
- # Fresh proc expressions already carry refcount=1; retaining them would over-count.
323
- # For existing proc values (variables, member accesses, return values), we retain to share ownership.
324
- # When `original_ast` is a struct aggregate literal (AST::Call), fields are matched by name.
325
- def lower_proc_selective_retain_statements(ir_value, original_ast, type)
326
- return [] unless contains_proc_storage_type?(type)
322
+ # Retain only proc fields that did NOT originate from a fresh proc expression in `original_ast`.
323
+ # Fresh proc expressions already carry refcount=1; retaining them would over-count.
324
+ # For existing proc values (variables, member accesses, return values), we retain to share ownership.
325
+ # When `original_ast` is a struct aggregate literal (AST::Call), fields are matched by name.
326
+ def lower_proc_selective_retain_statements(ir_value, original_ast, type)
327
+ return [] unless contains_proc_storage_type?(type)
327
328
 
328
- if proc_type?(type)
329
- # If the direct expression is a fresh proc, ownership transfers — no retain needed.
330
- return [] if expression_contains_proc_expr?(original_ast)
329
+ if proc_type?(type)
330
+ # If the direct expression is a fresh proc, ownership transfers — no retain needed.
331
+ return [] if expression_contains_proc_expr?(original_ast)
331
332
 
332
- return [IR::ExpressionStmt.new(expression: lower_proc_retain_expression(ir_value, type))]
333
- end
334
-
335
- case type
336
- when Types::Struct, Types::StructInstance
337
- statements = []
338
- type.fields.each do |field_name, field_type|
339
- next unless contains_proc_storage_type?(field_type)
333
+ return [IR::ExpressionStmt.new(expression: lower_proc_retain_expression(ir_value, type))]
334
+ end
340
335
 
341
- # Try to extract the AST sub-expression for this specific field when the source
342
- # is a struct aggregate literal (struct-name(field = value, ...)).
343
- ast_field_source = if original_ast.is_a?(AST::Call)
344
- original_ast.arguments.find { |arg| arg.name == field_name }&.value
336
+ case type
337
+ when Types::Struct, Types::StructInstance
338
+ statements = []
339
+ type.fields.each do |field_name, field_type|
340
+ next unless contains_proc_storage_type?(field_type)
341
+
342
+ # Try to extract the AST sub-expression for this specific field when the source
343
+ # is a struct aggregate literal (struct-name(field = value, ...)).
344
+ ast_field_source = if original_ast.is_a?(AST::Call)
345
+ original_ast.arguments.find { |arg| arg.name == field_name }&.value
346
+ end
347
+ # Fall back to the whole RHS expression (conservative — treats as existing proc → retains).
348
+ ast_field_source ||= original_ast
349
+
350
+ member = IR::Member.new(receiver: ir_value, member: field_name, type: field_type)
351
+ statements.concat(lower_proc_selective_retain_statements(member, ast_field_source, field_type))
345
352
  end
346
- # Fall back to the whole RHS expression (conservative — treats as existing proc → retains).
347
- ast_field_source ||= original_ast
348
-
349
- member = IR::Member.new(receiver: ir_value, member: field_name, type: field_type)
350
- statements.concat(lower_proc_selective_retain_statements(member, ast_field_source, field_type))
353
+ statements
354
+ when Types::Nullable
355
+ []
356
+ else
357
+ []
351
358
  end
352
- statements
353
- when Types::Nullable
354
- []
355
- else
356
- []
357
359
  end
358
- end
359
360
 
360
- def expression_contains_proc_expr?(expression)
361
- return false unless expression
362
-
363
- case expression
364
- when AST::ProcExpr
365
- true
366
- when AST::MemberAccess
367
- expression_contains_proc_expr?(expression.receiver)
368
- when AST::IndexAccess
369
- expression_contains_proc_expr?(expression.receiver) || expression_contains_proc_expr?(expression.index)
370
- when AST::UnaryOp
371
- expression_contains_proc_expr?(expression.operand)
372
- when AST::BinaryOp
373
- expression_contains_proc_expr?(expression.left) || expression_contains_proc_expr?(expression.right)
374
- when AST::IfExpr
375
- expression_contains_proc_expr?(expression.condition) ||
376
- expression_contains_proc_expr?(expression.then_expression) ||
377
- expression_contains_proc_expr?(expression.else_expression)
378
- when AST::UnsafeExpr
379
- expression_contains_proc_expr?(expression.expression)
380
- when AST::AwaitExpr
381
- expression_contains_proc_expr?(expression.expression)
382
- when AST::Call
383
- expression_contains_proc_expr?(expression.callee) || expression.arguments.any? { |argument| expression_contains_proc_expr?(argument.value) }
384
- when AST::Specialization
385
- expression_contains_proc_expr?(expression.callee)
386
- else
387
- false
361
+ def expression_contains_proc_expr?(expression)
362
+ return false unless expression
363
+
364
+ case expression
365
+ when AST::ProcExpr
366
+ true
367
+ when AST::MemberAccess
368
+ expression_contains_proc_expr?(expression.receiver)
369
+ when AST::IndexAccess
370
+ expression_contains_proc_expr?(expression.receiver) || expression_contains_proc_expr?(expression.index)
371
+ when AST::UnaryOp
372
+ expression_contains_proc_expr?(expression.operand)
373
+ when AST::BinaryOp
374
+ expression_contains_proc_expr?(expression.left) || expression_contains_proc_expr?(expression.right)
375
+ when AST::IfExpr
376
+ expression_contains_proc_expr?(expression.condition) ||
377
+ expression_contains_proc_expr?(expression.then_expression) ||
378
+ expression_contains_proc_expr?(expression.else_expression)
379
+ when AST::UnsafeExpr
380
+ expression_contains_proc_expr?(expression.expression)
381
+ when AST::AwaitExpr
382
+ expression_contains_proc_expr?(expression.expression)
383
+ when AST::Call
384
+ expression_contains_proc_expr?(expression.callee) || expression.arguments.any? { |argument| expression_contains_proc_expr?(argument.value) }
385
+ when AST::Specialization
386
+ expression_contains_proc_expr?(expression.callee)
387
+ else
388
+ false
389
+ end
390
+ end
391
+ def build_proc_noop_release_function(release_linkage_name)
392
+ IR::Function.new(
393
+ name: release_linkage_name,
394
+ linkage_name: release_linkage_name,
395
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
396
+ return_type: @ctx.types.fetch("void"),
397
+ body: [IR::ReturnStmt.new(value: nil)],
398
+ entry_point: false,
399
+ )
388
400
  end
389
- end
390
- def build_proc_noop_release_function(release_linkage_name)
391
- IR::Function.new(
392
- name: release_linkage_name,
393
- linkage_name: release_linkage_name,
394
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
395
- return_type: @ctx.types.fetch("void"),
396
- body: [IR::ReturnStmt.new(value: nil)],
397
- entry_point: false,
398
- )
399
- end
400
401
 
401
- def build_proc_noop_retain_function(retain_c_name)
402
- IR::Function.new(
403
- name: retain_c_name,
404
- linkage_name: retain_c_name,
405
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
406
- return_type: @ctx.types.fetch("void"),
407
- body: [IR::ReturnStmt.new(value: nil)],
408
- entry_point: false,
409
- )
402
+ def build_proc_noop_retain_function(retain_c_name)
403
+ IR::Function.new(
404
+ name: retain_c_name,
405
+ linkage_name: retain_c_name,
406
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
407
+ return_type: @ctx.types.fetch("void"),
408
+ body: [IR::ReturnStmt.new(value: nil)],
409
+ entry_point: false,
410
+ )
411
+ end
410
412
  end
411
413
  end
412
414
  end