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,1735 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
7
+
8
+ def infer_lvalue(expression, scopes:)
9
+ case expression
10
+ when AST::Identifier
11
+ binding = lookup_value(expression.name, scopes)
12
+ unless binding
13
+ scoped_names = scopes.flat_map { |s| s.is_a?(Hash) ? s.keys : [] }.map(&:to_s)
14
+ suggestion = suggest_name(expression.name, scoped_names)
15
+ raise_sema_error("unknown name #{expression.name}", expression, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil)
16
+ end
17
+ record_identifier_binding(expression, binding)
18
+ raise_sema_error("cannot assign to immutable #{expression.name}") unless binding.mutable
19
+
20
+ binding.storage_type
21
+ when AST::MemberAccess
22
+ receiver_type = infer_lvalue_receiver(expression.receiver, scopes:, allow_ref_identifier: true, allow_pointer_identifier: true, allow_span_param_identifier: true)
23
+ receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer: true)
24
+ unless aggregate_type?(receiver_type)
25
+ raise_sema_error("cannot assign to member #{expression.member} of #{receiver_type}")
26
+ end
27
+
28
+ field_type = receiver_type.field(expression.member)
29
+ raise_sema_error("unknown field #{receiver_type}.#{expression.member}") unless field_type
30
+
31
+ field_type
32
+ when AST::IndexAccess
33
+ receiver_type = infer_lvalue_receiver(
34
+ expression.receiver,
35
+ scopes:,
36
+ allow_pointer_identifier: true,
37
+ require_mutable_pointer: true,
38
+ allow_span_param_identifier: true,
39
+ )
40
+
41
+ index_type = infer_expression(expression.index, scopes:)
42
+ infer_index_result_type(receiver_type, index_type)
43
+ when AST::Call
44
+ if read_call?(expression)
45
+ validate_read_call_arguments!(expression.arguments)
46
+ return infer_reference_value_type(expression.arguments.first.value, scopes:)
47
+ end
48
+
49
+ raise_sema_error("invalid assignment target")
50
+ when AST::BinaryOp
51
+ raise_sema_error("invalid assignment target")
52
+ else
53
+ raise_sema_error("invalid assignment target")
54
+ end
55
+ end
56
+
57
+ def infer_lvalue_receiver(expression, scopes:, allow_ref_identifier: false, allow_pointer_identifier: false, require_mutable_pointer: false, allow_span_param_identifier: false)
58
+ case expression
59
+ when AST::Identifier
60
+ binding = lookup_value(expression.name, scopes)
61
+ raise_sema_error("unknown name #{expression.name}") unless binding
62
+ record_identifier_binding(expression, binding)
63
+
64
+ return referenced_type(binding.type) if allow_ref_identifier && ref_type?(binding.type)
65
+ if allow_pointer_identifier && pointer_type?(binding.type)
66
+ require_unsafe!("raw pointer dereference requires unsafe") unless own_type?(binding.type)
67
+ raise_sema_error("cannot assign through read-only raw pointer #{binding.type}") if require_mutable_pointer && const_pointer_type?(binding.type)
68
+
69
+ return binding.type
70
+ end
71
+ if allow_span_param_identifier && binding.kind == :param && span_type?(binding.type)
72
+ return binding.type
73
+ end
74
+
75
+ raise_sema_error("cannot assign through immutable #{expression.name}") unless binding.mutable
76
+
77
+ binding.type
78
+ when AST::MemberAccess
79
+ receiver_type = infer_lvalue_receiver(
80
+ expression.receiver,
81
+ scopes:,
82
+ allow_ref_identifier:,
83
+ allow_pointer_identifier:,
84
+ require_mutable_pointer:,
85
+ allow_span_param_identifier:,
86
+ )
87
+ receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer:)
88
+ unless aggregate_type?(receiver_type)
89
+ raise_sema_error("cannot access member #{expression.member} of #{receiver_type}")
90
+ end
91
+
92
+ field_type = receiver_type.field(expression.member)
93
+ raise_sema_error("unknown field #{receiver_type}.#{expression.member}") unless field_type
94
+
95
+ field_type
96
+ when AST::IndexAccess
97
+ receiver_type = infer_lvalue_receiver(
98
+ expression.receiver,
99
+ scopes:,
100
+ allow_ref_identifier:,
101
+ allow_pointer_identifier:,
102
+ require_mutable_pointer:,
103
+ allow_span_param_identifier:,
104
+ )
105
+ index_type = infer_expression(expression.index, scopes:)
106
+ infer_index_result_type(receiver_type, index_type)
107
+ when AST::Call
108
+ if read_call?(expression)
109
+ validate_read_call_arguments!(expression.arguments)
110
+ return infer_reference_value_type(expression.arguments.first.value, scopes:)
111
+ end
112
+
113
+ raise_sema_error("invalid assignment target")
114
+ when AST::BinaryOp
115
+ require_unsafe!("raw pointer arithmetic as lvalue receiver requires unsafe")
116
+ type = infer_expression(expression, scopes:)
117
+ raise_sema_error("binary op lvalue receiver must be a pointer") unless pointer_type?(type)
118
+ raise_sema_error("cannot assign through read-only raw pointer #{type}") if require_mutable_pointer && const_pointer_type?(type)
119
+
120
+ type
121
+ else
122
+ raise_sema_error("invalid assignment target")
123
+ end
124
+ end
125
+
126
+ def external_numeric_assignment_target?(expression, scopes:)
127
+ case expression
128
+ when AST::MemberAccess
129
+ receiver_type = infer_field_receiver_type(expression.receiver, scopes:, require_mutable_pointer: true)
130
+ receiver_type.respond_to?(:external) && receiver_type.external
131
+ else
132
+ false
133
+ end
134
+ end
135
+
136
+ def infer_expression(expression, scopes:, expected_type: nil)
137
+ with_error_node(expression) do
138
+ type = case expression
139
+ when AST::ErrorExpr
140
+ expected_type || @error_type
141
+ when AST::IntegerLiteral
142
+ infer_integer_literal(expression, expected_type)
143
+ when AST::FloatLiteral
144
+ infer_float_literal(expression, expected_type)
145
+ when AST::CharLiteral
146
+ @ctx.types.fetch("ubyte")
147
+ when AST::SizeofExpr
148
+ unless check_layout_type_via_ct(expression.type, context: "size_of", scopes:)
149
+ infer_layout_query_type(expression.type, context: "size_of")
150
+ end
151
+ @ctx.types.fetch("ptr_uint")
152
+ when AST::AlignofExpr
153
+ unless check_layout_type_via_ct(expression.type, context: "align_of", scopes:)
154
+ infer_layout_query_type(expression.type, context: "align_of")
155
+ end
156
+ @ctx.types.fetch("ptr_uint")
157
+ when AST::OffsetofExpr
158
+ infer_offsetof_type(expression.type, expression.field, scopes:)
159
+ resolve_and_store_offset(expression, scopes:)
160
+ @ctx.types.fetch("ptr_uint")
161
+ when AST::StringLiteral
162
+ @ctx.types.fetch(expression.cstring ? "cstr" : "str")
163
+ when AST::FormatString
164
+ check_format_string_literal(expression, scopes:)
165
+ @ctx.types.fetch("str")
166
+ when AST::BooleanLiteral
167
+ @ctx.types.fetch("bool")
168
+ when AST::NullLiteral
169
+ infer_null_literal(expression)
170
+ when AST::Identifier
171
+ infer_identifier(expression, scopes:, expected_type:)
172
+ when AST::MemberAccess
173
+ infer_member_access(expression, scopes:, expected_type:)
174
+ when AST::IndexAccess
175
+ infer_index_access(expression, scopes:)
176
+ when AST::UnaryOp
177
+ infer_unary(expression, scopes:, expected_type:)
178
+ when AST::BinaryOp
179
+ infer_binary(expression, scopes:, expected_type:)
180
+ when AST::IfExpr
181
+ infer_if_expression(expression, scopes:, expected_type:)
182
+ when AST::MatchExpr
183
+ infer_match_expression(expression, scopes:, expected_type:)
184
+ when AST::UnsafeExpr
185
+ infer_unsafe_expression(expression, scopes:, expected_type:)
186
+ when AST::ProcExpr
187
+ infer_proc_expression(expression, scopes:, expected_type:)
188
+ when AST::AwaitExpr
189
+ infer_await_expression(expression, scopes:)
190
+ when AST::DetachExpr
191
+ @uses_parallel_for = true
192
+ validate_detach_expression!(expression.body.first&.expression || expression.body, scopes:)
193
+ check_block(expression.body, scopes:, return_type: @ctx.types.fetch("void"), allow_return: false)
194
+ Types::Handle.new
195
+ when AST::Call
196
+ infer_call(expression, scopes:, expected_type:)
197
+ when AST::PrefixCast
198
+ target_type = resolve_type_ref(expression.target_type)
199
+ check_cast_call(target_type, [AST::Argument.new(name: nil, value: expression.expression)], scopes:)
200
+ target_type
201
+ when AST::Specialization
202
+ if expression.callee.is_a?(AST::Identifier)
203
+ if expression.callee.name == "zero"
204
+ callable_kind, callable, = resolve_callable(expression, scopes:)
205
+ return check_zero_call(callable, [], expected_type:) if callable_kind == :zero
206
+ end
207
+
208
+ if expression.callee.name == "default"
209
+ resolution = resolve_default_specialization(expression)
210
+ return resolution.target_type
211
+ end
212
+ end
213
+
214
+ if (callable_resolution = resolve_specialized_callable_binding(expression, scopes:))
215
+ callable_kind, function_binding, = callable_resolution
216
+ raise_sema_error("specialized method #{describe_expression(expression)} must be called") if callable_kind == :method
217
+
218
+ return function_binding.type
219
+ end
220
+
221
+ raise_sema_error("specialized name #{describe_expression(expression)} must be called")
222
+ when AST::RangeExpr
223
+ raise_sema_error("range expression can only be used as a for-loop iterable or range index target")
224
+ when AST::ExpressionList
225
+ if expected_type && expected_type.is_a?(Types::GenericInstance) && expected_type.name == "array"
226
+ element_type = expected_type.arguments.first
227
+ expression.elements.each do |element|
228
+ value = element.is_a?(AST::Argument) ? element.value : element
229
+ actual = infer_expression(value, scopes:, expected_type: element_type)
230
+ ensure_assignable!(actual, element_type, "array element type mismatch: expected #{element_type}, got #{actual}", expression: value)
231
+ end
232
+ expected_type
233
+ else
234
+ names = []
235
+ element_types = []
236
+ expression.elements.each do |element|
237
+ if element.is_a?(AST::Argument)
238
+ names << element.name
239
+ element_types << infer_expression(element.value, scopes:)
240
+ else
241
+ names << nil
242
+ element_types << infer_expression(element, scopes:)
243
+ end
244
+ end
245
+ has_named = names.any?
246
+ Types::Registry.tuple(element_types, field_names: has_named ? names : nil)
247
+ end
248
+ else
249
+ raise_sema_error("unsupported expression #{expression.class.name}")
250
+ end
251
+
252
+ @resolved_expr_types[@ctx.ast.node_ids[expression.object_id]] = type
253
+ type
254
+ end
255
+ end
256
+
257
+ def infer_unsafe_expression(expression, scopes:, expected_type: nil)
258
+ @unsafe_statement_lines << expression.line
259
+ begin
260
+ with_unsafe do
261
+ infer_expression(expression.expression, scopes:, expected_type:)
262
+ end
263
+ ensure
264
+ @unsafe_statement_lines.pop
265
+ end
266
+ end
267
+
268
+ def infer_integer_literal(expression, expected_type)
269
+ # Integer type suffixes: 42u8, 0xFFub, 100uz, etc.
270
+ suffix_type = integer_suffix_type(expression.lexeme)
271
+ return @ctx.types.fetch(suffix_type) if suffix_type
272
+
273
+ if expected_type.is_a?(Types::Primitive) && expected_type.integer? &&
274
+ value_fits_integer_type?(expression.value, expected_type)
275
+ expected_type
276
+ else
277
+ @ctx.types.fetch("int")
278
+ end
279
+ end
280
+
281
+ INTEGER_SUFFIX_TYPES = {
282
+ "ub" => "ubyte",
283
+ "b" => "byte",
284
+ "us" => "ushort",
285
+ "s" => "short",
286
+ "u" => "uint",
287
+ "i" => "int",
288
+ "ul" => "ulong",
289
+ "l" => "long",
290
+ "z" => "ptr_uint",
291
+ "iz" => "ptr_int",
292
+ }.freeze
293
+
294
+ def integer_suffix_type(lexeme)
295
+ INTEGER_SUFFIX_TYPES.sort_by { |k, _| -k.length }.find { |suffix, _| lexeme.end_with?(suffix) }&.last
296
+ end
297
+
298
+ def infer_float_literal(expression, expected_type)
299
+ if expression.lexeme.end_with?("f")
300
+ @ctx.types.fetch("float")
301
+ elsif expression.lexeme.end_with?("d")
302
+ @ctx.types.fetch("double")
303
+ elsif expected_type.is_a?(Types::Primitive) && expected_type.float?
304
+ expected_type
305
+ else
306
+ @ctx.types.fetch("float")
307
+ end
308
+ end
309
+
310
+ def infer_identifier(expression, scopes:, expected_type: nil)
311
+ binding = lookup_value(expression.name, scopes)
312
+ if binding
313
+ record_identifier_binding(expression, binding)
314
+ return binding.type
315
+ end
316
+
317
+ if @ctx.top_level_functions.key?(expression.name)
318
+ raise_sema_error("generic function #{expression.name} must be called") if @ctx.top_level_functions.fetch(expression.name).type_params.any?
319
+
320
+ function_type = function_type_for_name(expression.name)
321
+ if expected_type
322
+ record_callable_value_identifier_site(expression)
323
+ return function_type
324
+ end
325
+
326
+ raise_sema_error("function #{expression.name} must be called")
327
+ end
328
+
329
+ raise_sema_error("module #{expression.name} cannot be used as a value") if @ctx.imports.key?(expression.name)
330
+ if @ctx.types.key?(expression.name)
331
+ return Types::BUILTIN_TYPE_META_TYPE if expected_type.is_a?(Types::TypeType)
332
+ return @ctx.types.fetch(expression.name)
333
+ end
334
+
335
+ if @ctx.top_level_functions && @ctx.types && scopes
336
+ func_names = @ctx.top_level_functions.keys.map(&:to_s)
337
+ type_names = @ctx.types.keys.map(&:to_s)
338
+ scoped_names = scopes.flat_map { |s| s.is_a?(Hash) ? s.keys : [] }.map(&:to_s)
339
+ suggestion = suggest_name(expression.name, func_names + type_names + scoped_names)
340
+ end
341
+ raise_sema_error("unknown name #{expression.name}", expression, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil)
342
+ end
343
+
344
+ def infer_member_access(expression, scopes:, expected_type: nil)
345
+ type = resolve_type_expression(expression.receiver)
346
+ if type
347
+ return @error_type if error_type?(type)
348
+
349
+ member_type = resolve_type_member(type, expression.member)
350
+ return member_type if member_type
351
+
352
+ if type.is_a?(Types::Variant) && type.arm_names.include?(expression.member)
353
+ raise_sema_error("variant arm #{type}.#{expression.member} has payload; construct it with #{type}.#{expression.member}(field: value, ...)")
354
+ end
355
+
356
+ if (method = lookup_method(type, expression.member))
357
+ raise_sema_error("associated function #{type}.#{expression.member} must be called") unless method.type.receiver_type.nil?
358
+ raise_sema_error("method #{type}.#{expression.member} must be called")
359
+ end
360
+
361
+ raise_sema_error("unknown member #{type}.#{expression.member}")
362
+ end
363
+
364
+ if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
365
+ imported_module = @ctx.imports.fetch(expression.receiver.name)
366
+ value = imported_module.values[expression.member]
367
+ return value.type if value
368
+
369
+ if imported_module.functions.key?(expression.member)
370
+ function = imported_module.functions.fetch(expression.member)
371
+ raise_sema_error("generic function #{expression.receiver.name}.#{expression.member} must be called") if function.type_params.any?
372
+ if expected_type
373
+ record_callable_value_member_access_site(expression)
374
+ return function.type
375
+ end
376
+
377
+ raise_sema_error("function #{expression.receiver.name}.#{expression.member} must be called")
378
+ end
379
+
380
+ if imported_module.types.key?(expression.member)
381
+ raise_sema_error("type #{expression.receiver.name}.#{expression.member} cannot be used as a value")
382
+ end
383
+
384
+ if imported_module.private_value?(expression.member) || imported_module.private_function?(expression.member) || imported_module.private_type?(expression.member)
385
+ raise_sema_error("#{expression.receiver.name}.#{expression.member} is private to module #{imported_module.name}")
386
+ end
387
+
388
+ raise_sema_error("unknown member #{expression.receiver.name}.#{expression.member}")
389
+ end
390
+
391
+ field_receiver_type = infer_field_receiver_type(expression.receiver, scopes:)
392
+ method_receiver_type = infer_method_receiver_type(expression.receiver, scopes:, member_name: expression.member)
393
+ return @error_type if error_type?(field_receiver_type) || error_type?(method_receiver_type)
394
+
395
+ if array_type?(field_receiver_type) && expression.member == "as_span"
396
+ element_type = array_element_type(field_receiver_type)
397
+ return Types::Registry.span(element_type)
398
+ end
399
+ if char_array_removed_text_method?(method_receiver_type, expression.member)
400
+ raise_sema_error("#{method_receiver_type}.#{expression.member} is not available; array[char, N] is raw storage, use str_buffer[N] or an explicit helper")
401
+ end
402
+ if str_buffer_type?(method_receiver_type) && str_buffer_method_kind(method_receiver_type, expression.member)
403
+ raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called")
404
+ end
405
+ if event_type?(method_receiver_type) && event_method_kind(method_receiver_type, expression.member)
406
+ raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called")
407
+ end
408
+
409
+ unless aggregate_type?(field_receiver_type)
410
+ if field_receiver_type == builtin_field_handle_type
411
+ return infer_field_handle_member(expression, scopes:)
412
+ end
413
+ if field_receiver_type == builtin_member_handle_type
414
+ return infer_member_handle_member(expression)
415
+ end
416
+ raise_sema_error("cannot access member #{expression.member} of #{field_receiver_type}")
417
+ end
418
+
419
+ field_type = field_receiver_type.field(expression.member)
420
+ return field_type if field_type
421
+
422
+ if (event_type = event_member_type(field_receiver_type, expression.member))
423
+ unless event_visible_from_current_module?(event_type)
424
+ raise_sema_error("event #{field_receiver_type}.#{expression.member} is private to module #{event_type.module_name}")
425
+ end
426
+
427
+ return event_type
428
+ end
429
+
430
+ if lookup_method(method_receiver_type, expression.member)
431
+ raise_sema_error("method #{method_receiver_type.name}.#{expression.member} must be called")
432
+ end
433
+
434
+ if (imported_module = imported_module_with_private_method(method_receiver_type, expression.member))
435
+ raise_sema_error("#{method_receiver_type}.#{expression.member} is private to module #{imported_module.name}")
436
+ end
437
+
438
+ raise_sema_error("unknown field #{field_receiver_type}.#{expression.member}")
439
+ end
440
+
441
+ def infer_index_access(expression, scopes:)
442
+ receiver_type = infer_expression(expression.receiver, scopes:)
443
+ index_type = infer_expression(expression.index, scopes:)
444
+
445
+ if soa_type?(receiver_type)
446
+ return receiver_type.element_type
447
+ end
448
+
449
+ if array_type?(receiver_type) && !unsafe_context? && !addressable_storage_expression?(expression.receiver, scopes:)
450
+ raise_sema_error("safe array indexing requires an addressable array value; bind it to a local first")
451
+ end
452
+
453
+ infer_index_result_type(receiver_type, index_type)
454
+ end
455
+
456
+ def infer_unary(expression, scopes:, expected_type: nil)
457
+ return infer_propagate_expression(expression.operand, scopes:) if expression.operator == "?"
458
+
459
+ operand_type = infer_expression(expression.operand, scopes:, expected_type:)
460
+
461
+ case expression.operator
462
+ when "not"
463
+ ensure_assignable!(operand_type, @ctx.types.fetch("bool"), "operator not requires bool, got #{operand_type}")
464
+ @ctx.types.fetch("bool")
465
+ when "+", "-"
466
+ raise_sema_error("operator #{expression.operator} requires a numeric operand, got #{operand_type}") unless operand_type.numeric?
467
+
468
+ if expression.operator == "-" && operand_type.integer?
469
+ ct_value = evaluate_compile_time_const_value(expression, scopes:)
470
+ if ct_value.is_a?(Integer) && !value_fits_integer_type?(ct_value, operand_type)
471
+ raise_sema_error("negated value #{ct_value} does not fit in type #{operand_type}", expression)
472
+ end
473
+ end
474
+
475
+ operand_type
476
+ when "~"
477
+ raise_sema_error("operator ~ requires an integer or flags operand, got #{operand_type}") unless bitwise_type?(operand_type)
478
+
479
+ operand_type
480
+ when "out", "in", "inout"
481
+ raise_sema_error("#{expression.operator} is only allowed for foreign call arguments")
482
+ else
483
+ raise_sema_error("unsupported unary operator #{expression.operator}")
484
+ end
485
+ end
486
+
487
+ def infer_propagate_expression(operand, scopes:, allow_void_success: false)
488
+ source_type = infer_expression(operand, scopes:)
489
+ if result_let_else_type?(source_type)
490
+ infer_result_propagation(source_type, allow_void_success:)
491
+ elsif option_let_else_type?(source_type)
492
+ infer_option_propagation(source_type, allow_void_success:)
493
+ else
494
+ raise_sema_error("propagation expects Result[T, E] or Option[T], got #{source_type}")
495
+ end
496
+ end
497
+
498
+ def infer_result_propagation(source_type, allow_void_success:)
499
+ success_type = let_else_success_type(source_type)
500
+ error_type = let_else_error_type(source_type)
501
+ raise_sema_error("propagation requires a non-void Result success type") if success_type == @ctx.types.fetch("void") && !allow_void_success
502
+
503
+ context = current_return_context
504
+ raise_sema_error("propagation is only allowed inside function and proc bodies") unless context
505
+ raise_sema_error("propagation is not allowed inside defer blocks") unless context[:allow_return]
506
+
507
+ return_type = context[:return_type]
508
+ unless result_let_else_type?(return_type)
509
+ raise_sema_error("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}")
510
+ end
511
+
512
+ return_error_type = let_else_error_type(return_type)
513
+ unless return_error_type == error_type
514
+ raise_sema_error("propagation error type #{error_type} must match enclosing Result error type #{return_error_type}")
515
+ end
516
+
517
+ success_type
518
+ end
519
+
520
+ def infer_option_propagation(source_type, allow_void_success:)
521
+ success_type = let_else_success_type(source_type)
522
+ raise_sema_error("propagation requires a non-void Option success type") if success_type == @ctx.types.fetch("void") && !allow_void_success
523
+
524
+ context = current_return_context
525
+ raise_sema_error("propagation is only allowed inside function and proc bodies") unless context
526
+ raise_sema_error("propagation is not allowed inside defer blocks") unless context[:allow_return]
527
+
528
+ return_type = context[:return_type]
529
+ unless option_let_else_type?(return_type)
530
+ raise_sema_error("propagation requires enclosing function/proc to return Option[_], got #{return_type}")
531
+ end
532
+
533
+ success_type
534
+ end
535
+
536
+
537
+ def infer_binary(expression, scopes:, expected_type: nil)
538
+ propagated_type = propagating_expected_type(expression.operator, expected_type)
539
+ left_type = infer_expression(expression.left, scopes:, expected_type: propagated_type)
540
+
541
+ right_scopes = case expression.operator
542
+ when "and"
543
+ scopes_with_refinements(scopes, flow_refinements(expression.left, truthy: true, scopes:))
544
+ when "or"
545
+ scopes_with_refinements(scopes, flow_refinements(expression.left, truthy: false, scopes:))
546
+ else
547
+ scopes
548
+ end
549
+
550
+ right_expected_type = case expression.operator
551
+ when "<<", ">>"
552
+ propagated_type || left_type
553
+ when "+", "-", "*", "/", "%"
554
+ propagated_type || left_type
555
+ when "|", "&", "^"
556
+ left_type
557
+ else
558
+ left_type
559
+ end
560
+
561
+ right_type = infer_expression(expression.right, scopes: right_scopes, expected_type: right_expected_type)
562
+ left_type, right_type = harmonize_binary_float_literal_types(
563
+ expression.left,
564
+ expression.right,
565
+ left_type,
566
+ right_type,
567
+ scopes: right_scopes,
568
+ )
569
+
570
+ left_type, right_type = harmonize_binary_integer_literal_types(
571
+ expression.left,
572
+ expression.right,
573
+ left_type,
574
+ right_type,
575
+ scopes: right_scopes,
576
+ )
577
+
578
+ case expression.operator
579
+ when "and", "or"
580
+ ensure_assignable!(left_type, @ctx.types.fetch("bool"), "operator #{expression.operator} requires bool operands")
581
+ ensure_assignable!(right_type, @ctx.types.fetch("bool"), "operator #{expression.operator} requires bool operands")
582
+ @ctx.types.fetch("bool")
583
+ when "|", "&", "^"
584
+ # For Flags/Enum types, the operands must match and be bitwise-capable.
585
+ unless left_type == right_type && (bitwise_type?(left_type) || left_type.is_a?(Types::Flags))
586
+ raise_sema_error("operator #{expression.operator} requires matching integer or flags types, got #{left_type} and #{right_type}")
587
+ end
588
+
589
+ left_type
590
+ when "+", "-", "*", "/"
591
+ if expression.operator == "+" && (string_like_type?(left_type) || string_like_type?(right_type))
592
+ raise_sema_error("operator + does not support str/cstr concatenation; use continued string literals for static text or string.String/str_buffer for dynamic text")
593
+ end
594
+
595
+ pointer_result = pointer_arithmetic_result(expression.operator, left_type, right_type)
596
+ return pointer_result if pointer_result
597
+
598
+ vector_result = vector_arithmetic_result(expression.operator, left_type, right_type)
599
+ return vector_result if vector_result
600
+
601
+ result_type = common_numeric_type(left_type, right_type)
602
+ unless result_type
603
+ if left_type.respond_to?(:signed_integer?) && right_type.respond_to?(:signed_integer?) &&
604
+ left_type.integer? && right_type.integer? &&
605
+ left_type.signed_integer? != right_type.signed_integer?
606
+ raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type} (use an explicit cast to resolve signed/unsigned mismatch)")
607
+ else
608
+ raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type}")
609
+ end
610
+ end
611
+
612
+ result_type
613
+ when "%"
614
+ result_type = common_integer_type(left_type, right_type)
615
+ unless result_type
616
+ raise_sema_error("operator % requires compatible integer types, got #{left_type} and #{right_type}")
617
+ end
618
+
619
+ result_type
620
+ when "<<", ">>"
621
+ unless left_type.is_a?(Types::Primitive) && left_type.integer? && right_type.is_a?(Types::Primitive) && right_type.integer?
622
+ raise_sema_error("operator #{expression.operator} requires integer operands, got #{left_type} and #{right_type}")
623
+ end
624
+
625
+ left_type
626
+ when "<", "<=", ">", ">="
627
+ unless common_numeric_type(left_type, right_type)
628
+ raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type}")
629
+ end
630
+
631
+ @ctx.types.fetch("bool")
632
+ when "==", "!="
633
+ unless c_natively_equality_comparable_type?(left_type) && c_natively_equality_comparable_type?(right_type)
634
+ bad_type = c_natively_equality_comparable_type?(right_type) ? left_type : right_type
635
+ if struct_instance_type?(bad_type)
636
+ raise_sema_error("operator #{expression.operator} is not supported for struct type #{bad_type}; use equal[#{bad_type}](...) instead")
637
+ else
638
+ raise_sema_error("operator #{expression.operator} is not supported for type #{bad_type}")
639
+ end
640
+ end
641
+ unless common_numeric_type(left_type, right_type) || types_compatible?(left_type, right_type) || types_compatible?(right_type, left_type)
642
+ raise_sema_error("operator #{expression.operator} requires comparable types, got #{left_type} and #{right_type}")
643
+ end
644
+
645
+ @ctx.types.fetch("bool")
646
+ else
647
+ raise_sema_error("unsupported binary operator #{expression.operator}")
648
+ end
649
+ end
650
+
651
+ def infer_if_expression(expression, scopes:, expected_type: nil)
652
+ condition_type = infer_expression(expression.condition, scopes:, expected_type: @ctx.types.fetch("bool"))
653
+ ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if expression condition must be bool, got #{condition_type}")
654
+
655
+ then_scopes = scopes_with_refinements(scopes, flow_refinements(expression.condition, truthy: true, scopes:))
656
+ else_scopes = scopes_with_refinements(scopes, flow_refinements(expression.condition, truthy: false, scopes:))
657
+ then_type = infer_expression(expression.then_expression, scopes: then_scopes, expected_type:)
658
+ else_type = infer_expression(expression.else_expression, scopes: else_scopes, expected_type:)
659
+
660
+ return expected_type if expected_type &&
661
+ types_compatible?(then_type, expected_type, expression: expression.then_expression) &&
662
+ types_compatible?(else_type, expected_type, expression: expression.else_expression)
663
+
664
+ common_type = conditional_common_type(
665
+ then_type,
666
+ else_type,
667
+ then_expression: expression.then_expression,
668
+ else_expression: expression.else_expression,
669
+ )
670
+ return common_type if common_type
671
+
672
+ raise_sema_error("if expression branches require compatible types, got #{then_type} and #{else_type}")
673
+ end
674
+
675
+ def validate_detach_expression!(expression, scopes:)
676
+ case expression
677
+ when AST::Call, AST::Specialization
678
+ expression.arguments.each do |arg|
679
+ validate_detach_argument!(arg.value, scopes:)
680
+ end
681
+ else
682
+ raise_sema_error("detach currently only supports global function calls")
683
+ end
684
+ end
685
+
686
+ def validate_detach_argument!(expression, scopes:)
687
+ return true if expression.is_a?(AST::IntegerLiteral) || expression.is_a?(AST::FloatLiteral) ||
688
+ expression.is_a?(AST::StringLiteral) || expression.is_a?(AST::BooleanLiteral) ||
689
+ expression.is_a?(AST::NullLiteral) || expression.is_a?(AST::CharLiteral) ||
690
+ expression.is_a?(AST::FormatString)
691
+
692
+ if expression.is_a?(AST::Identifier)
693
+ binding = lookup_value(expression.name, scopes)
694
+ return true unless binding && %i[let var param].include?(binding.kind)
695
+
696
+ raise_sema_error("detach argument cannot capture local variable #{expression.name}")
697
+ end
698
+
699
+ true
700
+ end
701
+
702
+ def infer_match_expression(expression, scopes:, expected_type: nil)
703
+ validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false)
704
+ validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false)
705
+ scrutinee_type = infer_expression(expression.expression, scopes:)
706
+
707
+ if error_type?(scrutinee_type)
708
+ infer_recovered_match_expression(expression, scopes:, expected_type:)
709
+ elsif scrutinee_type.is_a?(Types::Enum)
710
+ infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:)
711
+ elsif scrutinee_type.is_a?(Types::Variant)
712
+ infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:)
713
+ elsif integer_type?(scrutinee_type)
714
+ infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:)
715
+ elsif scrutinee_type.is_a?(Types::StringView)
716
+ infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:)
717
+ elsif scrutinee_type.is_a?(Types::Tuple)
718
+ infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:)
719
+ else
720
+ raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}")
721
+ end
722
+ end
723
+
724
+ def infer_recovered_match_expression(expression, scopes:, expected_type:)
725
+ arm_entries = expression.arms.map do |arm|
726
+ arm_scopes = scopes
727
+ if arm.binding_name
728
+ ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column)
729
+ binding = value_binding(
730
+ name: arm.binding_name,
731
+ type: @error_type,
732
+ mutable: false,
733
+ kind: :local,
734
+ id: @preassigned_local_binding_ids.fetch(arm.object_id),
735
+ )
736
+ arm_scopes = scopes + [{ arm.binding_name => binding }]
737
+ record_declaration_binding(arm, binding)
738
+ end
739
+ [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value]
740
+ end
741
+
742
+ match_expression_common_type(arm_entries, expected_type)
743
+ end
744
+
745
+ def infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:)
746
+ arm_entries = []
747
+ each_enum_match_arm(expression, scrutinee_type, scopes:) do |arm, arm_scopes|
748
+ arm_entries << [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value]
749
+ end
750
+ match_expression_common_type(arm_entries, expected_type)
751
+ end
752
+
753
+ def infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:)
754
+ has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
755
+ raise_sema_error("match on integer type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
756
+
757
+ covered_values = {}
758
+ wildcard_seen = false
759
+ arm_entries = []
760
+
761
+ expression.arms.each do |arm|
762
+ if arm.pattern.is_a?(AST::ErrorExpr)
763
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
764
+ next
765
+ end
766
+
767
+ if wildcard_pattern?(arm.pattern)
768
+ raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
769
+
770
+ wildcard_seen = true
771
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
772
+ next
773
+ end
774
+
775
+ unless arm.pattern.is_a?(AST::IntegerLiteral) || arm.pattern.is_a?(AST::CharLiteral)
776
+ raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, or _, got #{arm.pattern.class.name}")
777
+ end
778
+
779
+ value = arm.pattern.value
780
+ raise_sema_error("duplicate match arm value #{value}") if covered_values.key?(value)
781
+
782
+ covered_values[value] = true
783
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
784
+ end
785
+
786
+ match_expression_common_type(arm_entries, expected_type)
787
+ end
788
+
789
+ def infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:)
790
+ has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
791
+ raise_sema_error("match on str requires a wildcard arm (_:)") unless has_wildcard
792
+
793
+ covered_values = {}
794
+ wildcard_seen = false
795
+ arm_entries = []
796
+
797
+ expression.arms.each do |arm|
798
+ if arm.pattern.is_a?(AST::ErrorExpr)
799
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
800
+ next
801
+ end
802
+
803
+ if wildcard_pattern?(arm.pattern)
804
+ raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
805
+ wildcard_seen = true
806
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
807
+ next
808
+ end
809
+
810
+ unless arm.pattern.is_a?(AST::StringLiteral)
811
+ raise_sema_error("match arm for str scrutinee must be a string literal or _, got #{arm.pattern.class.name}")
812
+ end
813
+
814
+ value = arm.pattern.value
815
+ raise_sema_error("duplicate match arm value \"#{value}\"") if covered_values.key?(value)
816
+
817
+ covered_values[value] = true
818
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
819
+ end
820
+
821
+ match_expression_common_type(arm_entries, expected_type)
822
+ end
823
+
824
+ def infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:)
825
+ has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
826
+ raise_sema_error("match on tuple type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
827
+
828
+ arity = scrutinee_type.element_types.length
829
+ covered_values = {}
830
+ wildcard_seen = false
831
+ arm_entries = []
832
+
833
+ expression.arms.each do |arm|
834
+ if arm.pattern.is_a?(AST::ErrorExpr)
835
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
836
+ next
837
+ end
838
+
839
+ if wildcard_pattern?(arm.pattern)
840
+ raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
841
+ wildcard_seen = true
842
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
843
+ next
844
+ end
845
+
846
+ unless arm.pattern.is_a?(AST::ExpressionList)
847
+ raise_sema_error("match arm for tuple scrutinee must be a tuple literal or _, got #{arm.pattern.class.name}")
848
+ end
849
+
850
+ elements = arm.pattern.elements
851
+ raise_sema_error("tuple match arm has #{elements.length} elements but scrutinee has #{arity}") unless elements.length == arity
852
+
853
+ values = elements.map do |elem|
854
+ if elem.is_a?(AST::Identifier) && elem.name == "_"
855
+ :wildcard
856
+ elsif elem.is_a?(AST::IntegerLiteral) || elem.is_a?(AST::CharLiteral) ||
857
+ elem.is_a?(AST::StringLiteral) || elem.is_a?(AST::BooleanLiteral)
858
+ elem.value
859
+ else
860
+ raise_sema_error("tuple match arm element must be a literal or _, got #{elem.class.name}")
861
+ end
862
+ end
863
+
864
+ all_literal = values.all? { |v| v != :wildcard }
865
+ if all_literal
866
+ raise_sema_error("duplicate tuple match arm #{arm.pattern.elements.map { |e| e.respond_to?(:value) ? e.value.inspect : '_' }.join(', ')}") if covered_values.key?(values)
867
+ covered_values[values] = true
868
+ end
869
+
870
+ arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
871
+ end
872
+
873
+ match_expression_common_type(arm_entries, expected_type)
874
+ end
875
+
876
+ def infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:)
877
+ arm_entries = []
878
+ each_variant_match_arm(expression, scrutinee_type, scopes:) do |arm, arm_scopes|
879
+ arm_entries << [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value]
880
+ end
881
+ match_expression_common_type(arm_entries, expected_type)
882
+ end
883
+
884
+ def infer_match_expression_arm_value(arm, scopes:, expected_type:)
885
+ validate_consuming_foreign_expression!(arm.value, scopes:, root_allowed: false)
886
+ validate_hoistable_foreign_expression!(arm.value, scopes:, root_hoistable: false)
887
+ infer_expression(arm.value, scopes:, expected_type:)
888
+ end
889
+
890
+ def match_expression_common_type(arm_entries, expected_type)
891
+ return expected_type || @error_type if arm_entries.empty?
892
+
893
+ if expected_type && arm_entries.all? { |type, expr| types_compatible?(type, expected_type, expression: expr) }
894
+ return expected_type
895
+ end
896
+
897
+ common_type, common_expression = arm_entries.first
898
+ arm_entries.drop(1).each do |type, expr|
899
+ next if type == common_type
900
+
901
+ merged_type = conditional_common_type(
902
+ common_type,
903
+ type,
904
+ then_expression: common_expression,
905
+ else_expression: expr,
906
+ )
907
+ raise_sema_error("match expression arms require compatible types, got #{common_type} and #{type}") unless merged_type
908
+
909
+ common_type = merged_type
910
+ common_expression = expr
911
+ end
912
+
913
+ common_type
914
+ end
915
+
916
+ def infer_proc_expression(expression, scopes:, expected_type: nil)
917
+ proc_type = resolve_type_ref(AST::ProcType.new(params: expression.params, return_type: expression.return_type))
918
+ if expected_type && !proc_type_compatible?(proc_type, expected_type)
919
+ raise_sema_error("proc expression expects #{proc_type}, got #{expected_type}")
920
+ end
921
+
922
+ proc_scopes = scopes.map { |scope| freeze_scope_bindings(scope) }
923
+ proc_scope = {}
924
+ expression.params.each do |param|
925
+ ensure_non_reserved_primitive_name!(param.name, kind_label: "parameter", line: param.respond_to?(:line) ? param.line : nil, column: param.respond_to?(:column) ? param.column : nil)
926
+ param_type = resolve_type_ref(param.type)
927
+ validate_parameter_ref_type!(param_type, function_name: "proc", parameter_name: param.name, external: false)
928
+ validate_parameter_proc_type!(param_type, function_name: "proc", parameter_name: param.name, external: false, foreign: false)
929
+ proc_scope[param.name] = value_binding(name: param.name, type: param_type, mutable: false, kind: :param)
930
+ end
931
+
932
+ check_block(expression.body, scopes: proc_scopes + [proc_scope], return_type: proc_type.return_type, allow_return: true)
933
+ proc_type
934
+ end
935
+
936
+ def infer_await_expression(expression, scopes:)
937
+ raise_sema_error("await is only allowed inside async functions") unless inside_async_function?
938
+
939
+ task_type = infer_expression(expression.expression, scopes:)
940
+ raise_sema_error("await expects Task[T], got #{task_type}") unless task_type.is_a?(Types::Task)
941
+
942
+ task_type.result_type
943
+ end
944
+
945
+ def harmonize_binary_float_literal_types(left_expression, right_expression, left_type, right_type, scopes:)
946
+ if float_literal_expression?(left_expression) && right_type.is_a?(Types::Primitive) && right_type.float?
947
+ left_type = infer_expression(left_expression, scopes:, expected_type: right_type)
948
+ end
949
+
950
+ if float_literal_expression?(right_expression) && left_type.is_a?(Types::Primitive) && left_type.float?
951
+ right_type = infer_expression(right_expression, scopes:, expected_type: left_type)
952
+ end
953
+
954
+ [left_type, right_type]
955
+ end
956
+
957
+ def float_literal_expression?(expression)
958
+ expression.is_a?(AST::FloatLiteral) ||
959
+ (expression.is_a?(AST::UnaryOp) && ["+", "-"].include?(expression.operator) && float_literal_expression?(expression.operand))
960
+ end
961
+
962
+ def harmonize_binary_integer_literal_types(left_expression, right_expression, left_type, right_type, scopes:)
963
+ if integer_literal_expression?(left_expression) && right_type.is_a?(Types::Primitive) && right_type.integer?
964
+ if exact_compile_time_numeric_compatibility?(left_type, left_expression, right_type, scopes:)
965
+ left_type = infer_expression(left_expression, scopes:, expected_type: right_type)
966
+ end
967
+ end
968
+
969
+ if integer_literal_expression?(right_expression) && left_type.is_a?(Types::Primitive) && left_type.integer?
970
+ if exact_compile_time_numeric_compatibility?(right_type, right_expression, left_type, scopes:)
971
+ right_type = infer_expression(right_expression, scopes:, expected_type: left_type)
972
+ end
973
+ end
974
+
975
+ [left_type, right_type]
976
+ end
977
+
978
+ def integer_literal_expression?(expression)
979
+ expression.is_a?(AST::IntegerLiteral)
980
+ end
981
+
982
+ def propagating_expected_type(operator, expected_type)
983
+ case operator
984
+ when "+", "-", "*", "/", "%", "<<", ">>"
985
+ return expected_type if expected_type.is_a?(Types::Primitive) && expected_type.numeric?
986
+ when "|", "&", "^"
987
+ return expected_type if expected_type.is_a?(Types::Primitive) && expected_type.integer?
988
+ return expected_type if expected_type.is_a?(Types::Flags)
989
+ end
990
+
991
+ nil
992
+ end
993
+
994
+ def infer_call(expression, scopes:, expected_type: nil)
995
+ callable_kind, callable, receiver = resolve_callable(expression.callee, scopes:)
996
+ @resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind
997
+
998
+ case callable_kind
999
+ when :function
1000
+ callable = specialize_function_binding(
1001
+ callable,
1002
+ expression.arguments,
1003
+ scopes:,
1004
+ receiver_type: callable_receiver_type_for_specialization(expression.callee, scopes:),
1005
+ )
1006
+
1007
+ check_function_call(callable, expression.arguments, scopes:)
1008
+ callable.owner.send(:check_function, callable) unless callable.type_arguments.empty?
1009
+ callable.type.return_type
1010
+ when :method
1011
+ callable = specialize_function_binding(
1012
+ callable,
1013
+ expression.arguments,
1014
+ scopes:,
1015
+ receiver_type: infer_method_receiver_type(receiver, scopes:, member_name: expression.callee.member),
1016
+ ) if callable.type_params.any?
1017
+ record_editable_receiver_expression(receiver) if callable.type.receiver_editable
1018
+ raise_sema_error("cannot call editable method #{callable.name} on an immutable receiver") if callable.type.receiver_editable && !assignable_receiver?(receiver, scopes)
1019
+
1020
+ check_function_call(callable, expression.arguments, scopes:)
1021
+ callable.owner.send(:check_function, callable) unless callable.type_arguments.empty?
1022
+ callable.type.return_type
1023
+ when :callable_value
1024
+ check_callable_value_call(callable, expression.arguments, scopes:, callee_expression: expression.callee)
1025
+ callable.return_type
1026
+ when :str_buffer_clear, :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format,
1027
+ :str_buffer_len, :str_buffer_capacity, :str_buffer_as_str, :str_buffer_as_cstr
1028
+ check_str_buffer_method_call(callable_kind, receiver, expression.arguments, scopes:)
1029
+ when :array_as_span
1030
+ raise_sema_error("as_span does not support named arguments") if expression.arguments.any?(&:name)
1031
+ raise_sema_error("as_span expects 0 arguments, got #{expression.arguments.length}") unless expression.arguments.empty?
1032
+ Types::Registry.span(array_element_type(callable))
1033
+ when :event_subscribe, :event_subscribe_once, :event_unsubscribe, :event_emit, :event_wait
1034
+ check_event_method_call(callable_kind, receiver, expression.arguments, scopes:)
1035
+ when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange
1036
+ check_atomic_method_call(callable_kind, callable, receiver, expression.arguments, scopes:)
1037
+ when :struct
1038
+ check_aggregate_construction(callable, expression.arguments, scopes:)
1039
+ when :struct_with
1040
+ check_struct_with_call(callable, receiver, expression.arguments, scopes:)
1041
+ when :variant_arm_ctor
1042
+ check_variant_arm_construction(callable, expression.arguments, scopes:)
1043
+ when :array
1044
+ check_array_construction(callable, expression.arguments, scopes:)
1045
+ when :reinterpret
1046
+ check_reinterpret_call(callable, expression.arguments, scopes:)
1047
+ when :hash
1048
+ check_hash_call(callable, expression.arguments, scopes:)
1049
+ when :equal
1050
+ check_equal_call(callable, expression.arguments, scopes:)
1051
+ when :order
1052
+ check_order_call(callable, expression.arguments, scopes:)
1053
+ when :fatal
1054
+ check_fatal_call(expression.arguments, scopes:)
1055
+ when :ref_of
1056
+ check_ref_of_call(expression.arguments, scopes:)
1057
+ when :const_ptr_of
1058
+ check_const_ptr_of_call(expression.arguments, scopes:)
1059
+ when :read
1060
+ check_read_call(expression.arguments, scopes:)
1061
+ when :ptr_of
1062
+ check_ptr_of_call(expression.arguments, scopes:)
1063
+ when :field_of
1064
+ check_field_of_call(expression.arguments, scopes:)
1065
+ when :callable_of
1066
+ check_callable_of_call(expression.arguments, scopes:)
1067
+ when :attribute_of
1068
+ check_attribute_of_call(expression.arguments, scopes:)
1069
+ when :has_attribute
1070
+ check_has_attribute_call(expression.arguments, scopes:)
1071
+ when :get
1072
+ check_get_call(expression.arguments, scopes:)
1073
+ when :attribute_arg
1074
+ check_attribute_arg_call(callable, expression.arguments, scopes:)
1075
+ when :dyn_method
1076
+ check_dyn_method_call(callable, receiver, expression.arguments, scopes:)
1077
+ callable.return_type
1078
+ when :adapt
1079
+ check_adapt_call(callable, expression.arguments, scopes:)
1080
+ else
1081
+ raise_sema_error("#{describe_expression(expression.callee)} is not callable")
1082
+ end
1083
+ end
1084
+
1085
+ def validate_consuming_foreign_expression!(expression, scopes:, root_allowed: false)
1086
+ return unless expression
1087
+ return if expression.is_a?(AST::ErrorExpr)
1088
+
1089
+ if (foreign_call = resolve_foreign_call_expression(expression, scopes:)) && foreign_call_consumes_binding?(foreign_call[:binding])
1090
+ raise_sema_error("consuming foreign calls must be top-level expression statements") unless root_allowed
1091
+ end
1092
+
1093
+ case expression
1094
+ when AST::Call, AST::Specialization
1095
+ validate_consuming_foreign_expression!(expression.callee, scopes:, root_allowed: false)
1096
+ expression.arguments.each do |argument|
1097
+ validate_consuming_foreign_expression!(argument.value, scopes:, root_allowed: false)
1098
+ end
1099
+ when AST::UnaryOp
1100
+ validate_consuming_foreign_expression!(expression.operand, scopes:, root_allowed: false)
1101
+ when AST::BinaryOp
1102
+ validate_consuming_foreign_expression!(expression.left, scopes:, root_allowed: false)
1103
+ validate_consuming_foreign_expression!(expression.right, scopes:, root_allowed: false)
1104
+ when AST::IfExpr
1105
+ validate_consuming_foreign_expression!(expression.condition, scopes:, root_allowed: false)
1106
+ validate_consuming_foreign_expression!(expression.then_expression, scopes:, root_allowed: false)
1107
+ validate_consuming_foreign_expression!(expression.else_expression, scopes:, root_allowed: false)
1108
+ when AST::MatchExpr
1109
+ validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false)
1110
+ expression.arms.each do |arm|
1111
+ validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false)
1112
+ arm_scopes = arm.binding_name ? scopes + [{ arm.binding_name => value_binding(name: arm.binding_name, type: @error_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id)) }] : scopes
1113
+ validate_consuming_foreign_expression!(arm.value, scopes: arm_scopes, root_allowed: false)
1114
+ end
1115
+ when AST::UnsafeExpr
1116
+ validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false)
1117
+ when AST::FormatString
1118
+ expression.parts.each do |part|
1119
+ next unless part.is_a?(AST::FormatExprPart)
1120
+
1121
+ validate_consuming_foreign_expression!(part.expression, scopes:, root_allowed: false)
1122
+ end
1123
+ when AST::MemberAccess
1124
+ validate_consuming_foreign_expression!(expression.receiver, scopes:, root_allowed: false)
1125
+ when AST::IndexAccess
1126
+ validate_consuming_foreign_expression!(expression.receiver, scopes:, root_allowed: false)
1127
+ validate_consuming_foreign_expression!(expression.index, scopes:, root_allowed: false)
1128
+ when AST::RangeExpr
1129
+ validate_consuming_foreign_expression!(expression.start_expr, scopes:, root_allowed: false)
1130
+ validate_consuming_foreign_expression!(expression.end_expr, scopes:, root_allowed: false)
1131
+ when AST::PrefixCast
1132
+ validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false)
1133
+ end
1134
+ end
1135
+
1136
+ def validate_hoistable_foreign_expression!(expression, scopes:, root_hoistable: false)
1137
+ return unless expression
1138
+ return if expression.is_a?(AST::ErrorExpr)
1139
+
1140
+ if (foreign_call = resolve_foreign_call_expression(expression, scopes:)) && (message = inline_foreign_call_requires_hoisting_message(foreign_call, scopes:))
1141
+ raise_sema_error(message) unless root_hoistable
1142
+ end
1143
+
1144
+ case expression
1145
+ when AST::Call, AST::Specialization
1146
+ validate_hoistable_foreign_expression!(expression.callee, scopes:, root_hoistable: false)
1147
+ expression.arguments.each do |argument|
1148
+ validate_hoistable_foreign_expression!(argument.value, scopes:, root_hoistable: false)
1149
+ end
1150
+ when AST::UnaryOp
1151
+ validate_hoistable_foreign_expression!(expression.operand, scopes:, root_hoistable: false)
1152
+ when AST::BinaryOp
1153
+ validate_hoistable_foreign_expression!(expression.left, scopes:, root_hoistable: false)
1154
+ validate_hoistable_foreign_expression!(expression.right, scopes:, root_hoistable: false)
1155
+ when AST::IfExpr
1156
+ validate_hoistable_foreign_expression!(expression.condition, scopes:, root_hoistable: false)
1157
+ validate_hoistable_foreign_expression!(expression.then_expression, scopes:, root_hoistable: false)
1158
+ validate_hoistable_foreign_expression!(expression.else_expression, scopes:, root_hoistable: false)
1159
+ when AST::MatchExpr
1160
+ validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false)
1161
+ expression.arms.each do |arm|
1162
+ validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false)
1163
+ arm_scopes = arm.binding_name ? scopes + [{ arm.binding_name => value_binding(name: arm.binding_name, type: @error_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id)) }] : scopes
1164
+ validate_hoistable_foreign_expression!(arm.value, scopes: arm_scopes, root_hoistable: false)
1165
+ end
1166
+ when AST::UnsafeExpr
1167
+ validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false)
1168
+ when AST::FormatString
1169
+ expression.parts.each do |part|
1170
+ next unless part.is_a?(AST::FormatExprPart)
1171
+
1172
+ validate_hoistable_foreign_expression!(part.expression, scopes:, root_hoistable: false)
1173
+ end
1174
+ when AST::MemberAccess
1175
+ validate_hoistable_foreign_expression!(expression.receiver, scopes:, root_hoistable: false)
1176
+ when AST::IndexAccess
1177
+ validate_hoistable_foreign_expression!(expression.receiver, scopes:, root_hoistable: false)
1178
+ validate_hoistable_foreign_expression!(expression.index, scopes:, root_hoistable: false)
1179
+ when AST::RangeExpr
1180
+ validate_hoistable_foreign_expression!(expression.start_expr, scopes:, root_hoistable: false)
1181
+ validate_hoistable_foreign_expression!(expression.end_expr, scopes:, root_hoistable: false)
1182
+ when AST::PrefixCast
1183
+ validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false)
1184
+ end
1185
+ end
1186
+
1187
+ def inline_foreign_call_requires_hoisting_message(foreign_call, scopes:)
1188
+ binding = foreign_call[:binding]
1189
+ call = foreign_call[:call]
1190
+ reference_counts = foreign_mapping_reference_counts(foreign_mapping_expression(binding.ast))
1191
+
1192
+ binding.ast.params.each_with_index do |param_ast, index|
1193
+ public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil
1194
+ total_references = reference_counts.fetch(param_ast.name, 0)
1195
+ total_references += reference_counts.fetch(public_alias, 0) if public_alias
1196
+ next if total_references <= 1 || simple_foreign_argument_expression?(call.arguments.fetch(index).value)
1197
+
1198
+ return inline_foreign_hoisting_message(binding.name, param_ast.name, reason: "is referenced multiple times in its mapping")
1199
+ end
1200
+
1201
+ binding.ast.params.each_with_index do |param_ast, index|
1202
+ parameter = binding.type.params.fetch(index)
1203
+ argument_expression = call.arguments.fetch(index).value
1204
+ next unless automatic_foreign_cstr_temp_needed?(parameter, argument_expression, scopes:) || automatic_foreign_cstr_list_temp_needed?(parameter)
1205
+
1206
+ return inline_foreign_hoisting_message(binding.name, param_ast.name, reason: "needs temporary foreign text storage")
1207
+ end
1208
+
1209
+ nil
1210
+ end
1211
+
1212
+ def inline_foreign_hoisting_message(binding_name, parameter_name, reason:)
1213
+ "foreign call #{binding_name} cannot be used inline because #{parameter_name} #{reason}; use it as a statement, local initializer, assignment, or return expression"
1214
+ end
1215
+
1216
+ def resolve_foreign_call_expression(expression, scopes:)
1217
+ call = expression
1218
+ return unless call.is_a?(AST::Call)
1219
+
1220
+ callable_kind, callable, _receiver = resolve_callable(call.callee, scopes:)
1221
+ return unless callable_kind == :function
1222
+
1223
+ callable = specialize_function_binding(
1224
+ callable,
1225
+ call.arguments,
1226
+ scopes:,
1227
+ receiver_type: callable_receiver_type_for_specialization(call.callee, scopes:),
1228
+ ) if callable.type_params.any?
1229
+ return unless foreign_function_binding?(callable)
1230
+
1231
+ { call:, binding: callable }
1232
+ rescue SemanticError
1233
+ nil
1234
+ end
1235
+
1236
+ def foreign_call_consumes_binding?(binding)
1237
+ binding.type.params.any? { |parameter| parameter.passing_mode == :consuming }
1238
+ end
1239
+
1240
+ def foreign_mapping_reference_counts(expression, counts = Hash.new(0))
1241
+ case expression
1242
+ when AST::Identifier
1243
+ counts[expression.name] += 1
1244
+ when AST::MemberAccess
1245
+ foreign_mapping_reference_counts(expression.receiver, counts)
1246
+ when AST::IndexAccess
1247
+ foreign_mapping_reference_counts(expression.receiver, counts)
1248
+ foreign_mapping_reference_counts(expression.index, counts)
1249
+ when AST::Specialization, AST::Call
1250
+ foreign_mapping_reference_counts(expression.callee, counts)
1251
+ expression.arguments.each { |argument| foreign_mapping_reference_counts(argument.value, counts) }
1252
+ when AST::UnaryOp
1253
+ foreign_mapping_reference_counts(expression.operand, counts)
1254
+ when AST::BinaryOp
1255
+ foreign_mapping_reference_counts(expression.left, counts)
1256
+ foreign_mapping_reference_counts(expression.right, counts)
1257
+ when AST::IfExpr
1258
+ foreign_mapping_reference_counts(expression.condition, counts)
1259
+ foreign_mapping_reference_counts(expression.then_expression, counts)
1260
+ foreign_mapping_reference_counts(expression.else_expression, counts)
1261
+ when AST::UnsafeExpr
1262
+ foreign_mapping_reference_counts(expression.expression, counts)
1263
+ when AST::PrefixCast
1264
+ foreign_mapping_reference_counts(expression.expression, counts)
1265
+ end
1266
+
1267
+ counts
1268
+ end
1269
+
1270
+ def simple_foreign_argument_expression?(expression)
1271
+ case expression
1272
+ when AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
1273
+ true
1274
+ when AST::MemberAccess
1275
+ simple_foreign_argument_expression?(expression.receiver)
1276
+ else
1277
+ false
1278
+ end
1279
+ end
1280
+
1281
+ def automatic_foreign_cstr_list_temp_needed?(parameter)
1282
+ return false unless parameter.type.is_a?(Types::Span) && parameter.type.element_type == @ctx.types.fetch("str")
1283
+ return false unless parameter.boundary_type.is_a?(Types::Span)
1284
+
1285
+ boundary_element_type = parameter.boundary_type.element_type
1286
+ boundary_element_type == @ctx.types.fetch("cstr") || char_pointer_type?(boundary_element_type)
1287
+ end
1288
+
1289
+ def automatic_foreign_cstr_temp_needed?(parameter, expression, scopes:)
1290
+ return false unless parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
1291
+ return false if expression.is_a?(AST::StringLiteral) && !expression.cstring
1292
+
1293
+ infer_expression(expression, scopes:) != @ctx.types.fetch("cstr")
1294
+ end
1295
+
1296
+ def consuming_foreign_call_refinements(expression, scopes:)
1297
+ foreign_call = resolve_foreign_call_expression(expression, scopes:)
1298
+ return {} unless foreign_call
1299
+
1300
+ binding = foreign_call[:binding]
1301
+ return {} unless foreign_call_consumes_binding?(binding)
1302
+
1303
+ binding.type.params.each_with_index.each_with_object({}) do |(parameter, index), refinements|
1304
+ next unless parameter.passing_mode == :consuming
1305
+
1306
+ argument = foreign_call[:call].arguments.fetch(index)
1307
+ argument_binding = foreign_consuming_argument_binding(parameter, argument, scopes:, function_name: binding.name)
1308
+ refinements[argument.value.name] = @null_type if argument_binding.storage_type.is_a?(Types::Nullable)
1309
+ end
1310
+ end
1311
+
1312
+ def resolve_callable(callee, scopes:)
1313
+ case callee
1314
+ when AST::Identifier
1315
+ if (binding = lookup_value(callee.name, scopes))
1316
+ return [:callable_value, binding.type, nil] if callable_type?(binding.type)
1317
+
1318
+ raise_sema_error("#{callee.name} is not callable")
1319
+ end
1320
+
1321
+ return [:function, @ctx.top_level_functions.fetch(callee.name), nil] if @ctx.top_level_functions.key?(callee.name)
1322
+ return [:fatal, nil, nil] if callee.name == "fatal"
1323
+ return [:ref_of, nil, nil] if callee.name == "ref_of"
1324
+ return [:const_ptr_of, nil, nil] if callee.name == "const_ptr_of"
1325
+ return [:read, nil, nil] if callee.name == "read"
1326
+ return [:ptr_of, nil, nil] if callee.name == "ptr_of"
1327
+ return [:field_of, nil, nil] if callee.name == "field_of"
1328
+ return [:callable_of, nil, nil] if callee.name == "callable_of"
1329
+ return [:attribute_of, nil, nil] if callee.name == "attribute_of"
1330
+ return [:has_attribute, nil, nil] if callee.name == "has_attribute"
1331
+ return [:get, nil, nil] if callee.name == "get"
1332
+
1333
+ type = @ctx.types[callee.name]
1334
+ return [:struct, type, nil] if type.is_a?(Types::Struct) || type.is_a?(Types::StringView) || task_type?(type) || type.is_a?(Types::Vector) || type.is_a?(Types::Matrix) || type.is_a?(Types::Quaternion)
1335
+ if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
1336
+ raise_sema_error("generic type #{callee.name} requires type arguments")
1337
+ end
1338
+
1339
+ raise_sema_error("unknown callable #{callee.name}")
1340
+ when AST::MemberAccess
1341
+ if callee.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(callee.receiver.name)
1342
+ imported_module = @ctx.imports.fetch(callee.receiver.name)
1343
+ return [:function, imported_module.functions.fetch(callee.member), nil] if imported_module.functions.key?(callee.member)
1344
+ imported_type = imported_module.types[callee.member]
1345
+ if imported_type.is_a?(Types::Struct) || imported_type.is_a?(Types::GenericStructDefinition) || imported_type.is_a?(Types::StringView) || task_type?(imported_type) || imported_type.is_a?(Types::Vector) || imported_type.is_a?(Types::Matrix) || imported_type.is_a?(Types::Quaternion)
1346
+ return [:struct, imported_module.types.fetch(callee.member), nil]
1347
+ end
1348
+
1349
+ if imported_type.is_a?(Types::Variant)
1350
+ arm_name = callee.member
1351
+ unless imported_type.arm_names.include?(arm_name)
1352
+ raise_sema_error("unknown arm #{arm_name} for variant #{imported_type}")
1353
+ end
1354
+
1355
+ return [:variant_arm_ctor, [imported_type, arm_name], nil]
1356
+ end
1357
+
1358
+ if imported_module.private_function?(callee.member) || imported_module.private_type?(callee.member) || imported_module.private_value?(callee.member)
1359
+ raise_sema_error("#{callee.receiver.name}.#{callee.member} is private to module #{imported_module.name}")
1360
+ end
1361
+
1362
+ raise_sema_error("unknown callable #{callee.receiver.name}.#{callee.member}") unless @ctx.types.key?(callee.receiver.name)
1363
+ end
1364
+
1365
+ if (type_expr = resolve_type_expression(callee.receiver))
1366
+ if type_expr.is_a?(Types::Variant)
1367
+ arm_name = callee.member
1368
+ unless type_expr.arm_names.include?(arm_name)
1369
+ raise_sema_error("unknown arm #{arm_name} for variant #{type_expr}")
1370
+ end
1371
+
1372
+ return [:variant_arm_ctor, [type_expr, arm_name], nil]
1373
+ end
1374
+
1375
+ if type_expr.respond_to?(:nested_types) && type_expr.nested_types.key?(callee.member)
1376
+ return [:struct, type_expr.nested_types[callee.member], nil]
1377
+ end
1378
+
1379
+ method = lookup_method(type_expr, callee.member)
1380
+ method ||= lookup_static_method(type_expr, callee.member)
1381
+ return [:function, method, nil] if method && method.type.receiver_type.nil?
1382
+
1383
+ raise_sema_error("unknown associated function #{type_expr}.#{callee.member}")
1384
+ end
1385
+
1386
+ method_receiver_type = infer_method_receiver_type(callee.receiver, scopes:, member_name: callee.member)
1387
+
1388
+ if dyn_type?(method_receiver_type)
1389
+ interface = method_receiver_type.interface_binding
1390
+ method_binding = interface.methods[callee.member]
1391
+ raise_sema_error("no method '#{callee.member}' on interface #{interface.name}") unless method_binding
1392
+ raise_sema_error("cannot call static method '#{callee.member}' on dyn value") if method_binding.kind == :static
1393
+ return [:dyn_method, method_binding, callee.receiver]
1394
+ end
1395
+
1396
+ method = lookup_method(method_receiver_type, callee.member)
1397
+ return [:method, method, callee.receiver] if method
1398
+
1399
+ if callee.member == "with" && struct_with_target_type?(method_receiver_type)
1400
+ return [:struct_with, method_receiver_type, callee.receiver]
1401
+ end
1402
+
1403
+ if char_array_removed_text_method?(method_receiver_type, callee.member)
1404
+ raise_sema_error("#{method_receiver_type}.#{callee.member} is not available; array[char, N] is raw storage, use str_buffer[N] or an explicit helper")
1405
+ end
1406
+
1407
+ if (str_buffer_method = str_buffer_method_kind(method_receiver_type, callee.member))
1408
+ return [str_buffer_method, method_receiver_type, callee.receiver]
1409
+ end
1410
+
1411
+ if (event_method = event_method_kind(method_receiver_type, callee.member))
1412
+ return [event_method, method_receiver_type, callee.receiver]
1413
+ end
1414
+
1415
+ if (atomic_method = atomic_method_kind(method_receiver_type, callee.member))
1416
+ return [atomic_method, method_receiver_type, callee.receiver]
1417
+ end
1418
+
1419
+ field_receiver_type = infer_field_receiver_type(callee.receiver, scopes:)
1420
+ if array_type?(field_receiver_type) && callee.member == "as_span"
1421
+ return [:array_as_span, field_receiver_type, callee.receiver]
1422
+ end
1423
+
1424
+ return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member))
1425
+ return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member))
1426
+
1427
+ if (imported_module = imported_module_with_private_method(method_receiver_type, callee.member))
1428
+ raise_sema_error("#{method_receiver_type}.#{callee.member} is private to module #{imported_module.name}")
1429
+ end
1430
+
1431
+ raise_sema_error("unknown method #{method_receiver_type}.#{callee.member}")
1432
+ when AST::Specialization
1433
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "reinterpret"
1434
+ raise_sema_error("reinterpret requires exactly one type argument") unless callee.arguments.length == 1
1435
+
1436
+ type_arg = callee.arguments.first.value
1437
+ raise_sema_error("reinterpret type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
1438
+
1439
+ return [:reinterpret, resolve_type_ref(type_arg), nil]
1440
+ end
1441
+
1442
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "array"
1443
+ raise_sema_error("array requires exactly two type arguments") unless callee.arguments.length == 2
1444
+
1445
+ array_type = resolve_type_ref(AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["array"]), arguments: callee.arguments, nullable: false))
1446
+ raise_sema_error("array specialization must be array[T, N]") unless array_type?(array_type)
1447
+
1448
+ return [:array, array_type, nil]
1449
+ end
1450
+
1451
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "span"
1452
+ raise_sema_error("span requires exactly one type argument") unless callee.arguments.length == 1
1453
+
1454
+ span_type = resolve_type_ref(AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: callee.arguments, nullable: false))
1455
+ raise_sema_error("span specialization must be span[T]") unless span_type?(span_type)
1456
+
1457
+ return [:struct, span_type, nil]
1458
+ end
1459
+
1460
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "zero"
1461
+ raise_sema_error("zero requires exactly one type argument") unless callee.arguments.length == 1
1462
+
1463
+ type_arg = callee.arguments.first.value
1464
+ raise_sema_error("zero type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
1465
+
1466
+ return [:zero, resolve_type_ref(type_arg), nil]
1467
+ end
1468
+
1469
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "default"
1470
+ return [:default, resolve_default_specialization(callee), nil]
1471
+ end
1472
+
1473
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "hash"
1474
+ return [:hash, resolve_hash_specialization(callee), nil]
1475
+ end
1476
+
1477
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "equal"
1478
+ return [:equal, resolve_equal_specialization(callee), nil]
1479
+ end
1480
+
1481
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "order"
1482
+ return [:order, resolve_order_specialization(callee), nil]
1483
+ end
1484
+
1485
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "attribute_arg"
1486
+ raise_sema_error("attribute_arg requires exactly one type argument") unless callee.arguments.length == 1
1487
+
1488
+ type_arg = callee.arguments.first.value
1489
+ raise_sema_error("attribute_arg type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
1490
+
1491
+ return [:attribute_arg, resolve_type_ref(type_arg), nil]
1492
+ end
1493
+
1494
+ if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "adapt"
1495
+ raise_sema_error("adapt requires exactly one type argument") unless callee.arguments.length == 1
1496
+
1497
+ type_arg = callee.arguments.first.value
1498
+ raise_sema_error("adapt type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
1499
+
1500
+ interface = resolve_adapt_interface(type_arg)
1501
+ return [:adapt, interface, nil]
1502
+ end
1503
+
1504
+ if (callable_resolution = resolve_specialized_callable_binding(callee, scopes:))
1505
+ return callable_resolution
1506
+ end
1507
+
1508
+ if (type_ref = type_ref_from_specialization(callee))
1509
+ specialized_type = resolve_type_ref(type_ref)
1510
+ return [:struct, specialized_type, nil] if specialized_type.is_a?(Types::Struct) || task_type?(specialized_type) || specialized_type.is_a?(Types::Vector) || specialized_type.is_a?(Types::Matrix) || specialized_type.is_a?(Types::Quaternion)
1511
+ end
1512
+
1513
+ raise_sema_error("unsupported callable specialization #{describe_expression(callee)}")
1514
+ else
1515
+ callee_type = infer_expression(callee, scopes:)
1516
+ return [:callable_value, callee_type, nil] if callable_type?(callee_type)
1517
+
1518
+ raise_sema_error("unsupported callee #{describe_expression(callee)}")
1519
+ end
1520
+ end
1521
+
1522
+
1523
+ def record_editable_receiver_expression(receiver)
1524
+ return unless receiver
1525
+
1526
+ @editable_receiver_expression_ids[receiver.object_id] = true
1527
+ end
1528
+
1529
+ def record_mutable_lvalue_argument_identifier(expression)
1530
+ return unless expression.is_a?(AST::Identifier)
1531
+
1532
+ @mutable_lvalue_argument_identifier_ids[expression.object_id] = true
1533
+ end
1534
+
1535
+ def check_format_string_literal(format_string, scopes:)
1536
+ format_string.parts.each do |part|
1537
+ next unless part.is_a?(AST::FormatExprPart)
1538
+
1539
+ value_type = infer_expression(part.expression, scopes:)
1540
+
1541
+ if part.format_spec
1542
+ case part.format_spec[:kind]
1543
+ when :precision
1544
+ unless value_type.is_a?(Types::Primitive) && value_type.float?
1545
+ raise_sema_error("format spec ':.N' is only valid for float and double, got #{value_type}")
1546
+ end
1547
+ when :hex
1548
+ unless format_string_integer_base_spec_supported?(value_type)
1549
+ raise_sema_error("format spec ':x' and ':X' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}")
1550
+ end
1551
+ when :oct
1552
+ unless format_string_integer_base_spec_supported?(value_type)
1553
+ raise_sema_error("format spec ':o' and ':O' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}")
1554
+ end
1555
+ when :bin
1556
+ unless format_string_integer_base_spec_supported?(value_type)
1557
+ raise_sema_error("format spec ':b' and ':B' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}")
1558
+ end
1559
+ else
1560
+ raise_sema_error("unsupported format spec #{part.format_spec.inspect}")
1561
+ end
1562
+ else
1563
+ next if format_string_interpolation_supported?(value_type, context: "formatted string interpolation of #{value_type}")
1564
+ raise_sema_error("formatted string interpolation supports str, cstr, bool, numeric primitives, integer-backed enums/flags, and types implementing format_len()/append_format(output: ref[std.string.String]), got #{value_type}")
1565
+ end
1566
+ end
1567
+ end
1568
+
1569
+ def format_string_integer_base_spec_supported?(type)
1570
+ resolved = type.is_a?(Types::EnumBase) ? type.backing_type : type
1571
+ resolved.is_a?(Types::Primitive) && resolved.integer?
1572
+ end
1573
+
1574
+ def format_string_interpolation_supported?(type, context:)
1575
+ return true if type == @ctx.types.fetch("str")
1576
+ return true if type == @ctx.types.fetch("cstr")
1577
+ return true if type == @ctx.types.fetch("bool")
1578
+ return true if type.is_a?(Types::Primitive) && type.integer?
1579
+ return true if type.is_a?(Types::Primitive) && type.float?
1580
+ return true if type.is_a?(Types::EnumBase) && type.backing_type.is_a?(Types::Primitive) && type.backing_type.integer?
1581
+ return true if resolve_explicit_format_binding(type, context:)
1582
+
1583
+ false
1584
+ end
1585
+
1586
+
1587
+
1588
+
1589
+
1590
+
1591
+
1592
+
1593
+ def evaluate_field_of_call(arguments, scopes:)
1594
+ raise_sema_error("field_of does not support named arguments") if arguments.any?(&:name)
1595
+ raise_sema_error("field_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
1596
+
1597
+ struct_handle = resolve_struct_handle_argument(arguments.first.value, scopes:)
1598
+ field_name = reflection_identifier_name(arguments[1].value, context: "field_of")
1599
+ field_handle = CompileTime::Reflection.core_field_handle(struct_handle, field_name)
1600
+ raise_sema_error("unknown field #{struct_handle.struct_type}.#{field_name}") unless field_handle
1601
+
1602
+ field_handle
1603
+ end
1604
+
1605
+
1606
+ def evaluate_callable_of_call(arguments, scopes:)
1607
+ raise_sema_error("callable_of does not support named arguments") if arguments.any?(&:name)
1608
+ raise_sema_error("callable_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
1609
+
1610
+ resolve_callable_handle_argument(arguments.first.value, scopes:)
1611
+ end
1612
+
1613
+
1614
+
1615
+
1616
+ def resolve_struct_handle_argument(expression, scopes:)
1617
+ if (type_expr = resolve_type_expression(expression))
1618
+ handle = struct_handle_for_type(type_expr)
1619
+ return handle if handle
1620
+ end
1621
+
1622
+ raise_sema_error("field_of expects a struct type expression")
1623
+ end
1624
+
1625
+ def resolve_reflection_target_argument(expression, scopes:)
1626
+ if (type_expr = resolve_type_expression(expression))
1627
+ handle = struct_handle_for_type(type_expr)
1628
+ return handle if handle
1629
+ end
1630
+
1631
+ handle = evaluate_compile_time_const_value(expression, scopes:)
1632
+ return handle if handle.is_a?(Types::FieldHandle) || handle.is_a?(Types::CallableHandle)
1633
+
1634
+ raise_sema_error("attribute reflection expects a struct type, field handle, or callable handle")
1635
+ end
1636
+
1637
+ def resolve_callable_handle_argument(expression, scopes:)
1638
+ callable_kind, callable, _receiver = resolve_callable(expression, scopes:)
1639
+ raise_sema_error("callable_of expects a callable declaration name") unless callable_kind == :function
1640
+
1641
+ Types::CallableHandle.new(describe_expression(expression), callable.ast)
1642
+ end
1643
+
1644
+ def resolve_attribute_name_argument(expression)
1645
+ case expression
1646
+ when AST::Identifier
1647
+ resolve_attribute_binding(AST::QualifiedName.new(parts: [expression.name]))
1648
+ when AST::MemberAccess
1649
+ raise_sema_error("attribute name must use a module qualifier") unless expression.receiver.is_a?(AST::Identifier)
1650
+
1651
+ resolve_attribute_binding(AST::QualifiedName.new(parts: [expression.receiver.name, expression.member]))
1652
+ else
1653
+ raise_sema_error("attribute name must be an identifier or module-qualified attribute name")
1654
+ end
1655
+ end
1656
+
1657
+ def reflection_identifier_name(expression, context:)
1658
+ raise_sema_error("#{context} expects an identifier argument") unless expression.is_a?(AST::Identifier)
1659
+
1660
+ expression.name
1661
+ end
1662
+
1663
+ def struct_handle_for_type(type)
1664
+ base_type = type.is_a?(Types::StructInstance) ? type.definition : type
1665
+ return nil unless base_type.is_a?(Types::Struct) || base_type.is_a?(Types::GenericStructDefinition)
1666
+
1667
+ declaration = struct_declaration_for_type(base_type)
1668
+ return nil unless declaration
1669
+
1670
+ Types::StructHandle.new(base_type, declaration)
1671
+ end
1672
+
1673
+ def struct_declaration_for_type(type)
1674
+ return type.ast_declaration if type.respond_to?(:ast_declaration) && type.ast_declaration
1675
+
1676
+ return nil unless type.respond_to?(:module_name)
1677
+ if type.module_name == @ctx.module_name
1678
+ return @ctx.ast.declarations.find do |decl|
1679
+ decl.is_a?(AST::StructDecl) && decl.name == type.name
1680
+ end
1681
+ end
1682
+
1683
+ imported_module = imported_module_binding_for_name(type.module_name)
1684
+ return nil unless imported_module
1685
+
1686
+ declaration = imported_module.type_declarations[type.name]
1687
+ declaration if declaration.is_a?(AST::StructDecl)
1688
+ end
1689
+
1690
+ def imported_module_binding_for_name(module_name)
1691
+ @ctx.imports.each_value.find { |binding| binding.name == module_name }
1692
+ end
1693
+
1694
+ def validate_attribute_target_compatibility!(target, binding)
1695
+ target_kind = attribute_target_kind(target)
1696
+ raise_sema_error("attribute #{qualified_attribute_name(binding)} cannot target #{target_kind}") unless binding.targets.include?(target_kind)
1697
+ end
1698
+
1699
+ def attribute_target_kind(target)
1700
+ case target
1701
+ when Types::StructHandle then :struct
1702
+ when Types::FieldHandle then :field
1703
+ when Types::CallableHandle then :callable
1704
+ else
1705
+ raise_sema_error("unsupported attribute reflection target #{target}")
1706
+ end
1707
+ end
1708
+
1709
+ def infer_field_handle_member(expression, scopes:)
1710
+ case expression.member
1711
+ when "name"
1712
+ @ctx.types.fetch("str")
1713
+ when "type"
1714
+ handle = evaluate_compile_time_const_value(expression.receiver, scopes:)
1715
+ return @error_type unless handle.is_a?(Types::FieldHandle)
1716
+
1717
+ resolve_type_ref(handle.field_declaration.type)
1718
+ else
1719
+ raise_sema_error("unknown member #{expression.member} of field_handle")
1720
+ end
1721
+ end
1722
+
1723
+ def infer_member_handle_member(expression)
1724
+ case expression.member
1725
+ when "name"
1726
+ @ctx.types.fetch("str")
1727
+ when "value"
1728
+ @ctx.types.fetch("int")
1729
+ else
1730
+ raise_sema_error("unknown member #{expression.member} of member_handle")
1731
+ end
1732
+ end
1733
+ end
1734
+ end
1735
+ end