mt-lang 0.3.42 → 0.4.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.
@@ -213,6 +213,10 @@ module MilkTea
213
213
 
214
214
  receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
215
215
 
216
+ if receiver_type.is_a?(Types::StringView)
217
+ return @ctx.types.fetch("ubyte")
218
+ end
219
+
216
220
  if array_type?(receiver_type)
217
221
  return array_element_type(receiver_type)
218
222
  end
@@ -236,6 +240,28 @@ module MilkTea
236
240
  raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
237
241
  end
238
242
 
243
+ def range_index_result_type(receiver_type)
244
+ receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
245
+
246
+ if receiver_type.is_a?(Types::StringView)
247
+ return @ctx.types.fetch("str")
248
+ end
249
+
250
+ if array_type?(receiver_type)
251
+ return Types::Span.new(array_element_type(receiver_type))
252
+ end
253
+
254
+ if receiver_type.is_a?(Types::Span)
255
+ return receiver_type
256
+ end
257
+
258
+ raise LoweringError.new("cannot range-index #{receiver_type}; expected str, array[T, N], or span[T]", line: 0, column: 0, path: @ctx.current_analysis_path)
259
+ end
260
+
261
+ def span_slice_callee(span_type)
262
+ "mt_span_slice_#{sanitize_identifier(span_type.element_type.to_s)}"
263
+ end
264
+
239
265
  def stored_ref_supported_type?(type, visited = {})
240
266
  return true unless type
241
267
 
@@ -459,6 +459,7 @@ module MilkTea
459
459
  match(:async)
460
460
  match(:editable) if check(:editable)
461
461
  match(:static) if check(:static)
462
+ match(:const) if check(:const)
462
463
  result = check(:function) && !check_next(:colon)
463
464
  @current = saved
464
465
  result
@@ -657,10 +658,10 @@ module MilkTea
657
658
  end
658
659
 
659
660
  def parse_method_def(attributes: [])
660
- visibility, _visibility_token, async, kind, line, name_token = parse_method_head
661
+ visibility, _visibility_token, async, kind, const, line, name_token = parse_method_head
661
662
  name = name_token.lexeme
662
663
  type_params, params, return_type, body = parse_callable_signature
663
- AST::MethodDef.new(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, attributes:, line:, column: name_token.column)
664
+ AST::MethodDef.new(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, const:, attributes:, line:, column: name_token.column)
664
665
  rescue ParseError => e
665
666
  raise unless @recovery_errors
666
667
 
@@ -668,11 +669,12 @@ module MilkTea
668
669
  name = name_token&.lexeme || "unknown"
669
670
  col = name_token&.column || 1
670
671
  advance until eof? || check(:function) || check(:dedent)
671
- AST::MethodDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, kind: kind || :plain, visibility: visibility || :private, async: async || false, attributes:, line:, column: col)
672
+ AST::MethodDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, kind: kind || :plain, visibility: visibility || :private, async: async || false, const: const || false, attributes:, line:, column: col)
672
673
  end
673
674
 
674
675
  def parse_interface_method_decl(attributes: [])
675
- visibility, visibility_token, async, kind, line, name_token = parse_method_head
676
+ visibility, visibility_token, async, kind, const, line, name_token = parse_method_head
677
+ raise error(name_token, "const is not allowed on interface methods") if const
676
678
  raise error(visibility_token, "public is not allowed on interface methods") if visibility == :public
677
679
 
678
680
  name = name_token.lexeme
@@ -689,6 +691,9 @@ module MilkTea
689
691
  visibility, visibility_token = parse_visibility
690
692
  async = match(:async)
691
693
  kind = parse_method_kind
694
+ const = match(:const)
695
+ raise error(previous, "editable methods cannot be const") if kind == :editable && const
696
+ raise error(previous, "async methods cannot be const") if async && const
692
697
  if check(:function)
693
698
  consume(:function, "expected function declaration")
694
699
  elsif check(:identifier)
@@ -699,7 +704,7 @@ module MilkTea
699
704
  end
700
705
  line = previous.line
701
706
  name_token = consume_name("expected function name")
702
- [visibility, visibility_token, async, kind, line, name_token]
707
+ [visibility, visibility_token, async, kind, const, line, name_token]
703
708
  end
704
709
 
705
710
  def parse_method_kind
@@ -298,14 +298,16 @@ module MilkTea
298
298
 
299
299
  def render_function_signature(function, prefix: "")
300
300
  signature_prefix = if function.is_a?(AST::MethodDef)
301
- case function.kind
302
- when :editable
303
- "editable function "
304
- when :static
305
- "static function "
306
- else
307
- "function "
308
- end
301
+ kind_prefix = case function.kind
302
+ when :editable
303
+ "editable "
304
+ when :static
305
+ "static "
306
+ else
307
+ ""
308
+ end
309
+ const_prefix = function.respond_to?(:const) && function.const ? "const " : ""
310
+ "#{kind_prefix}#{const_prefix}function "
309
311
  elsif function.is_a?(AST::ForeignFunctionDecl)
310
312
  "foreign function "
311
313
  else
@@ -37,6 +37,8 @@ module MilkTea
37
37
  allow_span_param_identifier: true,
38
38
  )
39
39
 
40
+ raise_sema_error("cannot assign through str index; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
41
+
40
42
  index_type = infer_expression(expression.index, scopes:)
41
43
  infer_index_result_type(receiver_type, index_type)
42
44
  when AST::Call
@@ -101,6 +103,8 @@ module MilkTea
101
103
  require_mutable_pointer:,
102
104
  allow_span_param_identifier:,
103
105
  )
106
+ raise_sema_error("cannot index through str as an assignment receiver; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
107
+
104
108
  index_type = infer_expression(expression.index, scopes:)
105
109
  infer_index_result_type(receiver_type, index_type)
106
110
  when AST::Call
@@ -470,8 +474,17 @@ module MilkTea
470
474
 
471
475
  def infer_index_access(expression, scopes:)
472
476
  receiver_type = infer_expression(expression.receiver, scopes:)
477
+
478
+ if expression.index.is_a?(AST::RangeExpr)
479
+ return infer_range_index_access(expression, receiver_type, scopes:)
480
+ end
481
+
473
482
  index_type = infer_expression(expression.index, scopes:)
474
483
 
484
+ if receiver_type.is_a?(Types::StringView) && (literal = integer_literal_bound_value(expression.index)) && literal < 0
485
+ raise_sema_error("str index #{literal} is negative; str indices must be non-negative", expression)
486
+ end
487
+
475
488
  if soa_type?(receiver_type)
476
489
  return receiver_type.element_type
477
490
  end
@@ -1022,6 +1022,10 @@ module MilkTea
1022
1022
  def infer_index_result_type(receiver_type, index_type)
1023
1023
  raise_sema_error("index must be an integer type, got #{index_type}") unless integer_type?(index_type)
1024
1024
 
1025
+ if receiver_type.is_a?(Types::StringView)
1026
+ return @ctx.types.fetch("ubyte")
1027
+ end
1028
+
1025
1029
  if array_type?(receiver_type)
1026
1030
  return array_element_type(receiver_type)
1027
1031
  end
@@ -1047,6 +1051,48 @@ module MilkTea
1047
1051
  raise_sema_error("cannot index #{receiver_type}")
1048
1052
  end
1049
1053
 
1054
+ def infer_range_index_access(expression, receiver_type, scopes:)
1055
+ start_type = infer_expression(expression.index.start_expr, scopes:)
1056
+ stop_type = infer_expression(expression.index.end_expr, scopes:)
1057
+ raise_sema_error("range index bounds must be integer types, got #{start_type} and #{stop_type}") unless start_type.integer? && stop_type.integer?
1058
+
1059
+ if array_type?(receiver_type) && !addressable_storage_expression?(expression.receiver, scopes:)
1060
+ raise_sema_error("array range slice requires an addressable array value; bind it to a local first", expression)
1061
+ end
1062
+
1063
+ if array_type?(receiver_type) && (start_val = integer_literal_bound_value(expression.index.start_expr)) && (stop_val = integer_literal_bound_value(expression.index.end_expr))
1064
+ length = array_length(receiver_type)
1065
+ unless start_val >= 0 && start_val <= stop_val && stop_val <= length
1066
+ raise_sema_error("range index [#{start_val}..#{stop_val}] is out of bounds for array[T, #{length}]", expression)
1067
+ end
1068
+ end
1069
+
1070
+ range_index_result_type(receiver_type)
1071
+ end
1072
+
1073
+ def range_index_result_type(receiver_type)
1074
+ if receiver_type.is_a?(Types::StringView)
1075
+ return @ctx.types.fetch("str")
1076
+ end
1077
+
1078
+ if array_type?(receiver_type)
1079
+ return Types::Span.new(array_element_type(receiver_type))
1080
+ end
1081
+
1082
+ if span_type?(receiver_type)
1083
+ return receiver_type
1084
+ end
1085
+
1086
+ raise_sema_error("cannot range-index #{receiver_type}; expected str, array[T, N], or span[T]")
1087
+ end
1088
+
1089
+ def integer_literal_bound_value(expression)
1090
+ return expression.value if expression.is_a?(AST::IntegerLiteral)
1091
+ return -expression.operand.value if expression.is_a?(AST::UnaryOp) && expression.operator == "-" && expression.operand.is_a?(AST::IntegerLiteral)
1092
+
1093
+ nil
1094
+ end
1095
+
1050
1096
  def addressable_storage_expression?(expression, scopes:)
1051
1097
  case expression
1052
1098
  when AST::Identifier
@@ -538,6 +538,7 @@ module MilkTea
538
538
  require_mutable_pointer: true,
539
539
  allow_span_param_identifier: true,
540
540
  )
541
+ raise_sema_error("cannot assign through str index; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
541
542
  element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint"))
542
543
 
543
544
  statement.value.elements.each_with_index do |elem, i|
@@ -298,6 +298,33 @@ module MilkTea
298
298
  current_type_params[identifier_expression.name] || @ctx.types[identifier_expression.name]
299
299
  end,
300
300
  resolve_member_access: lambda do |member_access_expression|
301
+ if (receiver_value = CompileTime.evaluate(
302
+ member_access_expression.receiver,
303
+ resolve_identifier: lambda do |identifier_expression|
304
+ if scopes
305
+ binding = lookup_value(identifier_expression.name, scopes)
306
+ return binding.const_value unless binding&.const_value.nil?
307
+ end
308
+
309
+ resolve_current_module_const_value(identifier_expression.name)
310
+ end,
311
+ resolve_member_access: lambda { |ma| evaluate_compile_time_const_value(ma, scopes:) },
312
+ resolve_type_ref: lambda { |tr| resolve_type_ref(tr) },
313
+ resolve_call: lambda { |ce| evaluate_compile_time_call(ce, scopes:) },
314
+ ))
315
+ case receiver_value
316
+ when Hash
317
+ next receiver_value[member_access_expression.member] if receiver_value.key?(member_access_expression.member)
318
+ when Array
319
+ if member_access_expression.member =~ /\A_(\d+)\z/
320
+ next receiver_value[Regexp.last_match(1).to_i]
321
+ end
322
+ next receiver_value.length if member_access_expression.member == "len"
323
+ when String
324
+ next receiver_value.length if member_access_expression.member == "len"
325
+ end
326
+ end
327
+
301
328
  if member_access_expression.receiver.is_a?(AST::Identifier) && scopes
302
329
  binding = lookup_value(member_access_expression.receiver.name, scopes)
303
330
  if binding && binding.const_value
@@ -392,6 +419,11 @@ module MilkTea
392
419
  end
393
420
  return CompileTime::VariantValue.new(arm: arm_name, fields: fields)
394
421
  end
422
+
423
+ if (method_binding = comptime_method_binding(expression.callee, scopes))
424
+ receiver_value = comptime_member_receiver_value(expression.callee.receiver, scopes)
425
+ return comptime_const_method_body(method_binding, expression.arguments, scopes:, receiver_value:)
426
+ end
395
427
  when AST::Identifier
396
428
  if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct)
397
429
  fields = {}
@@ -587,7 +619,10 @@ module MilkTea
587
619
  initial_vars[param.name] = arg_value
588
620
  end
589
621
 
590
- ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars)
622
+ variable_types = func.body_params.each_with_object({}) do |param, acc|
623
+ acc[param.name] = param.type
624
+ end
625
+ ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars, variable_types: variable_types)
591
626
  result = ctx.evaluate_block(func.ast.body, scopes: nil)
592
627
  result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
593
628
  rescue CompileTime::Error => e
@@ -613,6 +648,40 @@ module MilkTea
613
648
  end
614
649
  end
615
650
 
651
+ def comptime_methods_map
652
+ @ctx.methods
653
+ end
654
+
655
+ def comptime_methods_map_by_to_s(_type)
656
+ nil
657
+ end
658
+
659
+ def comptime_scoped_value_type(name, ctx)
660
+ return nil unless ctx
661
+
662
+ lookup_value(name, ctx)&.type
663
+ end
664
+
665
+ def comptime_value_type(name)
666
+ @ctx.top_level_values[name]&.type
667
+ end
668
+
669
+ def comptime_const_function(name)
670
+ @ctx.top_level_functions[name]
671
+ end
672
+
673
+ def comptime_eval(expression, scopes)
674
+ evaluate_compile_time_const_value(expression, scopes: scopes)
675
+ end
676
+
677
+ def comptime_block_context(initial_vars, variable_types)
678
+ ::MilkTea::CompileTime::BlockContext.new(self, initial_variables: initial_vars, variable_types: variable_types)
679
+ end
680
+
681
+ def comptime_raise_error(error)
682
+ raise_sema_error(error.message)
683
+ end
684
+
616
685
  def evaluate_has_attribute_call(arguments, scopes:)
617
686
  target = evaluate_reflection_target_argument(arguments.first.value, scopes:)
618
687
  binding = resolve_attribute_name_argument(arguments[1].value)
@@ -131,6 +131,7 @@ module MilkTea
131
131
 
132
132
  class Checker
133
133
  include Intrinsics
134
+ include CompileTime::MethodFolding
134
135
 
135
136
  attr_reader :ctx
136
137
 
@@ -222,8 +223,14 @@ module MilkTea
222
223
  when AST::WhenStmt
223
224
  body = when_chosen_body(decl)
224
225
  collect_emit_from_declarations(body) if body
225
- when AST::StructDecl, AST::ExtendingBlock
226
- # no emit in structs/extending blocks
226
+ when AST::StructDecl
227
+ # no emit in structs
228
+ when AST::ExtendingBlock
229
+ decl.methods.each do |method|
230
+ next unless method.respond_to?(:const) && method.const && method.body
231
+
232
+ collect_emit_from_statements(method.body)
233
+ end
227
234
  end
228
235
  end
229
236
  end
@@ -21,40 +21,63 @@ module MilkTea
21
21
  end
22
22
 
23
23
  def self.nullable(base)
24
- _intern([:nullable, base]) { Nullable.new(base) }
24
+ _intern([:nullable, type_signature(base)]) { Nullable.new(base) }
25
25
  end
26
26
 
27
27
  def self.generic_instance(name, arguments)
28
28
  args = arguments.freeze
29
- _intern([:generic, name, args]) { GenericInstance.new(name, args) }
29
+ _intern([:generic, name, args.map { |a| type_signature(a) }.freeze]) { GenericInstance.new(name, args) }
30
30
  end
31
31
 
32
32
  def self.span(element_type)
33
- _intern([:span, element_type]) { Span.new(element_type) }
33
+ _intern([:span, type_signature(element_type)]) { Span.new(element_type) }
34
34
  end
35
35
 
36
36
  def self.task(result_type)
37
- _intern([:task, result_type]) { Task.new(result_type) }
37
+ _intern([:task, type_signature(result_type)]) { Task.new(result_type) }
38
38
  end
39
39
 
40
40
  def self.string_view
41
41
  _intern([:string_view]) { StringView.new }
42
42
  end
43
43
 
44
- # Param signature for intern keys. Parameter#eql? is name-insensitive
45
- # (assignability must not depend on parameter names), so arrays of
46
- # Parameter objects would conflate fn(value: int) with fn(arg0: int) in
47
- # the intern pool. Embedding names here keeps distinct signatures
48
- # distinct across independent programs sharing a long-lived pool (the LSP
49
- # never resets the registry between checks).
44
+ # Name-sensitive signature for intern keys. Parameter#eql? is
45
+ # name-insensitive (assignability must not depend on parameter names), so
46
+ # Function/Proc objects (and any wrapper containing them) that differ only
47
+ # in parameter names collide under Hash/==. Pool keys that embed such
48
+ # types raw therefore conflate fn(value: int) with fn(arg0: int) in a
49
+ # long-lived pool (the LSP never resets the registry between checks),
50
+ # leaking one program's parameter names into another's generated C.
51
+ # Embedding names here keeps distinct signatures distinct.
52
+ def self.type_signature(type)
53
+ case type
54
+ when Function
55
+ [:function, type.name, param_signature(type.params), type_signature(type.return_type), type_signature(type.receiver_type), type.receiver_editable, type.variadic, type.external]
56
+ when Proc
57
+ [:proc, param_signature(type.params), type_signature(type.return_type)]
58
+ when Nullable
59
+ [:nullable, type_signature(type.base)]
60
+ when GenericInstance
61
+ [:generic, type.name, type.arguments.map { |a| type_signature(a) }]
62
+ when Tuple
63
+ [:tuple, type.element_types.map { |t| type_signature(t) }, type.field_names]
64
+ when Span
65
+ [:span, type_signature(type.element_type)]
66
+ when Task
67
+ [:task, type_signature(type.result_type)]
68
+ else
69
+ type
70
+ end
71
+ end
72
+
50
73
  def self.param_signature(params)
51
- params.map { |p| [p.name, p.type, p.mutable, p.passing_mode, p.boundary_type] }
74
+ params.map { |p| [p.name, type_signature(p.type), p.mutable, p.passing_mode, p.boundary_type] }
52
75
  end
53
76
 
54
77
  def self.function(name, params:, return_type:, receiver_type: nil, receiver_editable: false, variadic: false, external: false)
55
78
  params_frozen = params.freeze
56
79
  param_key = param_signature(params_frozen)
57
- _intern([:function, name, param_key, return_type, receiver_type, receiver_editable, variadic, external]) {
80
+ _intern([:function, name, param_key, type_signature(return_type), type_signature(receiver_type), receiver_editable, variadic, external]) {
58
81
  Function.new(name, params: params_frozen, return_type: return_type, receiver_type: receiver_type, receiver_editable: receiver_editable, variadic: variadic, external: external)
59
82
  }
60
83
  end
@@ -62,7 +85,7 @@ module MilkTea
62
85
  def self.proc(params:, return_type:)
63
86
  params_frozen = params.freeze
64
87
  param_key = param_signature(params_frozen)
65
- _intern([:proc, param_key, return_type]) { Proc.new(params: params_frozen, return_type: return_type) }
88
+ _intern([:proc, param_key, type_signature(return_type)]) { Proc.new(params: params_frozen, return_type: return_type) }
66
89
  end
67
90
 
68
91
  def self.parameter(name, type, mutable: false, passing_mode: :plain, boundary_type: nil)
@@ -74,15 +97,15 @@ module MilkTea
74
97
  def self.tuple(element_types, field_names: nil)
75
98
  et_frozen = element_types.freeze
76
99
  fn_frozen = field_names&.freeze
77
- _intern([:tuple, et_frozen, fn_frozen]) { Tuple.new(et_frozen, field_names: fn_frozen) }
100
+ _intern([:tuple, et_frozen.map { |t| type_signature(t) }.freeze, fn_frozen]) { Tuple.new(et_frozen, field_names: fn_frozen) }
78
101
  end
79
102
 
80
103
  def self.soa(element_type, count:)
81
- _intern([:soa, element_type, count]) { SoA.new(element_type, count: count) }
104
+ _intern([:soa, type_signature(element_type), count]) { SoA.new(element_type, count: count) }
82
105
  end
83
106
 
84
107
  def self.simd(element_type, lane_count:)
85
- _intern([:simd, element_type, lane_count]) { Simd.new(element_type, lane_count: lane_count) }
108
+ _intern([:simd, type_signature(element_type), lane_count]) { Simd.new(element_type, lane_count: lane_count) }
86
109
  end
87
110
 
88
111
  def self.lifetime_ref(name)
@@ -1330,7 +1330,6 @@ module MilkTea
1330
1330
  end
1331
1331
  return nil unless idx && tokens[idx].type == :identifier
1332
1332
 
1333
- type_name = tokens[idx].lexeme
1334
1333
  type = resolve_arm_receiver_type(facts, tokens, idx)
1335
1334
 
1336
1335
  arm = if type.respond_to?(:arm)
@@ -86,7 +86,7 @@ module MilkTea
86
86
  rescue StandardError
87
87
  nil
88
88
  end
89
- clear_shared_module_cache if previous_content != disk_content
89
+ @workspace.clear_shared_module_cache if previous_content != disk_content
90
90
  unless defined?(@pull_diagnostics_active) && @pull_diagnostics_active
91
91
  @protocol.write_notification('textDocument/publishDiagnostics', {
92
92
  uri: uri,
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.42
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -205,6 +205,7 @@ files:
205
205
  - lib/milk_tea/core/c_backend/type_declaration.rb
206
206
  - lib/milk_tea/core/c_backend/type_system.rb
207
207
  - lib/milk_tea/core/compile_time.rb
208
+ - lib/milk_tea/core/compile_time/method_folding.rb
208
209
  - lib/milk_tea/core/control_flow.rb
209
210
  - lib/milk_tea/core/control_flow/builder.rb
210
211
  - lib/milk_tea/core/control_flow/constant_propagation.rb
@@ -628,7 +629,7 @@ metadata:
628
629
  homepage_uri: https://teefan.github.io/mt-lang/
629
630
  source_code_uri: https://github.com/teefan/mt-lang
630
631
  post_install_message: |
631
- Milk Tea 0.3.42 installed!
632
+ Milk Tea 0.4.0 installed!
632
633
 
633
634
  System requirements:
634
635
  - A C compiler (gcc or clang) must be available on PATH