mt-lang 0.3.23 → 0.3.25
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/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
- data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
- data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
- data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
- data/lib/milk_tea/core/bindings.rb +4 -9
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
- data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
- data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
- data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/statements.rb +20 -20
- data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
- data/lib/milk_tea/core/c_backend/type_system.rb +2 -1
- data/lib/milk_tea/core/c_backend.rb +3 -3
- data/lib/milk_tea/core/compile_time.rb +41 -70
- data/lib/milk_tea/core/control_flow/builder.rb +3 -3
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
- data/lib/milk_tea/core/control_flow/graph.rb +1 -1
- data/lib/milk_tea/core/control_flow/termination.rb +1 -1
- data/lib/milk_tea/core/cst.rb +16 -0
- data/lib/milk_tea/core/flow_scope.rb +13 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
- data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
- data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
- data/lib/milk_tea/core/lexer/indentation.rb +4 -4
- data/lib/milk_tea/core/lexer/numbers.rb +1 -1
- data/lib/milk_tea/core/lexer/strings.rb +4 -4
- data/lib/milk_tea/core/lexer/symbols.rb +3 -3
- data/lib/milk_tea/core/lexer/trivia.rb +4 -4
- data/lib/milk_tea/core/lexer.rb +17 -17
- data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
- data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
- data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
- data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
- data/lib/milk_tea/core/lowering/block.rb +982 -980
- data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
- data/lib/milk_tea/core/lowering/declarations.rb +175 -173
- data/lib/milk_tea/core/lowering/dyn.rb +171 -169
- data/lib/milk_tea/core/lowering/events.rb +946 -944
- data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
- data/lib/milk_tea/core/lowering/functions.rb +186 -184
- data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
- data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
- data/lib/milk_tea/core/lowering/proc.rb +355 -353
- data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
- data/lib/milk_tea/core/lowering/scans.rb +162 -168
- data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
- data/lib/milk_tea/core/lowering/utils.rb +965 -963
- data/lib/milk_tea/core/lowering.rb +22 -22
- data/lib/milk_tea/core/module_binder.rb +1 -1
- data/lib/milk_tea/core/module_loader.rb +2 -2
- data/lib/milk_tea/core/parser/attributes.rb +1 -1
- data/lib/milk_tea/core/parser/blocks.rb +2 -2
- data/lib/milk_tea/core/parser/declarations.rb +15 -15
- data/lib/milk_tea/core/parser/expressions.rb +20 -20
- data/lib/milk_tea/core/parser/recovery.rb +1 -1
- data/lib/milk_tea/core/parser/statements.rb +3 -3
- data/lib/milk_tea/core/parser/types.rb +4 -4
- data/lib/milk_tea/core/parser.rb +39 -21
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
- data/lib/milk_tea/core/types/layout.rb +3 -1
- data/lib/milk_tea/core/types/visitor.rb +351 -349
- data/lib/milk_tea/core.rb +0 -2
- data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
- metadata +4 -5
- data/lib/milk_tea/core/cst_builder.rb +0 -19
- data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
- data/lib/milk_tea/core/token_stream.rb +0 -30
|
@@ -58,14 +58,14 @@ module MilkTea
|
|
|
58
58
|
start_offset = line_offset + index
|
|
59
59
|
if consumed_lines == 1
|
|
60
60
|
lexeme = line[index...segment.next_index]
|
|
61
|
-
@tokens <<
|
|
61
|
+
@tokens << build_token(cstring ? :cstring : :string, lexeme, value, line_number, index + 1, start_offset:, end_offset: line_offset + segment.next_index)
|
|
62
62
|
return StringLexResult.new(consumed_lines:, next_index: segment.next_index)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
end_offset = last_line_offset + last_segment_end
|
|
66
66
|
lexeme = @source.byteslice(start_offset, end_offset - start_offset)
|
|
67
|
-
@tokens <<
|
|
68
|
-
|
|
67
|
+
@tokens << build_token(cstring ? :cstring : :string, lexeme, value, line_number, index + 1, start_offset:, end_offset:)
|
|
68
|
+
emit_newline(last_line, last_line_number, last_line_offset, last_line_has_newline)
|
|
69
69
|
StringLexResult.new(consumed_lines:, next_index: last_segment_end)
|
|
70
70
|
end
|
|
71
71
|
|
|
@@ -144,7 +144,7 @@ module MilkTea
|
|
|
144
144
|
index += 1
|
|
145
145
|
|
|
146
146
|
lexeme = line[start...index]
|
|
147
|
-
@tokens <<
|
|
147
|
+
@tokens << build_token(:char_literal, lexeme, value, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
|
|
148
148
|
index
|
|
149
149
|
end
|
|
150
150
|
|
|
@@ -9,14 +9,14 @@ module MilkTea
|
|
|
9
9
|
lexeme = line[index, 3]
|
|
10
10
|
if lexeme && THREE_CHAR_TOKENS.key?(lexeme)
|
|
11
11
|
type = THREE_CHAR_TOKENS.fetch(lexeme)
|
|
12
|
-
@tokens <<
|
|
12
|
+
@tokens << build_token(type, lexeme, nil, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 3)
|
|
13
13
|
return index + 3
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
lexeme = line[index, 2]
|
|
17
17
|
if lexeme && TWO_CHAR_TOKENS.key?(lexeme)
|
|
18
18
|
type = TWO_CHAR_TOKENS.fetch(lexeme)
|
|
19
|
-
@tokens <<
|
|
19
|
+
@tokens << build_token(type, lexeme, nil, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 2)
|
|
20
20
|
adjust_grouping_depth(type, line_number, start + 1)
|
|
21
21
|
return index + 2
|
|
22
22
|
end
|
|
@@ -37,7 +37,7 @@ module MilkTea
|
|
|
37
37
|
raise LexError.new("unexpected character #{lexeme.inspect}", line: line_number, column: start + 1, path: @path)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
@tokens <<
|
|
40
|
+
@tokens << build_token(type, lexeme, nil, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 1)
|
|
41
41
|
adjust_grouping_depth(type, line_number, start + 1)
|
|
42
42
|
index + 1
|
|
43
43
|
end
|
|
@@ -9,7 +9,7 @@ module MilkTea
|
|
|
9
9
|
@mode == :with_trivia
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def
|
|
12
|
+
def record_line_trivia(kind, line, line_number, line_offset, has_newline:)
|
|
13
13
|
return unless with_trivia?
|
|
14
14
|
|
|
15
15
|
text = has_newline ? (line + "\n") : line
|
|
@@ -21,17 +21,17 @@ module MilkTea
|
|
|
21
21
|
start_offset: line_offset,
|
|
22
22
|
end_offset: line_offset + text.bytesize,
|
|
23
23
|
)
|
|
24
|
-
|
|
24
|
+
push_leading_trivia(trivia)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def
|
|
27
|
+
def push_leading_trivia(trivia)
|
|
28
28
|
return unless with_trivia?
|
|
29
29
|
|
|
30
30
|
@trivia << trivia
|
|
31
31
|
@pending_leading_trivia << trivia
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def attach_trivia(trivia)
|
|
35
35
|
return unless with_trivia?
|
|
36
36
|
|
|
37
37
|
@trivia << trivia
|
data/lib/milk_tea/core/lexer.rb
CHANGED
|
@@ -171,10 +171,10 @@ module MilkTea
|
|
|
171
171
|
|
|
172
172
|
while @indent_stack.length > 1
|
|
173
173
|
@indent_stack.pop
|
|
174
|
-
@tokens <<
|
|
174
|
+
@tokens << build_token(:dedent, "", nil, @line_count, 1, start_offset: @source.bytesize, end_offset: @source.bytesize)
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
-
@tokens <<
|
|
177
|
+
@tokens << build_token(:eof, "", nil, @line_count + 1, 1, start_offset: @source.bytesize, end_offset: @source.bytesize)
|
|
178
178
|
return LexResult.new(tokens: @tokens, trivia: @trivia) if with_trivia?
|
|
179
179
|
|
|
180
180
|
@tokens
|
|
@@ -193,12 +193,12 @@ module MilkTea
|
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
if line.strip.empty?
|
|
196
|
-
|
|
196
|
+
record_line_trivia(:blank_line, line, line_number, line_offset, has_newline:)
|
|
197
197
|
return 1
|
|
198
198
|
end
|
|
199
199
|
|
|
200
200
|
if line.lstrip.start_with?("#")
|
|
201
|
-
|
|
201
|
+
record_line_trivia(:comment, line, line_number, line_offset, has_newline:)
|
|
202
202
|
return 1
|
|
203
203
|
end
|
|
204
204
|
|
|
@@ -206,11 +206,11 @@ module MilkTea
|
|
|
206
206
|
if @recovery_errors && @grouping_depth.positive? && index.zero? && top_level_resync_line?(line)
|
|
207
207
|
@recovery_errors << LexError.new("unclosed grouping delimiter", line: @grouping_start_line, column: @grouping_start_column, path: @path)
|
|
208
208
|
@grouping_depth = 0
|
|
209
|
-
@tokens <<
|
|
209
|
+
@tokens << build_token(:newline, "\n", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
|
|
210
210
|
end
|
|
211
211
|
|
|
212
212
|
if with_trivia? && index.positive?
|
|
213
|
-
|
|
213
|
+
push_leading_trivia(
|
|
214
214
|
TriviaToken.new(
|
|
215
215
|
kind: :space,
|
|
216
216
|
text: line[0...index],
|
|
@@ -244,7 +244,7 @@ module MilkTea
|
|
|
244
244
|
if with_trivia?
|
|
245
245
|
span_start = index
|
|
246
246
|
index += 1 while index < line.length && line[index] == " "
|
|
247
|
-
|
|
247
|
+
push_leading_trivia(
|
|
248
248
|
TriviaToken.new(
|
|
249
249
|
kind: :space,
|
|
250
250
|
text: line[span_start...index],
|
|
@@ -265,7 +265,7 @@ module MilkTea
|
|
|
265
265
|
if with_trivia?
|
|
266
266
|
comment_end = line.length
|
|
267
267
|
comment_text = line[index...comment_end]
|
|
268
|
-
|
|
268
|
+
attach_trivia(
|
|
269
269
|
TriviaToken.new(
|
|
270
270
|
kind: :comment,
|
|
271
271
|
text: comment_text,
|
|
@@ -280,7 +280,7 @@ module MilkTea
|
|
|
280
280
|
end
|
|
281
281
|
|
|
282
282
|
if (char == "c" || char == "f")
|
|
283
|
-
result =
|
|
283
|
+
result = dispatch_prefixed_literal(char, line, index, line_number, line_offset, lines:, line_index:)
|
|
284
284
|
if result
|
|
285
285
|
if result[0] == :return
|
|
286
286
|
return result[1]
|
|
@@ -327,10 +327,10 @@ module MilkTea
|
|
|
327
327
|
if Token::LINE_CONTINUATION_OPERATORS.include?(@tokens.last&.type)
|
|
328
328
|
@continuation_pending = true
|
|
329
329
|
else
|
|
330
|
-
@tokens <<
|
|
330
|
+
@tokens << build_token(:newline, "\n", nil, line_number, line.length + 1, start_offset: newline_start, end_offset: newline_end)
|
|
331
331
|
end
|
|
332
332
|
elsif with_trivia? && has_newline
|
|
333
|
-
|
|
333
|
+
attach_trivia(
|
|
334
334
|
TriviaToken.new(
|
|
335
335
|
kind: :newline,
|
|
336
336
|
text: "\n",
|
|
@@ -345,14 +345,14 @@ module MilkTea
|
|
|
345
345
|
1
|
|
346
346
|
end
|
|
347
347
|
|
|
348
|
-
def
|
|
348
|
+
def emit_newline(line, line_number, line_offset, has_newline)
|
|
349
349
|
newline_start = line_offset + line.bytesize
|
|
350
350
|
newline_end = has_newline ? (newline_start + 1) : newline_start
|
|
351
351
|
|
|
352
352
|
if @grouping_depth.zero?
|
|
353
|
-
@tokens <<
|
|
353
|
+
@tokens << build_token(:newline, "\n", nil, line_number, line.bytesize + 1, start_offset: newline_start, end_offset: newline_end)
|
|
354
354
|
elsif with_trivia? && has_newline
|
|
355
|
-
|
|
355
|
+
attach_trivia(
|
|
356
356
|
TriviaToken.new(
|
|
357
357
|
kind: :newline,
|
|
358
358
|
text: "\n",
|
|
@@ -365,7 +365,7 @@ module MilkTea
|
|
|
365
365
|
end
|
|
366
366
|
end
|
|
367
367
|
|
|
368
|
-
def
|
|
368
|
+
def dispatch_prefixed_literal(char, line, index, line_number, line_offset, lines:, line_index:)
|
|
369
369
|
return nil unless char == "c" || char == "f"
|
|
370
370
|
|
|
371
371
|
is_c = char == "c"
|
|
@@ -377,7 +377,7 @@ module MilkTea
|
|
|
377
377
|
end
|
|
378
378
|
|
|
379
379
|
if line[index + 1] == "<" && line[index + 2] == "-" && identifier_start?(line[index + 3])
|
|
380
|
-
error = LexError.new("expected '<<-' for heredoc string; did you mean '#{char}<<-#{
|
|
380
|
+
error = LexError.new("expected '<<-' for heredoc string; did you mean '#{char}<<-#{extract_identifier_at(line, index + 3)}'?", line: line_number, column: index + 1, path: @path)
|
|
381
381
|
if @recovery_errors
|
|
382
382
|
@recovery_errors << error
|
|
383
383
|
else
|
|
@@ -402,7 +402,7 @@ module MilkTea
|
|
|
402
402
|
nil
|
|
403
403
|
end
|
|
404
404
|
|
|
405
|
-
def
|
|
405
|
+
def build_token(type, lexeme, literal, line, column, start_offset:, end_offset:)
|
|
406
406
|
Token.new(
|
|
407
407
|
type:,
|
|
408
408
|
lexeme:,
|
|
@@ -1,237 +1,239 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MilkTea
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
param_fields = {}
|
|
14
|
-
local_fields = {}
|
|
15
|
-
await_fields = {}
|
|
16
|
-
await_counter = 0
|
|
17
|
-
|
|
18
|
-
binding.body_params.each do |param_binding|
|
|
19
|
-
pointer = binding.type.receiver_type && binding.type.receiver_editable && param_binding.name == "this"
|
|
20
|
-
field_type = pointer ? pointer_to(param_binding.type) : param_binding.type
|
|
21
|
-
field_name = "param_#{param_binding.name}"
|
|
22
|
-
param_fields[param_binding.name] = {
|
|
23
|
-
field_name:,
|
|
24
|
-
type: field_type,
|
|
25
|
-
param_type: param_binding.type,
|
|
26
|
-
mutable: param_binding.mutable,
|
|
27
|
-
pointer:,
|
|
28
|
-
}
|
|
29
|
-
env[:scopes].last[param_binding.name] = local_binding(
|
|
30
|
-
type: param_binding.type,
|
|
31
|
-
linkage_name: field_name,
|
|
32
|
-
mutable: param_binding.mutable,
|
|
33
|
-
pointer:,
|
|
4
|
+
module Lowering
|
|
5
|
+
module Async
|
|
6
|
+
def analyze_async_function(binding, statements)
|
|
7
|
+
env = empty_env
|
|
8
|
+
void_ptr = pointer_to(@ctx.types.fetch("void"))
|
|
9
|
+
wake_type = Types::Registry.function(
|
|
10
|
+
nil,
|
|
11
|
+
params: [Types::Registry.parameter("frame", void_ptr)],
|
|
12
|
+
return_type: @ctx.types.fetch("void"),
|
|
34
13
|
)
|
|
14
|
+
param_fields = {}
|
|
15
|
+
local_fields = {}
|
|
16
|
+
await_fields = {}
|
|
17
|
+
await_counter = 0
|
|
18
|
+
|
|
19
|
+
binding.body_params.each do |param_binding|
|
|
20
|
+
pointer = binding.type.receiver_type && binding.type.receiver_editable && param_binding.name == "this"
|
|
21
|
+
field_type = pointer ? pointer_to(param_binding.type) : param_binding.type
|
|
22
|
+
field_name = "param_#{param_binding.name}"
|
|
23
|
+
param_fields[param_binding.name] = {
|
|
24
|
+
field_name:,
|
|
25
|
+
type: field_type,
|
|
26
|
+
param_type: param_binding.type,
|
|
27
|
+
mutable: param_binding.mutable,
|
|
28
|
+
pointer:,
|
|
29
|
+
}
|
|
30
|
+
env[:scopes].last[param_binding.name] = local_binding(
|
|
31
|
+
type: param_binding.type,
|
|
32
|
+
linkage_name: field_name,
|
|
33
|
+
mutable: param_binding.mutable,
|
|
34
|
+
pointer:,
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
env[:return_context] = {
|
|
38
|
+
return_type: binding.body_return_type,
|
|
39
|
+
active_defers: [],
|
|
40
|
+
local_defers: [],
|
|
41
|
+
allow_return: true,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await_counter = analyze_async_statements!(statements, await_counter, env, param_fields, local_fields, await_fields)
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
task_type: binding.type.return_type,
|
|
48
|
+
result_type: binding.body_return_type,
|
|
49
|
+
void_ptr:,
|
|
50
|
+
wake_type:,
|
|
51
|
+
param_fields:,
|
|
52
|
+
local_fields:,
|
|
53
|
+
await_fields:,
|
|
54
|
+
format_str_fields: {},
|
|
55
|
+
}
|
|
35
56
|
end
|
|
36
|
-
env[:return_context] = {
|
|
37
|
-
return_type: binding.body_return_type,
|
|
38
|
-
active_defers: [],
|
|
39
|
-
local_defers: [],
|
|
40
|
-
allow_return: true,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
await_counter = analyze_async_statements!(statements, await_counter, env, param_fields, local_fields, await_fields)
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
task_type: binding.type.return_type,
|
|
47
|
-
result_type: binding.body_return_type,
|
|
48
|
-
void_ptr:,
|
|
49
|
-
wake_type:,
|
|
50
|
-
param_fields:,
|
|
51
|
-
local_fields:,
|
|
52
|
-
await_fields:,
|
|
53
|
-
format_str_fields: {},
|
|
54
|
-
}
|
|
55
|
-
end
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
end
|
|
70
|
-
if bind_let_else_local?(statement)
|
|
71
|
-
env[:scopes].last[statement.name] = local_binding(
|
|
72
|
-
type:,
|
|
73
|
-
storage_type:,
|
|
74
|
-
linkage_name: statement.name,
|
|
75
|
-
mutable: statement.kind == :var,
|
|
76
|
-
pointer: false,
|
|
77
|
-
const_value: statement.else_body ? nil : statement.kind == :let && statement.value ? compile_time_const_value(statement.value, env:) : nil,
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
await_counter = analyze_async_statements!(statement.else_body, await_counter, env, param_fields, local_fields, await_fields) if statement.else_body
|
|
81
|
-
when AST::Assignment
|
|
82
|
-
if statement.value.is_a?(AST::AwaitExpr)
|
|
83
|
-
await_fields[statement.value.object_id] = build_async_await_field_info(statement.value, await_counter, env:, param_fields:, local_fields:)
|
|
84
|
-
await_counter += 1
|
|
85
|
-
end
|
|
86
|
-
when AST::ExpressionStmt
|
|
87
|
-
if statement.expression.is_a?(AST::AwaitExpr)
|
|
88
|
-
await_fields[statement.expression.object_id] = build_async_await_field_info(statement.expression, await_counter, env:, param_fields:, local_fields:)
|
|
89
|
-
await_counter += 1
|
|
90
|
-
end
|
|
91
|
-
when AST::ReturnStmt
|
|
92
|
-
if statement.value&.is_a?(AST::AwaitExpr)
|
|
93
|
-
await_fields[statement.value.object_id] = build_async_await_field_info(statement.value, await_counter, env:, param_fields:, local_fields:)
|
|
94
|
-
await_counter += 1
|
|
95
|
-
end
|
|
96
|
-
when AST::IfStmt
|
|
97
|
-
statement.branches.each do |branch|
|
|
98
|
-
await_counter = analyze_async_statements!(branch.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
99
|
-
end
|
|
100
|
-
await_counter = analyze_async_statements!(statement.else_body, await_counter, env, param_fields, local_fields, await_fields) if statement.else_body
|
|
101
|
-
when AST::WhileStmt
|
|
102
|
-
await_counter = analyze_async_statements!(statement.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
103
|
-
when AST::ForStmt
|
|
104
|
-
if range_iterable?(statement.iterable)
|
|
105
|
-
loop_type = infer_range_loop_type(statement.iterable, env:)
|
|
106
|
-
local_fields[statement.name] ||= { field_name: "local_#{statement.name}", type: loop_type, storage_type: loop_type, mutable: true }
|
|
107
|
-
stop_field_name = "local_#{statement.name}_stop"
|
|
108
|
-
local_fields[stop_field_name] ||= { field_name: stop_field_name, type: loop_type, storage_type: loop_type, mutable: true }
|
|
109
|
-
else
|
|
110
|
-
statement.bindings.each_with_index do |binding, index|
|
|
111
|
-
iterable_type = infer_expression_type(statement.iterables[index], env:)
|
|
112
|
-
element_type = collection_loop_type(iterable_type)
|
|
113
|
-
binding_type = collection_loop_binding_type(iterable_type, element_type) || element_type
|
|
114
|
-
local_fields[binding.name] ||= { field_name: "local_#{binding.name}", type: binding_type, storage_type: binding_type, mutable: true }
|
|
115
|
-
iterable_field_name = async_collection_iterable_field_name(statement, index)
|
|
116
|
-
iterable_field_key = async_collection_iterable_field_key(statement, index)
|
|
117
|
-
local_fields[iterable_field_key] ||= { field_name: iterable_field_name, type: iterable_type, storage_type: iterable_type, mutable: true }
|
|
58
|
+
# Recursively scan nested statement bodies for await slots, assigning state IDs.
|
|
59
|
+
# Returns the updated await_counter.
|
|
60
|
+
def analyze_async_statements!(statements, await_counter, env, param_fields, local_fields, await_fields)
|
|
61
|
+
statements.each do |statement|
|
|
62
|
+
case statement
|
|
63
|
+
when AST::LocalDecl
|
|
64
|
+
type, storage_type = async_local_decl_types(statement, env:)
|
|
65
|
+
local_field_key = async_local_decl_field_key(statement)
|
|
66
|
+
local_fields[local_field_key] ||= { field_name: async_local_decl_field_name(statement), type:, storage_type:, mutable: statement.kind == :var }
|
|
67
|
+
if statement.value.is_a?(AST::AwaitExpr)
|
|
68
|
+
await_fields[statement.value.object_id] = build_async_await_field_info(statement.value, await_counter, env:, param_fields:, local_fields:)
|
|
69
|
+
await_counter += 1
|
|
118
70
|
end
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
71
|
+
if bind_let_else_local?(statement)
|
|
72
|
+
env[:scopes].last[statement.name] = local_binding(
|
|
73
|
+
type:,
|
|
74
|
+
storage_type:,
|
|
75
|
+
linkage_name: statement.name,
|
|
76
|
+
mutable: statement.kind == :var,
|
|
77
|
+
pointer: false,
|
|
78
|
+
const_value: statement.else_body ? nil : statement.kind == :let && statement.value ? compile_time_const_value(statement.value, env:) : nil,
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
await_counter = analyze_async_statements!(statement.else_body, await_counter, env, param_fields, local_fields, await_fields) if statement.else_body
|
|
82
|
+
when AST::Assignment
|
|
83
|
+
if statement.value.is_a?(AST::AwaitExpr)
|
|
84
|
+
await_fields[statement.value.object_id] = build_async_await_field_info(statement.value, await_counter, env:, param_fields:, local_fields:)
|
|
85
|
+
await_counter += 1
|
|
86
|
+
end
|
|
87
|
+
when AST::ExpressionStmt
|
|
88
|
+
if statement.expression.is_a?(AST::AwaitExpr)
|
|
89
|
+
await_fields[statement.expression.object_id] = build_async_await_field_info(statement.expression, await_counter, env:, param_fields:, local_fields:)
|
|
90
|
+
await_counter += 1
|
|
91
|
+
end
|
|
92
|
+
when AST::ReturnStmt
|
|
93
|
+
if statement.value&.is_a?(AST::AwaitExpr)
|
|
94
|
+
await_fields[statement.value.object_id] = build_async_await_field_info(statement.value, await_counter, env:, param_fields:, local_fields:)
|
|
95
|
+
await_counter += 1
|
|
96
|
+
end
|
|
97
|
+
when AST::IfStmt
|
|
98
|
+
statement.branches.each do |branch|
|
|
99
|
+
await_counter = analyze_async_statements!(branch.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
100
|
+
end
|
|
101
|
+
await_counter = analyze_async_statements!(statement.else_body, await_counter, env, param_fields, local_fields, await_fields) if statement.else_body
|
|
102
|
+
when AST::WhileStmt
|
|
103
|
+
await_counter = analyze_async_statements!(statement.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
104
|
+
when AST::ForStmt
|
|
105
|
+
if range_iterable?(statement.iterable)
|
|
106
|
+
loop_type = infer_range_loop_type(statement.iterable, env:)
|
|
107
|
+
local_fields[statement.name] ||= { field_name: "local_#{statement.name}", type: loop_type, storage_type: loop_type, mutable: true }
|
|
108
|
+
stop_field_name = "local_#{statement.name}_stop"
|
|
109
|
+
local_fields[stop_field_name] ||= { field_name: stop_field_name, type: loop_type, storage_type: loop_type, mutable: true }
|
|
110
|
+
else
|
|
111
|
+
statement.bindings.each_with_index do |binding, index|
|
|
112
|
+
iterable_type = infer_expression_type(statement.iterables[index], env:)
|
|
113
|
+
element_type = collection_loop_type(iterable_type)
|
|
114
|
+
binding_type = collection_loop_binding_type(iterable_type, element_type) || element_type
|
|
115
|
+
local_fields[binding.name] ||= { field_name: "local_#{binding.name}", type: binding_type, storage_type: binding_type, mutable: true }
|
|
116
|
+
iterable_field_name = async_collection_iterable_field_name(statement, index)
|
|
117
|
+
iterable_field_key = async_collection_iterable_field_key(statement, index)
|
|
118
|
+
local_fields[iterable_field_key] ||= { field_name: iterable_field_name, type: iterable_type, storage_type: iterable_type, mutable: true }
|
|
119
|
+
end
|
|
120
|
+
index_field_name = async_collection_index_field_name(statement)
|
|
121
|
+
index_field_key = async_collection_index_field_key(statement)
|
|
122
|
+
local_fields[index_field_key] ||= { field_name: index_field_name, type: @ctx.types.fetch("ptr_uint"), storage_type: @ctx.types.fetch("ptr_uint"), mutable: true }
|
|
123
|
+
end
|
|
124
|
+
await_counter = analyze_async_statements!(statement.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
125
|
+
when AST::MatchStmt
|
|
126
|
+
scrutinee_type = infer_expression_type(statement.expression, env:)
|
|
127
|
+
statement.arms.each do |arm|
|
|
128
|
+
arm_env = duplicate_env(env)
|
|
129
|
+
bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
|
|
130
|
+
arm_await_count = await_counter
|
|
131
|
+
await_counter = analyze_async_statements!(arm.body, await_counter, arm_env, param_fields, local_fields, await_fields)
|
|
132
|
+
if arm.binding_name && await_counter > arm_await_count
|
|
133
|
+
field_key = async_match_binding_field_key(arm)
|
|
134
|
+
unless local_fields.key?(field_key)
|
|
135
|
+
arm_name = variant_match_arm_name_from_pattern(arm.pattern)
|
|
136
|
+
if arm_name
|
|
137
|
+
arm_binding = arm_env[:scopes].last[arm.binding_name]
|
|
138
|
+
payload_type = arm_binding && arm_binding[:type]
|
|
139
|
+
if payload_type
|
|
140
|
+
local_fields[field_key] = { field_name: async_match_binding_field_name(arm), type: payload_type, storage_type: payload_type, mutable: false }
|
|
141
|
+
end
|
|
140
142
|
end
|
|
141
143
|
end
|
|
142
144
|
end
|
|
143
145
|
end
|
|
146
|
+
when AST::UnsafeStmt
|
|
147
|
+
await_counter = analyze_async_statements!(statement.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
148
|
+
when AST::DeferStmt
|
|
149
|
+
cleanup_env = duplicate_env(env)
|
|
150
|
+
cleanup_env[:return_context] = cleanup_env[:return_context]&.merge(allow_return: false)
|
|
151
|
+
await_counter = analyze_async_statements!(statement.body, await_counter, cleanup_env, param_fields, local_fields, await_fields)
|
|
152
|
+
else
|
|
153
|
+
nil
|
|
144
154
|
end
|
|
145
|
-
when AST::UnsafeStmt
|
|
146
|
-
await_counter = analyze_async_statements!(statement.body, await_counter, env, param_fields, local_fields, await_fields)
|
|
147
|
-
when AST::DeferStmt
|
|
148
|
-
cleanup_env = duplicate_env(env)
|
|
149
|
-
cleanup_env[:return_context] = cleanup_env[:return_context]&.merge(allow_return: false)
|
|
150
|
-
await_counter = analyze_async_statements!(statement.body, await_counter, cleanup_env, param_fields, local_fields, await_fields)
|
|
151
|
-
else
|
|
152
|
-
nil
|
|
153
155
|
end
|
|
156
|
+
await_counter
|
|
154
157
|
end
|
|
155
|
-
await_counter
|
|
156
|
-
end
|
|
157
158
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
[type, storage_type]
|
|
173
|
-
end
|
|
159
|
+
def async_local_decl_types(statement, env:)
|
|
160
|
+
storage_type = if statement.else_body
|
|
161
|
+
infer_expression_type(statement.value, env:)
|
|
162
|
+
elsif statement.type
|
|
163
|
+
resolve_type_ref(statement.type)
|
|
164
|
+
else
|
|
165
|
+
infer_expression_type(statement.value, env:)
|
|
166
|
+
end
|
|
167
|
+
type = if statement.else_body
|
|
168
|
+
statement.type ? resolve_type_ref(statement.type) : let_else_success_type(storage_type)
|
|
169
|
+
else
|
|
170
|
+
storage_type
|
|
171
|
+
end
|
|
174
172
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
173
|
+
[type, storage_type]
|
|
174
|
+
end
|
|
178
175
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
def async_collection_iterable_field_key(statement, index = 0)
|
|
177
|
+
"__async_for_iterable_#{statement.line}_#{index}"
|
|
178
|
+
end
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
def async_collection_iterable_field_name(statement, index = 0)
|
|
181
|
+
"for_iterable_#{statement.line}_#{index}"
|
|
182
|
+
end
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
def async_collection_index_field_key(statement)
|
|
185
|
+
"__async_for_index_#{statement.line}"
|
|
186
|
+
end
|
|
190
187
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
field_name: reused_field_name || "await_#{await_counter}",
|
|
196
|
-
task_type: infer_expression_type(task_expression, env:),
|
|
197
|
-
result_type: infer_expression_type(await_expression, env:),
|
|
198
|
-
state: await_counter + 1,
|
|
199
|
-
reuse_existing_storage: !reused_field_name.nil?,
|
|
200
|
-
}
|
|
201
|
-
end
|
|
188
|
+
def async_collection_index_field_name(statement)
|
|
189
|
+
"for_index_#{statement.line}"
|
|
190
|
+
end
|
|
202
191
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
192
|
+
def build_async_await_field_info(await_expression, await_counter, env:, param_fields:, local_fields:)
|
|
193
|
+
task_expression = await_expression.expression
|
|
194
|
+
reused_field_name = reusable_async_await_task_field_name(task_expression, param_fields:, local_fields:)
|
|
195
|
+
{
|
|
196
|
+
field_name: reused_field_name || "await_#{await_counter}",
|
|
197
|
+
task_type: infer_expression_type(task_expression, env:),
|
|
198
|
+
result_type: infer_expression_type(await_expression, env:),
|
|
199
|
+
state: await_counter + 1,
|
|
200
|
+
reuse_existing_storage: !reused_field_name.nil?,
|
|
201
|
+
}
|
|
202
|
+
end
|
|
207
203
|
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
def reusable_async_await_task_field_name(task_expression, param_fields:, local_fields:)
|
|
205
|
+
return unless task_expression.is_a?(AST::Identifier)
|
|
206
|
+
return local_fields.fetch(task_expression.name)[:field_name] if local_fields.key?(task_expression.name)
|
|
207
|
+
return param_fields.fetch(task_expression.name)[:field_name] if param_fields.key?(task_expression.name)
|
|
210
208
|
|
|
211
|
-
|
|
212
|
-
fields = {
|
|
213
|
-
"ready" => @ctx.types.fetch("bool"),
|
|
214
|
-
"cancelled" => @ctx.types.fetch("bool"),
|
|
215
|
-
"waiter_frame" => async_info[:void_ptr],
|
|
216
|
-
"waiter" => async_info[:wake_type],
|
|
217
|
-
}
|
|
218
|
-
fields["state"] = @ctx.types.fetch("int") unless async_info[:await_fields].empty?
|
|
219
|
-
unless async_info[:result_type] == @ctx.types.fetch("void")
|
|
220
|
-
fields["result"] = async_info[:result_type]
|
|
221
|
-
end
|
|
222
|
-
async_info[:param_fields].each_value do |field_info|
|
|
223
|
-
fields[field_info[:field_name]] = field_info[:type]
|
|
209
|
+
nil
|
|
224
210
|
end
|
|
225
|
-
async_info[:local_fields].each_value do |field_info|
|
|
226
|
-
fields[field_info[:field_name]] = field_info[:storage_type]
|
|
227
|
-
end
|
|
228
|
-
async_info[:await_fields].each_value do |field_info|
|
|
229
|
-
next if fields.key?(field_info[:field_name])
|
|
230
211
|
|
|
231
|
-
|
|
232
|
-
|
|
212
|
+
def build_async_frame_type(frame_linkage_name, async_info)
|
|
213
|
+
fields = {
|
|
214
|
+
"ready" => @ctx.types.fetch("bool"),
|
|
215
|
+
"cancelled" => @ctx.types.fetch("bool"),
|
|
216
|
+
"waiter_frame" => async_info[:void_ptr],
|
|
217
|
+
"waiter" => async_info[:wake_type],
|
|
218
|
+
}
|
|
219
|
+
fields["state"] = @ctx.types.fetch("int") unless async_info[:await_fields].empty?
|
|
220
|
+
unless async_info[:result_type] == @ctx.types.fetch("void")
|
|
221
|
+
fields["result"] = async_info[:result_type]
|
|
222
|
+
end
|
|
223
|
+
async_info[:param_fields].each_value do |field_info|
|
|
224
|
+
fields[field_info[:field_name]] = field_info[:type]
|
|
225
|
+
end
|
|
226
|
+
async_info[:local_fields].each_value do |field_info|
|
|
227
|
+
fields[field_info[:field_name]] = field_info[:storage_type]
|
|
228
|
+
end
|
|
229
|
+
async_info[:await_fields].each_value do |field_info|
|
|
230
|
+
next if fields.key?(field_info[:field_name])
|
|
233
231
|
|
|
234
|
-
|
|
232
|
+
fields[field_info[:field_name]] = field_info[:task_type]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
Types::Struct.new(frame_linkage_name).define_fields(fields)
|
|
236
|
+
end
|
|
235
237
|
end
|
|
236
238
|
end
|
|
237
239
|
end
|