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,1453 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LowererUtils
5
+ private
6
+
7
+
8
+ def range_iterable?(expression)
9
+ range_expr?(expression)
10
+ end
11
+
12
+ def range_start_of(iterable)
13
+ iterable.start_expr
14
+ end
15
+
16
+ def range_end_of(iterable)
17
+ iterable.end_expr
18
+ end
19
+
20
+ def wildcard_arm_pattern?(expression)
21
+ expression.is_a?(AST::Identifier) && expression.name == "_"
22
+ end
23
+
24
+ def variant_match_arm_name_from_pattern(pattern)
25
+ callee = case pattern
26
+ when AST::Call
27
+ pattern.callee
28
+ else
29
+ pattern
30
+ end
31
+ callee.is_a?(AST::MemberAccess) ? callee.member : nil
32
+ end
33
+
34
+ def async_variant_match_arm_binding(arm, scrutinee_expr, scrutinee_type, env:, frame_expr: nil, local_fields: nil)
35
+ arm_env = duplicate_env(env)
36
+ binding_decl = nil
37
+
38
+ if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
39
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
40
+ if arm_name && scrutinee_type.has_payload?(arm_name)
41
+ fields = scrutinee_type.arm(arm_name)
42
+ payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
43
+
44
+ field_key = async_match_binding_field_key(arm)
45
+ field_info = local_fields&.fetch(field_key, nil)
46
+ if field_info && frame_expr
47
+ target = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
48
+ binding_c = async_frame_field_c_name(field_info[:field_name])
49
+ arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
50
+ data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
51
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
52
+ binding_decl = IR::Assignment.new(target:, operator: "=", value: arm_expr)
53
+ else
54
+ data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
55
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
56
+ binding_c = c_local_name(arm.binding_name)
57
+ arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
58
+ binding_decl = IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
59
+ end
60
+ end
61
+ end
62
+
63
+ [arm_env, binding_decl]
64
+ end
65
+
66
+ def bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
67
+ return unless scrutinee_type.is_a?(Types::Variant)
68
+ return unless arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
69
+
70
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
71
+ return unless arm_name && scrutinee_type.has_payload?(arm_name)
72
+
73
+ fields = scrutinee_type.arm(arm_name)
74
+ payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
75
+ arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: c_local_name(arm.binding_name), mutable: true, pointer: false)
76
+ end
77
+
78
+ def array_type?(type)
79
+ type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
80
+ type.arguments[1].is_a?(Types::LiteralTypeArg)
81
+ end
82
+
83
+ def array_element_type(type)
84
+ return unless array_type?(type)
85
+
86
+ type.arguments.first
87
+ end
88
+
89
+ def array_to_span_compatible?(actual_type, expected_type)
90
+ array_type?(actual_type) && expected_type.is_a?(Types::Span) && array_element_type(actual_type) == expected_type.element_type
91
+ end
92
+
93
+ def cstr_trackable_type?(type)
94
+ type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
95
+ end
96
+
97
+ def struct_contains_string_field?(type)
98
+ return false unless type.is_a?(Types::Struct)
99
+
100
+ type.fields.any? { |_name, field_type| cstr_trackable_type?(field_type) || struct_contains_string_field?(field_type) }
101
+ end
102
+
103
+ def suppress_format_releases_for_assignment(cleanups, target_type)
104
+ return cleanups unless cstr_trackable_type?(target_type) || struct_contains_string_field?(target_type)
105
+
106
+ cleanups.reject do |items|
107
+ items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
108
+ end
109
+ end
110
+
111
+ def cstr_list_trackable_type?(type)
112
+ return false unless array_type?(type)
113
+
114
+ element_type = array_element_type(type)
115
+ element_type == @ctx.types.fetch("str") || element_type == @ctx.types.fetch("cstr")
116
+ end
117
+
118
+ def str_buffer_to_span_compatible?(actual_type, expected_type)
119
+ str_buffer_type?(actual_type) && expected_type.is_a?(Types::Span) && expected_type.element_type == @ctx.types.fetch("char")
120
+ end
121
+
122
+ def array_length(type)
123
+ return unless array_type?(type)
124
+
125
+ type.arguments[1].value
126
+ end
127
+
128
+ def char_array_text_type?(type)
129
+ array_type?(type) && array_element_type(type) == @ctx.types.fetch("char")
130
+ end
131
+
132
+ def str_buffer_type?(type)
133
+ type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
134
+ type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
135
+ end
136
+
137
+ def str_buffer_capacity(type)
138
+ type.arguments.first.value
139
+ end
140
+
141
+ def str_buffer_storage_capacity(type)
142
+ str_buffer_capacity(type) + 1
143
+ end
144
+
145
+ def addressable_storage_expression?(expression)
146
+ case expression
147
+ when AST::Identifier
148
+ true
149
+ when AST::MemberAccess, AST::IndexAccess
150
+ addressable_storage_expression?(expression.receiver)
151
+ when AST::Call
152
+ read_call?(expression)
153
+ else
154
+ false
155
+ end
156
+ end
157
+
158
+ def read_call?(expression)
159
+ expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read"
160
+ end
161
+
162
+ def infer_value_type(handle_expression, env:)
163
+ handle_type = infer_expression_type(handle_expression, env:)
164
+ return referenced_type(handle_type) if ref_type?(handle_type)
165
+ return pointee_type(handle_type) if pointer_type?(handle_type)
166
+
167
+ raise LoweringError, "read expects ref[...] or ptr[...], got #{handle_type}"
168
+ end
169
+
170
+ def infer_method_receiver_type(receiver_expression, env:, member_name: nil)
171
+ receiver_type = infer_expression_type(receiver_expression, env:)
172
+ receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
173
+
174
+ if pointer_type?(receiver_type)
175
+ dispatch_receiver_type = method_dispatch_receiver_type(receiver_type)
176
+ return receiver_type if member_name && (@method_definitions.key?([receiver_type, member_name]) || @method_definitions.key?([dispatch_receiver_type, member_name]) || @method_definitions.key?([receiver_type, "static:#{member_name}"]) || @method_definitions.key?([dispatch_receiver_type, "static:#{member_name}"]))
177
+
178
+ return pointee_type(receiver_type)
179
+ end
180
+
181
+ receiver_type
182
+ end
183
+
184
+ def infer_field_receiver_type(receiver_expression, env:)
185
+ receiver_type = infer_expression_type(receiver_expression, env:)
186
+ return referenced_type(receiver_type) if ref_type?(receiver_type)
187
+ return pointee_type(receiver_type) if pointer_type?(receiver_type)
188
+
189
+ receiver_type
190
+ end
191
+
192
+ def collection_loop_type(type)
193
+ super
194
+ end
195
+
196
+ def collection_loop_binding_type(iterable_type, element_type)
197
+ super
198
+ end
199
+
200
+ def collection_loop_ref_element_type?(type)
201
+ super
202
+ end
203
+
204
+ def iterator_loop_info(type, env:)
205
+ iter_name = "__mt_for_iterable__"
206
+ iterator_name = "__mt_for_iterator__"
207
+ probe_env = duplicate_env(env)
208
+ current_actual_scope(probe_env[:scopes])[iter_name] = local_binding(type:, linkage_name: iter_name, mutable: false, pointer: false)
209
+
210
+ iter_call = AST::Call.new(
211
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iter_name), member: "iter"),
212
+ arguments: [],
213
+ )
214
+ iterator_type = infer_expression_type(iter_call, env: probe_env)
215
+
216
+ current_actual_scope(probe_env[:scopes])[iterator_name] = local_binding(type: iterator_type, linkage_name: iterator_name, mutable: true, pointer: false)
217
+ next_call = AST::Call.new(
218
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
219
+ arguments: [],
220
+ )
221
+ item_storage_type = infer_expression_type(next_call, env: probe_env)
222
+ if item_storage_type.is_a?(Types::Nullable) && nullable_iterator_item_type?(item_storage_type.base)
223
+ return {
224
+ kind: :nullable_item,
225
+ iterator_type:,
226
+ item_storage_type:,
227
+ item_type: item_storage_type.base,
228
+ }
229
+ end
230
+
231
+ if item_storage_type == @ctx.types.fetch("bool")
232
+ current_call = AST::Call.new(
233
+ callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
234
+ arguments: [],
235
+ )
236
+ current_type = infer_expression_type(current_call, env: probe_env)
237
+ return {
238
+ kind: :current_item,
239
+ iterator_type:,
240
+ item_storage_type: current_type,
241
+ item_type: current_type,
242
+ }
243
+ end
244
+
245
+ nil
246
+ rescue LoweringError
247
+ nil
248
+ end
249
+
250
+ def nullable_iterator_item_type?(type)
251
+ type == @ctx.types.fetch("cstr") || pointer_type?(type)
252
+ end
253
+
254
+ def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
255
+ if array_type?(iterable_type)
256
+ IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
257
+ else
258
+ data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
259
+ IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
260
+ end
261
+ end
262
+
263
+ def collection_loop_stop_value(iterable_ref, iterable_type)
264
+ if array_type?(iterable_type)
265
+ IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
266
+ else
267
+ IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
268
+ end
269
+ end
270
+
271
+ def lower_fatal_statement(message, env:)
272
+ IR::ExpressionStmt.new(
273
+ expression: lower_expression(
274
+ AST::Call.new(
275
+ callee: AST::Identifier.new(name: "fatal"),
276
+ arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
277
+ ),
278
+ env:,
279
+ expected_type: @ctx.types.fetch("void"),
280
+ ),
281
+ )
282
+ end
283
+
284
+ def infer_range_loop_type(expression, env:)
285
+ start_expr = range_start_of(expression)
286
+ stop_expr = range_end_of(expression)
287
+ start_type = infer_expression_type(start_expr, env:)
288
+ stop_type = infer_expression_type(stop_expr, env:)
289
+
290
+ if start_type != stop_type
291
+ if start_expr.is_a?(AST::IntegerLiteral)
292
+ start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
293
+ elsif stop_expr.is_a?(AST::IntegerLiteral)
294
+ stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
295
+ end
296
+ end
297
+
298
+ raise LoweringError, "range bounds must use matching integer types, got #{start_type} and #{stop_type}" unless start_type == stop_type
299
+
300
+ start_type
301
+ end
302
+
303
+ def integer_type?(type)
304
+ type.is_a?(Types::Primitive) && type.integer?
305
+ end
306
+
307
+ def infer_index_result_type(receiver_type, index_type)
308
+ raise LoweringError, "index must be an integer type, got #{index_type}" unless integer_type?(index_type)
309
+
310
+ receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
311
+
312
+ if array_type?(receiver_type)
313
+ return array_element_type(receiver_type)
314
+ end
315
+
316
+ if receiver_type.is_a?(Types::Span)
317
+ return receiver_type.element_type
318
+ end
319
+
320
+ if receiver_type.is_a?(Types::SoA)
321
+ return receiver_type.element_type
322
+ end
323
+
324
+ if pointer_type?(receiver_type)
325
+ return pointee_type(receiver_type)
326
+ end
327
+
328
+ raise LoweringError, "cannot index #{receiver_type}"
329
+ end
330
+
331
+ def contains_type_var?(type)
332
+ super
333
+ end
334
+
335
+
336
+ def stored_ref_supported_type?(type, visited = {})
337
+ return true unless type
338
+
339
+ visit_key = [type.class, type.object_id]
340
+ return true if visited[visit_key]
341
+
342
+ visited[visit_key] = true
343
+ case type
344
+ when Types::Nullable
345
+ stored_ref_supported_type?(type.base, visited)
346
+ when Types::GenericInstance
347
+ if ref_type?(type)
348
+ lt = ref_lifetime(type)
349
+ return !!lt # lifetime-ref is supported by default; bare ref is not
350
+ end
351
+
352
+ type.arguments.all? { |argument| argument.is_a?(Types::LiteralTypeArg) || stored_ref_supported_type?(argument, visited) }
353
+ when Types::Span
354
+ stored_ref_supported_type?(type.element_type, visited)
355
+ when Types::Task
356
+ stored_ref_supported_type?(type.result_type, visited)
357
+ when Types::StructInstance, Types::VariantInstance
358
+ type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
359
+ when Types::Proc, Types::Function
360
+ callable_param_ref_supported?(type)
361
+ else
362
+ !contains_ref_type?(type)
363
+ end
364
+ end
365
+
366
+ def pointer_to(type)
367
+ Types::Registry.generic_instance("ptr", [type])
368
+ end
369
+
370
+ def with_analysis_context(analysis)
371
+ saved = @ctx.save
372
+ @ctx.install(analysis)
373
+ @ctx.module_prefix = module_c_prefix(@ctx.module_name)
374
+ yield
375
+ ensure
376
+ @ctx.restore(saved)
377
+ end
378
+
379
+ def lookup_value(name, env)
380
+ env[:scopes].reverse_each do |scope|
381
+ return scope[name] if scope.key?(name)
382
+ end
383
+
384
+ if @ctx.values.key?(name)
385
+ binding = @ctx.values.fetch(name)
386
+ {
387
+ type: binding.type,
388
+ storage_type: binding.storage_type,
389
+ linkage_name: value_c_name(name),
390
+ mutable: binding.mutable,
391
+ pointer: false,
392
+ cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
393
+ cstr_list_backed: false,
394
+ const_value: binding.const_value,
395
+ }
396
+ end
397
+ end
398
+
399
+ def lower_static_storage_initializer(expression, env:, expected_type: nil)
400
+ if expected_type && (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), expected_type))
401
+ return literal
402
+ end
403
+
404
+ lower_expression(rewrite_static_storage_initializer(expression), env:, expected_type: expected_type)
405
+ end
406
+
407
+ def lower_compile_time_literal(value, type)
408
+ case value
409
+ when true, false
410
+ return IR::BooleanLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.boolean?
411
+ when Integer
412
+ return IR::IntegerLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.integer?
413
+ return IR::FloatLiteral.new(value: value.to_f, type:) if type.is_a?(Types::Primitive) && type.float?
414
+ when Float
415
+ return IR::FloatLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.float?
416
+ when String
417
+ if type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
418
+ return IR::StringLiteral.new(value:, type:, cstring: type == @ctx.types.fetch("cstr"))
419
+ end
420
+ end
421
+
422
+ nil
423
+ end
424
+
425
+ def compile_time_builtin_function_type(name, arguments, env)
426
+ return_type = case name
427
+ when "field_of"
428
+ @ctx.types.fetch("field_handle")
429
+ when "callable_of"
430
+ @ctx.types.fetch("callable_handle")
431
+ when "has_attribute"
432
+ @ctx.types.fetch("bool")
433
+ when "attribute_of"
434
+ @ctx.types.fetch("attribute_handle")
435
+ else
436
+ nil
437
+ end
438
+ raise LoweringError, "unsupported compile-time builtin #{name}" unless return_type
439
+
440
+ Types::Registry.function(name, params: [], return_type: return_type)
441
+ end
442
+
443
+ def compile_time_builtin_specialization_function_type(callee)
444
+ Types::Registry.function("attribute_arg", params: [], return_type: resolve_type_ref(callee.arguments.fetch(0).value))
445
+ end
446
+
447
+
448
+ def rewrite_static_storage_initializer(expression)
449
+ case expression
450
+ when AST::Identifier
451
+ binding = @ctx.values[expression.name]
452
+ if binding&.kind == :const
453
+ declaration = const_declaration_for_module(@ctx.module_name, expression.name)
454
+ return rewrite_static_storage_initializer(declaration.value)
455
+ end
456
+
457
+ expression
458
+ when AST::MemberAccess
459
+ if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
460
+ imported_module = @ctx.imports.fetch(expression.receiver.name)
461
+ if (binding = imported_module.values[expression.member])&.kind == :const
462
+ imported_analysis = analysis_for_module(imported_module.name)
463
+ declaration = const_declaration_for_module(imported_module.name, expression.member)
464
+ return with_analysis_context(imported_analysis) do
465
+ rewrite_static_storage_initializer(declaration.value)
466
+ end
467
+ end
468
+ end
469
+
470
+ AST::MemberAccess.new(
471
+ receiver: rewrite_static_storage_initializer(expression.receiver),
472
+ member: expression.member,
473
+ )
474
+ when AST::UnaryOp
475
+ AST::UnaryOp.new(operator: expression.operator, operand: rewrite_static_storage_initializer(expression.operand))
476
+ when AST::BinaryOp
477
+ AST::BinaryOp.new(
478
+ operator: expression.operator,
479
+ left: rewrite_static_storage_initializer(expression.left),
480
+ right: rewrite_static_storage_initializer(expression.right),
481
+ )
482
+ when AST::IfExpr
483
+ AST::IfExpr.new(
484
+ condition: rewrite_static_storage_initializer(expression.condition),
485
+ then_expression: rewrite_static_storage_initializer(expression.then_expression),
486
+ else_expression: rewrite_static_storage_initializer(expression.else_expression),
487
+ )
488
+ when AST::UnsafeExpr
489
+ AST::UnsafeExpr.new(expression: rewrite_static_storage_initializer(expression.expression))
490
+ when AST::Call
491
+ AST::Call.new(
492
+ callee: rewrite_static_storage_initializer(expression.callee),
493
+ arguments: expression.arguments.map do |argument|
494
+ AST::Argument.new(name: argument.name, value: rewrite_static_storage_initializer(argument.value))
495
+ end,
496
+ )
497
+ when AST::Specialization
498
+ AST::Specialization.new(
499
+ callee: rewrite_static_storage_initializer(expression.callee),
500
+ arguments: expression.arguments.map { |argument| AST::TypeArgument.new(value: argument.value) },
501
+ )
502
+ else
503
+ expression
504
+ end
505
+ end
506
+
507
+ def local_binding(type:, linkage_name:, mutable:, pointer:, storage_type: nil, projection: nil, cstr_backed: false, cstr_list_backed: false, const_value: nil)
508
+ { type:, storage_type: storage_type || type, linkage_name:, mutable:, pointer:, projection:, cstr_backed:, cstr_list_backed:, const_value: }
509
+ end
510
+
511
+ def callable_type?(type)
512
+ type.is_a?(Types::Function) || proc_type?(type)
513
+ end
514
+
515
+ def contains_proc_storage_type?(type, visited = Set.new)
516
+ return false if visited.include?(type.object_id)
517
+
518
+ case type
519
+ when Types::Proc
520
+ true
521
+ when Types::Struct, Types::StructInstance
522
+ visited.add(type.object_id)
523
+ type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
524
+ when Types::Nullable
525
+ contains_proc_storage_type?(type.base, visited)
526
+ else
527
+ false
528
+ end
529
+ end
530
+
531
+ def contains_task_type?(type, visited = Set.new)
532
+ return false if visited.include?(type.object_id)
533
+
534
+ case type
535
+ when Types::Task
536
+ true
537
+ when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
538
+ visited.add(type.object_id)
539
+ type.fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
540
+ when Types::VariantInstance
541
+ type.arguments.any? { |arg| contains_task_type?(arg, visited) }
542
+ when Types::Variant, Types::GenericVariantDefinition
543
+ visited.add(type.object_id)
544
+ type.arms.each_value.any? do |arm_fields|
545
+ arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
546
+ end
547
+ when Types::GenericInstance
548
+ type.arguments.any? { |arg| contains_task_type?(arg, visited) }
549
+ when Types::Nullable
550
+ contains_task_type?(type.base, visited)
551
+ else
552
+ false
553
+ end
554
+ end
555
+
556
+ def proc_env_pointer_type
557
+ @proc_env_pointer_type ||= pointer_to(@ctx.types.fetch("void"))
558
+ end
559
+
560
+ def proc_invoke_function_type(proc_type)
561
+ Types::Registry.function(
562
+ nil,
563
+ params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
564
+ return_type: proc_type.return_type,
565
+ )
566
+ end
567
+
568
+ def proc_release_function_type
569
+ @proc_release_function_type ||= Types::Registry.function(
570
+ nil,
571
+ params: [Types::Registry.parameter("env", proc_env_pointer_type)],
572
+ return_type: @ctx.types.fetch("void"),
573
+ )
574
+ end
575
+
576
+ def proc_retain_function_type
577
+ @proc_retain_function_type ||= Types::Registry.function(
578
+ nil,
579
+ params: [Types::Registry.parameter("env", proc_env_pointer_type)],
580
+ return_type: @ctx.types.fetch("void"),
581
+ )
582
+ end
583
+
584
+ def fresh_proc_symbol
585
+ @synthetic_proc_counter += 1
586
+ end
587
+
588
+ def current_actual_scope(scopes)
589
+ scopes.reverse_each do |scope|
590
+ return scope unless scope.is_a?(FlowScope)
591
+ end
592
+
593
+ raise LoweringError, "missing lexical scope"
594
+ end
595
+
596
+ def env_with_refinements(env, refinements)
597
+ updated = env.dup
598
+ updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
599
+ updated
600
+ end
601
+
602
+ def scopes_with_refinements(scopes, refinements)
603
+ return scopes if refinements.nil? || refinements.empty?
604
+
605
+ base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
606
+ merged_refinements = scopes.last.is_a?(FlowScope) ? scopes.last.each_with_object({}) { |(name, binding), result| result[name] = binding[:type] } : {}
607
+ merged_refinements = merge_refinements(merged_refinements, refinements)
608
+ flow_scope = FlowScope.new
609
+
610
+ merged_refinements.each do |name, refined_type|
611
+ binding = lookup_value(name, { scopes: base_scopes })
612
+ next unless binding
613
+
614
+ flow_scope[name] = binding.merge(type: refined_type)
615
+ end
616
+
617
+ return base_scopes if flow_scope.empty?
618
+
619
+ base_scopes + [flow_scope]
620
+ end
621
+
622
+ def merge_refinements(existing, incoming)
623
+ merged = existing.dup
624
+ incoming.each do |name, refined_type|
625
+ if merged.key?(name) && merged[name] != refined_type
626
+ merged.delete(name)
627
+ else
628
+ merged[name] = refined_type
629
+ end
630
+ end
631
+
632
+ merged
633
+ end
634
+
635
+ def flow_refinements(expression, truthy:, env:)
636
+ case expression
637
+ when AST::UnaryOp
638
+ return flow_refinements(expression.operand, truthy: !truthy, env:) if expression.operator == "not"
639
+ when AST::BinaryOp
640
+ case expression.operator
641
+ when "and"
642
+ if truthy
643
+ left_truthy = flow_refinements(expression.left, truthy: true, env:)
644
+ right_env = env_with_refinements(env, left_truthy)
645
+ right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
646
+ return merge_refinements(left_truthy, right_truthy)
647
+ end
648
+ when "or"
649
+ unless truthy
650
+ left_falsy = flow_refinements(expression.left, truthy: false, env:)
651
+ right_env = env_with_refinements(env, left_falsy)
652
+ right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
653
+ return merge_refinements(left_falsy, right_falsy)
654
+ end
655
+ when "==", "!="
656
+ return null_test_refinements(expression, truthy:, env:)
657
+ end
658
+ end
659
+
660
+ {}
661
+ end
662
+
663
+ def null_test_refinements(expression, truthy:, env:)
664
+ identifier_expression = nil
665
+ if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
666
+ identifier_expression = expression.left
667
+ elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
668
+ identifier_expression = expression.right
669
+ else
670
+ return {}
671
+ end
672
+
673
+ binding = lookup_value(identifier_expression.name, env)
674
+ return {} unless binding && binding[:storage_type].is_a?(Types::Nullable)
675
+
676
+ null_result = expression.operator == "==" ? truthy : !truthy
677
+ refined_type = null_result ? null_type : binding[:storage_type].base
678
+ { identifier_expression.name => refined_type }
679
+ end
680
+
681
+ def cfg_block_always_terminates?(statements)
682
+ ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
683
+ end
684
+
685
+ def conditional_common_type(then_type, else_type)
686
+ return then_type if then_type == else_type
687
+
688
+ numeric_type = common_numeric_type(then_type, else_type)
689
+ return numeric_type if numeric_type
690
+
691
+ if (nullable_type = conditional_null_common_type(then_type, else_type))
692
+ return nullable_type
693
+ end
694
+
695
+ if (nullable_type = conditional_null_common_type(else_type, then_type))
696
+ return nullable_type
697
+ end
698
+
699
+ return then_type if then_type.is_a?(Types::Nullable) && else_type == then_type.base
700
+ return else_type if else_type.is_a?(Types::Nullable) && then_type == else_type.base
701
+
702
+ nil
703
+ end
704
+
705
+ def if_expression_branch_compatible?(actual_type, expected_type)
706
+ return true if actual_type == expected_type
707
+ return true if null_assignable_to?(actual_type, expected_type)
708
+ return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
709
+ return true if common_numeric_type(actual_type, expected_type) == expected_type
710
+
711
+ false
712
+ end
713
+
714
+ def nullable_candidate?(type)
715
+ !ref_type?(type) && type != @ctx.types.fetch("void")
716
+ end
717
+
718
+ def conditional_null_common_type(null_type, other_type)
719
+ return unless null_type.is_a?(Types::Null)
720
+
721
+ if other_type.is_a?(Types::Nullable)
722
+ return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
723
+
724
+ return nil
725
+ end
726
+
727
+ return unless nullable_candidate?(other_type)
728
+ return if null_type.target_type && null_type.target_type != other_type
729
+
730
+ Types::Registry.nullable(other_type)
731
+ end
732
+
733
+ def null_type
734
+ @null_type ||= Types::Null.new
735
+ end
736
+
737
+ def loop_flow(break_target:, continue_target:, break_defers: [], continue_defers: [])
738
+ {
739
+ break_target:,
740
+ continue_target:,
741
+ break_defers:,
742
+ continue_defers:,
743
+ }
744
+ end
745
+
746
+ def nested_loop_flow(current_loop_flow, local_defers)
747
+ return nil unless current_loop_flow
748
+
749
+ loop_flow(
750
+ break_target: current_loop_flow[:break_target],
751
+ continue_target: current_loop_flow[:continue_target],
752
+ break_defers: current_loop_flow[:break_defers] + local_defers,
753
+ continue_defers: current_loop_flow[:continue_defers] + local_defers,
754
+ )
755
+ end
756
+
757
+ def switch_loop_target(target)
758
+ return target unless target && target[:label]
759
+
760
+ loop_exit_label(target[:label])
761
+ end
762
+
763
+ def switch_loop_flow(current_loop_flow, local_defers)
764
+ nested = nested_loop_flow(current_loop_flow, local_defers)
765
+ return nil unless nested
766
+
767
+ loop_flow(
768
+ break_target: switch_loop_target(nested[:break_target]),
769
+ continue_target: switch_loop_target(nested[:continue_target]),
770
+ break_defers: nested[:break_defers],
771
+ continue_defers: nested[:continue_defers],
772
+ )
773
+ end
774
+
775
+ def cleanup_statements(local_defers, outer_defers)
776
+ local_defers.reverse.flat_map(&:itself) + outer_defers.reverse.flat_map(&:itself)
777
+ end
778
+
779
+ def loop_exit_break(label = nil)
780
+ { kind: :break, label: }
781
+ end
782
+
783
+ def loop_exit_continue(label = nil)
784
+ { kind: :continue, label: }
785
+ end
786
+
787
+ def loop_exit_label(label)
788
+ { kind: :label, label: }
789
+ end
790
+
791
+ def loop_exit_statement(target, local_defers:, outer_defers:)
792
+ case target[:kind]
793
+ when :break
794
+ IR::BreakStmt.new
795
+ when :continue
796
+ IR::ContinueStmt.new
797
+ when :label
798
+ return IR::GotoStmt.new(label: target[:label]) if target[:label]
799
+
800
+ IR::GotoStmt.new(label: target[:label])
801
+ else
802
+ raise LoweringError, "unsupported loop exit target #{target.inspect}"
803
+ end
804
+ end
805
+
806
+ def lower_loop_exit(target, local_defers, outer_defers)
807
+ cleanup = cleanup_statements(local_defers, outer_defers)
808
+ if cleanup.empty?
809
+ [loop_exit_statement(target, local_defers:, outer_defers:)]
810
+ else
811
+ label = target[:label]
812
+ raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
813
+
814
+ cleanup + [IR::GotoStmt.new(label:)]
815
+ end
816
+ end
817
+
818
+ def lower_async_loop_exit(target, local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
819
+ cleanup = lower_async_cleanup_entries(local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
820
+ if cleanup.empty?
821
+ [loop_exit_statement(target, local_defers:, outer_defers:)]
822
+ else
823
+ label = target[:label]
824
+ raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
825
+
826
+ cleanup + [IR::GotoStmt.new(label:)]
827
+ end
828
+ end
829
+
830
+ def contains_label_target?(statements, label)
831
+ statements.any? do |statement|
832
+ case statement
833
+ when IR::GotoStmt
834
+ statement.label == label
835
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
836
+ contains_label_target?(statement.body, label)
837
+ when IR::IfStmt
838
+ contains_label_target?(statement.then_body, label) || (statement.else_body && contains_label_target?(statement.else_body, label))
839
+ when IR::SwitchStmt
840
+ statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
841
+ else
842
+ false
843
+ end
844
+ end
845
+ end
846
+
847
+ def lower_defer_cleanup_expression(expression, env:)
848
+ prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
849
+ expression,
850
+ env:,
851
+ expected_type: infer_expression_type(expression, env:),
852
+ allow_root_statement_foreign: true,
853
+ )
854
+
855
+ lowered = []
856
+ lowered.concat(prepared_setup)
857
+ if (foreign_call = foreign_call_info(prepared_expression, env))
858
+ setup, = lower_foreign_call_statement(
859
+ foreign_call,
860
+ env:,
861
+ expected_type: foreign_call[:binding].type.return_type,
862
+ statement_position: true,
863
+ discard_result: true,
864
+ )
865
+ lowered.concat(setup)
866
+ else
867
+ lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env:))
868
+ end
869
+ lowered.concat(prepared_cleanups.flat_map(&:itself))
870
+ lowered
871
+ end
872
+
873
+ def lower_defer_cleanup_body(statements, env:, return_type:)
874
+ lower_block(statements, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: false)
875
+ end
876
+
877
+ def terminating_ir_statement?(statement)
878
+ statement.is_a?(IR::ReturnStmt) || statement.is_a?(IR::GotoStmt)
879
+ end
880
+
881
+ def empty_env
882
+ { scopes: [{}], counter: { value: 0 } }
883
+ end
884
+
885
+ def snapshot_env(env)
886
+ { scopes: env[:scopes].map(&:dup), counter: env[:counter] }
887
+ end
888
+
889
+ def duplicate_env(env)
890
+ duplicated = env.dup
891
+ duplicated[:scopes] = env[:scopes].map(&:dup) + [{}]
892
+ duplicated[:counter] = env[:counter]
893
+ duplicated.delete(:prepared_expression_cleanups)
894
+ duplicated
895
+ end
896
+
897
+ def let_else_discard_binding_syntax?(statement)
898
+ statement.is_a?(AST::LocalDecl) && statement.else_body && statement.name == "_"
899
+ end
900
+
901
+ def bind_let_else_local?(statement)
902
+ !let_else_discard_binding_syntax?(statement)
903
+ end
904
+
905
+ def async_local_decl_field_key(statement)
906
+ return "__discard_#{statement.line}" if statement.name == "_"
907
+
908
+ statement.name
909
+ end
910
+
911
+ def async_local_decl_field_name(statement)
912
+ return "local_discard_#{statement.line}" if statement.name == "_"
913
+
914
+ "local_#{statement.name}"
915
+ end
916
+
917
+ def async_match_binding_field_key(arm)
918
+ @async_binding_counter ||= 0
919
+ @async_binding_counter += 1
920
+ "match_binding_#{@async_binding_counter}"
921
+ end
922
+
923
+ def async_match_binding_field_name(arm)
924
+ "local_match_binding_#{@async_binding_counter}"
925
+ end
926
+
927
+ def let_else_storage_c_name(statement, env)
928
+ return fresh_c_temp_name(env, "let_else_discard") if let_else_discard_binding_syntax?(statement)
929
+
930
+ return fresh_c_temp_name(env, "_") if statement.name == "_"
931
+
932
+ c_local_name(statement.name)
933
+ end
934
+
935
+ def let_else_success_type(type)
936
+ return type.base if type.is_a?(Types::Nullable)
937
+ return type.arm("some").fetch("value") if option_let_else_type?(type)
938
+ return unless result_let_else_type?(type)
939
+
940
+ type.arm("success").fetch("value")
941
+ end
942
+
943
+ def let_else_error_type(type)
944
+ return unless result_let_else_type?(type)
945
+
946
+ type.arm("failure").fetch("error")
947
+ end
948
+
949
+ def let_else_binding_projection(type)
950
+ return :result_success_value if result_let_else_type?(type)
951
+ return :option_some_value if option_let_else_type?(type)
952
+
953
+ nil
954
+ end
955
+
956
+ def option_let_else_type?(type)
957
+ return false unless type.is_a?(Types::Variant)
958
+
959
+ some_fields = type.arm("some")
960
+ none_fields = type.arm("none")
961
+ some_fields && some_fields.length == 1 && some_fields.key?("value") &&
962
+ none_fields && none_fields.empty?
963
+ end
964
+
965
+ def result_let_else_type?(type)
966
+ return false unless type.is_a?(Types::Variant)
967
+
968
+ success_fields = type.arm("success")
969
+ failure_fields = type.arm("failure")
970
+ success_fields && success_fields.length == 1 && success_fields.key?("value") &&
971
+ failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
972
+ end
973
+
974
+ def let_else_failure_condition(storage_expr, storage_type)
975
+ if storage_type.is_a?(Types::Nullable)
976
+ return IR::Binary.new(
977
+ operator: "==",
978
+ left: storage_expr,
979
+ right: IR::NullLiteral.new(type: storage_type),
980
+ type: @ctx.types.fetch("bool"),
981
+ )
982
+ end
983
+
984
+ if result_let_else_type?(storage_type)
985
+ kind_type = @ctx.types.fetch("int")
986
+ return IR::Binary.new(
987
+ operator: "==",
988
+ left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
989
+ right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
990
+ type: @ctx.types.fetch("bool"),
991
+ )
992
+ end
993
+
994
+ if option_let_else_type?(storage_type)
995
+ kind_type = @ctx.types.fetch("int")
996
+ return IR::Binary.new(
997
+ operator: "==",
998
+ left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
999
+ right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
1000
+ type: @ctx.types.fetch("bool"),
1001
+ )
1002
+ end
1003
+
1004
+ raise LoweringError, "unsupported let-else storage type #{storage_type}"
1005
+ end
1006
+
1007
+ def lower_bound_identifier(binding, expected_type: nil)
1008
+ storage_type = binding[:storage_type]
1009
+ visible_type = binding[:type]
1010
+ projection = binding[:projection]
1011
+
1012
+ if projection == :result_success_value
1013
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1014
+ return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
1015
+ end
1016
+
1017
+ if projection == :result_failure_error
1018
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1019
+ return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
1020
+ end
1021
+
1022
+ if projection == :option_some_value
1023
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1024
+ return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
1025
+ end
1026
+
1027
+ return IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer]) if visible_type == storage_type
1028
+ if storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type
1029
+ name = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1030
+ return name if pointer_like_type?(storage_type.base)
1031
+ return name if expected_type == storage_type
1032
+ return IR::Member.new(receiver: name, member: "value", type: visible_type)
1033
+ end
1034
+
1035
+ if result_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
1036
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1037
+ return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
1038
+ end
1039
+
1040
+ if option_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
1041
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1042
+ return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
1043
+ end
1044
+
1045
+ IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
1046
+ end
1047
+
1048
+ def variant_binding_projection_expression(storage_expr, storage_type, arm_name, field_name, field_type)
1049
+ payload_type = Types::VariantArmPayload.new(storage_type, arm_name, storage_type.arm(arm_name))
1050
+ data_expr = IR::Member.new(receiver: storage_expr, member: "data", type: nil)
1051
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
1052
+ IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
1053
+ end
1054
+
1055
+ def infer_result_propagation_type(expression, env:)
1056
+ _storage_type, success_type, = infer_result_propagation_types(expression, env:)
1057
+
1058
+ success_type
1059
+ end
1060
+
1061
+ def infer_result_propagation_types(expression, env:, allow_void_success: false)
1062
+ storage_type = infer_expression_type(expression.operand, env:)
1063
+ if result_let_else_type?(storage_type)
1064
+ infer_result_propagation_details(storage_type, env:, allow_void_success:)
1065
+ elsif option_let_else_type?(storage_type)
1066
+ infer_option_propagation_details(storage_type, env:, allow_void_success:)
1067
+ else
1068
+ raise LoweringError, "propagation expects Result[T, E] or Option[T], got #{storage_type}"
1069
+ end
1070
+ end
1071
+
1072
+ def infer_result_propagation_details(storage_type, env:, allow_void_success:)
1073
+ success_type = let_else_success_type(storage_type)
1074
+ error_type = let_else_error_type(storage_type)
1075
+ raise LoweringError, "propagation requires a non-void Result success type" if success_type == @ctx.types.fetch("void") && !allow_void_success
1076
+
1077
+ context = env[:return_context]
1078
+ raise LoweringError, "propagation is only allowed inside function and proc bodies" unless context
1079
+ raise LoweringError, "propagation is not allowed inside defer blocks" unless context[:allow_return]
1080
+
1081
+ return_type = context[:return_type]
1082
+ unless result_let_else_type?(return_type)
1083
+ raise LoweringError, "propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}"
1084
+ end
1085
+
1086
+ return_error_type = let_else_error_type(return_type)
1087
+ unless return_error_type == error_type
1088
+ raise LoweringError, "propagation error type #{error_type} must match enclosing Result error type #{return_error_type}"
1089
+ end
1090
+
1091
+ [storage_type, success_type, return_type, error_type]
1092
+ end
1093
+
1094
+ def infer_option_propagation_details(storage_type, env:, allow_void_success:)
1095
+ success_type = let_else_success_type(storage_type)
1096
+ raise LoweringError, "propagation requires a non-void Option success type" if success_type == @ctx.types.fetch("void") && !allow_void_success
1097
+
1098
+ context = env[:return_context]
1099
+ raise LoweringError, "propagation is only allowed inside function and proc bodies" unless context
1100
+ raise LoweringError, "propagation is not allowed inside defer blocks" unless context[:allow_return]
1101
+
1102
+ return_type = context[:return_type]
1103
+ unless option_let_else_type?(return_type)
1104
+ raise LoweringError, "propagation requires enclosing function/proc to return Option[_], got #{return_type}"
1105
+ end
1106
+
1107
+ [storage_type, success_type, return_type, nil]
1108
+ end
1109
+
1110
+ def prepare_result_propagation_for_inline_lowering(expression, env:, allow_void_success: false)
1111
+ storage_type, success_type, return_type, error_type = infer_result_propagation_types(expression, env:, allow_void_success:)
1112
+ is_option = option_let_else_type?(storage_type)
1113
+
1114
+ env[:prepared_expression_cleanups] ||= []
1115
+ cleanup_start = env[:prepared_expression_cleanups].length
1116
+ operand_setup, operand = prepare_expression_for_inline_lowering(expression.operand, env:, expected_type: storage_type)
1117
+ operand_cleanups = env[:prepared_expression_cleanups].drop(cleanup_start)
1118
+
1119
+ result_name = fresh_c_temp_name(env, "propagate")
1120
+ result_ref = IR::Name.new(name: result_name, type: storage_type, pointer: false)
1121
+ return_context = env.fetch(:return_context)
1122
+ failure_return = if storage_type == return_type
1123
+ result_ref
1124
+ elsif is_option
1125
+ IR::VariantLiteral.new(
1126
+ type: return_type,
1127
+ arm_name: "none",
1128
+ fields: [],
1129
+ )
1130
+ else
1131
+ IR::VariantLiteral.new(
1132
+ type: return_type,
1133
+ arm_name: "failure",
1134
+ fields: [
1135
+ IR::AggregateField.new(
1136
+ name: "error",
1137
+ value: variant_binding_projection_expression(result_ref, storage_type, "failure", "error", error_type),
1138
+ ),
1139
+ ],
1140
+ )
1141
+ end
1142
+ failure_cleanup = operand_cleanups.flat_map(&:itself)
1143
+ failure_terminator = if return_context[:async_info]
1144
+ failure_cleanup +
1145
+ lower_async_cleanup_entries(
1146
+ return_context[:local_defers],
1147
+ return_context[:active_defers],
1148
+ frame_expr: return_context.fetch(:frame_expr),
1149
+ raw_frame_expr: return_context.fetch(:raw_frame_expr),
1150
+ async_info: return_context.fetch(:async_info),
1151
+ ) +
1152
+ async_complete_statements(
1153
+ frame_expr: return_context.fetch(:frame_expr),
1154
+ raw_frame_expr: return_context.fetch(:raw_frame_expr),
1155
+ async_info: return_context.fetch(:async_info),
1156
+ value: failure_return,
1157
+ )
1158
+ else
1159
+ failure_cleanup +
1160
+ cleanup_statements(return_context[:local_defers], return_context[:active_defers]) +
1161
+ [IR::ReturnStmt.new(value: failure_return, source_path: @ctx.current_analysis_path)]
1162
+ end
1163
+
1164
+ if success_type == @ctx.types.fetch("void")
1165
+ return [
1166
+ operand_setup + [
1167
+ IR::LocalDecl.new(
1168
+ name: result_name,
1169
+ linkage_name: result_name,
1170
+ type: storage_type,
1171
+ value: lower_contextual_expression(operand, env:, expected_type: storage_type),
1172
+ ),
1173
+ IR::IfStmt.new(
1174
+ condition: let_else_failure_condition(result_ref, storage_type),
1175
+ then_body: failure_terminator,
1176
+ else_body: nil,
1177
+ ),
1178
+ ],
1179
+ nil,
1180
+ ]
1181
+ end
1182
+
1183
+ projection = is_option ? :option_some_value : :result_success_value
1184
+ register_prepared_temp!(env, result_name, success_type, storage_type:, projection:)
1185
+
1186
+ [
1187
+ operand_setup + [
1188
+ IR::LocalDecl.new(
1189
+ name: result_name,
1190
+ linkage_name: result_name,
1191
+ type: storage_type,
1192
+ value: lower_contextual_expression(operand, env:, expected_type: storage_type),
1193
+ ),
1194
+ IR::IfStmt.new(
1195
+ condition: let_else_failure_condition(result_ref, storage_type),
1196
+ then_body: failure_terminator,
1197
+ else_body: nil,
1198
+ ),
1199
+ ],
1200
+ AST::Identifier.new(name: result_name),
1201
+ ]
1202
+ end
1203
+
1204
+ def c_type_name(type)
1205
+ if type.is_a?(Types::Nullable)
1206
+ return "nullable_#{c_type_name(type.base)}"
1207
+ end
1208
+
1209
+ if type.respond_to?(:linkage_name) && type.linkage_name
1210
+ return type.linkage_name
1211
+ end
1212
+
1213
+ if type.is_a?(Types::GenericInstance)
1214
+ base = if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1215
+ type.name
1216
+ elsif type.respond_to?(:module_name) && !type.module_name.nil?
1217
+ "#{module_c_prefix(type.module_name)}_#{type.name}"
1218
+ else
1219
+ type.name
1220
+ end
1221
+
1222
+ return "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1223
+ end
1224
+
1225
+ return type.name if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1226
+
1227
+ base = (type.respond_to?(:module_name) && type.module_name) ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
1228
+ return base unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
1229
+
1230
+ "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1231
+ end
1232
+
1233
+ def opaque_c_type_name(type)
1234
+ type.linkage_name || c_type_name(type)
1235
+ end
1236
+
1237
+ def opaque_forward_declarable?(type)
1238
+ return false unless opaque_c_type_name(type).match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
1239
+
1240
+ !type.external || type.linkage_name.nil?
1241
+ end
1242
+
1243
+ def forward_declarable_external_opaque?(type)
1244
+ type.external && opaque_forward_declarable?(type)
1245
+ end
1246
+
1247
+ def validate_generic_type!(name, arguments)
1248
+ super(name, arguments) { |msg| raise LoweringError, msg }
1249
+ end
1250
+
1251
+ def integer_type_argument?(argument)
1252
+ argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
1253
+ end
1254
+
1255
+ def generic_integer_type_argument?(argument)
1256
+ integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
1257
+ end
1258
+
1259
+ def enum_member_c_name(type, member_name)
1260
+ "#{c_type_name(type)}_#{member_name}"
1261
+ end
1262
+
1263
+ def function_binding_c_name(binding, module_name:, receiver_type: nil)
1264
+ if receiver_type.nil? && binding.name == "main" && binding.type_arguments.empty?
1265
+ return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
1266
+ end
1267
+ if receiver_type
1268
+ base = "#{c_type_name(receiver_type)}_#{binding.name}"
1269
+ base = "#{base}_static" if binding.type.receiver_type.nil?
1270
+ return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
1271
+ end
1272
+
1273
+ module_function_c_name(module_name, binding.name, type_arguments: binding.type_arguments)
1274
+ end
1275
+
1276
+ def external_function_c_name(binding)
1277
+ return binding.ast.mapping.value if binding.external && binding.ast.is_a?(AST::ExternFunctionDecl) && binding.ast.mapping
1278
+
1279
+ binding.name
1280
+ end
1281
+
1282
+ def value_c_name(name)
1283
+ module_value_c_name(@ctx.module_name, name)
1284
+ end
1285
+
1286
+ def imported_value_c_name(imported_module, name)
1287
+ imported_analysis = analysis_for_module(imported_module.name)
1288
+ return name if imported_analysis.module_kind == :raw_module
1289
+
1290
+ module_value_c_name(imported_module.name, name)
1291
+ end
1292
+
1293
+ def module_function_c_name(module_name, name, type_arguments: [])
1294
+ base = "#{module_c_prefix(module_name)}_#{name}"
1295
+ return base if type_arguments.empty?
1296
+
1297
+ # A double-underscore separates a generic function instance's type
1298
+ # arguments so it cannot collide with a distinct regular function whose
1299
+ # name happens to be `<name>_<typearg>` (e.g. the instance
1300
+ # `expect_equal[str]` vs the function `expect_equal_str`). The method path
1301
+ # in `function_binding_c_name` uses the same scheme for consistency.
1302
+ "#{base}__#{generic_type_argument_suffix(type_arguments)}"
1303
+ end
1304
+
1305
+ # Joins resolved generic type arguments into the instance-name suffix used
1306
+ # by both the free-function (`module_function_c_name`) and method
1307
+ # (`function_binding_c_name`) paths, keeping the scheme consistent.
1308
+ def generic_type_argument_suffix(type_arguments)
1309
+ sanitize_identifier(type_arguments.join('_'))
1310
+ end
1311
+
1312
+ def module_value_c_name(module_name, name)
1313
+ "#{module_c_prefix(module_name)}_#{name}"
1314
+ end
1315
+
1316
+ def module_c_prefix(module_name)
1317
+ sanitize_identifier(module_name.to_s.tr('.', '_'))
1318
+ end
1319
+
1320
+ def c_local_name(name)
1321
+ return "value" unless name
1322
+ identifier = sanitize_identifier(name)
1323
+ return "#{identifier}_" if c_reserved_identifier?(identifier)
1324
+
1325
+ identifier
1326
+ end
1327
+
1328
+ def c_reserved_identifier?(identifier)
1329
+ %w[
1330
+ auto break case char const continue default do double else enum extern
1331
+ float for goto if inline int long register restrict return short signed
1332
+ sizeof static struct switch typedef union unsigned void volatile while
1333
+ _Alignas _Alignof _Atomic _Bool _Complex _Generic _Imaginary _Noreturn
1334
+ _Static_assert _Thread_local
1335
+ ].include?(identifier)
1336
+ end
1337
+
1338
+ def fresh_c_temp_name(env, prefix)
1339
+ env[:counter][:value] += 1
1340
+ "__mt_#{prefix}_#{env[:counter][:value]}"
1341
+ end
1342
+
1343
+ def cleanup_safe_return_expression?(expression)
1344
+ case expression
1345
+ when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
1346
+ true
1347
+ else
1348
+ false
1349
+ end
1350
+ end
1351
+
1352
+ def sanitize_identifier(text)
1353
+ return "value" unless text
1354
+
1355
+ cache = (@sanitize_identifier_cache ||= {})
1356
+ cached = cache[text]
1357
+ return cached if cached
1358
+
1359
+ identifier = text.gsub(/[^A-Za-z0-9_]+/, "_").gsub(/_+/, "_").sub(/_+$/, "").sub(/^_{2,}/, "_")
1360
+ cache[text] = identifier.empty? ? "value" : identifier
1361
+ end
1362
+
1363
+ def lower_assignment_target(expression, env:)
1364
+ case expression
1365
+ when AST::Identifier
1366
+ binding = lookup_value(expression.name, env)
1367
+ lower_assignment_binding_target(binding)
1368
+ when AST::MemberAccess
1369
+ if expression.receiver.is_a?(AST::IndexAccess)
1370
+ base_type = infer_expression_type(expression.receiver.receiver, env:)
1371
+ if base_type.is_a?(Types::SoA)
1372
+ soa_base = lower_expression(expression.receiver.receiver, env:)
1373
+ index = lower_expression(expression.receiver.index, env:)
1374
+ field_type = base_type.fields[expression.member]
1375
+ target_type = infer_expression_type(expression, env:)
1376
+ return IR::Index.new(
1377
+ receiver: IR::Member.new(receiver: soa_base, member: expression.member, type: field_type),
1378
+ index:,
1379
+ type: target_type,
1380
+ )
1381
+ end
1382
+ end
1383
+ receiver_type = infer_expression_type(expression.receiver, env:)
1384
+ receiver = lower_expression(expression.receiver, env:)
1385
+ type = infer_expression_type(expression, env:)
1386
+ IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
1387
+ when AST::IndexAccess
1388
+ receiver_type = infer_expression_type(expression.receiver, env:)
1389
+ receiver = lower_expression(expression.receiver, env:)
1390
+ index = lower_expression(expression.index, env:)
1391
+ type = infer_expression_type(expression, env:)
1392
+ if array_type?(receiver_type)
1393
+ IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1394
+ elsif receiver_type.is_a?(Types::Span)
1395
+ IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1396
+ else
1397
+ IR::Index.new(receiver:, index:, type:)
1398
+ end
1399
+ when AST::Call
1400
+ if read_call?(expression)
1401
+ type = infer_expression_type(expression, env:)
1402
+ operand = lower_expression(expression.arguments.first.value, env:)
1403
+ return IR::Unary.new(operator: "*", operand:, type:)
1404
+ end
1405
+
1406
+ raise LoweringError, "unsupported assignment target #{expression.class.name}"
1407
+ else
1408
+ raise LoweringError, "unsupported assignment target #{expression.class.name}"
1409
+ end
1410
+ end
1411
+
1412
+ def lower_assignment_binding_target(binding)
1413
+ storage_type = binding[:storage_type]
1414
+ visible_type = binding[:type]
1415
+ storage_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1416
+
1417
+ case binding[:projection]
1418
+ when :result_success_value
1419
+ variant_binding_projection_expression(storage_ref, storage_type, "success", "value", visible_type)
1420
+ when :option_some_value
1421
+ variant_binding_projection_expression(storage_ref, storage_type, "some", "value", visible_type)
1422
+ else
1423
+ if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
1424
+ IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
1425
+ else
1426
+ storage_ref
1427
+ end
1428
+ end
1429
+ end
1430
+
1431
+ def wrap_nullable_field_value(field_type, lowered_value, env)
1432
+ return lowered_value unless field_type.is_a?(Types::Nullable)
1433
+ return lowered_value if pointer_like_type?(field_type.base)
1434
+ return lowered_value if lowered_value.type.is_a?(Types::Nullable)
1435
+
1436
+ nullable_some_literal(field_type, lowered_value)
1437
+ end
1438
+
1439
+ def nullable_some_literal(nullable_type, value)
1440
+ IR::AggregateLiteral.new(
1441
+ type: nullable_type,
1442
+ fields: [
1443
+ IR::AggregateField.new(name: "has_value", value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool"))),
1444
+ IR::AggregateField.new(name: "value", value:),
1445
+ ],
1446
+ )
1447
+ end
1448
+
1449
+ def pointer_like_type?(type)
1450
+ pointer_type?(type) || own_type?(type) || (type.is_a?(Types::Primitive) && type.name == "cstr") || type.is_a?(Types::Function) || type.is_a?(Types::Proc) || type.is_a?(Types::Opaque)
1451
+ end
1452
+ end
1453
+ end