mt-lang 0.3.41 → 0.3.43
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 +5 -2
- data/docs/index.html +20 -4
- data/docs/language-design.md +5 -2
- data/docs/language-manual.md +28 -5
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/bindings/bindgen/declaration.rb +17 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +44 -5
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +7 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +22 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +9 -0
- data/lib/milk_tea/bindings/vendored_box3d.rb +77 -0
- data/lib/milk_tea/bindings.rb +1 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +21 -5
- data/lib/milk_tea/core/c_backend/feature_detection.rb +6 -1
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +38 -4
- data/lib/milk_tea/core/c_backend/statements.rb +1 -1
- data/lib/milk_tea/core/c_backend/type_collectors.rb +97 -4
- data/lib/milk_tea/core/c_backend.rb +9 -0
- data/lib/milk_tea/core/lowering/async/async_lowering.rb +2 -6
- data/lib/milk_tea/core/lowering/async/frame_builder.rb +12 -5
- data/lib/milk_tea/core/lowering/async/normalization.rb +28 -5
- data/lib/milk_tea/core/lowering/expressions.rb +31 -6
- data/lib/milk_tea/core/lowering/proc.rb +3 -0
- data/lib/milk_tea/core/lowering/resolve.rb +3 -0
- data/lib/milk_tea/core/lowering/utils.rb +26 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +21 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +49 -1
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1 -0
- data/lib/milk_tea/core/types/registry.rb +39 -16
- data/lib/milk_tea/lsp/server/hover.rb +0 -1
- data/lib/milk_tea/lsp/server/text_documents.rb +1 -1
- data/lib/milk_tea/tooling/docs_app.rb +1 -1
- data/std/box3d.mt +784 -0
- data/std/c/box3d.mt +1871 -0
- data/std/c/miniaudio.mt +19 -28
- data/std/c/steamworks.mt +2 -5
- data/std/miniaudio.mt +9 -12
- data/std/steamworks.mt +0 -1
- metadata +5 -2
|
@@ -51,6 +51,7 @@ module MilkTea
|
|
|
51
51
|
"",
|
|
52
52
|
"static mt_str mt_str_concat(mt_str a, mt_str b) {",
|
|
53
53
|
"#{INDENT}uintptr_t total = a.len + b.len;",
|
|
54
|
+
"#{INDENT}if (total > MT_STR_CONCAT_BUF_SIZE) mt_fatal(\"str concatenation exceeds the scratch buffer budget\");",
|
|
54
55
|
"#{INDENT}if (mt_str_concat_offset + total > MT_STR_CONCAT_BUF_SIZE) {",
|
|
55
56
|
"#{INDENT * 2}mt_str_concat_offset = 0;",
|
|
56
57
|
"#{INDENT}}",
|
|
@@ -63,6 +64,36 @@ module MilkTea
|
|
|
63
64
|
]
|
|
64
65
|
end
|
|
65
66
|
|
|
67
|
+
def emit_str_slice_helpers
|
|
68
|
+
[
|
|
69
|
+
"static uint8_t mt_str_index(mt_str text, uintptr_t index) {",
|
|
70
|
+
"#{INDENT}if (index >= text.len) mt_fatal(\"str index out of bounds\");",
|
|
71
|
+
"#{INDENT}return (uint8_t)text.data[index];",
|
|
72
|
+
"}",
|
|
73
|
+
"",
|
|
74
|
+
"static bool mt_str_utf8_boundary(mt_str text, uintptr_t index) {",
|
|
75
|
+
"#{INDENT}if (index == 0 || index == text.len) return true;",
|
|
76
|
+
"#{INDENT}return ((uint8_t)text.data[index] & 0xC0) != 0x80;",
|
|
77
|
+
"}",
|
|
78
|
+
"",
|
|
79
|
+
"static mt_str mt_str_slice(mt_str text, uintptr_t start, uintptr_t stop) {",
|
|
80
|
+
"#{INDENT}if (start > stop || stop > text.len) mt_fatal(\"str slice out of bounds\");",
|
|
81
|
+
"#{INDENT}if (!mt_str_utf8_boundary(text, start) || !mt_str_utf8_boundary(text, stop)) mt_fatal(\"str slice bounds must be UTF-8 code-unit boundaries\");",
|
|
82
|
+
"#{INDENT}return (mt_str){ .data = text.data + start, .len = stop - start };",
|
|
83
|
+
"}",
|
|
84
|
+
]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def emit_span_slice_helper(helper_name)
|
|
88
|
+
span_name = helper_name.sub("mt_span_slice_", "mt_span_")
|
|
89
|
+
[
|
|
90
|
+
"static inline #{span_name} #{helper_name}(#{span_name} span, uintptr_t start, uintptr_t stop) {",
|
|
91
|
+
"#{INDENT}if (start > stop || stop > span.len) mt_fatal(\"span slice out of bounds\");",
|
|
92
|
+
"#{INDENT}return (#{span_name}){ .data = span.data + start, .len = stop - start };",
|
|
93
|
+
"}",
|
|
94
|
+
]
|
|
95
|
+
end
|
|
96
|
+
|
|
66
97
|
def emit_variant_equality_helpers
|
|
67
98
|
variant_decls_by_linkage = (emitted_aggregate_variants + collect_generic_variant_decls).each_with_object({}) { |decl, map| map[decl.linkage_name] = decl }
|
|
68
99
|
variant_equality_types
|
|
@@ -297,6 +328,9 @@ module MilkTea
|
|
|
297
328
|
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
298
329
|
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
299
330
|
"#{INDENT}}",
|
|
331
|
+
"#{INDENT}for (int t = nworkers + 1; t < count; t++) {",
|
|
332
|
+
"#{INDENT * 2}items[t].work(items[t].data);",
|
|
333
|
+
"#{INDENT}}",
|
|
300
334
|
"}",
|
|
301
335
|
]
|
|
302
336
|
end
|
|
@@ -590,11 +624,11 @@ module MilkTea
|
|
|
590
624
|
|
|
591
625
|
def emit_checked_array_index_helper(type)
|
|
592
626
|
helper_name = checked_array_index_helper_name(type)
|
|
593
|
-
params = [c_declaration(type, '
|
|
627
|
+
params = [c_declaration(pointer_to(array_element_type(type)), 'array'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
594
628
|
[
|
|
595
629
|
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
596
630
|
"#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
|
|
597
|
-
"#{INDENT}return &
|
|
631
|
+
"#{INDENT}return &array[index];",
|
|
598
632
|
"}",
|
|
599
633
|
]
|
|
600
634
|
end
|
|
@@ -612,11 +646,11 @@ module MilkTea
|
|
|
612
646
|
|
|
613
647
|
def emit_nullable_array_index_helper(type)
|
|
614
648
|
helper_name = nullable_array_index_helper_name(type)
|
|
615
|
-
params = [c_declaration(type, '
|
|
649
|
+
params = [c_declaration(pointer_to(array_element_type(type)), 'array'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
616
650
|
[
|
|
617
651
|
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
618
652
|
"#{INDENT}if (index >= #{array_length(type)}) return NULL;",
|
|
619
|
-
"#{INDENT}return &
|
|
653
|
+
"#{INDENT}return &array[index];",
|
|
620
654
|
"}",
|
|
621
655
|
]
|
|
622
656
|
end
|
|
@@ -495,7 +495,7 @@ module MilkTea
|
|
|
495
495
|
def emit_checked_index_pointer(expression)
|
|
496
496
|
case expression
|
|
497
497
|
when IR::CheckedIndex
|
|
498
|
-
"#{checked_array_index_helper_name(expression.receiver_type)}(#{
|
|
498
|
+
"#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_checked_array_index_argument(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
499
499
|
when IR::CheckedSpanIndex
|
|
500
500
|
"#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
501
501
|
else
|
|
@@ -19,6 +19,80 @@ module MilkTea
|
|
|
19
19
|
span_types.uniq
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def collect_span_slice_helper_names
|
|
23
|
+
names = []
|
|
24
|
+
emitted_functions.each do |function|
|
|
25
|
+
collect_span_slice_helper_names_from_statements(function.body, names)
|
|
26
|
+
end
|
|
27
|
+
names.uniq
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def collect_span_slice_helper_names_from_statements(statements, names)
|
|
31
|
+
statements.each do |statement|
|
|
32
|
+
case statement
|
|
33
|
+
when IR::LocalDecl
|
|
34
|
+
collect_span_slice_helper_names_from_expression(statement.value, names)
|
|
35
|
+
when IR::Assignment
|
|
36
|
+
collect_span_slice_helper_names_from_expression(statement.target, names)
|
|
37
|
+
collect_span_slice_helper_names_from_expression(statement.value, names)
|
|
38
|
+
when IR::BlockStmt
|
|
39
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
40
|
+
when IR::WhileStmt
|
|
41
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
42
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
43
|
+
when IR::ForStmt
|
|
44
|
+
collect_span_slice_helper_names_from_statements([statement.init], names)
|
|
45
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
46
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
47
|
+
collect_span_slice_helper_names_from_statements([statement.post], names)
|
|
48
|
+
when IR::IfStmt
|
|
49
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
50
|
+
collect_span_slice_helper_names_from_statements(statement.then_body, names)
|
|
51
|
+
collect_span_slice_helper_names_from_statements(statement.else_body, names) if statement.else_body
|
|
52
|
+
when IR::SwitchStmt
|
|
53
|
+
collect_span_slice_helper_names_from_expression(statement.expression, names)
|
|
54
|
+
statement.cases.each { |switch_case| collect_span_slice_helper_names_from_statements(switch_case.body, names) }
|
|
55
|
+
when IR::ReturnStmt
|
|
56
|
+
collect_span_slice_helper_names_from_expression(statement.value, names) if statement.value
|
|
57
|
+
when IR::ExpressionStmt
|
|
58
|
+
collect_span_slice_helper_names_from_expression(statement.expression, names)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def collect_span_slice_helper_names_from_expression(expression, names)
|
|
64
|
+
return unless expression
|
|
65
|
+
|
|
66
|
+
case expression
|
|
67
|
+
when IR::Call
|
|
68
|
+
names << expression.callee if expression.callee.is_a?(String) && expression.callee.start_with?("mt_span_slice_")
|
|
69
|
+
collect_span_slice_helper_names_from_expression(expression.callee, names) unless expression.callee.is_a?(String)
|
|
70
|
+
expression.arguments.each { |argument| collect_span_slice_helper_names_from_expression(argument, names) }
|
|
71
|
+
when IR::Member
|
|
72
|
+
collect_span_slice_helper_names_from_expression(expression.receiver, names)
|
|
73
|
+
when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
|
|
74
|
+
collect_span_slice_helper_names_from_expression(expression.receiver, names)
|
|
75
|
+
collect_span_slice_helper_names_from_expression(expression.index, names)
|
|
76
|
+
when IR::Unary
|
|
77
|
+
collect_span_slice_helper_names_from_expression(expression.operand, names)
|
|
78
|
+
when IR::Binary
|
|
79
|
+
collect_span_slice_helper_names_from_expression(expression.left, names)
|
|
80
|
+
collect_span_slice_helper_names_from_expression(expression.right, names)
|
|
81
|
+
when IR::Conditional
|
|
82
|
+
collect_span_slice_helper_names_from_expression(expression.condition, names)
|
|
83
|
+
collect_span_slice_helper_names_from_expression(expression.then_expression, names)
|
|
84
|
+
collect_span_slice_helper_names_from_expression(expression.else_expression, names)
|
|
85
|
+
when IR::Cast, IR::AddressOf
|
|
86
|
+
collect_span_slice_helper_names_from_expression(expression.expression, names)
|
|
87
|
+
when IR::AggregateLiteral
|
|
88
|
+
expression.fields.each { |field| collect_span_slice_helper_names_from_expression(field.value, names) }
|
|
89
|
+
when IR::ArrayLiteral
|
|
90
|
+
expression.elements.each { |element| collect_span_slice_helper_names_from_expression(element, names) }
|
|
91
|
+
when IR::VariantLiteral
|
|
92
|
+
expression.fields.each { |field| collect_span_slice_helper_names_from_expression(field.value, names) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
22
96
|
def collect_checked_array_index_types_from_statements(statements, array_types, nullable_only: false)
|
|
23
97
|
statements.each do |statement|
|
|
24
98
|
case statement
|
|
@@ -223,6 +297,26 @@ module MilkTea
|
|
|
223
297
|
soa_types = []
|
|
224
298
|
visited = {}
|
|
225
299
|
|
|
300
|
+
all_emitted_top_level_values.each do |value|
|
|
301
|
+
collect_soa_type(value.type, soa_types, visited)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
@program.structs.each do |struct_decl|
|
|
305
|
+
struct_decl.fields.each do |field|
|
|
306
|
+
collect_soa_type(field.type, soa_types, visited)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
@program.unions.each do |union_decl|
|
|
311
|
+
union_decl.fields.each do |field|
|
|
312
|
+
collect_soa_type(field.type, soa_types, visited)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
each_variant_arm_field_type do |field_type|
|
|
317
|
+
collect_soa_type(field_type, soa_types, visited)
|
|
318
|
+
end
|
|
319
|
+
|
|
226
320
|
emitted_functions.each do |function|
|
|
227
321
|
collect_soa_type(function.return_type, soa_types, visited)
|
|
228
322
|
function.params.each do |param|
|
|
@@ -231,10 +325,9 @@ module MilkTea
|
|
|
231
325
|
collect_soa_from_statements(function.body, soa_types, visited)
|
|
232
326
|
end
|
|
233
327
|
|
|
234
|
-
@program.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
end
|
|
328
|
+
@program.static_asserts.each do |statement|
|
|
329
|
+
collect_soa_type_in_expression(statement.condition, soa_types, visited)
|
|
330
|
+
collect_soa_type_in_expression(statement.message, soa_types, visited)
|
|
238
331
|
end
|
|
239
332
|
|
|
240
333
|
soa_types.uniq
|
|
@@ -162,6 +162,10 @@ module MilkTea
|
|
|
162
162
|
lines.concat(emit_str_concat_helper)
|
|
163
163
|
lines << ""
|
|
164
164
|
end
|
|
165
|
+
if uses_str_slice_helpers?
|
|
166
|
+
lines.concat(emit_str_slice_helpers)
|
|
167
|
+
lines << ""
|
|
168
|
+
end
|
|
165
169
|
if uses_str_buffer_helpers?
|
|
166
170
|
lines.concat(emit_utf8_validation_helpers)
|
|
167
171
|
lines << ""
|
|
@@ -335,6 +339,11 @@ module MilkTea
|
|
|
335
339
|
lines << ""
|
|
336
340
|
end
|
|
337
341
|
|
|
342
|
+
collect_span_slice_helper_names.each do |helper_name|
|
|
343
|
+
lines.concat(emit_span_slice_helper(helper_name))
|
|
344
|
+
lines << ""
|
|
345
|
+
end
|
|
346
|
+
|
|
338
347
|
collect_checked_array_index_types(nullable_only: true).each do |type|
|
|
339
348
|
lines.concat(emit_nullable_array_index_helper(type))
|
|
340
349
|
lines << ""
|
|
@@ -636,13 +636,9 @@ module MilkTea
|
|
|
636
636
|
active_defers: active_defers + local_defers,
|
|
637
637
|
loop_flow: nested_loop_flow(loop_flow, local_defers),
|
|
638
638
|
)
|
|
639
|
+
storage_ref = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
|
|
639
640
|
lowered << IR::IfStmt.new(
|
|
640
|
-
condition:
|
|
641
|
-
operator: "==",
|
|
642
|
-
left: IR::Name.new(name: linkage_name, type: storage_type, pointer: false),
|
|
643
|
-
right: IR::NullLiteral.new(type: storage_type),
|
|
644
|
-
type: @ctx.types.fetch("bool"),
|
|
645
|
-
),
|
|
641
|
+
condition: let_else_failure_condition(storage_ref, storage_type),
|
|
646
642
|
then_body: else_body,
|
|
647
643
|
else_body: nil,
|
|
648
644
|
)
|
|
@@ -421,7 +421,8 @@ module MilkTea
|
|
|
421
421
|
body = [async_frame_cast_declaration(frame_type, async_info)]
|
|
422
422
|
|
|
423
423
|
not_ready_expr = IR::Unary.new(operator: "not", operand: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")), type: @ctx.types.fetch("bool"))
|
|
424
|
-
|
|
424
|
+
frame_free_stmt = IR::ExpressionStmt.new(expression: IR::Call.new(callee: "mt_async_free", arguments: [raw_frame_expr], type: @ctx.types.fetch("void")))
|
|
425
|
+
not_ready_return = [frame_free_stmt, IR::ReturnStmt.new(value: nil)]
|
|
425
426
|
|
|
426
427
|
if async_info[:await_fields].any?
|
|
427
428
|
await_release_stmts = []
|
|
@@ -589,11 +590,17 @@ module MilkTea
|
|
|
589
590
|
)
|
|
590
591
|
body << IR::ExpressionStmt.new(expression: ready_assign)
|
|
591
592
|
|
|
592
|
-
|
|
593
|
+
waiter_frame_field = async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr])
|
|
593
594
|
wake_stmts = [
|
|
595
|
+
IR::LocalDecl.new(
|
|
596
|
+
name: "waiter_frame",
|
|
597
|
+
linkage_name: "__mt_waiter_frame",
|
|
598
|
+
type: async_info[:void_ptr],
|
|
599
|
+
value: waiter_frame_field,
|
|
600
|
+
),
|
|
594
601
|
IR::ExpressionStmt.new(
|
|
595
602
|
expression: IR::Assignment.new(
|
|
596
|
-
target:
|
|
603
|
+
target: waiter_frame_field,
|
|
597
604
|
operator: "=",
|
|
598
605
|
value: IR::NullLiteral.new(type: async_info[:void_ptr]),
|
|
599
606
|
),
|
|
@@ -601,13 +608,13 @@ module MilkTea
|
|
|
601
608
|
IR::ExpressionStmt.new(
|
|
602
609
|
expression: IR::Call.new(
|
|
603
610
|
callee: IR::Name.new(name: async_frame_field_c_name("waiter"), type: async_info[:wake_type], pointer: false),
|
|
604
|
-
arguments: [
|
|
611
|
+
arguments: [IR::Name.new(name: "__mt_waiter_frame", type: async_info[:void_ptr], pointer: false)],
|
|
605
612
|
type: @ctx.types.fetch("void"),
|
|
606
613
|
),
|
|
607
614
|
),
|
|
608
615
|
]
|
|
609
616
|
body << IR::IfStmt.new(
|
|
610
|
-
condition:
|
|
617
|
+
condition: waiter_frame_field,
|
|
611
618
|
then_body: wake_stmts,
|
|
612
619
|
else_body: nil,
|
|
613
620
|
)
|
|
@@ -364,6 +364,17 @@ module MilkTea
|
|
|
364
364
|
end
|
|
365
365
|
end
|
|
366
366
|
|
|
367
|
+
# Emits a type-name prefix that the semantic re-check of generated proc
|
|
368
|
+
# roots can actually resolve: bare names for prelude and current-module
|
|
369
|
+
# types, the qualified module path otherwise.
|
|
370
|
+
def ast_type_ref_base_parts(type)
|
|
371
|
+
return [type.name] if type.module_name.nil?
|
|
372
|
+
return [type.name] if type.module_name == @ctx.module_name
|
|
373
|
+
return [type.name] if %w[std.option std.result].include?(type.module_name)
|
|
374
|
+
|
|
375
|
+
type.module_name.split(".") + [type.name]
|
|
376
|
+
end
|
|
377
|
+
|
|
367
378
|
def ast_type_ref_for(type)
|
|
368
379
|
case type
|
|
369
380
|
when Types::Primitive
|
|
@@ -389,12 +400,25 @@ module MilkTea
|
|
|
389
400
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.element_type))], nullable: false)
|
|
390
401
|
when Types::Task
|
|
391
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)
|
|
392
405
|
when Types::TypeVar
|
|
393
406
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
|
|
394
407
|
when Types::StructInstance
|
|
395
|
-
base_parts = type.module_name ? type.module_name.split(".") + [type.name] : [type.name]
|
|
396
408
|
AST::TypeRef.new(
|
|
397
|
-
name: AST::QualifiedName.new(parts:
|
|
409
|
+
name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)),
|
|
410
|
+
arguments: type.arguments.map do |argument|
|
|
411
|
+
if argument.is_a?(Types::LiteralTypeArg)
|
|
412
|
+
AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
|
|
413
|
+
else
|
|
414
|
+
AST::TypeArgument.new(value: ast_type_ref_for(argument))
|
|
415
|
+
end
|
|
416
|
+
end,
|
|
417
|
+
nullable: false,
|
|
418
|
+
)
|
|
419
|
+
when Types::VariantInstance
|
|
420
|
+
AST::TypeRef.new(
|
|
421
|
+
name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)),
|
|
398
422
|
arguments: type.arguments.map do |argument|
|
|
399
423
|
if argument.is_a?(Types::LiteralTypeArg)
|
|
400
424
|
AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
|
|
@@ -404,9 +428,8 @@ module MilkTea
|
|
|
404
428
|
end,
|
|
405
429
|
nullable: false,
|
|
406
430
|
)
|
|
407
|
-
when Types::Struct, Types::Union, Types::Opaque, Types::Enum, Types::Flags
|
|
408
|
-
|
|
409
|
-
AST::TypeRef.new(name: AST::QualifiedName.new(parts: parts), arguments: [], nullable: false)
|
|
431
|
+
when Types::Struct, Types::Union, Types::Opaque, Types::Enum, Types::Flags, Types::Variant
|
|
432
|
+
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)), arguments: [], nullable: false)
|
|
410
433
|
when Types::Function
|
|
411
434
|
AST::FunctionType.new(
|
|
412
435
|
params: type.params.each_with_index.map { |param, i| AST::Param.new(name: param.name || "p#{i}", type: ast_type_ref_for(param.type)) },
|
|
@@ -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
|
-
|
|
1368
|
-
|
|
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
|
-
|
|
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) }
|
|
@@ -786,6 +786,9 @@ module MilkTea
|
|
|
786
786
|
raise LoweringError.new("unknown member #{expression.member}", line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
|
|
787
787
|
when AST::IndexAccess
|
|
788
788
|
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
789
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
790
|
+
return range_index_result_type(receiver_type)
|
|
791
|
+
end
|
|
789
792
|
index_type = infer_expression_type(expression.index, env:)
|
|
790
793
|
infer_index_result_type(receiver_type, index_type)
|
|
791
794
|
when AST::UnaryOp
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -248,7 +252,13 @@ module MilkTea
|
|
|
248
252
|
raise_sema_error("unsupported expression #{expression.class.name}")
|
|
249
253
|
end
|
|
250
254
|
|
|
251
|
-
|
|
255
|
+
# Specialized generic-instance bodies share the same AST nodes across
|
|
256
|
+
# substitutions, so a node-id keyed cache cannot be populated there:
|
|
257
|
+
# the last-checked instance would overwrite every other instance's
|
|
258
|
+
# types and leak wrong types to lowering, which reads this cache.
|
|
259
|
+
# Regular functions carry an empty (but non-nil) substitution hash, so
|
|
260
|
+
# only a non-empty hash identifies an instance body.
|
|
261
|
+
@resolved_expr_types[@ctx.ast.node_ids[expression.object_id]] = type unless @current_type_substitutions&.any?
|
|
252
262
|
type
|
|
253
263
|
end
|
|
254
264
|
end
|
|
@@ -464,8 +474,17 @@ module MilkTea
|
|
|
464
474
|
|
|
465
475
|
def infer_index_access(expression, scopes:)
|
|
466
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
|
+
|
|
467
482
|
index_type = infer_expression(expression.index, scopes:)
|
|
468
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
|
+
|
|
469
488
|
if soa_type?(receiver_type)
|
|
470
489
|
return receiver_type.element_type
|
|
471
490
|
end
|
|
@@ -961,7 +980,7 @@ module MilkTea
|
|
|
961
980
|
callable_kind = resolution.kind
|
|
962
981
|
callable = resolution.value
|
|
963
982
|
receiver = resolution.receiver
|
|
964
|
-
@resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind
|
|
983
|
+
@resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind unless @current_type_substitutions&.any?
|
|
965
984
|
|
|
966
985
|
case callable_kind
|
|
967
986
|
when :function
|
|
@@ -695,7 +695,9 @@ module MilkTea
|
|
|
695
695
|
offset = CompileTime::Layout.offset_of(type, binding.const_value.field_name)
|
|
696
696
|
return unless offset
|
|
697
697
|
|
|
698
|
-
|
|
698
|
+
# Same node-id key collision as resolved_expr_types: instance bodies
|
|
699
|
+
# share AST nodes across substitutions, so never cache their values.
|
|
700
|
+
@const_values[@ctx.ast.node_ids[expression.object_id]] = offset unless @current_type_substitutions&.any?
|
|
699
701
|
end
|
|
700
702
|
|
|
701
703
|
def infer_offsetof_type(type_ref, field_name, scopes: nil)
|
|
@@ -1020,6 +1022,10 @@ module MilkTea
|
|
|
1020
1022
|
def infer_index_result_type(receiver_type, index_type)
|
|
1021
1023
|
raise_sema_error("index must be an integer type, got #{index_type}") unless integer_type?(index_type)
|
|
1022
1024
|
|
|
1025
|
+
if receiver_type.is_a?(Types::StringView)
|
|
1026
|
+
return @ctx.types.fetch("ubyte")
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1023
1029
|
if array_type?(receiver_type)
|
|
1024
1030
|
return array_element_type(receiver_type)
|
|
1025
1031
|
end
|
|
@@ -1045,6 +1051,48 @@ module MilkTea
|
|
|
1045
1051
|
raise_sema_error("cannot index #{receiver_type}")
|
|
1046
1052
|
end
|
|
1047
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
|
+
|
|
1048
1096
|
def addressable_storage_expression?(expression, scopes:)
|
|
1049
1097
|
case expression
|
|
1050
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|
|