Skip to content
← Back

src/semantic/symbols/state_machine_frame_base.ghul

1
namespace Semantic.Symbols is
2
use IO.Std
3
4
use System.Text.StringBuilder
5
6
use IoC
7
use Source
8
9
use Types.Type
10
11
use Ghul.Pipes
12
13
// Shared frame-class machinery for both generator and async
14
// state machines: type-parameter capture + substitution, lazily-
15
// allocated locals/anonymous fields, and the outer-self-type
16
// helper. Subclasses add their protocol-specific fields and
17
// ancestors.
18
class STATE_MACHINE_FRAME_BASE: Classy is
19
_owning_function: Function
20
_local_fields: Collections.LIST[Field]
21
_hoisted_locals: Collections.LIST[Variable]
22
_next_local_id: int
23
_function_to_class_type_map: Collections.MAP[Symbol, Type]
24
_mirror_by_key: Collections.MAP[TYPE_PARAMETER_KEY, Type]
25
26
// The owning function's parameters, one frame field each; the
27
// constructor takes one parameter per field and stores into it,
28
// so the body's IR for a parameter load routes through
29
// `state_machine_field` and becomes ldarg.0; ldfld instead of
30
// ldarg. `_ctor_argument_names` records the constructor's
31
// parameter names as first declared - the `$outer_self` slot
32
// (when present) followed by one per argument field - so
33
// refresh re-reads each user name's local to rebuild the types
34
// without recomputing the pairing.
35
_argument_fields: Collections.LIST[Field]
36
_argument_initial_fields: Collections.LIST[Field]
37
_ctor_argument_names: Collections.LIST[string]?
38
39
_outer_self_field: Field?
40
_constructor: Method?
41
42
owning_function: Function => _owning_function
43
local_fields: Collections.Iterable[Field] => _local_fields
44
function_to_class_type_map: Collections.Map[Symbol, Type] => _function_to_class_type_map
45
argument_fields: Collections.Iterable[Field] => _argument_fields
46
// Pristine copies of the constructor arguments, so a rewind can
47
// restore what the body started from after the body has written
48
// the working fields. A generator frame declares one per entry in
49
// `argument_fields`, at the same index and the same type; an async
50
// frame declares none, since an async method runs once. Nothing
51
// in between is legal - `has_argument_initial_fields` is the test,
52
// and a consumer that has one twin may index all of them.
53
argument_initial_fields: Collections.List[Field] => _argument_initial_fields
54
55
// Whether this frame carries pristine argument copies at all.
56
has_argument_initial_fields: bool => _argument_initial_fields.count > 0
57
outer_self_field: Field? => _outer_self_field
58
constructor: Method => _constructor!
59
60
init(
61
location: LOCATION,
62
span: LOCATION,
63
owner_owner: Scope,
64
name: string,
65
owner: Scope,
66
owning_function: Function
67
) is
68
super.init(
69
location,
70
span,
71
owner_owner,
72
name,
73
System.Array.empty[string](),
74
owner
75
)
76
77
_owning_function = owning_function
78
_local_fields = Collections.LIST[Field]()
79
_hoisted_locals = Collections.LIST[Variable]()
80
_function_to_class_type_map = Collections.MAP[Symbol, Type]()
81
_mirror_by_key = Collections.MAP[TYPE_PARAMETER_KEY, Type]()
82
_argument_fields = Collections.LIST[Field]()
83
_argument_initial_fields = Collections.LIST[Field]()
84
si
85
86
// Declare class-level type parameters mirroring those visible
87
// inside the owning function: enclosing class's first, then
88
// function's own. Order must match
89
// STATE_MACHINE_TYPE_PARAMS.walk_install /
90
// construction_type_arguments so `!N` indices line up at IL.
91
_declared_type_param_count: int
92
93
// Whether this frame declares a parameter of its own standing
94
// for `parameter`, and which one. Keyed on the symbol: two
95
// parameters in different scopes are routinely spelled alike.
96
mirrors(parameter: Symbol) -> bool =>
97
if let argument: GenericArgument = parameter then
98
_mirror_by_key.contains_key(TYPE_PARAMETER_KEY.of(argument))
99
else
100
false
101
fi
102
103
mirror_of(parameter: Symbol) -> Type =>
104
_mirror_by_key[TYPE_PARAMETER_KEY.of(cast GenericArgument?(parameter)!)]
105
106
107
declare_captured_type_params(listener: SymbolDefinitionListener) -> Collections.LIST[string] is
108
let parameters = STATE_MACHINE_TYPE_PARAMS.captured_parameters(_owning_function)
109
110
let captured_names = Collections.LIST[string]()
111
112
for parameter in parameters do
113
captured_names.add(parameter.name)
114
od
115
116
// A closure's captured set grows while its body is walked,
117
// and the frame is asked for its parameters before that
118
// finishes. Declare the ones that have arrived since rather
119
// than the whole list again: an index already handed out
120
// names a position the body has rendered against.
121
if _declared_type_param_count >= parameters.count then
122
return captured_names
123
fi
124
125
let mirror = CAPTURED_CONSTRAINT_MIRROR(t => _class_relative(t))
126
127
for i in _declared_type_param_count..parameters.count do
128
let parameter = parameters[i]
129
130
let class_type_arg = declare_type(LOCATION.internal, parameter.name, i, listener)
131
132
if let argument: GenericArgument = parameter then
133
let mirrored = cast Types.Typed?(class_type_arg)!.type!
134
135
_function_to_class_type_map[argument] = mirrored
136
_mirror_by_key[TYPE_PARAMETER_KEY.of(argument)] = mirrored
137
138
if let source: Symbol = argument.owner then
139
mirror.capture(class_type_arg, source, argument.index)
140
fi
141
fi
142
od
143
144
_declared_type_param_count = parameters.count
145
146
mirror.apply_bounds()
147
148
argument_names = captured_names
149
150
return captured_names
151
si
152
153
// Type for the `$outer_self` field + ctor argument, taking each
154
// of a generic outer class's arguments from the frame's mirrored
155
// class-T's.
156
outer_self_type(outer_classy: Classy) -> Type =>
157
SELF_CAPTURE_TYPE((classy, name) => _mirrored_class_type(classy, name)).of(outer_classy) ?? outer_classy.type!
158
159
_mirrored_class_type(owner_classy: Classy, name: string) -> Type? is
160
for i in 0..owner_classy.argument_names.count do
161
if owner_classy.argument_names[i] =~ name then
162
let parameter = cast GenericArgument?(owner_classy.type_parameter_at(i))
163
164
if parameter? /\ _function_to_class_type_map.contains_key(parameter) then
165
return _function_to_class_type_map[parameter]
166
fi
167
fi
168
od
169
170
return null
171
si
172
173
// Frame field for a body local. Idempotent — same Variable
174
// gets the same field — but refreshes the field's type on
175
// every call so iterative-inference narrowing of
176
// `local.storage_type` flows into the IL signature. A field
177
// of another frame is one a dropped frame declared, and is
178
// replaced rather than reused.
179
// A local a walk reaches before anything has typed it - a
180
// destructure leaf whose source is still an unresolved
181
// inference placeholder - has no type to give the field. A
182
// later walk declares it once the source settles, which is
183
// still before the rows are numbered.
184
declare_local_field(local: Variable) -> Field? is
185
let storage_type = local.storage_type
186
187
if !storage_type? then
188
return null
189
fi
190
191
let existing = local.state_machine_field
192
193
if existing? /\ existing.owner == self then
194
existing.set_type(_class_relative(storage_type))
195
return existing
196
fi
197
198
let id = _next_local_id
199
_next_local_id = _next_local_id + 1
200
201
let listener = IoC.CONTAINER.instance.symbol_definition_locations
202
203
let `field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$local_{local.name}_{id}")
204
`field.set_type(_class_relative(storage_type))
205
declare(LOCATION.internal, `field, listener)
206
207
_local_fields.add(`field)
208
_hoisted_locals.add(local)
209
210
local.state_machine_field = `field
211
212
return `field
213
si
214
215
// Re-derive every hoisted local field's type from the source
216
// local. declare_local_field snapshots the type at the walk
217
// moment, but flow narrowing mutates a local's type later in
218
// compile-expressions — an `if let`-bound local is typed at
219
// the scrutinee's optional type when the pattern registers and
220
// at the unwrapped type once the body has walked — and nothing
221
// re-registers it. IL emission calls this before emitting the
222
// frame, mirroring refresh_argument_field_types.
223
refresh_local_field_types() is
224
for local in _hoisted_locals do
225
if let `field = local.state_machine_field then
226
if let storage_type = local.storage_type then
227
`field.set_type(_class_relative(storage_type))
228
fi
229
fi
230
od
231
si
232
233
// Re-derive every argument field's type - and the constructor
234
// signature that hands the values in - from the source locals.
235
// Types captured at first declare() can predate inference: a
236
// closure's first declare() runs mid-inference, so an inferred
237
// parameter group holds an INFERRED_VARIABLE_TYPE placeholder
238
// until the body-retry walk settles it. Subclasses call this
239
// from their declare() when already declared; IL emission
240
// declares again before emitting the frame.
241
refresh_argument_field_types() is
242
if !_constructor? \/ !_ctor_argument_names? then
243
return
244
fi
245
246
let names = _ctor_argument_names
247
let offset = names.count - _argument_fields.count
248
249
let types = Collections.LIST[Type](names.count)
250
251
for i in 0.._argument_fields.count do
252
let local =
253
cast LOCAL_ARGUMENT?(owning_function.find_direct(names[offset + i]))
254
255
if !local? then
256
// Cannot currently happen - declare() created a
257
// field only for a resolvable local - but keep the
258
// constructor's arity stable rather than guess.
259
types.add(_argument_fields[i].type!)
260
continue
261
fi
262
263
let arg_field_type = _class_relative(
264
SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(local.type ?? Types.ERROR()))
265
266
_argument_fields[i].set_type(arg_field_type)
267
268
// The pristine copies keep step with the fields they
269
// shadow, so a rewind restores values at the type the
270
// constructor actually took.
271
if has_argument_initial_fields then
272
_argument_initial_fields[i].set_type(arg_field_type)
273
fi
274
275
types.add(arg_field_type)
276
od
277
278
// The `$outer_self` slot keeps its type; splice it back on.
279
if offset > 0 then
280
let outer_types = Collections.LIST[Type]()
281
outer_types.add(_outer_self_field!.type!)
282
for t in types do
283
outer_types.add(t)
284
od
285
_constructor!.set_arguments(names, outer_types)
286
else
287
_constructor!.set_arguments(names, types)
288
fi
289
si
290
291
// Anonymous frame field for a synthesised value with no
292
// user-named source symbol (the FOR iterator, async spill
293
// slots). Each call produces a fresh field.
294
declare_anonymous_field(prefix: string, type: Type) -> Field is
295
let id = _next_local_id
296
_next_local_id = _next_local_id + 1
297
298
let listener = IoC.CONTAINER.instance.symbol_definition_locations
299
300
let `field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "${prefix}_{id}")
301
`field.set_type(_class_relative(type))
302
declare(LOCATION.internal, `field, listener)
303
304
_local_fields.add(`field)
305
306
return `field
307
si
308
309
// The field for a syntactic site a body re-walk reaches again.
310
// Reusing the one the earlier walk allocated keeps the frame
311
// from growing a dead member per walk, and re-reading the type
312
// is what carries an inference result the earlier walk did not
313
// have: a field left holding that walk's placeholder reaches
314
// the emitter as a type it cannot encode. A field of another
315
// frame is one a dropped frame declared, and is replaced.
316
declare_or_retype_anonymous_field(existing: Field?, prefix: string, type: Type) -> Field is
317
if let `field = existing /\ `field.owner == self then
318
`field.set_type(_class_relative(type))
319
320
return `field
321
fi
322
323
return declare_anonymous_field(prefix, type)
324
si
325
326
// A fresh field for a spilled intermediate value or for an
327
// await's result. The frame holds them so they survive across
328
// MoveNext re-entries.
329
declare_spill_field(type: Type) -> Field =>
330
declare_anonymous_field("spill", type)
331
332
// Bump the shared counter without creating a field — for
333
// subclasses that name fields outside the standard prefixes
334
// (e.g. async's `$awaiter_N`).
335
next_local_id() -> int is
336
let id = _next_local_id
337
_next_local_id = _next_local_id + 1
338
return id
339
si
340
341
// Mirrors Classy.FRAME.set_type_arguments — base-level Classy
342
// doesn't define one.
343
populate_argument_names(arguments: Collections.Iterable[Symbol]) is
344
argument_names = arguments |> map(a => a.name) |> collect()
345
si
346
347
// Override Classy's default declare_type (which errors out)
348
// so generic state machines can install their type
349
// parameters, matching the CLASS shape.
350
declare_type(location: LOCATION, name: string, index: int, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol is
351
let result = Symbols.CLASSY_GENERIC_ARGUMENT(location, self, name, index)
352
declare(location, result, symbol_definition_listener)
353
return result
354
si
355
356
// Rewrite function-T references in `t` to the parallel
357
// CLASSY_GENERIC_ARGUMENT declared on this frame, so types
358
// emit `!N` (class-level) rather than `!!N` (method-level).
359
// No-op when the owning function is non-generic.
360
_class_relative(t: Type) -> Type is
361
if _function_to_class_type_map.count == 0 then
362
return t
363
fi
364
365
return t.specialize(_function_to_class_type_map)
366
si
367
368
si
369
si