mt-lang 0.3.39 → 0.3.40

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d9af5865fb54b82fbf9169cdf8f8feef652af6a2b84cbd8e63e35f99b33ac08
4
- data.tar.gz: 2c5d57827cc19e8de3ee5fff7e576860aa4ae30fa5f7b588eba80c9e95227e02
3
+ metadata.gz: 3a57605301758f1ff1307cfb5aa7300bb73876be082b9736f853bf14dce18352
4
+ data.tar.gz: 8d540b3326a74c0a8370d5799b19eadb78f8d579899ffdfed263847bbfa0c367
5
5
  SHA512:
6
- metadata.gz: 9fc438f51cba2f5a65bd3a8ea3441fb87f26018be991146e060a828b036ba9d5eebde4f5546ce344f276ba68e5f1a99b4fe92b026c839c3f77740b15c108a4eb
7
- data.tar.gz: 232a3879248bbd638c7710135047c09dbe4a140758da63c2fc5434df4602750c6459faa3fa843abca99801c6628b963ec747509be17de4c73f408626882dfa22
6
+ metadata.gz: c7ff36f0b7a609a7c4dbd69264c70ee7dca22201d520753e254609a3009844c23572c578cbdb5802afdd04897559b578f1e539b5c9c6c2a97c64f16f5e286062
7
+ data.tar.gz: 1987f20a6c919b3c8341a339c6473292bf31866a07ffe1805707153b077a282284686366dd299eeb02e91de23a83eec1b8aaa837fc901eadefe58acf54df07a9
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.3.39"
6
+ VERSION = "0.3.40"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -111,8 +111,8 @@ module MilkTea
111
111
  InterfaceDecl = Data.define(:name, :type_params, :methods, :visibility, :line, :column) do
112
112
  def initialize(name:, type_params: [], methods:, visibility:, line: nil, column: nil) = super
113
113
  end
114
- ExtendingBlock = Data.define(:type_name, :methods, :line, :column) do
115
- def initialize(type_name:, methods:, line: nil, column: nil) = super
114
+ ExtendingBlock = Data.define(:type_name, :methods, :line, :column, :inline) do
115
+ def initialize(type_name:, methods:, line: nil, column: nil, inline: false) = super
116
116
  end
117
117
  InterfaceMethodDecl = Data.define(:name, :params, :return_type, :kind, :async, :attributes, :line, :column) do
118
118
  def initialize(name:, params:, return_type:, kind:, async:, attributes: [], line: nil, column: nil) = super
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MilkTea
4
4
  module Bindings
5
- AttributeBinding = Data.define(:name, :targets, :params, :module_name, :builtin, :ast)
5
+ AttributeBinding = Data.define(:name, :targets, :params, :module_name, :builtin)
6
6
  BUILTIN_ATTRIBUTE_NAMES = %w[packed align deprecated test expect_fatal].freeze
7
7
 
8
8
  def self.builtin_attribute_binding(name, types)
@@ -14,7 +14,6 @@ module MilkTea
14
14
  params: [].freeze,
15
15
  module_name: nil,
16
16
  builtin: true,
17
- ast: nil,
18
17
  )
19
18
  when "align"
20
19
  AttributeBinding.new(
@@ -23,7 +22,6 @@ module MilkTea
23
22
  params: [Types::Registry.parameter("bytes", types.fetch("ptr_uint"))].freeze,
24
23
  module_name: nil,
25
24
  builtin: true,
26
- ast: nil,
27
25
  )
28
26
  when "deprecated"
29
27
  AttributeBinding.new(
@@ -32,7 +30,6 @@ module MilkTea
32
30
  params: [Types::Registry.parameter("message", types.fetch("str"))].freeze,
33
31
  module_name: nil,
34
32
  builtin: true,
35
- ast: nil,
36
33
  )
37
34
  when "test"
38
35
  AttributeBinding.new(
@@ -41,7 +38,6 @@ module MilkTea
41
38
  params: [].freeze,
42
39
  module_name: nil,
43
40
  builtin: true,
44
- ast: nil,
45
41
  )
46
42
  when "expect_fatal"
47
43
  AttributeBinding.new(
@@ -50,7 +46,6 @@ module MilkTea
50
46
  params: [].freeze,
51
47
  module_name: nil,
52
48
  builtin: true,
53
- ast: nil,
54
49
  )
55
50
  end
56
51
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MilkTea
4
4
  module Bindings
5
- ModuleBinding = Data.define(:name, :types, :type_declarations, :interfaces, :attributes, :attribute_applications, :values, :functions, :methods, :implemented_interfaces, :imports, :private_types, :private_interfaces, :private_attributes, :private_values, :private_functions, :private_methods, :private_implemented_interfaces) do
5
+ ModuleBinding = Data.define(:name, :types, :type_declarations, :interfaces, :attributes, :attribute_applications, :values, :functions, :methods, :implemented_interfaces, :imports, :private_types, :private_interfaces, :private_attributes, :private_values, :private_functions, :private_methods) do
6
6
  def private_type?(name)
7
7
  private_types.key?(name)
8
8
  end
@@ -84,5 +84,27 @@ module MilkTea
84
84
  def string_literal_cstr_compatibility?(expression, expected_type)
85
85
  expression.is_a?(AST::StringLiteral) && !expression.cstring && expected_type == BUILTIN_CSTR
86
86
  end
87
+
88
+ def current_nested_types
89
+ @current_nested_types
90
+ end
91
+
92
+ # Resolves the receiver type's short name (self-reference) and its own
93
+ # nested types as a bare-name scope for method bodies and signatures.
94
+ def method_receiver_nested_scope(receiver_type)
95
+ return nil unless receiver_type.respond_to?(:name)
96
+
97
+ nested = receiver_type.respond_to?(:nested_types) ? receiver_type.nested_types : nil
98
+ scope = { receiver_type.name => receiver_type }
99
+ scope = scope.merge(nested) if nested && !nested.empty?
100
+ scope
101
+ end
102
+
103
+ # Looks up a bare type name, consulting the active method-receiver nested
104
+ # scope before the module type namespace so nested types self-reference
105
+ # by their short name inside their own methods.
106
+ def lookup_named_type(name)
107
+ (@current_nested_types && @current_nested_types[name]) || @ctx.types[name]
108
+ end
87
109
  end
88
- end
110
+ end
@@ -118,8 +118,10 @@ module MilkTea
118
118
  parameter_setup = []
119
119
  previous_type_substitutions = @ctx.current_type_substitutions
120
120
  previous_value_type_params = @ctx.current_value_type_params
121
+ previous_nested_types = @current_nested_types
121
122
  @ctx.current_type_substitutions = binding.type_substitutions
122
123
  @ctx.current_value_type_params = resolve_value_type_params(decl.type_params)
124
+ @current_nested_types = method_receiver_nested_scope(binding.declared_receiver_type)
123
125
 
124
126
  return lower_async_function_decl(binding, receiver_type:) if binding.async
125
127
 
@@ -179,6 +181,7 @@ module MilkTea
179
181
  ensure
180
182
  @ctx.current_type_substitutions = previous_type_substitutions
181
183
  @ctx.current_value_type_params = previous_value_type_params
184
+ @current_nested_types = previous_nested_types
182
185
  end
183
186
 
184
187
  def resolve_value_type_params(type_params)
@@ -444,7 +444,7 @@ module MilkTea
444
444
  return [:compile_time_builtin, callee.name, nil, compile_time_builtin_function_type(callee.name, arguments, env)]
445
445
  end
446
446
 
447
- type = @ctx.types[callee.name]
447
+ type = lookup_named_type(callee.name)
448
448
  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)
449
449
  return [:struct_literal, nil, nil, type]
450
450
  end
@@ -606,7 +606,6 @@ module MilkTea
606
606
  return [:array_as_span, nil, callee.receiver, Types::Registry.span(array_element_type(field_receiver_type))]
607
607
  end
608
608
 
609
- member_type = field_receiver_type.respond_to?(:field) ? field_receiver_type.field(callee.member) : nil
610
609
  member_type = field_receiver_type.respond_to?(:field) ? field_receiver_type.field(callee.member) : nil
611
610
  return [:callable_value, nil, nil, member_type, nil] if callable_type?(member_type)
612
611
 
@@ -1055,7 +1054,7 @@ module MilkTea
1055
1054
  end
1056
1055
  end
1057
1056
 
1058
- def type_contains_array_storage?(type, visited = Set.new)
1057
+ def type_contains_array_storage?(type)
1059
1058
  visitor = Types::ContainsArrayStorageVisitor.new
1060
1059
  visitor.visit(type)
1061
1060
  visitor.found?
@@ -1118,62 +1117,6 @@ module MilkTea
1118
1117
  expected_type || null_type
1119
1118
  end
1120
1119
 
1121
- def common_numeric_type(left_type, right_type)
1122
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
1123
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
1124
- return left_type if left_type == right_type
1125
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
1126
- return unless left_type.numeric? && right_type.numeric?
1127
-
1128
- return common_integer_type(left_type, right_type) if left_type.integer? && right_type.integer?
1129
- return wider_float_type(left_type, right_type) if left_type.float? && right_type.float?
1130
-
1131
- float_type, integer_type = left_type.float? ? [left_type, right_type] : [right_type, left_type]
1132
- return unless integer_type.integer? && integer_type.fixed_width_integer?
1133
-
1134
- float_type
1135
- end
1136
-
1137
- def common_integer_type(left_type, right_type)
1138
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
1139
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
1140
- return left_type if left_type == right_type
1141
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
1142
- return unless left_type.integer? && right_type.integer?
1143
- return unless left_type.fixed_width_integer? && right_type.fixed_width_integer?
1144
-
1145
- # Mirrors the semantic analyzer's rule: same signedness picks the wider
1146
- # type; mixed signed/unsigned promotes to the narrowest signed type that
1147
- # holds both operands' full ranges (a strictly-wider signed type covers
1148
- # an unsigned operand, otherwise widen to the next signed width). Mixing
1149
- # with a 64-bit unsigned type has no safe signed common type.
1150
- if left_type.signed_integer? == right_type.signed_integer?
1151
- return left_type.integer_width >= right_type.integer_width ? left_type : right_type
1152
- end
1153
-
1154
- signed_type, unsigned_type = if left_type.signed_integer?
1155
- [left_type, right_type]
1156
- else
1157
- [right_type, left_type]
1158
- end
1159
-
1160
- return signed_type if signed_type.integer_width > unsigned_type.integer_width
1161
-
1162
- signed_type_above_width(unsigned_type.integer_width)
1163
- end
1164
-
1165
- def signed_type_above_width(width)
1166
- case width
1167
- when 8 then @ctx.types.fetch("short")
1168
- when 16 then @ctx.types.fetch("int")
1169
- when 32 then @ctx.types.fetch("long")
1170
- end
1171
- end
1172
-
1173
- def wider_float_type(left_type, right_type)
1174
- left_type.float_width >= right_type.float_width ? left_type : right_type
1175
- end
1176
-
1177
1120
  def aggregate_arithmetic_result_type(operator, left_type, right_type)
1178
1121
  if left_type.is_a?(Types::Vector) && right_type.is_a?(Types::Vector) && left_type.name == right_type.name
1179
1122
  return left_type
@@ -1217,7 +1160,7 @@ module MilkTea
1217
1160
  when AST::Identifier
1218
1161
  return current_type_params[expression.name] if current_type_params.key?(expression.name)
1219
1162
 
1220
- @ctx.types[expression.name]
1163
+ lookup_named_type(expression.name)
1221
1164
  when AST::MemberAccess
1222
1165
  return nil unless expression.receiver.is_a?(AST::Identifier)
1223
1166
 
@@ -1870,10 +1813,6 @@ module MilkTea
1870
1813
  def top_level_function(name)
1871
1814
  @lowerer.instance_variable_get(:@ctx).functions&.[](name)
1872
1815
  end
1873
-
1874
- def raise_sema_error(message)
1875
- raise CompileTime::Error, message
1876
- end
1877
1816
  end
1878
1817
 
1879
1818
  def evaluate_reflection_target_argument(expression, env:)
@@ -2532,7 +2471,7 @@ module MilkTea
2532
2471
  elsif parts.length == 1 && type_params.key?(parts.first)
2533
2472
  type_params.fetch(parts.first)
2534
2473
  elsif parts.length == 1
2535
- type = @ctx.types[parts.first]
2474
+ type = lookup_named_type(parts.first)
2536
2475
  raise LoweringError.new("unknown type #{parts.first}", line: 0, column: 0, path: @ctx.current_analysis_path) unless type
2537
2476
  raise LoweringError.new("generic type #{parts.first} requires type arguments", line: 0, column: 0, path: @ctx.current_analysis_path) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
2538
2477
 
@@ -155,18 +155,6 @@ module MilkTea
155
155
  receiver_type
156
156
  end
157
157
 
158
- def collection_loop_type(type)
159
- super
160
- end
161
-
162
- def collection_loop_binding_type(iterable_type, element_type)
163
- super
164
- end
165
-
166
- def collection_loop_ref_element_type?(type)
167
- super
168
- end
169
-
170
158
  def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
171
159
  if array_type?(iterable_type)
172
160
  IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
@@ -248,11 +236,6 @@ module MilkTea
248
236
  raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
249
237
  end
250
238
 
251
- def contains_type_var?(type)
252
- super
253
- end
254
-
255
-
256
239
  def stored_ref_supported_type?(type, visited = {})
257
240
  return true unless type
258
241
 
@@ -100,7 +100,6 @@ module MilkTea
100
100
  include Intrinsics
101
101
 
102
102
  attr_accessor :bypass_sema_type_cache
103
- attr_reader :recorded_expr_types
104
103
 
105
104
  include Lowering::Scans
106
105
  include Lowering::Declarations
@@ -122,6 +121,7 @@ module MilkTea
122
121
  @program = program
123
122
  @ctx = LoweringContext.new
124
123
  @artifacts = Artifacts.new
124
+ @error_type = Types::Error.new
125
125
  @synthetic_proc_counter = 0
126
126
  @parallel_for_counter = 0
127
127
  @async_binding_counter = 0
@@ -130,7 +130,6 @@ module MilkTea
130
130
  end
131
131
 
132
132
  def lower
133
- @recorded_expr_types = {} if @bypass_sema_type_cache
134
133
  ir_program, _modules, _synths = lower_and_assemble
135
134
  ir_program
136
135
  end
@@ -42,7 +42,7 @@ module MilkTea
42
42
  end
43
43
 
44
44
  methods, private_methods = exported_methods(analysis, types)
45
- implemented_interfaces, private_implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)
45
+ implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)
46
46
 
47
47
  Bindings::ModuleBinding.new(
48
48
  name: analysis.module_name,
@@ -62,7 +62,6 @@ module MilkTea
62
62
  private_values:,
63
63
  private_functions:,
64
64
  private_methods:,
65
- private_implemented_interfaces:,
66
65
  )
67
66
  end
68
67
 
@@ -102,28 +101,18 @@ module MilkTea
102
101
 
103
102
  def exported_interface_implementations(analysis, exported_types, exported_interfaces)
104
103
  implemented_interfaces = {}
105
- private_implemented_interfaces = {}
106
104
 
107
105
  analysis.implemented_interfaces.each do |receiver_type, interfaces|
108
- public_interfaces = []
109
- hidden_interfaces = []
110
-
111
- interfaces.each do |interface|
112
- visible = exported_method_receiver?(receiver_type, analysis, exported_types) &&
106
+ public_interfaces = interfaces.select do |interface|
107
+ exported_method_receiver?(receiver_type, analysis, exported_types) &&
113
108
  exported_interface_binding?(interface, analysis, exported_interfaces) &&
114
109
  exported_interface_methods?(receiver_type, interface, analysis, exported_types)
115
- if visible
116
- public_interfaces << interface
117
- else
118
- hidden_interfaces << interface
119
- end
120
110
  end
121
111
 
122
112
  implemented_interfaces[receiver_type] = public_interfaces.freeze unless public_interfaces.empty?
123
- private_implemented_interfaces[receiver_type] = hidden_interfaces.freeze unless hidden_interfaces.empty?
124
113
  end
125
114
 
126
- [implemented_interfaces.freeze, private_implemented_interfaces.freeze]
115
+ implemented_interfaces.freeze
127
116
  end
128
117
 
129
118
  def exported_interface_methods?(receiver_type, interface, analysis, exported_types)
@@ -691,7 +691,6 @@ module MilkTea
691
691
  private_values: full_binding.private_values,
692
692
  private_functions: full_binding.private_functions,
693
693
  private_methods: full_binding.private_methods,
694
- private_implemented_interfaces: full_binding.private_implemented_interfaces,
695
694
  )
696
695
  end
697
696
  end
@@ -728,7 +727,6 @@ module MilkTea
728
727
  implemented_interfaces: {}, imports: {},
729
728
  private_types: {}, private_interfaces: {}, private_attributes: {},
730
729
  private_values: {}, private_functions: {}, private_methods: {},
731
- private_implemented_interfaces: {},
732
730
  )
733
731
  end
734
732
 
@@ -369,10 +369,11 @@ module MilkTea
369
369
  AST::AttributeDecl.new(name: name_token.lexeme, targets:, params:, visibility:, line:, column: name_token.column)
370
370
  end
371
371
 
372
- def parse_struct_decl(packed: false, alignment: nil, visibility: :private, attributes: [], inline_methods: true)
372
+ def parse_struct_decl(packed: false, alignment: nil, visibility: :private, attributes: [], qualified_parts: [])
373
373
  line = previous.line
374
374
  name_token = consume_name("expected struct name")
375
375
  name = name_token.lexeme
376
+ qualified_name = qualified_parts + [name]
376
377
  lifetime_params, type_params = parse_struct_decl_params
377
378
  implements = parse_implements_clause
378
379
  c_name = parse_optional_c_name
@@ -380,29 +381,33 @@ module MilkTea
380
381
  receiver_type_param_names = type_params.map(&:name)
381
382
  members = with_type_param_names(receiver_type_param_names) do
382
383
  parse_recoverable_block do
383
- parse_struct_member
384
+ parse_struct_member(qualified_name)
384
385
  end
385
386
  end
386
387
  fields = members.filter_map { |kind, member| member if kind == :field }
387
388
  events = members.filter_map { |kind, member| member if kind == :event }
388
389
  nested_types = members.filter_map { |kind, member| member if kind == :nested_type }
389
390
  methods = members.filter_map { |kind, member| member if kind == :method }
391
+ nested_extending_blocks = members.filter_map { |kind, _, blocks| blocks if kind == :nested_type }.flatten(1)
390
392
  struct_decl = AST::StructDecl.new(name:, type_params:, implements:, c_name:, fields:, events:, nested_types:, attributes:, packed:, alignment:, visibility:, lifetime_params:, line:, column: name_token.column)
391
393
 
392
- if inline_methods && methods.any?
393
- type_ref_args = type_params.map do |tp|
394
- AST::TypeArgument.new(
395
- value: AST::TypeRef.new(name: AST::QualifiedName.new(parts: [tp.name]), arguments: [], nullable: false, line: tp.line, column: tp.column),
396
- line: tp.line,
397
- column: tp.column,
398
- )
399
- end
400
- type_ref = AST::TypeRef.new(name: AST::QualifiedName.new(parts: [name]), arguments: type_ref_args, nullable: false, line: name_token.line, column: name_token.column)
401
- extending_block = AST::ExtendingBlock.new(type_name: type_ref, methods:, line: name_token.line, column: name_token.column)
402
- [struct_decl, extending_block]
403
- else
404
- struct_decl
394
+ extending_blocks = []
395
+ extending_blocks << inline_methods_extending_block(qualified_name, type_params, methods, name_token) if methods.any?
396
+ extending_blocks.concat(nested_extending_blocks)
397
+
398
+ extending_blocks.empty? ? struct_decl : [struct_decl, *extending_blocks]
399
+ end
400
+
401
+ def inline_methods_extending_block(qualified_name, type_params, methods, name_token)
402
+ type_ref_args = type_params.map do |tp|
403
+ AST::TypeArgument.new(
404
+ value: AST::TypeRef.new(name: AST::QualifiedName.new(parts: [tp.name]), arguments: [], nullable: false, line: tp.line, column: tp.column),
405
+ line: tp.line,
406
+ column: tp.column,
407
+ )
405
408
  end
409
+ type_ref = AST::TypeRef.new(name: AST::QualifiedName.new(parts: qualified_name), arguments: type_ref_args, nullable: false, line: name_token.line, column: name_token.column)
410
+ AST::ExtendingBlock.new(type_name: type_ref, methods:, line: name_token.line, column: name_token.column, inline: true)
406
411
  end
407
412
 
408
413
  def parse_struct_decl_params
@@ -459,7 +464,7 @@ module MilkTea
459
464
  result
460
465
  end
461
466
 
462
- def parse_struct_member
467
+ def parse_struct_member(qualified_name)
463
468
  field_attributes = parse_attribute_applications
464
469
 
465
470
  if check_method_start?
@@ -475,7 +480,9 @@ module MilkTea
475
480
 
476
481
  if check(:struct) && !check_next(:colon)
477
482
  advance
478
- return [:nested_type, parse_struct_decl(visibility:, attributes: field_attributes, inline_methods: false)]
483
+ result = parse_struct_decl(visibility:, attributes: field_attributes, qualified_parts: qualified_name)
484
+ nested_decl, *nested_blocks = result.is_a?(Array) ? result : [result]
485
+ return [:nested_type, nested_decl, nested_blocks]
479
486
  end
480
487
 
481
488
  raise error(visibility_token, "public is only allowed on struct events") if visibility == :public
@@ -129,53 +129,23 @@ module MilkTea
129
129
  def validate_async_statement!(statement)
130
130
  case statement
131
131
  when AST::ErrorBlockStmt
132
- if statement.header_expression
133
- context = case statement.header_type
134
- when :if then "if conditions"
135
- when :while then "while conditions"
136
- end
137
- validate_async_expression_support!(statement.header_expression, context:) if context
138
- end
139
- if statement.header_type == :for
140
- Array(statement.header_iterables).each do |iterable|
141
- validate_async_expression_support!(iterable, context: "for iterables")
142
- end
143
- end
144
132
  statement.body.each { |s| validate_async_statement!(s) }
145
133
  when AST::ErrorStmt
146
134
  nil
147
135
  when AST::LocalDecl
148
- validate_async_expression_support!(statement.value, context: "local initializer") if statement.value
149
136
  statement.else_body&.each { |s| validate_async_statement!(s) }
150
- when AST::Assignment
151
- validate_async_expression_support!(statement.target, context: "assignment target")
152
- validate_async_expression_support!(statement.value, context: "assignment")
153
- when AST::ExpressionStmt
154
- validate_async_expression_support!(statement.expression, context: "expression statement")
155
- when AST::ReturnStmt
156
- return unless statement.value
157
-
158
- validate_async_expression_support!(statement.value, context: "return statement")
137
+ when AST::Assignment, AST::ExpressionStmt, AST::ReturnStmt
138
+ nil
159
139
  when AST::IfStmt
160
140
  statement.branches.each do |branch|
161
- validate_async_expression_support!(branch.condition, context: "if conditions")
162
-
163
141
  branch.body.each { |s| validate_async_statement!(s) }
164
142
  end
165
143
  statement.else_body&.each { |s| validate_async_statement!(s) }
166
144
  when AST::WhileStmt
167
- validate_async_expression_support!(statement.condition, context: "while conditions")
168
-
169
145
  statement.body.each { |s| validate_async_statement!(s) }
170
146
  when AST::ForStmt
171
- statement.iterables.each do |iterable|
172
- validate_async_expression_support!(iterable, context: "for iterables")
173
- end
174
-
175
147
  statement.body.each { |s| validate_async_statement!(s) }
176
148
  when AST::MatchStmt
177
- validate_async_expression_support!(statement.expression, context: "match discriminants")
178
-
179
149
  statement.arms.each { |arm| arm.body.each { |s| validate_async_statement!(s) } }
180
150
  when AST::UnsafeStmt
181
151
  statement.body.each { |s| validate_async_statement!(s) }
@@ -191,85 +161,6 @@ module MilkTea
191
161
  end
192
162
  end
193
163
 
194
- def validate_async_expression_support!(expression, context:)
195
- unsupported_context = unsupported_await_position(expression)
196
- return unless unsupported_context
197
-
198
- raise_sema_error("await in async functions is not supported inside #{unsupported_context} yet")
199
- end
200
-
201
- def unsupported_await_position(expression)
202
- nil
203
- end
204
-
205
- def statement_contains_await?(statement)
206
- case statement
207
- when AST::ErrorBlockStmt
208
- (statement.header_expression && expression_contains_await?(statement.header_expression)) ||
209
- Array(statement.header_iterables).any? { |iterable| expression_contains_await?(iterable) } ||
210
- statements_contain_await?(statement.body)
211
- when AST::LocalDecl
212
- (statement.value && expression_contains_await?(statement.value)) ||
213
- (statement.else_body && statements_contain_await?(statement.else_body))
214
- when AST::Assignment
215
- expression_contains_await?(statement.target) || expression_contains_await?(statement.value)
216
- when AST::IfStmt
217
- statement.branches.any? { |branch| expression_contains_await?(branch.condition) || statements_contain_await?(branch.body) } ||
218
- (statement.else_body && statements_contain_await?(statement.else_body))
219
- when AST::MatchStmt
220
- expression_contains_await?(statement.expression) || statement.arms.any? { |arm| expression_contains_await?(arm.pattern) || statements_contain_await?(arm.body) }
221
- when AST::UnsafeStmt
222
- statements_contain_await?(statement.body)
223
- when AST::StaticAssert
224
- expression_contains_await?(statement.condition) || expression_contains_await?(statement.message)
225
- when AST::ForStmt
226
- statement.iterables.any? { |iterable| expression_contains_await?(iterable) } || statements_contain_await?(statement.body)
227
- when AST::WhileStmt
228
- expression_contains_await?(statement.condition) || statements_contain_await?(statement.body)
229
- when AST::ReturnStmt
230
- statement.value && expression_contains_await?(statement.value)
231
- when AST::DeferStmt
232
- statements_contain_await?(statement.body)
233
- when AST::ExpressionStmt
234
- expression_contains_await?(statement.expression)
235
- else
236
- false
237
- end
238
- end
239
-
240
- def statements_contain_await?(statements)
241
- statements.any? { |statement| statement_contains_await?(statement) }
242
- end
243
-
244
- def expression_contains_await?(expression)
245
- case expression
246
- when AST::AwaitExpr
247
- true
248
- when AST::Call, AST::Specialization
249
- expression_contains_await?(expression.callee) || expression.arguments.any? { |argument| expression_contains_await?(argument.value) }
250
- when AST::UnaryOp
251
- expression_contains_await?(expression.operand)
252
- when AST::BinaryOp
253
- expression_contains_await?(expression.left) || expression_contains_await?(expression.right)
254
- when AST::IfExpr
255
- expression_contains_await?(expression.condition) || expression_contains_await?(expression.then_expression) || expression_contains_await?(expression.else_expression)
256
- when AST::MatchExpr
257
- expression_contains_await?(expression.expression) || expression.arms.any? { |arm| expression_contains_await?(arm.pattern) || expression_contains_await?(arm.value) }
258
- when AST::UnsafeExpr
259
- expression_contains_await?(expression.expression)
260
- when AST::PrefixCast
261
- expression_contains_await?(expression.expression)
262
- when AST::MemberAccess
263
- expression_contains_await?(expression.receiver)
264
- when AST::IndexAccess
265
- expression_contains_await?(expression.receiver) || expression_contains_await?(expression.index)
266
- when AST::FormatString
267
- expression.parts.any? { |part| part.is_a?(AST::FormatExprPart) && expression_contains_await?(part.expression) }
268
- else
269
- false
270
- end
271
- end
272
-
273
164
  def suggest_name(wrong, candidates, max_distance: 2)
274
165
  return nil if wrong.nil? || wrong.to_s.empty? || candidates.nil? || candidates.empty?
275
166
 
@@ -1309,7 +1309,7 @@ module MilkTea
1309
1309
  return [:has_attribute, nil, nil] if callee.name == "has_attribute"
1310
1310
  return [:get, nil, nil] if callee.name == "get"
1311
1311
 
1312
- type = @ctx.types[callee.name]
1312
+ type = lookup_named_type(callee.name)
1313
1313
  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)
1314
1314
  if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
1315
1315
  raise_sema_error("generic type #{callee.name} requires type arguments")
@@ -1396,7 +1396,6 @@ module MilkTea
1396
1396
  return [:array_as_span, field_receiver_type, callee.receiver]
1397
1397
  end
1398
1398
 
1399
- return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member))
1400
1399
  return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member))
1401
1400
 
1402
1401
  if (imported_module = imported_module_with_private_method(method_receiver_type, callee.member))
@@ -22,25 +22,31 @@ module MilkTea
22
22
  when AST::ExtendingBlock
23
23
  dispatch_receiver_type, receiver_type, receiver_type_param_names, receiver_type_param_constraints = resolve_methods_receiver_target(decl.type_name)
24
24
 
25
- decl.methods.each do |method|
26
- begin
27
- binding = with_error_node(method) do
28
- declare_function_binding(
29
- method,
30
- receiver_type:,
31
- declared_receiver_type: receiver_type,
32
- receiver_type_param_names:,
33
- receiver_type_param_constraints:,
34
- )
35
- end
36
- instance_method = receiver_type && method.kind != :static
37
- method_key = instance_method ? binding.name : "static:#{binding.name}"
38
- raise_sema_error("duplicate method #{decl.type_name}.#{binding.name}") if @ctx.methods[dispatch_receiver_type].key?(method_key)
25
+ previous_nested_types = @current_nested_types
26
+ @current_nested_types = method_receiver_nested_scope(receiver_type)
27
+ begin
28
+ decl.methods.each do |method|
29
+ begin
30
+ binding = with_error_node(method) do
31
+ declare_function_binding(
32
+ method,
33
+ receiver_type:,
34
+ declared_receiver_type: receiver_type,
35
+ receiver_type_param_names:,
36
+ receiver_type_param_constraints:,
37
+ )
38
+ end
39
+ instance_method = receiver_type && method.kind != :static
40
+ method_key = instance_method ? binding.name : "static:#{binding.name}"
41
+ raise_sema_error("duplicate method #{decl.type_name}.#{binding.name}") if @ctx.methods[dispatch_receiver_type].key?(method_key)
39
42
 
40
- @ctx.methods[dispatch_receiver_type][method_key] = binding
41
- rescue SemanticError => e
42
- collect_structural_error(e)
43
+ @ctx.methods[dispatch_receiver_type][method_key] = binding
44
+ rescue SemanticError => e
45
+ collect_structural_error(e)
46
+ end
43
47
  end
48
+ ensure
49
+ @current_nested_types = previous_nested_types
44
50
  end
45
51
  end
46
52
  end
@@ -604,6 +610,7 @@ module MilkTea
604
610
  previous_type_substitutions = @current_type_substitutions
605
611
  previous_specialization_owner = @current_specialization_owner
606
612
  previous_value_type_params = @current_value_type_params
613
+ previous_nested_types = @current_nested_types
607
614
  started_check = false
608
615
  return if binding.external
609
616
  return if @checked_function_bindings[binding.object_id]
@@ -614,6 +621,7 @@ module MilkTea
614
621
  @current_type_substitutions = binding.type_substitutions
615
622
  @current_specialization_owner = binding.specialization_owner
616
623
  @current_value_type_params = resolve_value_type_params(binding.ast.type_params)
624
+ @current_nested_types = method_receiver_nested_scope(binding.declared_receiver_type)
617
625
  with_error_node(binding.ast) do
618
626
  with_scope(binding.body_params) do |scopes|
619
627
  start_local_completion_frame(binding, scopes)
@@ -669,6 +677,7 @@ module MilkTea
669
677
  @current_type_substitutions = previous_type_substitutions
670
678
  @current_specialization_owner = previous_specialization_owner
671
679
  @current_value_type_params = previous_value_type_params
680
+ @current_nested_types = previous_nested_types
672
681
  @checking_function_bindings.delete(binding.object_id)
673
682
  end
674
683
 
@@ -236,7 +236,7 @@ module MilkTea
236
236
  end
237
237
  end
238
238
 
239
- def resolve_type_ref(type_ref, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: nil)
239
+ def resolve_type_ref(type_ref, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: current_nested_types)
240
240
  base = resolve_non_nullable_type(type_ref, type_params:, type_param_constraints:, nested_types:)
241
241
  return base if type_ref.is_a?(AST::FunctionType) || type_ref.is_a?(AST::ProcType) || type_ref.is_a?(AST::TupleType)
242
242
 
@@ -247,11 +247,11 @@ module MilkTea
247
247
 
248
248
  def resolve_non_nullable_type(type_ref, type_params: {}, type_param_constraints: {}, nested_types: nil)
249
249
  if type_ref.is_a?(AST::FunctionType)
250
- return resolve_function_type_ref(type_ref, type_params:, type_param_constraints:)
250
+ return resolve_function_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
251
251
  end
252
252
 
253
253
  if type_ref.is_a?(AST::ProcType)
254
- return resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:)
254
+ return resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
255
255
  end
256
256
 
257
257
  if type_ref.is_a?(AST::DynType)
@@ -259,13 +259,13 @@ module MilkTea
259
259
  end
260
260
 
261
261
  if type_ref.is_a?(AST::TupleType)
262
- return resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:)
262
+ return resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
263
263
  end
264
264
 
265
265
  parts = type_ref.name.parts
266
266
 
267
267
  if type_ref.arguments.any?
268
- return resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:)
268
+ return resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:, nested_types:)
269
269
  end
270
270
 
271
271
  if parts.length == 1 && type_ref.lifetime
@@ -279,18 +279,18 @@ module MilkTea
279
279
  resolve_multi_part_type_ref(type_ref, parts)
280
280
  end
281
281
 
282
- def resolve_function_type_ref(type_ref, type_params:, type_param_constraints:)
282
+ def resolve_function_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
283
283
  params = type_ref.params.map do |param|
284
- Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
284
+ Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:, nested_types:))
285
285
  end
286
- Types::Registry.function(nil, params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
286
+ Types::Registry.function(nil, params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:, nested_types:))
287
287
  end
288
288
 
289
- def resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:)
289
+ def resolve_proc_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
290
290
  params = type_ref.params.map do |param|
291
- Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:))
291
+ Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:, nested_types:))
292
292
  end
293
- Types::Registry.proc(params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:))
293
+ Types::Registry.proc(params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:, nested_types:))
294
294
  end
295
295
 
296
296
  def resolve_dyn_type_ref(type_ref)
@@ -302,25 +302,25 @@ module MilkTea
302
302
  type
303
303
  end
304
304
 
305
- def resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:)
305
+ def resolve_tuple_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
306
306
  names = []
307
307
  element_types = []
308
308
  type_ref.element_types.each do |et|
309
309
  if et.is_a?(AST::Argument)
310
310
  names << et.name
311
- element_types << resolve_type_ref(et.value, type_params:, type_param_constraints:)
311
+ element_types << resolve_type_ref(et.value, type_params:, type_param_constraints:, nested_types:)
312
312
  else
313
313
  names << nil
314
- element_types << resolve_type_ref(et, type_params:, type_param_constraints:)
314
+ element_types << resolve_type_ref(et, type_params:, type_param_constraints:, nested_types:)
315
315
  end
316
316
  end
317
317
  has_named = names.any?
318
318
  Types::Registry.tuple(element_types, field_names: has_named ? names : nil)
319
319
  end
320
320
 
321
- def resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:)
321
+ def resolve_generic_instance_type_ref(type_ref, parts, type_params:, type_param_constraints:, nested_types:)
322
322
  name = parts.join(".")
323
- arguments = type_ref.arguments.map { |argument| resolve_type_argument(argument.value, type_params:, type_param_constraints:) }
323
+ arguments = type_ref.arguments.map { |argument| resolve_type_argument(argument.value, type_params:, type_param_constraints:, nested_types:) }
324
324
 
325
325
  if name != "ref" && arguments.any? { |argument| contains_ref_type?(argument) && !stored_ref_supported_type?(argument) }
326
326
  raise_sema_error("ref types cannot be nested inside #{name}", type_ref)
@@ -434,12 +434,12 @@ module MilkTea
434
434
  value.is_a?(Types::Base) ? value : nil
435
435
  end
436
436
 
437
- def resolve_type_argument(argument, type_params: current_type_params, type_param_constraints: current_type_param_constraints)
437
+ def resolve_type_argument(argument, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: current_nested_types)
438
438
  case argument
439
439
  when AST::TypeRef
440
- resolve_type_argument_ref(argument, type_params:, type_param_constraints:)
440
+ resolve_type_argument_ref(argument, type_params:, type_param_constraints:, nested_types:)
441
441
  when AST::FunctionType, AST::ProcType, AST::TupleType
442
- resolve_type_ref(argument, type_params:, type_param_constraints:)
442
+ resolve_type_ref(argument, type_params:, type_param_constraints:, nested_types:)
443
443
  when AST::IntegerLiteral, AST::FloatLiteral
444
444
  Types::LiteralTypeArg.new(argument.value)
445
445
  else
@@ -447,20 +447,20 @@ module MilkTea
447
447
  end
448
448
  end
449
449
 
450
- def resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:)
451
- return resolve_type_ref(type_ref, type_params:, type_param_constraints:) unless literal_type_argument_name_candidate?(type_ref)
450
+ def resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
451
+ return resolve_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:) unless literal_type_argument_name_candidate?(type_ref)
452
452
 
453
- result = try_resolve_type_ref(type_ref, type_params:, type_param_constraints:)
453
+ result = try_resolve_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
454
454
  return result if result
455
455
 
456
456
  literal_type_argument = resolve_named_literal_type_argument(type_ref)
457
457
  return literal_type_argument if literal_type_argument
458
458
 
459
- resolve_type_ref(type_ref, type_params:, type_param_constraints:)
459
+ resolve_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
460
460
  end
461
461
 
462
- def try_resolve_type_ref(type_ref, type_params:, type_param_constraints:)
463
- resolve_type_ref(type_ref, type_params:, type_param_constraints:)
462
+ def try_resolve_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
463
+ resolve_type_ref(type_ref, type_params:, type_param_constraints:, nested_types:)
464
464
  rescue SemanticError
465
465
  nil
466
466
  end
@@ -867,10 +867,6 @@ module MilkTea
867
867
  Types.pointer_to(type)
868
868
  end
869
869
 
870
- def contains_type_var?(type)
871
- super
872
- end
873
-
874
870
  def resolve_nested_type_ref(parts)
875
871
  current = @ctx.types[parts.first]
876
872
  return nil unless current.is_a?(Types::Struct) || current.is_a?(Types::GenericStructDefinition)
@@ -975,18 +971,6 @@ module MilkTea
975
971
  nil
976
972
  end
977
973
 
978
- def collection_loop_type(type)
979
- super
980
- end
981
-
982
- def collection_loop_binding_type(iterable_type, element_type)
983
- super
984
- end
985
-
986
- def collection_loop_ref_element_type?(type)
987
- super
988
- end
989
-
990
974
  def iterator_loop_type(type)
991
975
  type = referenced_type(type) if ref_type?(type)
992
976
  iter_method = lookup_method(type, "iter")
@@ -1091,7 +1075,7 @@ module MilkTea
1091
1075
  when AST::Identifier
1092
1076
  return current_type_params[expression.name] if current_type_params.key?(expression.name)
1093
1077
 
1094
- @ctx.types[expression.name]
1078
+ lookup_named_type(expression.name)
1095
1079
  when AST::MemberAccess
1096
1080
  return nil unless expression.receiver.is_a?(AST::Identifier)
1097
1081
 
@@ -164,65 +164,6 @@ module MilkTea
164
164
  backing_type.integer_width == expected_type.integer_width
165
165
  end
166
166
 
167
- def common_numeric_type(left_type, right_type)
168
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
169
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
170
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
171
- return unless left_type.numeric? && right_type.numeric?
172
- return left_type if left_type == right_type
173
-
174
- return common_integer_type(left_type, right_type) if left_type.integer? && right_type.integer?
175
- return wider_float_type(left_type, right_type) if left_type.float? && right_type.float?
176
-
177
- float_type, integer_type = left_type.float? ? [left_type, right_type] : [right_type, left_type]
178
- return unless integer_type.integer? && integer_type.fixed_width_integer?
179
-
180
- float_type
181
- end
182
-
183
- def common_integer_type(left_type, right_type)
184
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
185
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
186
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
187
- return unless left_type.integer? && right_type.integer?
188
- return left_type if left_type == right_type
189
- return unless left_type.fixed_width_integer? && right_type.fixed_width_integer?
190
-
191
- # Same signedness: the wider type wins (both operands are losslessly
192
- # assignable to it).
193
- if left_type.signed_integer? == right_type.signed_integer?
194
- return left_type.integer_width >= right_type.integer_width ? left_type : right_type
195
- end
196
-
197
- # Mixed signed/unsigned: promote to the narrowest signed type that holds
198
- # both operands' full ranges, mirroring the lossless assignment rule. A
199
- # strictly-wider signed type covers an unsigned operand; equal-width or
200
- # wider unsigned operands widen to the next signed width. Mixing with a
201
- # 64-bit unsigned type has no safe signed common type, so callers fall
202
- # back to requiring an explicit cast.
203
- signed_type, unsigned_type = if left_type.signed_integer?
204
- [left_type, right_type]
205
- else
206
- [right_type, left_type]
207
- end
208
-
209
- return signed_type if signed_type.integer_width > unsigned_type.integer_width
210
-
211
- signed_type_above_width(unsigned_type.integer_width)
212
- end
213
-
214
- def signed_type_above_width(width)
215
- case width
216
- when 8 then @ctx.types.fetch("short")
217
- when 16 then @ctx.types.fetch("int")
218
- when 32 then @ctx.types.fetch("long")
219
- end
220
- end
221
-
222
- def wider_float_type(left_type, right_type)
223
- left_type.float_width >= right_type.float_width ? left_type : right_type
224
- end
225
-
226
167
  def pointer_arithmetic_result(operator, left_type, right_type)
227
168
  if pointer_type?(left_type) && integer_type?(right_type)
228
169
  require_unsafe!("pointer arithmetic requires unsafe") unless own_type?(left_type)
@@ -186,7 +186,6 @@ module MilkTea
186
186
  implemented_interfaces: {}, imports: {},
187
187
  private_types: {}, private_interfaces: {}, private_attributes: {},
188
188
  private_values: {}, private_functions: {}, private_methods: {},
189
- private_implemented_interfaces: {},
190
189
  )
191
190
  end
192
191
 
@@ -342,7 +341,6 @@ module MilkTea
342
341
  params: params.freeze,
343
342
  module_name: @ctx.module_name,
344
343
  builtin: false,
345
- ast: decl,
346
344
  )
347
345
  end
348
346
  end
@@ -650,6 +650,63 @@ module MilkTea
650
650
  def array_element_type(type)
651
651
  type.arguments.first
652
652
  end
653
+
654
+ def common_numeric_type(left_type, right_type)
655
+ left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
656
+ right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
657
+ return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
658
+ return unless left_type.numeric? && right_type.numeric?
659
+ return left_type if left_type == right_type
660
+
661
+ return common_integer_type(left_type, right_type) if left_type.integer? && right_type.integer?
662
+ return wider_float_type(left_type, right_type) if left_type.float? && right_type.float?
663
+
664
+ float_type, integer_type = left_type.float? ? [left_type, right_type] : [right_type, left_type]
665
+ return unless integer_type.integer? && integer_type.fixed_width_integer?
666
+
667
+ float_type
668
+ end
669
+
670
+ def common_integer_type(left_type, right_type)
671
+ left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
672
+ right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
673
+ return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
674
+ return unless left_type.integer? && right_type.integer?
675
+ return left_type if left_type == right_type
676
+ return unless left_type.fixed_width_integer? && right_type.fixed_width_integer?
677
+
678
+ # Same signedness: the wider type wins. Mixed signed/unsigned: promote to
679
+ # the narrowest signed type that holds both operands' full ranges. A
680
+ # strictly-wider signed type covers an unsigned operand; equal-width or
681
+ # wider unsigned operands widen to the next signed width. Mixing with a
682
+ # 64-bit unsigned type has no safe signed common type, so callers fall
683
+ # back to requiring an explicit cast.
684
+ if left_type.signed_integer? == right_type.signed_integer?
685
+ return left_type.integer_width >= right_type.integer_width ? left_type : right_type
686
+ end
687
+
688
+ signed_type, unsigned_type = if left_type.signed_integer?
689
+ [left_type, right_type]
690
+ else
691
+ [right_type, left_type]
692
+ end
693
+
694
+ return signed_type if signed_type.integer_width > unsigned_type.integer_width
695
+
696
+ signed_type_above_width(unsigned_type.integer_width)
697
+ end
698
+
699
+ def signed_type_above_width(width)
700
+ case width
701
+ when 8 then Types::Registry.primitive("short")
702
+ when 16 then Types::Registry.primitive("int")
703
+ when 32 then Types::Registry.primitive("long")
704
+ end
705
+ end
706
+
707
+ def wider_float_type(left_type, right_type)
708
+ left_type.float_width >= right_type.float_width ? left_type : right_type
709
+ end
653
710
  end
654
711
  end
655
712
  end
@@ -1712,10 +1712,6 @@ module MilkTea
1712
1712
  GenericInstance.new("ptr", [type])
1713
1713
  end
1714
1714
 
1715
- def self.integer_type?(type)
1716
- type.is_a?(Primitive) && %w[int ptr_uint i8 i16 i32 i64 u8 u16 u32 u64].include?(type.name)
1717
- end
1718
-
1719
1715
  def self.array_type?(type)
1720
1716
  type.is_a?(GenericInstance) && type.name == "array" && type.arguments.length == 2
1721
1717
  end
@@ -443,10 +443,6 @@ module MilkTea
443
443
  { items: [] }
444
444
  end
445
445
 
446
- def refresh_workspace_diagnostics
447
- @protocol.write_notification('workspace/diagnostic/refresh', nil)
448
- end
449
-
450
446
  def find_match_end_line(lines, match_start_idx)
451
447
  return nil if match_start_idx >= lines.length
452
448
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../core"
4
- require_relative "cst_formatter"
5
4
 
6
5
  module MilkTea
7
6
  class FormatterError < StandardError; end
@@ -49,12 +48,12 @@ module MilkTea
49
48
 
50
49
  def self.preserve_format(source, path:, profile: nil)
51
50
  cst = profile_phase(profile, "format.cst") { build_cst(source, path:) }
52
- profile_phase(profile, "format.cst_fmt") { CSTFormatter.format(cst) }
51
+ profile_phase(profile, "format.cst_fmt") { cst.reconstruct }
53
52
  end
54
53
 
55
54
  def self.tidy_format(source, path:, max_line_length: DEFAULT_MAX_LINE_LENGTH, profile: nil)
56
55
  cst = profile_phase(profile, "format.cst") { build_cst(source, path:) }
57
- normalized = profile_phase(profile, "format.normalize") { CSTFormatter.format_normalized(cst) }
56
+ normalized = profile_phase(profile, "format.normalize") { cst.reconstruct_normalized }
58
57
  wrapped = profile_phase(profile, "format.wrap") { wrap_long_argument_lists(normalized, max_line_length:, path:) }
59
58
  profile_phase(profile, "format.blank_lines") { normalize_blank_lines(wrapped, path:) }
60
59
  end
@@ -17,6 +17,7 @@ module MilkTea
17
17
  when "redundant-else" then redundant_else_edits(lines, warning)
18
18
  when "redundant-return" then redundant_return_edits(lines, warning)
19
19
  when "redundant-type-annotation" then redundant_type_annotation_edits(lines, warning)
20
+ when "prefer-inline-methods" then prefer_inline_methods_edits(lines, warning)
20
21
  when "unused-import" then unused_import_edits(lines, warning)
21
22
  when "trailing-list-comma" then trailing_list_comma_edits(lines, warning)
22
23
  else []
@@ -152,6 +153,51 @@ module MilkTea
152
153
  [FixEdit.new(start_line: line_idx, start_char: 0, end_line: line_idx + 1, end_char: 0, new_text: "")]
153
154
  end
154
155
 
156
+ # Moves the methods of an `extending X:` block inline into the matching
157
+ # `struct X:` declaration. Only rewrites when the struct immediately
158
+ # precedes the extending block (blank lines allowed between them); other
159
+ # layouts are left alone since the move would be non-local.
160
+ def self.prefer_inline_methods_edits(lines, warning)
161
+ name = warning.symbol_name
162
+ return [] unless name && warning.line
163
+
164
+ struct_idx = lines.index { |l| l.match?(/\A\s*struct\s+#{Regexp.escape(name)}\b/) }
165
+ return [] unless struct_idx
166
+
167
+ last_member_idx = nil
168
+ ((struct_idx + 1)...lines.length).each do |i|
169
+ l = lines[i]
170
+ break if !l.chomp.empty? && !l.start_with?(" ", "\t")
171
+
172
+ last_member_idx = i unless l.chomp.empty?
173
+ end
174
+ return [] unless last_member_idx
175
+
176
+ ext_start_idx = warning.line - 1
177
+ return [] unless last_member_idx < ext_start_idx
178
+
179
+ ext_end_idx = ext_start_idx
180
+ ((ext_start_idx + 1)...lines.length).each do |i|
181
+ l = lines[i]
182
+ break if !l.chomp.empty? && !l.start_with?(" ", "\t")
183
+
184
+ ext_end_idx = i unless l.chomp.empty?
185
+ end
186
+
187
+ method_lines = lines[(ext_start_idx + 1)..ext_end_idx].to_a
188
+ method_lines.shift while method_lines.first && method_lines.first.chomp.empty?
189
+ method_lines.pop while method_lines.last && method_lines.last.chomp.empty?
190
+ return [] if method_lines.empty?
191
+
192
+ between = lines[(last_member_idx + 1)...ext_start_idx]
193
+ return [] unless between.all? { |l| l.chomp.empty? }
194
+
195
+ method_text = method_lines.join
196
+ new_text = "#{lines[last_member_idx].chomp}\n\n#{method_text}"
197
+
198
+ [FixEdit.new(start_line: last_member_idx, start_char: 0, end_line: ext_end_idx + 1, end_char: 0, new_text: new_text)]
199
+ end
200
+
155
201
  def self.unused_import_edits(lines, warning)
156
202
  return [] unless warning.line
157
203
 
@@ -586,6 +586,38 @@ module MilkTea
586
586
  def lvalue_expression?(expression)
587
587
  expression.is_a?(AST::Identifier) || expression.is_a?(AST::MemberAccess) || expression.is_a?(AST::IndexAccess)
588
588
  end
589
+ # ── prefer-inline-methods ──────────────────────────────────────────────
590
+ # Flag `extending X:` blocks whose receiver struct is declared in the same
591
+ # file, suggesting the methods be written inline inside the struct body
592
+ # (which desugars to the identical `extending` block).
593
+
594
+ def emit_prefer_inline_methods_warnings(source_file)
595
+ struct_names = source_file.declarations.filter_map { |decl| decl.name if decl.is_a?(AST::StructDecl) }.to_set
596
+ return if struct_names.empty?
597
+
598
+ source_file.declarations.each do |declaration|
599
+ next unless declaration.is_a?(AST::ExtendingBlock)
600
+ next if declaration.inline
601
+
602
+ parts = declaration.type_name.name.parts
603
+ next unless parts.length == 1
604
+ next if declaration.type_name.arguments.any?
605
+
606
+ name = parts.first
607
+ next unless struct_names.include?(name)
608
+
609
+ @warnings << Warning.new(
610
+ path: @path,
611
+ line: declaration.line,
612
+ column: declaration.column,
613
+ length: name.length,
614
+ code: "prefer-inline-methods",
615
+ message: "methods on '#{name}' can be written inline inside the struct declaration",
616
+ severity: :hint,
617
+ symbol_name: name,
618
+ )
619
+ end
620
+ end
589
621
  end
590
622
  end
591
623
  end
@@ -35,6 +35,7 @@ module MilkTea
35
35
  owning-release-double
36
36
  prefer-conditional-expression
37
37
  prefer-inline-if
38
+ prefer-inline-methods
38
39
  prefer-is-variant
39
40
  prefer-let
40
41
  prefer-let-else
@@ -69,6 +70,7 @@ module MilkTea
69
70
  redundant-ignored-match-binding
70
71
  prefer-let-else
71
72
  prefer-var-else
73
+ prefer-inline-methods
72
74
  redundant-bool-compare
73
75
  redundant-cast
74
76
  redundant-else
@@ -95,6 +97,7 @@ module MilkTea
95
97
  "redundant-type-annotation" => "Remove redundant type annotation",
96
98
  "prefer-let-else" => "Rewrite as let-else",
97
99
  "prefer-var-else" => "Rewrite as var-else",
100
+ "prefer-inline-methods" => "Inline methods into struct",
98
101
  "trailing-list-comma" => "Remove trailing list comma",
99
102
  }.freeze
100
103
  EVENT_STACK_SNAPSHOT_WARNING_THRESHOLD = 128
@@ -771,6 +774,7 @@ module MilkTea
771
774
  profile_phase("rule.doc_tag") { emit_doc_tag_warnings(ast) } if full_tier?
772
775
  profile_phase("rule.event_capacity") { emit_event_capacity_warnings(ast) }
773
776
  profile_phase("rule.trailing_list_comma") { emit_trailing_list_comma_warnings(ast) }
777
+ profile_phase("rule.prefer_inline_methods") { emit_prefer_inline_methods_warnings(ast) }
774
778
  profile_phase("rule.line_too_long") { emit_line_too_long_warnings }
775
779
  @warnings
776
780
  end
@@ -11,7 +11,6 @@ require_relative "tooling/sexpr_dumper"
11
11
  require_relative "tooling/sexpr_parser"
12
12
  require_relative "tooling/build"
13
13
  require_relative "tooling/run"
14
- require_relative "tooling/cst_formatter"
15
14
  require_relative "tooling/error_formatter"
16
15
  require_relative "tooling/formatter"
17
16
  require_relative "tooling/linter"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mt-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.39
4
+ version: 0.3.40
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -385,7 +385,6 @@ files:
385
385
  - lib/milk_tea/tooling/cli/commands/snapshot.rb
386
386
  - lib/milk_tea/tooling/cli/commands/test.rb
387
387
  - lib/milk_tea/tooling/cli/commands/toolchain.rb
388
- - lib/milk_tea/tooling/cst_formatter.rb
389
388
  - lib/milk_tea/tooling/debug_info_formatter.rb
390
389
  - lib/milk_tea/tooling/debug_map.rb
391
390
  - lib/milk_tea/tooling/docs_app.rb
@@ -626,7 +625,7 @@ metadata:
626
625
  homepage_uri: https://teefan.github.io/mt-lang/
627
626
  source_code_uri: https://github.com/teefan/mt-lang
628
627
  post_install_message: |
629
- Milk Tea 0.3.39 installed!
628
+ Milk Tea 0.3.40 installed!
630
629
 
631
630
  System requirements:
632
631
  - A C compiler (gcc or clang) must be available on PATH
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MilkTea
4
- class CSTFormatter
5
- def self.format(cst)
6
- cst.reconstruct
7
- end
8
-
9
- def self.format_normalized(cst)
10
- cst.reconstruct_normalized
11
- end
12
- end
13
- end