Skip to content
← Back

src/semantic/symbols/async_state_machine.ghul

1
namespace Semantic.Symbols is
2
use IO.Std
3
4
use System.Text.StringBuilder
5
6
use IoC
7
use Logging
8
use Source
9
10
use IR.Values.Value
11
12
use Types.Type
13
14
// Per-`let await` bookkeeping recorded by await IL emission.
15
// The resume label is placed immediately after the per-await
16
// `leave` that suspends MoveNext; entry-dispatch jumps here
17
// when the builder re-enters MoveNext on awaiter completion.
18
class ASYNC_STATE_LABEL is
19
state: int public
20
awaiter_field: Field public
21
label: IR.LABEL public
22
23
init(state: int, awaiter_field: Field, label: IR.LABEL) is
24
self.state = state
25
self.awaiter_field = awaiter_field
26
self.label = label
27
si
28
si
29
30
// State held on an async function symbol: the synthesised frame
31
// class, the running state-number counter, the recorded
32
// (state, awaiter_field, resume_label) triples for entry-dispatch
33
// emission. Lives as a non-null field on each *_ASYNC_*
34
// function; checked-for via `async_state_machine_for(...)`.
35
class ASYNC_STATE_MACHINE is
36
function: Function public
37
38
_frame: ASYNC_STATE_MACHINE_FRAME?
39
_next_state: int
40
_await_labels: Collections.LIST[ASYNC_STATE_LABEL]
41
42
init(function: Function) is
43
self.function = function
44
45
_next_state = 1
46
_await_labels = Collections.LIST[ASYNC_STATE_LABEL]()
47
si
48
49
// Drops the frame so the next access builds it afresh from the
50
// function's current return type, returning the frame dropped
51
// so its owner can forget whatever was recorded against it. A closure is walked more
52
// than once while its slot is being resolved, and a frame built
53
// on the first walk carries that walk's builder and result
54
// types - wrong once a later walk settles the closure on a
55
// different task-like.
56
reset() -> ASYNC_STATE_MACHINE_FRAME? is
57
let dropped = _frame
58
59
_frame = null
60
_next_state = 1
61
_await_labels.clear()
62
63
return dropped
64
si
65
66
// Lazy: the frame class is materialised on first access,
67
// pulling its result type from the (now-resolved) function
68
// return type (Tasks.TASK[T] → T, Tasks.TASK → void). Returns
69
// null only when the return type isn't a Task — which is an
70
// upstream diagnostic, not something the frame can recover
71
// from.
72
frame: ASYNC_STATE_MACHINE_FRAME? is
73
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
74
75
if !_frame? then
76
// A return type that failed to resolve has been reported
77
// where it was written; a frame built on it would only
78
// fail again, less legibly, when declared.
79
if !function.return_type? \/ function.return_type.is_error then
80
return null
81
fi
82
83
let result_type: Type? mut = null
84
let is_void mut = false
85
86
// A task-like return type settles both at once: the
87
// result is its single type argument, and a non-generic
88
// task-like has none. Task is itself a task-like through
89
// its AsyncMethodBuilderAttribute, so this covers
90
// Tasks.TASK the same way it covers any other. Lambdas
91
// with INFERRED_RETURN_TYPE can't resolve (an inferred
92
// placeholder matches anything) so they fall through to
93
// the AST flag `is_void_async`; `result_type` fills in
94
// later via `ensure_result_field`.
95
let task_like =
96
TASK_LIKE_RESOLVER(IoC.CONTAINER.instance.logger)
97
.resolve(function.location, function.return_type)
98
99
if task_like? then
100
if let task_result = task_like.result_type then
101
result_type = task_result
102
else
103
is_void = true
104
fi
105
else
106
let extracted = _extract_result_type(lookup)
107
108
if extracted? then
109
result_type = extracted
110
elif function.return_type!.is_inferred then
111
is_void = function.is_void_async
112
else
113
let void_task = lookup.get_void_task_type()
114
if void_task? /\ function.return_type!.matches(void_task) then
115
is_void = true
116
else
117
return null
118
fi
119
fi
120
fi
121
122
_frame = ASYNC_STATE_MACHINE_FRAME(
123
function.owner!,
124
function,
125
result_type,
126
is_void
127
)
128
else
129
// Re-poll on each access: a value-async lambda whose
130
// return type wasn't yet settled when the frame was
131
// first realised needs its _result_type filled in
132
// once the type pins. Idempotent.
133
let frame = _frame
134
frame.ensure_result_field(lookup)
135
return frame
136
fi
137
138
return _frame
139
si
140
141
_extract_result_type(lookup: Lookups.InnateSymbolLookup) -> Type? =>
142
TYPE_ARGUMENT_EXTRACTOR.extract(function.return_type, lookup.get_unspecialized_task_type())
143
144
allocate_state() -> int is
145
let n = _next_state
146
147
_next_state = _next_state + 1
148
149
return n
150
si
151
152
record_label(state: int, awaiter_field: Field, label: IR.LABEL) is
153
_await_labels.add(ASYNC_STATE_LABEL(state, awaiter_field, label))
154
si
155
156
await_labels: Collections.Iterable[ASYNC_STATE_LABEL] => _await_labels
157
158
// See `STATE_MACHINE_TYPE_PARAMS` — generator + async use
159
// the same capture order so the helper handles both.
160
install_body_emission_overrides() is
161
STATE_MACHINE_TYPE_PARAMS.walk_install(function, true)
162
si
163
164
uninstall_body_emission_overrides() is
165
STATE_MACHINE_TYPE_PARAMS.walk_install(function, false)
166
si
167
168
get_construction_type_arguments() -> Collections.List[Type]? =>
169
STATE_MACHINE_TYPE_PARAMS.construction_type_arguments(function)
170
si
171
172
// Frame class synthesised for each async function. Implements
173
// `IAsyncStateMachine` (MoveNext, SetStateMachine); holds the
174
// current state, a builder, per-await awaiter fields, captured
175
// arguments + locals + outer-self, and a result slot for
176
// value-async (omitted for void-async). The class itself is
177
// generic in any type parameters visible inside the owning
178
// function (mirrors `STATE_MACHINE_FRAME`).
179
class ASYNC_STATE_MACHINE_FRAME: STATE_MACHINE_FRAME_BASE is
180
_next_id: int static
181
182
// Null for void-async, and for a value-async lambda whose
183
// inferred return type hasn't pinned yet (filled in by
184
// ensure_result_field).
185
_result_type: Type?
186
_is_void: bool
187
188
// Null until declare() runs. Builder, result and outer-self
189
// stay null past declare() on some paths: builder when the
190
// builder type cannot be resolved, result for void-async,
191
// outer-self for statics and globals.
192
_state_field: Field?
193
_builder_field: Field?
194
_result_field: Field?
195
196
result_type: Type? => _result_type
197
is_void: bool => _is_void
198
199
class_result_type: Type? =>
200
if _result_type? then
201
_class_relative(_result_type)
202
else
203
null
204
fi
205
206
// Non-null once declare() has run.
207
state_field: Field => _state_field!
208
209
builder_field: Field? => _builder_field
210
result_field: Field? => _result_field
211
212
next_id: int static is
213
let result = _next_id
214
_next_id = _next_id + 1
215
return result
216
si
217
218
init(owner: Scope, owning_function: Function, result_type: Type?, is_void: bool) is
219
let owner_owner: Scope mut
220
221
if isa Symbol(owner) then
222
let owner_symbol = owner
223
owner_owner = owner_symbol.owner!
224
else
225
owner_owner = owner
226
fi
227
228
super.init(
229
LOCATION.internal,
230
LOCATION.internal,
231
owner_owner,
232
"$AsyncStateMachine_{owning_function.name}_{next_id}",
233
owner,
234
owning_function
235
)
236
237
_result_type = result_type
238
_is_void = is_void
239
240
_awaiter_fields = Collections.LIST[Field]()
241
242
set_type(Types.NAMED(self))
243
si
244
245
_awaiter_fields: Collections.LIST[Field]
246
247
awaiter_fields: Collections.Iterable[Field] => _awaiter_fields
248
249
// The type parameters and the `$outer_self` field, declared
250
// apart from the members that wait on the return type. A
251
// literal whose return is inferred declares nothing else until
252
// that return settles, and a capture load built in the meantime
253
// has to find the field: without it the load falls back to a
254
// plain self reference, which inside MoveNext is the state
255
// machine rather than the captures frame.
256
ensure_outer_self_field() is
257
let listener = IoC.CONTAINER.instance.symbol_definition_locations
258
259
// A closure's captured type arguments arrive while its body
260
// is walked, so the set is empty at the first call and
261
// complete only by emission. Asking again each time is what
262
// lets the parameters that arrive late be declared at all;
263
// the declaration itself only ever adds.
264
declare_captured_type_params(listener)
265
266
let outer_classy = _outer_self_target()
267
268
if !outer_classy? then
269
return
270
fi
271
272
let self_type = _target_type(outer_classy)
273
274
// The frame is a class of its own, and the type parameters
275
// it carries are installed as its captures are emitted, so
276
// the type naming it is only complete once that has
277
// happened. Re-derive rather than keep what the first call
278
// produced, which named the frame with no arguments at all.
279
if let existing = _outer_self_field then
280
existing.set_type(self_type)
281
282
return
283
fi
284
285
let outer_self_field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$outer_self")
286
outer_self_field.set_type(self_type)
287
declare(LOCATION.internal, outer_self_field, listener)
288
_outer_self_field = outer_self_field
289
si
290
291
// The target named over this machine's own parameters. The
292
// target carries the same set this machine mirrors - a frame is
293
// built from the captures, and an enclosing class supplies them
294
// through the receiver - so pairing them off by position, on
295
// the symbols rather than on their names, gives the target the
296
// arguments a reference from inside MoveNext resolves against.
297
// Falls back to the name-matched form while the two disagree,
298
// which is every call before the target has its own.
299
_target_type(outer_classy: Classy) -> Type is
300
let parameters = STATE_MACHINE_TYPE_PARAMS.captured_parameters(owning_function)
301
302
if
303
parameters.count == 0 \/
304
!outer_classy.is_generic \/
305
outer_classy.argument_names.count != parameters.count
306
then
307
return outer_self_type(outer_classy)
308
fi
309
310
let arguments = Collections.LIST[Type]()
311
312
for parameter in parameters do
313
if !mirrors(parameter) then
314
return outer_self_type(outer_classy)
315
fi
316
317
arguments.add(mirror_of(parameter))
318
od
319
320
return Types.GENERIC(LOCATION.internal, outer_classy, arguments)
321
si
322
323
// What `$outer_self` points at: the captures frame for a
324
// closure that has one, since the launch is emitted as a method
325
// on that frame, and otherwise the enclosing class for an
326
// instance method. A closure's own owner is the enclosing class
327
// until emission repoints it at the frame, so asking the
328
// closure first is what keeps the two apart.
329
_outer_self_target() -> Classy? is
330
if let closure: Closure = owning_function then
331
if let closure.frame? then
332
return frame
333
fi
334
fi
335
336
if owning_function.is_instance /\ isa Classy(owning_function.owner) then
337
return cast Classy?(owning_function.owner)!
338
fi
339
340
return null
341
si
342
343
// Lazy + idempotent realisation of frame members. Value-async
344
// frames short-circuit until `_result_type` is available
345
// (otherwise we'd resolve `AsyncTaskMethodBuilder<null>`).
346
declare() is
347
ensure_outer_self_field()
348
349
if _state_field? then
350
// A closure's first declare() runs mid-inference, so an
351
// inferred parameter group's field captured a settled-
352
// later INFERRED_VARIABLE_TYPE placeholder. Recompute
353
// from the locals on every subsequent call - generate-il
354
// declares again before emitting the frame, by which
355
// point inference has settled.
356
refresh_argument_field_types()
357
return
358
fi
359
360
if !_is_void /\ !_result_type? then
361
return
362
fi
363
364
let listener = IoC.CONTAINER.instance.symbol_definition_locations
365
366
let int_type = IoC.CONTAINER.instance.innate_symbol_lookup.get_int_type()
367
368
let state_field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$state")
369
state_field.set_type(int_type)
370
declare(LOCATION.internal, state_field, listener)
371
_state_field = state_field
372
373
// Builder field — `AsyncTaskMethodBuilder<T>` for
374
// value-async, non-generic for void-async. Omitted when
375
// the type cannot be resolved; IL emission supplies it
376
// literally.
377
let builder_type = _resolve_builder_type()
378
if builder_type? then
379
let builder_field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$builder")
380
builder_field.set_type(builder_type)
381
declare(LOCATION.internal, builder_field, listener)
382
_builder_field = builder_field
383
fi
384
385
// `$result` only for value-async. Lambdas with
386
// unresolved result_type defer to `ensure_result_field`.
387
if !_is_void /\ _result_type? then
388
let result_type = _result_type
389
390
let result_field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$result")
391
result_field.set_type(_class_relative(result_type))
392
declare(LOCATION.internal, result_field, listener)
393
_result_field = result_field
394
fi
395
396
let ctor_argument_names = Collections.LIST[string]()
397
let ctor_argument_types = Collections.LIST[Type]()
398
399
if let outer_self_field = _outer_self_field then
400
ctor_argument_names.add("$outer_self")
401
ctor_argument_types.add(outer_self_field.type!)
402
fi
403
404
if owning_function.argument_names.count > 0 then
405
for arg_name in owning_function.argument_names do
406
let local = cast Symbols.LOCAL_ARGUMENT?(owning_function.find_direct(arg_name))
407
408
if !local? then
409
continue
410
fi
411
412
// An async closure's parameter group with no
413
// written aggregate type carries an
414
// INFERRED_VARIABLE_TYPE placeholder - settled by
415
// the body-retry walk, but still held as the
416
// Variable's type. Collapse it to the concrete type
417
// here: a placeholder in a field signature reaches
418
// the encoder and is refused.
419
let arg_field_type = _class_relative(
420
SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(local.type ?? Types.ERROR()))
421
422
let arg_field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$arg_{arg_name}")
423
arg_field.set_type(arg_field_type)
424
declare(LOCATION.internal, arg_field, listener)
425
426
_argument_fields.add(arg_field)
427
428
local.state_machine_field = arg_field
429
430
ctor_argument_names.add(arg_name)
431
ctor_argument_types.add(arg_field_type)
432
od
433
fi
434
435
let ctor = Symbols.INSTANCE_METHOD(LOCATION.internal, LOCATION.internal, self, "init", self)
436
ctor.set_arguments(ctor_argument_names, ctor_argument_types)
437
ctor.set_void_return_type()
438
declare(LOCATION.internal, ctor, listener)
439
_constructor = ctor
440
441
_ctor_argument_names = ctor_argument_names
442
443
// Ancestors: Object as base class, IAsyncStateMachine as
444
// implemented interface. The frame doesn't expose
445
// Iterable/Iterator like the generator frame; the runtime
446
// calls MoveNext via the builder.
447
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
448
449
add_ancestor(lookup.get_object_type())
450
451
let async_state_machine_type = _resolve_async_state_machine_type()
452
if async_state_machine_type? then
453
add_ancestor(async_state_machine_type)
454
fi
455
si
456
457
// The builder the owning function's return type names through its
458
// AsyncMethodBuilderAttribute - `AsyncTaskMethodBuilder<T>` for
459
// Task[T], `AsyncValueTaskMethodBuilder<T>` for ValueTask[T], or
460
// whatever a source-declared task-like names. Falls back to the
461
// Task builders when the return type resolves no task-like, which
462
// keeps the Task path answering even where the attribute read
463
// cannot run.
464
_resolve_builder_type() -> Type? is
465
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
466
467
let task_like =
468
TASK_LIKE_RESOLVER(IoC.CONTAINER.instance.logger)
469
.resolve(owning_function.location, owning_function.return_type)
470
471
if !task_like? then
472
if _is_void then
473
return lookup.get_async_task_method_builder_void_type()
474
else
475
if !_result_type? then
476
return null
477
fi
478
let class_result = _class_relative(_result_type)
479
return lookup.get_async_task_method_builder_type(class_result)
480
fi
481
fi
482
483
let builder = task_like.builder_type
484
let builder_classy = cast Symbols.Classy?(builder.symbol.unspecialized_symbol)
485
486
if !builder_classy? then
487
return builder
488
fi
489
490
// A generic builder takes the task-like's result as its own
491
// type argument, at the frame's class-relative spelling.
492
if builder_classy.is_generic then
493
if !_result_type? then
494
return null
495
fi
496
497
let class_result = _class_relative(_result_type)
498
499
return
500
Types.GENERIC(
501
Source.LOCATION.internal,
502
builder_classy,
503
Collections.LIST[Type]([class_result])
504
)
505
fi
506
507
return builder
508
si
509
510
_resolve_async_state_machine_type() -> Type? is
511
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
512
return lookup.get_async_state_machine_interface_type()
513
si
514
515
// Late-fill `_result_type` once the owning lambda's
516
// wrap-as-task pin settles. Unblocks declare() for value-
517
// async closures whose return type was inferred. Idempotent.
518
ensure_result_field(lookup: Lookups.InnateSymbolLookup) is
519
if _is_void \/ _result_type? then
520
return
521
fi
522
if !owning_function.return_type? then
523
return
524
fi
525
526
let extracted = TYPE_ARGUMENT_EXTRACTOR.extract(
527
owning_function.return_type,
528
lookup.get_unspecialized_task_type()
529
)
530
531
if extracted? then
532
_result_type = extracted
533
return
534
fi
535
536
// A closure pinned to a task-like other than Task carries
537
// its result the same way, as the single type argument.
538
let task_like =
539
TASK_LIKE_RESOLVER(IoC.CONTAINER.instance.logger)
540
.resolve(owning_function.location, owning_function.return_type)
541
542
if let result = task_like?.result_type then
543
_result_type = result
544
return
545
fi
546
547
// A literal whose return was left to its body and settled
548
// result-less is void-async after all: the frame realised
549
// while the return was still inferred holds the value-async
550
// shape, and follows the settled return here. An inferred
551
// return says nothing yet - a placeholder matches every
552
// type, this one included - so it waits for a later poll.
553
if owning_function.return_type!.is_inferred then
554
return
555
fi
556
557
if task_like? then
558
_is_void = true
559
return
560
fi
561
562
if let void_task = lookup.get_void_task_type() then
563
if owning_function.return_type!.matches(void_task) then
564
_is_void = true
565
fi
566
fi
567
si
568
569
// Per-await awaiter field. Each call site gets its own slot
570
// since the awaiter is typically a value type
571
// (`TaskAwaiter<T>` / its non-generic sibling).
572
// The awaiter field for an `await` a body re-walk reaches
573
// again. Same reasoning as the frame base's anonymous fields:
574
// reuse the field rather than leaving a dead one behind, and
575
// retype it so an awaiter type the first walk had not settled
576
// still reaches the emitter.
577
declare_or_retype_awaiter_field(existing: Field?, awaiter_type: Type) -> Field is
578
if let `field = existing /\ `field.owner == self then
579
`field.set_type(_class_relative(awaiter_type))
580
581
return `field
582
fi
583
584
return declare_awaiter_field(awaiter_type)
585
si
586
587
declare_awaiter_field(awaiter_type: Type) -> Field is
588
let id = next_local_id()
589
590
let listener = IoC.CONTAINER.instance.symbol_definition_locations
591
592
let `field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$awaiter_{id}")
593
`field.set_type(_class_relative(awaiter_type))
594
declare(LOCATION.internal, `field, listener)
595
596
_awaiter_fields.add(`field)
597
598
return `field
599
si
600
601
si
602
603
// Free helper: returns the ASYNC_STATE_MACHINE held by any of
604
// the async function or closure forms, or null if `f` is a plain
605
// function / generator / sync closure.
606
async_state_machine_for(f: Function?) -> ASYNC_STATE_MACHINE? is
607
if !f? then
608
return null
609
fi
610
611
if isa STATIC_ASYNC_METHOD(f) then
612
return f.async_state_machine
613
fi
614
615
if isa INSTANCE_ASYNC_METHOD(f) then
616
return f.async_state_machine
617
fi
618
619
if isa GLOBAL_ASYNC_FUNCTION(f) then
620
return f.async_state_machine
621
fi
622
623
if isa INSTANCE_ASYNC_CLOSURE(f) then
624
return f.async_state_machine
625
fi
626
627
if isa STATIC_ASYNC_CLOSURE(f) then
628
return f.async_state_machine
629
fi
630
631
if isa GLOBAL_ASYNC_CLOSURE(f) then
632
return f.async_state_machine
633
fi
634
635
return null
636
si
637
638
// Concrete async-function forms — thin extensions of the
639
// STATIC_METHOD / INSTANCE_METHOD / GLOBAL_FUNCTION classes
640
// that additionally carry an ASYNC_STATE_MACHINE.
641
642
class STATIC_ASYNC_METHOD: STATIC_METHOD is
643
async_state_machine: ASYNC_STATE_MACHINE public
644
645
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
646
"class async"
647
648
is_accessible_to(accessor: Classy?) -> bool =>
649
_underscore_is_accessible_to(accessor)
650
651
init(location: LOCATION, span: LOCATION, owner: Scope, name: string, enclosing_scope: Scope) is
652
super.init(location, span, owner, name, enclosing_scope)
653
654
async_state_machine = ASYNC_STATE_MACHINE(self)
655
si
656
si
657
658
class INSTANCE_ASYNC_METHOD: INSTANCE_METHOD is
659
async_state_machine: ASYNC_STATE_MACHINE public
660
661
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
662
"async"
663
664
is_accessible_to(accessor: Classy?) -> bool =>
665
_underscore_is_accessible_to(accessor)
666
667
init(location: LOCATION, span: LOCATION, owner: Scope, name: string, enclosing_scope: Scope) is
668
super.init(location, span, owner, name, enclosing_scope)
669
670
async_state_machine = ASYNC_STATE_MACHINE(self)
671
si
672
673
// Inside the body, `self` and instance-member access go via
674
// the async state-machine frame's _outer_self field rather
675
// than ldarg.0 (which inside MoveNext refers to the state
676
// machine itself). Mirrors INSTANCE_GENERATOR_METHOD.load_self.
677
load_self(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
678
let frame = async_state_machine.frame
679
680
if frame? then
681
frame.declare()
682
683
let outer_self_field = frame.outer_self_field
684
685
if outer_self_field? then
686
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context
687
if context? then
688
return IR.Values.Load.OUTER_SELF(context, context.type, outer_self_field)
689
fi
690
fi
691
fi
692
693
return super.load_self(location, loader)
694
si
695
si
696
697
// An async method declared in a trait body. Always has a body — the
698
// async classification comes from finding `await` in one — so there
699
// is no abstract counterpart, and it is a default trait method for
700
// inheritance purposes like any other bodied trait member.
701
class DEFAULT_TRAIT_ASYNC_METHOD: INSTANCE_ASYNC_METHOD is
702
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
703
"default trait async"
704
705
is_default_trait_method: bool => true
706
707
init(location: LOCATION, span: LOCATION, owner: Scope, name: string, enclosing_scope: Scope) is
708
super.init(location, span, owner, name, enclosing_scope)
709
si
710
711
try_instance_override_me(into: Classy, overrider: Function, logger: Logger) is
712
super.try_instance_override_me(into, overrider, logger)
713
714
_check_ineffective_trait_override(into, overrider, logger)
715
si
716
si
717
718
class GLOBAL_ASYNC_FUNCTION: GLOBAL_FUNCTION is
719
async_state_machine: ASYNC_STATE_MACHINE public
720
721
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
722
"global async"
723
724
init(location: LOCATION, span: LOCATION, owner: Scope, name: string, enclosing_scope: Scope) is
725
super.init(location, span, owner, name, enclosing_scope)
726
727
async_state_machine = ASYNC_STATE_MACHINE(self)
728
si
729
si
730
731
// Async closure forms — parallel to the *_ASYNC_METHOD /
732
// GLOBAL_ASYNC_FUNCTION trio, but for user-written async lambdas.
733
// Each is a thin extension of the corresponding sync closure that
734
// additionally carries an ASYNC_STATE_MACHINE; generate-il dispatches
735
// on `async_state_machine_for(symbol)` to route lambda emission down
736
// the state-machine path. Nested lambdas inside an async closure
737
// are sync by default — `declare_closure` returns the regular
738
// form, matching the named-function convention.
739
740
class INSTANCE_ASYNC_CLOSURE: INSTANCE_CLOSURE is
741
async_state_machine: ASYNC_STATE_MACHINE public
742
743
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
744
super.init(location, owner, name, enclosing_scope, is_recursive)
745
746
async_state_machine = ASYNC_STATE_MACHINE(self)
747
si
748
749
to_string() -> string => "[instance async closure {name}]"
750
si
751
752
class STATIC_ASYNC_CLOSURE: STATIC_CLOSURE is
753
async_state_machine: ASYNC_STATE_MACHINE public
754
755
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
756
super.init(location, owner, name, enclosing_scope, is_recursive)
757
758
async_state_machine = ASYNC_STATE_MACHINE(self)
759
si
760
761
to_string() -> string => "[static async closure {name}]"
762
si
763
764
class GLOBAL_ASYNC_CLOSURE: GLOBAL_CLOSURE is
765
async_state_machine: ASYNC_STATE_MACHINE public
766
767
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
768
super.init(location, owner, name, enclosing_scope, is_recursive)
769
770
async_state_machine = ASYNC_STATE_MACHINE(self)
771
si
772
773
to_string() -> string => "[global async closure {name}]"
774
si
775
si