mt-lang 0.2.21 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +24 -1
- data/docs/index.html +2 -1
- data/docs/language-design.md +2 -0
- data/docs/language-manual.md +16 -1
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/ast.rb +2 -2
- data/lib/milk_tea/core/c_backend/expressions.rb +11 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +49 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +7 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +12 -0
- data/lib/milk_tea/core/c_backend.rb +8 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +4 -0
- data/lib/milk_tea/core/compile_time.rb +16 -0
- data/lib/milk_tea/core/ir.rb +1 -0
- data/lib/milk_tea/core/keywords.rb +1 -1
- data/lib/milk_tea/core/lowering/block.rb +48 -16
- data/lib/milk_tea/core/lowering/calls.rb +75 -1
- data/lib/milk_tea/core/lowering/declarations.rb +7 -0
- data/lib/milk_tea/core/lowering/expressions.rb +39 -8
- data/lib/milk_tea/core/lowering/functions.rb +1 -1
- data/lib/milk_tea/core/lowering/resolve.rb +70 -5
- data/lib/milk_tea/core/lowering/utils.rb +26 -0
- data/lib/milk_tea/core/module_binder.rb +4 -0
- data/lib/milk_tea/core/parser/expressions.rb +11 -3
- data/lib/milk_tea/core/parser/statements.rb +2 -2
- data/lib/milk_tea/core/parser/types.rb +8 -1
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +97 -4
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +52 -2
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +7 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +40 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +11 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +21 -4
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +23 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +37 -0
- data/lib/milk_tea/core/types/layout.rb +7 -0
- data/lib/milk_tea/core/types/predicates.rb +44 -3
- data/lib/milk_tea/core/types/registry.rb +4 -0
- data/lib/milk_tea/core/types/types.rb +42 -0
- data/lib/milk_tea/core/types/visitor.rb +2 -0
- data/lib/milk_tea/lsp/server/completion.rb +1 -1
- data/lib/milk_tea/lsp/server/hover.rb +8 -1
- data/std/asset_pack.mt +13 -13
- data/std/binary.mt +1 -1
- data/std/hash.mt +11 -11
- data/std/net/session.mt +5 -5
- data/std/random.mt +23 -31
- data/std/raylib.mt +14 -14
- metadata +2 -2
|
@@ -291,6 +291,8 @@ module MilkTea
|
|
|
291
291
|
|
|
292
292
|
return Types::Registry.soa(arguments[0], count: arguments[1].value) if name == "SoA"
|
|
293
293
|
|
|
294
|
+
return Types::Registry.simd(arguments[0], lane_count: arguments[1].value) if name == "simd"
|
|
295
|
+
|
|
294
296
|
arguments = [type_ref.lifetime] + arguments if name == "ref" && type_ref.lifetime
|
|
295
297
|
return Types::Registry.generic_instance(name, arguments)
|
|
296
298
|
end
|
|
@@ -684,6 +686,7 @@ module MilkTea
|
|
|
684
686
|
return true if matrix_type?(type)
|
|
685
687
|
return true if quaternion_type?(type)
|
|
686
688
|
return true if soa_type?(type)
|
|
689
|
+
return true if simd_type?(type)
|
|
687
690
|
return true if atomic_type?(type)
|
|
688
691
|
|
|
689
692
|
raise_sema_error("#{operation} does not support type #{type}")
|
|
@@ -717,6 +720,10 @@ module MilkTea
|
|
|
717
720
|
type.is_a?(Types::SoA)
|
|
718
721
|
end
|
|
719
722
|
|
|
723
|
+
def simd_type?(type)
|
|
724
|
+
type.is_a?(Types::Simd)
|
|
725
|
+
end
|
|
726
|
+
|
|
720
727
|
def array_type?(type)
|
|
721
728
|
type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
|
|
722
729
|
!type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments[1].is_a?(Types::LiteralTypeArg)
|
|
@@ -929,6 +936,10 @@ module MilkTea
|
|
|
929
936
|
return receiver_type.element_type
|
|
930
937
|
end
|
|
931
938
|
|
|
939
|
+
if simd_type?(receiver_type)
|
|
940
|
+
return receiver_type.element_type
|
|
941
|
+
end
|
|
942
|
+
|
|
932
943
|
if pointer_type?(receiver_type)
|
|
933
944
|
require_unsafe!("pointer indexing requires unsafe") unless own_type?(receiver_type)
|
|
934
945
|
|
|
@@ -511,15 +511,15 @@ module MilkTea
|
|
|
511
511
|
)
|
|
512
512
|
end
|
|
513
513
|
when "%="
|
|
514
|
-
unless common_integer_type(target_type, value_type) == target_type
|
|
514
|
+
unless common_integer_type(target_type, value_type) == target_type || simd_mod_result(target_type, value_type)
|
|
515
515
|
raise_sema_error("operator #{statement.operator} requires compatible integer types, got #{target_type} and #{value_type}")
|
|
516
516
|
end
|
|
517
517
|
when "&=", "|=", "^="
|
|
518
|
-
unless target_type == value_type && bitwise_type?(target_type)
|
|
518
|
+
unless (target_type == value_type && bitwise_type?(target_type)) || (simd_type?(target_type) && simd_type?(value_type) && target_type == value_type)
|
|
519
519
|
raise_sema_error("operator #{statement.operator} requires matching integer or flags types, got #{target_type} and #{value_type}")
|
|
520
520
|
end
|
|
521
521
|
when "<<=", ">>="
|
|
522
|
-
unless target_type.is_a?(Types::Primitive) && target_type.integer? && value_type.is_a?(Types::Primitive) && value_type.integer?
|
|
522
|
+
unless (target_type.is_a?(Types::Primitive) && target_type.integer? && value_type.is_a?(Types::Primitive) && value_type.integer?) || simd_shift_result(target_type, value_type)
|
|
523
523
|
raise_sema_error("operator #{statement.operator} requires integer operands, got #{target_type} and #{value_type}")
|
|
524
524
|
end
|
|
525
525
|
else
|
|
@@ -645,8 +645,25 @@ module MilkTea
|
|
|
645
645
|
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
646
646
|
next
|
|
647
647
|
end
|
|
648
|
+
if arm.pattern.is_a?(AST::RangeExpr)
|
|
649
|
+
start_val = arm.pattern.start_expr
|
|
650
|
+
end_val = arm.pattern.end_expr
|
|
651
|
+
unless (start_val.is_a?(AST::IntegerLiteral) || start_val.is_a?(AST::CharLiteral)) &&
|
|
652
|
+
(end_val.is_a?(AST::IntegerLiteral) || end_val.is_a?(AST::CharLiteral))
|
|
653
|
+
raise_sema_error("range match arm bounds must be integer or char literals, got #{start_val.class.name}..#{end_val.class.name}")
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
raise_sema_error("range match arm start #{start_val.value} must be <= end #{end_val.value}") if start_val.value > end_val.value
|
|
657
|
+
|
|
658
|
+
range_key = [start_val.value, end_val.value]
|
|
659
|
+
raise_sema_error("duplicate match arm range #{range_key[0]}..#{range_key[1]}") if covered_values.key?(range_key)
|
|
660
|
+
|
|
661
|
+
covered_values[range_key] = true
|
|
662
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
663
|
+
next
|
|
664
|
+
end
|
|
648
665
|
unless arm.pattern.is_a?(AST::IntegerLiteral) || arm.pattern.is_a?(AST::CharLiteral)
|
|
649
|
-
raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, or _, got #{arm.pattern.class.name}")
|
|
666
|
+
raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, range, or _, got #{arm.pattern.class.name}")
|
|
650
667
|
end
|
|
651
668
|
value = arm.pattern.value
|
|
652
669
|
raise_sema_error("duplicate match arm value #{value}") if covered_values.key?(value)
|
|
@@ -324,6 +324,29 @@ module MilkTea
|
|
|
324
324
|
def evaluate_compile_time_call(expression, scopes: nil)
|
|
325
325
|
case expression.callee
|
|
326
326
|
when AST::Identifier
|
|
327
|
+
if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct)
|
|
328
|
+
fields = {}
|
|
329
|
+
expression.arguments.each do |argument|
|
|
330
|
+
val = CompileTime.evaluate(argument.value, resolve_identifier: lambda { |id|
|
|
331
|
+
if scopes
|
|
332
|
+
binding = lookup_value(id.name, scopes)
|
|
333
|
+
return binding.const_value unless binding&.const_value.nil?
|
|
334
|
+
end
|
|
335
|
+
resolve_current_module_const_value(id.name)
|
|
336
|
+
}, resolve_member_access: lambda { |ma|
|
|
337
|
+
if (receiver_type = resolve_type_expression(ma.receiver))
|
|
338
|
+
next resolve_enum_member_const_value(receiver_type, ma.member)
|
|
339
|
+
end
|
|
340
|
+
nil
|
|
341
|
+
}, resolve_call: lambda { |inner_call|
|
|
342
|
+
evaluate_compile_time_call(inner_call, scopes:)
|
|
343
|
+
})
|
|
344
|
+
return nil unless val
|
|
345
|
+
fields[argument.name] = val
|
|
346
|
+
end
|
|
347
|
+
return fields
|
|
348
|
+
end
|
|
349
|
+
|
|
327
350
|
case expression.callee.name
|
|
328
351
|
when "field_of"
|
|
329
352
|
evaluate_field_of_call(expression.arguments, scopes: scopes || [])
|
|
@@ -218,12 +218,49 @@ module MilkTea
|
|
|
218
218
|
return vector_op_result(operator, left_type, right_type) if vector_type?(left_type) || vector_type?(right_type)
|
|
219
219
|
return matrix_op_result(operator, left_type, right_type) if matrix_type?(left_type) || matrix_type?(right_type)
|
|
220
220
|
return quaternion_op_result(operator, left_type, right_type) if quaternion_type?(left_type) || quaternion_type?(right_type)
|
|
221
|
+
return simd_op_result(operator, left_type, right_type) if simd_type?(left_type) || simd_type?(right_type)
|
|
221
222
|
|
|
222
223
|
nil
|
|
223
224
|
end
|
|
224
225
|
|
|
225
226
|
private
|
|
226
227
|
|
|
228
|
+
def simd_op_result(operator, left_type, right_type)
|
|
229
|
+
if simd_type?(left_type) && simd_type?(right_type) && left_type.element_type == right_type.element_type && left_type.lane_count == right_type.lane_count
|
|
230
|
+
return left_type if operator == "+" || operator == "-" || operator == "*" || operator == "/"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
if simd_type?(left_type) && right_type.numeric? && right_type == left_type.element_type
|
|
234
|
+
return left_type if operator == "*" || operator == "/"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if left_type.numeric? && simd_type?(right_type) && left_type == right_type.element_type
|
|
238
|
+
return right_type if operator == "*"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def simd_bitwise_result(left_type, right_type)
|
|
245
|
+
return nil unless simd_type?(left_type) && simd_type?(right_type) && left_type == right_type
|
|
246
|
+
|
|
247
|
+
left_type
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def simd_mod_result(left_type, right_type)
|
|
251
|
+
return nil unless simd_type?(left_type) && left_type.element_type.integer?
|
|
252
|
+
return nil unless right_type == left_type.element_type || (simd_type?(right_type) && right_type == left_type)
|
|
253
|
+
|
|
254
|
+
left_type
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def simd_shift_result(left_type, right_type)
|
|
258
|
+
return nil unless simd_type?(left_type) && left_type.element_type.integer?
|
|
259
|
+
return nil unless right_type.is_a?(Types::Primitive) && right_type.integer?
|
|
260
|
+
|
|
261
|
+
left_type
|
|
262
|
+
end
|
|
263
|
+
|
|
227
264
|
def vector_op_result(operator, left_type, right_type)
|
|
228
265
|
if vector_type?(left_type) && vector_type?(right_type) && left_type.element_type == right_type.element_type
|
|
229
266
|
return left_type if operator == "+" || operator == "-"
|
|
@@ -70,6 +70,13 @@ module MilkTea
|
|
|
70
70
|
end
|
|
71
71
|
when Types::GenericInstance
|
|
72
72
|
generic_layout(type, stack)
|
|
73
|
+
when Types::Simd
|
|
74
|
+
element_layout = size_and_alignment(type.element_type, stack)
|
|
75
|
+
return unless element_layout
|
|
76
|
+
|
|
77
|
+
total = element_layout.first * type.lane_count
|
|
78
|
+
alignment = total > 16 ? 32 : 16
|
|
79
|
+
[total, alignment]
|
|
73
80
|
else
|
|
74
81
|
nil
|
|
75
82
|
end
|
|
@@ -4,18 +4,38 @@ module MilkTea
|
|
|
4
4
|
module TypePredicates
|
|
5
5
|
def method_dispatch_receiver_type(receiver_type)
|
|
6
6
|
return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
|
|
7
|
+
|
|
7
8
|
if receiver_type.is_a?(Types::Nullable)
|
|
8
9
|
dispatch_base_type = method_dispatch_receiver_type(receiver_type.base)
|
|
9
10
|
return receiver_type if dispatch_base_type == receiver_type.base
|
|
10
11
|
|
|
11
12
|
return Types::Registry.nullable(dispatch_base_type)
|
|
12
13
|
end
|
|
14
|
+
|
|
15
|
+
case receiver_type
|
|
16
|
+
when Types::Span
|
|
17
|
+
normalized = Types::Registry.span(Types::TypeVar.new("__receiver_arg0"))
|
|
18
|
+
return normalized == receiver_type ? receiver_type : normalized
|
|
19
|
+
when Types::SoA
|
|
20
|
+
normalized = Types::Registry.soa(Types::TypeVar.new("__receiver_arg0"), count: 0)
|
|
21
|
+
return normalized == receiver_type ? receiver_type : normalized
|
|
22
|
+
when Types::Simd
|
|
23
|
+
normalized = Types::Registry.simd(Types::TypeVar.new("__receiver_arg0"), lane_count: 0)
|
|
24
|
+
return normalized == receiver_type ? receiver_type : normalized
|
|
25
|
+
when Types::Task
|
|
26
|
+
normalized = Types::Registry.task(Types::TypeVar.new("__receiver_arg0"))
|
|
27
|
+
return normalized == receiver_type ? receiver_type : normalized
|
|
28
|
+
when Types::Dyn
|
|
29
|
+
normalized = Types::Registry.dyn(Types::TypeVar.new("__receiver_arg0"))
|
|
30
|
+
return normalized == receiver_type ? receiver_type : normalized
|
|
31
|
+
end
|
|
32
|
+
|
|
13
33
|
return receiver_type unless receiver_type.is_a?(Types::GenericInstance)
|
|
14
34
|
|
|
15
35
|
dispatch_receiver_type = Types::Registry.generic_instance(
|
|
16
36
|
receiver_type.name,
|
|
17
|
-
receiver_type.arguments.each_with_index.map do |
|
|
18
|
-
|
|
37
|
+
receiver_type.arguments.each_with_index.map do |_argument, index|
|
|
38
|
+
Types::TypeVar.new("__receiver_arg#{index}")
|
|
19
39
|
end,
|
|
20
40
|
)
|
|
21
41
|
dispatch_receiver_type == receiver_type ? receiver_type : dispatch_receiver_type
|
|
@@ -458,9 +478,11 @@ module MilkTea
|
|
|
458
478
|
actual_count == function_type.params.length
|
|
459
479
|
end
|
|
460
480
|
|
|
461
|
-
def arity_error_message(function_type, name, actual_count)
|
|
481
|
+
def arity_error_message(function_type, name, actual_count, required_count: nil)
|
|
462
482
|
if function_type.is_a?(Types::Function) && function_type.variadic
|
|
463
483
|
"function #{name} expects at least #{function_type.params.length} arguments, got #{actual_count}"
|
|
484
|
+
elsif required_count && required_count < function_type.params.length
|
|
485
|
+
"function #{name} expects #{required_count}..#{function_type.params.length} arguments, got #{actual_count}"
|
|
464
486
|
else
|
|
465
487
|
"function #{name} expects #{function_type.params.length} arguments, got #{actual_count}"
|
|
466
488
|
end
|
|
@@ -564,6 +586,17 @@ module MilkTea
|
|
|
564
586
|
error.call("SoA element type must be a struct with fields") unless arguments.first.respond_to?(:fields) && arguments.first.fields.any?
|
|
565
587
|
error.call("SoA length must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments[1])
|
|
566
588
|
error.call("SoA length must be positive") if integer_type_argument?(arguments[1]) && !arguments[1].value.positive?
|
|
589
|
+
when "simd"
|
|
590
|
+
error.call("simd requires exactly two type arguments") unless arguments.length == 2
|
|
591
|
+
error.call("simd element type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
|
|
592
|
+
error.call("simd element type must be a numeric primitive") unless arguments.first.is_a?(Types::Primitive) && arguments.first.numeric?
|
|
593
|
+
error.call("simd lane count must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments[1])
|
|
594
|
+
error.call("simd lane count must be positive") if integer_type_argument?(arguments[1]) && !arguments[1].value.positive?
|
|
595
|
+
if integer_type_argument?(arguments[1])
|
|
596
|
+
width_bytes = arguments.first.name == "double" || arguments.first.name == "long" || arguments.first.name == "ulong" ? arguments[1].value * 8 : arguments.first.name == "int" || arguments.first.name == "uint" || arguments.first.name == "float" ? arguments[1].value * 4 : arguments.first.name == "short" || arguments.first.name == "ushort" ? arguments[1].value * 2 : arguments[1].value
|
|
597
|
+
valid = [16, 32].include?(width_bytes)
|
|
598
|
+
error.call("simd width must be 128 or 256 bits, got #{width_bytes * 8}") unless valid
|
|
599
|
+
end
|
|
567
600
|
when "str_buffer"
|
|
568
601
|
error.call("str_buffer requires exactly one type argument") unless arguments.length == 1
|
|
569
602
|
error.call("str_buffer capacity must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments.first)
|
|
@@ -598,6 +631,14 @@ module MilkTea
|
|
|
598
631
|
type.arguments.length == 2 && type.arguments[1].is_a?(Types::LiteralTypeArg) && type.arguments[1].value.is_a?(Integer)
|
|
599
632
|
end
|
|
600
633
|
|
|
634
|
+
def soa_type?(type)
|
|
635
|
+
type.is_a?(Types::SoA)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def simd_type?(type)
|
|
639
|
+
type.is_a?(Types::Simd)
|
|
640
|
+
end
|
|
641
|
+
|
|
601
642
|
def array_length(type)
|
|
602
643
|
type.arguments[1].value
|
|
603
644
|
end
|
|
@@ -71,6 +71,10 @@ module MilkTea
|
|
|
71
71
|
_intern([:soa, element_type, count]) { SoA.new(element_type, count: count) }
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def simd(element_type, lane_count:)
|
|
75
|
+
_intern([:simd, element_type, lane_count]) { Simd.new(element_type, lane_count: lane_count) }
|
|
76
|
+
end
|
|
77
|
+
|
|
74
78
|
def lifetime_ref(name)
|
|
75
79
|
LifetimeRef.new(name)
|
|
76
80
|
end
|
|
@@ -1560,6 +1560,48 @@ module MilkTea
|
|
|
1560
1560
|
end
|
|
1561
1561
|
end
|
|
1562
1562
|
|
|
1563
|
+
class Simd < Base
|
|
1564
|
+
attr_reader :name, :element_type, :lane_count, :module_name
|
|
1565
|
+
|
|
1566
|
+
def initialize(element_type, lane_count:)
|
|
1567
|
+
@element_type = element_type
|
|
1568
|
+
@lane_count = lane_count
|
|
1569
|
+
@name = "simd[#{element_type}, #{lane_count}]"
|
|
1570
|
+
@module_name = nil
|
|
1571
|
+
@fields = {}.freeze
|
|
1572
|
+
@hash = [self.class, element_type, lane_count].hash
|
|
1573
|
+
freeze
|
|
1574
|
+
end
|
|
1575
|
+
|
|
1576
|
+
def eql?(other)
|
|
1577
|
+
other.is_a?(Simd) && other.element_type == element_type && other.lane_count == lane_count
|
|
1578
|
+
end
|
|
1579
|
+
|
|
1580
|
+
alias == eql?
|
|
1581
|
+
|
|
1582
|
+
attr_reader :hash
|
|
1583
|
+
|
|
1584
|
+
def fields
|
|
1585
|
+
@fields
|
|
1586
|
+
end
|
|
1587
|
+
|
|
1588
|
+
def field(name)
|
|
1589
|
+
@fields[name]
|
|
1590
|
+
end
|
|
1591
|
+
|
|
1592
|
+
def numeric?
|
|
1593
|
+
true
|
|
1594
|
+
end
|
|
1595
|
+
|
|
1596
|
+
def to_s
|
|
1597
|
+
@name
|
|
1598
|
+
end
|
|
1599
|
+
|
|
1600
|
+
def children
|
|
1601
|
+
[element_type]
|
|
1602
|
+
end
|
|
1603
|
+
end
|
|
1604
|
+
|
|
1563
1605
|
class Tuple < Base
|
|
1564
1606
|
attr_reader :element_types, :field_names
|
|
1565
1607
|
|
|
@@ -38,6 +38,7 @@ module MilkTea
|
|
|
38
38
|
when Types::Tuple then visit_tuple(type)
|
|
39
39
|
when Types::Dyn then visit_dyn(type)
|
|
40
40
|
when Types::SoA then visit_soa(type)
|
|
41
|
+
when Types::Simd then visit_simd(type)
|
|
41
42
|
when Types::Event then visit_event(type)
|
|
42
43
|
when Types::LifetimeRef then visit_lifetime_ref(type)
|
|
43
44
|
else visit_default(type)
|
|
@@ -66,6 +67,7 @@ module MilkTea
|
|
|
66
67
|
def visit_tuple(type); visit_children(type); end
|
|
67
68
|
def visit_dyn(type); visit_children(type); end
|
|
68
69
|
def visit_soa(type); visit_children(type); end
|
|
70
|
+
def visit_simd(type); visit_children(type); end
|
|
69
71
|
def visit_event(type); visit_children(type); end
|
|
70
72
|
|
|
71
73
|
def visit_type_var(type); end
|
|
@@ -263,6 +263,7 @@ module MilkTea
|
|
|
263
263
|
'Option' => 'Built-in optional type: `Option[T]`. Arms: `some(value: T)` and `none`. Use `let ... else:` or `?` for safe unwrapping.',
|
|
264
264
|
'Result' => 'Built-in result type: `Result[T, E]`. Arms: `success(value: T)` and `failure(error: E)`. Use `let ... else:` or `?` for error propagation.',
|
|
265
265
|
'SoA' => 'Struct-of-Arrays: `SoA[T, N]`. Each struct field becomes a separate array of length `N`. Access `soa[i].field` reads from column `field` at row `i`.',
|
|
266
|
+
'simd' => 'SIMD vector type: `simd[T, N]`. Fixed-width vector of `N` lanes of numeric primitive type `T`. Supports component-wise arithmetic, lane access via `[i]`, and explicit aligned/unaligned load/store. Lowers to GCC/Clang vector extensions for portable, readable C output.',
|
|
266
267
|
'struct_handle' => 'Compile-time handle for a struct type. Obtained via reflection builtins like `fields_of`.',
|
|
267
268
|
'field_handle' => 'Compile-time handle for a struct field. Exposes `.name` and `.type`. Obtained via `field_of` and `fields_of`.',
|
|
268
269
|
'callable_handle' => 'Compile-time handle for a callable declaration. Obtained via `callable_of`. Used with `has_attribute`, `attribute_of`.',
|
|
@@ -1561,7 +1562,7 @@ module MilkTea
|
|
|
1561
1562
|
end
|
|
1562
1563
|
|
|
1563
1564
|
def builtin_type_constructor_hover_info(name, tokens, token_index)
|
|
1564
|
-
return nil unless %w[array span Option Result SoA str_buffer ref ptr const_ptr own Task atomic].include?(name)
|
|
1565
|
+
return nil unless %w[array span Option Result SoA str_buffer ref ptr const_ptr own Task atomic simd].include?(name)
|
|
1565
1566
|
|
|
1566
1567
|
lbracket_index = next_non_trivia_token_index(tokens, token_index + 1)
|
|
1567
1568
|
return nil unless lbracket_index && tokens[lbracket_index].type == :lbracket
|
|
@@ -1580,6 +1581,8 @@ module MilkTea
|
|
|
1580
1581
|
'`span[T](data = ..., len = ...)` constructs a span view over contiguous `T` storage.'
|
|
1581
1582
|
when 'SoA'
|
|
1582
1583
|
'`SoA[T, N](...)` constructs a Struct-of-Arrays value with `N` elements of type `T`. Fields are stored in separate contiguous arrays.'
|
|
1584
|
+
when 'simd'
|
|
1585
|
+
'`simd[T, N](...)` constructs a SIMD vector value with `N` lanes of type `T`. Lanes are stored in a single vector register.'
|
|
1583
1586
|
when 'Option'
|
|
1584
1587
|
'`Option[T]` is a built-in optional type with arms `some(value: T)` and `none`.'
|
|
1585
1588
|
when 'Result'
|
|
@@ -1594,6 +1597,8 @@ module MilkTea
|
|
|
1594
1597
|
"builtin #{specialization}(data = ..., len = ...) -> #{specialization}"
|
|
1595
1598
|
when 'SoA'
|
|
1596
1599
|
"builtin #{specialization}(...) -> #{specialization}"
|
|
1600
|
+
when 'simd'
|
|
1601
|
+
"builtin #{specialization}(...) -> #{specialization}"
|
|
1597
1602
|
when 'Option'
|
|
1598
1603
|
"builtin #{specialization}(some: value = ...) / #{specialization}(none:)"
|
|
1599
1604
|
when 'Result'
|
|
@@ -1610,6 +1615,8 @@ module MilkTea
|
|
|
1610
1615
|
'`span[T]` is the built-in non-owning contiguous view type.'
|
|
1611
1616
|
when 'SoA'
|
|
1612
1617
|
'`SoA[T, N]` is the built-in Struct-of-Arrays type. Each struct field is stored in a separate contiguous array of `N` elements, improving SIMD/cache behavior for parallel field access.'
|
|
1618
|
+
when 'simd'
|
|
1619
|
+
'`simd[T, N]` is the built-in SIMD vector type. Fixed-width vector of `N` numeric lanes. Supports component-wise arithmetic, lane access via `[i]`, and explicit aligned/unaligned load/store.'
|
|
1613
1620
|
when 'Option'
|
|
1614
1621
|
'`Option[T]` is the built-in optional value type with `some(value = ...)` and `none` arms.'
|
|
1615
1622
|
when 'Result'
|
data/std/asset_pack.mt
CHANGED
|
@@ -50,18 +50,18 @@ public function open(path: str) -> Result[Reader, Error]:
|
|
|
50
50
|
stdio.file_close(file)
|
|
51
51
|
return Result[Reader, Error].failure(error= Error.invalid_magic)
|
|
52
52
|
|
|
53
|
-
let version =
|
|
53
|
+
let version = decode_ushort_le(unsafe: header_ptr + 4)
|
|
54
54
|
if version != VERSION:
|
|
55
55
|
stdio.file_close(file)
|
|
56
56
|
return Result[Reader, Error].failure(error= Error.unsupported_version)
|
|
57
57
|
|
|
58
|
-
let header_bits =
|
|
58
|
+
let header_bits = decode_ushort_le(unsafe: header_ptr + 6)
|
|
59
59
|
if header_bits != HEADER_FLAGS:
|
|
60
60
|
stdio.file_close(file)
|
|
61
61
|
return Result[Reader, Error].failure(error= Error.unsupported_flags)
|
|
62
62
|
|
|
63
|
-
let entry_count =
|
|
64
|
-
let index_size_result =
|
|
63
|
+
let entry_count = decode_uint_le(unsafe: header_ptr + 8)
|
|
64
|
+
let index_size_result = decode_ulong_le(unsafe: header_ptr + 12)
|
|
65
65
|
var index_size: ptr_uint
|
|
66
66
|
match index_size_result:
|
|
67
67
|
Result.failure as payload:
|
|
@@ -70,7 +70,7 @@ public function open(path: str) -> Result[Reader, Error]:
|
|
|
70
70
|
Result.success as index_payload:
|
|
71
71
|
index_size = index_payload.value
|
|
72
72
|
|
|
73
|
-
let data_offset_result =
|
|
73
|
+
let data_offset_result = decode_ulong_le(unsafe: header_ptr + 20)
|
|
74
74
|
var data_offset: ptr_uint
|
|
75
75
|
match data_offset_result:
|
|
76
76
|
Result.failure as payload:
|
|
@@ -147,12 +147,12 @@ function read_entry_metadata(file: stdio.File?) -> Result[EntryMetadata, Error]:
|
|
|
147
147
|
if not read_exact(file, prefix_ptr, ENTRY_PREFIX_SIZE_BYTES):
|
|
148
148
|
return Result[EntryMetadata, Error].failure(error= Error.malformed_index)
|
|
149
149
|
|
|
150
|
-
let path_length = ptr_uint<-
|
|
150
|
+
let path_length = ptr_uint<-decode_uint_le(prefix_ptr)
|
|
151
151
|
if path_length == 0:
|
|
152
152
|
return Result[EntryMetadata, Error].failure(error= Error.malformed_index)
|
|
153
153
|
|
|
154
|
-
let entry_bits =
|
|
155
|
-
let data_offset_result =
|
|
154
|
+
let entry_bits = decode_uint_le(unsafe: prefix_ptr + 4)
|
|
155
|
+
let data_offset_result = decode_ulong_le(unsafe: prefix_ptr + 8)
|
|
156
156
|
var data_offset: ptr_uint
|
|
157
157
|
match data_offset_result:
|
|
158
158
|
Result.failure as payload:
|
|
@@ -160,7 +160,7 @@ function read_entry_metadata(file: stdio.File?) -> Result[EntryMetadata, Error]:
|
|
|
160
160
|
Result.success as data_offset_payload:
|
|
161
161
|
data_offset = data_offset_payload.value
|
|
162
162
|
|
|
163
|
-
let stored_size_result =
|
|
163
|
+
let stored_size_result = decode_ulong_le(unsafe: prefix_ptr + 16)
|
|
164
164
|
var stored_size: ptr_uint
|
|
165
165
|
match stored_size_result:
|
|
166
166
|
Result.failure as payload:
|
|
@@ -168,7 +168,7 @@ function read_entry_metadata(file: stdio.File?) -> Result[EntryMetadata, Error]:
|
|
|
168
168
|
Result.success as stored_size_payload:
|
|
169
169
|
stored_size = stored_size_payload.value
|
|
170
170
|
|
|
171
|
-
let unpacked_size_result =
|
|
171
|
+
let unpacked_size_result = decode_ulong_le(unsafe: prefix_ptr + 24)
|
|
172
172
|
var unpacked_size: ptr_uint
|
|
173
173
|
match unpacked_size_result:
|
|
174
174
|
Result.failure as payload:
|
|
@@ -228,12 +228,12 @@ function bytes_equal_str(left: ptr[ubyte], left_len: ptr_uint, right: str) -> bo
|
|
|
228
228
|
return true
|
|
229
229
|
|
|
230
230
|
|
|
231
|
-
function
|
|
231
|
+
function decode_ushort_le(bytes: ptr[ubyte]) -> uint:
|
|
232
232
|
unsafe:
|
|
233
233
|
return uint<-read(bytes + 0) | (uint<-read(bytes + 1) << 8)
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
function
|
|
236
|
+
function decode_uint_le(bytes: ptr[ubyte]) -> uint:
|
|
237
237
|
unsafe:
|
|
238
238
|
return (
|
|
239
239
|
uint<-read(bytes + 0) |
|
|
@@ -243,7 +243,7 @@ function decode_u32_le(bytes: ptr[ubyte]) -> uint:
|
|
|
243
243
|
)
|
|
244
244
|
|
|
245
245
|
|
|
246
|
-
function
|
|
246
|
+
function decode_ulong_le(bytes: ptr[ubyte]) -> Result[ptr_uint, Error]:
|
|
247
247
|
if size_of(ptr[void]) < 8:
|
|
248
248
|
var upper_index: ptr_uint = 4
|
|
249
249
|
while upper_index < 8:
|
data/std/binary.mt
CHANGED
|
@@ -114,7 +114,7 @@ extending Writer:
|
|
|
114
114
|
public editable function write_uint_at(position: ptr_uint, value: uint) -> void:
|
|
115
115
|
let buffer_span = this.buffer.as_span()
|
|
116
116
|
if position + 4 > buffer_span.len:
|
|
117
|
-
fatal(c"binary.
|
|
117
|
+
fatal(c"binary.write_uint_at position out of bounds")
|
|
118
118
|
unsafe:
|
|
119
119
|
read(buffer_span.data + position) = ubyte<-(value & 0xFF)
|
|
120
120
|
read(buffer_span.data + position + 1) = ubyte<-((value >> 8) & 0xFF)
|
data/std/hash.mt
CHANGED
|
@@ -189,7 +189,7 @@ extending char:
|
|
|
189
189
|
# hash mixes the value's bytes (FNV-1a); equal/order use native operators.
|
|
190
190
|
# ---------------------------------------------------------------------------
|
|
191
191
|
|
|
192
|
-
function
|
|
192
|
+
function hash_uint(value: uint) -> uint:
|
|
193
193
|
let fnv: uint = 0x811C9DC5
|
|
194
194
|
let prime: uint = 0x01000193
|
|
195
195
|
var h = fnv
|
|
@@ -200,14 +200,14 @@ function hash_u32(value: uint) -> uint:
|
|
|
200
200
|
return h
|
|
201
201
|
|
|
202
202
|
|
|
203
|
-
function
|
|
204
|
-
return
|
|
203
|
+
function hash_ulong(value: ulong) -> uint:
|
|
204
|
+
return hash_uint(uint<-((value >> uint<-(32)) ^ value))
|
|
205
205
|
|
|
206
206
|
|
|
207
207
|
extending byte:
|
|
208
208
|
public static function hash(value: const_ptr[byte]) -> uint:
|
|
209
209
|
unsafe:
|
|
210
|
-
return
|
|
210
|
+
return hash_uint(uint<-(int<-read(ptr[byte]<-value)))
|
|
211
211
|
|
|
212
212
|
|
|
213
213
|
public static function equal(a: const_ptr[byte], b: const_ptr[byte]) -> bool:
|
|
@@ -229,7 +229,7 @@ extending byte:
|
|
|
229
229
|
extending ubyte:
|
|
230
230
|
public static function hash(value: const_ptr[ubyte]) -> uint:
|
|
231
231
|
unsafe:
|
|
232
|
-
return
|
|
232
|
+
return hash_uint(uint<-read(ptr[ubyte]<-value))
|
|
233
233
|
|
|
234
234
|
|
|
235
235
|
public static function equal(a: const_ptr[ubyte], b: const_ptr[ubyte]) -> bool:
|
|
@@ -251,7 +251,7 @@ extending ubyte:
|
|
|
251
251
|
extending short:
|
|
252
252
|
public static function hash(value: const_ptr[short]) -> uint:
|
|
253
253
|
unsafe:
|
|
254
|
-
return
|
|
254
|
+
return hash_uint(uint<-(int<-read(ptr[short]<-value)))
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
public static function equal(a: const_ptr[short], b: const_ptr[short]) -> bool:
|
|
@@ -273,7 +273,7 @@ extending short:
|
|
|
273
273
|
extending ushort:
|
|
274
274
|
public static function hash(value: const_ptr[ushort]) -> uint:
|
|
275
275
|
unsafe:
|
|
276
|
-
return
|
|
276
|
+
return hash_uint(uint<-read(ptr[ushort]<-value))
|
|
277
277
|
|
|
278
278
|
|
|
279
279
|
public static function equal(a: const_ptr[ushort], b: const_ptr[ushort]) -> bool:
|
|
@@ -295,7 +295,7 @@ extending ushort:
|
|
|
295
295
|
extending long:
|
|
296
296
|
public static function hash(value: const_ptr[long]) -> uint:
|
|
297
297
|
unsafe:
|
|
298
|
-
return
|
|
298
|
+
return hash_ulong(ulong<-read(ptr[long]<-value))
|
|
299
299
|
|
|
300
300
|
|
|
301
301
|
public static function equal(a: const_ptr[long], b: const_ptr[long]) -> bool:
|
|
@@ -317,7 +317,7 @@ extending long:
|
|
|
317
317
|
extending ulong:
|
|
318
318
|
public static function hash(value: const_ptr[ulong]) -> uint:
|
|
319
319
|
unsafe:
|
|
320
|
-
return
|
|
320
|
+
return hash_ulong(read(ptr[ulong]<-value))
|
|
321
321
|
|
|
322
322
|
|
|
323
323
|
public static function equal(a: const_ptr[ulong], b: const_ptr[ulong]) -> bool:
|
|
@@ -339,7 +339,7 @@ extending ulong:
|
|
|
339
339
|
extending ptr_int:
|
|
340
340
|
public static function hash(value: const_ptr[ptr_int]) -> uint:
|
|
341
341
|
unsafe:
|
|
342
|
-
return
|
|
342
|
+
return hash_ulong(ulong<-read(ptr[ptr_int]<-value))
|
|
343
343
|
|
|
344
344
|
|
|
345
345
|
public static function equal(a: const_ptr[ptr_int], b: const_ptr[ptr_int]) -> bool:
|
|
@@ -410,7 +410,7 @@ public function order_struct[T](a: const_ptr[T], b: const_ptr[T]) -> int:
|
|
|
410
410
|
extending ptr_uint:
|
|
411
411
|
public static function hash(value: const_ptr[ptr_uint]) -> uint:
|
|
412
412
|
unsafe:
|
|
413
|
-
return
|
|
413
|
+
return hash_ulong(ulong<-read(ptr[ptr_uint]<-value))
|
|
414
414
|
|
|
415
415
|
|
|
416
416
|
public static function equal(a: const_ptr[ptr_uint], b: const_ptr[ptr_uint]) -> bool:
|
data/std/net/session.mt
CHANGED
|
@@ -167,7 +167,7 @@ function generate_handshake_key() -> Result[ulong, net.Error]:
|
|
|
167
167
|
return Result[ulong, net.Error].success(value = value)
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
function
|
|
170
|
+
function decode_uint_at(data: span[ubyte], offset: ptr_uint) -> uint:
|
|
171
171
|
unsafe:
|
|
172
172
|
return uint<-read(data.data + offset) | (uint<-read(data.data + offset + 1) << 8) | (uint<-read(data.data + offset + 2) << 16) | (uint<-read(data.data + offset + 3) << 24)
|
|
173
173
|
|
|
@@ -536,7 +536,7 @@ function handle_incoming_conn(conn: ref[Connection], msg: ref[chan.Message]) ->
|
|
|
536
536
|
conn.frame_since_last_recv = 0
|
|
537
537
|
let hb_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
538
538
|
if hb_span.len >= 4:
|
|
539
|
-
let echoed =
|
|
539
|
+
let echoed = decode_uint_at(hb_span, 0)
|
|
540
540
|
var ack = build_heartbeat_ack(echoed)
|
|
541
541
|
let peer_addr_result = conn.channel.peer_address()
|
|
542
542
|
match peer_addr_result:
|
|
@@ -555,7 +555,7 @@ function handle_incoming_conn(conn: ref[Connection], msg: ref[chan.Message]) ->
|
|
|
555
555
|
conn.frame_since_last_recv = 0
|
|
556
556
|
let echo_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
557
557
|
if echo_span.len >= 4:
|
|
558
|
-
let echoed =
|
|
558
|
+
let echoed = decode_uint_at(echo_span, 0)
|
|
559
559
|
if conn.last_heartbeat_sent > echoed:
|
|
560
560
|
conn.ping_rtt_frames = conn.last_heartbeat_sent - echoed
|
|
561
561
|
msg.release()
|
|
@@ -920,7 +920,7 @@ function handle_incoming_session(session: ref[Session], msg: ref[chan.HostMessag
|
|
|
920
920
|
update_peer_last_recv(session, peer_payload.value)
|
|
921
921
|
let hb_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
922
922
|
if hb_span.len >= 4:
|
|
923
|
-
let echoed =
|
|
923
|
+
let echoed = decode_uint_at(hb_span, 0)
|
|
924
924
|
var ack = build_heartbeat_ack(echoed)
|
|
925
925
|
let addr_result = msg.source.copy()
|
|
926
926
|
match addr_result:
|
|
@@ -973,7 +973,7 @@ function handle_connect_request(session: ref[Session], msg: ref[chan.HostMessage
|
|
|
973
973
|
msg.release()
|
|
974
974
|
return
|
|
975
975
|
|
|
976
|
-
let client_version =
|
|
976
|
+
let client_version = decode_uint_at(req_span, 0)
|
|
977
977
|
if client_version != session.config.protocol_version:
|
|
978
978
|
var reject = build_connect_reject(reject_reason_version)
|
|
979
979
|
let addr_result = msg.source.copy()
|