mt-lang 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,992 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
7
+
8
+ def resolved_attribute_applications_for_target(target)
9
+ target_id = case target
10
+ when Types::StructHandle then target.declaration.object_id
11
+ when Types::FieldHandle then target.field_declaration.object_id
12
+ when Types::CallableHandle then target.declaration.object_id
13
+ end
14
+ return [] unless target_id
15
+
16
+ applications = @ctx.resolved_attribute_applications[target_id]
17
+ return applications if applications
18
+
19
+ @ctx.imports.each_value do |imported_module|
20
+ applications = imported_module.attribute_applications[target_id]
21
+ return applications if applications
22
+ end
23
+
24
+ []
25
+ end
26
+
27
+ def find_attribute_application(target, binding)
28
+ resolved_attribute_applications_for_target(target).find do |application|
29
+ same_attribute_binding?(application.binding, binding)
30
+ end
31
+ end
32
+
33
+ def same_attribute_binding?(left, right)
34
+ left.name == right.name && left.module_name == right.module_name
35
+ end
36
+
37
+ def qualified_attribute_name(binding)
38
+ binding.module_name ? "#{binding.module_name}.#{binding.name}" : binding.name
39
+ end
40
+
41
+ def attribute_presence_refinement_key(target, binding)
42
+ AttributePresenceKey.new(target, binding.module_name, binding.name)
43
+ end
44
+
45
+ def attribute_presence_guard_active?(scopes, key)
46
+ scopes.any? { |scope| scope.key?(key) }
47
+ end
48
+
49
+ def check_aggregate_construction(struct_type, arguments, scopes:)
50
+ require_unsafe!("str construction requires unsafe") if struct_type.is_a?(Types::StringView)
51
+
52
+ check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "aggregate construction")
53
+ struct_type
54
+ end
55
+
56
+ def check_struct_with_call(struct_type, _receiver_expression, arguments, scopes:)
57
+ check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "struct.with()")
58
+ struct_type
59
+ end
60
+
61
+ def check_aggregate_field_arguments(struct_type, arguments, scopes:, context:)
62
+ display_name = aggregate_display_name(struct_type)
63
+
64
+ raise_sema_error("#{context} for #{display_name} requires named arguments") unless arguments.all?(&:name)
65
+
66
+ provided = {}
67
+ arguments.each do |argument|
68
+ field_type = struct_type.field(argument.name)
69
+ raise_sema_error("unknown field #{display_name}.#{argument.name}") unless field_type
70
+ raise_sema_error("duplicate field #{display_name}.#{argument.name}") if provided.key?(argument.name)
71
+
72
+ actual_type = infer_expression(argument.value, scopes:, expected_type: field_type)
73
+ ensure_assignable!(
74
+ actual_type,
75
+ field_type,
76
+ "field #{display_name}.#{argument.name} expects #{field_type}, got #{actual_type}",
77
+ expression: argument.value,
78
+ external_numeric: struct_type.respond_to?(:external) && struct_type.external,
79
+ external_pointer_null: struct_type.respond_to?(:external) && struct_type.external,
80
+ contextual_int_to_float: contextual_int_to_float_target?(field_type),
81
+ )
82
+ provided[argument.name] = true
83
+ end
84
+ end
85
+
86
+ def check_variant_arm_construction(callable, arguments, scopes:)
87
+ variant_type, arm_name = callable
88
+ fields = variant_type.arm(arm_name)
89
+
90
+ if fields.nil? || fields.empty?
91
+ raise_sema_error("variant arm #{variant_type}.#{arm_name} has no payload; construct it without arguments") unless arguments.empty?
92
+
93
+ return variant_type
94
+ end
95
+
96
+ raise_sema_error("variant arm construction requires named arguments") unless arguments.all?(&:name)
97
+
98
+ provided = {}
99
+ arguments.each do |argument|
100
+ field_type = fields[argument.name]
101
+ raise_sema_error("unknown field #{variant_type}.#{arm_name}.#{argument.name}") unless field_type
102
+ raise_sema_error("duplicate field #{variant_type}.#{arm_name}.#{argument.name}") if provided.key?(argument.name)
103
+
104
+ actual_type = infer_expression(argument.value, scopes:, expected_type: field_type)
105
+ ensure_assignable!(
106
+ actual_type,
107
+ field_type,
108
+ "field #{variant_type}.#{arm_name}.#{argument.name} expects #{field_type}, got #{actual_type}",
109
+ expression: argument.value,
110
+ contextual_int_to_float: contextual_int_to_float_target?(field_type),
111
+ )
112
+ provided[argument.name] = true
113
+ end
114
+
115
+ missing = fields.keys - provided.keys
116
+ missing.reject! { |name| fields[name].void? }
117
+ raise_sema_error("variant arm #{variant_type}.#{arm_name} is missing fields: #{missing.join(', ')}") unless missing.empty?
118
+
119
+ variant_type
120
+ end
121
+
122
+ def check_array_construction(array_type, arguments, scopes:)
123
+ raise_sema_error("array construction does not support named arguments") if arguments.any?(&:name)
124
+
125
+ element_type = array_element_type(array_type)
126
+ length = array_length(array_type)
127
+ raise_sema_error("array expects at most #{length} elements, got #{arguments.length}") if arguments.length > length
128
+
129
+ arguments.each do |argument|
130
+ actual_type = infer_expression(argument.value, scopes:, expected_type: element_type)
131
+ ensure_assignable!(
132
+ actual_type,
133
+ element_type,
134
+ "array element expects #{element_type}, got #{actual_type}",
135
+ expression: argument.value,
136
+ )
137
+ end
138
+
139
+ array_type
140
+ end
141
+
142
+ def check_cast_call(target_type, arguments, scopes:)
143
+ raise_sema_error("cast requires exactly one argument") unless arguments.length == 1
144
+ raise_sema_error("cast does not support named arguments") if arguments.first.name
145
+
146
+ source_type = infer_expression(arguments.first.value, scopes:)
147
+ if source_type == target_type
148
+ return target_type
149
+ end
150
+
151
+ if pointer_cast?(source_type, target_type)
152
+ expression = arguments.first.value
153
+ require_unsafe!("pointer cast requires unsafe", line: source_line(expression), column: source_column(expression))
154
+
155
+ return target_type
156
+ end
157
+
158
+ if ref_to_pointer_cast?(source_type, target_type)
159
+ expression = arguments.first.value
160
+ require_unsafe!("ref to pointer cast requires unsafe", line: source_line(expression), column: source_column(expression))
161
+
162
+ return target_type
163
+ end
164
+
165
+ source_numeric_type = cast_numeric_type(source_type)
166
+ target_numeric_type = cast_numeric_type(target_type)
167
+
168
+ unless source_numeric_type && target_numeric_type
169
+ raise_sema_error("cast requires compatible types, got #{source_type} -> #{target_type}")
170
+ end
171
+
172
+ target_type
173
+ end
174
+
175
+ def cast_numeric_type(type)
176
+ return type if type.is_a?(Types::Primitive) && (type.numeric? || type.name == "bool")
177
+ return type.backing_type if type.is_a?(Types::EnumBase) && type.backing_type.numeric?
178
+ return type if char_type?(type)
179
+ return type.backing_type if type.is_a?(Types::EnumBase) && char_type?(type.backing_type)
180
+
181
+ nil
182
+ end
183
+
184
+ def infer_null_literal(expression)
185
+ return @null_type unless expression.type
186
+
187
+ target_type = resolve_type_ref(expression.type)
188
+ unless typed_null_target_type?(target_type)
189
+ raise_sema_error("typed null requires pointer-like type, got #{target_type}")
190
+ end
191
+
192
+ Types::Null.new(target_type)
193
+ end
194
+
195
+ def check_reinterpret_call(target_type, arguments, scopes:)
196
+ raise_sema_error("reinterpret requires exactly one argument") unless arguments.length == 1
197
+ raise_sema_error("reinterpret does not support named arguments") if arguments.first.name
198
+ require_unsafe!("reinterpret requires unsafe")
199
+
200
+ source_type = infer_expression(arguments.first.value, scopes:)
201
+ unless reinterpretable_type?(source_type) && reinterpretable_type?(target_type)
202
+ raise_sema_error("reinterpret requires non-array concrete sized types, got #{source_type} -> #{target_type}")
203
+ end
204
+
205
+ source_size = CompileTime::Layout.size_of(source_type)
206
+ target_size = CompileTime::Layout.size_of(target_type)
207
+ if source_size && target_size && source_size != target_size
208
+ raise_sema_error("reinterpret requires equal-size types, got #{source_type} (#{source_size} byte#{source_size == 1 ? '' : 's'}) -> #{target_type} (#{target_size} byte#{target_size == 1 ? '' : 's'})")
209
+ end
210
+
211
+ target_type
212
+ end
213
+
214
+ def check_zero_call(target_type, arguments, expected_type: nil, operation: "zero")
215
+ raise_sema_error("#{operation} expects 0 arguments, got #{arguments.length}") unless arguments.empty?
216
+
217
+ zero_initializable_type?(target_type, operation:)
218
+ if expected_type.is_a?(Types::Nullable) && typed_null_target_type?(expected_type.base) && types_compatible?(target_type, expected_type.base)
219
+ raise_sema_error("use null instead of #{operation}[#{target_type}] in nullable pointer-like context #{expected_type}")
220
+ end
221
+
222
+ target_type
223
+ end
224
+
225
+ def resolve_default_specialization(callee)
226
+ raise_sema_error("default requires exactly one type argument") unless callee.arguments.length == 1
227
+
228
+ type_arg = callee.arguments.first.value
229
+ raise_sema_error("default type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
230
+
231
+ target_type = resolve_type_ref(type_arg)
232
+ binding = resolve_explicit_default_binding(target_type, context: "default[#{target_type}]")
233
+ raise_sema_error("default[#{target_type}] requires associated function #{target_type}.default()") unless binding
234
+
235
+ DefaultResolution.new(
236
+ target_type:,
237
+ binding:,
238
+ )
239
+ end
240
+
241
+ def resolve_hash_specialization(callee)
242
+ raise_sema_error("hash requires exactly one type argument") unless callee.arguments.length == 1
243
+
244
+ type_arg = callee.arguments.first.value
245
+ raise_sema_error("hash type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
246
+
247
+ target_type = resolve_type_ref(type_arg)
248
+ binding = resolve_explicit_hash_binding(target_type, context: "hash[#{target_type}]")
249
+ raise_sema_error("hash[#{target_type}] requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint") unless binding
250
+
251
+ HashResolution.new(
252
+ target_type:,
253
+ binding:,
254
+ )
255
+ end
256
+
257
+ def resolve_equal_specialization(callee)
258
+ raise_sema_error("equal requires exactly one type argument") unless callee.arguments.length == 1
259
+
260
+ type_arg = callee.arguments.first.value
261
+ raise_sema_error("equal type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
262
+
263
+ target_type = resolve_type_ref(type_arg)
264
+ binding = resolve_explicit_equal_binding(target_type, context: "equal[#{target_type}]")
265
+ raise_sema_error("equal[#{target_type}] requires associated function #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool") unless binding
266
+
267
+ EqualResolution.new(
268
+ target_type:,
269
+ binding:,
270
+ )
271
+ end
272
+
273
+ def resolve_order_specialization(callee)
274
+ raise_sema_error("order requires exactly one type argument") unless callee.arguments.length == 1
275
+
276
+ type_arg = callee.arguments.first.value
277
+ raise_sema_error("order type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
278
+
279
+ target_type = resolve_type_ref(type_arg)
280
+ binding = resolve_explicit_order_binding(target_type, context: "order[#{target_type}]")
281
+ raise_sema_error("order[#{target_type}] requires associated function #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int") unless binding
282
+
283
+ OrderResolution.new(
284
+ target_type:,
285
+ binding:,
286
+ )
287
+ end
288
+
289
+ def resolve_explicit_default_binding(target_type, context:)
290
+ requirement_message = "#{context} requires associated function #{target_type}.default()"
291
+ resolve_explicit_associated_binding(target_type, "default", requirement_message:) do |method|
292
+ raise_sema_error("#{context} requires #{target_type}.default() to take 0 arguments") unless method.type.params.empty?
293
+ unless types_compatible?(method.type.return_type, target_type)
294
+ raise_sema_error("#{context} requires #{target_type}.default() to return #{target_type}, got #{method.type.return_type}")
295
+ end
296
+ end
297
+ end
298
+
299
+ def resolve_explicit_hash_binding(target_type, context:)
300
+ requirement_message = "#{context} requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint"
301
+ resolve_explicit_associated_binding(target_type, "hash", requirement_message:) do |method|
302
+ unless method.type.params.map(&:type) == [const_pointer_to(target_type)]
303
+ raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint")
304
+ end
305
+ unless method.type.return_type == @ctx.types.fetch("uint")
306
+ raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint, got #{method.type.return_type}")
307
+ end
308
+ end
309
+ end
310
+
311
+ def resolve_explicit_equal_binding(target_type, context:)
312
+ requirement_message = "#{context} requires associated function #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool"
313
+ resolve_explicit_associated_binding(target_type, "equal", requirement_message:) do |method|
314
+ expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)]
315
+ unless method.type.params.map(&:type) == expected_param_types
316
+ raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool")
317
+ end
318
+ unless method.type.return_type == @ctx.types.fetch("bool")
319
+ raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool, got #{method.type.return_type}")
320
+ end
321
+ end
322
+ end
323
+
324
+ def resolve_explicit_order_binding(target_type, context:)
325
+ requirement_message = "#{context} requires associated function #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int"
326
+ resolve_explicit_associated_binding(target_type, "order", requirement_message:) do |method|
327
+ expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)]
328
+ unless method.type.params.map(&:type) == expected_param_types
329
+ raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int")
330
+ end
331
+ unless method.type.return_type == @ctx.types.fetch("int")
332
+ raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int, got #{method.type.return_type}")
333
+ end
334
+ end
335
+ end
336
+
337
+ def resolve_explicit_format_binding(target_type, context:)
338
+ length_binding = resolve_explicit_format_len_binding(target_type, context:)
339
+ append_binding = resolve_explicit_format_append_binding(target_type, context:)
340
+
341
+ return [length_binding, append_binding] if length_binding && append_binding
342
+
343
+ if length_binding || append_binding
344
+ raise_sema_error("#{context} requires methods #{target_type}.format_len() -> ptr_uint and #{target_type}.append_format(output: ref[std.string.String]) -> void")
345
+ end
346
+
347
+ nil
348
+ end
349
+
350
+ def resolve_explicit_format_len_binding(target_type, context:)
351
+ requirement_message = "#{context} requires method #{target_type}.format_len() -> ptr_uint"
352
+ resolve_explicit_instance_binding(target_type, "format_len", requirement_message:) do |method|
353
+ raise_sema_error("#{context} requires #{target_type}.format_len() to take 0 arguments") unless method.type.params.empty?
354
+ raise_sema_error("#{context} requires #{target_type}.format_len() to be non-editable") if method.type.receiver_editable
355
+ unless method.type.return_type == @ctx.types.fetch("ptr_uint")
356
+ raise_sema_error("#{context} requires #{target_type}.format_len() -> ptr_uint, got #{method.type.return_type}")
357
+ end
358
+ end
359
+ end
360
+
361
+ def resolve_explicit_format_append_binding(target_type, context:)
362
+ requirement_message = "#{context} requires method #{target_type}.append_format(output: ref[std.string.String]) -> void"
363
+ resolve_explicit_instance_binding(target_type, "append_format", requirement_message:) do |method|
364
+ raise_sema_error("#{context} requires #{target_type}.append_format() to be non-editable") if method.type.receiver_editable
365
+ unless method.type.params.length == 1 && string_builder_ref_type?(method.type.params.first.type)
366
+ raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void")
367
+ end
368
+ unless method.type.return_type == @ctx.types.fetch("void")
369
+ raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void, got #{method.type.return_type}")
370
+ end
371
+ end
372
+ end
373
+
374
+ def resolve_explicit_associated_binding(target_type, method_name, requirement_message:)
375
+ method = lookup_static_method(target_type, method_name)
376
+ if method
377
+ raise_sema_error(requirement_message) unless method.type.receiver_type.nil?
378
+
379
+ method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any?
380
+ yield method
381
+
382
+ return method
383
+ end
384
+
385
+ if (imported_module = imported_module_with_private_method(target_type, method_name))
386
+ raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}")
387
+ end
388
+
389
+ nil
390
+ end
391
+
392
+ def resolve_explicit_instance_binding(target_type, method_name, requirement_message:)
393
+ method = lookup_method(target_type, method_name)
394
+ if method
395
+ raise_sema_error(requirement_message) if method.type.receiver_type.nil?
396
+
397
+ method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any?
398
+ yield method
399
+
400
+ return method
401
+ end
402
+
403
+ if (imported_module = imported_module_with_private_method(target_type, method_name))
404
+ raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}")
405
+ end
406
+
407
+ nil
408
+ end
409
+
410
+ def check_hash_call(resolution, arguments, scopes:)
411
+ raise_sema_error("hash does not support named arguments") if arguments.any?(&:name)
412
+ raise_sema_error("hash expects 1 argument, got #{arguments.length}") unless arguments.length == 1
413
+
414
+ validate_hash_operation_argument!(arguments.first.value, resolution.target_type, scopes:, operation: "hash")
415
+ @ctx.types.fetch("uint")
416
+ end
417
+
418
+ def check_equal_call(resolution, arguments, scopes:)
419
+ raise_sema_error("equal does not support named arguments") if arguments.any?(&:name)
420
+ raise_sema_error("equal expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
421
+
422
+ arguments.each do |argument|
423
+ validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "equal")
424
+ end
425
+
426
+ @ctx.types.fetch("bool")
427
+ end
428
+
429
+ def check_order_call(resolution, arguments, scopes:)
430
+ raise_sema_error("order does not support named arguments") if arguments.any?(&:name)
431
+ raise_sema_error("order expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
432
+
433
+ arguments.each do |argument|
434
+ validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "order")
435
+ end
436
+
437
+ @ctx.types.fetch("int")
438
+ end
439
+
440
+ def validate_hash_operation_argument!(expression, target_type, scopes:, operation:)
441
+ actual_type = infer_expression(expression, scopes:)
442
+ expected_pointer_type = const_pointer_to(target_type)
443
+ return if argument_types_compatible?(actual_type, expected_pointer_type, external: false, expression:, scopes:)
444
+ return if ref_type?(actual_type) && types_compatible?(referenced_type(actual_type), target_type, expression:, scopes:)
445
+ return if safe_reference_source_expression?(expression, scopes:) && types_compatible?(actual_type, target_type, expression:, scopes:)
446
+
447
+ raise_sema_error("#{operation}[#{target_type}] expects a safe #{target_type} lvalue, ref[#{target_type}], ptr[#{target_type}], or const_ptr[#{target_type}], got #{actual_type}")
448
+ end
449
+
450
+ def check_str_buffer_method_call(kind, receiver, arguments, scopes:)
451
+ method_name = str_buffer_method_name(kind)
452
+ receiver_type = infer_expression(receiver, scopes:)
453
+ raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless str_buffer_type?(receiver_type)
454
+
455
+ case kind
456
+ when :str_buffer_clear, :str_buffer_len, :str_buffer_capacity, :str_buffer_as_str, :str_buffer_as_cstr
457
+ raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
458
+ raise_sema_error("#{method_name} expects 0 arguments, got #{arguments.length}") unless arguments.empty?
459
+ when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format
460
+ raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
461
+ raise_sema_error("#{method_name} expects 1 argument, got #{arguments.length}") unless arguments.length == 1
462
+ else
463
+ raise_sema_error("unsupported str_buffer method #{kind}")
464
+ end
465
+
466
+ case kind
467
+ when :str_buffer_clear
468
+ record_editable_receiver_expression(receiver)
469
+ raise_sema_error("cannot call editable method #{receiver_type}.clear on an immutable receiver") unless assignable_receiver?(receiver, scopes)
470
+
471
+ @ctx.types.fetch("void")
472
+ when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format
473
+ record_editable_receiver_expression(receiver)
474
+ raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless assignable_receiver?(receiver, scopes)
475
+
476
+ actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str"))
477
+ ensure_argument_assignable!(
478
+ actual_type,
479
+ @ctx.types.fetch("str"),
480
+ external: false,
481
+ message: "argument value to #{receiver_type}.#{method_name} expects str, got #{actual_type}",
482
+ expression: arguments.first.value,
483
+ )
484
+
485
+ @ctx.types.fetch("void")
486
+ when :str_buffer_len, :str_buffer_capacity
487
+ @ctx.types.fetch("ptr_uint")
488
+ when :str_buffer_as_str
489
+ raise_sema_error("#{receiver_type}.as_str requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:)
490
+
491
+ @ctx.types.fetch("str")
492
+ when :str_buffer_as_cstr
493
+ raise_sema_error("#{receiver_type}.as_cstr requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:)
494
+
495
+ @ctx.types.fetch("cstr")
496
+ else
497
+ raise_sema_error("unsupported str_buffer method #{kind}")
498
+ end
499
+ end
500
+
501
+ def check_event_method_call(kind, receiver, arguments, scopes:)
502
+ method_name = event_method_name(kind)
503
+ receiver_type = infer_expression(receiver, scopes:)
504
+ raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless event_type?(receiver_type)
505
+
506
+ raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
507
+ raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless event_receiver_mutable?(receiver, scopes:)
508
+ if kind == :event_emit
509
+ raise_sema_error("#{receiver_type}.emit is only available inside module #{receiver_type.module_name}") unless receiver_type.module_name == @ctx.module_name
510
+ end
511
+
512
+ case kind
513
+ when :event_subscribe, :event_subscribe_once
514
+ if arguments.length == 2 && arguments.none?(&:name)
515
+ state_type = infer_expression(arguments[0].value, scopes:)
516
+ unless pointer_type?(state_type)
517
+ suggestion = if arguments[0].value.is_a?(AST::Identifier)
518
+ "use ptr_of(#{arguments[0].value.name})"
519
+ end
520
+ raise_sema_error("first argument to #{receiver_type}.#{method_name} stateful overload must be a non-null pointer, got #{state_type}", arguments[0].value, suggestion:)
521
+ end
522
+
523
+ state_pointed_type = state_type.arguments.first
524
+ expected_listener_type = event_stateful_listener_type(receiver_type, state_pointed_type)
525
+ actual_listener_type = infer_expression(arguments[1].value, scopes:, expected_type: expected_listener_type)
526
+ ensure_argument_assignable!(
527
+ actual_listener_type,
528
+ expected_listener_type,
529
+ external: false,
530
+ message: "listener argument to #{receiver_type}.#{method_name} expects #{expected_listener_type}, got #{actual_listener_type}",
531
+ expression: arguments[1].value,
532
+ )
533
+ elsif arguments.length == 1
534
+ listener_type = event_listener_type(receiver_type)
535
+ actual_type = infer_expression(arguments.first.value, scopes:, expected_type: listener_type)
536
+ ensure_argument_assignable!(
537
+ actual_type,
538
+ listener_type,
539
+ external: false,
540
+ message: "argument listener to #{receiver_type}.#{method_name} expects #{listener_type}, got #{actual_type}",
541
+ expression: arguments.first.value,
542
+ )
543
+ else
544
+ raise_sema_error("#{method_name} expects 1 or 2 arguments, got #{arguments.length}")
545
+ end
546
+
547
+ event_subscription_result_type
548
+ when :event_unsubscribe
549
+ raise_sema_error("unsubscribe expects 1 argument, got #{arguments.length}") unless arguments.length == 1
550
+
551
+ actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("Subscription"))
552
+ ensure_argument_assignable!(
553
+ actual_type,
554
+ @ctx.types.fetch("Subscription"),
555
+ external: false,
556
+ message: "argument subscription to #{receiver_type}.unsubscribe expects Subscription, got #{actual_type}",
557
+ expression: arguments.first.value,
558
+ )
559
+
560
+ @ctx.types.fetch("bool")
561
+ when :event_emit
562
+ if receiver_type.payload_type.nil?
563
+ raise_sema_error("emit expects 0 arguments, got #{arguments.length}") unless arguments.empty?
564
+ else
565
+ raise_sema_error("emit expects 1 argument, got #{arguments.length}") unless arguments.length == 1
566
+
567
+ actual_type = infer_expression(arguments.first.value, scopes:, expected_type: receiver_type.payload_type)
568
+ ensure_argument_assignable!(
569
+ actual_type,
570
+ receiver_type.payload_type,
571
+ external: false,
572
+ message: "argument value to #{receiver_type}.emit expects #{receiver_type.payload_type}, got #{actual_type}",
573
+ expression: arguments.first.value,
574
+ )
575
+ end
576
+
577
+ @ctx.types.fetch("void")
578
+ when :event_wait
579
+ raise_sema_error("wait expects 0 arguments, got #{arguments.length}") unless arguments.empty?
580
+
581
+ Types::Registry.task(event_wait_result_type(receiver_type))
582
+ else
583
+ raise_sema_error("unsupported event method #{kind}")
584
+ end
585
+ end
586
+
587
+ def event_listener_type(event_type)
588
+ params = []
589
+ params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type
590
+
591
+ Types::Registry.function(
592
+ nil,
593
+ params:,
594
+ return_type: @ctx.types.fetch("void"),
595
+ external: false,
596
+ )
597
+ end
598
+
599
+ def event_stateful_listener_type(event_type, state_type)
600
+ params = [Types::Registry.parameter("state", Types::Registry.generic_instance("ptr", [state_type]))]
601
+ params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type
602
+
603
+ Types::Registry.function(
604
+ nil,
605
+ params:,
606
+ return_type: @ctx.types.fetch("void"),
607
+ external: false,
608
+ )
609
+ end
610
+
611
+ def event_method_kind(receiver_type, name)
612
+ return unless event_type?(receiver_type)
613
+
614
+ EVENT_METHOD_KINDS[name]
615
+ end
616
+
617
+ def atomic_method_kind(receiver_type, name)
618
+ return unless atomic_type?(receiver_type)
619
+
620
+ ATOMIC_METHOD_KINDS[name]
621
+ end
622
+
623
+ def check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:)
624
+ elem_type = atomic_element_type(receiver_type)
625
+ raise_sema_error("atomic methods do not support named arguments") if arguments.any?(&:name)
626
+
627
+ case kind
628
+ when :atomic_load
629
+ raise_sema_error("load expects 0 arguments, got #{arguments.length}") unless arguments.empty?
630
+ elem_type
631
+ when :atomic_store
632
+ raise_sema_error("store expects 1 argument, got #{arguments.length}") unless arguments.length == 1
633
+ record_editable_receiver_expression(receiver)
634
+ raise_sema_error("cannot call editable method store on an immutable receiver") unless assignable_receiver?(receiver, scopes)
635
+ arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type)
636
+ ensure_assignable!(arg_type, elem_type, "store expects #{elem_type}, got #{arg_type}")
637
+ @ctx.types.fetch("void")
638
+ when :atomic_add, :atomic_sub, :atomic_exchange
639
+ raise_sema_error("#{kind.to_s.delete_prefix("atomic_")} expects 1 argument, got #{arguments.length}") unless arguments.length == 1
640
+ record_editable_receiver_expression(receiver)
641
+ raise_sema_error("cannot call editable method #{kind.to_s.delete_prefix("atomic_")} on an immutable receiver") unless assignable_receiver?(receiver, scopes)
642
+ arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type)
643
+ ensure_assignable!(arg_type, elem_type, "#{kind.to_s.delete_prefix("atomic_")} expects #{elem_type}, got #{arg_type}")
644
+ elem_type
645
+ when :atomic_compare_exchange
646
+ raise_sema_error("compare_exchange expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
647
+ record_editable_receiver_expression(receiver)
648
+ raise_sema_error("cannot call editable method compare_exchange on an immutable receiver") unless assignable_receiver?(receiver, scopes)
649
+ exp_type = infer_expression(arguments[0].value, scopes:, expected_type: elem_type)
650
+ des_type = infer_expression(arguments[1].value, scopes:, expected_type: elem_type)
651
+ ensure_assignable!(exp_type, elem_type, "compare_exchange expected type #{elem_type}, got #{exp_type}")
652
+ ensure_assignable!(des_type, elem_type, "compare_exchange desired type #{elem_type}, got #{des_type}")
653
+ @ctx.types.fetch("bool")
654
+ end
655
+ end
656
+
657
+ def event_method_name(kind)
658
+ EVENT_METHOD_NAMES.fetch(kind)
659
+ end
660
+
661
+ def event_visible_from_current_module?(event_type)
662
+ event_type.module_name == @ctx.module_name || event_type.visibility == :public
663
+ end
664
+
665
+ def event_receiver_mutable?(receiver_expression, scopes:)
666
+ return true if top_level_event_receiver?(receiver_expression, scopes:)
667
+ return false unless receiver_expression.is_a?(AST::MemberAccess)
668
+
669
+ receiver_type = infer_lvalue_receiver(
670
+ receiver_expression.receiver,
671
+ scopes:,
672
+ allow_ref_identifier: true,
673
+ allow_pointer_identifier: true,
674
+ require_mutable_pointer: true,
675
+ allow_span_param_identifier: true,
676
+ )
677
+ receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer: true)
678
+ !event_member_type(receiver_type, receiver_expression.member).nil?
679
+ rescue SemanticError
680
+ false
681
+ end
682
+
683
+ def top_level_event_receiver?(receiver_expression, scopes:)
684
+ case receiver_expression
685
+ when AST::Identifier
686
+ binding = lookup_value(receiver_expression.name, scopes)
687
+ binding&.kind == :event
688
+ when AST::MemberAccess
689
+ return false unless receiver_expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(receiver_expression.receiver.name)
690
+
691
+ imported_module = @ctx.imports.fetch(receiver_expression.receiver.name)
692
+ binding = imported_module.values[receiver_expression.member]
693
+ binding && binding.kind == :event
694
+ else
695
+ false
696
+ end
697
+ end
698
+
699
+ def event_member_type(receiver_type, name)
700
+ return unless receiver_type.respond_to?(:event)
701
+
702
+ receiver_type.event(name)
703
+ end
704
+
705
+ def fresh_noncopyable_event_initializer?(expression, target_type, scopes:)
706
+ return true unless expression
707
+
708
+ case expression
709
+ when AST::Call
710
+ callable_kind, callable, _receiver = resolve_callable(expression.callee, scopes:)
711
+ callable_kind == :struct && callable == target_type
712
+ when AST::Specialization
713
+ return false unless expression.callee.is_a?(AST::Identifier)
714
+ return false unless %w[zero default].include?(expression.callee.name)
715
+ return false unless expression.arguments.length == 1
716
+
717
+ type_arg = expression.arguments.first.value
718
+ type_arg.is_a?(AST::TypeRef) && resolve_type_ref(type_arg) == target_type
719
+ else
720
+ false
721
+ end
722
+ rescue SemanticError
723
+ false
724
+ end
725
+
726
+ def char_array_removed_text_method?(receiver_type, name)
727
+ return unless char_array_text_type?(receiver_type)
728
+
729
+ name == "as_str" || name == "as_cstr"
730
+ end
731
+
732
+ def str_buffer_method_kind(receiver_type, name)
733
+ return unless str_buffer_type?(receiver_type)
734
+
735
+ STR_BUFFER_METHOD_KINDS[name]
736
+ end
737
+
738
+ def str_buffer_method_name(kind)
739
+ STR_BUFFER_METHOD_NAMES.fetch(kind)
740
+ end
741
+
742
+ def check_function_call(binding, arguments, scopes:)
743
+ arguments = canonicalize_call_arguments(arguments, binding.type.params, binding.name)
744
+ expected_params = binding.type.params
745
+ unless call_arity_matches?(binding.type, arguments.length)
746
+ raise_sema_error(arity_error_message(binding.type, binding.name, arguments.length))
747
+ end
748
+
749
+ expected_params.each_with_index do |parameter, index|
750
+ argument = arguments.fetch(index)
751
+ actual_type = foreign_argument_actual_type(parameter, argument, scopes:, function_name: binding.name, expected_type: parameter.type)
752
+ record_mutating_argument_identifier(argument, parameter)
753
+ if foreign_cstr_boundary_parameter?(parameter)
754
+ unless foreign_cstr_argument_compatible?(actual_type, parameter, expression: foreign_argument_expression(argument))
755
+ raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}")
756
+ end
757
+ else
758
+ unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: binding.external, expression: foreign_argument_expression(argument))
759
+ suggestion = explicit_cast_suggestion(actual_type, parameter.type)
760
+ raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}", suggestion:)
761
+ end
762
+ end
763
+ end
764
+
765
+ arguments.drop(expected_params.length).each do |argument|
766
+ infer_expression(argument.value, scopes:)
767
+ end
768
+ end
769
+
770
+ def record_mutating_argument_identifier(argument, parameter)
771
+ return unless %i[out inout].include?(parameter.passing_mode)
772
+ return unless argument.value.is_a?(AST::Identifier)
773
+
774
+ @mutating_argument_identifier_ids[argument.value.object_id] = true
775
+ end
776
+ def check_callable_value_call(function_type, arguments, scopes:, callee_expression:)
777
+ arguments = canonicalize_call_arguments(arguments, function_type.params, describe_expression(callee_expression))
778
+
779
+ unless call_arity_matches?(function_type, arguments.length)
780
+ raise_sema_error(arity_error_message(function_type, describe_expression(callee_expression), arguments.length))
781
+ end
782
+
783
+ function_type.params.each_with_index do |parameter, index|
784
+ argument = arguments.fetch(index)
785
+ actual_type = infer_expression(argument.value, scopes:, expected_type: parameter.type)
786
+ unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: false, expression: argument.value)
787
+ suggestion = explicit_cast_suggestion(actual_type, parameter.type)
788
+ raise_sema_error("argument #{parameter.name || index} to #{describe_expression(callee_expression)} expects #{parameter.type}, got #{actual_type}", argument, suggestion:)
789
+ end
790
+ end
791
+
792
+ arguments.drop(function_type.params.length).each do |argument|
793
+ infer_expression(argument.value, scopes:)
794
+ end
795
+ end
796
+ def check_fatal_call(arguments, scopes:)
797
+ raise_sema_error("fatal does not support named arguments") if arguments.any?(&:name)
798
+ raise_sema_error("fatal expects 1 argument, got #{arguments.length}") unless arguments.length == 1
799
+
800
+ message_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str"))
801
+ return @ctx.types.fetch("void") if string_like_type?(message_type)
802
+
803
+ raise_sema_error("fatal expects str or cstr, got #{message_type}")
804
+ end
805
+ def check_get_call(arguments, scopes:)
806
+ raise_sema_error("get does not support named arguments") if arguments.any?(&:name)
807
+ raise_sema_error("get expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
808
+
809
+ source_expr = arguments.first.value
810
+ index_expr = arguments[1].value
811
+ source_type = infer_expression(source_expr, scopes:)
812
+ index_type = infer_expression(index_expr, scopes:)
813
+ raise_sema_error("get index must be an integer type, got #{index_type}") unless integer_type?(index_type)
814
+
815
+ if array_type?(source_type)
816
+ raise_sema_error("get requires an addressable array value") unless addressable_storage_expression?(source_expr, scopes:)
817
+
818
+ Types::Registry.nullable(pointer_to(array_element_type(source_type)))
819
+ elsif source_type.is_a?(Types::Span)
820
+ Types::Registry.nullable(pointer_to(source_type.element_type))
821
+ else
822
+ raise_sema_error("get expects an array or span, got #{source_type}")
823
+ end
824
+ end
825
+ def check_ref_of_call(arguments, scopes:)
826
+ raise_sema_error("ref_of does not support named arguments") if arguments.any?(&:name)
827
+ raise_sema_error("ref_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
828
+
829
+ source_type = infer_addr_source_type(arguments.first.value, scopes:)
830
+ Types::Registry.generic_instance("ref", [source_type])
831
+ end
832
+ def check_const_ptr_of_call(arguments, scopes:)
833
+ raise_sema_error("const_ptr_of does not support named arguments") if arguments.any?(&:name)
834
+ raise_sema_error("const_ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
835
+
836
+ source_type = infer_ro_addr_source_type(arguments.first.value, scopes:)
837
+ const_pointer_to(source_type)
838
+ end
839
+ def check_read_call(arguments, scopes:)
840
+ validate_read_call_arguments!(arguments)
841
+
842
+ infer_reference_value_type(arguments.first.value, scopes:)
843
+ end
844
+ def check_ptr_of_call(arguments, scopes:)
845
+ raise_sema_error("ptr_of does not support named arguments") if arguments.any?(&:name)
846
+ raise_sema_error("ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
847
+
848
+ source_expression = arguments.first.value
849
+ source_type = infer_expression(source_expression, scopes:)
850
+ return pointer_to(referenced_type(source_type)) if ref_type?(source_type)
851
+
852
+ pointer_to(infer_addr_source_type(source_expression, scopes:))
853
+ end
854
+ def check_field_of_call(arguments, scopes:)
855
+ raise_sema_error("field_of does not support named arguments") if arguments.any?(&:name)
856
+ raise_sema_error("field_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
857
+
858
+ evaluate_field_of_call(arguments, scopes:)
859
+ builtin_field_handle_type
860
+ end
861
+ def check_callable_of_call(arguments, scopes:)
862
+ raise_sema_error("callable_of does not support named arguments") if arguments.any?(&:name)
863
+ raise_sema_error("callable_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
864
+
865
+ evaluate_callable_of_call(arguments, scopes:)
866
+ builtin_callable_handle_type
867
+ end
868
+ def check_has_attribute_call(arguments, scopes:)
869
+ raise_sema_error("has_attribute does not support named arguments") if arguments.any?(&:name)
870
+ raise_sema_error("has_attribute expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
871
+
872
+ target = resolve_reflection_target_argument(arguments.first.value, scopes:)
873
+ binding = resolve_attribute_name_argument(arguments[1].value)
874
+ validate_attribute_target_compatibility!(target, binding)
875
+
876
+ @ctx.types.fetch("bool")
877
+ end
878
+ def check_attribute_of_call(arguments, scopes:)
879
+ raise_sema_error("attribute_of does not support named arguments") if arguments.any?(&:name)
880
+ raise_sema_error("attribute_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
881
+
882
+ target = resolve_reflection_target_argument(arguments.first.value, scopes:)
883
+ binding = resolve_attribute_name_argument(arguments[1].value)
884
+ validate_attribute_target_compatibility!(target, binding)
885
+
886
+ application = find_attribute_application(target, binding)
887
+ unless application || attribute_presence_guard_active?(scopes, attribute_presence_refinement_key(target, binding))
888
+ raise_sema_error("attribute #{qualified_attribute_name(binding)} is not applied to #{target}")
889
+ end
890
+
891
+ builtin_attribute_handle_type
892
+ end
893
+ def check_attribute_arg_call(expected_type, arguments, scopes:)
894
+ raise_sema_error("attribute_arg does not support named arguments") if arguments.any?(&:name)
895
+ raise_sema_error("attribute_arg expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
896
+
897
+ handle_value = evaluate_compile_time_const_value(arguments.first.value, scopes:)
898
+ parameter_source = if handle_value.is_a?(Types::AttributeHandle)
899
+ [handle_value.attribute_name, handle_value.params]
900
+ else
901
+ handle_type = infer_expression(arguments.first.value, scopes:)
902
+ raise_sema_error("attribute_arg expects an attribute handle") unless handle_type == builtin_attribute_handle_type
903
+
904
+ binding = attribute_binding_for_handle_expression(arguments.first.value)
905
+ raise_sema_error("attribute_arg expects an attribute handle") unless binding
906
+
907
+ [binding.name, binding.params]
908
+ end
909
+
910
+ param_name = reflection_identifier_name(arguments[1].value, context: "attribute_arg")
911
+ attribute_name, params = parameter_source
912
+ parameter = params.find { |candidate| candidate.name == param_name }
913
+ raise_sema_error("attribute #{attribute_name} has no parameter #{param_name}") unless parameter
914
+ raise_sema_error("attribute_arg[#{expected_type}] does not match declared type #{parameter.type} for #{attribute_name}.#{param_name}") unless parameter.type == expected_type
915
+
916
+ expected_type
917
+ end
918
+
919
+ def attribute_binding_for_handle_expression(expression)
920
+ return unless expression.is_a?(AST::Call)
921
+ return unless expression.callee.is_a?(AST::Identifier) && expression.callee.name == "attribute_of"
922
+ return unless expression.arguments.length == 2 && expression.arguments.none?(&:name)
923
+
924
+ resolve_attribute_name_argument(expression.arguments[1].value)
925
+ end
926
+
927
+ def check_adapt_call(interface, arguments, scopes:)
928
+ raise_sema_error("adapt does not support named arguments") if arguments.any?(&:name)
929
+ raise_sema_error("adapt expects 1 argument, got #{arguments.length}") unless arguments.length == 1
930
+
931
+ argument = arguments.first
932
+ concrete_type = infer_expression(argument.value, scopes:)
933
+ concrete_type = referenced_type(concrete_type) if ref_type?(concrete_type)
934
+ unless concrete_type.is_a?(Types::Struct) || concrete_type.is_a?(Types::Opaque) || concrete_type.is_a?(Types::StructInstance)
935
+ raise_sema_error("adapt requires a struct or opaque type, got #{concrete_type}")
936
+ end
937
+
938
+ unless type_implements_interface?(concrete_type, interface)
939
+ raise_sema_error("#{concrete_type} does not implement #{interface.name}")
940
+ end
941
+
942
+ Types::Dyn.new(interface, interface.type_arguments || [])
943
+ end
944
+
945
+ def check_dyn_method_call(method_binding, _receiver, arguments, scopes:)
946
+ raise_sema_error("#{method_binding.name} expects #{method_binding.params.length} arguments, got #{arguments.length}") unless arguments.length == method_binding.params.length
947
+
948
+ method_binding.params.each_with_index do |param, index|
949
+ actual_type = infer_expression(arguments[index].value, scopes:, expected_type: param.type)
950
+ ensure_assignable!(
951
+ actual_type,
952
+ param.type,
953
+ "argument #{index + 1} of #{method_binding.name} expects #{param.type}, got #{actual_type}",
954
+ expression: arguments[index].value,
955
+ )
956
+ end
957
+ end
958
+
959
+ def resolve_adapt_interface(type_arg)
960
+ parts = type_arg.name.parts
961
+ type_args = type_arg.arguments.map { |a| a.value }
962
+ interface_ref = AST::QualifiedName.new(parts:, type_arguments: type_args)
963
+ resolve_interface_ref(interface_ref)
964
+ end
965
+
966
+ def canonicalize_call_arguments(arguments, params, context_name)
967
+ return arguments unless arguments.any?(&:name)
968
+
969
+ named_seen = false
970
+ by_position = Array.new(params.length)
971
+ param_names = params.map(&:name)
972
+
973
+ arguments.each_with_index do |arg, _idx|
974
+ if arg.name
975
+ named_seen = true
976
+ raise_sema_error("duplicate named argument '#{arg.name}' in call to #{context_name}") if by_position.any? { |slot| slot&.name == arg.name }
977
+ param_idx = param_names.index(arg.name)
978
+ raise_sema_error("unknown parameter '#{arg.name}' for #{context_name}") unless param_idx
979
+ by_position[param_idx] = arg
980
+ else
981
+ raise_sema_error("positional argument after named argument in call to #{context_name}") if named_seen
982
+ param_idx = _idx
983
+ raise_sema_error("#{context_name} expects #{params.length} arguments, got #{arguments.length}") if param_idx >= params.length
984
+ by_position[param_idx] = arg
985
+ end
986
+ end
987
+
988
+ by_position.reject(&:nil?)
989
+ end
990
+ end
991
+ end
992
+ end