mt-lang 0.2.0

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Lowerer
5
+ class ModuleContext
6
+ attr_accessor :analysis, :current_analysis_path
7
+ attr_accessor :ast, :module_name, :module_prefix, :module_kind, :directives
8
+ attr_accessor :imports, :types, :values, :functions, :interfaces
9
+ attr_accessor :methods, :attributes, :attribute_applications, :implemented_interfaces
10
+ attr_accessor :struct_types, :union_types, :opaque_types
11
+ attr_accessor :current_type_substitutions
12
+ attr_accessor :resolved_expr_types
13
+ attr_accessor :resolved_call_kinds
14
+ attr_accessor :const_values
15
+
16
+ def initialize
17
+ @imports = {}
18
+ @types = {}
19
+ @values = {}
20
+ @functions = {}
21
+ @interfaces = {}
22
+ @struct_types = {}
23
+ @union_types = {}
24
+ @opaque_types = {}
25
+ @methods = {}
26
+ @attributes = {}
27
+ @attribute_applications = {}
28
+ @implemented_interfaces = {}
29
+ @current_type_substitutions = nil
30
+ @resolved_expr_types = {}
31
+ @resolved_call_kinds = {}
32
+ @const_values = {}
33
+ end
34
+
35
+ def install(analysis, module_roots: nil)
36
+ @analysis = analysis
37
+ @ast = analysis.ast
38
+ @module_name = analysis.module_name
39
+ @module_kind = analysis.module_kind
40
+ @imports = analysis.imports
41
+ @types = analysis.types
42
+ @values = analysis.values
43
+ @functions = analysis.functions
44
+ @interfaces = analysis.interfaces
45
+ @methods = analysis.methods
46
+ @attributes = analysis.attributes
47
+ @attribute_applications = analysis.attribute_applications
48
+ @implemented_interfaces = analysis.implemented_interfaces
49
+ @directives = analysis.directives
50
+ @resolved_expr_types = analysis.resolved_expr_types
51
+ @resolved_call_kinds = analysis.resolved_call_kinds
52
+ @const_values = analysis.const_values
53
+ @struct_types = {}
54
+ @union_types = {}
55
+ @opaque_types = {}
56
+ end
57
+
58
+ def save
59
+ {
60
+ analysis: @analysis, current_analysis_path: @current_analysis_path,
61
+ ast: @ast, module_name: @module_name, module_prefix: @module_prefix,
62
+ module_kind: @module_kind, directives: @directives,
63
+ imports: @imports, types: @types, values: @values, functions: @functions,
64
+ interfaces: @interfaces, methods: @methods, attributes: @attributes,
65
+ attribute_applications: @attribute_applications,
66
+ implemented_interfaces: @implemented_interfaces,
67
+ struct_types: @struct_types, union_types: @union_types,
68
+ opaque_types: @opaque_types, current_type_substitutions: @current_type_substitutions,
69
+ resolved_expr_types: @resolved_expr_types,
70
+ resolved_call_kinds: @resolved_call_kinds,
71
+ const_values: @const_values,
72
+ }
73
+ end
74
+
75
+ def restore(saved)
76
+ saved.each { |k, v| send(:"#{k}=", v) }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,419 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LowererProc
5
+ private
6
+
7
+
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)
17
+ 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
+ ),
26
+ )
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
+ end
40
+
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
58
+ 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
+ end
63
+
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"))
70
+
71
+ free_body = []
72
+
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)
76
+
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
81
+
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
+ )
89
+
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
+ )
137
+ end
138
+
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
144
+ 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)
149
+ 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)
182
+ 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
+ else
196
+ raise LoweringError, "unsupported proc capture statement #{statement.class.name}"
197
+ end
198
+ end
199
+
200
+ def collect_proc_captures_from_expression(expression, env, local_scopes, captures)
201
+ return unless expression
202
+
203
+ case expression
204
+ when AST::Identifier
205
+ return if local_scopes.any? { |scope| scope.key?(expression.name) }
206
+
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, "unsupported proc capture expression #{expression.class.name}"
252
+ 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)
258
+ end
259
+
260
+ nil
261
+ end
262
+
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)
281
+ end
282
+
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)
287
+ end
288
+
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
293
+
294
+ def lower_proc_contained_retain_statements(value_expression, type)
295
+ lower_proc_contained_lifecycle_statements(value_expression, type, :retain)
296
+ end
297
+
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
310
+
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)
316
+
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, "unsupported proc lifecycle container #{type.class.name}"
325
+ end
326
+ end
327
+
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)
338
+
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))
358
+ end
359
+ statements
360
+ when Types::Nullable
361
+ []
362
+ else
363
+ []
364
+ end
365
+ end
366
+
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
395
+ end
396
+ 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
+ )
417
+ end
418
+ end
419
+ end