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.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "types/layout"
4
+ require_relative "compile_time/method_folding"
4
5
 
5
6
  module MilkTea
6
7
  module CompileTime
@@ -9,6 +10,10 @@ module MilkTea
9
10
  # Carries the value of a `return` statement out of the block evaluator as
10
11
  # an ordinary value instead of an exception; callers unwrap it when present.
11
12
  ReturnOutcome = Data.define(:value)
13
+ # Signals that a `break` was executed inside a compile-time loop body.
14
+ BreakOutcome = Data.define(:value)
15
+ # Signals that a `continue` was executed inside a compile-time loop body.
16
+ ContinueOutcome = Data.define(:value)
12
17
 
13
18
  class Error < StandardError
14
19
  def code
@@ -93,7 +98,7 @@ module MilkTea
93
98
  start_val = evaluate(expression.start_expr)
94
99
  end_val = evaluate(expression.end_expr)
95
100
  start_val.is_a?(Integer) && end_val.is_a?(Integer) ? (start_val...end_val).to_a : nil
96
- when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
101
+ when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral, AST::CharLiteral
97
102
  expression.value
98
103
  when AST::StringLiteral
99
104
  expression.value
@@ -133,11 +138,98 @@ module MilkTea
133
138
  return unless CompileTime.boolean_value?(condition)
134
139
 
135
140
  evaluate(condition ? expression.then_expression : expression.else_expression)
141
+ when AST::MatchExpr
142
+ evaluate_match_expression(expression)
143
+ when AST::IndexAccess
144
+ evaluate_index_access(expression)
145
+ when AST::PrefixCast
146
+ evaluate_prefix_cast(expression)
136
147
  else
137
148
  nil
138
149
  end
139
150
  end
140
151
 
152
+ def evaluate_prefix_cast(expression)
153
+ operand = evaluate(expression.expression)
154
+ operand = 1 if operand == true
155
+ operand = 0 if operand == false
156
+ return nil unless operand.is_a?(Numeric)
157
+
158
+ target_type = begin
159
+ @resolve_type_ref&.call(expression.target_type)
160
+ rescue SemanticError
161
+ nil
162
+ end
163
+ return nil unless target_type.is_a?(Types::Primitive)
164
+
165
+ if target_type.boolean?
166
+ return operand != 0
167
+ end
168
+
169
+ if target_type.integer?
170
+ return nil unless target_type.integer_width
171
+
172
+ value = operand.is_a?(Float) ? operand.truncate : operand
173
+ return nil unless value.is_a?(Integer)
174
+
175
+ return wrap_integer(value, target_type.integer_width, target_type.signed_integer?)
176
+ end
177
+
178
+ return nil unless target_type.float?
179
+
180
+ value = operand.to_f
181
+ target_type.name == "float" ? float32_round(value) : value
182
+ end
183
+
184
+ def wrap_integer(value, width, signed)
185
+ mask = (1 << width) - 1
186
+ wrapped = value & mask
187
+ return wrapped unless signed
188
+
189
+ half = 1 << (width - 1)
190
+ wrapped >= half ? wrapped - (1 << width) : wrapped
191
+ end
192
+
193
+ def float32_round(value)
194
+ [value].pack("f").unpack1("f")
195
+ end
196
+
197
+ def evaluate_index_access(expression)
198
+ receiver = evaluate(expression.receiver)
199
+ return nil if receiver.nil?
200
+
201
+ if expression.index.is_a?(AST::RangeExpr)
202
+ start_val = evaluate(expression.index.start_expr)
203
+ end_val = evaluate(expression.index.end_expr)
204
+ return nil unless start_val.is_a?(Integer) && end_val.is_a?(Integer)
205
+
206
+ return receiver[start_val...end_val] if receiver.is_a?(Array) || receiver.is_a?(String)
207
+
208
+ return nil
209
+ end
210
+
211
+ index = evaluate(expression.index)
212
+ return nil unless index.is_a?(Integer)
213
+ return receiver[index] if receiver.is_a?(Array)
214
+ return receiver.getbyte(index) if receiver.is_a?(String)
215
+
216
+ nil
217
+ end
218
+
219
+ def evaluate_match_expression(expression)
220
+ scrutinee = evaluate(expression.expression)
221
+ return nil unless scrutinee
222
+
223
+ expression.arms.each do |arm|
224
+ wildcard = arm.pattern.is_a?(AST::Identifier) && arm.pattern.name == "_"
225
+ if wildcard || CompileTime.equality_result(scrutinee, evaluate(arm.pattern)) == true
226
+ return evaluate(arm.value)
227
+ end
228
+ end
229
+
230
+ nil
231
+ end
232
+
141
233
  def resolve_layout_type(type_ref)
142
234
  result = begin
143
235
  @resolve_type_ref&.call(type_ref)
@@ -207,7 +299,13 @@ module MilkTea
207
299
  result = CompileTime.equality_result(left, right)
208
300
  result.nil? ? nil : !result
209
301
  when "+"
210
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
302
+ if left.is_a?(String) && right.is_a?(String)
303
+ left + right
304
+ elsif left.is_a?(Numeric) && right.is_a?(Numeric)
305
+ left + right
306
+ else
307
+ nil
308
+ end
211
309
  when "-"
212
310
  left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
213
311
  when "*"
@@ -251,9 +349,10 @@ module MilkTea
251
349
  class BlockContext
252
350
  attr_reader :checker
253
351
 
254
- def initialize(checker, initial_variables: nil)
352
+ def initialize(checker, initial_variables: nil, variable_types: nil)
255
353
  @checker = checker
256
354
  @variables = initial_variables || {}
355
+ @variable_types = variable_types || {}
257
356
  end
258
357
 
259
358
  def evaluate_block(statements, scopes: nil)
@@ -261,7 +360,7 @@ module MilkTea
261
360
 
262
361
  statements.each do |statement|
263
362
  outcome = evaluate_statement(statement, scopes:)
264
- return outcome if outcome.is_a?(ReturnOutcome)
363
+ return outcome if control_outcome?(outcome)
265
364
 
266
365
  result = outcome
267
366
  end
@@ -269,6 +368,10 @@ module MilkTea
269
368
  result
270
369
  end
271
370
 
371
+ def control_outcome?(outcome)
372
+ outcome.is_a?(ReturnOutcome) || outcome.is_a?(BreakOutcome) || outcome.is_a?(ContinueOutcome)
373
+ end
374
+
272
375
  def evaluate_statement(statement, scopes:)
273
376
  case statement
274
377
  when AST::LocalDecl
@@ -288,9 +391,13 @@ module MilkTea
288
391
  evaluate_if(statement, scopes:)
289
392
  when AST::ExpressionStmt
290
393
  evaluate_expression(statement.expression, scopes:)
291
- when AST::PassStmt, AST::BreakStmt, AST::ContinueStmt
394
+ when AST::PassStmt
292
395
  # no-op at compile time
293
396
  nil
397
+ when AST::BreakStmt
398
+ BreakOutcome.new(nil)
399
+ when AST::ContinueStmt
400
+ ContinueOutcome.new(nil)
294
401
  when AST::EmitStmt
295
402
  # emitted declarations are collected during lowering
296
403
  nil
@@ -312,9 +419,25 @@ module MilkTea
312
419
  @checker.evaluate_compile_time_const_value(id_expr, scopes:)
313
420
  },
314
421
  resolve_member_access: ->(ma_expr) {
422
+ if ma_expr.receiver.is_a?(AST::Identifier) && @variables.key?(ma_expr.receiver.name)
423
+ value = @variables[ma_expr.receiver.name]
424
+ case value
425
+ when Hash
426
+ return value[ma_expr.member] if value.key?(ma_expr.member)
427
+ when Array
428
+ if ma_expr.member =~ /\A_(\d+)\z/
429
+ return value[Regexp.last_match(1).to_i]
430
+ end
431
+ return value.length if ma_expr.member == "len"
432
+ when String
433
+ return value.length if ma_expr.member == "len"
434
+ end
435
+ end
436
+
315
437
  @checker.evaluate_compile_time_const_value(ma_expr, scopes:)
316
438
  },
317
439
  resolve_call: ->(call_expr) { resolve_compile_time_call(call_expr, scopes:) },
440
+ resolve_type_ref: ->(type_ref) { @checker.resolve_type_ref(type_ref) },
318
441
  )
319
442
  end
320
443
  end
@@ -323,26 +446,153 @@ module MilkTea
323
446
  return nil unless decl.value
324
447
 
325
448
  value = evaluate_expression(decl.value, scopes:)
449
+ return value unless value
450
+
451
+ if decl.else_body
452
+ return evaluate_guard_local_decl(decl, value, scopes:)
453
+ end
454
+
455
+ if decl.destructure_bindings&.any?
456
+ return evaluate_destructure_local_decl(decl, value)
457
+ end
458
+
459
+ @variables[decl.name] = value
460
+ @variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
461
+ value
462
+ end
463
+
464
+ def evaluate_guard_local_decl(decl, value, scopes:)
465
+ if value.is_a?(CompileTime::VariantValue)
466
+ case value.arm
467
+ when "some", "success"
468
+ @variables[decl.name] = value.fields["value"]
469
+ @variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
470
+ return value
471
+ when "none", "failure"
472
+ @variables[decl.else_binding.name] = value.fields["error"] if decl.else_binding
473
+ return evaluate_block(decl.else_body, scopes:)
474
+ end
475
+ return nil
476
+ end
477
+
326
478
  @variables[decl.name] = value
479
+ @variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
327
480
  value
328
481
  end
329
482
 
483
+ def evaluate_destructure_local_decl(decl, value)
484
+ if value.is_a?(Array)
485
+ decl.destructure_bindings.each_with_index do |name, idx|
486
+ next if name == "_"
487
+
488
+ @variables[name] = value[idx]
489
+ end
490
+ return value
491
+ end
492
+
493
+ if value.is_a?(Hash)
494
+ field_names = destructure_field_names(decl.destructure_type_name)
495
+ decl.destructure_bindings.each_with_index do |name, idx|
496
+ next if name == "_"
497
+
498
+ field_name = field_names&.[](idx) || name
499
+ @variables[name] = value[field_name]
500
+ end
501
+ return value
502
+ end
503
+
504
+ nil
505
+ end
506
+
507
+ def destructure_field_names(type_name)
508
+ return nil unless type_name
509
+ return nil unless @checker.respond_to?(:comptime_struct_field_names)
510
+
511
+ @checker.comptime_struct_field_names(type_name)
512
+ rescue StandardError
513
+ nil
514
+ end
515
+
516
+ def compile_time_decl_type(expression, scopes:)
517
+ return nil unless @checker.respond_to?(:comptime_expression_type)
518
+
519
+ @checker.comptime_expression_type(expression, scopes:)
520
+ rescue StandardError
521
+ nil
522
+ end
523
+
330
524
  def evaluate_assignment(assignment, scopes:)
331
525
  value = evaluate_expression(assignment.value, scopes:)
526
+ return nil unless value
527
+
332
528
  case assignment.target
333
529
  when AST::Identifier
530
+ return nil unless @variables.key?(assignment.target.name)
531
+
334
532
  if assignment.operator != "="
335
533
  current = @variables[assignment.target.name]
336
534
  value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
337
535
  end
338
536
  @variables[assignment.target.name] = value
537
+ when AST::MemberAccess
538
+ return nil unless evaluate_member_assignment(assignment, value, scopes:)
539
+ when AST::IndexAccess
540
+ return nil unless evaluate_index_assignment(assignment, value, scopes:)
339
541
  end
340
542
  value
341
543
  end
342
544
 
545
+ def evaluate_member_assignment(assignment, value, scopes:)
546
+ target = assignment.target
547
+ return nil unless target.receiver.is_a?(AST::Identifier)
548
+ return nil unless @variables.key?(target.receiver.name)
549
+
550
+ receiver = @variables[target.receiver.name]
551
+ return nil unless receiver.is_a?(Hash)
552
+
553
+ if assignment.operator != "="
554
+ current = receiver[target.member]
555
+ value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
556
+ return nil unless value
557
+ end
558
+
559
+ updated = receiver.dup
560
+ updated[target.member] = value
561
+ @variables[target.receiver.name] = updated
562
+ end
563
+
564
+ def evaluate_index_assignment(assignment, value, scopes:)
565
+ target = assignment.target
566
+ return nil unless target.receiver.is_a?(AST::Identifier)
567
+ return nil unless @variables.key?(target.receiver.name)
568
+
569
+ receiver = @variables[target.receiver.name]
570
+ return nil unless receiver.is_a?(Array)
571
+
572
+ index = evaluate_expression(target.index, scopes:)
573
+ return nil unless index.is_a?(Integer)
574
+
575
+ if assignment.operator != "="
576
+ current = receiver[index]
577
+ value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
578
+ return nil unless value
579
+ end
580
+
581
+ updated = receiver.dup
582
+ updated[index] = value
583
+ @variables[target.receiver.name] = updated
584
+ end
585
+
343
586
  def apply_compile_time_binary(operator, left, right)
344
587
  case operator
345
- when "+" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
588
+ when "+"
589
+ if left.is_a?(String) && right.is_a?(String)
590
+ left + right
591
+ elsif left.is_a?(Numeric) && right.is_a?(Numeric)
592
+ left + right
593
+ else
594
+ nil
595
+ end
346
596
  when "-" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
347
597
  when "*" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
348
598
  when "/" then left.is_a?(Numeric) && right.is_a?(Numeric) && !zero_numeric?(right) ? left / right : nil
@@ -371,7 +621,11 @@ module MilkTea
371
621
 
372
622
  statement.body.each do |body_stmt|
373
623
  outcome = evaluate_statement(body_stmt, scopes:)
374
- return outcome if outcome.is_a?(ReturnOutcome)
624
+ case outcome
625
+ when ReturnOutcome then return outcome
626
+ when BreakOutcome then return result
627
+ when ContinueOutcome then break
628
+ end
375
629
  end
376
630
  iterations += 1
377
631
  end
@@ -392,32 +646,35 @@ module MilkTea
392
646
  @variables[loop_var_name] = element
393
647
  statement.body.each do |body_stmt|
394
648
  outcome = evaluate_statement(body_stmt, scopes:)
395
- return outcome if outcome.is_a?(ReturnOutcome)
649
+ case outcome
650
+ when ReturnOutcome then return outcome
651
+ when BreakOutcome then return result
652
+ when ContinueOutcome then break
653
+ end
396
654
  end
397
655
  end
398
656
 
399
657
  result
400
658
  end
401
659
 
660
+ def run_body(body, scopes:, fallback: nil)
661
+ body.each do |body_stmt|
662
+ outcome = evaluate_statement(body_stmt, scopes:)
663
+ return outcome if control_outcome?(outcome)
664
+ end
665
+ fallback
666
+ end
667
+
402
668
  def evaluate_if(statement, scopes:)
403
669
  statement.branches.each do |branch|
404
670
  condition = evaluate_expression(branch.condition, scopes:)
405
- if CompileTime.boolean_value?(condition) && condition
406
- branch.body.each do |body_stmt|
407
- outcome = evaluate_statement(body_stmt, scopes:)
408
- return outcome if outcome.is_a?(ReturnOutcome)
409
- end
410
- return condition
411
- end
412
- end
671
+ next unless CompileTime.boolean_value?(condition) && condition
413
672
 
414
- if statement.else_body
415
- statement.else_body.each do |body_stmt|
416
- outcome = evaluate_statement(body_stmt, scopes:)
417
- return outcome if outcome.is_a?(ReturnOutcome)
418
- end
673
+ return run_body(branch.body, scopes:, fallback: condition)
419
674
  end
420
675
 
676
+ return run_body(statement.else_body, scopes:) if statement.else_body
677
+
421
678
  nil
422
679
  end
423
680
 
@@ -428,11 +685,7 @@ module MilkTea
428
685
  statement.arms.each do |arm|
429
686
  wildcard = arm.pattern.is_a?(AST::Identifier) && arm.pattern.name == "_"
430
687
  if wildcard || CompileTime.equality_result(scrutinee, evaluate_expression(arm.pattern, scopes:)) == true
431
- arm.body.each do |body_stmt|
432
- outcome = evaluate_statement(body_stmt, scopes:)
433
- return outcome if outcome.is_a?(ReturnOutcome)
434
- end
435
- return scrutinee
688
+ return run_body(arm.body, scopes:, fallback: scrutinee)
436
689
  end
437
690
  end
438
691
 
@@ -443,6 +696,9 @@ module MilkTea
443
696
  result = try_const_function_call(call_expr, scopes:)
444
697
  return result if result
445
698
 
699
+ result = try_const_method_call(call_expr, scopes:)
700
+ return result if result
701
+
446
702
  result = try_struct_constructor_call(call_expr, scopes:)
447
703
  return result if result
448
704
 
@@ -452,6 +708,27 @@ module MilkTea
452
708
  @checker.evaluate_compile_time_const_value(call_expr, scopes:)
453
709
  end
454
710
 
711
+ def try_const_method_call(call_expr, scopes:)
712
+ return unless call_expr.callee.is_a?(AST::MemberAccess)
713
+ return unless @checker.respond_to?(:comptime_method_binding_for_receiver)
714
+ return unless @checker.respond_to?(:comptime_const_method_body)
715
+
716
+ receiver = call_expr.callee.receiver
717
+ return unless receiver.is_a?(AST::Identifier)
718
+ return unless @variables.key?(receiver.name)
719
+
720
+ receiver_value = @variables[receiver.name]
721
+ return nil if receiver_value.nil? || receiver_value.is_a?(Types::Base)
722
+
723
+ receiver_type = @variable_types[receiver.name]
724
+ return nil unless receiver_type
725
+
726
+ binding = @checker.comptime_method_binding_for_receiver(receiver_type, call_expr.callee.member)
727
+ return nil unless binding
728
+
729
+ @checker.comptime_const_method_body(binding, call_expr.arguments, scopes:, receiver_value:)
730
+ end
731
+
455
732
  def try_const_function_call(call_expr, scopes:)
456
733
  return unless call_expr.callee.is_a?(AST::Identifier)
457
734
 
@@ -400,6 +400,8 @@ module MilkTea
400
400
  AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.element_type))], nullable: false)
401
401
  when Types::Task
402
402
  AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["Task"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.result_type))], nullable: false)
403
+ when Types::StringView
404
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["str"]), arguments: [], nullable: false)
403
405
  when Types::TypeVar
404
406
  AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
405
407
  when Types::StructInstance
@@ -1364,13 +1364,19 @@ module MilkTea
1364
1364
  when AST::IndexAccess
1365
1365
  receiver_type = infer_expression_type(expression.receiver, env:)
1366
1366
  receiver = lower_expression(expression.receiver, env:)
1367
- index = lower_expression(expression.index, env:)
1368
- if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
1369
- IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1370
- elsif receiver_type.is_a?(Types::Span)
1371
- IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1367
+ if expression.index.is_a?(AST::RangeExpr)
1368
+ lower_range_index_access(receiver, receiver_type, expression.index, env:, type:)
1372
1369
  else
1373
- IR::Index.new(receiver:, index:, type:)
1370
+ index = lower_expression(expression.index, env:)
1371
+ if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
1372
+ IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
1373
+ elsif receiver_type.is_a?(Types::Span)
1374
+ IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
1375
+ elsif receiver_type.is_a?(Types::StringView)
1376
+ IR::Call.new(callee: "mt_str_index", arguments: [receiver, index], type:)
1377
+ else
1378
+ IR::Index.new(receiver:, index:, type:)
1379
+ end
1374
1380
  end
1375
1381
  when AST::UnaryOp
1376
1382
  raise LoweringError.new("propagation expressions must be prepared before direct lowering", line: 0, column: 0, path: @ctx.current_analysis_path) if expression.operator == "?"
@@ -1452,6 +1458,25 @@ module MilkTea
1452
1458
  end
1453
1459
  end
1454
1460
 
1461
+ def lower_range_index_access(receiver, receiver_type, range, env:, type:)
1462
+ start = lower_range_index_bound(range.start_expr, env:)
1463
+ stop = lower_range_index_bound(range.end_expr, env:)
1464
+
1465
+ if receiver_type.is_a?(Types::StringView)
1466
+ return IR::Call.new(callee: "mt_str_slice", arguments: [receiver, start, stop], type:)
1467
+ end
1468
+
1469
+ span_type = range_index_result_type(receiver_type)
1470
+ span_argument = receiver_type.is_a?(Types::Span) ? receiver : lower_array_to_span_expression(receiver, span_type)
1471
+ IR::Call.new(callee: span_slice_callee(span_type), arguments: [span_argument, start, stop], type: span_type)
1472
+ end
1473
+
1474
+ def lower_range_index_bound(bound, env:)
1475
+ ptr_uint = @ctx.types.fetch("ptr_uint")
1476
+ lowered = lower_expression(bound, env:, expected_type: ptr_uint)
1477
+ lowered.type == ptr_uint ? lowered : IR::Cast.new(target_type: ptr_uint, expression: lowered, type: ptr_uint)
1478
+ end
1479
+
1455
1480
  def lower_member_access(expression, env:, type:)
1456
1481
  if (type_expr = resolve_type_expression(expression.receiver))
1457
1482
  if type_expr.is_a?(Types::Variant)
@@ -206,6 +206,9 @@ module MilkTea
206
206
  when AST::IndexAccess
207
207
  collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
208
208
  collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
209
+ when AST::RangeExpr
210
+ collect_proc_captures_from_expression(expression.start_expr, env, local_scopes, captures)
211
+ collect_proc_captures_from_expression(expression.end_expr, env, local_scopes, captures)
209
212
  when AST::Specialization
210
213
  collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
211
214
  expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
@@ -3,6 +3,7 @@
3
3
  module MilkTea
4
4
  module Lowering
5
5
  module Resolve
6
+ include CompileTime::MethodFolding
6
7
  PASS_THROUGH_BUILTINS = {
7
8
  "fatal" => :fatal,
8
9
  "ref_of" => :ref_of,
@@ -786,6 +787,9 @@ module MilkTea
786
787
  raise LoweringError.new("unknown member #{expression.member}", line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
787
788
  when AST::IndexAccess
788
789
  receiver_type = infer_expression_type(expression.receiver, env:)
790
+ if expression.index.is_a?(AST::RangeExpr)
791
+ return range_index_result_type(receiver_type)
792
+ end
789
793
  index_type = infer_expression_type(expression.index, env:)
790
794
  infer_index_result_type(receiver_type, index_type)
791
795
  when AST::UnaryOp
@@ -1555,7 +1559,7 @@ module MilkTea
1555
1559
  end
1556
1560
  resolve_current_module_const_value(identifier_expression.name)
1557
1561
  end,
1558
- resolve_member_access: lambda { |ma| nil },
1562
+ resolve_member_access: lambda { |ma| compile_time_const_value(ma, env:) },
1559
1563
  resolve_type_ref: lambda { |tr| resolve_type_ref(tr) },
1560
1564
  resolve_call: lambda { |ce| evaluate_compile_time_call(ce, env:) },
1561
1565
  ))
@@ -1570,6 +1574,15 @@ module MilkTea
1570
1574
  when "name" then next receiver_value.member_name
1571
1575
  when "value" then next receiver_value.member_value
1572
1576
  end
1577
+ when Hash
1578
+ next receiver_value[member_access_expression.member] if receiver_value.key?(member_access_expression.member)
1579
+ when Array
1580
+ if member_access_expression.member =~ /\A_(\d+)\z/
1581
+ next receiver_value[Regexp.last_match(1).to_i]
1582
+ end
1583
+ next receiver_value.length if member_access_expression.member == "len"
1584
+ when String
1585
+ next receiver_value.length if member_access_expression.member == "len"
1573
1586
  end
1574
1587
  end
1575
1588
 
@@ -1591,6 +1604,11 @@ module MilkTea
1591
1604
 
1592
1605
  def evaluate_compile_time_call(expression, env:)
1593
1606
  case expression.callee
1607
+ when AST::MemberAccess
1608
+ if (method_binding = comptime_method_binding(expression.callee, env))
1609
+ receiver_value = comptime_member_receiver_value(expression.callee.receiver, env)
1610
+ return comptime_const_method_body(method_binding, expression.arguments, scopes: nil, receiver_value:)
1611
+ end
1594
1612
  when AST::Identifier
1595
1613
  case expression.callee.name
1596
1614
  when "field_of"
@@ -1791,12 +1809,51 @@ module MilkTea
1791
1809
  initial_vars[param.name] = arg_value
1792
1810
  end
1793
1811
 
1812
+ variable_types = func.body_params.each_with_object({}) do |param, acc|
1813
+ acc[param.name] = param.type
1814
+ end
1794
1815
  evaluator = ConstFnLowerEvaluator.new(self)
1795
- ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars)
1816
+ ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars, variable_types: variable_types)
1796
1817
  result = ctx.evaluate_block(func.ast.body, scopes: nil)
1797
1818
  result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
1798
1819
  end
1799
1820
 
1821
+ def comptime_methods_map
1822
+ @ctx.methods
1823
+ end
1824
+
1825
+ def comptime_methods_map_by_to_s(type)
1826
+ entry = @ctx.methods.find { |k, _| k.to_s == type.to_s }
1827
+ entry&.last
1828
+ end
1829
+
1830
+ def comptime_scoped_value_type(name, ctx)
1831
+ return nil unless ctx
1832
+
1833
+ binding = lookup_value(name, ctx)
1834
+ binding[:type] if binding && binding[:type]
1835
+ end
1836
+
1837
+ def comptime_value_type(name)
1838
+ @ctx.values[name]&.type
1839
+ end
1840
+
1841
+ def comptime_const_function(name)
1842
+ @ctx.functions[name]
1843
+ end
1844
+
1845
+ def comptime_eval(expression, _scopes)
1846
+ compile_time_const_value(expression, env: empty_env)
1847
+ end
1848
+
1849
+ def comptime_block_context(initial_vars, variable_types)
1850
+ ::MilkTea::CompileTime::BlockContext.new(ConstFnLowerEvaluator.new(self), initial_variables: initial_vars, variable_types: variable_types)
1851
+ end
1852
+
1853
+ def comptime_raise_error(error)
1854
+ raise error
1855
+ end
1856
+
1800
1857
  class ConstFnLowerEvaluator
1801
1858
  def initialize(lowerer)
1802
1859
  @lowerer = lowerer
@@ -1813,6 +1870,26 @@ module MilkTea
1813
1870
  def top_level_function(name)
1814
1871
  @lowerer.instance_variable_get(:@ctx).functions&.[](name)
1815
1872
  end
1873
+
1874
+ def resolve_type_ref(type_ref)
1875
+ @lowerer.resolve_type_ref(type_ref)
1876
+ end
1877
+
1878
+ def comptime_struct_field_names(type_name)
1879
+ @lowerer.comptime_struct_field_names(type_name)
1880
+ end
1881
+
1882
+ def comptime_method_binding_for_receiver(receiver_type, member)
1883
+ @lowerer.comptime_method_binding_for_receiver(receiver_type, member)
1884
+ end
1885
+
1886
+ def comptime_const_method_body(binding, arguments, scopes: nil, receiver_value: nil)
1887
+ @lowerer.comptime_const_method_body(binding, arguments, scopes:, receiver_value:)
1888
+ end
1889
+
1890
+ def comptime_expression_type(expression, scopes: nil)
1891
+ @lowerer.comptime_expression_type(expression, scopes:)
1892
+ end
1816
1893
  end
1817
1894
 
1818
1895
  def evaluate_reflection_target_argument(expression, env:)