mt-lang 0.3.8 → 0.3.10

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