mt-lang 0.3.17 → 0.3.22

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -1
  3. data/lib/milk_tea/core/c_backend/expressions.rb +34 -23
  4. data/lib/milk_tea/core/c_backend/feature_detection.rb +25 -45
  5. data/lib/milk_tea/core/c_backend/type_collectors.rb +18 -30
  6. data/lib/milk_tea/core/c_backend/type_declaration.rb +0 -6
  7. data/lib/milk_tea/core/c_backend.rb +49 -39
  8. data/lib/milk_tea/core/compile_time.rb +98 -72
  9. data/lib/milk_tea/core/control_flow/builder.rb +2 -1
  10. data/lib/milk_tea/core/intrinsics.rb +7 -0
  11. data/lib/milk_tea/core/lexer.rb +46 -35
  12. data/lib/milk_tea/core/lowering/block.rb +9 -0
  13. data/lib/milk_tea/core/lowering/calls.rb +2 -0
  14. data/lib/milk_tea/core/lowering/declarations.rb +1 -1
  15. data/lib/milk_tea/core/lowering/functions.rb +11 -8
  16. data/lib/milk_tea/core/lowering/resolve.rb +11 -4
  17. data/lib/milk_tea/core/lowering/scans.rb +13 -18
  18. data/lib/milk_tea/core/module_binder.rb +19 -15
  19. data/lib/milk_tea/core/module_loader.rb +212 -59
  20. data/lib/milk_tea/core/module_path_resolver.rb +8 -7
  21. data/lib/milk_tea/core/parser/declarations.rb +43 -19
  22. data/lib/milk_tea/core/parser/expressions.rb +5 -8
  23. data/lib/milk_tea/core/parser/statements.rb +5 -5
  24. data/lib/milk_tea/core/parser.rb +26 -0
  25. data/lib/milk_tea/core/semantic_analyzer/calls.rb +24 -31
  26. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +20 -23
  27. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +146 -92
  28. data/lib/milk_tea/core/semantic_analyzer/statements.rb +19 -41
  29. data/lib/milk_tea/core/semantic_analyzer.rb +56 -37
  30. data/lib/milk_tea/core/types.rb +12 -17
  31. data/lib/milk_tea/lsp/diagnostics.rb +13 -0
  32. data/lib/milk_tea/lsp/server/semantic_tokens.rb +6 -0
  33. data/lib/milk_tea/tooling/cli/commands/bindgen.rb +11 -0
  34. data/lib/milk_tea/tooling/cli/commands/build.rb +37 -0
  35. data/lib/milk_tea/tooling/cli/commands/cache.rb +46 -0
  36. data/lib/milk_tea/tooling/cli/commands/check.rb +106 -0
  37. data/lib/milk_tea/tooling/cli/commands/command_base.rb +8 -0
  38. data/lib/milk_tea/tooling/cli/commands/completions.rb +48 -0
  39. data/lib/milk_tea/tooling/cli/commands/dap.rb +58 -0
  40. data/lib/milk_tea/tooling/cli/commands/debug.rb +77 -0
  41. data/lib/milk_tea/tooling/cli/commands/deps.rb +17 -0
  42. data/lib/milk_tea/tooling/cli/commands/docs.rb +58 -0
  43. data/lib/milk_tea/tooling/cli/commands/emit_c.rb +64 -0
  44. data/lib/milk_tea/tooling/cli/commands/format.rb +199 -0
  45. data/lib/milk_tea/tooling/cli/commands/lex.rb +46 -0
  46. data/lib/milk_tea/tooling/cli/commands/lint.rb +248 -0
  47. data/lib/milk_tea/tooling/cli/commands/lower.rb +50 -0
  48. data/lib/milk_tea/tooling/cli/commands/lsp.rb +43 -0
  49. data/lib/milk_tea/tooling/cli/commands/new.rb +26 -0
  50. data/lib/milk_tea/tooling/cli/commands/parse.rb +51 -0
  51. data/lib/milk_tea/tooling/cli/commands/run.rb +99 -0
  52. data/lib/milk_tea/tooling/cli/commands/snapshot.rb +117 -0
  53. data/lib/milk_tea/tooling/cli/commands/test.rb +557 -0
  54. data/lib/milk_tea/tooling/cli/commands/toolchain.rb +16 -0
  55. data/lib/milk_tea/tooling/cli.rb +101 -1893
  56. data/lib/milk_tea/tooling/sexpr_dumper.rb +7 -7
  57. metadata +24 -2
@@ -645,22 +645,13 @@ module MilkTea
645
645
  )
646
646
  end
647
647
 
648
- def event_method_kind(receiver_type, name)
649
- return unless event_type?(receiver_type)
648
+ def specialized_receiver_method_kind(receiver_type, name)
649
+ SPECIALIZED_RECEIVERS.each_value do |spec|
650
+ next unless send(spec[:predicate], receiver_type)
650
651
 
651
- EVENT_METHOD_KINDS[name]
652
- end
653
-
654
- def atomic_method_kind(receiver_type, name)
655
- return unless atomic_type?(receiver_type)
656
-
657
- ATOMIC_METHOD_KINDS[name]
658
- end
659
-
660
- def simd_method_kind(receiver_type, name)
661
- return unless simd_type?(receiver_type)
662
-
663
- SIMD_METHOD_KINDS[name]
652
+ return spec[:kinds][name]
653
+ end
654
+ nil
664
655
  end
665
656
 
666
657
  def check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:)
@@ -750,8 +741,8 @@ module MilkTea
750
741
 
751
742
  case expression
752
743
  when AST::Call
753
- callable_kind, callable, _receiver = resolve_callable(expression.callee, scopes:)
754
- callable_kind == :struct && callable == target_type
744
+ resolution = resolve_callable(expression.callee, scopes:)
745
+ resolution.kind == :struct && resolution.value == target_type
755
746
  when AST::Specialization
756
747
  return false unless expression.callee.is_a?(AST::Identifier)
757
748
  return false unless %w[zero default].include?(expression.callee.name)
@@ -772,12 +763,6 @@ module MilkTea
772
763
  name == "as_str" || name == "as_cstr"
773
764
  end
774
765
 
775
- def str_buffer_method_kind(receiver_type, name)
776
- return unless str_buffer_type?(receiver_type)
777
-
778
- STR_BUFFER_METHOD_KINDS[name]
779
- end
780
-
781
766
  def str_buffer_method_name(kind)
782
767
  STR_BUFFER_METHOD_NAMES.fetch(kind)
783
768
  end
@@ -1032,42 +1017,50 @@ module MilkTea
1032
1017
  end
1033
1018
  end
1034
1019
 
1035
- fill_parameter_defaults!(by_position, params, binding, context_name)
1020
+ fill_parameter_defaults!(by_position, params, binding)
1036
1021
 
1037
1022
  by_position.reject(&:nil?)
1038
1023
  end
1039
1024
 
1040
- def fill_parameter_defaults!(by_position, params, binding, context_name)
1025
+ def fill_parameter_defaults!(by_position, params, binding)
1041
1026
  return unless binding
1042
1027
 
1043
1028
  ast_params = binding.ast.params
1044
- return unless ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value }
1029
+ return unless ast_params.any? { |p| param_has_default?(p) }
1045
1030
 
1046
1031
  params.each_with_index do |param, idx|
1047
1032
  next unless by_position[idx].nil?
1048
1033
  next unless idx < ast_params.length
1049
1034
 
1050
1035
  ast_param = ast_params[idx]
1051
- next unless ast_param.respond_to?(:default_value) && ast_param.default_value
1036
+ next unless param_has_default?(ast_param)
1052
1037
 
1053
- by_position[idx] = AST::Argument.new(name: nil, value: ast_param.default_value)
1038
+ by_position[idx] = default_arg_for(ast_param)
1054
1039
  end
1055
1040
  end
1056
1041
 
1057
1042
  def fill_positional_defaults!(arguments, binding)
1058
- return unless binding.ast.params.any? { |p| p.respond_to?(:default_value) && p.default_value }
1043
+ return unless binding.ast.params.any? { |p| param_has_default?(p) }
1059
1044
 
1060
1045
  ast_params = binding.ast.params
1061
1046
  while arguments.length < ast_params.length
1062
1047
  ast_param = ast_params[arguments.length]
1063
- if ast_param.respond_to?(:default_value) && ast_param.default_value
1064
- arguments << AST::Argument.new(name: nil, value: ast_param.default_value)
1048
+ if param_has_default?(ast_param)
1049
+ arguments << default_arg_for(ast_param)
1065
1050
  else
1066
1051
  break
1067
1052
  end
1068
1053
  end
1069
1054
  end
1070
1055
 
1056
+ def param_has_default?(ast_param)
1057
+ ast_param.respond_to?(:default_value) && ast_param.default_value
1058
+ end
1059
+
1060
+ def default_arg_for(ast_param)
1061
+ AST::Argument.new(name: nil, value: ast_param.default_value)
1062
+ end
1063
+
1071
1064
  def count_required_params(binding)
1072
1065
  return binding.type.params.length unless binding.ast.params.any? { |p| p.respond_to?(:default_value) && p.default_value }
1073
1066
 
@@ -199,8 +199,8 @@ module MilkTea
199
199
  when AST::Specialization
200
200
  if expression.callee.is_a?(AST::Identifier)
201
201
  if expression.callee.name == "zero"
202
- callable_kind, callable, = resolve_callable(expression, scopes:)
203
- return check_zero_call(callable, [], expected_type:) if callable_kind == :zero
202
+ resolution = resolve_callable(expression, scopes:)
203
+ return check_zero_call(resolution.value, [], expected_type:) if resolution.kind == :zero
204
204
  end
205
205
 
206
206
  if expression.callee.name == "default"
@@ -397,10 +397,7 @@ module MilkTea
397
397
  if char_array_removed_text_method?(method_receiver_type, expression.member)
398
398
  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")
399
399
  end
400
- if str_buffer_type?(method_receiver_type) && str_buffer_method_kind(method_receiver_type, expression.member)
401
- raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called")
402
- end
403
- if event_type?(method_receiver_type) && event_method_kind(method_receiver_type, expression.member)
400
+ if specialized_receiver_method_kind(method_receiver_type, expression.member)
404
401
  raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called")
405
402
  end
406
403
 
@@ -923,7 +920,10 @@ module MilkTea
923
920
  end
924
921
 
925
922
  def infer_call(expression, scopes:, expected_type: nil)
926
- callable_kind, callable, receiver = resolve_callable(expression.callee, scopes:)
923
+ resolution = resolve_callable(expression.callee, scopes:)
924
+ callable_kind = resolution.kind
925
+ callable = resolution.value
926
+ receiver = resolution.receiver
927
927
  @resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind
928
928
 
929
929
  case callable_kind
@@ -1152,7 +1152,9 @@ module MilkTea
1152
1152
  call = expression
1153
1153
  return unless call.is_a?(AST::Call)
1154
1154
 
1155
- callable_kind, callable, _receiver = resolve_callable(call.callee, scopes:)
1155
+ resolution = resolve_callable(call.callee, scopes:)
1156
+ callable_kind = resolution.kind
1157
+ callable = resolution.value
1156
1158
  return unless callable_kind == :function
1157
1159
 
1158
1160
  callable = specialize_function_binding(
@@ -1245,6 +1247,11 @@ module MilkTea
1245
1247
  end
1246
1248
 
1247
1249
  def resolve_callable(callee, scopes:)
1250
+ kind, value, receiver = resolve_callable_raw(callee, scopes:)
1251
+ CallableResolution.new(kind:, value:, receiver:)
1252
+ end
1253
+
1254
+ def resolve_callable_raw(callee, scopes:)
1248
1255
  case callee
1249
1256
  when AST::Identifier
1250
1257
  if (binding = lookup_value(callee.name, scopes))
@@ -1343,20 +1350,8 @@ module MilkTea
1343
1350
  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")
1344
1351
  end
1345
1352
 
1346
- if (str_buffer_method = str_buffer_method_kind(method_receiver_type, callee.member))
1347
- return [str_buffer_method, method_receiver_type, callee.receiver]
1348
- end
1349
-
1350
- if (event_method = event_method_kind(method_receiver_type, callee.member))
1351
- return [event_method, method_receiver_type, callee.receiver]
1352
- end
1353
-
1354
- if (atomic_method = atomic_method_kind(method_receiver_type, callee.member))
1355
- return [atomic_method, method_receiver_type, callee.receiver]
1356
- end
1357
-
1358
- if (simd_method = simd_method_kind(method_receiver_type, callee.member))
1359
- return [simd_method, method_receiver_type, callee.receiver]
1353
+ if (kind = specialized_receiver_method_kind(method_receiver_type, callee.member))
1354
+ return [kind, method_receiver_type, callee.receiver]
1360
1355
  end
1361
1356
 
1362
1357
  field_receiver_type = infer_field_receiver_type(callee.receiver, scopes:)
@@ -1574,7 +1569,9 @@ module MilkTea
1574
1569
  end
1575
1570
 
1576
1571
  def resolve_callable_handle_argument(expression, scopes:)
1577
- callable_kind, callable, _receiver = resolve_callable(expression, scopes:)
1572
+ resolution = resolve_callable(expression, scopes:)
1573
+ callable_kind = resolution.kind
1574
+ callable = resolution.value
1578
1575
  raise_sema_error("callable_of expects a callable declaration name") unless callable_kind == :function
1579
1576
 
1580
1577
  Types::CallableHandle.new(describe_expression(expression), callable.ast)
@@ -58,6 +58,20 @@ module MilkTea
58
58
  imported_candidates << [module_binding, imported_method] if imported_method
59
59
  end
60
60
 
61
+ if imported_candidates.empty?
62
+ # Identity-based lookup failed. Try a name-based fallback across
63
+ # all imports, but only take the first match — not all candidates —
64
+ # to avoid false "ambiguous method" errors when two modules both
65
+ # extend the same receiver type with the same method name.
66
+ @ctx.imports.each_value do |module_binding|
67
+ method = find_method_by_receiver_name(module_binding, dispatch_receiver_type, name)
68
+ if method
69
+ imported_candidates << [module_binding, method]
70
+ break
71
+ end
72
+ end
73
+ end
74
+
61
75
  if imported_candidates.empty?
62
76
  owner_module = reachable_module_binding_for_type(receiver_type)
63
77
  return nil unless owner_module
@@ -77,17 +91,25 @@ module MilkTea
77
91
  def module_binding_method(module_binding, receiver_type, dispatch_receiver_type, name)
78
92
  method = module_binding.methods.fetch(receiver_type, {})[name]
79
93
  method ||= module_binding.methods.fetch(dispatch_receiver_type, {})[name] unless dispatch_receiver_type == receiver_type
94
+ method ||= find_method_by_receiver_name(module_binding, dispatch_receiver_type, name)
80
95
  method
81
96
  end
82
97
 
83
- def reachable_module_binding_for_type(receiver_type)
84
- module_name = receiver_type_module_name(receiver_type)
85
- return nil unless module_name
86
- return nil if module_name == @ctx.module_name
87
-
88
- find_reachable_imported_module(module_name)
98
+ def find_method_by_receiver_name(module_binding, receiver_type, name)
99
+ module_binding.methods.each do |key, methods|
100
+ return methods[name] if key.is_a?(receiver_type.class) && key.name == receiver_type.name && methods.key?(name)
101
+ end
102
+ nil
89
103
  end
90
104
 
105
+ def reachable_module_binding_for_type(receiver_type)
106
+ module_name = receiver_type_module_name(receiver_type)
107
+ return nil unless module_name
108
+ return nil if module_name == @ctx.module_name
109
+
110
+ find_reachable_imported_module(module_name)
111
+ end
112
+
91
113
  def receiver_type_module_name(receiver_type)
92
114
  return receiver_type_module_name(receiver_type.base) if receiver_type.is_a?(Types::Nullable)
93
115
  return receiver_type.module_name if receiver_type.respond_to?(:module_name)
@@ -211,123 +233,148 @@ module MilkTea
211
233
 
212
234
  def resolve_non_nullable_type(type_ref, type_params: {}, type_param_constraints: {}, nested_types: nil)
213
235
  if type_ref.is_a?(AST::FunctionType)
214
- params = type_ref.params.map do |param|
215
- Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
216
- end
217
- return Types::Registry.function(nil, params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
236
+ return resolve_function_type_ref(type_ref, type_params:, type_param_constraints:)
218
237
  end
219
238
 
220
239
  if type_ref.is_a?(AST::ProcType)
221
- params = type_ref.params.map do |param|
222
- Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
223
- end
224
- return Types::Registry.proc(params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
240
+ return resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:)
225
241
  end
226
242
 
227
243
  if type_ref.is_a?(AST::DynType)
228
- interface = resolve_interface_ref(type_ref.interface)
229
- raise_sema_error("generic interface #{interface.name} requires type arguments") if interface.is_a?(GenericInterfaceBinding)
230
- type_arguments = interface.type_arguments || []
231
- type = Types::Dyn.new(interface, type_arguments)
232
- type = Types::Registry.nullable(type) if type_ref.nullable
233
- return type
244
+ return resolve_dyn_type_ref(type_ref)
234
245
  end
235
246
 
236
247
  if type_ref.is_a?(AST::TupleType)
237
- names = []
238
- element_types = []
239
- type_ref.element_types.each do |et|
240
- if et.is_a?(AST::Argument)
241
- names << et.name
242
- element_types << resolve_type_ref(et.value, type_params:, type_param_constraints:)
243
- else
244
- names << nil
245
- element_types << resolve_type_ref(et, type_params:, type_param_constraints:)
246
- end
247
- end
248
- has_named = names.any?
249
- return Types::Registry.tuple(element_types, field_names: has_named ? names : nil)
248
+ return resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:)
250
249
  end
251
250
 
252
251
  parts = type_ref.name.parts
253
252
 
254
253
  if type_ref.arguments.any?
255
- name = parts.join(".")
256
- arguments = type_ref.arguments.map { |argument| resolve_type_argument(argument.value, type_params:, type_param_constraints:) }
257
-
258
- if name != "ref" && arguments.any? { |argument| contains_ref_type?(argument) && !stored_ref_supported_type?(argument) }
259
- raise_sema_error("ref types cannot be nested inside #{name}", type_ref)
260
- end
254
+ return resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:)
255
+ end
261
256
 
262
- if name == "Task"
263
- validate_generic_type!(name, arguments)
264
- return Types::Registry.task(arguments[0])
265
- end
257
+ if parts.length == 1 && type_ref.lifetime
258
+ raise_sema_error("lifetime annotations are only valid on ref types, got #{type_ref.name}", type_ref)
259
+ end
266
260
 
267
- if (generic_type = resolve_named_generic_type(parts))
268
- begin
269
- validate_generic_type_param_constraints!(generic_type, arguments, context: "type #{generic_type}", available_type_param_constraints: type_param_constraints)
270
- return generic_type.instantiate(arguments)
271
- rescue ArgumentError => error
272
- raise_sema_error(error.message)
273
- end
274
- end
261
+ if parts.length == 1
262
+ return resolve_single_part_type_ref(type_ref, parts.first, nested_types:, type_params:)
263
+ end
275
264
 
276
- # Handle types with lifetime params only (no type params)
277
- if arguments.all? { |a| a.is_a?(Types::LifetimeRef) }
278
- type = @ctx.types[name]
279
- if type.is_a?(Types::Struct) && type.lifetime_params&.any?
280
- lifetime_args = arguments.select { |a| a.is_a?(Types::LifetimeRef) }.map(&:name)
281
- if lifetime_args.to_set == type.lifetime_params.to_set
282
- return type
283
- end
284
- end
285
- end
265
+ resolve_multi_part_type_ref(type_ref, parts)
266
+ end
286
267
 
287
- validate_generic_type!(name, arguments)
288
- return Types::Registry.span(arguments.first) if name == "span"
268
+ def resolve_function_type_ref(type_ref, type_params:, type_param_constraints:)
269
+ params = type_ref.params.map do |param|
270
+ Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
271
+ end
272
+ Types::Registry.function(nil, params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
273
+ end
289
274
 
290
- return Types::Registry.soa(arguments[0], count: arguments[1].value) if name == "SoA"
275
+ def resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:)
276
+ params = type_ref.params.map do |param|
277
+ Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
278
+ end
279
+ Types::Registry.proc(params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
280
+ end
291
281
 
292
- return Types::Registry.simd(arguments[0], lane_count: arguments[1].value) if name == "simd"
282
+ def resolve_dyn_type_ref(type_ref)
283
+ interface = resolve_interface_ref(type_ref.interface)
284
+ raise_sema_error("generic interface #{interface.name} requires type arguments") if interface.is_a?(GenericInterfaceBinding)
285
+ type_arguments = interface.type_arguments || []
286
+ type = Types::Dyn.new(interface, type_arguments)
287
+ type = Types::Registry.nullable(type) if type_ref.nullable
288
+ type
289
+ end
293
290
 
294
- arguments = [type_ref.lifetime] + arguments if name == "ref" && type_ref.lifetime
295
- return Types::Registry.generic_instance(name, arguments)
291
+ def resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:)
292
+ names = []
293
+ element_types = []
294
+ type_ref.element_types.each do |et|
295
+ if et.is_a?(AST::Argument)
296
+ names << et.name
297
+ element_types << resolve_type_ref(et.value, type_params:, type_param_constraints:)
298
+ else
299
+ names << nil
300
+ element_types << resolve_type_ref(et, type_params:, type_param_constraints:)
301
+ end
296
302
  end
303
+ has_named = names.any?
304
+ Types::Registry.tuple(element_types, field_names: has_named ? names : nil)
305
+ end
297
306
 
298
- if parts.length == 1 && type_ref.lifetime
299
- raise_sema_error("lifetime annotations are only valid on ref types, got #{type_ref.name}", type_ref)
300
- end
307
+ def resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:)
308
+ name = parts.join(".")
309
+ arguments = type_ref.arguments.map { |argument| resolve_type_argument(argument.value, type_params:, type_param_constraints:) }
301
310
 
302
- if parts.length == 1
303
- return type_params.fetch(parts.first) if type_params.key?(parts.first)
311
+ if name != "ref" && arguments.any? { |argument| contains_ref_type?(argument) && !stored_ref_supported_type?(argument) }
312
+ raise_sema_error("ref types cannot be nested inside #{name}", type_ref)
313
+ end
304
314
 
305
- if nested_types && (type = nested_types[parts.first])
306
- return type
307
- end
315
+ if name == "Task"
316
+ validate_generic_type!(name, arguments)
317
+ return Types::Registry.task(arguments[0])
318
+ end
308
319
 
309
- if parts.first.start_with?("@")
310
- raise_sema_error("unknown lifetime #{parts.first}", type_ref)
320
+ if (generic_type = resolve_named_generic_type(parts))
321
+ begin
322
+ validate_generic_type_param_constraints!(generic_type, arguments, context: "type #{generic_type}", available_type_param_constraints: type_param_constraints)
323
+ return generic_type.instantiate(arguments)
324
+ rescue ArgumentError => error
325
+ raise_sema_error(error.message)
311
326
  end
327
+ end
312
328
 
313
- type = @ctx.types[parts.first]
314
- unless type
315
- type_names = @ctx.types.keys
316
- suggestion = suggest_name(parts.first, type_names)
317
- unless suggestion
318
- suggestion = import_suggestion_for_type(parts.first)
329
+ if arguments.all? { |a| a.is_a?(Types::LifetimeRef) }
330
+ type = @ctx.types[name]
331
+ if type.is_a?(Types::Struct) && type.lifetime_params&.any?
332
+ lifetime_args = arguments.select { |a| a.is_a?(Types::LifetimeRef) }.map(&:name)
333
+ if lifetime_args.to_set == type.lifetime_params.to_set
334
+ return type
319
335
  end
320
- raise_sema_error("unknown type #{parts.first}", type_ref, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil)
321
336
  end
322
- raise_sema_error("generic type #{parts.first} requires type arguments", type_ref) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
337
+ end
338
+
339
+ validate_generic_type!(name, arguments)
340
+ return Types::Registry.span(arguments.first) if name == "span"
341
+
342
+ return Types::Registry.soa(arguments[0], count: arguments[1].value) if name == "SoA"
343
+
344
+ return Types::Registry.simd(arguments[0], lane_count: arguments[1].value) if name == "simd"
323
345
 
346
+ arguments = [type_ref.lifetime] + arguments if name == "ref" && type_ref.lifetime
347
+ Types::Registry.generic_instance(name, arguments)
348
+ end
349
+
350
+ def resolve_single_part_type_ref(type_ref, name, nested_types:, type_params:)
351
+ return type_params.fetch(name) if type_params.key?(name)
352
+
353
+ if nested_types && (type = nested_types[name])
324
354
  return type
325
355
  end
326
356
 
327
- if parts.length >= 2
328
- type = resolve_nested_type_ref(parts)
329
- return type if type
357
+ if name.start_with?("@")
358
+ raise_sema_error("unknown lifetime #{name}", type_ref)
359
+ end
360
+
361
+ type = @ctx.types[name]
362
+ unless type
363
+ type_names = @ctx.types.keys
364
+ suggestion = suggest_name(name, type_names)
365
+ unless suggestion
366
+ suggestion = import_suggestion_for_type(name)
367
+ end
368
+ raise_sema_error("unknown type #{name}", type_ref, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil)
330
369
  end
370
+ raise_sema_error("generic type #{name} requires type arguments", type_ref) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
371
+
372
+ type
373
+ end
374
+
375
+ def resolve_multi_part_type_ref(type_ref, parts)
376
+ type = resolve_nested_type_ref(parts)
377
+ return type if type
331
378
 
332
379
  if parts.length == 2 && @ctx.imports.key?(parts.first)
333
380
  imported_module = @ctx.imports.fetch(parts.first)
@@ -374,7 +421,7 @@ module MilkTea
374
421
  case argument
375
422
  when AST::TypeRef
376
423
  resolve_type_argument_ref(argument, type_params:, type_param_constraints:)
377
- when AST::FunctionType, AST::ProcType
424
+ when AST::FunctionType, AST::ProcType, AST::TupleType
378
425
  resolve_type_ref(argument, type_params:, type_param_constraints:)
379
426
  when AST::IntegerLiteral, AST::FloatLiteral
380
427
  Types::LiteralTypeArg.new(argument.value)
@@ -386,12 +433,19 @@ module MilkTea
386
433
  def resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:)
387
434
  return resolve_type_ref(type_ref, type_params:, type_param_constraints:) unless literal_type_argument_name_candidate?(type_ref)
388
435
 
389
- resolve_type_ref(type_ref, type_params:, type_param_constraints:)
390
- rescue SemanticError => error
436
+ result = try_resolve_type_ref(type_ref, type_params:, type_param_constraints:)
437
+ return result if result
438
+
391
439
  literal_type_argument = resolve_named_literal_type_argument(type_ref)
392
440
  return literal_type_argument if literal_type_argument
393
441
 
394
- raise error
442
+ resolve_type_ref(type_ref, type_params:, type_param_constraints:)
443
+ end
444
+
445
+ def try_resolve_type_ref(type_ref, type_params:, type_param_constraints:)
446
+ resolve_type_ref(type_ref, type_params:, type_param_constraints:)
447
+ rescue SemanticError
448
+ nil
395
449
  end
396
450
 
397
451
  def literal_type_argument_name_candidate?(type_ref)
@@ -555,30 +555,32 @@ module MilkTea
555
555
  end
556
556
  end
557
557
 
558
+ MATCH_STMT_DISPATCH = [
559
+ [Types::Enum, :each_enum_match_arm, true],
560
+ [Types::Variant, :each_variant_match_arm, true],
561
+ [:integer, :each_integer_match_arm, false],
562
+ [Types::StringView, :each_string_match_arm, false],
563
+ [Types::Tuple, :each_tuple_match_arm, false],
564
+ ].freeze
565
+
558
566
  def check_match_stmt(statement, scopes:, return_type:, allow_return:)
559
567
  validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: false)
560
568
  scrutinee_type = infer_expression(statement.expression, scopes:)
569
+
561
570
  if error_type?(scrutinee_type)
562
- check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:)
563
- elsif scrutinee_type.is_a?(Types::Enum)
564
- check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
565
- elsif scrutinee_type.is_a?(Types::Variant)
566
- check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
567
- elsif integer_type?(scrutinee_type)
568
- check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
569
- elsif scrutinee_type.is_a?(Types::StringView)
570
- check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
571
- elsif scrutinee_type.is_a?(Types::Tuple)
572
- check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
573
- else
574
- raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}")
571
+ return check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:)
575
572
  end
576
- end
577
573
 
578
- def check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
579
- each_enum_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes|
580
- check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:)
574
+ MATCH_STMT_DISPATCH.each do |type_class, arm_method, per_arm_scopes|
575
+ next unless type_class == :integer ? integer_type?(scrutinee_type) : scrutinee_type.is_a?(type_class)
576
+
577
+ send(arm_method, statement, scrutinee_type, scopes:) do |arm, arm_scopes|
578
+ check_block(arm.body, scopes: per_arm_scopes ? arm_scopes : scopes, return_type:, allow_return:)
579
+ end
580
+ return
581
581
  end
582
+
583
+ raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}")
582
584
  end
583
585
 
584
586
  def each_enum_match_arm(statement, scrutinee_type, scopes:)
@@ -746,24 +748,6 @@ module MilkTea
746
748
  end
747
749
  end
748
750
 
749
- def check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
750
- each_integer_match_arm(statement, scrutinee_type, scopes:) do |arm|
751
- check_block(arm.body, scopes:, return_type:, allow_return:)
752
- end
753
- end
754
-
755
- def check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
756
- each_string_match_arm(statement, scrutinee_type, scopes:) do |arm|
757
- check_block(arm.body, scopes:, return_type:, allow_return:)
758
- end
759
- end
760
-
761
- def check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
762
- each_tuple_match_arm(statement, scrutinee_type, scopes:) do |arm|
763
- check_block(arm.body, scopes:, return_type:, allow_return:)
764
- end
765
- end
766
-
767
751
  def wildcard_pattern?(expression)
768
752
  expression.is_a?(AST::Identifier) && expression.name == "_"
769
753
  end
@@ -810,12 +794,6 @@ module MilkTea
810
794
  failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
811
795
  end
812
796
 
813
- def check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
814
- each_variant_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes|
815
- check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:)
816
- end
817
- end
818
-
819
797
  def each_variant_match_arm(statement, scrutinee_type, scopes:)
820
798
  covered_arms = {}
821
799
  wildcard_seen = false