Skip to content
← Back

src/semantic/symbols/closure.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 Ghul.Pipes
13
14
class Closure: Function, Types.Typed abstract is
15
_is_loading_captures: bool
16
17
// A null element is the `$self` capture: the frame's captured
18
// instance has no symbol of its own, and its position in the set
19
// fixes its position in the frame constructor's argument list.
20
captured_values: Collections.SET[Symbol?]?
21
captured_type_arguments: Collections.MAP[Symbol, CAPTURED_TYPE_ARGUMENT]?
22
next_type_argument_index: int
23
24
is_self_captured: bool
25
is_delegate: bool
26
is_anon_func: bool => !frame? /\ !is_delegate
27
28
// The named delegate type this literal was compiled as, when its
29
// context expected one. The literal's own type stays the
30
// equivalent function type, so inference and the body walk are
31
// unaffected; only the constructed delegate differs.
32
delegate_target_type: Types.Type? public
33
34
// The parameters a literal written with an argument pack spread
35
// out declares in place of the one tuple it is emitted taking, in
36
// order, and absent for every other literal. The emitted method
37
// takes `pack_group` and unpacks it into a local for each on entry.
38
packed_parameters: Collections.List[Variable]? public
39
pack_group: LOCAL_ARGUMENT? public
40
41
// Set for a generated argument pack thunk, which has no literal
42
// behind it and whose body is emitted from this.
43
pack_thunk: PACK_THUNK? public
44
45
is_packed_parameter(symbol: Symbol) -> bool is
46
if let packed = packed_parameters then
47
for p in packed do
48
if p == symbol then
49
return true
50
fi
51
od
52
fi
53
54
return false
55
si
56
57
// The type of the delegate this literal constructs, and so of the
58
// value its load yields.
59
// The target a context pushed may name a placeholder whose origin
60
// has since settled; the delegate is constructed at what it
61
// settled to.
62
constructed_delegate_type: Types.Type =>
63
if let target = delegate_target_type then
64
if target.contains_inferred then
65
SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(target)
66
else
67
target
68
fi
69
else
70
type!
71
fi
72
73
could_be_delegate: bool => false
74
75
frame: FRAME?
76
77
short_description: string => description
78
79
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
80
PARTS.type_ref(type!)
81
82
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "closure"
83
84
symbol_kind: SymbolKind => SymbolKind.FUNCTION
85
completion_kind: CompletionKind => CompletionKind.FUNCTION
86
87
qualified_name: string => "[closure]{name}" // FIXME
88
89
il_name: string => name // FIXME
90
owner_il_name: string => "[closure]" // FIXME
91
92
is_closure: bool => true
93
94
// The local a nested named function statement declared for this
95
// literal. The local is not assigned until that statement
96
// completes, so a reference to the name from inside the body -
97
// the function calling itself - is served from the recurse field
98
// rather than from the local, which is still empty there.
99
self_reference_variable: Variable? public
100
is_recursive: bool
101
102
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
103
super.init(location, location, owner, name, enclosing_scope)
104
105
self.is_recursive = is_recursive
106
si
107
108
is_stateless_delegate: bool
109
110
// Latched when the load site emits this delegate as a static
111
// method with a null target instead of an instance method bound
112
// to self. The load-site IL is frozen at load time while the
113
// method definition is emitted later by generate-il, so the
114
// decision is recorded here rather than recomputed - both must
115
// agree on the calling convention.
116
is_static_delegate: bool
117
118
// Set when the delegate's body loads self (its `ldarg.0` target).
119
// Such a delegate, e.g. `() => self`, returns a self-dependent
120
// result, so it must not be shared across receivers even though it
121
// captures no values.
122
_delegate_body_loads_self: bool
123
124
delegate_body_loads_self: bool => _delegate_body_loads_self
125
126
note_delegate_body_loads_self() is
127
_delegate_body_loads_self = true
128
si
129
130
// True only while this closure's own literal body is being walked,
131
// so a self-load can be attributed to the body rather than to a
132
// separate scope constructing the delegate (binding its target).
133
_walking_literal_body: bool
134
135
enter_literal_body() is
136
_walking_literal_body = true
137
138
// Each walk of the body answers for itself: a self-load an
139
// earlier walk saw - a nested delegate bound to the receiver
140
// before it was found capture-free, say - is not one this
141
// walk makes.
142
_delegate_body_loads_self = false
143
si
144
145
leave_literal_body() is
146
_walking_literal_body = false
147
si
148
149
// A receiver load (`ldarg.0`) is about to be emitted; mark the
150
// closure whose body it lands in. That is the innermost closure
151
// currently walking its literal body - not necessarily the closure
152
// that answered the load: a nested literal's construction site sits
153
// in its enclosing literal's body, so binding the nested delegate's
154
// target loads the receiver in the enclosing body.
155
note_enclosing_body_loads_self() is
156
let stack = IoC.CONTAINER.instance.symbol_table.stack
157
let index mut = stack.count - 1
158
159
while index >= 0 do
160
if let closure: Closure = stack[index] then
161
if closure._walking_literal_body then
162
closure.note_delegate_body_loads_self()
163
return
164
fi
165
fi
166
167
index = index - 1
168
od
169
si
170
171
convert_to_delegate() is
172
assert could_be_delegate
173
174
is_stateless_delegate = !captured_values?
175
176
is_self_captured = false
177
captured_values = null
178
frame = null
179
is_delegate = true
180
181
_drop_carried_type_arguments()
182
si
183
184
// A frame is a class of its own, so it declares every type
185
// parameter the closure's signature names. A delegate's method
186
// is emitted on the enclosing class instead and reaches that
187
// class's parameters through the receiver, so it must not
188
// declare them again - only an enclosing generic method's are
189
// left for it to carry.
190
_drop_carried_type_arguments() is
191
if !captured_type_arguments? then
192
return
193
fi
194
195
let kept =
196
get_sorted_captured_type_argument_symbols()! |>
197
filter(p => !is_type_argument_carried_by_emitting_class(p)) |>
198
collect()
199
200
// The slots that remain number from zero again: the position
201
// each installs is its index in the emitted method's own
202
// parameter list. Renumbered in place rather than recorded
203
// again, because recording goes back through the guard that
204
// treats a frozen copy of a parameter as that parameter, and
205
// a survivor that is a frozen copy of another survivor would
206
// be dropped there instead of renumbered.
207
let renumbered = Collections.MAP[Symbol, CAPTURED_TYPE_ARGUMENT]()
208
209
for i in 0..kept.count do
210
renumbered.add(kept[i], CAPTURED_TYPE_ARGUMENT(i))
211
od
212
213
next_type_argument_index = renumbered.count
214
215
if renumbered.count > 0 then
216
captured_type_arguments = renumbered
217
else
218
captured_type_arguments = null
219
fi
220
si
221
222
// True when this closure is emitted into the class that declares
223
// `parameter`, which then reaches the closure's body through the
224
// receiver rather than through a parameter of its own. A trait's
225
// parameter never is: the closure is not emitted on the trait.
226
is_type_argument_carried_by_emitting_class(parameter: Symbol) -> bool is
227
let symbol: Symbol? mut = self
228
229
while symbol? do
230
if let classy: Classy = symbol then
231
return !classy.is_trait /\ parameter.owner == classy
232
fi
233
234
let enclosing = cast Symbol?(symbol.owner)
235
236
// A namespace is its own owner, so a chain that reaches
237
// one without passing a class ends there.
238
if enclosing == symbol then
239
return false
240
fi
241
242
symbol = enclosing
243
od
244
245
return false
246
si
247
248
// A stateless, ground function literal whose delegate carries no
249
// per-evaluation state, so it can be built once and cached in a
250
// static field instead of reallocated on every use. Two shapes
251
// qualify, both with no captured type arguments and a fully-settled,
252
// ground function type:
253
//
254
// - a static or global anonymous function (`ldnull` target, no
255
// captures), cached on the enclosing namespace's `$globals`;
256
// - an instance-context delegate that captures no values and never
257
// loads self in its body: it is emitted as a static method with
258
// a null target and cached on its enclosing non-generic class,
259
// so the cache retains no receiver instance.
260
//
261
// The settled/ground-type gate excludes literals whose inference
262
// never resolved or whose type mentions a type variable (either
263
// would produce an invalid cache field). The self-load gate
264
// excludes a delegate like `() => self` whose result depends on the
265
// receiver even though it captures nothing.
266
is_memoizable_delegate: bool is
267
if captured_type_arguments? then
268
return false
269
fi
270
271
let function_type = type
272
273
if !function_type? \/ !function_type.is_settled \/ !_is_ground_type(function_type) then
274
return false
275
fi
276
277
if is_anon_func then
278
return _enclosing_namespace()?
279
fi
280
281
if is_delegate /\ is_stateless_delegate /\ !_delegate_body_loads_self then
282
// Async literals lower through the state-machine path;
283
// their delegate creation is not worth caching (the state
284
// machine allocation dominates) and is left untouched.
285
if async_state_machine_for(self)? then
286
return false
287
fi
288
289
if let owner_class: CLASS = get_il_owner() then
290
return !owner_class.is_generic
291
fi
292
293
return false
294
fi
295
296
return false
297
si
298
299
_is_ground_type(candidate: Semantic.Types.Type) -> bool is
300
let mentions_type_variable mut = false
301
302
candidate.walk((element: Semantic.Types.Type) is
303
if element.is_type_variable then
304
mentions_type_variable = true
305
fi
306
307
// A plain named reference to a generic type's own symbol -
308
// a union variant among these - rather than an
309
// instantiation of it names the open generic. It carries no
310
// arguments for a type-argument walk to see, and an open
311
// generic in a static field signature fails to load. A
312
// pack wrapper whose generic callee has not yet had its
313
// type arguments bound reaches this shape.
314
if
315
isa Semantic.Types.NAMED(element) /\
316
!isa Semantic.Types.GENERIC(element) /\
317
isa Semantic.Symbols.Classy(element.symbol) /\
318
cast Semantic.Symbols.Classy(element.symbol).is_generic
319
then
320
mentions_type_variable = true
321
fi
322
si)
323
324
return !mentions_type_variable
325
si
326
327
// Static field caching this literal's delegate. A static/global
328
// anonymous function caches on the enclosing namespace's `$globals`
329
// class; a stateless instance-context delegate caches on its
330
// enclosing class. Built lazily and shared between the load-site
331
// reference and the definition emitted by generate-il.
332
_delegate_cache_field: Field?
333
334
// True once a load site has referenced the cache field; the field
335
// definition is emitted iff this holds, so the definition tracks
336
// what the frozen load-site IL actually references.
337
has_delegate_cache_field: bool => _delegate_cache_field?
338
339
delegate_cache_field: Field is
340
if !_delegate_cache_field? then
341
let `field: Field =
342
if is_anon_func then
343
Symbols.GLOBAL_VARIABLE(LOCATION.internal, _enclosing_namespace()!, "{name}$cache")
344
else
345
Symbols.STATIC_FIELD(LOCATION.internal, get_il_owner(), "{name}$cache")
346
fi
347
`field.set_type(constructed_delegate_type)
348
_delegate_cache_field = `field
349
fi
350
351
// Retyped on every access: the closure's own type is
352
// rebuilt whenever its return is settled again, and the
353
// field created by an earlier walk has to hold what the
354
// final walk constructs.
355
let `field = _delegate_cache_field!
356
357
`field.set_type(constructed_delegate_type)
358
359
return `field
360
si
361
362
// Wrap a delegate-creation value so it is built once and reused.
363
// A no-op for literals that aren't memoizable.
364
memoize_delegate(value: Value) -> Value is
365
if !is_memoizable_delegate then
366
return value
367
fi
368
369
return IR.Values.MEMOIZED_DELEGATE(
370
value, delegate_cache_field, constructed_delegate_type)
371
si
372
373
_enclosing_namespace() -> NAMESPACE? is
374
let scope: Scope? mut = owner
375
376
let guard mut = 0
377
while scope? /\ guard < 64 do
378
if let ns: NAMESPACE = scope then
379
return ns
380
fi
381
382
if let ns_scope: Semantic.NAMESPACE_SCOPE = scope then
383
return ns_scope.containing_namespace
384
fi
385
386
if let symbol: Symbol = scope then
387
scope = symbol.owner
388
else
389
return null
390
fi
391
392
guard = guard + 1
393
od
394
395
return null
396
si
397
398
// The frame and its captured-value set materialise together on
399
// first capture.
400
_ensure_frame() -> FRAME is
401
if !frame? then
402
captured_values = Collections.SET[Symbol?]()
403
404
frame = Semantic.Symbols.FRAME(owner!, self)
405
fi
406
407
return frame!
408
si
409
410
// A captured value's type is what makes the frame generic, and
411
// the frame is a class of its own, so every parameter that type
412
// names has to be one of its own. Recording it where the
413
// capture is made rather than where the frame is built is what
414
// lets anything compiled against the frame agree with it: the
415
// set is otherwise empty until emission.
416
_capture_type_arguments_of(type: Types.Type?) is
417
if !type? then
418
return
419
fi
420
421
type.walk(
422
t is
423
if t.is_type_variable then
424
add_type_argument_reference(t.symbol)
425
fi
426
si)
427
si
428
429
find_or_add_capture(variable: Symbol?) -> Field is
430
let frame = _ensure_frame()
431
let captured_values = self.captured_values!
432
433
if variable? then
434
if !captured_values.contains(variable) then
435
captured_values.add(variable)
436
437
// `storage_type` is the slot/field type at IL
438
// level — `Ghul.BOX[T]` when the captured
439
// local is boxed, plain `T` otherwise. For
440
// boxed locals the frame holds a reference to
441
// the shared box, so closure and enclosing
442
// scope agree on the cell.
443
let `field_type =
444
if isa Variable(variable) then
445
variable.storage_type
446
else
447
cast Types.Typed?(variable)!.type
448
fi
449
450
let captured = frame.declare_captured(variable.name, `field_type, IoC.CONTAINER.instance.symbol_definition_locations)
451
452
captured.captured_symbol = variable
453
454
_capture_type_arguments_of(`field_type)
455
456
return captured
457
else
458
let existing = frame.get_captured(variable.name)
459
460
if should_refresh_capture(existing, variable) then
461
// Mirror the boxed/unboxed distinction
462
// from declaration time — see comment in
463
// the declare-new branch above.
464
let refreshed_type =
465
if isa Variable(variable) then
466
variable.storage_type
467
else
468
cast Types.Typed?(variable)!.type
469
fi
470
471
existing!.set_type(refreshed_type!)
472
473
_capture_type_arguments_of(refreshed_type)
474
fi
475
476
return existing!
477
fi
478
else
479
if !is_self_captured then
480
is_self_captured = true
481
482
captured_values.add(null)
483
484
return frame.declare_captured("$self", _self_capture_type(), IoC.CONTAINER.instance.symbol_definition_locations)
485
else
486
// is_self_captured guarantees a prior declare_captured
487
// populated the "$self" slot.
488
return frame.get_captured("$self")!
489
fi
490
fi
491
si
492
493
// Type of the frame's captured `$self` field. The enclosing
494
// instance type comes from the scope stack rather than from the
495
// closure's owner, which for a body written in a `partial` or
496
// `impl` block is the block's own scope and carries no type.
497
_self_capture_type() -> Types.Type? is
498
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context ?? cast Classy?(owner)
499
500
if !context? then
501
return owner!.type
502
fi
503
504
let resolved = Collections.LIST[Symbol]()
505
506
let result = SELF_CAPTURE_TYPE((classy, name) => _own_type_argument(classy, name, resolved)).of(context)
507
508
// Recording is what gives the frame class the parameters the
509
// instantiated reference resolves against, so it applies only
510
// once every parameter resolved and that reference was built.
511
if isa Types.GENERIC(result) then
512
for argument in resolved do
513
add_type_argument_reference(argument)
514
od
515
fi
516
517
return result
518
si
519
520
_own_type_argument(context: Classy, name: string, resolved: Collections.LIST[Symbol]) -> Types.Type? is
521
let argument = context.find_direct(name)
522
523
if !argument? \/ !argument.type? then
524
return null
525
fi
526
527
resolved.add(argument)
528
529
return argument.type
530
si
531
532
// True iff the captured field's recorded type should be replaced
533
// on a re-capture pass. The previous iteration may have captured
534
// `source` before its type was IL-emit-stable, leaving the field
535
// pinned to an `INFERRED_VARIABLE_TYPE` sentinel or a null slot.
536
// Refresh when the source has a usable shape AND the existing
537
// field's recorded type is itself still un-settled. Two source-
538
// side gates:
539
//
540
// - `!is_sentinel` rather than `is_settled` so a composite like
541
// `Function[T,U]` with inner placeholders still promotes the
542
// field from `INFERRED_VARIABLE_TYPE` to `Function[T,U]`,
543
// letting call sites on the captured value see a NAMED
544
// function rather than an opaque sentinel. Inner T/U slots
545
// refine via the next body-retry iteration.
546
//
547
// - `is_defined` (for LOCAL_VARIABLE sources) guards against a
548
// `let f = ... f ...` forward self-reference silently
549
// "fixing" itself before the user-visible "cannot emit IL for
550
// function with unresolved return type" diagnostic fires.
551
// Destructured locals set `is_defined` ahead of the inner
552
// closure reference, so they still refresh correctly.
553
should_refresh_capture(existing: Field?, source: Symbol?) -> bool static is
554
if !existing? \/ !source? then
555
return false
556
fi
557
558
let source_type = source.type
559
560
if !source_type? \/ source_type.is_sentinel then
561
return false
562
fi
563
564
let local = cast LOCAL_VARIABLE?(source)
565
if local? /\ !local.is_defined then
566
return false
567
fi
568
569
let existing_type = existing.type
570
571
return !existing_type? \/ !existing_type.is_settled
572
si
573
574
find_or_add_recurse() -> Field is
575
let frame = _ensure_frame()
576
577
let recurse = frame.find_member("$recurse")
578
579
if recurse? then
580
return cast Field?(recurse)!
581
else
582
return frame.declare_recurse(IoC.CONTAINER.instance.symbol_definition_locations)
583
fi
584
si
585
586
find_or_add_capture_self() -> Field => find_or_add_capture(null)
587
588
outer_recurse_field_name(outer: Closure) -> string =>
589
"$outer_recurse_{outer.name}"
590
591
// Captures the recurse-target of an outer recursive ancestor
592
// so `rec` can be used from a nested non-recursive lambda.
593
// The captured Symbol stored in `captured_values` is the
594
// outer Closure itself, used as a sentinel by
595
// `_get_actual_arguments` to dispatch to the outer-recurse
596
// codepath. Declaration order in `captured_values` mirrors
597
// declaration order in the frame, so the constructor-arg
598
// ordering stays in step with the frame-field ordering.
599
find_or_add_captured_outer_recurse(outer: Closure) -> Field is
600
assert outer != self else "cannot capture self as outer recurse"
601
602
let frame = _ensure_frame()
603
let captured_values = self.captured_values!
604
605
let field_name = outer_recurse_field_name(outer)
606
let existing = frame.find_member(field_name)
607
608
if existing? then
609
let `field = cast Field?(existing)!
610
611
// Always refresh from outer.type when non-error.
612
// outer.type is rebuilt by Function.set_return_type
613
// each iteration so a stale field would miss any
614
// widening (arg slot CONS[int] → List[int] from rec
615
// match propagation) or any return-slot resolution (INFERRED
616
// → string). Type-level `matches` returns true whenever
617
// one side is INFERRED, so it can't distinguish
618
// "still placeholder" from "now concrete".
619
// Refreshing unconditionally is safe: gen_type on
620
// placeholders emits a comment (per
621
// INFERRED_VARIABLE_TYPE / INFERRED_RETURN_TYPE
622
// overrides) and the next iter's freeze produces a
623
// cleaner RAW.
624
let outer_type = outer.type
625
626
if outer_type? /\ !outer_type.is_error then
627
`field.set_type(outer_type)
628
fi
629
630
return `field
631
fi
632
633
captured_values.add(outer)
634
635
return frame.declare_captured(field_name, outer.type, IoC.CONTAINER.instance.symbol_definition_locations)
636
si
637
638
load_captured_outer_recurse(location: LOCATION, outer: Closure, loader: SYMBOL_LOADER) -> Value is
639
let `field = find_or_add_captured_outer_recurse(outer)
640
let frame = self.frame!
641
642
return loader.load_instance_variable(LOCATION.internal, IR.Values.Load.REFERENCE_SELF(frame, frame.type), `field)
643
si
644
645
// Called from the enclosing closure's _get_actual_arguments to
646
// produce the value to pass for an inner frame's
647
// $outer_recurse_<outer> capture. If we are outer directly,
648
// load $recurse from our own frame; otherwise we must have
649
// captured outer's recurse ourselves (the chain-establish loop
650
// in visit(RECURSE) guarantees it).
651
load_outer_recurse_value(outer: Closure, location: LOCATION, loader: SYMBOL_LOADER) -> Value is
652
if self == outer then
653
return load_recurse(location, loader)
654
fi
655
656
// the chain-establish loop captured outer's recurse here,
657
// so the frame exists
658
let frame = self.frame!
659
660
let field_name = outer_recurse_field_name(outer)
661
let `field = cast Field?(frame.find_member(field_name))
662
663
assert `field? else "outer recurse field {field_name} not found on intermediate closure {name}"
664
665
return loader.load_instance_variable(LOCATION.internal, IR.Values.Load.REFERENCE_SELF(frame, frame.type), `field)
666
si
667
668
// Walks the live scope stack to find the closure that
669
// lexically encloses self — i.e. the closure into whose body
670
// the IR currently being built will be inserted. `current_function`
671
// is no good here: when this runs, self is still on the stack so
672
// `current_function` returns self.
673
_find_enclosing_closure() -> Closure? is
674
let stack = IoC.CONTAINER.instance.symbol_table.stack
675
let i mut = stack.count - 1
676
let seen_self mut = false
677
678
while i >= 0 do
679
let scope = stack[i]
680
681
if seen_self /\ isa Closure(scope) then
682
return scope
683
fi
684
685
if scope == self then
686
seen_self = true
687
fi
688
689
i = i - 1
690
od
691
return null
692
si
693
694
type_updated(type: Semantic.Types.Type) is
695
if let self.frame? /\ is_recursive then
696
frame.try_update_recurse_type(type)
697
fi
698
si
699
700
add_type_argument_reference(type: Symbol) is
701
if !captured_type_arguments? then
702
captured_type_arguments = Collections.MAP[Symbol,CAPTURED_TYPE_ARGUMENT]()
703
fi
704
705
let captures = captured_type_arguments!
706
707
if captures.contains_key(type) then
708
return
709
fi
710
711
// One frame slot per parameter, not per symbol standing for
712
// one. A captured type can arrive as the parameter's own
713
// symbol or as a frozen copy of it, and those are different
714
// objects; giving each a slot would number one parameter
715
// twice, and the position each slot installs is held against
716
// the parameter, so the second would overwrite the first and
717
// leave one of the two frame indices unreachable.
718
if _is_captured_parameter(captures, type) then
719
return
720
fi
721
722
captures.add(type, CAPTURED_TYPE_ARGUMENT(next_type_argument_index))
723
next_type_argument_index = next_type_argument_index + 1
724
si
725
726
_is_captured_parameter(
727
captures: Collections.MAP[Symbol,CAPTURED_TYPE_ARGUMENT],
728
type: Symbol
729
) -> bool is
730
let argument = cast GenericArgument?(type)
731
732
if !argument? then
733
return false
734
fi
735
736
let key = TYPE_PARAMETER_KEY.of(argument)
737
738
for captured in captures.keys do
739
if let captured_argument: GenericArgument = captured then
740
if TYPE_PARAMETER_KEY.of(captured_argument) =~ key then
741
return true
742
fi
743
fi
744
od
745
746
return false
747
si
748
749
set_type_arguments() is
750
if !captured_type_arguments? then
751
// not needed
752
return
753
fi
754
755
if let self.frame? then
756
frame.set_type_arguments(get_sorted_captured_type_argument_symbols()!)
757
return
758
fi
759
760
if generic_arguments.count > 0 then
761
// already done
762
return
763
fi
764
765
let args = get_sorted_captured_type_argument_symbols()!
766
767
generic_arguments = args |> map(a => a.type!) |> collect()
768
generic_argument_names = args |> map(a => a.name) |> collect()
769
si
770
771
get_sorted_captured_type_argument_symbols() -> Collections.List[Symbol]? =>
772
if captured_type_arguments? then
773
captured_type_arguments |> sort((a, b) => a.value.index - b.value.index) |> map(p => p.key) |> collect()
774
else
775
null
776
fi
777
778
get_sorted_captured_type_argument_types() -> Collections.List[Types.Type]? =>
779
if captured_type_arguments? then
780
captured_type_arguments |> sort((a, b) => a.value.index - b.value.index) |> map(p => p.key.type!) |> collect()
781
else
782
null
783
fi
784
785
get_type_arguments_as_specialize_map() -> Collections.Map[Symbol,Types.Type]? =>
786
if captured_type_arguments? then
787
Collections.MAP[Symbol,Types.Type](
788
// filter ensures key.type and key.type.freeze() are non-null
789
captured_type_arguments.keys
790
|> filter(key => let kt = key.type in kt? /\ kt.freeze()?) |>
791
map(key => Collections.KeyValuePair[Symbol,Types.Type](key, key.type!.freeze()!))
792
)
793
else
794
null
795
fi
796
797
// Saved emitted positions per captured type-arg symbol, set on
798
// entry to map_type_arguments and reinstated by
799
// unmap_type_arguments. Without this save and restore, an outer
800
// override (e.g. the enclosing generator's
801
// install_body_emission_overrides) would be clobbered by the
802
// closure's nullout in unmap and any later freeze of the
803
// outer's IR would emit the wrong type-arg index.
804
//
805
// It is a stack because the same closure's bracket nests inside
806
// itself: emitting a signature within a body already bracketed
807
// for that closure enters it a second time. With one slot per
808
// symbol the inner entry saves the closure's own position over
809
// the outer entry's saved value, and the matching pair of unmaps
810
// then clears the position instead of reinstating what was
811
// installed before the outermost entry.
812
_saved_emitted_positions: Collections.MAP[Symbol, Collections.LIST[(() -> TYPE_PARAMETER_POSITION)?]]?
813
814
map_type_arguments() is
815
if !captured_type_arguments? then
816
return
817
fi
818
819
let saved =
820
_saved_emitted_positions ??
821
Collections.MAP[Symbol, Collections.LIST[(() -> TYPE_PARAMETER_POSITION)?]]()
822
823
_saved_emitted_positions = saved
824
825
for symbol_cta in captured_type_arguments! do
826
let symbol = symbol_cta.key
827
let cta = symbol_cta.value
828
829
let prior_positions: Collections.LIST[(() -> TYPE_PARAMETER_POSITION)?] mut
830
831
if !saved.try_get_value(symbol, prior_positions ref) then
832
prior_positions = Collections.LIST[(() -> TYPE_PARAMETER_POSITION)?]()
833
saved[symbol] = prior_positions
834
fi
835
836
prior_positions.add(symbol.current_emitted_position)
837
838
// A closure with a frame reaches the argument as one of
839
// the frame class's own; without one it stays on the
840
// method. Which it is can still change after this point,
841
// so the answer is deferred rather than decided here.
842
symbol.set_emitted_position(
843
() => TYPE_PARAMETER_POSITION(frame?, cta.index))
844
od
845
si
846
847
unmap_type_arguments() is
848
if !captured_type_arguments? then
849
return
850
fi
851
852
let saved = _saved_emitted_positions
853
854
for symbol_cta in captured_type_arguments do
855
let symbol = symbol_cta.key
856
857
let prior_positions: Collections.LIST[(() -> TYPE_PARAMETER_POSITION)?] mut
858
859
if
860
saved? /\
861
saved.try_get_value(symbol, prior_positions ref) /\
862
prior_positions.count > 0
863
then
864
let last = prior_positions.count - 1
865
866
symbol.set_emitted_position(prior_positions[last])
867
prior_positions.remove_at(last)
868
else
869
symbol.set_emitted_position(null)
870
fi
871
od
872
si
873
874
load(location: LOCATION, loader: SYMBOL_LOADER) -> Value => throw System.NotImplementedException("abstract")
875
load_closure(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
876
try
877
return _load_closure(location, loader)
878
catch e: System.Exception
879
IoC.CONTAINER.instance.logger.exception(location, e, "exception loading closure")
880
881
return IR.Values.DUMMY(Types.ERROR(), location)
882
yrt
883
si
884
885
_get_actual_arguments(loader: SYMBOL_LOADER) -> Collections.LIST[Value] is
886
let actual_arguments = Collections.LIST[Value]()
887
888
let capture_type_argument = (t: Types.Type) is
889
if t.is_type_variable then
890
add_type_argument_reference(t.symbol)
891
fi
892
si
893
894
let enclosing: Closure? mut = null
895
896
for c in captured_values! do
897
if !c? then
898
// The "captured self" passed to the closure's
899
// frame constructor. Same construction-site
900
// distinction as `_load_delegate`: inside a
901
// generator's MoveNext, route through the state-
902
// machine frame's `_outer_self` so the frame ctor
903
// receives the user's instance and not the state
904
// machine.
905
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context
906
let outer_self: Value? mut = null
907
if context? then
908
outer_self = _try_load_self_for_construction_via_outer_self(context)
909
fi
910
if !outer_self? then
911
outer_self = loader.load_outer_self(location)
912
fi
913
914
actual_arguments.add(outer_self)
915
916
// Guard: a forward-reference capture (e.g.
917
// mutual recursion between two let-bound
918
// lambdas) leaves the capture's type null
919
// during the first walk. Skipping the type-var
920
// discovery here is correct — the outer
921
// function's body retry will re-walk the
922
// closure with the resolved type. NRE-ing here
923
// would mask the underlying "variable is not
924
// defined here" diagnostic.
925
if outer_self.type? then
926
outer_self.type.walk(capture_type_argument)
927
fi
928
elif isa Closure(c) then
929
let outer = c
930
931
if !enclosing? then
932
enclosing = _find_enclosing_closure()
933
fi
934
935
let value = enclosing!.load_outer_recurse_value(outer, location, loader)
936
937
actual_arguments.add(value)
938
939
if value.type? then
940
value.type.walk(capture_type_argument)
941
fi
942
else
943
let outer_captured_value =
944
c.load_outer(
945
location,
946
null,
947
loader
948
)
949
950
actual_arguments.add(outer_captured_value)
951
952
if outer_captured_value.type? then
953
outer_captured_value.type.walk(capture_type_argument)
954
fi
955
fi
956
od
957
958
return actual_arguments
959
si
960
961
// A closure emitted on the enclosing class reaches that class's
962
// type parameters as `!N` through the instance, which is why
963
// RECORD_TYPE_ARGUMENT_USES leaves them uncaptured. A closure
964
// with captured values is emitted on a frame instead, and the
965
// frame is a class of its own with no parameters of the
966
// enclosing one, so whatever its signature names it must carry.
967
// Whether there is a frame is not known when that pass runs.
968
capture_signature_type_arguments() is
969
let capture = (t: Types.Type) is
970
if t.is_type_variable then
971
add_type_argument_reference(t.symbol)
972
fi
973
si
974
975
for argument in arguments do
976
argument.walk(capture)
977
od
978
979
if let self.return_type? then
980
return_type.walk(capture)
981
fi
982
si
983
984
_load_closure(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
985
let closure_type = type
986
987
if !closure_type? then
988
return IR.Values.DUMMY(Types.ERROR(), LOCATION.internal)
989
fi
990
991
if closure_type.is_error then
992
return IR.Values.DUMMY(closure_type, LOCATION.internal)
993
fi
994
995
if is_delegate then
996
return loader.load_instance_anonymous_function(self, constructed_delegate_type)
997
fi
998
999
let actual_arguments = _get_actual_arguments(loader)
1000
1001
capture_signature_type_arguments()
1002
1003
if captured_type_arguments? then
1004
return _load_closure_generic(actual_arguments, loader)
1005
else
1006
return _load_closure_non_generic(actual_arguments, loader)
1007
fi
1008
si
1009
1010
_load_closure_generic(actual_arguments: Collections.LIST[Value], loader: SYMBOL_LOADER) -> Value is
1011
// See _load_closure_non_generic for the failed-speculation
1012
// try-catch rationale.
1013
try
1014
return _load_closure_generic_body(actual_arguments, loader)
1015
catch ex: System.Exception
1016
IoC.CONTAINER.instance.logger.mark_consumed_any()
1017
return IR.Values.DUMMY(self.type!, location)
1018
yrt
1019
si
1020
1021
_load_closure_generic_body(actual_arguments: Collections.LIST[Value], loader: SYMBOL_LOADER) -> Value is
1022
let frame = self.frame!
1023
1024
let block = IR.Values.BLOCK(constructed_delegate_type)
1025
1026
owner = frame
1027
1028
set_type_arguments()
1029
1030
// we want these type arguments to remain in the current context.
1031
// Frozen forms are filtered for non-null so we hand a real
1032
// List[Type] to get_create_instance — a captured generic with
1033
// no resolved freeze would point at an unresolved slot.
1034
let frozen_argument_types = get_sorted_captured_type_argument_types()!
1035
|> map(t => t.freeze()) |>
1036
filter(f => f?) |>
1037
map(f => f!) |>
1038
collect()
1039
1040
// but the formal arguments of the frame class constructor need to be in the frame's context
1041
map_type_arguments()
1042
let frame_instance = frame.get_create_instance(actual_arguments, frozen_argument_types)
1043
unmap_type_arguments()
1044
1045
let frame_instance_copier = frame_instance!.get_temp_copier(block, "frame-instance")
1046
1047
owner = frame_instance.type!.symbol
1048
1049
map_type_arguments()
1050
let function = IR.Values.Load.FUNCTION_POINTER(self)
1051
unmap_type_arguments()
1052
1053
let result = IR.Values.Load.DELEGATE(constructed_delegate_type, function, frame_instance_copier())
1054
1055
let result_copier = result.get_temp_copier(block, "closure")
1056
1057
let rec_member_symbol = owner!.find_member("$recurse")
1058
1059
if rec_member_symbol? /\ !frame_instance.type!.is_error then
1060
let ff = frame_instance_copier()
1061
let rr = result_copier()
1062
map_type_arguments()
1063
block.add(rec_member_symbol.store(location, ff, rr, loader, true))
1064
unmap_type_arguments()
1065
fi
1066
1067
block.add(result_copier())
1068
block.close()
1069
return block
1070
si
1071
1072
_load_closure_non_generic(actual_arguments: Collections.LIST[Value], loader: SYMBOL_LOADER) -> Value is
1073
// Bail early when any captured value has an error or
1074
// unresolved type. Trying to construct the frame
1075
// instance with ERROR-typed captures cascades into IL
1076
// gen_type throws further down the path.
1077
for a in actual_arguments do
1078
if !a.type? \/ a.type.is_error then
1079
return IR.Values.DUMMY(Types.ERROR(), location)
1080
fi
1081
od
1082
1083
// The iterative-inference body-retry loop may reach
1084
// closure loading during an early iter where the FRAME's
1085
// declare_constructor defensive-defaulted a not-yet-typed
1086
// field to Types.ERROR — gen_type throws on that.
1087
// Treat the exception as failed speculation: return a
1088
// DUMMY of the closure's own type so downstream paths
1089
// see a placeholder closure rather than a poison value,
1090
// and mark_consumed_any so the body-retry loop iterates
1091
// again from improved types.
1092
try
1093
return _load_closure_non_generic_body(actual_arguments, loader)
1094
catch ex: System.Exception
1095
IoC.CONTAINER.instance.logger.mark_consumed_any()
1096
return IR.Values.DUMMY(self.type!, location)
1097
yrt
1098
si
1099
1100
_load_closure_non_generic_body(actual_arguments: Collections.LIST[Value], loader: SYMBOL_LOADER) -> Value is
1101
let frame = self.frame!
1102
1103
let block = IR.Values.BLOCK(constructed_delegate_type)
1104
1105
owner = frame
1106
1107
let frame_instance = frame.get_create_instance(actual_arguments, null)
1108
let frame_instance_copier = frame_instance!.get_temp_copier(block, "frame-instance")
1109
1110
owner = frame_instance.type!.symbol
1111
1112
let function = IR.Values.Load.FUNCTION_POINTER(self)
1113
let result = IR.Values.Load.DELEGATE(constructed_delegate_type, function, frame_instance_copier())
1114
1115
let result_copier = result.get_temp_copier(block, "closure")
1116
1117
let rec_member_symbol = frame.find_member("$recurse")
1118
1119
if rec_member_symbol? /\ !frame_instance.type!.is_error then
1120
block.add(rec_member_symbol.store(location, frame_instance_copier(), result_copier(), loader, true))
1121
fi
1122
1123
block.add(result_copier())
1124
block.close()
1125
return block
1126
si
1127
1128
_load_lambda() -> Value is
1129
let closure_type = type
1130
1131
if !closure_type? then
1132
return IR.Values.DUMMY(Types.ERROR(), LOCATION.internal)
1133
fi
1134
1135
if closure_type.is_error then
1136
return IR.Values.DUMMY(closure_type, LOCATION.internal)
1137
fi
1138
1139
if captured_type_arguments? then
1140
return _load_lambda_generic()
1141
else
1142
return _load_lambda_non_generic()
1143
fi
1144
si
1145
1146
_load_lambda_generic() -> Value is
1147
set_type_arguments()
1148
1149
// Generic lambdas always capture type arguments, so the
1150
// specialize map is present on this path.
1151
let args_map = get_type_arguments_as_specialize_map()!
1152
1153
map_type_arguments()
1154
1155
let specialized_lambda = specialize_function(args_map, null)
1156
1157
let function_pointer = IR.Values.Load.FUNCTION_POINTER(specialized_lambda)
1158
let delegate = IR.Values.Load.DELEGATE(delegate_target_type ?? specialized_lambda.type!, function_pointer, IR.Values.NULL(Types.NULL()))
1159
1160
unmap_type_arguments()
1161
1162
return delegate
1163
si
1164
1165
_load_lambda_non_generic() -> Value is
1166
let function_pointer = IR.Values.Load.FUNCTION_POINTER(self)
1167
let delegate = IR.Values.Load.DELEGATE(constructed_delegate_type, function_pointer, IR.Values.NULL(Types.NULL()))
1168
1169
return memoize_delegate(delegate)
1170
si
1171
1172
_load_delegate(loader: SYMBOL_LOADER) -> Value is
1173
let closure_type = type
1174
1175
if !closure_type? then
1176
return IR.Values.DUMMY(Types.ERROR(), LOCATION.internal)
1177
fi
1178
1179
if closure_type.is_error then
1180
return IR.Values.DUMMY(closure_type, LOCATION.internal)
1181
fi
1182
1183
// A delegate is converted from a literal that captured no
1184
// type arguments, but the walk that follows the conversion
1185
// can still settle a parameter type naming one. Such a
1186
// delegate is emitted as a generic method and reached
1187
// through an instantiation, as a frameless literal is.
1188
if captured_type_arguments? then
1189
return _load_delegate_generic(loader)
1190
fi
1191
1192
let function_pointer = IR.Values.Load.FUNCTION_POINTER(self)
1193
1194
// A stateless delegate that never reads self doesn't need a
1195
// receiver at all: emit it as a static method with a null
1196
// target (the same shape as a static anonymous function) and
1197
// cache it, so no receiver instance is retained by the cache.
1198
is_static_delegate = is_memoizable_delegate
1199
1200
if is_static_delegate then
1201
let delegate = IR.Values.Load.DELEGATE(constructed_delegate_type, function_pointer, IR.Values.NULL(Types.NULL()))
1202
1203
return memoize_delegate(delegate)
1204
fi
1205
1206
return IR.Values.Load.DELEGATE(constructed_delegate_type, function_pointer, _load_delegate_target(loader))
1207
si
1208
1209
// Delegate target: the receiver the runtime binds to the
1210
// delegate. Inside a generator's MoveNext, `ldarg.0` is
1211
// the state machine, so a plain `loader.load_self` would
1212
// bind the delegate to it rather than the user's
1213
// instance. Route through the state-machine frame's
1214
// `_outer_self` field at construction-time when there's
1215
// an enclosing generator; otherwise fall back to the
1216
// ordinary load_self path.
1217
_load_delegate_target(loader: SYMBOL_LOADER) -> Value is
1218
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context
1219
let self_pointer: Value? mut = null
1220
if context? then
1221
self_pointer = _try_load_self_for_construction_via_outer_self(context)
1222
fi
1223
if !self_pointer? then
1224
self_pointer = loader.load_self(LOCATION.internal)
1225
fi
1226
1227
// Binding the target loads the receiver at the construction
1228
// site, which sits in the enclosing literal's body when this
1229
// delegate is nested inside another literal.
1230
note_enclosing_body_loads_self()
1231
1232
return self_pointer
1233
si
1234
1235
// The delegate's own method carries the type parameters its
1236
// signature names, and the construction site reaches it through
1237
// an instantiation of them. Mirrors the frameless literal path;
1238
// a generic delegate is never memoised, since the cache field
1239
// would have to be one per instantiation.
1240
_load_delegate_generic(loader: SYMBOL_LOADER) -> Value is
1241
let self_pointer = _load_delegate_target(loader)
1242
1243
set_type_arguments()
1244
1245
let args_map = get_type_arguments_as_specialize_map()!
1246
1247
map_type_arguments()
1248
1249
let specialized = specialize_function(args_map, null)
1250
1251
let function_pointer = IR.Values.Load.FUNCTION_POINTER(specialized)
1252
let delegate =
1253
IR.Values.Load.DELEGATE(
1254
delegate_target_type ?? specialized.type!,
1255
function_pointer,
1256
self_pointer
1257
)
1258
1259
unmap_type_arguments()
1260
1261
return delegate
1262
si
1263
1264
load_outer_self(location: Source.LOCATION, loader: SYMBOL_LOADER) -> Value? is
1265
let symbol_table = IoC.CONTAINER.instance.symbol_table
1266
let context = symbol_table.current_instance_context
1267
1268
if is_delegate then
1269
note_enclosing_body_loads_self()
1270
1271
return IR.Values.Load.REFERENCE_SELF(context!, context.type)
1272
fi
1273
1274
let is_captured = false
1275
1276
let stack = symbol_table.stack
1277
1278
let index mut = stack.count - 1
1279
let seen_self mut = false
1280
1281
while index >= 0 do
1282
let scope = stack[index]
1283
1284
if scope.is_namespace then
1285
break
1286
fi
1287
1288
if seen_self /\ scope.is_capture_context then
1289
if scope.is_closure then
1290
let c = cast Closure?(scope)!
1291
1292
// FIXME: may not be needed - load_self() should capture self:
1293
c.find_or_add_capture_self()
1294
1295
return c.load_self(location, loader)
1296
fi
1297
fi
1298
1299
if scope == self then
1300
seen_self = true
1301
fi
1302
1303
index = index - 1
1304
od
1305
1306
if !is_captured then
1307
// load self normally:
1308
return IR.Values.Load.REFERENCE_SELF(context!, context.type)
1309
fi
1310
return null
1311
si
1312
1313
load_self(location: Source.LOCATION, loader: SYMBOL_LOADER) -> Value is
1314
if is_delegate then
1315
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context!
1316
1317
note_enclosing_body_loads_self()
1318
1319
// A delegate is emitted as a method on the enclosing type,
1320
// so its receiver is normally `ldarg.0`. For an async
1321
// literal the body lives in the state machine's MoveNext,
1322
// where `ldarg.0` is the state machine — the receiver is
1323
// reached through its `$outer_self` field instead. Only the
1324
// body is inside MoveNext: the load that binds the
1325
// delegate's target sits in the enclosing method, where
1326
// `ldarg.0` is the receiver as usual.
1327
if _walking_literal_body then
1328
if let redirected = _state_machine_outer_self_load(context) then
1329
return redirected
1330
fi
1331
fi
1332
1333
return IR.Values.Load.REFERENCE_SELF(context, context.type)
1334
fi
1335
1336
let `field = find_or_add_capture_self()
1337
1338
return loader.load_instance_variable(location, _capture_frame_self_load(), `field)
1339
si
1340
1341
// At a closure-construction emission site that ends up inside
1342
// an enclosing function's MoveNext, `ldarg.0` refers to that
1343
// function's state machine — so the closure's delegate target
1344
// / captured-self value can't be loaded via REFERENCE_SELF
1345
// (which would emit `ldarg.0` of the state machine). Instead
1346
// load through the state-machine frame's `_outer_self` field,
1347
// which the outer method populates with the user's instance at
1348
// .ctor time.
1349
//
1350
// Walks outward from this closure to find the closest
1351
// enclosing Function. If that function compiles into a state
1352
// machine whose frame carries `_outer_self`, returns the
1353
// matching `OUTER_SELF` IR Value; otherwise returns null and
1354
// the caller falls back to a plain REFERENCE_SELF.
1355
//
1356
// Called only at construction sites (`_load_delegate`,
1357
// `_get_actual_arguments`), which is what makes the *enclosing*
1358
// function's state machine the right one to look for. A
1359
// self-load in the closure's own body wants its own state
1360
// machine instead, and `load_self` handles that separately off
1361
// `_walking_literal_body`.
1362
_try_load_self_for_construction_via_outer_self(context: Symbol) -> Value? is
1363
let stack = IoC.CONTAINER.instance.symbol_table.stack
1364
let index mut = stack.count - 1
1365
1366
while index >= 0 do
1367
let scope = stack[index]
1368
1369
if scope == self then
1370
index = index - 1
1371
continue
1372
fi
1373
1374
if let function: Function = scope then
1375
if let outer_self_field = _enclosing_state_machine_outer_self_field(function) then
1376
return IR.Values.Load.OUTER_SELF(context, context.type, outer_self_field)
1377
fi
1378
1379
return null
1380
fi
1381
1382
index = index - 1
1383
od
1384
1385
return null
1386
si
1387
1388
// The `$outer_self` field of whichever state machine `function`
1389
// compiles into — generator or async. Null when it compiles into
1390
// neither, or when the frame carries no outer self (statics and
1391
// globals have none). declare() materialises the field; it is lazy
1392
// and idempotent, and required because the frame property only
1393
// constructs the bare FRAME object.
1394
_enclosing_state_machine_outer_self_field(function: Function) -> Field? is
1395
if let sm = state_machine_for(function) then
1396
if let frame = sm.frame then
1397
frame.declare()
1398
1399
return frame.outer_self_field
1400
fi
1401
elif let async_sm = async_state_machine_for(function) then
1402
if let frame = async_sm.frame then
1403
frame.declare()
1404
1405
return frame.outer_self_field
1406
fi
1407
fi
1408
1409
return null
1410
si
1411
1412
load_outer_captured_value(location: LOCATION, symbol: Variable, loader: SYMBOL_LOADER) -> Value? is
1413
let is_captured = false
1414
1415
let stack = IoC.CONTAINER.instance.symbol_table.stack
1416
1417
let index mut = stack.count - 1
1418
let seen_self mut = false
1419
1420
while index >= 0 do
1421
let scope = stack[index]
1422
1423
if seen_self /\ scope.is_capture_context then
1424
if symbol.owner == scope then
1425
// FIXME: use the symbol loader
1426
if isa LOCAL_ARGUMENT(symbol) then
1427
return IR.Values.Load.LOCAL_ARGUMENT(symbol)
1428
else
1429
return IR.Values.Load.LOCAL_VARIABLE(symbol)
1430
fi
1431
elif scope.is_closure then
1432
let c = cast Closure?(scope)!
1433
1434
// FIXME: probably redundant
1435
c.find_or_add_capture(symbol)
1436
1437
c.load_captured_value(location, symbol, loader)
1438
1439
let result = c.load_captured_value(location, symbol, loader)
1440
1441
return result
1442
fi
1443
fi
1444
1445
if scope == self then
1446
seen_self = true
1447
fi
1448
1449
index = index - 1
1450
od
1451
return null
1452
si
1453
1454
load_recurse(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
1455
let recurse = find_or_add_recurse()
1456
let frame = self.frame!
1457
1458
return recurse.load(location, IR.Values.Load.REFERENCE_SELF(frame, frame.type), loader)
1459
si
1460
1461
// Closure-body store into an outer-scope captured local.
1462
// Only legal when the captured local is boxed — outer
1463
// scope and closure share a heap cell, so writing via
1464
// `.value` on the captured box updates the cell visible
1465
// to the outer scope. Unboxed captures are still
1466
// rejected upstream by the symbol-loader diagnostic.
1467
store_captured_value(location: LOCATION, symbol: Variable, value: Value, loader: SYMBOL_LOADER) -> Value is
1468
symbol.is_captured = true
1469
1470
let `field = find_or_add_capture(symbol)
1471
1472
let frame_field_load =
1473
loader.load_instance_variable(LOCATION.internal, _capture_frame_self_load(), `field)
1474
1475
if symbol.is_boxed then
1476
let value_member = loader.resolve_box_value_member(symbol)
1477
1478
if value_member? then
1479
return value_member.store(LOCATION.internal, frame_field_load, value, loader, false)
1480
fi
1481
fi
1482
1483
// Fall back to a frame-field store for the unboxed
1484
// case — should not be reached in practice because
1485
// the upstream diagnostic blocks unboxed captured
1486
// assignment, but the defensive path keeps us out of
1487
// NRE territory if it ever does.
1488
return loader.store_instance_variable(LOCATION.internal, _capture_frame_self_load(), `field, value)
1489
si
1490
1491
// A `T ref` parameter's slot lives on the enclosing method's
1492
// stack and dies when it returns, so a frame field cannot hold
1493
// it: the emitted ctor would store the byref through a raw
1494
// pointer whose pointee is gone by the time the closure runs.
1495
captures_reference(symbol: Variable) -> bool static is
1496
if let symbol_type = symbol.type then
1497
return isa Types.REFERENCE(symbol_type)
1498
fi
1499
1500
return false
1501
si
1502
1503
load_captured_value(location: LOCATION, symbol: Variable, loader: SYMBOL_LOADER) -> Value is
1504
if captures_reference(symbol) then
1505
IoC.CONTAINER.instance.logger.error(
1506
location,
1507
"a function literal cannot capture a ref parameter, copy the value into a local variable first")
1508
fi
1509
1510
// Capture-before-define is detected by LOCAL_VARIABLE.load
1511
// (`variable.ghul`) via its own check_is_defined call;
1512
// duplicating the check here fired the same diagnostic
1513
// twice for the captured-load path. The capture field is
1514
// built with whatever type the variable has at this
1515
// point (often null/ERROR), which without the upstream
1516
// check would crash Type.gen_type at IL emission — but
1517
// the upstream check is the only place we need.
1518
symbol.is_captured = true
1519
1520
let `field = find_or_add_capture(symbol)
1521
1522
// Returns the raw frame field — for boxed captures
1523
// this is the BOX[T] reference. The user-code-side
1524
// unwrap to `.value` happens in
1525
// `SYMBOL_LOADER.load_local_variable` after this
1526
// returns, so that inter-frame capture transfers
1527
// (via `load_outer_captured_value`) keep passing the
1528
// box reference between frames untouched.
1529
return loader.load_instance_variable(LOCATION.internal, _capture_frame_self_load(), `field)
1530
si
1531
1532
// Load the captures-frame `this` for capture access. Normally
1533
// REFERENCE_SELF (`ldarg.0` of the frame's type). For an
1534
// ASYNC_CLOSURE compiled down the state-machine path, the
1535
// body lives in the SM's MoveNext where `ldarg.0` is the SM
1536
// instance — the captures-frame is reached via the SM's
1537
// `_outer_self` field. Mirror of INSTANCE_ASYNC_METHOD's
1538
// load_self.
1539
_capture_frame_self_load() -> Value is
1540
let frame = self.frame!
1541
1542
return _state_machine_outer_self_load(frame) ?? IR.Values.Load.REFERENCE_SELF(frame, frame.type)
1543
si
1544
1545
// The `$outer_self` load that stands in for `ldarg.0` inside this
1546
// literal's own MoveNext, where `ldarg.0` is the state machine
1547
// rather than whatever the body means by self. `context` is the
1548
// symbol the loaded value is typed against — the captures frame
1549
// for a framed literal, the enclosing instance for a delegate.
1550
// Null when this literal does not compile into a state machine,
1551
// leaving the caller to emit an ordinary self reference.
1552
//
1553
// In practice that means an async literal: `yield` in a literal
1554
// is not supported, so there is no generator closure kind for
1555
// the shared lookup's generator half to match. Should one ever
1556
// be added its body would be inside its own MoveNext for the
1557
// same reason, and would want the same redirect.
1558
_state_machine_outer_self_load(context: Symbol) -> Value? is
1559
if let outer_self_field = _enclosing_state_machine_outer_self_field(self) then
1560
return IR.Values.Load.OUTER_SELF(context, context.type, outer_self_field)
1561
fi
1562
1563
return null
1564
si
1565
1566
get_il_owner() -> Scope is
1567
if frame? \/ is_delegate then
1568
return owner!
1569
fi
1570
1571
let temp_owner = owner
1572
1573
let classy_owner = cast Classy?(owner)
1574
1575
if classy_owner? /\ classy_owner.owner? then
1576
return classy_owner.owner
1577
fi
1578
1579
// owner may be incorrect
1580
debug_always("suspect closure owner: {owner}")
1581
1582
return owner!
1583
si
1584
1585
gen_frame(context: IR.CONTEXT, symbol_loader: SYMBOL_LOADER) is
1586
if let self.frame? then
1587
map_type_arguments()
1588
set_type_arguments()
1589
1590
frame.gen_all(context, symbol_loader)
1591
1592
unmap_type_arguments()
1593
fi
1594
si
1595
1596
// Whether the method emitted for this closure takes a receiver,
1597
// which is not the same question as `is_instance`: a closure is
1598
// not an instance member of anything the language can see, while
1599
// the method emitted for one whose captures live on a frame does
1600
// take that frame, and reaches the captures through it.
1601
//
1602
// Its flags and its calling convention both turn on this, and
1603
// they have to agree.
1604
is_emitted_with_receiver: bool => !is_anon_func /\ !is_static_delegate
1605
1606
to_string() -> string => "[closure {name}]"
1607
si
1608
1609
// FIXME: pull common code across these Closure subclasses up
1610
// into Closure
1611
class INSTANCE_CLOSURE: Closure is
1612
is_instance: bool => true
1613
1614
could_be_delegate: bool =>
1615
let captured_values = self.captured_values in
1616
!captured_values? \/ (is_self_captured /\ captured_values.count == 1) /\ !is_recursive /\ _can_carry_captured_type_arguments
1617
1618
// A delegate's method is emitted on the enclosing class, which
1619
// supplies that class's own type parameters and nothing else, so
1620
// the only ones the method can declare for itself are an
1621
// enclosing generic method's. A trait's parameter is neither -
1622
// the method is not emitted on the trait - so a literal that
1623
// captured one keeps its frame, which declares every parameter
1624
// its signature names.
1625
_can_carry_captured_type_arguments: bool is
1626
let captures = captured_type_arguments
1627
1628
if !captures? then
1629
return true
1630
fi
1631
1632
for parameter in captures.keys do
1633
if
1634
!isa FUNCTION_GENERIC_ARGUMENT(parameter) /\
1635
!is_type_argument_carried_by_emitting_class(parameter)
1636
then
1637
return false
1638
fi
1639
od
1640
1641
return true
1642
si
1643
1644
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
1645
super.init(location, owner, name, enclosing_scope, is_recursive)
1646
si
1647
1648
declare_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1649
declare_closure_symbol(location, Symbols.INSTANCE_CLOSURE(location, owner, name, enclosing, is_recursive))
1650
1651
declare_async_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1652
declare_closure_symbol(location, Symbols.INSTANCE_ASYNC_CLOSURE(location, owner, name, enclosing, is_recursive))
1653
1654
load(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
1655
if frame? then
1656
return super.load_closure(location, loader)
1657
fi
1658
1659
if is_delegate then
1660
return _load_delegate(loader)
1661
fi
1662
1663
return _load_lambda()
1664
si
1665
1666
to_string() -> string => "[instance closure {name}]"
1667
si
1668
1669
class STATIC_CLOSURE: Closure is
1670
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
1671
super.init(location, owner, name, enclosing_scope, is_recursive)
1672
si
1673
1674
declare_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1675
declare_closure_symbol(location, Symbols.STATIC_CLOSURE(location, owner, name, enclosing, is_recursive))
1676
1677
declare_async_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1678
declare_closure_symbol(location, Symbols.STATIC_ASYNC_CLOSURE(location, owner, name, enclosing, is_recursive))
1679
1680
load(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
1681
if frame? then
1682
return super.load_closure(location, loader)
1683
fi
1684
1685
return _load_lambda()
1686
si
1687
1688
to_string() -> string => "[static closure {name}]"
1689
si
1690
1691
class GLOBAL_CLOSURE: Closure is
1692
init(location: LOCATION, owner: Scope, name: string, enclosing_scope: Scope, is_recursive: bool) is
1693
super.init(location, owner, name, enclosing_scope, is_recursive)
1694
si
1695
1696
declare_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1697
declare_closure_symbol(location, Symbols.GLOBAL_CLOSURE(location, owner, name, enclosing, is_recursive))
1698
1699
declare_async_closure(location: LOCATION, name: string, owner: Scope, enclosing: Scope, is_recursive: bool, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol =>
1700
declare_closure_symbol(location, Symbols.GLOBAL_ASYNC_CLOSURE(location, owner, name, enclosing, is_recursive))
1701
1702
load(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
1703
let closure_type = type
1704
1705
if !closure_type? then
1706
return IR.Values.DUMMY(Types.ERROR(), LOCATION.internal)
1707
fi
1708
1709
if closure_type.is_error then
1710
return IR.Values.DUMMY(closure_type, LOCATION.internal)
1711
fi
1712
1713
if frame? then
1714
return super.load_closure(location, loader)
1715
fi
1716
1717
set_type_arguments()
1718
1719
return memoize_delegate(loader.load_global_anonymous_function(self, constructed_delegate_type))
1720
si
1721
1722
to_string() -> string => "[global closure {name}]"
1723
si
1724
1725
// TODO: this could be replaced with plain 'int'
1726
struct CAPTURED_TYPE_ARGUMENT is
1727
index: int
1728
1729
init(index: int) is
1730
self.index = index
1731
si
1732
si
1733
si