mt-lang 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -2,418 +2,415 @@
2
2
 
3
3
  module MilkTea
4
4
  module LowererProc
5
- private
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 = []
6
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
+ )
7
24
 
8
- def build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
9
- env = empty_env
10
- params = [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)]
11
- parameter_setup = []
12
-
13
- if env_struct_type
14
- env_pointer_type = pointer_to(env_struct_type)
15
- env_pointer_name = "__mt_proc_env_ptr"
16
- env[:scopes].last[env_pointer_name] = local_binding(type: env_pointer_type, linkage_name: env_pointer_name, mutable: false, pointer: false)
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)
17
29
  parameter_setup << IR::LocalDecl.new(
18
- name: env_pointer_name,
19
- linkage_name: env_pointer_name,
20
- type: env_pointer_type,
21
- value: IR::Cast.new(
22
- target_type: env_pointer_type,
23
- expression: IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false),
24
- type: env_pointer_type,
25
- ),
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]),
26
34
  )
27
-
28
- env_pointer = IR::Name.new(name: env_pointer_name, type: env_pointer_type, pointer: false)
29
- captures.each do |capture|
30
- capture_c_name = "__mt_capture_#{capture[:name]}"
31
- env[:scopes].last[capture[:name]] = local_binding(type: capture[:type], linkage_name: capture_c_name, mutable: false, pointer: false)
32
- parameter_setup << IR::LocalDecl.new(
33
- name: capture[:name],
34
- linkage_name: capture_c_name,
35
- type: capture[:type],
36
- value: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
37
- )
38
- end
39
35
  end
36
+ end
40
37
 
41
- expression.params.each_with_index do |param, index|
42
- type = proc_type.params.fetch(index).type
43
- linkage_name = c_local_name(param.name)
44
- if array_type?(type)
45
- input_linkage_name = "#{linkage_name}_input"
46
- params << IR::Param.new(name: param.name, linkage_name: input_linkage_name, type:, pointer: false)
47
- env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
48
- parameter_setup << IR::LocalDecl.new(
49
- name: param.name,
50
- linkage_name:,
51
- type:,
52
- value: IR::Name.new(name: input_linkage_name, type:, pointer: false),
53
- )
54
- else
55
- env[:scopes].last[param.name] = local_binding(type:, linkage_name:, mutable: false, pointer: false)
56
- params << IR::Param.new(name: param.name, linkage_name:, type:, pointer: false)
57
- end
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)
58
54
  end
59
-
60
- body = parameter_setup + lower_block(expression.body, env:, active_defers: [], return_type: proc_type.return_type, loop_flow: nil, allow_return: true)
61
- IR::Function.new(name: invoke_c_name, linkage_name: invoke_c_name, params:, return_type: proc_type.return_type, body:, entry_point: false)
62
55
  end
63
56
 
64
- def build_proc_release_function(release_linkage_name, env_struct_type)
65
- return build_proc_noop_release_function(release_linkage_name) unless env_struct_type
66
-
67
- env_pointer_type = pointer_to(env_struct_type)
68
- env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
69
- ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
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
70
60
 
71
- free_body = []
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
72
63
 
73
- if contains_proc_storage_type?(env_struct_type)
74
- env_struct_type.fields.reject { |name, _| name == "__mt_ref_count" }.each do |field_name, field_type|
75
- next unless contains_proc_storage_type?(field_type)
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"))
76
67
 
77
- member = IR::Member.new(receiver: env_pointer, member: field_name, type: field_type)
78
- free_body.concat(lower_proc_contained_release_statements(member, field_type))
79
- end
80
- end
68
+ free_body = []
81
69
 
82
- free_body << IR::ExpressionStmt.new(
83
- expression: IR::Call.new(
84
- callee: "mt_async_free",
85
- arguments: [IR::Name.new(name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
86
- type: @ctx.types.fetch("void"),
87
- ),
88
- )
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)
89
73
 
90
- IR::Function.new(
91
- name: release_linkage_name,
92
- linkage_name: release_linkage_name,
93
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
94
- return_type: @ctx.types.fetch("void"),
95
- body: [
96
- IR::LocalDecl.new(
97
- name: "__mt_proc_env_ptr",
98
- linkage_name: "__mt_proc_env_ptr",
99
- type: env_pointer_type,
100
- 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),
101
- ),
102
- IR::Assignment.new(target: ref_count, operator: "-=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
103
- IR::IfStmt.new(
104
- 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")),
105
- then_body: free_body,
106
- else_body: nil,
107
- ),
108
- IR::ReturnStmt.new(value: nil),
109
- ],
110
- entry_point: false,
111
- )
112
- end
113
-
114
- def build_proc_retain_function(retain_c_name, env_struct_type)
115
- return build_proc_noop_retain_function(retain_c_name) unless env_struct_type
116
-
117
- env_pointer_type = pointer_to(env_struct_type)
118
- env_pointer = IR::Name.new(name: "__mt_proc_env_ptr", type: env_pointer_type, pointer: false)
119
- ref_count = IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint"))
120
- IR::Function.new(
121
- name: retain_c_name,
122
- linkage_name: retain_c_name,
123
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
124
- return_type: @ctx.types.fetch("void"),
125
- body: [
126
- IR::LocalDecl.new(
127
- name: "__mt_proc_env_ptr",
128
- linkage_name: "__mt_proc_env_ptr",
129
- type: env_pointer_type,
130
- 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),
131
- ),
132
- IR::Assignment.new(target: ref_count, operator: "+=", value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint"))),
133
- IR::ReturnStmt.new(value: nil),
134
- ],
135
- entry_point: false,
136
- )
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))
76
+ end
137
77
  end
138
78
 
139
- def proc_capture_entries(expression, env)
140
- local_scopes = [expression.params.each_with_object({}) { |param, names| names[param.name] = true }]
141
- captures = {}
142
- collect_proc_captures_from_statements(expression.body, env, local_scopes, captures)
143
- captures.values
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,
104
+ ),
105
+ IR::ReturnStmt.new(value: nil),
106
+ ],
107
+ entry_point: false,
108
+ )
109
+ end
110
+
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
135
+
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
142
+
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)
144
146
  end
145
-
146
- def collect_proc_captures_from_statements(statements, env, local_scopes, captures)
147
- statements.each do |statement|
148
- collect_proc_captures_from_statement(statement, env, local_scopes, captures)
147
+ end
148
+
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)
149
161
  end
150
- end
151
-
152
- def collect_proc_captures_from_statement(statement, env, local_scopes, captures)
153
- case statement
154
- when AST::LocalDecl
155
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
156
- local_scopes.last[statement.name] = true
157
- when AST::Assignment
158
- collect_proc_captures_from_expression(statement.target, env, local_scopes, captures)
159
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures)
160
- when AST::IfStmt
161
- statement.branches.each do |branch|
162
- collect_proc_captures_from_expression(branch.condition, env, local_scopes, captures)
163
- collect_proc_captures_from_statements(branch.body, env, local_scopes + [{}], captures)
164
- end
165
- collect_proc_captures_from_statements(statement.else_body, env, local_scopes + [{}], captures) if statement.else_body
166
- when AST::MatchStmt
167
- collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
168
- statement.arms.each do |arm|
169
- collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
170
- collect_proc_captures_from_statements(arm.body, env, local_scopes + [{}], captures)
171
- end
172
- when AST::UnsafeStmt
173
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
174
- when AST::StaticAssert
175
- collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
176
- collect_proc_captures_from_expression(statement.message, env, local_scopes, captures)
177
- when AST::ForStmt
178
- statement.iterables.each { |iterable| collect_proc_captures_from_expression(iterable, env, local_scopes, captures) }
179
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [statement.names.each_with_object({}) { |name, scope| scope[name] = true }], captures)
180
- when AST::WhileStmt
181
- collect_proc_captures_from_expression(statement.condition, env, local_scopes, captures)
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)
168
+ 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
+ if statement.body
182
184
  collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
183
- when AST::ReturnStmt
184
- collect_proc_captures_from_expression(statement.value, env, local_scopes, captures) if statement.value
185
- when AST::DeferStmt
186
- if statement.body
187
- collect_proc_captures_from_statements(statement.body, env, local_scopes + [{}], captures)
188
- else
189
- collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
190
- end
191
- when AST::ExpressionStmt
192
- collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
193
- when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt
194
- nil
195
185
  else
196
- raise LoweringError.new("unsupported proc capture statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
186
+ collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
197
187
  end
188
+ when AST::ExpressionStmt
189
+ collect_proc_captures_from_expression(statement.expression, env, local_scopes, captures)
190
+ when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt
191
+ nil
192
+ else
193
+ raise LoweringError.new("unsupported proc capture statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
198
194
  end
195
+ end
199
196
 
200
- def collect_proc_captures_from_expression(expression, env, local_scopes, captures)
201
- return unless expression
197
+ def collect_proc_captures_from_expression(expression, env, local_scopes, captures)
198
+ return unless expression
202
199
 
203
- case expression
204
- when AST::Identifier
205
- return if local_scopes.any? { |scope| scope.key?(expression.name) }
200
+ case expression
201
+ when AST::Identifier
202
+ return if local_scopes.any? { |scope| scope.key?(expression.name) }
206
203
 
207
- if (binding = proc_capture_binding(expression.name, env))
208
- captures[expression.name] ||= { name: expression.name, field_name: expression.name, type: binding[:type] }
209
- end
210
- when AST::MemberAccess
211
- collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
212
- when AST::IndexAccess
213
- collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
214
- collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
215
- when AST::Specialization
216
- collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
217
- expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
218
- when AST::Call
219
- collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
220
- expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
221
- when AST::UnaryOp
222
- collect_proc_captures_from_expression(expression.operand, env, local_scopes, captures)
223
- when AST::BinaryOp
224
- collect_proc_captures_from_expression(expression.left, env, local_scopes, captures)
225
- collect_proc_captures_from_expression(expression.right, env, local_scopes, captures)
226
- when AST::IfExpr
227
- collect_proc_captures_from_expression(expression.condition, env, local_scopes, captures)
228
- collect_proc_captures_from_expression(expression.then_expression, env, local_scopes, captures)
229
- collect_proc_captures_from_expression(expression.else_expression, env, local_scopes, captures)
230
- when AST::MatchExpr
231
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
232
- expression.arms.each do |arm|
233
- collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
234
- arm_scopes = arm.binding_name ? local_scopes + [{ arm.binding_name => true }] : local_scopes
235
- collect_proc_captures_from_expression(arm.value, env, arm_scopes, captures)
236
- end
237
- when AST::UnsafeExpr
238
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
239
- when AST::AwaitExpr
240
- collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
241
- when AST::FormatString
242
- expression.parts.each do |part|
243
- collect_proc_captures_from_expression(part.expression, env, local_scopes, captures) if part.is_a?(AST::FormatExprPart)
244
- end
245
- when AST::ProcExpr, AST::TypeRef, AST::FunctionType, AST::ProcType,
246
- AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr,
247
- AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
248
- AST::BooleanLiteral, AST::NullLiteral
249
- nil
250
- else
251
- raise LoweringError.new("unsupported proc capture expression #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
204
+ if (binding = proc_capture_binding(expression.name, env))
205
+ captures[expression.name] ||= { name: expression.name, field_name: expression.name, type: binding[:type] }
252
206
  end
253
- end
254
-
255
- def proc_capture_binding(name, env)
256
- env[:scopes].reverse_each do |scope|
257
- return scope[name] if scope.key?(name)
207
+ when AST::MemberAccess
208
+ collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
209
+ when AST::IndexAccess
210
+ collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
211
+ collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
212
+ when AST::Specialization
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::Call
216
+ collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
217
+ expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
218
+ when AST::UnaryOp
219
+ collect_proc_captures_from_expression(expression.operand, env, local_scopes, captures)
220
+ when AST::BinaryOp
221
+ collect_proc_captures_from_expression(expression.left, env, local_scopes, captures)
222
+ collect_proc_captures_from_expression(expression.right, env, local_scopes, captures)
223
+ when AST::IfExpr
224
+ collect_proc_captures_from_expression(expression.condition, env, local_scopes, captures)
225
+ collect_proc_captures_from_expression(expression.then_expression, env, local_scopes, captures)
226
+ collect_proc_captures_from_expression(expression.else_expression, env, local_scopes, captures)
227
+ when AST::MatchExpr
228
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
229
+ expression.arms.each do |arm|
230
+ collect_proc_captures_from_expression(arm.pattern, env, local_scopes, captures)
231
+ arm_scopes = arm.binding_name ? local_scopes + [{ arm.binding_name => true }] : local_scopes
232
+ collect_proc_captures_from_expression(arm.value, env, arm_scopes, captures)
258
233
  end
259
-
234
+ when AST::UnsafeExpr
235
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
236
+ when AST::AwaitExpr
237
+ collect_proc_captures_from_expression(expression.expression, env, local_scopes, captures)
238
+ when AST::FormatString
239
+ expression.parts.each do |part|
240
+ collect_proc_captures_from_expression(part.expression, env, local_scopes, captures) if part.is_a?(AST::FormatExprPart)
241
+ end
242
+ when AST::ProcExpr, AST::TypeRef, AST::FunctionType, AST::ProcType,
243
+ AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr,
244
+ AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
245
+ AST::BooleanLiteral, AST::NullLiteral
260
246
  nil
247
+ else
248
+ raise LoweringError.new("unsupported proc capture expression #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
261
249
  end
250
+ end
262
251
 
263
- def lower_proc_release_expression(proc_expression, _proc_type)
264
- IR::Call.new(
265
- callee: IR::Member.new(receiver: proc_expression, member: "release", type: proc_release_function_type),
266
- arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
267
- type: @ctx.types.fetch("void"),
268
- )
269
- end
270
-
271
- def lower_proc_retain_expression(proc_expression, _proc_type)
272
- IR::Call.new(
273
- callee: IR::Member.new(receiver: proc_expression, member: "retain", type: proc_retain_function_type),
274
- arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
275
- type: @ctx.types.fetch("void"),
276
- )
277
- end
278
-
279
- def lower_proc_contained_release_statements(value_expression, type)
280
- lower_proc_contained_lifecycle_statements(value_expression, type, :release)
252
+ def proc_capture_binding(name, env)
253
+ env[:scopes].reverse_each do |scope|
254
+ return scope[name] if scope.key?(name)
281
255
  end
282
256
 
283
- # Null-guarded release: safe when value may be zero-initialized (var locals, async frame fields).
284
- # Wraps each proc release in `if (proc.invoke) { proc.release(proc.env); }`.
285
- def lower_proc_contained_guarded_release_statements(value_expression, type)
286
- lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
257
+ nil
258
+ end
259
+
260
+ def lower_proc_release_expression(proc_expression, _proc_type)
261
+ IR::Call.new(
262
+ callee: IR::Member.new(receiver: proc_expression, member: "release", type: proc_release_function_type),
263
+ arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
264
+ type: @ctx.types.fetch("void"),
265
+ )
266
+ end
267
+
268
+ def lower_proc_retain_expression(proc_expression, _proc_type)
269
+ IR::Call.new(
270
+ callee: IR::Member.new(receiver: proc_expression, member: "retain", type: proc_retain_function_type),
271
+ arguments: [IR::Member.new(receiver: proc_expression, member: "env", type: proc_env_pointer_type)],
272
+ type: @ctx.types.fetch("void"),
273
+ )
274
+ end
275
+
276
+ def lower_proc_contained_release_statements(value_expression, type)
277
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release)
278
+ end
279
+
280
+ # Null-guarded release: safe when value may be zero-initialized (var locals, async frame fields).
281
+ # Wraps each proc release in `if (proc.invoke) { proc.release(proc.env); }`.
282
+ def lower_proc_contained_guarded_release_statements(value_expression, type)
283
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
284
+ end
285
+
286
+ # Alias used for async frame fields (always guarded).
287
+ def lower_async_frame_proc_release_statements(value_expression, type)
288
+ lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
289
+ end
290
+
291
+ def lower_proc_contained_retain_statements(value_expression, type)
292
+ lower_proc_contained_lifecycle_statements(value_expression, type, :retain)
293
+ end
294
+
295
+ def lower_proc_contained_lifecycle_statements(value_expression, type, mode, guarded: false)
296
+ return [] unless contains_proc_storage_type?(type)
297
+
298
+ if proc_type?(type)
299
+ if mode == :release && guarded
300
+ invoke_member = IR::Member.new(receiver: value_expression, member: "invoke", type: proc_invoke_function_type(type))
301
+ release_stmt = IR::ExpressionStmt.new(expression: lower_proc_release_expression(value_expression, type))
302
+ return [IR::IfStmt.new(condition: invoke_member, then_body: [release_stmt], else_body: nil)]
303
+ end
304
+ expression = mode == :retain ? lower_proc_retain_expression(value_expression, type) : lower_proc_release_expression(value_expression, type)
305
+ return [IR::ExpressionStmt.new(expression:)]
287
306
  end
288
307
 
289
- # Alias used for async frame fields (always guarded).
290
- def lower_async_frame_proc_release_statements(value_expression, type)
291
- lower_proc_contained_lifecycle_statements(value_expression, type, :release, guarded: true)
292
- end
308
+ case type
309
+ when Types::Struct, Types::StructInstance
310
+ statements = []
311
+ type.fields.each do |field_name, field_type|
312
+ next unless contains_proc_storage_type?(field_type)
293
313
 
294
- def lower_proc_contained_retain_statements(value_expression, type)
295
- lower_proc_contained_lifecycle_statements(value_expression, type, :retain)
314
+ member = IR::Member.new(receiver: value_expression, member: field_name, type: field_type)
315
+ statements.concat(lower_proc_contained_lifecycle_statements(member, field_type, mode, guarded:))
316
+ end
317
+ statements
318
+ when Types::Nullable
319
+ []
320
+ else
321
+ raise LoweringError.new("unsupported proc lifecycle container #{type.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
296
322
  end
323
+ end
297
324
 
298
- def lower_proc_contained_lifecycle_statements(value_expression, type, mode, guarded: false)
299
- return [] unless contains_proc_storage_type?(type)
300
-
301
- if proc_type?(type)
302
- if mode == :release && guarded
303
- invoke_member = IR::Member.new(receiver: value_expression, member: "invoke", type: proc_invoke_function_type(type))
304
- release_stmt = IR::ExpressionStmt.new(expression: lower_proc_release_expression(value_expression, type))
305
- return [IR::IfStmt.new(condition: invoke_member, then_body: [release_stmt], else_body: nil)]
306
- end
307
- expression = mode == :retain ? lower_proc_retain_expression(value_expression, type) : lower_proc_release_expression(value_expression, type)
308
- return [IR::ExpressionStmt.new(expression:)]
309
- end
325
+ # Retain only proc fields that did NOT originate from a fresh proc expression in `original_ast`.
326
+ # Fresh proc expressions already carry refcount=1; retaining them would over-count.
327
+ # For existing proc values (variables, member accesses, return values), we retain to share ownership.
328
+ # When `original_ast` is a struct aggregate literal (AST::Call), fields are matched by name.
329
+ def lower_proc_selective_retain_statements(ir_value, original_ast, type)
330
+ return [] unless contains_proc_storage_type?(type)
310
331
 
311
- case type
312
- when Types::Struct, Types::StructInstance
313
- statements = []
314
- type.fields.each do |field_name, field_type|
315
- next unless contains_proc_storage_type?(field_type)
332
+ if proc_type?(type)
333
+ # If the direct expression is a fresh proc, ownership transfers — no retain needed.
334
+ return [] if expression_contains_proc_expr?(original_ast)
316
335
 
317
- member = IR::Member.new(receiver: value_expression, member: field_name, type: field_type)
318
- statements.concat(lower_proc_contained_lifecycle_statements(member, field_type, mode, guarded:))
319
- end
320
- statements
321
- when Types::Nullable
322
- []
323
- else
324
- raise LoweringError.new("unsupported proc lifecycle container #{type.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
325
- end
336
+ return [IR::ExpressionStmt.new(expression: lower_proc_retain_expression(ir_value, type))]
326
337
  end
327
338
 
328
- # Retain only proc fields that did NOT originate from a fresh proc expression in `original_ast`.
329
- # Fresh proc expressions already carry refcount=1; retaining them would over-count.
330
- # For existing proc values (variables, member accesses, return values), we retain to share ownership.
331
- # When `original_ast` is a struct aggregate literal (AST::Call), fields are matched by name.
332
- def lower_proc_selective_retain_statements(ir_value, original_ast, type)
333
- return [] unless contains_proc_storage_type?(type)
334
-
335
- if proc_type?(type)
336
- # If the direct expression is a fresh proc, ownership transfers — no retain needed.
337
- return [] if expression_contains_proc_expr?(original_ast)
339
+ case type
340
+ when Types::Struct, Types::StructInstance
341
+ statements = []
342
+ type.fields.each do |field_name, field_type|
343
+ next unless contains_proc_storage_type?(field_type)
338
344
 
339
- return [IR::ExpressionStmt.new(expression: lower_proc_retain_expression(ir_value, type))]
340
- end
341
-
342
- case type
343
- when Types::Struct, Types::StructInstance
344
- statements = []
345
- type.fields.each do |field_name, field_type|
346
- next unless contains_proc_storage_type?(field_type)
347
-
348
- # Try to extract the AST sub-expression for this specific field when the source
349
- # is a struct aggregate literal (struct-name(field = value, ...)).
350
- ast_field_source = if original_ast.is_a?(AST::Call)
351
- original_ast.arguments.find { |arg| arg.name == field_name }&.value
352
- end
353
- # Fall back to the whole RHS expression (conservative — treats as existing proc → retains).
354
- ast_field_source ||= original_ast
355
-
356
- member = IR::Member.new(receiver: ir_value, member: field_name, type: field_type)
357
- statements.concat(lower_proc_selective_retain_statements(member, ast_field_source, field_type))
345
+ # Try to extract the AST sub-expression for this specific field when the source
346
+ # is a struct aggregate literal (struct-name(field = value, ...)).
347
+ ast_field_source = if original_ast.is_a?(AST::Call)
348
+ original_ast.arguments.find { |arg| arg.name == field_name }&.value
358
349
  end
359
- statements
360
- when Types::Nullable
361
- []
362
- else
363
- []
364
- end
365
- end
350
+ # Fall back to the whole RHS expression (conservative — treats as existing proc → retains).
351
+ ast_field_source ||= original_ast
366
352
 
367
- def expression_contains_proc_expr?(expression)
368
- return false unless expression
369
-
370
- case expression
371
- when AST::ProcExpr
372
- true
373
- when AST::MemberAccess
374
- expression_contains_proc_expr?(expression.receiver)
375
- when AST::IndexAccess
376
- expression_contains_proc_expr?(expression.receiver) || expression_contains_proc_expr?(expression.index)
377
- when AST::UnaryOp
378
- expression_contains_proc_expr?(expression.operand)
379
- when AST::BinaryOp
380
- expression_contains_proc_expr?(expression.left) || expression_contains_proc_expr?(expression.right)
381
- when AST::IfExpr
382
- expression_contains_proc_expr?(expression.condition) ||
383
- expression_contains_proc_expr?(expression.then_expression) ||
384
- expression_contains_proc_expr?(expression.else_expression)
385
- when AST::UnsafeExpr
386
- expression_contains_proc_expr?(expression.expression)
387
- when AST::AwaitExpr
388
- expression_contains_proc_expr?(expression.expression)
389
- when AST::Call
390
- expression_contains_proc_expr?(expression.callee) || expression.arguments.any? { |argument| expression_contains_proc_expr?(argument.value) }
391
- when AST::Specialization
392
- expression_contains_proc_expr?(expression.callee)
393
- else
394
- false
353
+ member = IR::Member.new(receiver: ir_value, member: field_name, type: field_type)
354
+ statements.concat(lower_proc_selective_retain_statements(member, ast_field_source, field_type))
395
355
  end
356
+ statements
357
+ when Types::Nullable
358
+ []
359
+ else
360
+ []
396
361
  end
397
- def build_proc_noop_release_function(release_linkage_name)
398
- IR::Function.new(
399
- name: release_linkage_name,
400
- linkage_name: release_linkage_name,
401
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
402
- return_type: @ctx.types.fetch("void"),
403
- body: [IR::ReturnStmt.new(value: nil)],
404
- entry_point: false,
405
- )
406
- end
407
-
408
- def build_proc_noop_retain_function(retain_c_name)
409
- IR::Function.new(
410
- name: retain_c_name,
411
- linkage_name: retain_c_name,
412
- params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
413
- return_type: @ctx.types.fetch("void"),
414
- body: [IR::ReturnStmt.new(value: nil)],
415
- entry_point: false,
416
- )
362
+ end
363
+
364
+ def expression_contains_proc_expr?(expression)
365
+ return false unless expression
366
+
367
+ case expression
368
+ when AST::ProcExpr
369
+ true
370
+ when AST::MemberAccess
371
+ expression_contains_proc_expr?(expression.receiver)
372
+ when AST::IndexAccess
373
+ expression_contains_proc_expr?(expression.receiver) || expression_contains_proc_expr?(expression.index)
374
+ when AST::UnaryOp
375
+ expression_contains_proc_expr?(expression.operand)
376
+ when AST::BinaryOp
377
+ expression_contains_proc_expr?(expression.left) || expression_contains_proc_expr?(expression.right)
378
+ when AST::IfExpr
379
+ expression_contains_proc_expr?(expression.condition) ||
380
+ expression_contains_proc_expr?(expression.then_expression) ||
381
+ expression_contains_proc_expr?(expression.else_expression)
382
+ when AST::UnsafeExpr
383
+ expression_contains_proc_expr?(expression.expression)
384
+ when AST::AwaitExpr
385
+ expression_contains_proc_expr?(expression.expression)
386
+ when AST::Call
387
+ expression_contains_proc_expr?(expression.callee) || expression.arguments.any? { |argument| expression_contains_proc_expr?(argument.value) }
388
+ when AST::Specialization
389
+ expression_contains_proc_expr?(expression.callee)
390
+ else
391
+ false
417
392
  end
393
+ end
394
+ def build_proc_noop_release_function(release_linkage_name)
395
+ IR::Function.new(
396
+ name: release_linkage_name,
397
+ linkage_name: release_linkage_name,
398
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
399
+ return_type: @ctx.types.fetch("void"),
400
+ body: [IR::ReturnStmt.new(value: nil)],
401
+ entry_point: false,
402
+ )
403
+ end
404
+
405
+ def build_proc_noop_retain_function(retain_c_name)
406
+ IR::Function.new(
407
+ name: retain_c_name,
408
+ linkage_name: retain_c_name,
409
+ params: [IR::Param.new(name: "env", linkage_name: "__mt_proc_env", type: proc_env_pointer_type, pointer: false)],
410
+ return_type: @ctx.types.fetch("void"),
411
+ body: [IR::ReturnStmt.new(value: nil)],
412
+ entry_point: false,
413
+ )
414
+ end
418
415
  end
419
416
  end