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
@@ -3,1288 +3,1268 @@
3
3
  require_relative "dyn"
4
4
 
5
5
  module MilkTea
6
- module LowererCalls
7
- def lower_call(expression, env:, type:)
8
- ct_value = compile_time_const_value(expression, env:)
9
- if ct_value && (literal = lower_compile_time_literal(ct_value, type))
10
- if expression.callee.is_a?(AST::Identifier)
11
- binding = @ctx.functions[expression.callee.name]
12
- return literal unless binding&.ast&.respond_to?(:const) && binding.ast.const
13
- else
14
- return literal
6
+ module Lowering
7
+ module Calls
8
+ def lower_call(expression, env:, type:)
9
+ ct_value = compile_time_const_value(expression, env:)
10
+ if ct_value && (literal = lower_compile_time_literal(ct_value, type))
11
+ if expression.callee.is_a?(AST::Identifier)
12
+ binding = @ctx.functions[expression.callee.name]
13
+ return literal unless binding&.ast&.respond_to?(:const) && binding.ast.const
14
+ else
15
+ return literal
16
+ end
15
17
  end
16
- end
17
18
 
18
- kind, callee_name, receiver, callee_type, callee_binding = resolve_callee(expression.callee, env, arguments: expression.arguments)
19
+ kind, callee_name, receiver, callee_type, callee_binding = resolve_callee(expression.callee, env, arguments: expression.arguments)
19
20
 
20
- case kind
21
- when :function
22
- if callee_binding && foreign_function_binding?(callee_binding)
23
- raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_call_consumes_binding?(callee_binding)
21
+ case kind
22
+ when :function
23
+ if callee_binding && foreign_function_binding?(callee_binding)
24
+ raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_call_consumes_binding?(callee_binding)
24
25
 
25
- return lower_foreign_call_inline(expression, callee_binding, env:, type:)
26
- end
26
+ return lower_foreign_call_inline(expression, callee_binding, env:, type:)
27
+ end
27
28
 
28
- lowered_arguments = expression.arguments
29
- lowered_arguments = expand_function_defaults(lowered_arguments, callee_binding) if callee_binding
30
- arguments = lower_call_arguments(lowered_arguments, callee_type, env:)
31
- IR::Call.new(callee: callee_name, arguments:, type:)
32
- when :callable_value
33
- callee_expression = lower_expression(expression.callee, env:, expected_type: callee_type)
34
- if proc_type?(callee_type)
35
- arguments = [
36
- IR::Member.new(receiver: callee_expression, member: "env", type: proc_env_pointer_type),
37
- *lower_call_arguments(expression.arguments, callee_type, env:),
38
- ]
29
+ lowered_arguments = expression.arguments
30
+ lowered_arguments = expand_function_defaults(lowered_arguments, callee_binding) if callee_binding
31
+ arguments = lower_call_arguments(lowered_arguments, callee_type, env:)
32
+ IR::Call.new(callee: callee_name, arguments:, type:)
33
+ when :callable_value
34
+ callee_expression = lower_expression(expression.callee, env:, expected_type: callee_type)
35
+ if proc_type?(callee_type)
36
+ arguments = [
37
+ IR::Member.new(receiver: callee_expression, member: "env", type: proc_env_pointer_type),
38
+ *lower_call_arguments(expression.arguments, callee_type, env:),
39
+ ]
40
+ IR::Call.new(
41
+ callee: IR::Member.new(receiver: callee_expression, member: "invoke", type: proc_invoke_function_type(callee_type)),
42
+ arguments:,
43
+ type:,
44
+ )
45
+ else
46
+ arguments = lower_call_arguments(expression.arguments, callee_type, env:)
47
+ IR::Call.new(callee: callee_expression, arguments:, type:)
48
+ end
49
+ when :method
50
+ receiver_arg = lower_method_receiver_argument(receiver, callee_type, callee_binding, env:)
51
+ arguments = [receiver_arg, *lower_call_arguments(expression.arguments, callee_type, env:)]
52
+ IR::Call.new(callee: callee_name, arguments:, type:)
53
+ when :str_buffer_clear
39
54
  IR::Call.new(
40
- callee: IR::Member.new(receiver: callee_expression, member: "invoke", type: proc_invoke_function_type(callee_type)),
41
- arguments:,
55
+ callee: "mt_str_buffer_clear",
56
+ arguments: str_buffer_args(receiver, env:),
42
57
  type:,
43
58
  )
44
- else
45
- arguments = lower_call_arguments(expression.arguments, callee_type, env:)
46
- IR::Call.new(callee: callee_expression, arguments:, type:)
47
- end
48
- when :method
49
- receiver_arg = lower_method_receiver_argument(receiver, callee_type, callee_binding, env:)
50
- arguments = [receiver_arg, *lower_call_arguments(expression.arguments, callee_type, env:)]
51
- IR::Call.new(callee: callee_name, arguments:, type:)
52
- when :str_buffer_clear
53
- IR::Call.new(
54
- callee: "mt_str_buffer_clear",
55
- arguments: str_buffer_args(receiver, env:),
56
- type:,
57
- )
58
- when :str_buffer_assign, :str_buffer_assign_format
59
- IR::Call.new(
60
- callee: "mt_str_buffer_assign",
61
- arguments: [
62
- lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("str")),
63
- *str_buffer_args(receiver, env:),
64
- ],
65
- type:,
66
- )
67
- when :str_buffer_append, :str_buffer_append_format
68
- IR::Call.new(
69
- callee: "mt_str_buffer_append",
70
- arguments: [
71
- lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("str")),
72
- *str_buffer_args(receiver, env:),
73
- ],
74
- type:,
75
- )
76
- when :str_buffer_len
77
- IR::Call.new(
78
- callee: "mt_str_buffer_len",
79
- arguments: str_buffer_args(receiver, env:),
80
- type:,
81
- )
82
- when :str_buffer_capacity
83
- receiver_type = infer_expression_type(receiver, env:)
84
- IR::IntegerLiteral.new(value: str_buffer_capacity(receiver_type), type: type)
85
- when :str_buffer_as_str
86
- args = str_buffer_args(receiver, env:)
87
- IR::AggregateLiteral.new(
88
- type:,
89
- fields: [
90
- IR::AggregateField.new(name: "data", value: args[0]),
91
- IR::AggregateField.new(
92
- name: "len",
93
- value: IR::Call.new(
94
- callee: "mt_str_buffer_len",
95
- arguments: args,
96
- type: @ctx.types.fetch("ptr_uint"),
97
- ),
98
- ),
99
- ],
100
- )
101
- when :str_buffer_as_cstr
102
- IR::Call.new(
103
- callee: "mt_str_buffer_as_cstr",
104
- arguments: str_buffer_args(receiver, env:),
105
- type:,
106
- )
107
- when :array_as_span
108
- receiver_type = infer_expression_type(receiver, env:)
109
- lower_array_to_span_expression(lower_expression(receiver, env:), type)
110
- when :event_subscribe, :event_subscribe_once, :event_unsubscribe, :event_emit, :event_wait
111
- event_type = infer_expression_type(receiver, env:)
112
- runtime = ensure_event_runtime(event_type)
113
- event_pointer = lower_event_storage_pointer(receiver, env:)
114
-
115
- case kind
116
- when :event_subscribe
117
- lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:)
118
- when :event_subscribe_once
119
- lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: true)
120
- when :event_unsubscribe
59
+ when :str_buffer_assign, :str_buffer_assign_format
121
60
  IR::Call.new(
122
- callee: runtime.fetch(:unsubscribe_linkage_name),
61
+ callee: "mt_str_buffer_assign",
123
62
  arguments: [
124
- event_pointer,
125
- lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("Subscription")),
63
+ lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("str")),
64
+ *str_buffer_args(receiver, env:),
126
65
  ],
127
66
  type:,
128
67
  )
129
- when :event_emit
130
- arguments = [event_pointer]
131
- if event_type.payload_type
132
- arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: event_type.payload_type)
133
- end
134
- IR::Call.new(callee: runtime.fetch(:emit_linkage_name), arguments:, type:)
135
- when :event_wait
136
- IR::Call.new(callee: runtime.fetch(:wait_linkage_name), arguments: [event_pointer], type:)
137
- end
138
- when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange
139
- lower_atomic_method_call(kind, receiver, expression, env:, type:)
140
- when :simd_lane_with
141
- lower_simd_lane_with(kind, receiver, expression, env:, type:)
142
- when :associated_method
143
- arguments = lower_call_arguments(expression.arguments, callee_type, env:)
144
- IR::Call.new(callee: callee_name, arguments:, type:)
145
- when :struct_literal
146
- fields = expression.arguments.map do |argument|
147
- field_type = type.field(argument.name)
148
- lowered_value = lower_contextual_expression(
149
- argument.value,
150
- env:,
151
- expected_type: field_type,
152
- external_numeric: type.respond_to?(:external) && type.external,
153
- contextual_int_to_float: contextual_int_to_float_target?(field_type),
68
+ when :str_buffer_append, :str_buffer_append_format
69
+ IR::Call.new(
70
+ callee: "mt_str_buffer_append",
71
+ arguments: [
72
+ lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("str")),
73
+ *str_buffer_args(receiver, env:),
74
+ ],
75
+ type:,
154
76
  )
155
- lowered_value = wrap_nullable_field_value(field_type, lowered_value, env)
156
- IR::AggregateField.new(
157
- name: argument.name,
158
- value: lowered_value,
77
+ when :str_buffer_len
78
+ IR::Call.new(
79
+ callee: "mt_str_buffer_len",
80
+ arguments: str_buffer_args(receiver, env:),
81
+ type:,
159
82
  )
160
- end
161
- IR::AggregateLiteral.new(type:, fields:)
162
- when :struct_with
163
- explicit_names = expression.arguments.each_with_object({}) { |a, h| h[a.name] = a }
164
- lowered_receiver = lower_expression(receiver, env:)
165
- field_hash = callee_type.respond_to?(:fields) ? callee_type.fields : {}
166
- fields = field_hash.map do |field_name, field_type|
167
- if (explicit_arg = explicit_names[field_name])
168
- IR::AggregateField.new(
169
- name: field_name,
170
- value: lower_contextual_expression(
171
- explicit_arg.value,
172
- env:,
173
- expected_type: field_type,
174
- contextual_int_to_float: contextual_int_to_float_target?(field_type),
83
+ when :str_buffer_capacity
84
+ receiver_type = infer_expression_type(receiver, env:)
85
+ IR::IntegerLiteral.new(value: str_buffer_capacity(receiver_type), type: type)
86
+ when :str_buffer_as_str
87
+ args = str_buffer_args(receiver, env:)
88
+ IR::AggregateLiteral.new(
89
+ type:,
90
+ fields: [
91
+ IR::AggregateField.new(name: "data", value: args[0]),
92
+ IR::AggregateField.new(
93
+ name: "len",
94
+ value: IR::Call.new(
95
+ callee: "mt_str_buffer_len",
96
+ arguments: args,
97
+ type: @ctx.types.fetch("ptr_uint"),
98
+ ),
175
99
  ),
100
+ ],
101
+ )
102
+ when :str_buffer_as_cstr
103
+ IR::Call.new(
104
+ callee: "mt_str_buffer_as_cstr",
105
+ arguments: str_buffer_args(receiver, env:),
106
+ type:,
107
+ )
108
+ when :array_as_span
109
+ receiver_type = infer_expression_type(receiver, env:)
110
+ lower_array_to_span_expression(lower_expression(receiver, env:), type)
111
+ when :event_subscribe, :event_subscribe_once, :event_unsubscribe, :event_emit, :event_wait
112
+ event_type = infer_expression_type(receiver, env:)
113
+ runtime = ensure_event_runtime(event_type)
114
+ event_pointer = lower_event_storage_pointer(receiver, env:)
115
+
116
+ case kind
117
+ when :event_subscribe
118
+ lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:)
119
+ when :event_subscribe_once
120
+ lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: true)
121
+ when :event_unsubscribe
122
+ IR::Call.new(
123
+ callee: runtime.fetch(:unsubscribe_linkage_name),
124
+ arguments: [
125
+ event_pointer,
126
+ lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: @ctx.types.fetch("Subscription")),
127
+ ],
128
+ type:,
176
129
  )
177
- else
130
+ when :event_emit
131
+ arguments = [event_pointer]
132
+ if event_type.payload_type
133
+ arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: event_type.payload_type)
134
+ end
135
+ IR::Call.new(callee: runtime.fetch(:emit_linkage_name), arguments:, type:)
136
+ when :event_wait
137
+ IR::Call.new(callee: runtime.fetch(:wait_linkage_name), arguments: [event_pointer], type:)
138
+ end
139
+ when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange
140
+ lower_atomic_method_call(kind, receiver, expression, env:, type:)
141
+ when :simd_lane_with
142
+ lower_simd_lane_with(kind, receiver, expression, env:, type:)
143
+ when :associated_method
144
+ arguments = lower_call_arguments(expression.arguments, callee_type, env:)
145
+ IR::Call.new(callee: callee_name, arguments:, type:)
146
+ when :struct_literal
147
+ fields = expression.arguments.map do |argument|
148
+ field_type = type.field(argument.name)
149
+ lowered_value = lower_contextual_expression(
150
+ argument.value,
151
+ env:,
152
+ expected_type: field_type,
153
+ external_numeric: type.respond_to?(:external) && type.external,
154
+ contextual_int_to_float: contextual_int_to_float_target?(field_type),
155
+ )
156
+ lowered_value = wrap_nullable_field_value(field_type, lowered_value, env)
157
+ IR::AggregateField.new(
158
+ name: argument.name,
159
+ value: lowered_value,
160
+ )
161
+ end
162
+ IR::AggregateLiteral.new(type:, fields:)
163
+ when :struct_with
164
+ explicit_names = expression.arguments.each_with_object({}) { |a, h| h[a.name] = a }
165
+ lowered_receiver = lower_expression(receiver, env:)
166
+ field_hash = callee_type.respond_to?(:fields) ? callee_type.fields : {}
167
+ fields = field_hash.map do |field_name, field_type|
168
+ if (explicit_arg = explicit_names[field_name])
169
+ IR::AggregateField.new(
170
+ name: field_name,
171
+ value: lower_contextual_expression(
172
+ explicit_arg.value,
173
+ env:,
174
+ expected_type: field_type,
175
+ contextual_int_to_float: contextual_int_to_float_target?(field_type),
176
+ ),
177
+ )
178
+ else
179
+ IR::AggregateField.new(
180
+ name: field_name,
181
+ value: IR::Member.new(
182
+ receiver: lowered_receiver,
183
+ member: field_name,
184
+ type: field_type,
185
+ ),
186
+ )
187
+ end
188
+ end
189
+ IR::AggregateLiteral.new(type: callee_type, fields:)
190
+ when :variant_arm_ctor
191
+ _, _, _, variant_type, (_, arm_name) = resolve_callee(expression.callee, env, arguments: expression.arguments)
192
+ arm_fields = variant_type.arm(arm_name)
193
+ provided_names = expression.arguments.map(&:name).to_set
194
+ payload_fields = expression.arguments.map do |argument|
195
+ field_type = arm_fields.fetch(argument.name)
196
+ lowered_value = lower_contextual_expression(
197
+ argument.value,
198
+ env:,
199
+ expected_type: field_type,
200
+ contextual_int_to_float: contextual_int_to_float_target?(field_type),
201
+ )
202
+ lowered_value = IR::AddressOf.new(expression: lowered_value, type: lowered_value.type) if field_type == variant_type
203
+ lowered_value = wrap_nullable_field_value(field_type, lowered_value, env) unless field_type == variant_type
178
204
  IR::AggregateField.new(
205
+ name: argument.name,
206
+ value: lowered_value,
207
+ )
208
+ end
209
+ arm_fields.each do |field_name, field_type|
210
+ next if provided_names.include?(field_name)
211
+ next unless field_type.void?
212
+
213
+ payload_fields << IR::AggregateField.new(
179
214
  name: field_name,
180
- value: IR::Member.new(
181
- receiver: lowered_receiver,
182
- member: field_name,
183
- type: field_type,
184
- ),
215
+ value: IR::IntegerLiteral.new(type: @ctx.types.fetch("ubyte"), value: 0),
185
216
  )
186
217
  end
187
- end
188
- IR::AggregateLiteral.new(type: callee_type, fields:)
189
- when :variant_arm_ctor
190
- _, _, _, variant_type, (_, arm_name) = resolve_callee(expression.callee, env, arguments: expression.arguments)
191
- arm_fields = variant_type.arm(arm_name)
192
- provided_names = expression.arguments.map(&:name).to_set
193
- payload_fields = expression.arguments.map do |argument|
194
- field_type = arm_fields.fetch(argument.name)
195
- lowered_value = lower_contextual_expression(
196
- argument.value,
197
- env:,
198
- expected_type: field_type,
199
- contextual_int_to_float: contextual_int_to_float_target?(field_type),
218
+ IR::VariantLiteral.new(type: variant_type, arm_name:, fields: payload_fields)
219
+ when :array
220
+ element_type = array_element_type(type)
221
+ elements = expression.arguments.map do |argument|
222
+ lower_contextual_expression(argument.value, env:, expected_type: element_type)
223
+ end
224
+ IR::ArrayLiteral.new(type:, elements:)
225
+ when :simd
226
+ element_type = simd_type?(type) ? type.element_type : array_element_type(type)
227
+ elements = expression.arguments.map do |argument|
228
+ lower_contextual_expression(argument.value, env:, expected_type: element_type)
229
+ end
230
+ IR::ArrayLiteral.new(type:, elements:)
231
+ when :reinterpret
232
+ argument = expression.arguments.fetch(0)
233
+ source_type = infer_expression_type(argument.value, env:)
234
+ IR::ReinterpretExpr.new(
235
+ target_type: type,
236
+ source_type:,
237
+ expression: lower_expression(argument.value, env:, expected_type: source_type),
238
+ type:,
200
239
  )
201
- lowered_value = IR::AddressOf.new(expression: lowered_value, type: lowered_value.type) if field_type == variant_type
202
- lowered_value = wrap_nullable_field_value(field_type, lowered_value, env) unless field_type == variant_type
203
- IR::AggregateField.new(
204
- name: argument.name,
205
- value: lowered_value,
240
+ when :hash
241
+ resolution = resolve_hash_specialization(expression.callee, env:)
242
+ argument = expression.arguments.fetch(0)
243
+ IR::Call.new(
244
+ callee: resolution.callee_name,
245
+ arguments: [lower_hash_operation_argument(argument.value, env:, target_type: resolution.target_type)],
246
+ type:,
206
247
  )
207
- end
208
- arm_fields.each do |field_name, field_type|
209
- next if provided_names.include?(field_name)
210
- next unless field_type.void?
211
-
212
- payload_fields << IR::AggregateField.new(
213
- name: field_name,
214
- value: IR::IntegerLiteral.new(type: @ctx.types.fetch("ubyte"), value: 0),
248
+ when :equal
249
+ resolution = resolve_equal_specialization(expression.callee, env:)
250
+ left = expression.arguments.fetch(0)
251
+ right = expression.arguments.fetch(1)
252
+ IR::Call.new(
253
+ callee: resolution.callee_name,
254
+ arguments: [
255
+ lower_hash_operation_argument(left.value, env:, target_type: resolution.target_type),
256
+ lower_hash_operation_argument(right.value, env:, target_type: resolution.target_type),
257
+ ],
258
+ type:,
215
259
  )
216
- end
217
- IR::VariantLiteral.new(type: variant_type, arm_name:, fields: payload_fields)
218
- when :array
219
- element_type = array_element_type(type)
220
- elements = expression.arguments.map do |argument|
221
- lower_contextual_expression(argument.value, env:, expected_type: element_type)
222
- end
223
- IR::ArrayLiteral.new(type:, elements:)
224
- when :simd
225
- element_type = simd_type?(type) ? type.element_type : array_element_type(type)
226
- elements = expression.arguments.map do |argument|
227
- lower_contextual_expression(argument.value, env:, expected_type: element_type)
228
- end
229
- IR::ArrayLiteral.new(type:, elements:)
230
- when :reinterpret
231
- argument = expression.arguments.fetch(0)
232
- source_type = infer_expression_type(argument.value, env:)
233
- IR::ReinterpretExpr.new(
234
- target_type: type,
235
- source_type:,
236
- expression: lower_expression(argument.value, env:, expected_type: source_type),
237
- type:,
238
- )
239
- when :hash
240
- resolution = resolve_hash_specialization(expression.callee, env:)
241
- argument = expression.arguments.fetch(0)
242
- IR::Call.new(
243
- callee: resolution.callee_name,
244
- arguments: [lower_hash_operation_argument(argument.value, env:, target_type: resolution.target_type)],
245
- type:,
246
- )
247
- when :equal
248
- resolution = resolve_equal_specialization(expression.callee, env:)
249
- left = expression.arguments.fetch(0)
250
- right = expression.arguments.fetch(1)
251
- IR::Call.new(
252
- callee: resolution.callee_name,
253
- arguments: [
254
- lower_hash_operation_argument(left.value, env:, target_type: resolution.target_type),
255
- lower_hash_operation_argument(right.value, env:, target_type: resolution.target_type),
256
- ],
257
- type:,
258
- )
259
- when :order
260
- resolution = resolve_order_specialization(expression.callee, env:)
261
- left = expression.arguments.fetch(0)
262
- right = expression.arguments.fetch(1)
263
- IR::Call.new(
264
- callee: resolution.callee_name,
265
- arguments: [
266
- lower_hash_operation_argument(left.value, env:, target_type: resolution.target_type),
267
- lower_hash_operation_argument(right.value, env:, target_type: resolution.target_type),
268
- ],
269
- type:,
270
- )
271
- when :zero
272
- IR::ZeroInit.new(type:)
273
- when :fatal
274
- argument = expression.arguments.fetch(0)
275
- message_type = infer_expression_type(argument.value, env:)
276
- callee = message_type == @ctx.types.fetch("cstr") ? "mt_fatal" : "mt_fatal_str"
277
- IR::Call.new(callee:, arguments: [lower_expression(argument.value, env:, expected_type: message_type)], type:)
278
- when :get
279
- receiver_arg = expression.arguments.fetch(0)
280
- index_arg = expression.arguments.fetch(1)
281
- receiver_type = infer_expression_type(receiver_arg.value, env:)
282
- receiver = lower_expression(receiver_arg.value, env:)
283
- index = lower_expression(index_arg.value, env:)
284
- if array_type?(receiver_type)
285
- IR::NullableIndex.new(receiver:, index:, receiver_type:, type:)
286
- elsif receiver_type.is_a?(Types::Span)
287
- IR::NullableSpanIndex.new(receiver:, index:, receiver_type:, type:)
288
- else
289
- raise LoweringError.new("get expects an array or span, got #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
290
- end
291
- when :ref_of
292
- argument = expression.arguments.fetch(0)
293
- lower_addr_expression(argument.value, env:, target_type: type)
294
- when :const_ptr_of
295
- argument = expression.arguments.fetch(0)
296
- lower_addr_expression(argument.value, env:, target_type: type)
297
- when :read
298
- argument = expression.arguments.fetch(0)
299
- IR::Unary.new(operator: "*", operand: lower_expression(argument.value, env:), type:)
300
- when :ptr_of
301
- argument = expression.arguments.fetch(0)
302
- argument_type = infer_expression_type(argument.value, env:)
303
- if ref_type?(argument_type)
304
- IR::Cast.new(target_type: type, expression: lower_expression(argument.value, env:), type:)
305
- else
260
+ when :order
261
+ resolution = resolve_order_specialization(expression.callee, env:)
262
+ left = expression.arguments.fetch(0)
263
+ right = expression.arguments.fetch(1)
264
+ IR::Call.new(
265
+ callee: resolution.callee_name,
266
+ arguments: [
267
+ lower_hash_operation_argument(left.value, env:, target_type: resolution.target_type),
268
+ lower_hash_operation_argument(right.value, env:, target_type: resolution.target_type),
269
+ ],
270
+ type:,
271
+ )
272
+ when :zero
273
+ IR::ZeroInit.new(type:)
274
+ when :fatal
275
+ argument = expression.arguments.fetch(0)
276
+ message_type = infer_expression_type(argument.value, env:)
277
+ callee = message_type == @ctx.types.fetch("cstr") ? "mt_fatal" : "mt_fatal_str"
278
+ IR::Call.new(callee:, arguments: [lower_expression(argument.value, env:, expected_type: message_type)], type:)
279
+ when :get
280
+ receiver_arg = expression.arguments.fetch(0)
281
+ index_arg = expression.arguments.fetch(1)
282
+ receiver_type = infer_expression_type(receiver_arg.value, env:)
283
+ receiver = lower_expression(receiver_arg.value, env:)
284
+ index = lower_expression(index_arg.value, env:)
285
+ if array_type?(receiver_type)
286
+ IR::NullableIndex.new(receiver:, index:, receiver_type:, type:)
287
+ elsif receiver_type.is_a?(Types::Span)
288
+ IR::NullableSpanIndex.new(receiver:, index:, receiver_type:, type:)
289
+ else
290
+ raise LoweringError.new("get expects an array or span, got #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
291
+ end
292
+ when :ref_of
293
+ argument = expression.arguments.fetch(0)
294
+ lower_addr_expression(argument.value, env:, target_type: type)
295
+ when :const_ptr_of
296
+ argument = expression.arguments.fetch(0)
306
297
  lower_addr_expression(argument.value, env:, target_type: type)
298
+ when :read
299
+ argument = expression.arguments.fetch(0)
300
+ IR::Unary.new(operator: "*", operand: lower_expression(argument.value, env:), type:)
301
+ when :ptr_of
302
+ argument = expression.arguments.fetch(0)
303
+ argument_type = infer_expression_type(argument.value, env:)
304
+ if ref_type?(argument_type)
305
+ IR::Cast.new(target_type: type, expression: lower_expression(argument.value, env:), type:)
306
+ else
307
+ lower_addr_expression(argument.value, env:, target_type: type)
308
+ end
309
+ when :adapt
310
+ lower_adapt_call(expression, env:, type:, interface: callee_binding)
311
+ when :dyn_method
312
+ lower_dyn_method_call(expression, receiver, callee_type, env:, type:)
313
+ else
314
+ raise LoweringError.new("unsupported call kind #{kind}", line: 0, column: 0, path: @ctx.current_analysis_path)
307
315
  end
308
- when :adapt
309
- lower_adapt_call(expression, env:, type:, interface: callee_binding)
310
- when :dyn_method
311
- lower_dyn_method_call(expression, receiver, callee_type, env:, type:)
312
- else
313
- raise LoweringError.new("unsupported call kind #{kind}", line: 0, column: 0, path: @ctx.current_analysis_path)
314
316
  end
315
- end
316
317
 
317
- def lower_method_receiver_argument(receiver, callee_type, callee_binding, env:)
318
- lowered_receiver = lower_expression(receiver, env:)
319
- declared_receiver_type = callee_type.receiver_type
318
+ def lower_method_receiver_argument(receiver, callee_type, callee_binding, env:)
319
+ lowered_receiver = lower_expression(receiver, env:)
320
+ declared_receiver_type = callee_type.receiver_type
321
+
322
+ if pointer_lowered_method_receiver?(callee_type, callee_binding)
323
+ return lowered_receiver if ref_type?(lowered_receiver.type)
324
+ return lowered_receiver if pointer_type?(lowered_receiver.type)
325
+
326
+ if lowered_receiver.is_a?(IR::Name) && lowered_receiver.pointer
327
+ return lowered_receiver
328
+ end
329
+
330
+ if lowered_receiver.is_a?(IR::Unary) && lowered_receiver.operator == "*"
331
+ return lowered_receiver.operand
332
+ end
333
+
334
+ return IR::AddressOf.new(expression: lowered_receiver, type: lowered_receiver.type)
335
+ end
336
+
337
+ return lowered_receiver if declared_receiver_type && pointer_type?(declared_receiver_type)
320
338
 
321
- if pointer_lowered_method_receiver?(callee_type, callee_binding)
322
- return lowered_receiver if ref_type?(lowered_receiver.type)
323
- return lowered_receiver if pointer_type?(lowered_receiver.type)
339
+ if ref_type?(lowered_receiver.type)
340
+ return IR::Unary.new(operator: "*", operand: lowered_receiver, type: referenced_type(lowered_receiver.type))
341
+ end
324
342
 
325
343
  if lowered_receiver.is_a?(IR::Name) && lowered_receiver.pointer
326
- return lowered_receiver
344
+ return IR::Unary.new(operator: "*", operand: lowered_receiver, type: lowered_receiver.type)
327
345
  end
328
346
 
329
- if lowered_receiver.is_a?(IR::Unary) && lowered_receiver.operator == "*"
330
- return lowered_receiver.operand
347
+ if pointer_type?(lowered_receiver.type)
348
+ return IR::Unary.new(operator: "*", operand: lowered_receiver, type: pointee_type(lowered_receiver.type))
331
349
  end
332
350
 
333
- return IR::AddressOf.new(expression: lowered_receiver, type: lowered_receiver.type)
351
+ lowered_receiver
334
352
  end
335
353
 
336
- return lowered_receiver if declared_receiver_type && pointer_type?(declared_receiver_type)
354
+ def lower_addr_expression(expression, env:, target_type:)
355
+ lowered_expression = lower_expression(expression, env:)
356
+ return cast_expression(lowered_expression, target_type) if lowered_expression.is_a?(IR::Name) && lowered_expression.pointer
337
357
 
338
- if ref_type?(lowered_receiver.type)
339
- return IR::Unary.new(operator: "*", operand: lowered_receiver, type: referenced_type(lowered_receiver.type))
340
- end
358
+ if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
359
+ return cast_expression(lowered_expression.operand, target_type)
360
+ end
341
361
 
342
- if lowered_receiver.is_a?(IR::Name) && lowered_receiver.pointer
343
- return IR::Unary.new(operator: "*", operand: lowered_receiver, type: lowered_receiver.type)
362
+ IR::AddressOf.new(expression: lowered_expression, type: target_type)
344
363
  end
345
364
 
346
- if pointer_type?(lowered_receiver.type)
347
- return IR::Unary.new(operator: "*", operand: lowered_receiver, type: pointee_type(lowered_receiver.type))
348
- end
365
+ def lower_hash_operation_argument(expression, env:, target_type:)
366
+ actual_type = infer_expression_type(expression, env:)
367
+ lowered_expression = lower_expression(expression, env:)
368
+ pointer_type = const_pointer_to(target_type)
349
369
 
350
- lowered_receiver
351
- end
370
+ if pointer_type?(actual_type) || ref_type?(actual_type)
371
+ return cast_expression(lowered_expression, pointer_type)
372
+ end
352
373
 
353
- def lower_addr_expression(expression, env:, target_type:)
354
- lowered_expression = lower_expression(expression, env:)
355
- return cast_expression(lowered_expression, target_type) if lowered_expression.is_a?(IR::Name) && lowered_expression.pointer
374
+ return cast_expression(lowered_expression.operand, pointer_type) if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
356
375
 
357
- if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
358
- return cast_expression(lowered_expression.operand, target_type)
376
+ IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
359
377
  end
360
378
 
361
- IR::AddressOf.new(expression: lowered_expression, type: target_type)
362
- end
363
-
364
- def lower_hash_operation_argument(expression, env:, target_type:)
365
- actual_type = infer_expression_type(expression, env:)
366
- lowered_expression = lower_expression(expression, env:)
367
- pointer_type = const_pointer_to(target_type)
379
+ def expand_function_defaults(arguments, binding)
380
+ return arguments unless binding.respond_to?(:ast) && binding.ast.respond_to?(:params)
368
381
 
369
- if pointer_type?(actual_type) || ref_type?(actual_type)
370
- return cast_expression(lowered_expression, pointer_type)
371
- end
382
+ ast_params = binding.ast.params
383
+ has_defaults = ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value }
384
+ return arguments unless has_defaults || arguments.any?(&:name)
372
385
 
373
- return cast_expression(lowered_expression.operand, pointer_type) if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
386
+ if arguments.any?(&:name)
387
+ by_position = Array.new(ast_params.length)
388
+ param_names = ast_params.map(&:name)
374
389
 
375
- IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
376
- end
390
+ arguments.each do |arg|
391
+ if arg.name
392
+ param_idx = param_names.index(arg.name)
393
+ next unless param_idx
377
394
 
378
- def expand_function_defaults(arguments, binding)
379
- return arguments unless binding.respond_to?(:ast) && binding.ast.respond_to?(:params)
395
+ by_position[param_idx] = arg
396
+ else
397
+ first_empty = by_position.index(nil)
398
+ by_position[first_empty] = arg if first_empty
399
+ end
400
+ end
380
401
 
381
- ast_params = binding.ast.params
382
- has_defaults = ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value }
383
- return arguments unless has_defaults || arguments.any?(&:name)
402
+ by_position.each_with_index do |slot, idx|
403
+ next if slot
404
+ next unless ast_params[idx].respond_to?(:default_value) && ast_params[idx].default_value
384
405
 
385
- if arguments.any?(&:name)
386
- by_position = Array.new(ast_params.length)
387
- param_names = ast_params.map(&:name)
406
+ by_position[idx] = AST::Argument.new(name: nil, value: ast_params[idx].default_value)
407
+ end
388
408
 
389
- arguments.each do |arg|
390
- if arg.name
391
- param_idx = param_names.index(arg.name)
392
- next unless param_idx
409
+ by_position.compact
410
+ else
411
+ expanded = arguments.dup
412
+ while expanded.length < ast_params.length
413
+ ast_param = ast_params[expanded.length]
414
+ break unless ast_param.respond_to?(:default_value) && ast_param.default_value
393
415
 
394
- by_position[param_idx] = arg
395
- else
396
- first_empty = by_position.index(nil)
397
- by_position[first_empty] = arg if first_empty
416
+ expanded << AST::Argument.new(name: nil, value: ast_param.default_value)
398
417
  end
418
+ expanded
399
419
  end
420
+ end
400
421
 
401
- by_position.each_with_index do |slot, idx|
402
- next if slot
403
- next unless ast_params[idx].respond_to?(:default_value) && ast_params[idx].default_value
422
+ def lower_call_arguments(arguments, callee_type, env:)
423
+ arguments.map.with_index do |argument, index|
424
+ parameter = index < callee_type.params.length ? callee_type.params[index] : nil
425
+ expected_type = parameter&.type
426
+ external_call = callee_type.respond_to?(:external) && callee_type.external && !expected_type.nil?
427
+ if external_call && parameter && %i[out inout].include?(parameter.passing_mode)
428
+ next lower_foreign_pointer_argument_value(parameter, argument, env:)
429
+ end
404
430
 
405
- by_position[idx] = AST::Argument.new(name: nil, value: ast_params[idx].default_value)
431
+ lower_contextual_expression(
432
+ argument.value,
433
+ env:,
434
+ expected_type:,
435
+ external_numeric: external_call,
436
+ contextual_int_to_float: expected_type && contextual_int_to_float_target?(expected_type) && !external_call,
437
+ )
406
438
  end
439
+ end
407
440
 
408
- by_position.compact
409
- else
410
- expanded = arguments.dup
411
- while expanded.length < ast_params.length
412
- ast_param = ast_params[expanded.length]
413
- break unless ast_param.respond_to?(:default_value) && ast_param.default_value
441
+ def implicit_ref_argument_bridge?(expression, expected_type, env:)
442
+ return false unless ref_type?(expected_type)
414
443
 
415
- expanded << AST::Argument.new(name: nil, value: ast_param.default_value)
416
- end
417
- expanded
444
+ actual_type = infer_expression_type(expression, env:)
445
+ actual_type == referenced_type(expected_type) && addressable_storage_expression?(expression)
418
446
  end
419
- end
420
447
 
421
- def lower_call_arguments(arguments, callee_type, env:)
422
- arguments.map.with_index do |argument, index|
423
- parameter = index < callee_type.params.length ? callee_type.params[index] : nil
424
- expected_type = parameter&.type
425
- external_call = callee_type.respond_to?(:external) && callee_type.external && !expected_type.nil?
426
- if external_call && parameter && %i[out inout].include?(parameter.passing_mode)
427
- next lower_foreign_pointer_argument_value(parameter, argument, env:)
428
- end
448
+ def task_expression_root_proc_bridge?(expression, expected_type, env:)
449
+ return false unless task_root_proc_type?(expected_type)
429
450
 
430
- lower_contextual_expression(
431
- argument.value,
432
- env:,
433
- expected_type:,
434
- external_numeric: external_call,
435
- contextual_int_to_float: expected_type && contextual_int_to_float_target?(expected_type) && !external_call,
436
- )
451
+ actual_type = infer_expression_type(expression, env:)
452
+ actual_type.is_a?(Types::Task) && actual_type == expected_type.return_type
437
453
  end
438
- end
439
454
 
440
- def implicit_ref_argument_bridge?(expression, expected_type, env:)
441
- return false unless ref_type?(expected_type)
455
+ def wrap_task_expression_in_root_proc(expression, env:)
456
+ task_type = infer_expression_type(expression, env:)
457
+ AST::ProcExpr.new(
458
+ params: [],
459
+ return_type: ast_type_ref_for(task_type),
460
+ body: [AST::ReturnStmt.new(value: expression)],
461
+ )
462
+ end
442
463
 
443
- actual_type = infer_expression_type(expression, env:)
444
- actual_type == referenced_type(expected_type) && addressable_storage_expression?(expression)
445
- end
464
+ def wrap_expression_in_ref_of(expression)
465
+ AST::Call.new(
466
+ callee: AST::Identifier.new(name: "ref_of"),
467
+ arguments: [AST::Argument.new(name: nil, value: expression)],
468
+ )
469
+ end
446
470
 
447
- def task_expression_root_proc_bridge?(expression, expected_type, env:)
448
- return false unless task_root_proc_type?(expected_type)
471
+ def foreign_call_info(expression, env)
472
+ call = expression if expression.is_a?(AST::Call)
473
+ return unless call
449
474
 
450
- actual_type = infer_expression_type(expression, env:)
451
- actual_type.is_a?(Types::Task) && actual_type == expected_type.return_type
452
- end
475
+ kind, _, _, _, binding = resolve_callee(call.callee, env, arguments: call.arguments)
476
+ return unless kind == :function && binding && foreign_function_binding?(binding)
453
477
 
454
- def wrap_task_expression_in_root_proc(expression, env:)
455
- task_type = infer_expression_type(expression, env:)
456
- AST::ProcExpr.new(
457
- params: [],
458
- return_type: ast_type_ref_for(task_type),
459
- body: [AST::ReturnStmt.new(value: expression)],
460
- )
461
- end
478
+ {
479
+ call:,
480
+ binding:,
481
+ }
482
+ end
462
483
 
463
- def wrap_expression_in_ref_of(expression)
464
- AST::Call.new(
465
- callee: AST::Identifier.new(name: "ref_of"),
466
- arguments: [AST::Argument.new(name: nil, value: expression)],
467
- )
468
- end
484
+ def foreign_call_consumes_binding?(binding)
485
+ binding.type.params.any? { |parameter| parameter.passing_mode == :consuming }
486
+ end
469
487
 
470
- def foreign_call_info(expression, env)
471
- call = expression if expression.is_a?(AST::Call)
472
- return unless call
488
+ def lower_foreign_call_components(foreign_call, env:, expected_type:, statement_position:)
489
+ call = foreign_call.fetch(:call)
490
+ binding = foreign_call.fetch(:binding)
491
+ raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_call_consumes_binding?(binding) && !statement_position
473
492
 
474
- kind, _, _, _, binding = resolve_callee(call.callee, env, arguments: call.arguments)
475
- return unless kind == :function && binding && foreign_function_binding?(binding)
493
+ previous_type_substitutions = @ctx.current_type_substitutions
494
+ @ctx.current_type_substitutions = binding.type_substitutions
476
495
 
477
- {
478
- call:,
479
- binding:,
480
- }
481
- end
496
+ owner_analysis = analysis_for_module(binding.owner.module_name)
497
+ mapping_expression = foreign_mapping_expression(binding.ast)
498
+ reference_counts = foreign_mapping_reference_counts(mapping_expression)
499
+ mapping_env = duplicate_env(env)
500
+ lowered = []
501
+ release_assignments = consuming_foreign_release_assignments(foreign_call, env:)
502
+ cleanup_statements = []
482
503
 
483
- def foreign_call_consumes_binding?(binding)
484
- binding.type.params.any? { |parameter| parameter.passing_mode == :consuming }
485
- end
504
+ replacements = bind_foreign_mapping_arguments(binding, call.arguments, mapping_env, lowered, env:, reference_counts:, cleanup: cleanup_statements)
486
505
 
487
- def lower_foreign_call_components(foreign_call, env:, expected_type:, statement_position:)
488
- call = foreign_call.fetch(:call)
489
- binding = foreign_call.fetch(:binding)
490
- raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_call_consumes_binding?(binding) && !statement_position
491
-
492
- previous_type_substitutions = @ctx.current_type_substitutions
493
- @ctx.current_type_substitutions = binding.type_substitutions
494
-
495
- owner_analysis = analysis_for_module(binding.owner.module_name)
496
- mapping_expression = foreign_mapping_expression(binding.ast)
497
- reference_counts = foreign_mapping_reference_counts(mapping_expression)
498
- mapping_env = duplicate_env(env)
499
- lowered = []
500
- release_assignments = consuming_foreign_release_assignments(foreign_call, env:)
501
- cleanup_statements = []
502
-
503
- replacements = bind_foreign_mapping_arguments(binding, call.arguments, mapping_env, lowered, env:, reference_counts:, cleanup: cleanup_statements)
504
-
505
- call_type = binding.type.return_type
506
- lowered_call = lower_inline_foreign_mapping_expression(
507
- mapping_expression,
508
- mapping_env:,
509
- replacements:,
510
- owner_analysis:,
511
- expected_type: expected_type || call_type,
512
- )
513
- lowered_call = append_variadic_foreign_call_arguments(
514
- lowered_call,
515
- call.arguments,
516
- binding.type,
517
- env:,
518
- lowered:,
519
- cleanup: cleanup_statements,
520
- )
521
-
522
- [lowered, lowered_call, call_type, release_assignments, cleanup_statements]
523
- ensure
524
- @ctx.current_type_substitutions = previous_type_substitutions
525
- end
506
+ call_type = binding.type.return_type
507
+ lowered_call = lower_inline_foreign_mapping_expression(
508
+ mapping_expression,
509
+ mapping_env:,
510
+ replacements:,
511
+ owner_analysis:,
512
+ expected_type: expected_type || call_type,
513
+ )
514
+ lowered_call = append_variadic_foreign_call_arguments(
515
+ lowered_call,
516
+ call.arguments,
517
+ binding.type,
518
+ env:,
519
+ lowered:,
520
+ cleanup: cleanup_statements,
521
+ )
526
522
 
527
- def lower_foreign_call_statement(foreign_call, env:, expected_type:, statement_position:, discard_result: false)
528
- lowered, lowered_call, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
529
- foreign_call,
530
- env:,
531
- expected_type:,
532
- statement_position:,
533
- )
534
-
535
- if call_type == @ctx.types.fetch("void")
536
- lowered << IR::ExpressionStmt.new(expression: lowered_call)
537
- lowered.concat(release_assignments)
538
- lowered.concat(cleanup_statements)
539
- return [lowered, nil]
523
+ [lowered, lowered_call, call_type, release_assignments, cleanup_statements]
524
+ ensure
525
+ @ctx.current_type_substitutions = previous_type_substitutions
540
526
  end
541
527
 
542
- raise LoweringError.new("consuming foreign calls must return void", line: 0, column: 0, path: @ctx.current_analysis_path) unless release_assignments.empty?
528
+ def lower_foreign_call_statement(foreign_call, env:, expected_type:, statement_position:, discard_result: false)
529
+ lowered, lowered_call, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
530
+ foreign_call,
531
+ env:,
532
+ expected_type:,
533
+ statement_position:,
534
+ )
543
535
 
544
- if discard_result
545
- lowered << IR::ExpressionStmt.new(expression: lowered_call)
546
- lowered.concat(cleanup_statements)
547
- return [lowered, nil]
548
- end
536
+ if call_type == @ctx.types.fetch("void")
537
+ lowered << IR::ExpressionStmt.new(expression: lowered_call)
538
+ lowered.concat(release_assignments)
539
+ lowered.concat(cleanup_statements)
540
+ return [lowered, nil]
541
+ end
549
542
 
550
- unless cleanup_statements.empty?
551
- result_name = fresh_c_temp_name(env, "foreign_result")
552
- lowered << IR::LocalDecl.new(name: result_name, linkage_name: result_name, type: call_type, value: lowered_call)
553
- lowered.concat(cleanup_statements)
554
- return [lowered, IR::Name.new(name: result_name, type: call_type, pointer: false)]
555
- end
543
+ raise LoweringError.new("consuming foreign calls must return void", line: 0, column: 0, path: @ctx.current_analysis_path) unless release_assignments.empty?
556
544
 
557
- [lowered, lowered_call]
558
- end
545
+ if discard_result
546
+ lowered << IR::ExpressionStmt.new(expression: lowered_call)
547
+ lowered.concat(cleanup_statements)
548
+ return [lowered, nil]
549
+ end
559
550
 
560
- def consuming_foreign_release_assignments(foreign_call, env:)
561
- consuming_foreign_release_bindings(foreign_call, env:).map do |binding|
562
- IR::Assignment.new(
563
- target: IR::Name.new(name: binding[:linkage_name], type: binding[:storage_type], pointer: binding[:pointer]),
564
- operator: "=",
565
- value: IR::NullLiteral.new(type: binding[:storage_type]),
566
- )
551
+ unless cleanup_statements.empty?
552
+ result_name = fresh_c_temp_name(env, "foreign_result")
553
+ lowered << IR::LocalDecl.new(name: result_name, linkage_name: result_name, type: call_type, value: lowered_call)
554
+ lowered.concat(cleanup_statements)
555
+ return [lowered, IR::Name.new(name: result_name, type: call_type, pointer: false)]
556
+ end
557
+
558
+ [lowered, lowered_call]
567
559
  end
568
- end
569
560
 
570
- def consuming_foreign_call_refinements(foreign_call, env)
571
- consuming_foreign_release_bindings(foreign_call, env:).each_with_object({}) do |binding, refinements|
572
- refinements[binding[:name]] = null_type
561
+ def consuming_foreign_release_assignments(foreign_call, env:)
562
+ consuming_foreign_release_bindings(foreign_call, env:).map do |binding|
563
+ IR::Assignment.new(
564
+ target: IR::Name.new(name: binding[:linkage_name], type: binding[:storage_type], pointer: binding[:pointer]),
565
+ operator: "=",
566
+ value: IR::NullLiteral.new(type: binding[:storage_type]),
567
+ )
568
+ end
573
569
  end
574
- end
575
570
 
576
- def consuming_foreign_release_bindings(foreign_call, env:)
577
- binding = foreign_call.fetch(:binding)
578
- call = foreign_call.fetch(:call)
571
+ def consuming_foreign_call_refinements(foreign_call, env)
572
+ consuming_foreign_release_bindings(foreign_call, env:).each_with_object({}) do |binding, refinements|
573
+ refinements[binding[:name]] = null_type
574
+ end
575
+ end
579
576
 
580
- binding.type.params.each_with_index.filter_map do |parameter, index|
581
- next unless parameter.passing_mode == :consuming
577
+ def consuming_foreign_release_bindings(foreign_call, env:)
578
+ binding = foreign_call.fetch(:binding)
579
+ call = foreign_call.fetch(:call)
582
580
 
583
- argument = call.arguments.fetch(index)
584
- unless argument.value.is_a?(AST::Identifier)
585
- raise LoweringError.new("consuming foreign calls require bare nullable local or parameter bindings", line: 0, column: 0, path: @ctx.current_analysis_path)
586
- end
581
+ binding.type.params.each_with_index.filter_map do |parameter, index|
582
+ next unless parameter.passing_mode == :consuming
587
583
 
588
- lowered_binding = lookup_value(argument.value.name, env)
589
- unless lowered_binding && lowered_binding[:storage_type].is_a?(Types::Nullable) && lowered_binding[:storage_type].base == parameter.type
590
- raise LoweringError.new("consuming foreign calls require bare nullable local or parameter bindings", line: 0, column: 0, path: @ctx.current_analysis_path)
591
- end
584
+ argument = call.arguments.fetch(index)
585
+ unless argument.value.is_a?(AST::Identifier)
586
+ raise LoweringError.new("consuming foreign calls require bare nullable local or parameter bindings", line: 0, column: 0, path: @ctx.current_analysis_path)
587
+ end
592
588
 
593
- lowered_binding.merge(name: argument.value.name)
594
- end
595
- end
589
+ lowered_binding = lookup_value(argument.value.name, env)
590
+ unless lowered_binding && lowered_binding[:storage_type].is_a?(Types::Nullable) && lowered_binding[:storage_type].base == parameter.type
591
+ raise LoweringError.new("consuming foreign calls require bare nullable local or parameter bindings", line: 0, column: 0, path: @ctx.current_analysis_path)
592
+ end
596
593
 
597
- def bind_foreign_mapping_arguments(binding, arguments, mapping_env, lowered, env:, reference_counts:, cleanup:)
598
- replacements = {}
599
- entries = binding.ast.params.each_with_index.map do |param_ast, index|
600
- parameter = binding.type.params.fetch(index)
601
- public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
602
- {
603
- argument: arguments.fetch(index),
604
- param_ast:,
605
- parameter:,
606
- temp_type: parameter.boundary_type || parameter.type,
607
- public_alias:,
608
- public_reference_count: public_alias ? reference_counts.fetch(public_alias, 0) : 0,
609
- reference_count: reference_counts.fetch(param_ast.name, 0),
610
- lowered_value: nil,
611
- }
594
+ lowered_binding.merge(name: argument.value.name)
595
+ end
612
596
  end
613
597
 
614
- entries.each do |entry|
615
- next unless entry[:public_reference_count].positive?
598
+ def bind_foreign_mapping_arguments(binding, arguments, mapping_env, lowered, env:, reference_counts:, cleanup:)
599
+ replacements = {}
600
+ entries = binding.ast.params.each_with_index.map do |param_ast, index|
601
+ parameter = binding.type.params.fetch(index)
602
+ public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
603
+ {
604
+ argument: arguments.fetch(index),
605
+ param_ast:,
606
+ parameter:,
607
+ temp_type: parameter.boundary_type || parameter.type,
608
+ public_alias:,
609
+ public_reference_count: public_alias ? reference_counts.fetch(public_alias, 0) : 0,
610
+ reference_count: reference_counts.fetch(param_ast.name, 0),
611
+ lowered_value: nil,
612
+ }
613
+ end
614
+
615
+ entries.each do |entry|
616
+ next unless entry[:public_reference_count].positive?
617
+
618
+ public_value = lower_contextual_expression(entry[:argument].value, env:, expected_type: entry[:parameter].type)
619
+ if public_value.is_a?(IR::Name)
620
+ current_actual_scope(mapping_env[:scopes])[entry[:public_alias]] = local_binding(
621
+ type: entry[:parameter].type,
622
+ linkage_name: public_value.name,
623
+ mutable: false,
624
+ pointer: public_value.pointer,
625
+ )
626
+ replacements[entry[:public_alias]] = public_value
627
+ next
628
+ end
616
629
 
617
- public_value = lower_contextual_expression(entry[:argument].value, env:, expected_type: entry[:parameter].type)
618
- if public_value.is_a?(IR::Name)
630
+ public_temp_name = fresh_c_temp_name(env, "foreign_arg_public")
631
+ lowered << IR::LocalDecl.new(
632
+ name: public_temp_name,
633
+ linkage_name: public_temp_name,
634
+ type: entry[:parameter].type,
635
+ value: public_value,
636
+ )
619
637
  current_actual_scope(mapping_env[:scopes])[entry[:public_alias]] = local_binding(
620
638
  type: entry[:parameter].type,
621
- linkage_name: public_value.name,
639
+ linkage_name: public_temp_name,
622
640
  mutable: false,
623
- pointer: public_value.pointer,
641
+ pointer: false,
624
642
  )
625
- replacements[entry[:public_alias]] = public_value
626
- next
643
+ replacements[entry[:public_alias]] = IR::Name.new(name: public_temp_name, type: entry[:parameter].type, pointer: false)
627
644
  end
628
645
 
629
- public_temp_name = fresh_c_temp_name(env, "foreign_arg_public")
630
- lowered << IR::LocalDecl.new(
631
- name: public_temp_name,
632
- linkage_name: public_temp_name,
633
- type: entry[:parameter].type,
634
- value: public_value,
635
- )
636
- current_actual_scope(mapping_env[:scopes])[entry[:public_alias]] = local_binding(
637
- type: entry[:parameter].type,
638
- linkage_name: public_temp_name,
639
- mutable: false,
640
- pointer: false,
641
- )
642
- replacements[entry[:public_alias]] = IR::Name.new(name: public_temp_name, type: entry[:parameter].type, pointer: false)
643
- end
644
-
645
- entries.each do |entry|
646
- next unless entry[:reference_count].positive?
647
-
648
- source_argument = if entry[:public_reference_count].positive?
649
- AST::Argument.new(name: nil, value: AST::Identifier.new(name: entry[:public_alias]))
650
- else
651
- entry[:argument]
652
- end
653
- source_env = entry[:public_reference_count].positive? ? mapping_env : env
654
- source_argument = prepare_foreign_in_argument(entry[:parameter], source_argument, source_env:, lowered:, env:)
655
- entry[:lowered_value] = if automatic_foreign_cstr_list_temp_needed?(entry[:parameter], source_argument.value, env: source_env)
656
- lower_foreign_cstr_list_argument_value(entry[:parameter], source_argument.value, env: source_env, lowered:, cleanup:)
657
- else
658
- lower_foreign_argument_value(entry[:parameter], source_argument, env: source_env)
659
- end
660
- end
646
+ entries.each do |entry|
647
+ next unless entry[:reference_count].positive?
648
+
649
+ source_argument = if entry[:public_reference_count].positive?
650
+ AST::Argument.new(name: nil, value: AST::Identifier.new(name: entry[:public_alias]))
651
+ else
652
+ entry[:argument]
653
+ end
654
+ source_env = entry[:public_reference_count].positive? ? mapping_env : env
655
+ source_argument = prepare_foreign_in_argument(entry[:parameter], source_argument, source_env:, lowered:, env:)
656
+ entry[:lowered_value] = if automatic_foreign_cstr_list_temp_needed?(entry[:parameter], source_argument.value, env: source_env)
657
+ lower_foreign_cstr_list_argument_value(entry[:parameter], source_argument.value, env: source_env, lowered:, cleanup:)
658
+ else
659
+ lower_foreign_argument_value(entry[:parameter], source_argument, env: source_env)
660
+ end
661
+ end
661
662
 
662
- inline_direct_call_names = inlineable_single_direct_call_names(entries)
663
+ inline_direct_call_names = inlineable_single_direct_call_names(entries)
663
664
 
664
- entries.each do |entry|
665
- next unless entry[:reference_count].positive?
665
+ entries.each do |entry|
666
+ next unless entry[:reference_count].positive?
666
667
 
667
- param_ast = entry[:param_ast]
668
- temp_type = entry[:temp_type]
669
- lowered_value = entry[:lowered_value]
668
+ param_ast = entry[:param_ast]
669
+ temp_type = entry[:temp_type]
670
+ lowered_value = entry[:lowered_value]
670
671
 
671
- if !inline_direct_call_names.include?(param_ast.name) && foreign_argument_needs_temporary_binding?(lowered_value, reference_count: entry[:reference_count])
672
- temp_name = fresh_c_temp_name(env, "foreign_arg")
673
- lowered << IR::LocalDecl.new(
674
- name: temp_name,
675
- linkage_name: temp_name,
676
- type: temp_type,
677
- value: lowered_value,
678
- )
679
- current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: temp_name, mutable: false, pointer: false)
680
- replacements[param_ast.name] = IR::Name.new(name: temp_name, type: temp_type, pointer: false)
681
- if temporary_foreign_cstr_expression?(lowered_value)
682
- cleanup << IR::ExpressionStmt.new(
683
- expression: IR::Call.new(
684
- callee: "mt_free_foreign_cstr_temp",
685
- arguments: [IR::Name.new(name: temp_name, type: temp_type, pointer: false)],
686
- type: @ctx.types.fetch("void"),
687
- ),
672
+ if !inline_direct_call_names.include?(param_ast.name) && foreign_argument_needs_temporary_binding?(lowered_value, reference_count: entry[:reference_count])
673
+ temp_name = fresh_c_temp_name(env, "foreign_arg")
674
+ lowered << IR::LocalDecl.new(
675
+ name: temp_name,
676
+ linkage_name: temp_name,
677
+ type: temp_type,
678
+ value: lowered_value,
688
679
  )
680
+ current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: temp_name, mutable: false, pointer: false)
681
+ replacements[param_ast.name] = IR::Name.new(name: temp_name, type: temp_type, pointer: false)
682
+ if temporary_foreign_cstr_expression?(lowered_value)
683
+ cleanup << IR::ExpressionStmt.new(
684
+ expression: IR::Call.new(
685
+ callee: "mt_free_foreign_cstr_temp",
686
+ arguments: [IR::Name.new(name: temp_name, type: temp_type, pointer: false)],
687
+ type: @ctx.types.fetch("void"),
688
+ ),
689
+ )
690
+ end
691
+ else
692
+ current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: param_ast.name, mutable: false, pointer: false)
693
+ replacements[param_ast.name] = lowered_value
689
694
  end
690
- else
691
- current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: param_ast.name, mutable: false, pointer: false)
692
- replacements[param_ast.name] = lowered_value
693
695
  end
694
- end
695
-
696
- replacements
697
- end
698
-
699
- def inlineable_single_direct_call_names(entries)
700
- blocked_entries = entries.select do |entry|
701
- next false unless entry[:reference_count].positive?
702
696
 
703
- entry[:reference_count] > 1 || !inlineable_foreign_argument_expression?(entry[:lowered_value])
697
+ replacements
704
698
  end
705
- return [] unless blocked_entries.length == 1
706
699
 
707
- blocked_entry = blocked_entries.first
708
- return [] unless blocked_entry[:reference_count] == 1 && blocked_entry[:lowered_value].is_a?(IR::Call)
709
- return [] if temporary_foreign_cstr_expression?(blocked_entry[:lowered_value])
700
+ def inlineable_single_direct_call_names(entries)
701
+ blocked_entries = entries.select do |entry|
702
+ next false unless entry[:reference_count].positive?
710
703
 
711
- [blocked_entry[:param_ast].name]
712
- end
704
+ entry[:reference_count] > 1 || !inlineable_foreign_argument_expression?(entry[:lowered_value])
705
+ end
706
+ return [] unless blocked_entries.length == 1
713
707
 
714
- def foreign_argument_expression(argument)
715
- if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
716
- argument.value.operand
717
- else
718
- argument.value
719
- end
720
- end
708
+ blocked_entry = blocked_entries.first
709
+ return [] unless blocked_entry[:reference_count] == 1 && blocked_entry[:lowered_value].is_a?(IR::Call)
710
+ return [] if temporary_foreign_cstr_expression?(blocked_entry[:lowered_value])
721
711
 
722
- def lower_foreign_argument_value(parameter, argument, env:)
723
- case parameter.passing_mode
724
- when :plain, :consuming
725
- if parameter.boundary_type.nil? || parameter.boundary_type == parameter.type
726
- expected = parameter.passing_mode == :consuming ? Types::Registry.nullable(parameter.type) : parameter.type
727
- lower_contextual_expression(argument.value, env:, expected_type: expected)
728
- elsif parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
729
- if argument.value.is_a?(AST::StringLiteral) && !argument.value.cstring
730
- return IR::StringLiteral.new(value: argument.value.value, type: parameter.boundary_type, cstring: true)
731
- end
712
+ [blocked_entry[:param_ast].name]
713
+ end
732
714
 
733
- actual_type = infer_expression_type(argument.value, env:)
734
- if actual_type == @ctx.types.fetch("cstr")
735
- return lower_expression(argument.value, env:, expected_type: parameter.boundary_type)
736
- end
715
+ def foreign_argument_expression(argument)
716
+ if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
717
+ argument.value.operand
718
+ else
719
+ argument.value
720
+ end
721
+ end
737
722
 
738
- if cstr_backed_expression?(argument.value, env)
723
+ def lower_foreign_argument_value(parameter, argument, env:)
724
+ case parameter.passing_mode
725
+ when :plain, :consuming
726
+ if parameter.boundary_type.nil? || parameter.boundary_type == parameter.type
727
+ expected = parameter.passing_mode == :consuming ? Types::Registry.nullable(parameter.type) : parameter.type
728
+ lower_contextual_expression(argument.value, env:, expected_type: expected)
729
+ elsif parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
730
+ if argument.value.is_a?(AST::StringLiteral) && !argument.value.cstring
731
+ return IR::StringLiteral.new(value: argument.value.value, type: parameter.boundary_type, cstring: true)
732
+ end
733
+
734
+ actual_type = infer_expression_type(argument.value, env:)
735
+ if actual_type == @ctx.types.fetch("cstr")
736
+ return lower_expression(argument.value, env:, expected_type: parameter.boundary_type)
737
+ end
738
+
739
+ if cstr_backed_expression?(argument.value, env)
740
+ lowered_value = lower_contextual_expression(argument.value, env:, expected_type: parameter.type)
741
+ data_expression = IR::Member.new(receiver: lowered_value, member: "data", type: pointer_to(@ctx.types.fetch("char")))
742
+ converted = foreign_identity_projection_expression(data_expression, parameter.boundary_type)
743
+ return converted if converted
744
+ end
745
+
746
+ IR::Call.new(
747
+ callee: "mt_foreign_str_to_cstr_temp",
748
+ arguments: [lower_contextual_expression(argument.value, env:, expected_type: parameter.type)],
749
+ type: parameter.boundary_type,
750
+ )
751
+ elsif foreign_span_boundary_compatible?(parameter.type, parameter.boundary_type)
752
+ lower_foreign_span_argument_value(parameter, argument, env:)
753
+ elsif foreign_char_pointer_buffer_boundary_compatible?(parameter.type, parameter.boundary_type)
754
+ lower_foreign_char_pointer_buffer_argument_value(parameter, argument, env:)
755
+ else
739
756
  lowered_value = lower_contextual_expression(argument.value, env:, expected_type: parameter.type)
740
- data_expression = IR::Member.new(receiver: lowered_value, member: "data", type: pointer_to(@ctx.types.fetch("char")))
741
- converted = foreign_identity_projection_expression(data_expression, parameter.boundary_type)
757
+ converted = foreign_identity_projection_expression(lowered_value, parameter.boundary_type)
742
758
  return converted if converted
743
- end
744
759
 
745
- IR::Call.new(
746
- callee: "mt_foreign_str_to_cstr_temp",
747
- arguments: [lower_contextual_expression(argument.value, env:, expected_type: parameter.type)],
748
- type: parameter.boundary_type,
749
- )
750
- elsif foreign_span_boundary_compatible?(parameter.type, parameter.boundary_type)
751
- lower_foreign_span_argument_value(parameter, argument, env:)
752
- elsif foreign_char_pointer_buffer_boundary_compatible?(parameter.type, parameter.boundary_type)
753
- lower_foreign_char_pointer_buffer_argument_value(parameter, argument, env:)
760
+ raise LoweringError.new("unsupported foreign boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
761
+ end
762
+ when :in
763
+ lower_foreign_in_argument_value(parameter, argument, env:)
764
+ when :out, :inout
765
+ lower_foreign_pointer_argument_value(parameter, argument, env:)
754
766
  else
755
- lowered_value = lower_contextual_expression(argument.value, env:, expected_type: parameter.type)
756
- converted = foreign_identity_projection_expression(lowered_value, parameter.boundary_type)
757
- return converted if converted
758
-
759
- raise LoweringError.new("unsupported foreign boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
767
+ raise LoweringError.new("unsupported foreign passing mode #{parameter.passing_mode}", line: 0, column: 0, path: @ctx.current_analysis_path)
760
768
  end
761
- when :in
762
- lower_foreign_in_argument_value(parameter, argument, env:)
763
- when :out, :inout
764
- lower_foreign_pointer_argument_value(parameter, argument, env:)
765
- else
766
- raise LoweringError.new("unsupported foreign passing mode #{parameter.passing_mode}", line: 0, column: 0, path: @ctx.current_analysis_path)
767
769
  end
768
- end
769
-
770
- def lower_foreign_span_argument_value(parameter, argument, env:)
771
- public_type = parameter.type
772
- boundary_type = parameter.boundary_type
773
- lowered_value = lower_contextual_expression(argument.value, env:, expected_type: public_type)
774
- return lowered_value if public_type == boundary_type
775
-
776
- public_element_type = public_type.element_type
777
- boundary_element_type = boundary_type.element_type
778
-
779
- data_expression = IR::Member.new(receiver: lowered_value, member: "data", type: pointer_to(public_element_type))
780
- converted_data = foreign_identity_projection_expression(data_expression, pointer_to(boundary_element_type))
781
- raise LoweringError.new("unsupported foreign boundary mapping #{public_type} as #{boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless converted_data
782
-
783
- len_expression = IR::Member.new(receiver: lowered_value, member: "len", type: @ctx.types.fetch("ptr_uint"))
784
- IR::AggregateLiteral.new(
785
- type: boundary_type,
786
- fields: [
787
- IR::AggregateField.new(name: "data", value: converted_data),
788
- IR::AggregateField.new(name: "len", value: len_expression),
789
- ],
790
- )
791
- end
792
-
793
- def lower_foreign_pointer_argument_value(parameter, argument, env:)
794
- slot_type = foreign_slot_boundary_value_type(parameter.type)
795
- operand = foreign_argument_expression(argument)
796
- address = lower_addr_expression(operand, env:, target_type: pointer_to(slot_type))
797
770
 
798
- converted = foreign_identity_projection_expression(address, parameter.boundary_type)
799
- return converted if converted
771
+ def lower_foreign_span_argument_value(parameter, argument, env:)
772
+ public_type = parameter.type
773
+ boundary_type = parameter.boundary_type
774
+ lowered_value = lower_contextual_expression(argument.value, env:, expected_type: public_type)
775
+ return lowered_value if public_type == boundary_type
800
776
 
801
- raise LoweringError.new("unsupported foreign pointer boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
802
- end
777
+ public_element_type = public_type.element_type
778
+ boundary_element_type = boundary_type.element_type
803
779
 
804
- def foreign_slot_boundary_value_type(type)
805
- if type.is_a?(Types::Nullable) && pointer_like_type?(type.base)
806
- return type.base
807
- end
780
+ data_expression = IR::Member.new(receiver: lowered_value, member: "data", type: pointer_to(public_element_type))
781
+ converted_data = foreign_identity_projection_expression(data_expression, pointer_to(boundary_element_type))
782
+ raise LoweringError.new("unsupported foreign boundary mapping #{public_type} as #{boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless converted_data
808
783
 
809
- type
810
- end
811
-
812
- def prepare_foreign_in_argument(parameter, argument, source_env:, lowered:, env:)
813
- return argument unless parameter.passing_mode == :in
814
-
815
- operand = foreign_argument_expression(argument)
816
- return argument if addressable_storage_expression?(operand)
817
-
818
- temp_name = fresh_c_temp_name(env, "foreign_in")
819
- lowered << IR::LocalDecl.new(
820
- name: temp_name,
821
- linkage_name: temp_name,
822
- type: parameter.type,
823
- value: lower_contextual_expression(operand, env: source_env, expected_type: parameter.type),
824
- )
825
- current_actual_scope(source_env[:scopes])[temp_name] = local_binding(type: parameter.type, linkage_name: temp_name, mutable: false, pointer: false)
826
-
827
- AST::Argument.new(
828
- name: argument.name,
829
- value: AST::Identifier.new(name: temp_name),
830
- )
831
- end
784
+ len_expression = IR::Member.new(receiver: lowered_value, member: "len", type: @ctx.types.fetch("ptr_uint"))
785
+ IR::AggregateLiteral.new(
786
+ type: boundary_type,
787
+ fields: [
788
+ IR::AggregateField.new(name: "data", value: converted_data),
789
+ IR::AggregateField.new(name: "len", value: len_expression),
790
+ ],
791
+ )
792
+ end
832
793
 
833
- def lower_foreign_in_argument_value(parameter, argument, env:)
834
- address = lower_addr_expression(
835
- foreign_argument_expression(argument),
836
- env:,
837
- target_type: const_pointer_to(parameter.type),
838
- )
794
+ def lower_foreign_pointer_argument_value(parameter, argument, env:)
795
+ slot_type = foreign_slot_boundary_value_type(parameter.type)
796
+ operand = foreign_argument_expression(argument)
797
+ address = lower_addr_expression(operand, env:, target_type: pointer_to(slot_type))
839
798
 
840
- converted = foreign_identity_projection_expression(address, parameter.boundary_type)
841
- return converted if converted
799
+ converted = foreign_identity_projection_expression(address, parameter.boundary_type)
800
+ return converted if converted
842
801
 
843
- raise LoweringError.new("unsupported foreign in boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
844
- end
802
+ raise LoweringError.new("unsupported foreign pointer boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
803
+ end
845
804
 
846
- def lower_foreign_char_pointer_buffer_argument_value(parameter, argument, env:)
847
- public_type = parameter.type
805
+ def foreign_slot_boundary_value_type(type)
806
+ if type.is_a?(Types::Nullable) && pointer_like_type?(type.base)
807
+ return type.base
808
+ end
848
809
 
849
- if char_array_text_type?(public_type)
850
- return lower_char_array_data_pointer(argument.value, env:)
810
+ type
851
811
  end
852
812
 
853
- if str_buffer_type?(public_type)
854
- return IR::Call.new(
855
- callee: "mt_str_buffer_prepare_write",
856
- arguments: [
857
- lower_str_buffer_data_pointer(argument.value, env:),
858
- IR::IntegerLiteral.new(value: str_buffer_capacity(public_type), type: @ctx.types.fetch("ptr_uint")),
859
- lower_str_buffer_dirty_pointer(argument.value, env:),
860
- ],
861
- type: parameter.boundary_type,
813
+ def prepare_foreign_in_argument(parameter, argument, source_env:, lowered:, env:)
814
+ return argument unless parameter.passing_mode == :in
815
+
816
+ operand = foreign_argument_expression(argument)
817
+ return argument if addressable_storage_expression?(operand)
818
+
819
+ temp_name = fresh_c_temp_name(env, "foreign_in")
820
+ lowered << IR::LocalDecl.new(
821
+ name: temp_name,
822
+ linkage_name: temp_name,
823
+ type: parameter.type,
824
+ value: lower_contextual_expression(operand, env: source_env, expected_type: parameter.type),
825
+ )
826
+ current_actual_scope(source_env[:scopes])[temp_name] = local_binding(type: parameter.type, linkage_name: temp_name, mutable: false, pointer: false)
827
+
828
+ AST::Argument.new(
829
+ name: argument.name,
830
+ value: AST::Identifier.new(name: temp_name),
862
831
  )
863
832
  end
864
833
 
865
- lowered_value = lower_contextual_expression(argument.value, env:, expected_type: public_type)
866
- return IR::Member.new(receiver: lowered_value, member: "data", type: parameter.boundary_type) if public_type.is_a?(Types::Span) && public_type.element_type == @ctx.types.fetch("char")
834
+ def lower_foreign_in_argument_value(parameter, argument, env:)
835
+ address = lower_addr_expression(
836
+ foreign_argument_expression(argument),
837
+ env:,
838
+ target_type: const_pointer_to(parameter.type),
839
+ )
867
840
 
868
- converted = foreign_identity_projection_expression(lowered_value, parameter.boundary_type)
869
- return converted if converted
841
+ converted = foreign_identity_projection_expression(address, parameter.boundary_type)
842
+ return converted if converted
870
843
 
871
- raise LoweringError.new("unsupported foreign boundary mapping #{public_type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
872
- end
844
+ raise LoweringError.new("unsupported foreign in boundary mapping #{parameter.type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
845
+ end
873
846
 
874
- def lower_foreign_call_inline(expression, binding, env:, type:)
875
- previous_type_substitutions = @ctx.current_type_substitutions
876
- @ctx.current_type_substitutions = binding.type_substitutions
847
+ def lower_foreign_char_pointer_buffer_argument_value(parameter, argument, env:)
848
+ public_type = parameter.type
877
849
 
878
- owner_analysis = analysis_for_module(binding.owner.module_name)
879
- mapping_expression = foreign_mapping_expression(binding.ast)
880
- reference_counts = foreign_mapping_reference_counts(mapping_expression)
881
- mapping_env = duplicate_env(env)
850
+ if char_array_text_type?(public_type)
851
+ return lower_char_array_data_pointer(argument.value, env:)
852
+ end
882
853
 
883
- binding.ast.params.each_with_index do |param_ast, index|
884
- public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
885
- total_references = reference_counts.fetch(param_ast.name, 0)
886
- total_references += reference_counts.fetch(public_alias, 0) if public_alias
887
- next unless total_references > 1
888
- next if duplicable_foreign_argument_expression?(expression.arguments.fetch(index).value)
854
+ if str_buffer_type?(public_type)
855
+ return IR::Call.new(
856
+ callee: "mt_str_buffer_prepare_write",
857
+ arguments: [
858
+ lower_str_buffer_data_pointer(argument.value, env:),
859
+ IR::IntegerLiteral.new(value: str_buffer_capacity(public_type), type: @ctx.types.fetch("ptr_uint")),
860
+ lower_str_buffer_dirty_pointer(argument.value, env:),
861
+ ],
862
+ type: parameter.boundary_type,
863
+ )
864
+ end
889
865
 
890
- raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} is referenced multiple times in its mapping; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
891
- end
866
+ lowered_value = lower_contextual_expression(argument.value, env:, expected_type: public_type)
867
+ return IR::Member.new(receiver: lowered_value, member: "data", type: parameter.boundary_type) if public_type.is_a?(Types::Span) && public_type.element_type == @ctx.types.fetch("char")
892
868
 
893
- binding.ast.params.each_with_index do |param_ast, index|
894
- parameter = binding.type.params.fetch(index)
895
- next unless automatic_foreign_cstr_temp_needed?(parameter, expression.arguments.fetch(index).value, env:) ||
896
- automatic_foreign_cstr_list_temp_needed?(parameter, expression.arguments.fetch(index).value, env:)
869
+ converted = foreign_identity_projection_expression(lowered_value, parameter.boundary_type)
870
+ return converted if converted
897
871
 
898
- raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
872
+ raise LoweringError.new("unsupported foreign boundary mapping #{public_type} as #{parameter.boundary_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
899
873
  end
900
874
 
901
- expression.arguments.drop(binding.type.params.length).each do |argument|
902
- next unless automatic_variadic_foreign_cstr_temp_needed?(argument.value, env:)
875
+ def lower_foreign_call_inline(expression, binding, env:, type:)
876
+ previous_type_substitutions = @ctx.current_type_substitutions
877
+ @ctx.current_type_substitutions = binding.type_substitutions
903
878
 
904
- raise LoweringError.new("foreign call #{binding.name} cannot be used inline because a variadic argument needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
905
- end
879
+ owner_analysis = analysis_for_module(binding.owner.module_name)
880
+ mapping_expression = foreign_mapping_expression(binding.ast)
881
+ reference_counts = foreign_mapping_reference_counts(mapping_expression)
882
+ mapping_env = duplicate_env(env)
906
883
 
907
- binding.ast.params.each_with_index do |param_ast, index|
908
- parameter = binding.type.params.fetch(index)
909
- argument = expression.arguments.fetch(index)
910
- next unless parameter.passing_mode == :in
911
- next if addressable_storage_expression?(foreign_argument_expression(argument))
884
+ binding.ast.params.each_with_index do |param_ast, index|
885
+ public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
886
+ total_references = reference_counts.fetch(param_ast.name, 0)
887
+ total_references += reference_counts.fetch(public_alias, 0) if public_alias
888
+ next unless total_references > 1
889
+ next if duplicable_foreign_argument_expression?(expression.arguments.fetch(index).value)
912
890
 
913
- raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} needs temporary in storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
914
- end
891
+ raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} is referenced multiple times in its mapping; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
892
+ end
915
893
 
916
- replacements = {}
917
- binding.ast.params.each_with_index do |param_ast, index|
918
- parameter = binding.type.params.fetch(index)
919
- temp_type = parameter.boundary_type || parameter.type
920
- public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
894
+ binding.ast.params.each_with_index do |param_ast, index|
895
+ parameter = binding.type.params.fetch(index)
896
+ next unless automatic_foreign_cstr_temp_needed?(parameter, expression.arguments.fetch(index).value, env:) ||
897
+ automatic_foreign_cstr_list_temp_needed?(parameter, expression.arguments.fetch(index).value, env:)
921
898
 
922
- if reference_counts.fetch(param_ast.name, 0).positive?
923
- current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: param_ast.name, mutable: false, pointer: false)
924
- replacements[param_ast.name] = lower_foreign_argument_value(parameter, expression.arguments.fetch(index), env:)
899
+ raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
925
900
  end
926
901
 
927
- next unless public_alias && reference_counts.fetch(public_alias, 0).positive?
902
+ expression.arguments.drop(binding.type.params.length).each do |argument|
903
+ next unless automatic_variadic_foreign_cstr_temp_needed?(argument.value, env:)
928
904
 
929
- current_actual_scope(mapping_env[:scopes])[public_alias] = local_binding(type: parameter.type, linkage_name: public_alias, mutable: false, pointer: false)
930
- replacements[public_alias] = lower_contextual_expression(expression.arguments.fetch(index).value, env:, expected_type: parameter.type)
931
- end
905
+ raise LoweringError.new("foreign call #{binding.name} cannot be used inline because a variadic argument needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
906
+ end
932
907
 
933
- lowered_expression = lower_inline_foreign_mapping_expression(
934
- mapping_expression,
935
- mapping_env:,
936
- replacements:,
937
- owner_analysis:,
938
- expected_type: type,
939
- )
940
- lowered_expression = append_variadic_foreign_call_arguments(
941
- lowered_expression,
942
- expression.arguments,
943
- binding.type,
944
- env:,
945
- )
946
-
947
- converted = foreign_identity_projection_expression(lowered_expression, type)
948
- return converted if converted
949
-
950
- lowered_expression
951
- ensure
952
- @ctx.current_type_substitutions = previous_type_substitutions
953
- end
908
+ binding.ast.params.each_with_index do |param_ast, index|
909
+ parameter = binding.type.params.fetch(index)
910
+ argument = expression.arguments.fetch(index)
911
+ next unless parameter.passing_mode == :in
912
+ next if addressable_storage_expression?(foreign_argument_expression(argument))
954
913
 
955
- def append_variadic_foreign_call_arguments(lowered_expression, arguments, function_type, env:, lowered: nil, cleanup: nil)
956
- return lowered_expression unless function_type.variadic
914
+ raise LoweringError.new("foreign call #{binding.name} cannot be used inline because #{param_ast.name} needs temporary in storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path)
915
+ end
957
916
 
958
- extra_arguments = arguments.drop(function_type.params.length)
959
- return lowered_expression if extra_arguments.empty?
960
- return lowered_expression unless lowered_expression.is_a?(IR::Call)
917
+ replacements = {}
918
+ binding.ast.params.each_with_index do |param_ast, index|
919
+ parameter = binding.type.params.fetch(index)
920
+ temp_type = parameter.boundary_type || parameter.type
921
+ public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
961
922
 
962
- IR::Call.new(
963
- callee: lowered_expression.callee,
964
- arguments: lowered_expression.arguments + extra_arguments.map { |argument| lower_variadic_foreign_argument(argument, env:, lowered:, cleanup:) },
965
- type: lowered_expression.type,
966
- )
967
- end
923
+ if reference_counts.fetch(param_ast.name, 0).positive?
924
+ current_actual_scope(mapping_env[:scopes])[param_ast.name] = local_binding(type: temp_type, linkage_name: param_ast.name, mutable: false, pointer: false)
925
+ replacements[param_ast.name] = lower_foreign_argument_value(parameter, expression.arguments.fetch(index), env:)
926
+ end
968
927
 
969
- def lower_variadic_foreign_argument(argument, env:, lowered:, cleanup:)
970
- actual_type = infer_expression_type(argument.value, env:)
971
- return lower_contextual_expression(argument.value, env:, expected_type: nil) unless actual_type == @ctx.types.fetch("str")
972
-
973
- lowered_argument = lower_foreign_argument_value(
974
- Types::Registry.parameter("__mt_variadic", actual_type, passing_mode: :plain, boundary_type: @ctx.types.fetch("cstr")),
975
- argument,
976
- env:,
977
- )
978
- return lowered_argument unless temporary_foreign_cstr_expression?(lowered_argument)
979
-
980
- raise LoweringError.new("foreign variadic call cannot be used inline because an extra argument needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path) unless lowered && cleanup
981
-
982
- temp_name = fresh_c_temp_name(env, "foreign_arg")
983
- lowered << IR::LocalDecl.new(
984
- name: temp_name,
985
- linkage_name: temp_name,
986
- type: @ctx.types.fetch("cstr"),
987
- value: lowered_argument,
988
- )
989
- cleanup << IR::ExpressionStmt.new(
990
- expression: IR::Call.new(
991
- callee: "mt_free_foreign_cstr_temp",
992
- arguments: [IR::Name.new(name: temp_name, type: @ctx.types.fetch("cstr"), pointer: false)],
993
- type: @ctx.types.fetch("void"),
994
- ),
995
- )
996
- IR::Name.new(name: temp_name, type: @ctx.types.fetch("cstr"), pointer: false)
997
- end
928
+ next unless public_alias && reference_counts.fetch(public_alias, 0).positive?
998
929
 
999
- def lower_inline_foreign_mapping_expression(expression, mapping_env:, replacements:, owner_analysis:, expected_type: nil)
1000
- unless foreign_mapping_uses_inline_replacement?(expression, replacements)
1001
- return with_analysis_context(owner_analysis) do
1002
- lower_expression(expression, env: mapping_env, expected_type:)
930
+ current_actual_scope(mapping_env[:scopes])[public_alias] = local_binding(type: parameter.type, linkage_name: public_alias, mutable: false, pointer: false)
931
+ replacements[public_alias] = lower_contextual_expression(expression.arguments.fetch(index).value, env:, expected_type: parameter.type)
1003
932
  end
1004
- end
1005
933
 
1006
- type = with_analysis_context(owner_analysis) do
1007
- infer_expression_type(expression, env: mapping_env, expected_type:)
1008
- end
1009
-
1010
- case expression
1011
- when AST::Identifier
1012
- replacements.fetch(expression.name)
1013
- when AST::MemberAccess
1014
- receiver_type = with_analysis_context(owner_analysis) do
1015
- infer_expression_type(expression.receiver, env: mapping_env)
1016
- end
1017
- receiver = lower_inline_foreign_mapping_expression(
1018
- expression.receiver,
934
+ lowered_expression = lower_inline_foreign_mapping_expression(
935
+ mapping_expression,
1019
936
  mapping_env:,
1020
937
  replacements:,
1021
938
  owner_analysis:,
939
+ expected_type: type,
1022
940
  )
1023
- IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
1024
- when AST::IndexAccess
1025
- receiver_type = with_analysis_context(owner_analysis) do
1026
- infer_expression_type(expression.receiver, env: mapping_env)
1027
- end
1028
- receiver = lower_inline_foreign_mapping_expression(
1029
- expression.receiver,
1030
- mapping_env:,
1031
- replacements:,
1032
- owner_analysis:,
941
+ lowered_expression = append_variadic_foreign_call_arguments(
942
+ lowered_expression,
943
+ expression.arguments,
944
+ binding.type,
945
+ env:,
1033
946
  )
1034
- index = lower_inline_foreign_mapping_expression(
1035
- expression.index,
1036
- mapping_env:,
1037
- replacements:,
1038
- owner_analysis:,
947
+
948
+ converted = foreign_identity_projection_expression(lowered_expression, type)
949
+ return converted if converted
950
+
951
+ lowered_expression
952
+ ensure
953
+ @ctx.current_type_substitutions = previous_type_substitutions
954
+ end
955
+
956
+ def append_variadic_foreign_call_arguments(lowered_expression, arguments, function_type, env:, lowered: nil, cleanup: nil)
957
+ return lowered_expression unless function_type.variadic
958
+
959
+ extra_arguments = arguments.drop(function_type.params.length)
960
+ return lowered_expression if extra_arguments.empty?
961
+ return lowered_expression unless lowered_expression.is_a?(IR::Call)
962
+
963
+ IR::Call.new(
964
+ callee: lowered_expression.callee,
965
+ arguments: lowered_expression.arguments + extra_arguments.map { |argument| lower_variadic_foreign_argument(argument, env:, lowered:, cleanup:) },
966
+ type: lowered_expression.type,
1039
967
  )
1040
- if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
1041
- IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1042
- elsif receiver_type.is_a?(Types::Span)
1043
- IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1044
- else
1045
- IR::Index.new(receiver:, index:, type:)
1046
- end
1047
- when AST::Call
1048
- lower_inline_foreign_mapping_call(expression, mapping_env:, replacements:, owner_analysis:, type:)
1049
- when AST::UnaryOp
1050
- IR::Unary.new(
1051
- operator: expression.operator,
1052
- operand: lower_inline_foreign_mapping_expression(
1053
- expression.operand,
1054
- mapping_env:,
1055
- replacements:,
1056
- owner_analysis:,
1057
- expected_type: type,
1058
- ),
1059
- type:,
968
+ end
969
+
970
+ def lower_variadic_foreign_argument(argument, env:, lowered:, cleanup:)
971
+ actual_type = infer_expression_type(argument.value, env:)
972
+ return lower_contextual_expression(argument.value, env:, expected_type: nil) unless actual_type == @ctx.types.fetch("str")
973
+
974
+ lowered_argument = lower_foreign_argument_value(
975
+ Types::Registry.parameter("__mt_variadic", actual_type, passing_mode: :plain, boundary_type: @ctx.types.fetch("cstr")),
976
+ argument,
977
+ env:,
1060
978
  )
1061
- when AST::BinaryOp
1062
- left_type, right_type = with_analysis_context(owner_analysis) do
1063
- infer_binary_operand_types(expression, env: mapping_env, expected_type: type)
1064
- end
1065
- operand_type = promoted_binary_operand_type(expression.operator, left_type, right_type)
1066
- left = lower_inline_foreign_mapping_expression(
1067
- expression.left,
1068
- mapping_env:,
1069
- replacements:,
1070
- owner_analysis:,
1071
- expected_type: operand_type || type,
979
+ return lowered_argument unless temporary_foreign_cstr_expression?(lowered_argument)
980
+
981
+ raise LoweringError.new("foreign variadic call cannot be used inline because an extra argument needs temporary foreign text storage; use it as a statement, local initializer, assignment, or return expression", line: 0, column: 0, path: @ctx.current_analysis_path) unless lowered && cleanup
982
+
983
+ temp_name = fresh_c_temp_name(env, "foreign_arg")
984
+ lowered << IR::LocalDecl.new(
985
+ name: temp_name,
986
+ linkage_name: temp_name,
987
+ type: @ctx.types.fetch("cstr"),
988
+ value: lowered_argument,
1072
989
  )
1073
- right = lower_inline_foreign_mapping_expression(
1074
- expression.right,
1075
- mapping_env:,
1076
- replacements:,
1077
- owner_analysis:,
1078
- expected_type: operand_type || left.type,
990
+ cleanup << IR::ExpressionStmt.new(
991
+ expression: IR::Call.new(
992
+ callee: "mt_free_foreign_cstr_temp",
993
+ arguments: [IR::Name.new(name: temp_name, type: @ctx.types.fetch("cstr"), pointer: false)],
994
+ type: @ctx.types.fetch("void"),
995
+ ),
1079
996
  )
1080
- left = cast_expression(left, operand_type) if operand_type
1081
- right = cast_expression(right, operand_type) if operand_type
1082
- IR::Binary.new(operator: expression.operator, left:, right:, type:)
1083
- when AST::IfExpr
1084
- IR::Conditional.new(
1085
- condition: lower_inline_foreign_mapping_expression(
1086
- expression.condition,
997
+ IR::Name.new(name: temp_name, type: @ctx.types.fetch("cstr"), pointer: false)
998
+ end
999
+
1000
+ def lower_inline_foreign_mapping_expression(expression, mapping_env:, replacements:, owner_analysis:, expected_type: nil)
1001
+ unless foreign_mapping_uses_inline_replacement?(expression, replacements)
1002
+ return with_analysis_context(owner_analysis) do
1003
+ lower_expression(expression, env: mapping_env, expected_type:)
1004
+ end
1005
+ end
1006
+
1007
+ type = with_analysis_context(owner_analysis) do
1008
+ infer_expression_type(expression, env: mapping_env, expected_type:)
1009
+ end
1010
+
1011
+ case expression
1012
+ when AST::Identifier
1013
+ replacements.fetch(expression.name)
1014
+ when AST::MemberAccess
1015
+ receiver_type = with_analysis_context(owner_analysis) do
1016
+ infer_expression_type(expression.receiver, env: mapping_env)
1017
+ end
1018
+ receiver = lower_inline_foreign_mapping_expression(
1019
+ expression.receiver,
1087
1020
  mapping_env:,
1088
1021
  replacements:,
1089
1022
  owner_analysis:,
1090
- expected_type: @ctx.types.fetch("bool"),
1091
- ),
1092
- then_expression: lower_inline_foreign_mapping_expression(
1093
- expression.then_expression,
1023
+ )
1024
+ IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
1025
+ when AST::IndexAccess
1026
+ receiver_type = with_analysis_context(owner_analysis) do
1027
+ infer_expression_type(expression.receiver, env: mapping_env)
1028
+ end
1029
+ receiver = lower_inline_foreign_mapping_expression(
1030
+ expression.receiver,
1094
1031
  mapping_env:,
1095
1032
  replacements:,
1096
1033
  owner_analysis:,
1097
- expected_type: type,
1098
- ),
1099
- else_expression: lower_inline_foreign_mapping_expression(
1100
- expression.else_expression,
1034
+ )
1035
+ index = lower_inline_foreign_mapping_expression(
1036
+ expression.index,
1101
1037
  mapping_env:,
1102
1038
  replacements:,
1103
1039
  owner_analysis:,
1104
- expected_type: type,
1105
- ),
1106
- type:,
1107
- )
1108
- when AST::PrefixCast
1109
- lowered_expr = lower_inline_foreign_mapping_expression(
1110
- expression.expression,
1111
- mapping_env:,
1112
- replacements:,
1113
- owner_analysis:,
1114
- )
1115
- IR::Cast.new(target_type: type, expression: lowered_expr, type:)
1116
- else
1117
- with_analysis_context(owner_analysis) do
1118
- lower_expression(expression, env: mapping_env, expected_type:)
1119
- end
1120
- end
1121
- end
1122
-
1123
- def lower_inline_foreign_mapping_call(expression, mapping_env:, replacements:, owner_analysis:, type:)
1124
- kind, callee_name, receiver, callee_type, callee_binding = with_analysis_context(owner_analysis) do
1125
- resolve_callee(expression.callee, mapping_env, arguments: expression.arguments)
1126
- end
1127
-
1128
- case kind
1129
- when :function
1130
- raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if callee_binding && foreign_function_binding?(callee_binding) && foreign_call_consumes_binding?(callee_binding)
1131
-
1132
- arguments = expression.arguments.map.with_index do |argument, index|
1133
- expected_arg_type = index < callee_type.params.length ? callee_type.params[index].type : nil
1134
- lower_inline_foreign_mapping_expression(
1135
- argument.value,
1040
+ )
1041
+ if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
1042
+ IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1043
+ elsif receiver_type.is_a?(Types::Span)
1044
+ IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1045
+ else
1046
+ IR::Index.new(receiver:, index:, type:)
1047
+ end
1048
+ when AST::Call
1049
+ lower_inline_foreign_mapping_call(expression, mapping_env:, replacements:, owner_analysis:, type:)
1050
+ when AST::UnaryOp
1051
+ IR::Unary.new(
1052
+ operator: expression.operator,
1053
+ operand: lower_inline_foreign_mapping_expression(
1054
+ expression.operand,
1055
+ mapping_env:,
1056
+ replacements:,
1057
+ owner_analysis:,
1058
+ expected_type: type,
1059
+ ),
1060
+ type:,
1061
+ )
1062
+ when AST::BinaryOp
1063
+ left_type, right_type = with_analysis_context(owner_analysis) do
1064
+ infer_binary_operand_types(expression, env: mapping_env, expected_type: type)
1065
+ end
1066
+ operand_type = promoted_binary_operand_type(expression.operator, left_type, right_type)
1067
+ left = lower_inline_foreign_mapping_expression(
1068
+ expression.left,
1136
1069
  mapping_env:,
1137
1070
  replacements:,
1138
1071
  owner_analysis:,
1139
- expected_type: expected_arg_type,
1072
+ expected_type: operand_type || type,
1140
1073
  )
1141
- end
1142
- IR::Call.new(callee: callee_name, arguments:, type:)
1143
- when :reinterpret
1144
- argument = expression.arguments.fetch(0)
1145
- source_type = with_analysis_context(owner_analysis) do
1146
- infer_expression_type(argument.value, env: mapping_env)
1147
- end
1148
- IR::ReinterpretExpr.new(
1149
- target_type: type,
1150
- source_type:,
1151
- expression: lower_inline_foreign_mapping_expression(
1152
- argument.value,
1074
+ right = lower_inline_foreign_mapping_expression(
1075
+ expression.right,
1153
1076
  mapping_env:,
1154
1077
  replacements:,
1155
1078
  owner_analysis:,
1156
- expected_type: source_type,
1157
- ),
1158
- type:,
1159
- )
1160
- when :hash
1161
- resolution = with_analysis_context(owner_analysis) do
1162
- resolve_hash_specialization(expression.callee, env: mapping_env)
1163
- end
1164
- argument = expression.arguments.fetch(0)
1165
- IR::Call.new(
1166
- callee: resolution.callee_name,
1167
- arguments: [
1168
- lower_inline_hash_operation_argument(
1169
- argument.value,
1079
+ expected_type: operand_type || left.type,
1080
+ )
1081
+ left = cast_expression(left, operand_type) if operand_type
1082
+ right = cast_expression(right, operand_type) if operand_type
1083
+ IR::Binary.new(operator: expression.operator, left:, right:, type:)
1084
+ when AST::IfExpr
1085
+ IR::Conditional.new(
1086
+ condition: lower_inline_foreign_mapping_expression(
1087
+ expression.condition,
1170
1088
  mapping_env:,
1171
1089
  replacements:,
1172
1090
  owner_analysis:,
1173
- target_type: resolution.target_type,
1091
+ expected_type: @ctx.types.fetch("bool"),
1174
1092
  ),
1175
- ],
1176
- type:,
1177
- )
1178
- when :equal
1179
- resolution = with_analysis_context(owner_analysis) do
1180
- resolve_equal_specialization(expression.callee, env: mapping_env)
1181
- end
1182
- left = expression.arguments.fetch(0)
1183
- right = expression.arguments.fetch(1)
1184
- IR::Call.new(
1185
- callee: resolution.callee_name,
1186
- arguments: [
1187
- lower_inline_hash_operation_argument(
1188
- left.value,
1093
+ then_expression: lower_inline_foreign_mapping_expression(
1094
+ expression.then_expression,
1189
1095
  mapping_env:,
1190
1096
  replacements:,
1191
1097
  owner_analysis:,
1192
- target_type: resolution.target_type,
1098
+ expected_type: type,
1193
1099
  ),
1194
- lower_inline_hash_operation_argument(
1195
- right.value,
1100
+ else_expression: lower_inline_foreign_mapping_expression(
1101
+ expression.else_expression,
1196
1102
  mapping_env:,
1197
1103
  replacements:,
1198
1104
  owner_analysis:,
1199
- target_type: resolution.target_type,
1105
+ expected_type: type,
1200
1106
  ),
1201
- ],
1202
- type:,
1203
- )
1204
- when :order
1205
- resolution = with_analysis_context(owner_analysis) do
1206
- resolve_order_specialization(expression.callee, env: mapping_env)
1107
+ type:,
1108
+ )
1109
+ when AST::PrefixCast
1110
+ lowered_expr = lower_inline_foreign_mapping_expression(
1111
+ expression.expression,
1112
+ mapping_env:,
1113
+ replacements:,
1114
+ owner_analysis:,
1115
+ )
1116
+ IR::Cast.new(target_type: type, expression: lowered_expr, type:)
1117
+ else
1118
+ with_analysis_context(owner_analysis) do
1119
+ lower_expression(expression, env: mapping_env, expected_type:)
1120
+ end
1207
1121
  end
1208
- left = expression.arguments.fetch(0)
1209
- right = expression.arguments.fetch(1)
1210
- IR::Call.new(
1211
- callee: resolution.callee_name,
1212
- arguments: [
1213
- lower_inline_hash_operation_argument(
1214
- left.value,
1122
+ end
1123
+
1124
+ def lower_inline_foreign_mapping_call(expression, mapping_env:, replacements:, owner_analysis:, type:)
1125
+ kind, callee_name, receiver, callee_type, callee_binding = with_analysis_context(owner_analysis) do
1126
+ resolve_callee(expression.callee, mapping_env, arguments: expression.arguments)
1127
+ end
1128
+
1129
+ case kind
1130
+ when :function
1131
+ raise LoweringError.new("consuming foreign calls must be top-level expression statements", line: 0, column: 0, path: @ctx.current_analysis_path) if callee_binding && foreign_function_binding?(callee_binding) && foreign_call_consumes_binding?(callee_binding)
1132
+
1133
+ arguments = expression.arguments.map.with_index do |argument, index|
1134
+ expected_arg_type = index < callee_type.params.length ? callee_type.params[index].type : nil
1135
+ lower_inline_foreign_mapping_expression(
1136
+ argument.value,
1215
1137
  mapping_env:,
1216
1138
  replacements:,
1217
1139
  owner_analysis:,
1218
- target_type: resolution.target_type,
1219
- ),
1220
- lower_inline_hash_operation_argument(
1221
- right.value,
1140
+ expected_type: expected_arg_type,
1141
+ )
1142
+ end
1143
+ IR::Call.new(callee: callee_name, arguments:, type:)
1144
+ when :reinterpret
1145
+ argument = expression.arguments.fetch(0)
1146
+ source_type = with_analysis_context(owner_analysis) do
1147
+ infer_expression_type(argument.value, env: mapping_env)
1148
+ end
1149
+ IR::ReinterpretExpr.new(
1150
+ target_type: type,
1151
+ source_type:,
1152
+ expression: lower_inline_foreign_mapping_expression(
1153
+ argument.value,
1222
1154
  mapping_env:,
1223
1155
  replacements:,
1224
1156
  owner_analysis:,
1225
- target_type: resolution.target_type,
1157
+ expected_type: source_type,
1226
1158
  ),
1227
- ],
1228
- type:,
1229
- )
1230
- when :zero
1231
- IR::ZeroInit.new(type:)
1232
- when :ref_of
1233
- argument = expression.arguments.fetch(0)
1234
- lowered_argument = lower_inline_foreign_mapping_expression(
1235
- argument.value,
1236
- mapping_env:,
1237
- replacements:,
1238
- owner_analysis:,
1239
- )
1240
- if lowered_argument.is_a?(IR::Name) && lowered_argument.pointer
1241
- cast_expression(lowered_argument, type)
1242
- elsif lowered_argument.is_a?(IR::Unary) && lowered_argument.operator == "*"
1243
- cast_expression(lowered_argument.operand, type)
1244
- else
1245
- IR::AddressOf.new(expression: lowered_argument, type:)
1246
- end
1247
- when :const_ptr_of
1248
- argument = expression.arguments.fetch(0)
1249
- lowered_argument = lower_inline_foreign_mapping_expression(
1250
- argument.value,
1251
- mapping_env:,
1252
- replacements:,
1253
- owner_analysis:,
1254
- )
1255
- if lowered_argument.is_a?(IR::Name) && lowered_argument.pointer
1256
- cast_expression(lowered_argument, type)
1257
- elsif lowered_argument.is_a?(IR::Unary) && lowered_argument.operator == "*"
1258
- cast_expression(lowered_argument.operand, type)
1259
- else
1260
- IR::AddressOf.new(expression: lowered_argument, type:)
1261
- end
1262
- when :read
1263
- argument = expression.arguments.fetch(0)
1264
- IR::Unary.new(
1265
- operator: "*",
1266
- operand: lower_inline_foreign_mapping_expression(
1159
+ type:,
1160
+ )
1161
+ when :hash
1162
+ resolution = with_analysis_context(owner_analysis) do
1163
+ resolve_hash_specialization(expression.callee, env: mapping_env)
1164
+ end
1165
+ argument = expression.arguments.fetch(0)
1166
+ IR::Call.new(
1167
+ callee: resolution.callee_name,
1168
+ arguments: [
1169
+ lower_inline_hash_operation_argument(
1170
+ argument.value,
1171
+ mapping_env:,
1172
+ replacements:,
1173
+ owner_analysis:,
1174
+ target_type: resolution.target_type,
1175
+ ),
1176
+ ],
1177
+ type:,
1178
+ )
1179
+ when :equal
1180
+ resolution = with_analysis_context(owner_analysis) do
1181
+ resolve_equal_specialization(expression.callee, env: mapping_env)
1182
+ end
1183
+ left = expression.arguments.fetch(0)
1184
+ right = expression.arguments.fetch(1)
1185
+ IR::Call.new(
1186
+ callee: resolution.callee_name,
1187
+ arguments: [
1188
+ lower_inline_hash_operation_argument(
1189
+ left.value,
1190
+ mapping_env:,
1191
+ replacements:,
1192
+ owner_analysis:,
1193
+ target_type: resolution.target_type,
1194
+ ),
1195
+ lower_inline_hash_operation_argument(
1196
+ right.value,
1197
+ mapping_env:,
1198
+ replacements:,
1199
+ owner_analysis:,
1200
+ target_type: resolution.target_type,
1201
+ ),
1202
+ ],
1203
+ type:,
1204
+ )
1205
+ when :order
1206
+ resolution = with_analysis_context(owner_analysis) do
1207
+ resolve_order_specialization(expression.callee, env: mapping_env)
1208
+ end
1209
+ left = expression.arguments.fetch(0)
1210
+ right = expression.arguments.fetch(1)
1211
+ IR::Call.new(
1212
+ callee: resolution.callee_name,
1213
+ arguments: [
1214
+ lower_inline_hash_operation_argument(
1215
+ left.value,
1216
+ mapping_env:,
1217
+ replacements:,
1218
+ owner_analysis:,
1219
+ target_type: resolution.target_type,
1220
+ ),
1221
+ lower_inline_hash_operation_argument(
1222
+ right.value,
1223
+ mapping_env:,
1224
+ replacements:,
1225
+ owner_analysis:,
1226
+ target_type: resolution.target_type,
1227
+ ),
1228
+ ],
1229
+ type:,
1230
+ )
1231
+ when :zero
1232
+ IR::ZeroInit.new(type:)
1233
+ when :ref_of
1234
+ argument = expression.arguments.fetch(0)
1235
+ lowered_argument = lower_inline_foreign_mapping_expression(
1267
1236
  argument.value,
1268
1237
  mapping_env:,
1269
1238
  replacements:,
1270
1239
  owner_analysis:,
1271
- ),
1272
- type:,
1273
- )
1274
- when :str_buffer_capacity
1275
- receiver_type = with_analysis_context(owner_analysis) do
1276
- infer_expression_type(receiver, env: mapping_env)
1277
- end
1278
- IR::IntegerLiteral.new(value: str_buffer_capacity(receiver_type), type:)
1279
- when :ptr_of
1280
- argument = expression.arguments.fetch(0)
1281
- argument_type = with_analysis_context(owner_analysis) do
1282
- infer_expression_type(argument.value, env: mapping_env)
1283
- end
1284
- if ref_type?(argument_type)
1285
- IR::Cast.new(
1286
- target_type: type,
1287
- expression: lower_inline_foreign_mapping_expression(
1240
+ )
1241
+ if lowered_argument.is_a?(IR::Name) && lowered_argument.pointer
1242
+ cast_expression(lowered_argument, type)
1243
+ elsif lowered_argument.is_a?(IR::Unary) && lowered_argument.operator == "*"
1244
+ cast_expression(lowered_argument.operand, type)
1245
+ else
1246
+ IR::AddressOf.new(expression: lowered_argument, type:)
1247
+ end
1248
+ when :const_ptr_of
1249
+ argument = expression.arguments.fetch(0)
1250
+ lowered_argument = lower_inline_foreign_mapping_expression(
1251
+ argument.value,
1252
+ mapping_env:,
1253
+ replacements:,
1254
+ owner_analysis:,
1255
+ )
1256
+ if lowered_argument.is_a?(IR::Name) && lowered_argument.pointer
1257
+ cast_expression(lowered_argument, type)
1258
+ elsif lowered_argument.is_a?(IR::Unary) && lowered_argument.operator == "*"
1259
+ cast_expression(lowered_argument.operand, type)
1260
+ else
1261
+ IR::AddressOf.new(expression: lowered_argument, type:)
1262
+ end
1263
+ when :read
1264
+ argument = expression.arguments.fetch(0)
1265
+ IR::Unary.new(
1266
+ operator: "*",
1267
+ operand: lower_inline_foreign_mapping_expression(
1288
1268
  argument.value,
1289
1269
  mapping_env:,
1290
1270
  replacements:,
@@ -1292,347 +1272,369 @@ module MilkTea
1292
1272
  ),
1293
1273
  type:,
1294
1274
  )
1275
+ when :str_buffer_capacity
1276
+ receiver_type = with_analysis_context(owner_analysis) do
1277
+ infer_expression_type(receiver, env: mapping_env)
1278
+ end
1279
+ IR::IntegerLiteral.new(value: str_buffer_capacity(receiver_type), type:)
1280
+ when :ptr_of
1281
+ argument = expression.arguments.fetch(0)
1282
+ argument_type = with_analysis_context(owner_analysis) do
1283
+ infer_expression_type(argument.value, env: mapping_env)
1284
+ end
1285
+ if ref_type?(argument_type)
1286
+ IR::Cast.new(
1287
+ target_type: type,
1288
+ expression: lower_inline_foreign_mapping_expression(
1289
+ argument.value,
1290
+ mapping_env:,
1291
+ replacements:,
1292
+ owner_analysis:,
1293
+ ),
1294
+ type:,
1295
+ )
1296
+ else
1297
+ lower_addr_expression(
1298
+ argument.value,
1299
+ env: mapping_env,
1300
+ target_type: type,
1301
+ )
1302
+ end
1295
1303
  else
1296
- lower_addr_expression(
1297
- argument.value,
1298
- env: mapping_env,
1299
- target_type: type,
1300
- )
1304
+ raise LoweringError.new("unsupported inline foreign mapping call kind #{kind}", line: 0, column: 0, path: @ctx.current_analysis_path)
1301
1305
  end
1302
- else
1303
- raise LoweringError.new("unsupported inline foreign mapping call kind #{kind}", line: 0, column: 0, path: @ctx.current_analysis_path)
1304
1306
  end
1305
- end
1306
1307
 
1307
- def lower_inline_hash_operation_argument(expression, mapping_env:, replacements:, owner_analysis:, target_type:)
1308
- actual_type = with_analysis_context(owner_analysis) do
1309
- infer_expression_type(expression, env: mapping_env)
1310
- end
1311
- lowered_expression = lower_inline_foreign_mapping_expression(
1312
- expression,
1313
- mapping_env:,
1314
- replacements:,
1315
- owner_analysis:,
1316
- )
1317
- pointer_type = const_pointer_to(target_type)
1318
-
1319
- if pointer_type?(actual_type) || ref_type?(actual_type)
1320
- return cast_expression(lowered_expression, pointer_type)
1321
- end
1308
+ def lower_inline_hash_operation_argument(expression, mapping_env:, replacements:, owner_analysis:, target_type:)
1309
+ actual_type = with_analysis_context(owner_analysis) do
1310
+ infer_expression_type(expression, env: mapping_env)
1311
+ end
1312
+ lowered_expression = lower_inline_foreign_mapping_expression(
1313
+ expression,
1314
+ mapping_env:,
1315
+ replacements:,
1316
+ owner_analysis:,
1317
+ )
1318
+ pointer_type = const_pointer_to(target_type)
1322
1319
 
1323
- return cast_expression(lowered_expression.operand, pointer_type) if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
1320
+ if pointer_type?(actual_type) || ref_type?(actual_type)
1321
+ return cast_expression(lowered_expression, pointer_type)
1322
+ end
1324
1323
 
1325
- IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
1326
- end
1324
+ return cast_expression(lowered_expression.operand, pointer_type) if lowered_expression.is_a?(IR::Unary) && lowered_expression.operator == "*"
1327
1325
 
1328
- def foreign_mapping_uses_inline_replacement?(expression, replacements)
1329
- case expression
1330
- when AST::Identifier
1331
- replacements.key?(expression.name)
1332
- when AST::MemberAccess
1333
- foreign_mapping_uses_inline_replacement?(expression.receiver, replacements)
1334
- when AST::IndexAccess
1335
- foreign_mapping_uses_inline_replacement?(expression.receiver, replacements) ||
1336
- foreign_mapping_uses_inline_replacement?(expression.index, replacements)
1337
- when AST::Specialization, AST::Call
1338
- foreign_mapping_uses_inline_replacement?(expression.callee, replacements) ||
1339
- expression.arguments.any? { |argument| foreign_mapping_uses_inline_replacement?(argument.value, replacements) }
1340
- when AST::UnaryOp
1341
- foreign_mapping_uses_inline_replacement?(expression.operand, replacements)
1342
- when AST::BinaryOp
1343
- foreign_mapping_uses_inline_replacement?(expression.left, replacements) ||
1344
- foreign_mapping_uses_inline_replacement?(expression.right, replacements)
1345
- when AST::IfExpr
1346
- foreign_mapping_uses_inline_replacement?(expression.condition, replacements) ||
1347
- foreign_mapping_uses_inline_replacement?(expression.then_expression, replacements) ||
1348
- foreign_mapping_uses_inline_replacement?(expression.else_expression, replacements)
1349
- when AST::UnsafeExpr
1350
- foreign_mapping_uses_inline_replacement?(expression.expression, replacements)
1351
- when AST::PrefixCast
1352
- foreign_mapping_uses_inline_replacement?(expression.expression, replacements)
1353
- else
1354
- false
1326
+ IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
1355
1327
  end
1356
- end
1357
-
1358
- def foreign_function_binding?(binding)
1359
- binding.ast.is_a?(AST::ForeignFunctionDecl)
1360
- end
1361
1328
 
1362
- def foreign_mapping_expression(decl)
1363
- return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping)
1364
-
1365
- AST::Call.new(
1366
- callee: decl.mapping,
1367
- arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) },
1368
- )
1369
- end
1329
+ def foreign_mapping_uses_inline_replacement?(expression, replacements)
1330
+ case expression
1331
+ when AST::Identifier
1332
+ replacements.key?(expression.name)
1333
+ when AST::MemberAccess
1334
+ foreign_mapping_uses_inline_replacement?(expression.receiver, replacements)
1335
+ when AST::IndexAccess
1336
+ foreign_mapping_uses_inline_replacement?(expression.receiver, replacements) ||
1337
+ foreign_mapping_uses_inline_replacement?(expression.index, replacements)
1338
+ when AST::Specialization, AST::Call
1339
+ foreign_mapping_uses_inline_replacement?(expression.callee, replacements) ||
1340
+ expression.arguments.any? { |argument| foreign_mapping_uses_inline_replacement?(argument.value, replacements) }
1341
+ when AST::UnaryOp
1342
+ foreign_mapping_uses_inline_replacement?(expression.operand, replacements)
1343
+ when AST::BinaryOp
1344
+ foreign_mapping_uses_inline_replacement?(expression.left, replacements) ||
1345
+ foreign_mapping_uses_inline_replacement?(expression.right, replacements)
1346
+ when AST::IfExpr
1347
+ foreign_mapping_uses_inline_replacement?(expression.condition, replacements) ||
1348
+ foreign_mapping_uses_inline_replacement?(expression.then_expression, replacements) ||
1349
+ foreign_mapping_uses_inline_replacement?(expression.else_expression, replacements)
1350
+ when AST::UnsafeExpr
1351
+ foreign_mapping_uses_inline_replacement?(expression.expression, replacements)
1352
+ when AST::PrefixCast
1353
+ foreign_mapping_uses_inline_replacement?(expression.expression, replacements)
1354
+ else
1355
+ false
1356
+ end
1357
+ end
1370
1358
 
1371
- def foreign_mapping_auto_call_shorthand?(expression)
1372
- case expression
1373
- when AST::Identifier
1374
- true
1375
- when AST::MemberAccess
1376
- foreign_mapping_auto_call_shorthand?(expression.receiver)
1377
- when AST::Specialization
1378
- foreign_mapping_auto_call_shorthand?(expression.callee)
1379
- else
1380
- false
1359
+ def foreign_function_binding?(binding)
1360
+ binding.ast.is_a?(AST::ForeignFunctionDecl)
1381
1361
  end
1382
- end
1383
1362
 
1384
- def foreign_mapping_public_alias_name(name)
1385
- "#{name}_public"
1386
- end
1363
+ def foreign_mapping_expression(decl)
1364
+ return decl.mapping unless foreign_mapping_implicit_call?(decl.mapping)
1387
1365
 
1388
- def substitute_foreign_mapping_expression(expression, replacements)
1389
- case expression
1390
- when AST::Identifier
1391
- replacements.fetch(expression.name, expression)
1392
- when AST::MemberAccess
1393
- AST::MemberAccess.new(receiver: substitute_foreign_mapping_expression(expression.receiver, replacements), member: expression.member)
1394
- when AST::IndexAccess
1395
- AST::IndexAccess.new(
1396
- receiver: substitute_foreign_mapping_expression(expression.receiver, replacements),
1397
- index: substitute_foreign_mapping_expression(expression.index, replacements),
1398
- )
1399
- when AST::Specialization
1400
- AST::Specialization.new(
1401
- callee: substitute_foreign_mapping_expression(expression.callee, replacements),
1402
- arguments: expression.arguments.map do |argument|
1403
- AST::Argument.new(name: argument.name, value: substitute_foreign_mapping_expression(argument.value, replacements))
1404
- end,
1405
- )
1406
- when AST::Call
1407
1366
  AST::Call.new(
1408
- callee: substitute_foreign_mapping_expression(expression.callee, replacements),
1409
- arguments: expression.arguments.map do |argument|
1410
- AST::Argument.new(name: argument.name, value: substitute_foreign_mapping_expression(argument.value, replacements))
1411
- end,
1412
- )
1413
- when AST::UnaryOp
1414
- AST::UnaryOp.new(operator: expression.operator, operand: substitute_foreign_mapping_expression(expression.operand, replacements))
1415
- when AST::BinaryOp
1416
- AST::BinaryOp.new(
1417
- operator: expression.operator,
1418
- left: substitute_foreign_mapping_expression(expression.left, replacements),
1419
- right: substitute_foreign_mapping_expression(expression.right, replacements),
1367
+ callee: decl.mapping,
1368
+ arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) },
1420
1369
  )
1421
- when AST::IfExpr
1422
- AST::IfExpr.new(
1423
- condition: substitute_foreign_mapping_expression(expression.condition, replacements),
1424
- then_expression: substitute_foreign_mapping_expression(expression.then_expression, replacements),
1425
- else_expression: substitute_foreign_mapping_expression(expression.else_expression, replacements),
1426
- )
1427
- when AST::UnsafeExpr
1428
- AST::UnsafeExpr.new(expression: substitute_foreign_mapping_expression(expression.expression, replacements))
1429
- when AST::PrefixCast
1430
- AST::PrefixCast.new(target_type: expression.target_type, expression: substitute_foreign_mapping_expression(expression.expression, replacements))
1431
- else
1432
- expression
1433
1370
  end
1434
- end
1435
1371
 
1436
- def foreign_mapping_reference_counts(expression, counts = Hash.new(0))
1437
- case expression
1438
- when AST::Identifier
1439
- counts[expression.name] += 1
1440
- when AST::MemberAccess
1441
- foreign_mapping_reference_counts(expression.receiver, counts)
1442
- when AST::IndexAccess
1443
- foreign_mapping_reference_counts(expression.receiver, counts)
1444
- foreign_mapping_reference_counts(expression.index, counts)
1445
- when AST::Specialization, AST::Call
1446
- foreign_mapping_reference_counts(expression.callee, counts)
1447
- expression.arguments.each { |argument| foreign_mapping_reference_counts(argument.value, counts) }
1448
- when AST::UnaryOp
1449
- foreign_mapping_reference_counts(expression.operand, counts)
1450
- when AST::BinaryOp
1451
- foreign_mapping_reference_counts(expression.left, counts)
1452
- foreign_mapping_reference_counts(expression.right, counts)
1453
- when AST::IfExpr
1454
- foreign_mapping_reference_counts(expression.condition, counts)
1455
- foreign_mapping_reference_counts(expression.then_expression, counts)
1456
- foreign_mapping_reference_counts(expression.else_expression, counts)
1457
- when AST::UnsafeExpr
1458
- foreign_mapping_reference_counts(expression.expression, counts)
1459
- when AST::PrefixCast
1460
- foreign_mapping_reference_counts(expression.expression, counts)
1372
+ def foreign_mapping_implicit_call?(expression)
1373
+ case expression
1374
+ when AST::Identifier
1375
+ true
1376
+ when AST::MemberAccess
1377
+ foreign_mapping_implicit_call?(expression.receiver)
1378
+ when AST::Specialization
1379
+ foreign_mapping_implicit_call?(expression.callee)
1380
+ else
1381
+ false
1382
+ end
1461
1383
  end
1462
1384
 
1463
- counts
1464
- end
1465
-
1466
- def duplicable_foreign_argument_expression?(expression)
1467
- case expression
1468
- when AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral,
1469
- IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral
1470
- true
1471
- when AST::MemberAccess
1472
- duplicable_foreign_argument_expression?(expression.receiver)
1473
- when IR::Member
1474
- duplicable_foreign_argument_expression?(expression.receiver)
1475
- when AST::UnaryOp
1476
- duplicable_foreign_argument_expression?(expression.operand)
1477
- when IR::Unary
1478
- duplicable_foreign_argument_expression?(expression.operand)
1479
- when AST::BinaryOp
1480
- duplicable_foreign_argument_expression?(expression.left) && duplicable_foreign_argument_expression?(expression.right)
1481
- when IR::Binary
1482
- duplicable_foreign_argument_expression?(expression.left) && duplicable_foreign_argument_expression?(expression.right)
1483
- else
1484
- false
1385
+ def foreign_mapping_public_alias_name(name)
1386
+ "#{name}_public"
1485
1387
  end
1486
- end
1487
1388
 
1488
- def foreign_argument_needs_temporary_binding?(expression, reference_count:)
1489
- return true if reference_count > 1 && !duplicable_foreign_argument_expression?(expression)
1389
+ def substitute_foreign_mapping_expression(expression, replacements)
1390
+ case expression
1391
+ when AST::Identifier
1392
+ replacements.fetch(expression.name, expression)
1393
+ when AST::MemberAccess
1394
+ AST::MemberAccess.new(receiver: substitute_foreign_mapping_expression(expression.receiver, replacements), member: expression.member)
1395
+ when AST::IndexAccess
1396
+ AST::IndexAccess.new(
1397
+ receiver: substitute_foreign_mapping_expression(expression.receiver, replacements),
1398
+ index: substitute_foreign_mapping_expression(expression.index, replacements),
1399
+ )
1400
+ when AST::Specialization
1401
+ AST::Specialization.new(
1402
+ callee: substitute_foreign_mapping_expression(expression.callee, replacements),
1403
+ arguments: expression.arguments.map do |argument|
1404
+ AST::Argument.new(name: argument.name, value: substitute_foreign_mapping_expression(argument.value, replacements))
1405
+ end,
1406
+ )
1407
+ when AST::Call
1408
+ AST::Call.new(
1409
+ callee: substitute_foreign_mapping_expression(expression.callee, replacements),
1410
+ arguments: expression.arguments.map do |argument|
1411
+ AST::Argument.new(name: argument.name, value: substitute_foreign_mapping_expression(argument.value, replacements))
1412
+ end,
1413
+ )
1414
+ when AST::UnaryOp
1415
+ AST::UnaryOp.new(operator: expression.operator, operand: substitute_foreign_mapping_expression(expression.operand, replacements))
1416
+ when AST::BinaryOp
1417
+ AST::BinaryOp.new(
1418
+ operator: expression.operator,
1419
+ left: substitute_foreign_mapping_expression(expression.left, replacements),
1420
+ right: substitute_foreign_mapping_expression(expression.right, replacements),
1421
+ )
1422
+ when AST::IfExpr
1423
+ AST::IfExpr.new(
1424
+ condition: substitute_foreign_mapping_expression(expression.condition, replacements),
1425
+ then_expression: substitute_foreign_mapping_expression(expression.then_expression, replacements),
1426
+ else_expression: substitute_foreign_mapping_expression(expression.else_expression, replacements),
1427
+ )
1428
+ when AST::UnsafeExpr
1429
+ AST::UnsafeExpr.new(expression: substitute_foreign_mapping_expression(expression.expression, replacements))
1430
+ when AST::PrefixCast
1431
+ AST::PrefixCast.new(target_type: expression.target_type, expression: substitute_foreign_mapping_expression(expression.expression, replacements))
1432
+ else
1433
+ expression
1434
+ end
1435
+ end
1490
1436
 
1491
- !inlineable_foreign_argument_expression?(expression)
1492
- end
1437
+ def foreign_mapping_reference_counts(expression, counts = Hash.new(0))
1438
+ case expression
1439
+ when AST::Identifier
1440
+ counts[expression.name] += 1
1441
+ when AST::MemberAccess
1442
+ foreign_mapping_reference_counts(expression.receiver, counts)
1443
+ when AST::IndexAccess
1444
+ foreign_mapping_reference_counts(expression.receiver, counts)
1445
+ foreign_mapping_reference_counts(expression.index, counts)
1446
+ when AST::Specialization, AST::Call
1447
+ foreign_mapping_reference_counts(expression.callee, counts)
1448
+ expression.arguments.each { |argument| foreign_mapping_reference_counts(argument.value, counts) }
1449
+ when AST::UnaryOp
1450
+ foreign_mapping_reference_counts(expression.operand, counts)
1451
+ when AST::BinaryOp
1452
+ foreign_mapping_reference_counts(expression.left, counts)
1453
+ foreign_mapping_reference_counts(expression.right, counts)
1454
+ when AST::IfExpr
1455
+ foreign_mapping_reference_counts(expression.condition, counts)
1456
+ foreign_mapping_reference_counts(expression.then_expression, counts)
1457
+ foreign_mapping_reference_counts(expression.else_expression, counts)
1458
+ when AST::UnsafeExpr
1459
+ foreign_mapping_reference_counts(expression.expression, counts)
1460
+ when AST::PrefixCast
1461
+ foreign_mapping_reference_counts(expression.expression, counts)
1462
+ end
1493
1463
 
1494
- def automatic_foreign_cstr_list_temp_needed?(parameter, _expression, env: nil)
1495
- return false unless parameter.type.is_a?(Types::Span) && parameter.type.element_type == @ctx.types.fetch("str")
1496
- return false unless parameter.boundary_type.is_a?(Types::Span)
1464
+ counts
1465
+ end
1497
1466
 
1498
- boundary_element_type = parameter.boundary_type.element_type
1499
- boundary_element_type == @ctx.types.fetch("cstr") || char_pointer_type?(boundary_element_type)
1500
- end
1467
+ def duplicable_foreign_argument_expression?(expression)
1468
+ case expression
1469
+ when AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral,
1470
+ IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral
1471
+ true
1472
+ when AST::MemberAccess
1473
+ duplicable_foreign_argument_expression?(expression.receiver)
1474
+ when IR::Member
1475
+ duplicable_foreign_argument_expression?(expression.receiver)
1476
+ when AST::UnaryOp
1477
+ duplicable_foreign_argument_expression?(expression.operand)
1478
+ when IR::Unary
1479
+ duplicable_foreign_argument_expression?(expression.operand)
1480
+ when AST::BinaryOp
1481
+ duplicable_foreign_argument_expression?(expression.left) && duplicable_foreign_argument_expression?(expression.right)
1482
+ when IR::Binary
1483
+ duplicable_foreign_argument_expression?(expression.left) && duplicable_foreign_argument_expression?(expression.right)
1484
+ else
1485
+ false
1486
+ end
1487
+ end
1501
1488
 
1502
- def automatic_foreign_cstr_temp_needed?(parameter, expression, env:)
1503
- return false unless parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
1504
- return false if expression.is_a?(AST::StringLiteral) && !expression.cstring
1505
- return false if cstr_backed_expression?(expression, env)
1489
+ def foreign_argument_needs_temporary_binding?(expression, reference_count:)
1490
+ return true if reference_count > 1 && !duplicable_foreign_argument_expression?(expression)
1506
1491
 
1507
- infer_expression_type(expression, env:) != @ctx.types.fetch("cstr")
1508
- end
1492
+ !inlineable_foreign_argument_expression?(expression)
1493
+ end
1509
1494
 
1510
- def automatic_variadic_foreign_cstr_temp_needed?(expression, env:)
1511
- return false if expression.is_a?(AST::StringLiteral) && !expression.cstring
1512
- return false if cstr_backed_expression?(expression, env)
1495
+ def automatic_foreign_cstr_list_temp_needed?(parameter, _expression, env: nil)
1496
+ return false unless parameter.type.is_a?(Types::Span) && parameter.type.element_type == @ctx.types.fetch("str")
1497
+ return false unless parameter.boundary_type.is_a?(Types::Span)
1513
1498
 
1514
- infer_expression_type(expression, env:) == @ctx.types.fetch("str")
1515
- end
1499
+ boundary_element_type = parameter.boundary_type.element_type
1500
+ boundary_element_type == @ctx.types.fetch("cstr") || char_pointer_type?(boundary_element_type)
1501
+ end
1516
1502
 
1517
- def temporary_foreign_cstr_expression?(expression)
1518
- expression.is_a?(IR::Call) && expression.callee == "mt_foreign_str_to_cstr_temp"
1519
- end
1503
+ def automatic_foreign_cstr_temp_needed?(parameter, expression, env:)
1504
+ return false unless parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
1505
+ return false if expression.is_a?(AST::StringLiteral) && !expression.cstring
1506
+ return false if cstr_backed_expression?(expression, env)
1520
1507
 
1521
- def lower_specialization(expression, env:, type:)
1522
- if expression.callee.is_a?(AST::Identifier) && expression.callee.name == "zero"
1523
- return IR::ZeroInit.new(type:)
1508
+ infer_expression_type(expression, env:) != @ctx.types.fetch("cstr")
1524
1509
  end
1525
1510
 
1526
- if expression.callee.is_a?(AST::Identifier) && expression.callee.name == "default"
1527
- resolution = resolve_default_specialization(expression, env:)
1528
- return IR::Call.new(callee: resolution.callee_name, arguments: [], type:) if resolution.binding
1511
+ def automatic_variadic_foreign_cstr_temp_needed?(expression, env:)
1512
+ return false if expression.is_a?(AST::StringLiteral) && !expression.cstring
1513
+ return false if cstr_backed_expression?(expression, env)
1529
1514
 
1530
- return IR::ZeroInit.new(type:)
1515
+ infer_expression_type(expression, env:) == @ctx.types.fetch("str")
1531
1516
  end
1532
1517
 
1533
- if (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), type))
1534
- return literal
1518
+ def temporary_foreign_cstr_expression?(expression)
1519
+ expression.is_a?(IR::Call) && expression.callee == "mt_foreign_str_to_cstr_temp"
1535
1520
  end
1536
1521
 
1537
- if (callable_resolution = resolve_specialized_callable_binding(expression, env:))
1538
- callable_kind, function_binding, = callable_resolution
1539
- raise LoweringError.new("specialized method must be called", line: 0, column: 0, path: @ctx.current_analysis_path) if callable_kind == :method
1522
+ def lower_specialization(expression, env:, type:)
1523
+ if expression.callee.is_a?(AST::Identifier) && expression.callee.name == "zero"
1524
+ return IR::ZeroInit.new(type:)
1525
+ end
1540
1526
 
1541
- raise LoweringError.new("foreign function #{function_binding.name} cannot be used as a value", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_function_binding?(function_binding)
1527
+ if expression.callee.is_a?(AST::Identifier) && expression.callee.name == "default"
1528
+ resolution = resolve_default_specialization(expression, env:)
1529
+ return IR::Call.new(callee: resolution.callee_name, arguments: [], type:) if resolution.binding
1542
1530
 
1543
- if function_binding.external
1544
- return IR::Name.new(name: external_function_c_name(function_binding), type:, pointer: false)
1531
+ return IR::ZeroInit.new(type:)
1545
1532
  end
1546
1533
 
1547
- return IR::Name.new(
1548
- name: function_binding_c_name(function_binding, module_name: function_binding.owner.module_name),
1549
- type:,
1550
- pointer: false,
1551
- )
1552
- end
1534
+ if (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), type))
1535
+ return literal
1536
+ end
1553
1537
 
1554
- raise LoweringError.new("specialization #{expression.callee.name} must be called", line: 0, column: 0, path: @ctx.current_analysis_path) if expression.callee.is_a?(AST::Identifier)
1538
+ if (callable_resolution = resolve_specialized_callable_binding(expression, env:))
1539
+ callable_kind, function_binding, = callable_resolution
1540
+ raise LoweringError.new("specialized method must be called", line: 0, column: 0, path: @ctx.current_analysis_path) if callable_kind == :method
1555
1541
 
1556
- raise LoweringError.new("unsupported specialization #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1557
- end
1542
+ raise LoweringError.new("foreign function #{function_binding.name} cannot be used as a value", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_function_binding?(function_binding)
1543
+
1544
+ if function_binding.external
1545
+ return IR::Name.new(name: external_function_c_name(function_binding), type:, pointer: false)
1546
+ end
1547
+
1548
+ return IR::Name.new(
1549
+ name: function_binding_c_name(function_binding, module_name: function_binding.owner.module_name),
1550
+ type:,
1551
+ pointer: false,
1552
+ )
1553
+ end
1558
1554
 
1559
- def atomic_method_kind(receiver_type, name)
1560
- return unless atomic_type?(receiver_type)
1555
+ raise LoweringError.new("specialization #{expression.callee.name} must be called", line: 0, column: 0, path: @ctx.current_analysis_path) if expression.callee.is_a?(AST::Identifier)
1561
1556
 
1562
- case name
1563
- when "load" then :atomic_load
1564
- when "store" then :atomic_store
1565
- when "add" then :atomic_add
1566
- when "sub" then :atomic_sub
1567
- when "exchange" then :atomic_exchange
1568
- when "compare_exchange" then :atomic_compare_exchange
1557
+ raise LoweringError.new("unsupported specialization #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
1569
1558
  end
1570
- end
1571
1559
 
1572
- def simd_method_kind(receiver_type, name)
1573
- return unless simd_type?(receiver_type)
1560
+ def atomic_method_kind(receiver_type, name)
1561
+ return unless atomic_type?(receiver_type)
1574
1562
 
1575
- case name
1576
- when "with" then :simd_lane_with
1563
+ case name
1564
+ when "load" then :atomic_load
1565
+ when "store" then :atomic_store
1566
+ when "add" then :atomic_add
1567
+ when "sub" then :atomic_sub
1568
+ when "exchange" then :atomic_exchange
1569
+ when "compare_exchange" then :atomic_compare_exchange
1570
+ end
1577
1571
  end
1578
- end
1579
1572
 
1580
- def lower_atomic_method_call(kind, receiver, expression, env:, type:)
1581
- receiver_type = infer_expression_type(receiver, env:)
1582
- elem_type = atomic_element_type(receiver_type)
1583
- ptr_type = Types::Registry.generic_instance("ptr", [elem_type])
1584
- receiver_ir = lower_expression(receiver, env:)
1585
- addr = IR::AddressOf.new(expression: receiver_ir, type: ptr_type)
1586
- seq_cst = IR::IntegerLiteral.new(value: 5, type: @ctx.types.fetch("int"))
1587
-
1588
- case kind
1589
- when :atomic_load
1590
- IR::Call.new(callee: "__atomic_load_n", arguments: [addr, seq_cst], type: elem_type)
1591
- when :atomic_store
1592
- arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1593
- IR::Call.new(callee: "__atomic_store_n", arguments: [addr, arg, seq_cst], type:)
1594
- when :atomic_add
1595
- arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1596
- IR::Call.new(callee: "__atomic_fetch_add", arguments: [addr, arg, seq_cst], type: elem_type)
1597
- when :atomic_sub
1598
- arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1599
- IR::Call.new(callee: "__atomic_fetch_sub", arguments: [addr, arg, seq_cst], type: elem_type)
1600
- when :atomic_exchange
1601
- arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1602
- IR::Call.new(callee: "__atomic_exchange_n", arguments: [addr, arg, seq_cst], type: elem_type)
1603
- when :atomic_compare_exchange
1604
- raise LoweringError.new("atomic compare_exchange is not yet implemented in the built-in surface; use std.sync.AtomicUint for compare-exchange operations", line: 0, column: 0, path: @ctx.current_analysis_path)
1573
+ def simd_method_kind(receiver_type, name)
1574
+ return unless simd_type?(receiver_type)
1575
+
1576
+ case name
1577
+ when "with" then :simd_lane_with
1578
+ end
1605
1579
  end
1606
- end
1607
1580
 
1608
- def lower_simd_lane_with(_kind, receiver, expression, env:, type:)
1609
- receiver_ir = lower_expression(receiver, env:)
1610
- index_ir = lower_expression(expression.arguments[0].value, env:, expected_type: @ctx.types.fetch("int"))
1611
- value_ir = lower_contextual_expression(expression.arguments[1].value, env:, expected_type: type.element_type)
1612
-
1613
- IR::SimdLaneWith.new(
1614
- src: receiver_ir,
1615
- index: index_ir,
1616
- value: value_ir,
1617
- type:,
1618
- )
1619
- end
1581
+ def lower_atomic_method_call(kind, receiver, expression, env:, type:)
1582
+ receiver_type = infer_expression_type(receiver, env:)
1583
+ elem_type = atomic_element_type(receiver_type)
1584
+ ptr_type = Types::Registry.generic_instance("ptr", [elem_type])
1585
+ receiver_ir = lower_expression(receiver, env:)
1586
+ addr = IR::AddressOf.new(expression: receiver_ir, type: ptr_type)
1587
+ seq_cst = IR::IntegerLiteral.new(value: 5, type: @ctx.types.fetch("int"))
1620
1588
 
1621
- def lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: false)
1622
- stateful = expression.arguments.length == 2
1623
- callee_key = if stateful
1624
- once ? :subscribe_once_stateful_linkage_name : :subscribe_stateful_linkage_name
1625
- else
1626
- once ? :subscribe_once_linkage_name : :subscribe_linkage_name
1627
- end
1628
- arguments = [event_pointer]
1629
- if stateful
1630
- arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: runtime.fetch(:void_ptr))
1631
- arguments << lower_contextual_expression(expression.arguments.fetch(1).value, env:, expected_type: runtime.fetch(:void_ptr))
1632
- else
1633
- arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: runtime.fetch(:listener_type))
1589
+ case kind
1590
+ when :atomic_load
1591
+ IR::Call.new(callee: "__atomic_load_n", arguments: [addr, seq_cst], type: elem_type)
1592
+ when :atomic_store
1593
+ arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1594
+ IR::Call.new(callee: "__atomic_store_n", arguments: [addr, arg, seq_cst], type:)
1595
+ when :atomic_add
1596
+ arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1597
+ IR::Call.new(callee: "__atomic_fetch_add", arguments: [addr, arg, seq_cst], type: elem_type)
1598
+ when :atomic_sub
1599
+ arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1600
+ IR::Call.new(callee: "__atomic_fetch_sub", arguments: [addr, arg, seq_cst], type: elem_type)
1601
+ when :atomic_exchange
1602
+ arg = lower_contextual_expression(expression.arguments.first.value, env:, expected_type: elem_type)
1603
+ IR::Call.new(callee: "__atomic_exchange_n", arguments: [addr, arg, seq_cst], type: elem_type)
1604
+ when :atomic_compare_exchange
1605
+ raise LoweringError.new("atomic compare_exchange is not yet implemented in the built-in surface; use std.sync.AtomicUint for compare-exchange operations", line: 0, column: 0, path: @ctx.current_analysis_path)
1606
+ end
1607
+ end
1608
+
1609
+ def lower_simd_lane_with(_kind, receiver, expression, env:, type:)
1610
+ receiver_ir = lower_expression(receiver, env:)
1611
+ index_ir = lower_expression(expression.arguments[0].value, env:, expected_type: @ctx.types.fetch("int"))
1612
+ value_ir = lower_contextual_expression(expression.arguments[1].value, env:, expected_type: type.element_type)
1613
+
1614
+ IR::SimdLaneWith.new(
1615
+ src: receiver_ir,
1616
+ index: index_ir,
1617
+ value: value_ir,
1618
+ type:,
1619
+ )
1620
+ end
1621
+
1622
+ def lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: false)
1623
+ stateful = expression.arguments.length == 2
1624
+ callee_key = if stateful
1625
+ once ? :subscribe_once_stateful_linkage_name : :subscribe_stateful_linkage_name
1626
+ else
1627
+ once ? :subscribe_once_linkage_name : :subscribe_linkage_name
1628
+ end
1629
+ arguments = [event_pointer]
1630
+ if stateful
1631
+ arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: runtime.fetch(:void_ptr))
1632
+ arguments << lower_contextual_expression(expression.arguments.fetch(1).value, env:, expected_type: runtime.fetch(:void_ptr))
1633
+ else
1634
+ arguments << lower_contextual_expression(expression.arguments.fetch(0).value, env:, expected_type: runtime.fetch(:listener_type))
1635
+ end
1636
+ IR::Call.new(callee: runtime.fetch(callee_key), arguments:, type:)
1634
1637
  end
1635
- IR::Call.new(callee: runtime.fetch(callee_key), arguments:, type:)
1636
1638
  end
1637
1639
  end
1638
1640
  end