mt-lang 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -2,1256 +2,1252 @@
2
2
 
3
3
  module MilkTea
4
4
  module LowererUtils
5
- private
5
+ def range_iterable?(expression)
6
+ range_expr?(expression)
7
+ end
6
8
 
7
-
8
- def range_iterable?(expression)
9
- range_expr?(expression)
9
+ def lower_range_match_condition(range_pattern, scrutinee_ir, bool_type, env:)
10
+ start_ir = lower_expression(range_pattern.start_expr, env:, expected_type: nil)
11
+ end_ir = lower_expression(range_pattern.end_expr, env:, expected_type: nil)
12
+ ge = IR::Binary.new(operator: ">=", left: scrutinee_ir, right: start_ir, type: bool_type)
13
+ le = IR::Binary.new(operator: "<=", left: scrutinee_ir, right: end_ir, type: bool_type)
14
+ IR::Binary.new(operator: "and", left: ge, right: le, type: bool_type)
15
+ end
16
+
17
+ def range_start_of(iterable)
18
+ iterable.start_expr
19
+ end
20
+
21
+ def range_end_of(iterable)
22
+ iterable.end_expr
23
+ end
24
+
25
+ def wildcard_arm_pattern?(expression)
26
+ expression.is_a?(AST::Identifier) && expression.name == "_"
27
+ end
28
+
29
+ def variant_match_arm_name_from_pattern(pattern)
30
+ callee = case pattern
31
+ when AST::Call
32
+ pattern.callee
33
+ else
34
+ pattern
35
+ end
36
+ callee.is_a?(AST::MemberAccess) ? callee.member : nil
37
+ end
38
+
39
+ def array_type?(type)
40
+ type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
41
+ type.arguments[1].is_a?(Types::LiteralTypeArg)
42
+ end
43
+
44
+ def simd_type?(type)
45
+ type.is_a?(Types::Simd)
46
+ end
47
+
48
+ def array_element_type(type)
49
+ return unless array_type?(type)
50
+
51
+ type.arguments.first
52
+ end
53
+
54
+ def array_to_span_compatible?(actual_type, expected_type)
55
+ array_type?(actual_type) && expected_type.is_a?(Types::Span) && array_element_type(actual_type) == expected_type.element_type
56
+ end
57
+
58
+ def cstr_trackable_type?(type)
59
+ type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
60
+ end
61
+
62
+ def struct_contains_string_field?(type)
63
+ return false unless type.is_a?(Types::Struct)
64
+
65
+ type.fields.any? { |_name, field_type| cstr_trackable_type?(field_type) || struct_contains_string_field?(field_type) }
66
+ end
67
+
68
+ def suppress_format_releases_for_assignment(cleanups, target_type)
69
+ return cleanups unless cstr_trackable_type?(target_type) || struct_contains_string_field?(target_type)
70
+
71
+ cleanups.reject do |items|
72
+ items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
73
+ end
74
+ end
75
+
76
+ def cstr_list_trackable_type?(type)
77
+ return false unless array_type?(type)
78
+
79
+ element_type = array_element_type(type)
80
+ element_type == @ctx.types.fetch("str") || element_type == @ctx.types.fetch("cstr")
81
+ end
82
+
83
+ def str_buffer_to_span_compatible?(actual_type, expected_type)
84
+ str_buffer_type?(actual_type) && expected_type.is_a?(Types::Span) && expected_type.element_type == @ctx.types.fetch("char")
85
+ end
86
+
87
+ def array_length(type)
88
+ return unless array_type?(type)
89
+
90
+ type.arguments[1].value
91
+ end
92
+
93
+ def char_array_text_type?(type)
94
+ array_type?(type) && array_element_type(type) == @ctx.types.fetch("char")
95
+ end
96
+
97
+ def str_buffer_type?(type)
98
+ type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
99
+ type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
100
+ end
101
+
102
+ def str_buffer_capacity(type)
103
+ type.arguments.first.value
104
+ end
105
+
106
+ def str_buffer_storage_capacity(type)
107
+ str_buffer_capacity(type) + 1
108
+ end
109
+
110
+ def addressable_storage_expression?(expression)
111
+ case expression
112
+ when AST::Identifier
113
+ true
114
+ when AST::MemberAccess, AST::IndexAccess
115
+ addressable_storage_expression?(expression.receiver)
116
+ when AST::Call
117
+ read_call?(expression)
118
+ else
119
+ false
10
120
  end
121
+ end
11
122
 
12
- def lower_range_match_condition(range_pattern, scrutinee_ir, bool_type, env:)
13
- start_ir = lower_expression(range_pattern.start_expr, env:, expected_type: nil)
14
- end_ir = lower_expression(range_pattern.end_expr, env:, expected_type: nil)
15
- ge = IR::Binary.new(operator: ">=", left: scrutinee_ir, right: start_ir, type: bool_type)
16
- le = IR::Binary.new(operator: "<=", left: scrutinee_ir, right: end_ir, type: bool_type)
17
- IR::Binary.new(operator: "and", left: ge, right: le, type: bool_type)
18
- end
123
+ def read_call?(expression)
124
+ expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read"
125
+ end
19
126
 
20
- def range_start_of(iterable)
21
- iterable.start_expr
22
- end
127
+ def infer_value_type(handle_expression, env:)
128
+ handle_type = infer_expression_type(handle_expression, env:)
129
+ return referenced_type(handle_type) if ref_type?(handle_type)
130
+ return pointee_type(handle_type) if pointer_type?(handle_type)
23
131
 
24
- def range_end_of(iterable)
25
- iterable.end_expr
26
- end
132
+ raise LoweringError.new("read expects ref[...] or ptr[...], got #{handle_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
133
+ end
27
134
 
28
- def wildcard_arm_pattern?(expression)
29
- expression.is_a?(AST::Identifier) && expression.name == "_"
30
- end
135
+ def infer_method_receiver_type(receiver_expression, env:, member_name: nil)
136
+ receiver_type = infer_expression_type(receiver_expression, env:)
137
+ receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
31
138
 
32
- def variant_match_arm_name_from_pattern(pattern)
33
- callee = case pattern
34
- when AST::Call
35
- pattern.callee
36
- else
37
- pattern
38
- end
39
- callee.is_a?(AST::MemberAccess) ? callee.member : nil
40
- end
139
+ if pointer_type?(receiver_type)
140
+ dispatch_receiver_type = method_dispatch_receiver_type(receiver_type)
141
+ 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}"]))
41
142
 
42
- def array_type?(type)
43
- type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
44
- type.arguments[1].is_a?(Types::LiteralTypeArg)
143
+ return pointee_type(receiver_type)
45
144
  end
46
145
 
47
- def simd_type?(type)
48
- type.is_a?(Types::Simd)
49
- end
146
+ receiver_type
147
+ end
50
148
 
51
- def array_element_type(type)
52
- return unless array_type?(type)
149
+ def infer_field_receiver_type(receiver_expression, env:)
150
+ receiver_type = infer_expression_type(receiver_expression, env:)
151
+ return referenced_type(receiver_type) if ref_type?(receiver_type)
152
+ return pointee_type(receiver_type) if pointer_type?(receiver_type)
53
153
 
54
- type.arguments.first
55
- end
154
+ receiver_type
155
+ end
56
156
 
57
- def array_to_span_compatible?(actual_type, expected_type)
58
- array_type?(actual_type) && expected_type.is_a?(Types::Span) && array_element_type(actual_type) == expected_type.element_type
59
- end
157
+ def collection_loop_type(type)
158
+ super
159
+ end
60
160
 
61
- def cstr_trackable_type?(type)
62
- type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
63
- end
161
+ def collection_loop_binding_type(iterable_type, element_type)
162
+ super
163
+ end
64
164
 
65
- def struct_contains_string_field?(type)
66
- return false unless type.is_a?(Types::Struct)
165
+ def collection_loop_ref_element_type?(type)
166
+ super
167
+ end
67
168
 
68
- type.fields.any? { |_name, field_type| cstr_trackable_type?(field_type) || struct_contains_string_field?(field_type) }
169
+ def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
170
+ if array_type?(iterable_type)
171
+ IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
172
+ else
173
+ data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
174
+ IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
69
175
  end
176
+ end
70
177
 
71
- def suppress_format_releases_for_assignment(cleanups, target_type)
72
- return cleanups unless cstr_trackable_type?(target_type) || struct_contains_string_field?(target_type)
178
+ def collection_loop_stop_value(iterable_ref, iterable_type)
179
+ if array_type?(iterable_type)
180
+ IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
181
+ else
182
+ IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
183
+ end
184
+ end
73
185
 
74
- cleanups.reject do |items|
75
- items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
186
+ def lower_fatal_statement(message, env:)
187
+ IR::ExpressionStmt.new(
188
+ expression: lower_expression(
189
+ AST::Call.new(
190
+ callee: AST::Identifier.new(name: "fatal"),
191
+ arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
192
+ ),
193
+ env:,
194
+ expected_type: @ctx.types.fetch("void"),
195
+ ),
196
+ )
197
+ end
198
+
199
+ def infer_range_loop_type(expression, env:)
200
+ start_expr = range_start_of(expression)
201
+ stop_expr = range_end_of(expression)
202
+ start_type = infer_expression_type(start_expr, env:)
203
+ stop_type = infer_expression_type(stop_expr, env:)
204
+
205
+ if start_type != stop_type
206
+ if start_expr.is_a?(AST::IntegerLiteral)
207
+ start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
208
+ elsif stop_expr.is_a?(AST::IntegerLiteral)
209
+ stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
76
210
  end
77
211
  end
78
212
 
79
- def cstr_list_trackable_type?(type)
80
- return false unless array_type?(type)
213
+ raise LoweringError.new("range bounds must use matching integer types, got #{start_type} and #{stop_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless start_type == stop_type
81
214
 
82
- element_type = array_element_type(type)
83
- element_type == @ctx.types.fetch("str") || element_type == @ctx.types.fetch("cstr")
84
- end
215
+ start_type
216
+ end
85
217
 
86
- def str_buffer_to_span_compatible?(actual_type, expected_type)
87
- str_buffer_type?(actual_type) && expected_type.is_a?(Types::Span) && expected_type.element_type == @ctx.types.fetch("char")
88
- end
218
+ def integer_type?(type)
219
+ type.is_a?(Types::Primitive) && type.integer?
220
+ end
89
221
 
90
- def array_length(type)
91
- return unless array_type?(type)
222
+ def infer_index_result_type(receiver_type, index_type)
223
+ raise LoweringError.new("index must be an integer type, got #{index_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless integer_type?(index_type)
92
224
 
93
- type.arguments[1].value
94
- end
225
+ receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
95
226
 
96
- def char_array_text_type?(type)
97
- array_type?(type) && array_element_type(type) == @ctx.types.fetch("char")
227
+ if array_type?(receiver_type)
228
+ return array_element_type(receiver_type)
98
229
  end
99
230
 
100
- def str_buffer_type?(type)
101
- type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
102
- type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
231
+ if receiver_type.is_a?(Types::Span)
232
+ return receiver_type.element_type
103
233
  end
104
234
 
105
- def str_buffer_capacity(type)
106
- type.arguments.first.value
235
+ if receiver_type.is_a?(Types::SoA)
236
+ return receiver_type.element_type
107
237
  end
108
238
 
109
- def str_buffer_storage_capacity(type)
110
- str_buffer_capacity(type) + 1
239
+ if simd_type?(receiver_type)
240
+ return receiver_type.element_type
111
241
  end
112
242
 
113
- def addressable_storage_expression?(expression)
114
- case expression
115
- when AST::Identifier
116
- true
117
- when AST::MemberAccess, AST::IndexAccess
118
- addressable_storage_expression?(expression.receiver)
119
- when AST::Call
120
- read_call?(expression)
121
- else
122
- false
123
- end
243
+ if pointer_type?(receiver_type)
244
+ return pointee_type(receiver_type)
124
245
  end
125
246
 
126
- def read_call?(expression)
127
- expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read"
128
- end
247
+ raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
248
+ end
129
249
 
130
- def infer_value_type(handle_expression, env:)
131
- handle_type = infer_expression_type(handle_expression, env:)
132
- return referenced_type(handle_type) if ref_type?(handle_type)
133
- return pointee_type(handle_type) if pointer_type?(handle_type)
250
+ def contains_type_var?(type)
251
+ super
252
+ end
134
253
 
135
- raise LoweringError.new("read expects ref[...] or ptr[...], got #{handle_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
136
- end
137
254
 
138
- def infer_method_receiver_type(receiver_expression, env:, member_name: nil)
139
- receiver_type = infer_expression_type(receiver_expression, env:)
140
- receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
255
+ def stored_ref_supported_type?(type, visited = {})
256
+ return true unless type
141
257
 
142
- if pointer_type?(receiver_type)
143
- dispatch_receiver_type = method_dispatch_receiver_type(receiver_type)
144
- 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}"]))
258
+ visit_key = [type.class, type.object_id]
259
+ return true if visited[visit_key]
145
260
 
146
- return pointee_type(receiver_type)
261
+ visited[visit_key] = true
262
+ case type
263
+ when Types::Nullable
264
+ stored_ref_supported_type?(type.base, visited)
265
+ when Types::GenericInstance
266
+ if ref_type?(type)
267
+ lt = ref_lifetime(type)
268
+ return !!lt # lifetime-ref is supported by default; bare ref is not
147
269
  end
148
270
 
149
- receiver_type
150
- end
151
-
152
- def infer_field_receiver_type(receiver_expression, env:)
153
- receiver_type = infer_expression_type(receiver_expression, env:)
154
- return referenced_type(receiver_type) if ref_type?(receiver_type)
155
- return pointee_type(receiver_type) if pointer_type?(receiver_type)
156
-
157
- receiver_type
158
- end
159
-
160
- def collection_loop_type(type)
161
- super
271
+ type.arguments.all? { |argument| argument.is_a?(Types::LiteralTypeArg) || stored_ref_supported_type?(argument, visited) }
272
+ when Types::Span
273
+ stored_ref_supported_type?(type.element_type, visited)
274
+ when Types::Task
275
+ stored_ref_supported_type?(type.result_type, visited)
276
+ when Types::StructInstance, Types::VariantInstance
277
+ type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
278
+ when Types::Proc, Types::Function
279
+ callable_param_ref_supported?(type)
280
+ else
281
+ !contains_ref_type?(type)
282
+ end
283
+ end
284
+
285
+ def pointer_to(type)
286
+ Types::Registry.generic_instance("ptr", [type])
287
+ end
288
+
289
+ def with_analysis_context(analysis)
290
+ saved = @ctx.save
291
+ @ctx.install(analysis)
292
+ @ctx.module_prefix = module_c_prefix(@ctx.module_name)
293
+ yield
294
+ ensure
295
+ @ctx.restore(saved)
296
+ end
297
+
298
+ def lookup_value(name, env)
299
+ env[:scopes].reverse_each do |scope|
300
+ return scope[name] if scope.key?(name)
301
+ end
302
+
303
+ if @ctx.values.key?(name)
304
+ binding = @ctx.values.fetch(name)
305
+ {
306
+ type: binding.type,
307
+ storage_type: binding.storage_type,
308
+ linkage_name: value_c_name(name),
309
+ mutable: binding.mutable,
310
+ pointer: false,
311
+ cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
312
+ cstr_list_backed: false,
313
+ const_value: binding.const_value,
314
+ }
162
315
  end
316
+ end
163
317
 
164
- def collection_loop_binding_type(iterable_type, element_type)
165
- super
318
+ def lower_static_storage_initializer(expression, env:, expected_type: nil)
319
+ if expected_type && (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), expected_type))
320
+ return literal
166
321
  end
167
322
 
168
- def collection_loop_ref_element_type?(type)
169
- super
170
- end
323
+ lower_expression(rewrite_static_storage_initializer(expression), env:, expected_type: expected_type)
324
+ end
171
325
 
172
- def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
173
- if array_type?(iterable_type)
174
- IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
175
- else
176
- data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
177
- IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
326
+ def lower_compile_time_literal(value, type)
327
+ case value
328
+ when true, false
329
+ return IR::BooleanLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.boolean?
330
+ when Integer
331
+ return IR::IntegerLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.integer?
332
+ return IR::FloatLiteral.new(value: value.to_f, type:) if type.is_a?(Types::Primitive) && type.float?
333
+ when Float
334
+ return IR::FloatLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.float?
335
+ when String
336
+ if type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
337
+ return IR::StringLiteral.new(value:, type:, cstring: type == @ctx.types.fetch("cstr"))
178
338
  end
179
- end
180
-
181
- def collection_loop_stop_value(iterable_ref, iterable_type)
182
- if array_type?(iterable_type)
183
- IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
184
- else
185
- IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
339
+ when Hash
340
+ return nil unless type.is_a?(Types::Struct)
341
+ fields = value.map do |name, field_value|
342
+ field_type = type.field(name)
343
+ return nil unless field_type
344
+ lowered = lower_compile_time_literal(field_value, field_type)
345
+ return nil unless lowered
346
+ IR::AggregateField.new(name:, value: lowered)
186
347
  end
348
+ return IR::AggregateLiteral.new(type:, fields:)
349
+ end
350
+
351
+ nil
352
+ end
353
+
354
+ def compile_time_builtin_function_type(name, arguments, env)
355
+ return_type = case name
356
+ when "field_of"
357
+ @ctx.types.fetch("field_handle")
358
+ when "callable_of"
359
+ @ctx.types.fetch("callable_handle")
360
+ when "has_attribute"
361
+ @ctx.types.fetch("bool")
362
+ when "attribute_of"
363
+ @ctx.types.fetch("attribute_handle")
364
+ else
365
+ nil
187
366
  end
367
+ raise LoweringError.new("unsupported compile-time builtin #{name}", line: 0, column: 0, path: @ctx.current_analysis_path) unless return_type
188
368
 
189
- def lower_fatal_statement(message, env:)
190
- IR::ExpressionStmt.new(
191
- expression: lower_expression(
192
- AST::Call.new(
193
- callee: AST::Identifier.new(name: "fatal"),
194
- arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
195
- ),
196
- env:,
197
- expected_type: @ctx.types.fetch("void"),
198
- ),
199
- )
200
- end
201
-
202
- def infer_range_loop_type(expression, env:)
203
- start_expr = range_start_of(expression)
204
- stop_expr = range_end_of(expression)
205
- start_type = infer_expression_type(start_expr, env:)
206
- stop_type = infer_expression_type(stop_expr, env:)
207
-
208
- if start_type != stop_type
209
- if start_expr.is_a?(AST::IntegerLiteral)
210
- start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
211
- elsif stop_expr.is_a?(AST::IntegerLiteral)
212
- stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
213
- end
214
- end
215
-
216
- raise LoweringError.new("range bounds must use matching integer types, got #{start_type} and #{stop_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless start_type == stop_type
217
-
218
- start_type
219
- end
220
-
221
- def integer_type?(type)
222
- type.is_a?(Types::Primitive) && type.integer?
223
- end
224
-
225
- def infer_index_result_type(receiver_type, index_type)
226
- raise LoweringError.new("index must be an integer type, got #{index_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless integer_type?(index_type)
227
-
228
- receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
229
-
230
- if array_type?(receiver_type)
231
- return array_element_type(receiver_type)
232
- end
233
-
234
- if receiver_type.is_a?(Types::Span)
235
- return receiver_type.element_type
236
- end
369
+ Types::Registry.function(name, params: [], return_type: return_type)
370
+ end
237
371
 
238
- if receiver_type.is_a?(Types::SoA)
239
- return receiver_type.element_type
240
- end
241
-
242
- if simd_type?(receiver_type)
243
- return receiver_type.element_type
244
- end
372
+ def compile_time_builtin_specialization_function_type(callee)
373
+ Types::Registry.function("attribute_arg", params: [], return_type: resolve_type_ref(callee.arguments.fetch(0).value))
374
+ end
245
375
 
246
- if pointer_type?(receiver_type)
247
- return pointee_type(receiver_type)
376
+ def rewrite_static_storage_initializer(expression)
377
+ case expression
378
+ when AST::Identifier
379
+ binding = @ctx.values[expression.name]
380
+ if binding&.kind == :const
381
+ declaration = const_declaration_for_module(@ctx.module_name, expression.name)
382
+ return rewrite_static_storage_initializer(declaration.value)
248
383
  end
249
384
 
250
- raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
251
- end
252
-
253
- def contains_type_var?(type)
254
- super
255
- end
256
-
257
-
258
- def stored_ref_supported_type?(type, visited = {})
259
- return true unless type
260
-
261
- visit_key = [type.class, type.object_id]
262
- return true if visited[visit_key]
263
-
264
- visited[visit_key] = true
265
- case type
266
- when Types::Nullable
267
- stored_ref_supported_type?(type.base, visited)
268
- when Types::GenericInstance
269
- if ref_type?(type)
270
- lt = ref_lifetime(type)
271
- return !!lt # lifetime-ref is supported by default; bare ref is not
385
+ expression
386
+ when AST::MemberAccess
387
+ if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
388
+ imported_module = @ctx.imports.fetch(expression.receiver.name)
389
+ if (binding = imported_module.values[expression.member])&.kind == :const
390
+ imported_analysis = analysis_for_module(imported_module.name)
391
+ declaration = const_declaration_for_module(imported_module.name, expression.member)
392
+ return with_analysis_context(imported_analysis) do
393
+ rewrite_static_storage_initializer(declaration.value)
394
+ end
272
395
  end
273
-
274
- type.arguments.all? { |argument| argument.is_a?(Types::LiteralTypeArg) || stored_ref_supported_type?(argument, visited) }
275
- when Types::Span
276
- stored_ref_supported_type?(type.element_type, visited)
277
- when Types::Task
278
- stored_ref_supported_type?(type.result_type, visited)
279
- when Types::StructInstance, Types::VariantInstance
280
- type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
281
- when Types::Proc, Types::Function
282
- callable_param_ref_supported?(type)
283
- else
284
- !contains_ref_type?(type)
285
396
  end
286
- end
287
-
288
- def pointer_to(type)
289
- Types::Registry.generic_instance("ptr", [type])
290
- end
291
397
 
292
- def with_analysis_context(analysis)
293
- saved = @ctx.save
294
- @ctx.install(analysis)
295
- @ctx.module_prefix = module_c_prefix(@ctx.module_name)
296
- yield
297
- ensure
298
- @ctx.restore(saved)
398
+ AST::MemberAccess.new(
399
+ receiver: rewrite_static_storage_initializer(expression.receiver),
400
+ member: expression.member,
401
+ )
402
+ when AST::UnaryOp
403
+ AST::UnaryOp.new(operator: expression.operator, operand: rewrite_static_storage_initializer(expression.operand))
404
+ when AST::BinaryOp
405
+ AST::BinaryOp.new(
406
+ operator: expression.operator,
407
+ left: rewrite_static_storage_initializer(expression.left),
408
+ right: rewrite_static_storage_initializer(expression.right),
409
+ )
410
+ when AST::IfExpr
411
+ AST::IfExpr.new(
412
+ condition: rewrite_static_storage_initializer(expression.condition),
413
+ then_expression: rewrite_static_storage_initializer(expression.then_expression),
414
+ else_expression: rewrite_static_storage_initializer(expression.else_expression),
415
+ )
416
+ when AST::UnsafeExpr
417
+ AST::UnsafeExpr.new(expression: rewrite_static_storage_initializer(expression.expression))
418
+ when AST::Call
419
+ AST::Call.new(
420
+ callee: rewrite_static_storage_initializer(expression.callee),
421
+ arguments: expression.arguments.map do |argument|
422
+ AST::Argument.new(name: argument.name, value: rewrite_static_storage_initializer(argument.value))
423
+ end,
424
+ )
425
+ when AST::Specialization
426
+ AST::Specialization.new(
427
+ callee: rewrite_static_storage_initializer(expression.callee),
428
+ arguments: expression.arguments.map { |argument| AST::TypeArgument.new(value: argument.value) },
429
+ )
430
+ else
431
+ expression
432
+ end
433
+ end
434
+
435
+ def local_binding(type:, linkage_name:, mutable:, pointer:, storage_type: nil, projection: nil, cstr_backed: false, cstr_list_backed: false, const_value: nil)
436
+ { type:, storage_type: storage_type || type, linkage_name:, mutable:, pointer:, projection:, cstr_backed:, cstr_list_backed:, const_value: }
437
+ end
438
+
439
+ def callable_type?(type)
440
+ type.is_a?(Types::Function) || proc_type?(type)
441
+ end
442
+
443
+ def contains_proc_storage_type?(type, visited = Set.new)
444
+ return false if visited.include?(type.object_id)
445
+
446
+ case type
447
+ when Types::Proc
448
+ true
449
+ when Types::Struct, Types::StructInstance
450
+ visited.add(type.object_id)
451
+ type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
452
+ when Types::Nullable
453
+ contains_proc_storage_type?(type.base, visited)
454
+ else
455
+ false
299
456
  end
300
-
301
- def lookup_value(name, env)
302
- env[:scopes].reverse_each do |scope|
303
- return scope[name] if scope.key?(name)
304
- end
305
-
306
- if @ctx.values.key?(name)
307
- binding = @ctx.values.fetch(name)
308
- {
309
- type: binding.type,
310
- storage_type: binding.storage_type,
311
- linkage_name: value_c_name(name),
312
- mutable: binding.mutable,
313
- pointer: false,
314
- cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
315
- cstr_list_backed: false,
316
- const_value: binding.const_value,
317
- }
457
+ end
458
+
459
+ def contains_task_type?(type, visited = Set.new)
460
+ return false if visited.include?(type.object_id)
461
+
462
+ case type
463
+ when Types::Task
464
+ true
465
+ when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
466
+ visited.add(type.object_id)
467
+ type.fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
468
+ when Types::VariantInstance
469
+ type.arguments.any? { |arg| contains_task_type?(arg, visited) }
470
+ when Types::Variant, Types::GenericVariantDefinition
471
+ visited.add(type.object_id)
472
+ type.arms.each_value.any? do |arm_fields|
473
+ arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
318
474
  end
475
+ when Types::GenericInstance
476
+ type.arguments.any? { |arg| contains_task_type?(arg, visited) }
477
+ when Types::Nullable
478
+ contains_task_type?(type.base, visited)
479
+ else
480
+ false
319
481
  end
482
+ end
320
483
 
321
- def lower_static_storage_initializer(expression, env:, expected_type: nil)
322
- if expected_type && (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), expected_type))
323
- return literal
324
- end
484
+ def proc_env_pointer_type
485
+ @proc_env_pointer_type ||= pointer_to(@ctx.types.fetch("void"))
486
+ end
325
487
 
326
- lower_expression(rewrite_static_storage_initializer(expression), env:, expected_type: expected_type)
327
- end
488
+ def proc_invoke_function_type(proc_type)
489
+ Types::Registry.function(
490
+ nil,
491
+ params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
492
+ return_type: proc_type.return_type,
493
+ )
494
+ end
328
495
 
329
- def lower_compile_time_literal(value, type)
330
- case value
331
- when true, false
332
- return IR::BooleanLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.boolean?
333
- when Integer
334
- return IR::IntegerLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.integer?
335
- return IR::FloatLiteral.new(value: value.to_f, type:) if type.is_a?(Types::Primitive) && type.float?
336
- when Float
337
- return IR::FloatLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.float?
338
- when String
339
- if type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
340
- return IR::StringLiteral.new(value:, type:, cstring: type == @ctx.types.fetch("cstr"))
341
- end
342
- when Hash
343
- return nil unless type.is_a?(Types::Struct)
344
- fields = value.map do |name, field_value|
345
- field_type = type.field(name)
346
- return nil unless field_type
347
- lowered = lower_compile_time_literal(field_value, field_type)
348
- return nil unless lowered
349
- IR::AggregateField.new(name:, value: lowered)
350
- end
351
- return IR::AggregateLiteral.new(type:, fields:)
352
- end
496
+ def proc_release_function_type
497
+ @proc_release_function_type ||= Types::Registry.function(
498
+ nil,
499
+ params: [Types::Registry.parameter("env", proc_env_pointer_type)],
500
+ return_type: @ctx.types.fetch("void"),
501
+ )
502
+ end
353
503
 
354
- nil
355
- end
504
+ def proc_retain_function_type
505
+ @proc_retain_function_type ||= Types::Registry.function(
506
+ nil,
507
+ params: [Types::Registry.parameter("env", proc_env_pointer_type)],
508
+ return_type: @ctx.types.fetch("void"),
509
+ )
510
+ end
356
511
 
357
- def compile_time_builtin_function_type(name, arguments, env)
358
- return_type = case name
359
- when "field_of"
360
- @ctx.types.fetch("field_handle")
361
- when "callable_of"
362
- @ctx.types.fetch("callable_handle")
363
- when "has_attribute"
364
- @ctx.types.fetch("bool")
365
- when "attribute_of"
366
- @ctx.types.fetch("attribute_handle")
367
- else
368
- nil
369
- end
370
- raise LoweringError.new("unsupported compile-time builtin #{name}", line: 0, column: 0, path: @ctx.current_analysis_path) unless return_type
512
+ def fresh_proc_symbol
513
+ @synthetic_proc_counter += 1
514
+ end
371
515
 
372
- Types::Registry.function(name, params: [], return_type: return_type)
516
+ def current_actual_scope(scopes)
517
+ scopes.reverse_each do |scope|
518
+ return scope unless scope.is_a?(FlowScope)
373
519
  end
374
520
 
375
- def compile_time_builtin_specialization_function_type(callee)
376
- Types::Registry.function("attribute_arg", params: [], return_type: resolve_type_ref(callee.arguments.fetch(0).value))
377
- end
521
+ raise LoweringError.new("missing lexical scope", line: 0, column: 0, path: @ctx.current_analysis_path)
522
+ end
378
523
 
524
+ def env_with_refinements(env, refinements)
525
+ updated = env.dup
526
+ updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
527
+ updated
528
+ end
379
529
 
380
- def rewrite_static_storage_initializer(expression)
381
- case expression
382
- when AST::Identifier
383
- binding = @ctx.values[expression.name]
384
- if binding&.kind == :const
385
- declaration = const_declaration_for_module(@ctx.module_name, expression.name)
386
- return rewrite_static_storage_initializer(declaration.value)
387
- end
530
+ def scopes_with_refinements(scopes, refinements)
531
+ return scopes if refinements.nil? || refinements.empty?
388
532
 
389
- expression
390
- when AST::MemberAccess
391
- if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
392
- imported_module = @ctx.imports.fetch(expression.receiver.name)
393
- if (binding = imported_module.values[expression.member])&.kind == :const
394
- imported_analysis = analysis_for_module(imported_module.name)
395
- declaration = const_declaration_for_module(imported_module.name, expression.member)
396
- return with_analysis_context(imported_analysis) do
397
- rewrite_static_storage_initializer(declaration.value)
398
- end
399
- end
400
- end
533
+ base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
534
+ merged_refinements = scopes.last.is_a?(FlowScope) ? scopes.last.each_with_object({}) { |(name, binding), result| result[name] = binding[:type] } : {}
535
+ merged_refinements = merge_refinements(merged_refinements, refinements)
536
+ flow_scope = FlowScope.new
401
537
 
402
- AST::MemberAccess.new(
403
- receiver: rewrite_static_storage_initializer(expression.receiver),
404
- member: expression.member,
405
- )
406
- when AST::UnaryOp
407
- AST::UnaryOp.new(operator: expression.operator, operand: rewrite_static_storage_initializer(expression.operand))
408
- when AST::BinaryOp
409
- AST::BinaryOp.new(
410
- operator: expression.operator,
411
- left: rewrite_static_storage_initializer(expression.left),
412
- right: rewrite_static_storage_initializer(expression.right),
413
- )
414
- when AST::IfExpr
415
- AST::IfExpr.new(
416
- condition: rewrite_static_storage_initializer(expression.condition),
417
- then_expression: rewrite_static_storage_initializer(expression.then_expression),
418
- else_expression: rewrite_static_storage_initializer(expression.else_expression),
419
- )
420
- when AST::UnsafeExpr
421
- AST::UnsafeExpr.new(expression: rewrite_static_storage_initializer(expression.expression))
422
- when AST::Call
423
- AST::Call.new(
424
- callee: rewrite_static_storage_initializer(expression.callee),
425
- arguments: expression.arguments.map do |argument|
426
- AST::Argument.new(name: argument.name, value: rewrite_static_storage_initializer(argument.value))
427
- end,
428
- )
429
- when AST::Specialization
430
- AST::Specialization.new(
431
- callee: rewrite_static_storage_initializer(expression.callee),
432
- arguments: expression.arguments.map { |argument| AST::TypeArgument.new(value: argument.value) },
433
- )
434
- else
435
- expression
436
- end
437
- end
538
+ merged_refinements.each do |name, refined_type|
539
+ binding = lookup_value(name, { scopes: base_scopes })
540
+ next unless binding
438
541
 
439
- def local_binding(type:, linkage_name:, mutable:, pointer:, storage_type: nil, projection: nil, cstr_backed: false, cstr_list_backed: false, const_value: nil)
440
- { type:, storage_type: storage_type || type, linkage_name:, mutable:, pointer:, projection:, cstr_backed:, cstr_list_backed:, const_value: }
542
+ flow_scope[name] = binding.merge(type: refined_type)
441
543
  end
442
544
 
443
- def callable_type?(type)
444
- type.is_a?(Types::Function) || proc_type?(type)
445
- end
545
+ return base_scopes if flow_scope.empty?
446
546
 
447
- def contains_proc_storage_type?(type, visited = Set.new)
448
- return false if visited.include?(type.object_id)
449
-
450
- case type
451
- when Types::Proc
452
- true
453
- when Types::Struct, Types::StructInstance
454
- visited.add(type.object_id)
455
- type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
456
- when Types::Nullable
457
- contains_proc_storage_type?(type.base, visited)
458
- else
459
- false
460
- end
461
- end
547
+ base_scopes + [flow_scope]
548
+ end
462
549
 
463
- def contains_task_type?(type, visited = Set.new)
464
- return false if visited.include?(type.object_id)
465
-
466
- case type
467
- when Types::Task
468
- true
469
- when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
470
- visited.add(type.object_id)
471
- type.fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
472
- when Types::VariantInstance
473
- type.arguments.any? { |arg| contains_task_type?(arg, visited) }
474
- when Types::Variant, Types::GenericVariantDefinition
475
- visited.add(type.object_id)
476
- type.arms.each_value.any? do |arm_fields|
477
- arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
478
- end
479
- when Types::GenericInstance
480
- type.arguments.any? { |arg| contains_task_type?(arg, visited) }
481
- when Types::Nullable
482
- contains_task_type?(type.base, visited)
550
+ def merge_refinements(existing, incoming)
551
+ merged = existing.dup
552
+ incoming.each do |name, refined_type|
553
+ if merged.key?(name) && merged[name] != refined_type
554
+ merged.delete(name)
483
555
  else
484
- false
556
+ merged[name] = refined_type
485
557
  end
486
558
  end
487
559
 
488
- def proc_env_pointer_type
489
- @proc_env_pointer_type ||= pointer_to(@ctx.types.fetch("void"))
490
- end
491
-
492
- def proc_invoke_function_type(proc_type)
493
- Types::Registry.function(
494
- nil,
495
- params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
496
- return_type: proc_type.return_type,
497
- )
498
- end
499
-
500
- def proc_release_function_type
501
- @proc_release_function_type ||= Types::Registry.function(
502
- nil,
503
- params: [Types::Registry.parameter("env", proc_env_pointer_type)],
504
- return_type: @ctx.types.fetch("void"),
505
- )
506
- end
507
-
508
- def proc_retain_function_type
509
- @proc_retain_function_type ||= Types::Registry.function(
510
- nil,
511
- params: [Types::Registry.parameter("env", proc_env_pointer_type)],
512
- return_type: @ctx.types.fetch("void"),
513
- )
514
- end
515
-
516
- def fresh_proc_symbol
517
- @synthetic_proc_counter += 1
518
- end
519
-
520
- def current_actual_scope(scopes)
521
- scopes.reverse_each do |scope|
522
- return scope unless scope.is_a?(FlowScope)
523
- end
524
-
525
- raise LoweringError.new("missing lexical scope", line: 0, column: 0, path: @ctx.current_analysis_path)
526
- end
527
-
528
- def env_with_refinements(env, refinements)
529
- updated = env.dup
530
- updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
531
- updated
532
- end
533
-
534
- def scopes_with_refinements(scopes, refinements)
535
- return scopes if refinements.nil? || refinements.empty?
536
-
537
- base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
538
- merged_refinements = scopes.last.is_a?(FlowScope) ? scopes.last.each_with_object({}) { |(name, binding), result| result[name] = binding[:type] } : {}
539
- merged_refinements = merge_refinements(merged_refinements, refinements)
540
- flow_scope = FlowScope.new
541
-
542
- merged_refinements.each do |name, refined_type|
543
- binding = lookup_value(name, { scopes: base_scopes })
544
- next unless binding
545
-
546
- flow_scope[name] = binding.merge(type: refined_type)
547
- end
548
-
549
- return base_scopes if flow_scope.empty?
550
-
551
- base_scopes + [flow_scope]
552
- end
553
-
554
- def merge_refinements(existing, incoming)
555
- merged = existing.dup
556
- incoming.each do |name, refined_type|
557
- if merged.key?(name) && merged[name] != refined_type
558
- merged.delete(name)
559
- else
560
- merged[name] = refined_type
560
+ merged
561
+ end
562
+
563
+ def flow_refinements(expression, truthy:, env:)
564
+ case expression
565
+ when AST::UnaryOp
566
+ return flow_refinements(expression.operand, truthy: !truthy, env:) if expression.operator == "not"
567
+ when AST::BinaryOp
568
+ case expression.operator
569
+ when "and"
570
+ if truthy
571
+ left_truthy = flow_refinements(expression.left, truthy: true, env:)
572
+ right_env = env_with_refinements(env, left_truthy)
573
+ right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
574
+ return merge_refinements(left_truthy, right_truthy)
561
575
  end
562
- end
563
-
564
- merged
565
- end
566
-
567
- def flow_refinements(expression, truthy:, env:)
568
- case expression
569
- when AST::UnaryOp
570
- return flow_refinements(expression.operand, truthy: !truthy, env:) if expression.operator == "not"
571
- when AST::BinaryOp
572
- case expression.operator
573
- when "and"
574
- if truthy
575
- left_truthy = flow_refinements(expression.left, truthy: true, env:)
576
- right_env = env_with_refinements(env, left_truthy)
577
- right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
578
- return merge_refinements(left_truthy, right_truthy)
579
- end
580
- when "or"
581
- unless truthy
582
- left_falsy = flow_refinements(expression.left, truthy: false, env:)
583
- right_env = env_with_refinements(env, left_falsy)
584
- right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
585
- return merge_refinements(left_falsy, right_falsy)
586
- end
587
- when "==", "!="
588
- return null_test_refinements(expression, truthy:, env:)
576
+ when "or"
577
+ unless truthy
578
+ left_falsy = flow_refinements(expression.left, truthy: false, env:)
579
+ right_env = env_with_refinements(env, left_falsy)
580
+ right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
581
+ return merge_refinements(left_falsy, right_falsy)
589
582
  end
583
+ when "==", "!="
584
+ return null_test_refinements(expression, truthy:, env:)
590
585
  end
591
-
592
- {}
593
586
  end
594
587
 
595
- def null_test_refinements(expression, truthy:, env:)
596
- identifier_expression = nil
597
- if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
598
- identifier_expression = expression.left
599
- elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
600
- identifier_expression = expression.right
601
- else
602
- return {}
603
- end
604
-
605
- binding = lookup_value(identifier_expression.name, env)
606
- return {} unless binding && binding[:storage_type].is_a?(Types::Nullable)
588
+ {}
589
+ end
607
590
 
608
- null_result = expression.operator == "==" ? truthy : !truthy
609
- refined_type = null_result ? null_type : binding[:storage_type].base
610
- { identifier_expression.name => refined_type }
591
+ def null_test_refinements(expression, truthy:, env:)
592
+ identifier_expression = nil
593
+ if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
594
+ identifier_expression = expression.left
595
+ elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
596
+ identifier_expression = expression.right
597
+ else
598
+ return {}
611
599
  end
612
600
 
613
- def cfg_block_always_terminates?(statements)
614
- ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
615
- end
601
+ binding = lookup_value(identifier_expression.name, env)
602
+ return {} unless binding && binding[:storage_type].is_a?(Types::Nullable)
616
603
 
617
- def conditional_common_type(then_type, else_type)
618
- return then_type if then_type == else_type
604
+ null_result = expression.operator == "==" ? truthy : !truthy
605
+ refined_type = null_result ? null_type : binding[:storage_type].base
606
+ { identifier_expression.name => refined_type }
607
+ end
619
608
 
620
- numeric_type = common_numeric_type(then_type, else_type)
621
- return numeric_type if numeric_type
609
+ def cfg_block_always_terminates?(statements)
610
+ ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
611
+ end
622
612
 
623
- if (nullable_type = conditional_null_common_type(then_type, else_type))
624
- return nullable_type
625
- end
613
+ def conditional_common_type(then_type, else_type)
614
+ return then_type if then_type == else_type
626
615
 
627
- if (nullable_type = conditional_null_common_type(else_type, then_type))
628
- return nullable_type
629
- end
630
-
631
- return then_type if then_type.is_a?(Types::Nullable) && else_type == then_type.base
632
- return else_type if else_type.is_a?(Types::Nullable) && then_type == else_type.base
616
+ numeric_type = common_numeric_type(then_type, else_type)
617
+ return numeric_type if numeric_type
633
618
 
634
- nil
619
+ if (nullable_type = conditional_null_common_type(then_type, else_type))
620
+ return nullable_type
635
621
  end
636
622
 
637
- def if_expression_branch_compatible?(actual_type, expected_type)
638
- return true if actual_type == expected_type
639
- return true if null_assignable_to?(actual_type, expected_type)
640
- return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
641
- return true if common_numeric_type(actual_type, expected_type) == expected_type
642
-
643
- false
623
+ if (nullable_type = conditional_null_common_type(else_type, then_type))
624
+ return nullable_type
644
625
  end
645
626
 
646
- def nullable_candidate?(type)
647
- !ref_type?(type) && type != @ctx.types.fetch("void")
648
- end
627
+ return then_type if then_type.is_a?(Types::Nullable) && else_type == then_type.base
628
+ return else_type if else_type.is_a?(Types::Nullable) && then_type == else_type.base
649
629
 
650
- def conditional_null_common_type(null_type, other_type)
651
- return unless null_type.is_a?(Types::Null)
630
+ nil
631
+ end
652
632
 
653
- if other_type.is_a?(Types::Nullable)
654
- return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
633
+ def if_expression_branch_compatible?(actual_type, expected_type)
634
+ return true if actual_type == expected_type
635
+ return true if null_assignable_to?(actual_type, expected_type)
636
+ return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
637
+ return true if common_numeric_type(actual_type, expected_type) == expected_type
655
638
 
656
- return nil
657
- end
639
+ false
640
+ end
658
641
 
659
- return unless nullable_candidate?(other_type)
660
- return if null_type.target_type && null_type.target_type != other_type
642
+ def nullable_candidate?(type)
643
+ !ref_type?(type) && type != @ctx.types.fetch("void")
644
+ end
661
645
 
662
- Types::Registry.nullable(other_type)
663
- end
646
+ def conditional_null_common_type(null_type, other_type)
647
+ return unless null_type.is_a?(Types::Null)
664
648
 
665
- def null_type
666
- @null_type ||= Types::Null.new
667
- end
649
+ if other_type.is_a?(Types::Nullable)
650
+ return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
668
651
 
669
- def loop_flow(break_target:, continue_target:, break_defers: [], continue_defers: [])
670
- {
671
- break_target:,
672
- continue_target:,
673
- break_defers:,
674
- continue_defers:,
675
- }
652
+ return nil
676
653
  end
677
654
 
678
- def nested_loop_flow(current_loop_flow, local_defers)
679
- return nil unless current_loop_flow
655
+ return unless nullable_candidate?(other_type)
656
+ return if null_type.target_type && null_type.target_type != other_type
680
657
 
681
- loop_flow(
682
- break_target: current_loop_flow[:break_target],
683
- continue_target: current_loop_flow[:continue_target],
684
- break_defers: current_loop_flow[:break_defers] + local_defers,
685
- continue_defers: current_loop_flow[:continue_defers] + local_defers,
686
- )
687
- end
658
+ Types::Registry.nullable(other_type)
659
+ end
688
660
 
689
- def switch_loop_target(target)
690
- return target unless target && target[:label]
661
+ def null_type
662
+ @null_type ||= Types::Null.new
663
+ end
691
664
 
692
- loop_exit_label(target[:label])
693
- end
665
+ def loop_flow(break_target:, continue_target:, break_defers: [], continue_defers: [])
666
+ {
667
+ break_target:,
668
+ continue_target:,
669
+ break_defers:,
670
+ continue_defers:,
671
+ }
672
+ end
694
673
 
695
- def switch_loop_flow(current_loop_flow, local_defers)
696
- nested = nested_loop_flow(current_loop_flow, local_defers)
697
- return nil unless nested
674
+ def nested_loop_flow(current_loop_flow, local_defers)
675
+ return nil unless current_loop_flow
698
676
 
699
- loop_flow(
700
- break_target: switch_loop_target(nested[:break_target]),
701
- continue_target: switch_loop_target(nested[:continue_target]),
702
- break_defers: nested[:break_defers],
703
- continue_defers: nested[:continue_defers],
704
- )
705
- end
677
+ loop_flow(
678
+ break_target: current_loop_flow[:break_target],
679
+ continue_target: current_loop_flow[:continue_target],
680
+ break_defers: current_loop_flow[:break_defers] + local_defers,
681
+ continue_defers: current_loop_flow[:continue_defers] + local_defers,
682
+ )
683
+ end
706
684
 
707
- def cleanup_statements(local_defers, outer_defers)
708
- local_defers.reverse.flat_map(&:itself) + outer_defers.reverse.flat_map(&:itself)
709
- end
685
+ def switch_loop_target(target)
686
+ return target unless target && target[:label]
710
687
 
711
- def loop_exit_break(label = nil)
712
- { kind: :break, label: }
713
- end
688
+ loop_exit_label(target[:label])
689
+ end
714
690
 
715
- def loop_exit_continue(label = nil)
716
- { kind: :continue, label: }
717
- end
691
+ def switch_loop_flow(current_loop_flow, local_defers)
692
+ nested = nested_loop_flow(current_loop_flow, local_defers)
693
+ return nil unless nested
718
694
 
719
- def loop_exit_label(label)
720
- { kind: :label, label: }
721
- end
695
+ loop_flow(
696
+ break_target: switch_loop_target(nested[:break_target]),
697
+ continue_target: switch_loop_target(nested[:continue_target]),
698
+ break_defers: nested[:break_defers],
699
+ continue_defers: nested[:continue_defers],
700
+ )
701
+ end
722
702
 
723
- def loop_exit_statement(target, local_defers:, outer_defers:)
724
- case target[:kind]
725
- when :break
726
- IR::BreakStmt.new
727
- when :continue
728
- IR::ContinueStmt.new
729
- when :label
730
- return IR::GotoStmt.new(label: target[:label]) if target[:label]
703
+ def cleanup_statements(local_defers, outer_defers)
704
+ local_defers.reverse.flat_map(&:itself) + outer_defers.reverse.flat_map(&:itself)
705
+ end
731
706
 
732
- IR::GotoStmt.new(label: target[:label])
733
- else
734
- raise LoweringError.new("unsupported loop exit target #{target.inspect}", line: 0, column: 0, path: @ctx.current_analysis_path)
735
- end
736
- end
707
+ def loop_exit_break(label = nil)
708
+ { kind: :break, label: }
709
+ end
737
710
 
738
- def lower_loop_exit(target, local_defers, outer_defers)
739
- cleanup = cleanup_statements(local_defers, outer_defers)
740
- if cleanup.empty?
741
- [loop_exit_statement(target, local_defers:, outer_defers:)]
742
- else
743
- label = target[:label]
744
- raise LoweringError.new("structured loop exits with cleanup are unsupported", line: 0, column: 0, path: @ctx.current_analysis_path) unless label
711
+ def loop_exit_continue(label = nil)
712
+ { kind: :continue, label: }
713
+ end
745
714
 
746
- cleanup + [IR::GotoStmt.new(label:)]
747
- end
748
- end
715
+ def loop_exit_label(label)
716
+ { kind: :label, label: }
717
+ end
749
718
 
750
- def contains_label_target?(statements, label)
751
- statements.any? do |statement|
752
- case statement
753
- when IR::GotoStmt
754
- statement.label == label
755
- when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
756
- contains_label_target?(statement.body, label)
757
- when IR::IfStmt
758
- contains_label_target?(statement.then_body, label) || (statement.else_body && contains_label_target?(statement.else_body, label))
759
- when IR::SwitchStmt
760
- statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
761
- else
762
- false
763
- end
719
+ def loop_exit_statement(target, local_defers:, outer_defers:)
720
+ case target[:kind]
721
+ when :break
722
+ IR::BreakStmt.new
723
+ when :continue
724
+ IR::ContinueStmt.new
725
+ when :label
726
+ return IR::GotoStmt.new(label: target[:label]) if target[:label]
727
+
728
+ IR::GotoStmt.new(label: target[:label])
729
+ else
730
+ raise LoweringError.new("unsupported loop exit target #{target.inspect}", line: 0, column: 0, path: @ctx.current_analysis_path)
731
+ end
732
+ end
733
+
734
+ def lower_loop_exit(target, local_defers, outer_defers)
735
+ cleanup = cleanup_statements(local_defers, outer_defers)
736
+ if cleanup.empty?
737
+ [loop_exit_statement(target, local_defers:, outer_defers:)]
738
+ else
739
+ label = target[:label]
740
+ raise LoweringError.new("structured loop exits with cleanup are unsupported", line: 0, column: 0, path: @ctx.current_analysis_path) unless label
741
+
742
+ cleanup + [IR::GotoStmt.new(label:)]
743
+ end
744
+ end
745
+
746
+ def contains_label_target?(statements, label)
747
+ statements.any? do |statement|
748
+ case statement
749
+ when IR::GotoStmt
750
+ statement.label == label
751
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
752
+ contains_label_target?(statement.body, label)
753
+ when IR::IfStmt
754
+ contains_label_target?(statement.then_body, label) || (statement.else_body && contains_label_target?(statement.else_body, label))
755
+ when IR::SwitchStmt
756
+ statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
757
+ else
758
+ false
764
759
  end
765
760
  end
766
-
767
- def lower_defer_cleanup_expression(expression, env:)
768
- prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
769
- expression,
761
+ end
762
+
763
+ def lower_defer_cleanup_expression(expression, env:)
764
+ prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
765
+ expression,
766
+ env:,
767
+ expected_type: infer_expression_type(expression, env:),
768
+ allow_root_statement_foreign: true,
769
+ )
770
+
771
+ lowered = []
772
+ lowered.concat(prepared_setup)
773
+ if (foreign_call = foreign_call_info(prepared_expression, env))
774
+ setup, = lower_foreign_call_statement(
775
+ foreign_call,
770
776
  env:,
771
- expected_type: infer_expression_type(expression, env:),
772
- allow_root_statement_foreign: true,
777
+ expected_type: foreign_call[:binding].type.return_type,
778
+ statement_position: true,
779
+ discard_result: true,
780
+ )
781
+ lowered.concat(setup)
782
+ else
783
+ lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env:))
784
+ end
785
+ lowered.concat(prepared_cleanups.flat_map(&:itself))
786
+ lowered
787
+ end
788
+
789
+ def lower_defer_cleanup_body(statements, env:, return_type:)
790
+ lower_block(statements, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: false)
791
+ end
792
+
793
+ def terminating_ir_statement?(statement)
794
+ statement.is_a?(IR::ReturnStmt) || statement.is_a?(IR::GotoStmt)
795
+ end
796
+
797
+ def empty_env
798
+ { scopes: [{}], counter: { value: 0 } }
799
+ end
800
+
801
+ def snapshot_env(env)
802
+ { scopes: env[:scopes].map(&:dup), counter: env[:counter] }
803
+ end
804
+
805
+ def duplicate_env(env)
806
+ duplicated = env.dup
807
+ duplicated[:scopes] = env[:scopes].map(&:dup) + [{}]
808
+ duplicated[:counter] = env[:counter]
809
+ duplicated.delete(:prepared_expression_cleanups)
810
+ duplicated
811
+ end
812
+
813
+ def let_else_discard_binding_syntax?(statement)
814
+ statement.is_a?(AST::LocalDecl) && statement.else_body && statement.name == "_"
815
+ end
816
+
817
+ def bind_let_else_local?(statement)
818
+ !let_else_discard_binding_syntax?(statement)
819
+ end
820
+
821
+ def let_else_storage_c_name(statement, env)
822
+ return fresh_c_temp_name(env, "let_else_discard") if let_else_discard_binding_syntax?(statement)
823
+
824
+ return fresh_c_temp_name(env, "_") if statement.name == "_"
825
+
826
+ c_local_name(statement.name)
827
+ end
828
+
829
+ def let_else_success_type(type)
830
+ return type.base if type.is_a?(Types::Nullable)
831
+ return type.arm("some").fetch("value") if option_let_else_type?(type)
832
+ return unless result_let_else_type?(type)
833
+
834
+ type.arm("success").fetch("value")
835
+ end
836
+
837
+ def let_else_error_type(type)
838
+ return unless result_let_else_type?(type)
839
+
840
+ type.arm("failure").fetch("error")
841
+ end
842
+
843
+ def let_else_binding_projection(type)
844
+ return :result_success_value if result_let_else_type?(type)
845
+ return :option_some_value if option_let_else_type?(type)
846
+
847
+ nil
848
+ end
849
+
850
+ def option_let_else_type?(type)
851
+ return false unless type.is_a?(Types::Variant)
852
+
853
+ some_fields = type.arm("some")
854
+ none_fields = type.arm("none")
855
+ some_fields && some_fields.length == 1 && some_fields.key?("value") &&
856
+ none_fields && none_fields.empty?
857
+ end
858
+
859
+ def result_let_else_type?(type)
860
+ return false unless type.is_a?(Types::Variant)
861
+
862
+ success_fields = type.arm("success")
863
+ failure_fields = type.arm("failure")
864
+ success_fields && success_fields.length == 1 && success_fields.key?("value") &&
865
+ failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
866
+ end
867
+
868
+ def let_else_failure_condition(storage_expr, storage_type)
869
+ if storage_type.is_a?(Types::Nullable)
870
+ return IR::Binary.new(
871
+ operator: "==",
872
+ left: storage_expr,
873
+ right: IR::NullLiteral.new(type: storage_type),
874
+ type: @ctx.types.fetch("bool"),
773
875
  )
774
-
775
- lowered = []
776
- lowered.concat(prepared_setup)
777
- if (foreign_call = foreign_call_info(prepared_expression, env))
778
- setup, = lower_foreign_call_statement(
779
- foreign_call,
780
- env:,
781
- expected_type: foreign_call[:binding].type.return_type,
782
- statement_position: true,
783
- discard_result: true,
784
- )
785
- lowered.concat(setup)
786
- else
787
- lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env:))
788
- end
789
- lowered.concat(prepared_cleanups.flat_map(&:itself))
790
- lowered
791
876
  end
792
877
 
793
- def lower_defer_cleanup_body(statements, env:, return_type:)
794
- lower_block(statements, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: false)
878
+ if result_let_else_type?(storage_type)
879
+ kind_type = @ctx.types.fetch("int")
880
+ return IR::Binary.new(
881
+ operator: "==",
882
+ left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
883
+ right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
884
+ type: @ctx.types.fetch("bool"),
885
+ )
795
886
  end
796
887
 
797
- def terminating_ir_statement?(statement)
798
- statement.is_a?(IR::ReturnStmt) || statement.is_a?(IR::GotoStmt)
888
+ if option_let_else_type?(storage_type)
889
+ kind_type = @ctx.types.fetch("int")
890
+ return IR::Binary.new(
891
+ operator: "==",
892
+ left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
893
+ right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
894
+ type: @ctx.types.fetch("bool"),
895
+ )
799
896
  end
800
897
 
801
- def empty_env
802
- { scopes: [{}], counter: { value: 0 } }
803
- end
898
+ raise LoweringError.new("unsupported let-else storage type #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
899
+ end
804
900
 
805
- def snapshot_env(env)
806
- { scopes: env[:scopes].map(&:dup), counter: env[:counter] }
807
- end
901
+ def lower_bound_identifier(binding, expected_type: nil)
902
+ storage_type = binding[:storage_type]
903
+ visible_type = binding[:type]
904
+ projection = binding[:projection]
808
905
 
809
- def duplicate_env(env)
810
- duplicated = env.dup
811
- duplicated[:scopes] = env[:scopes].map(&:dup) + [{}]
812
- duplicated[:counter] = env[:counter]
813
- duplicated.delete(:prepared_expression_cleanups)
814
- duplicated
906
+ if projection == :result_success_value
907
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
908
+ return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
815
909
  end
816
910
 
817
- def let_else_discard_binding_syntax?(statement)
818
- statement.is_a?(AST::LocalDecl) && statement.else_body && statement.name == "_"
911
+ if projection == :result_failure_error
912
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
913
+ return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
819
914
  end
820
915
 
821
- def bind_let_else_local?(statement)
822
- !let_else_discard_binding_syntax?(statement)
916
+ if projection == :option_some_value
917
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
918
+ return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
823
919
  end
824
920
 
825
- def let_else_storage_c_name(statement, env)
826
- return fresh_c_temp_name(env, "let_else_discard") if let_else_discard_binding_syntax?(statement)
827
-
828
- return fresh_c_temp_name(env, "_") if statement.name == "_"
829
-
830
- c_local_name(statement.name)
921
+ return IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer]) if visible_type == storage_type
922
+ if storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type
923
+ name = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
924
+ return name if pointer_like_type?(storage_type.base)
925
+ return name if expected_type == storage_type
926
+ return IR::Member.new(receiver: name, member: "value", type: visible_type)
831
927
  end
832
928
 
833
- def let_else_success_type(type)
834
- return type.base if type.is_a?(Types::Nullable)
835
- return type.arm("some").fetch("value") if option_let_else_type?(type)
836
- return unless result_let_else_type?(type)
837
-
838
- type.arm("success").fetch("value")
929
+ if result_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
930
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
931
+ return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
839
932
  end
840
933
 
841
- def let_else_error_type(type)
842
- return unless result_let_else_type?(type)
843
-
844
- type.arm("failure").fetch("error")
934
+ if option_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
935
+ local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
936
+ return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
845
937
  end
846
938
 
847
- def let_else_binding_projection(type)
848
- return :result_success_value if result_let_else_type?(type)
849
- return :option_some_value if option_let_else_type?(type)
850
-
851
- nil
852
- end
939
+ IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
940
+ end
853
941
 
854
- def option_let_else_type?(type)
855
- return false unless type.is_a?(Types::Variant)
942
+ def variant_binding_projection_expression(storage_expr, storage_type, arm_name, field_name, field_type)
943
+ payload_type = Types::VariantArmPayload.new(storage_type, arm_name, storage_type.arm(arm_name))
944
+ data_expr = IR::Member.new(receiver: storage_expr, member: "data", type: nil)
945
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
946
+ IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
947
+ end
856
948
 
857
- some_fields = type.arm("some")
858
- none_fields = type.arm("none")
859
- some_fields && some_fields.length == 1 && some_fields.key?("value") &&
860
- none_fields && none_fields.empty?
861
- end
949
+ def infer_result_propagation_type(expression, env:)
950
+ _storage_type, success_type, = infer_result_propagation_types(expression, env:)
862
951
 
863
- def result_let_else_type?(type)
864
- return false unless type.is_a?(Types::Variant)
952
+ success_type
953
+ end
865
954
 
866
- success_fields = type.arm("success")
867
- failure_fields = type.arm("failure")
868
- success_fields && success_fields.length == 1 && success_fields.key?("value") &&
869
- failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
955
+ def infer_result_propagation_types(expression, env:, allow_void_success: false)
956
+ storage_type = infer_expression_type(expression.operand, env:)
957
+ if result_let_else_type?(storage_type)
958
+ infer_result_propagation_details(storage_type, env:, allow_void_success:)
959
+ elsif option_let_else_type?(storage_type)
960
+ infer_option_propagation_details(storage_type, env:, allow_void_success:)
961
+ else
962
+ raise LoweringError.new("propagation expects Result[T, E] or Option[T], got #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
870
963
  end
964
+ end
871
965
 
872
- def let_else_failure_condition(storage_expr, storage_type)
873
- if storage_type.is_a?(Types::Nullable)
874
- return IR::Binary.new(
875
- operator: "==",
876
- left: storage_expr,
877
- right: IR::NullLiteral.new(type: storage_type),
878
- type: @ctx.types.fetch("bool"),
879
- )
880
- end
881
-
882
- if result_let_else_type?(storage_type)
883
- kind_type = @ctx.types.fetch("int")
884
- return IR::Binary.new(
885
- operator: "==",
886
- left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
887
- right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
888
- type: @ctx.types.fetch("bool"),
889
- )
890
- end
966
+ def infer_result_propagation_details(storage_type, env:, allow_void_success:)
967
+ success_type = let_else_success_type(storage_type)
968
+ error_type = let_else_error_type(storage_type)
969
+ raise LoweringError.new("propagation requires a non-void Result success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
891
970
 
892
- if option_let_else_type?(storage_type)
893
- kind_type = @ctx.types.fetch("int")
894
- return IR::Binary.new(
895
- operator: "==",
896
- left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
897
- right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
898
- type: @ctx.types.fetch("bool"),
899
- )
900
- end
971
+ context = env[:return_context]
972
+ raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
973
+ raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
901
974
 
902
- raise LoweringError.new("unsupported let-else storage type #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
975
+ return_type = context[:return_type]
976
+ unless result_let_else_type?(return_type)
977
+ raise LoweringError.new("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
903
978
  end
904
979
 
905
- def lower_bound_identifier(binding, expected_type: nil)
906
- storage_type = binding[:storage_type]
907
- visible_type = binding[:type]
908
- projection = binding[:projection]
909
-
910
- if projection == :result_success_value
911
- local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
912
- return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
913
- end
914
-
915
- if projection == :result_failure_error
916
- local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
917
- return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
918
- end
919
-
920
- if projection == :option_some_value
921
- local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
922
- return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
923
- end
924
-
925
- return IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer]) if visible_type == storage_type
926
- if storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type
927
- name = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
928
- return name if pointer_like_type?(storage_type.base)
929
- return name if expected_type == storage_type
930
- return IR::Member.new(receiver: name, member: "value", type: visible_type)
931
- end
932
-
933
- if result_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
934
- local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
935
- return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
936
- end
937
-
938
- if option_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
939
- local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
940
- return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
941
- end
942
-
943
- IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
980
+ return_error_type = let_else_error_type(return_type)
981
+ unless return_error_type == error_type
982
+ raise LoweringError.new("propagation error type #{error_type} must match enclosing Result error type #{return_error_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
944
983
  end
945
984
 
946
- def variant_binding_projection_expression(storage_expr, storage_type, arm_name, field_name, field_type)
947
- payload_type = Types::VariantArmPayload.new(storage_type, arm_name, storage_type.arm(arm_name))
948
- data_expr = IR::Member.new(receiver: storage_expr, member: "data", type: nil)
949
- arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
950
- IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
951
- end
985
+ [storage_type, success_type, return_type, error_type]
986
+ end
952
987
 
953
- def infer_result_propagation_type(expression, env:)
954
- _storage_type, success_type, = infer_result_propagation_types(expression, env:)
988
+ def infer_option_propagation_details(storage_type, env:, allow_void_success:)
989
+ success_type = let_else_success_type(storage_type)
990
+ raise LoweringError.new("propagation requires a non-void Option success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
955
991
 
956
- success_type
957
- end
992
+ context = env[:return_context]
993
+ raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
994
+ raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
958
995
 
959
- def infer_result_propagation_types(expression, env:, allow_void_success: false)
960
- storage_type = infer_expression_type(expression.operand, env:)
961
- if result_let_else_type?(storage_type)
962
- infer_result_propagation_details(storage_type, env:, allow_void_success:)
963
- elsif option_let_else_type?(storage_type)
964
- infer_option_propagation_details(storage_type, env:, allow_void_success:)
965
- else
966
- raise LoweringError.new("propagation expects Result[T, E] or Option[T], got #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
967
- end
996
+ return_type = context[:return_type]
997
+ unless option_let_else_type?(return_type)
998
+ raise LoweringError.new("propagation requires enclosing function/proc to return Option[_], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
968
999
  end
969
1000
 
970
- def infer_result_propagation_details(storage_type, env:, allow_void_success:)
971
- success_type = let_else_success_type(storage_type)
972
- error_type = let_else_error_type(storage_type)
973
- raise LoweringError.new("propagation requires a non-void Result success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
1001
+ [storage_type, success_type, return_type, nil]
1002
+ end
974
1003
 
975
- context = env[:return_context]
976
- raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
977
- raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
978
-
979
- return_type = context[:return_type]
980
- unless result_let_else_type?(return_type)
981
- raise LoweringError.new("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
982
- end
983
-
984
- return_error_type = let_else_error_type(return_type)
985
- unless return_error_type == error_type
986
- raise LoweringError.new("propagation error type #{error_type} must match enclosing Result error type #{return_error_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
987
- end
988
-
989
- [storage_type, success_type, return_type, error_type]
1004
+ def c_type_name(type)
1005
+ if type.is_a?(Types::Nullable)
1006
+ return "nullable_#{c_type_name(type.base)}"
990
1007
  end
991
1008
 
992
- def infer_option_propagation_details(storage_type, env:, allow_void_success:)
993
- success_type = let_else_success_type(storage_type)
994
- raise LoweringError.new("propagation requires a non-void Option success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
995
-
996
- context = env[:return_context]
997
- raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
998
- raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
999
-
1000
- return_type = context[:return_type]
1001
- unless option_let_else_type?(return_type)
1002
- raise LoweringError.new("propagation requires enclosing function/proc to return Option[_], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
1003
- end
1004
-
1005
- [storage_type, success_type, return_type, nil]
1009
+ if type.respond_to?(:linkage_name) && type.linkage_name
1010
+ return type.linkage_name
1006
1011
  end
1007
1012
 
1008
- def c_type_name(type)
1009
- if type.is_a?(Types::Nullable)
1010
- return "nullable_#{c_type_name(type.base)}"
1011
- end
1012
-
1013
- if type.respond_to?(:linkage_name) && type.linkage_name
1014
- return type.linkage_name
1015
- end
1016
-
1017
- if type.is_a?(Types::GenericInstance)
1018
- base = if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1019
- type.name
1020
- elsif type.respond_to?(:module_name) && !type.module_name.nil?
1021
- "#{module_c_prefix(type.module_name)}_#{type.name}"
1022
- else
1023
- type.name
1024
- end
1025
-
1026
- return "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1013
+ if type.is_a?(Types::GenericInstance)
1014
+ base = if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1015
+ type.name
1016
+ elsif type.respond_to?(:module_name) && !type.module_name.nil?
1017
+ "#{module_c_prefix(type.module_name)}_#{type.name}"
1018
+ else
1019
+ type.name
1027
1020
  end
1028
1021
 
1029
- return type.name if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1030
-
1031
- base = (type.respond_to?(:module_name) && type.module_name) ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
1032
- return base unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
1033
-
1034
- "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1022
+ return "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1035
1023
  end
1036
1024
 
1037
- def opaque_c_type_name(type)
1038
- type.linkage_name || c_type_name(type)
1039
- end
1025
+ return type.name if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
1040
1026
 
1041
- def opaque_forward_declarable?(type)
1042
- return false unless opaque_c_type_name(type).match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
1027
+ base = (type.respond_to?(:module_name) && type.module_name) ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
1028
+ return base unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
1043
1029
 
1044
- !type.external || type.linkage_name.nil?
1045
- end
1030
+ "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
1031
+ end
1046
1032
 
1047
- def forward_declarable_external_opaque?(type)
1048
- type.external && opaque_forward_declarable?(type)
1049
- end
1033
+ def opaque_c_type_name(type)
1034
+ type.linkage_name || c_type_name(type)
1035
+ end
1050
1036
 
1051
- def validate_generic_type!(name, arguments)
1052
- super(name, arguments) { |msg| raise LoweringError.new(msg, line: 0, column: 0, path: @ctx.current_analysis_path) }
1053
- end
1037
+ def opaque_forward_declarable?(type)
1038
+ return false unless opaque_c_type_name(type).match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
1054
1039
 
1055
- def integer_type_argument?(argument)
1056
- argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
1057
- end
1040
+ !type.external || type.linkage_name.nil?
1041
+ end
1058
1042
 
1059
- def generic_integer_type_argument?(argument)
1060
- integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
1061
- end
1043
+ def forward_declarable_external_opaque?(type)
1044
+ type.external && opaque_forward_declarable?(type)
1045
+ end
1062
1046
 
1063
- def enum_member_c_name(type, member_name)
1064
- "#{c_type_name(type)}_#{member_name}"
1065
- end
1047
+ def validate_generic_type!(name, arguments)
1048
+ super(name, arguments) { |msg| raise LoweringError.new(msg, line: 0, column: 0, path: @ctx.current_analysis_path) }
1049
+ end
1066
1050
 
1067
- def function_binding_c_name(binding, module_name:, receiver_type: nil)
1068
- if receiver_type.nil? && binding.name == "main" && binding.type_arguments.empty?
1069
- return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
1070
- end
1071
- if receiver_type
1072
- base = "#{c_type_name(receiver_type)}_#{binding.name}"
1073
- base = "#{base}_static" if binding.type.receiver_type.nil?
1074
- return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
1075
- end
1051
+ def integer_type_argument?(argument)
1052
+ argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
1053
+ end
1076
1054
 
1077
- module_function_c_name(module_name, binding.name, type_arguments: binding.type_arguments)
1078
- end
1055
+ def generic_integer_type_argument?(argument)
1056
+ integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
1057
+ end
1079
1058
 
1080
- def external_function_c_name(binding)
1081
- return binding.ast.mapping.value if binding.external && binding.ast.is_a?(AST::ExternFunctionDecl) && binding.ast.mapping
1059
+ def enum_member_c_name(type, member_name)
1060
+ "#{c_type_name(type)}_#{member_name}"
1061
+ end
1082
1062
 
1083
- binding.name
1063
+ def function_binding_c_name(binding, module_name:, receiver_type: nil)
1064
+ if receiver_type.nil? && binding.name == "main" && binding.type_arguments.empty?
1065
+ return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
1084
1066
  end
1085
-
1086
- def value_c_name(name)
1087
- module_value_c_name(@ctx.module_name, name)
1067
+ if receiver_type
1068
+ base = "#{c_type_name(receiver_type)}_#{binding.name}"
1069
+ base = "#{base}_static" if binding.type.receiver_type.nil?
1070
+ return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
1088
1071
  end
1089
1072
 
1090
- def imported_value_c_name(imported_module, name)
1091
- imported_analysis = analysis_for_module(imported_module.name)
1092
- return name if imported_analysis.module_kind == :raw_module
1073
+ module_function_c_name(module_name, binding.name, type_arguments: binding.type_arguments)
1074
+ end
1093
1075
 
1094
- module_value_c_name(imported_module.name, name)
1095
- end
1076
+ def external_function_c_name(binding)
1077
+ return binding.ast.mapping.value if binding.external && binding.ast.is_a?(AST::ExternFunctionDecl) && binding.ast.mapping
1096
1078
 
1097
- def module_function_c_name(module_name, name, type_arguments: [])
1098
- base = "#{module_c_prefix(module_name)}_#{name}"
1099
- return base if type_arguments.empty?
1079
+ binding.name
1080
+ end
1100
1081
 
1101
- # A double-underscore separates a generic function instance's type
1102
- # arguments so it cannot collide with a distinct regular function whose
1103
- # name happens to be `<name>_<typearg>` (e.g. the instance
1104
- # `expect_equal[str]` vs the function `expect_equal_str`). The method path
1105
- # in `function_binding_c_name` uses the same scheme for consistency.
1106
- "#{base}__#{generic_type_argument_suffix(type_arguments)}"
1107
- end
1082
+ def value_c_name(name)
1083
+ module_value_c_name(@ctx.module_name, name)
1084
+ end
1108
1085
 
1109
- # Joins resolved generic type arguments into the instance-name suffix used
1110
- # by both the free-function (`module_function_c_name`) and method
1111
- # (`function_binding_c_name`) paths, keeping the scheme consistent.
1112
- def generic_type_argument_suffix(type_arguments)
1113
- sanitize_identifier(type_arguments.join('_'))
1114
- end
1086
+ def imported_value_c_name(imported_module, name)
1087
+ imported_analysis = analysis_for_module(imported_module.name)
1088
+ return name if imported_analysis.module_kind == :raw_module
1115
1089
 
1116
- def module_value_c_name(module_name, name)
1117
- "#{module_c_prefix(module_name)}_#{name}"
1118
- end
1090
+ module_value_c_name(imported_module.name, name)
1091
+ end
1119
1092
 
1120
- def module_c_prefix(module_name)
1121
- sanitize_identifier(module_name.to_s.tr('.', '_'))
1122
- end
1093
+ def module_function_c_name(module_name, name, type_arguments: [])
1094
+ base = "#{module_c_prefix(module_name)}_#{name}"
1095
+ return base if type_arguments.empty?
1123
1096
 
1124
- def c_local_name(name)
1125
- return "value" unless name
1126
- identifier = sanitize_identifier(name)
1127
- return "#{identifier}_" if c_reserved_identifier?(identifier)
1097
+ # A double-underscore separates a generic function instance's type
1098
+ # arguments so it cannot collide with a distinct regular function whose
1099
+ # name happens to be `<name>_<typearg>` (e.g. the instance
1100
+ # `expect_equal[str]` vs the function `expect_equal_str`). The method path
1101
+ # in `function_binding_c_name` uses the same scheme for consistency.
1102
+ "#{base}__#{generic_type_argument_suffix(type_arguments)}"
1103
+ end
1128
1104
 
1129
- identifier
1130
- end
1105
+ # Joins resolved generic type arguments into the instance-name suffix used
1106
+ # by both the free-function (`module_function_c_name`) and method
1107
+ # (`function_binding_c_name`) paths, keeping the scheme consistent.
1108
+ def generic_type_argument_suffix(type_arguments)
1109
+ sanitize_identifier(type_arguments.join('_'))
1110
+ end
1131
1111
 
1132
- def c_reserved_identifier?(identifier)
1133
- %w[
1134
- auto break case char const continue default do double else enum extern
1135
- float for goto if inline int long register restrict return short signed
1136
- sizeof static struct switch typedef union unsigned void volatile while
1137
- _Alignas _Alignof _Atomic _Bool _Complex _Generic _Imaginary _Noreturn
1138
- _Static_assert _Thread_local
1139
- ].include?(identifier)
1140
- end
1141
-
1142
- def fresh_c_temp_name(env, prefix)
1143
- env[:counter][:value] += 1
1144
- "__mt_#{prefix}_#{env[:counter][:value]}"
1145
- end
1112
+ def module_value_c_name(module_name, name)
1113
+ "#{module_c_prefix(module_name)}_#{name}"
1114
+ end
1146
1115
 
1147
- def cleanup_safe_return_expression?(expression)
1148
- case expression
1149
- when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
1150
- true
1151
- else
1152
- false
1153
- end
1154
- end
1116
+ def module_c_prefix(module_name)
1117
+ sanitize_identifier(module_name.to_s.tr('.', '_'))
1118
+ end
1155
1119
 
1156
- def sanitize_identifier(text)
1157
- return "value" unless text
1158
-
1159
- cache = (@sanitize_identifier_cache ||= {})
1160
- cached = cache[text]
1161
- return cached if cached
1162
-
1163
- identifier = text.gsub(/[^A-Za-z0-9_]+/, "_").gsub(/_+/, "_").sub(/_+$/, "").sub(/^_{2,}/, "_")
1164
- cache[text] = identifier.empty? ? "value" : identifier
1120
+ def c_local_name(name)
1121
+ return "value" unless name
1122
+ identifier = sanitize_identifier(name)
1123
+ return "#{identifier}_" if c_reserved_identifier?(identifier)
1124
+
1125
+ identifier
1126
+ end
1127
+
1128
+ def c_reserved_identifier?(identifier)
1129
+ %w[
1130
+ auto break case char const continue default do double else enum extern
1131
+ float for goto if inline int long register restrict return short signed
1132
+ sizeof static struct switch typedef union unsigned void volatile while
1133
+ _Alignas _Alignof _Atomic _Bool _Complex _Generic _Imaginary _Noreturn
1134
+ _Static_assert _Thread_local
1135
+ ].include?(identifier)
1136
+ end
1137
+
1138
+ def fresh_c_temp_name(env, prefix)
1139
+ env[:counter][:value] += 1
1140
+ "__mt_#{prefix}_#{env[:counter][:value]}"
1141
+ end
1142
+
1143
+ def cleanup_safe_return_expression?(expression)
1144
+ case expression
1145
+ when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
1146
+ true
1147
+ else
1148
+ false
1165
1149
  end
1166
-
1167
- def lower_assignment_target(expression, env:)
1168
- case expression
1169
- when AST::Identifier
1170
- binding = lookup_value(expression.name, env)
1171
- lower_assignment_binding_target(binding)
1172
- when AST::MemberAccess
1173
- if expression.receiver.is_a?(AST::IndexAccess)
1174
- base_type = infer_expression_type(expression.receiver.receiver, env:)
1175
- if base_type.is_a?(Types::SoA)
1176
- soa_base = lower_expression(expression.receiver.receiver, env:)
1177
- index = lower_expression(expression.receiver.index, env:)
1178
- field_type = base_type.fields[expression.member]
1179
- target_type = infer_expression_type(expression, env:)
1180
- return IR::Index.new(
1181
- receiver: IR::Member.new(receiver: soa_base, member: expression.member, type: field_type),
1182
- index:,
1183
- type: target_type,
1184
- )
1185
- end
1186
- end
1187
- receiver_type = infer_expression_type(expression.receiver, env:)
1188
- receiver = lower_expression(expression.receiver, env:)
1189
- type = infer_expression_type(expression, env:)
1190
- IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
1191
- when AST::IndexAccess
1192
- receiver_type = infer_expression_type(expression.receiver, env:)
1193
- receiver = lower_expression(expression.receiver, env:)
1194
- index = lower_expression(expression.index, env:)
1195
- type = infer_expression_type(expression, env:)
1196
- if array_type?(receiver_type)
1197
- IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1198
- elsif receiver_type.is_a?(Types::Span)
1199
- IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1200
- else
1201
- IR::Index.new(receiver:, index:, type:)
1150
+ end
1151
+
1152
+ def sanitize_identifier(text)
1153
+ return "value" unless text
1154
+
1155
+ cache = (@sanitize_identifier_cache ||= {})
1156
+ cached = cache[text]
1157
+ return cached if cached
1158
+
1159
+ identifier = text.gsub(/[^A-Za-z0-9_]+/, "_").gsub(/_+/, "_").sub(/_+$/, "").sub(/^_{2,}/, "_")
1160
+ cache[text] = identifier.empty? ? "value" : identifier
1161
+ end
1162
+
1163
+ def lower_assignment_target(expression, env:)
1164
+ case expression
1165
+ when AST::Identifier
1166
+ binding = lookup_value(expression.name, env)
1167
+ lower_assignment_binding_target(binding)
1168
+ when AST::MemberAccess
1169
+ if expression.receiver.is_a?(AST::IndexAccess)
1170
+ base_type = infer_expression_type(expression.receiver.receiver, env:)
1171
+ if base_type.is_a?(Types::SoA)
1172
+ soa_base = lower_expression(expression.receiver.receiver, env:)
1173
+ index = lower_expression(expression.receiver.index, env:)
1174
+ field_type = base_type.fields[expression.member]
1175
+ target_type = infer_expression_type(expression, env:)
1176
+ return IR::Index.new(
1177
+ receiver: IR::Member.new(receiver: soa_base, member: expression.member, type: field_type),
1178
+ index:,
1179
+ type: target_type,
1180
+ )
1202
1181
  end
1203
- when AST::Call
1204
- if read_call?(expression)
1205
- type = infer_expression_type(expression, env:)
1206
- operand = lower_expression(expression.arguments.first.value, env:)
1207
- return IR::Unary.new(operator: "*", operand:, type:)
1208
- end
1209
-
1210
- raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1182
+ end
1183
+ receiver_type = infer_expression_type(expression.receiver, env:)
1184
+ receiver = lower_expression(expression.receiver, env:)
1185
+ type = infer_expression_type(expression, env:)
1186
+ IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
1187
+ when AST::IndexAccess
1188
+ receiver_type = infer_expression_type(expression.receiver, env:)
1189
+ receiver = lower_expression(expression.receiver, env:)
1190
+ index = lower_expression(expression.index, env:)
1191
+ type = infer_expression_type(expression, env:)
1192
+ if array_type?(receiver_type)
1193
+ IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1194
+ elsif receiver_type.is_a?(Types::Span)
1195
+ IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1211
1196
  else
1212
- raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1197
+ IR::Index.new(receiver:, index:, type:)
1198
+ end
1199
+ when AST::Call
1200
+ if read_call?(expression)
1201
+ type = infer_expression_type(expression, env:)
1202
+ operand = lower_expression(expression.arguments.first.value, env:)
1203
+ return IR::Unary.new(operator: "*", operand:, type:)
1213
1204
  end
1214
- end
1215
-
1216
- def lower_assignment_binding_target(binding)
1217
- storage_type = binding[:storage_type]
1218
- visible_type = binding[:type]
1219
- storage_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1220
1205
 
1221
- case binding[:projection]
1222
- when :result_success_value
1223
- variant_binding_projection_expression(storage_ref, storage_type, "success", "value", visible_type)
1224
- when :option_some_value
1225
- variant_binding_projection_expression(storage_ref, storage_type, "some", "value", visible_type)
1206
+ raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1207
+ else
1208
+ raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1209
+ end
1210
+ end
1211
+
1212
+ def lower_assignment_binding_target(binding)
1213
+ storage_type = binding[:storage_type]
1214
+ visible_type = binding[:type]
1215
+ storage_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
1216
+
1217
+ case binding[:projection]
1218
+ when :result_success_value
1219
+ variant_binding_projection_expression(storage_ref, storage_type, "success", "value", visible_type)
1220
+ when :option_some_value
1221
+ variant_binding_projection_expression(storage_ref, storage_type, "some", "value", visible_type)
1222
+ else
1223
+ if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
1224
+ IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
1226
1225
  else
1227
- if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
1228
- IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
1229
- else
1230
- storage_ref
1231
- end
1226
+ storage_ref
1232
1227
  end
1233
1228
  end
1234
-
1235
- def wrap_nullable_field_value(field_type, lowered_value, env)
1236
- return lowered_value unless field_type.is_a?(Types::Nullable)
1237
- return lowered_value if pointer_like_type?(field_type.base)
1238
- return lowered_value if lowered_value.type.is_a?(Types::Nullable)
1239
-
1240
- nullable_some_literal(field_type, lowered_value)
1241
- end
1242
-
1243
- def nullable_some_literal(nullable_type, value)
1244
- IR::AggregateLiteral.new(
1245
- type: nullable_type,
1246
- fields: [
1247
- IR::AggregateField.new(name: "has_value", value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool"))),
1248
- IR::AggregateField.new(name: "value", value:),
1249
- ],
1250
- )
1251
- end
1252
-
1253
- def pointer_like_type?(type)
1254
- 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)
1255
- end
1229
+ end
1230
+
1231
+ def wrap_nullable_field_value(field_type, lowered_value, env)
1232
+ return lowered_value unless field_type.is_a?(Types::Nullable)
1233
+ return lowered_value if pointer_like_type?(field_type.base)
1234
+ return lowered_value if lowered_value.type.is_a?(Types::Nullable)
1235
+
1236
+ nullable_some_literal(field_type, lowered_value)
1237
+ end
1238
+
1239
+ def nullable_some_literal(nullable_type, value)
1240
+ IR::AggregateLiteral.new(
1241
+ type: nullable_type,
1242
+ fields: [
1243
+ IR::AggregateField.new(name: "has_value", value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool"))),
1244
+ IR::AggregateField.new(name: "value", value:),
1245
+ ],
1246
+ )
1247
+ end
1248
+
1249
+ def pointer_like_type?(type)
1250
+ 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)
1251
+ end
1256
1252
  end
1257
1253
  end