mt-lang 0.3.24 → 0.3.26

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