Skip to content
← Back

src/syntax/process/compile-expressions/compile_expressions_state_machines.ghul

1
namespace Syntax.Process is
2
use System.Exception
3
4
use IO.Std
5
6
use Logging
7
use Source
8
9
use IR.Values
10
use IR.VALUE_CONVERTER
11
use IR.VALUE_BOXER
12
13
use Semantic.LEAST_UPPER_BOUND_MAP
14
use Semantic.Types.Type
15
16
use Syntax.Trees.Definitions.PRAGMA
17
18
use Ghul.Pipes
19
20
21
// State-machine frame declaration: iterator, spill, await-result and awaiter fields for generator and async bodies.
22
partial COMPILE_EXPRESSIONS is
23
// A `for` inside a generator or async function keeps its
24
// iterator in a frame field, so that it survives the
25
// suspensions in the loop body. Allocated here, with the rest
26
// of the frame's state, rather than while the loop is emitted:
27
// the emitted assembly's rows are numbered before any body is
28
// compiled, so a field first declared during emission is
29
// numbered by nothing and has no row to reference.
30
//
31
// No-op outside a state machine.
32
_declare_state_machine_iterator_field(carrier: Trees.IteratorCarrier, iterator_type: Type) is
33
let function = current_function
34
35
if !function? then
36
return
37
fi
38
39
let frame = _state_machine_frame_for(function)
40
41
if !frame? then
42
return
43
fi
44
45
carrier.iterator_field =
46
frame.declare_or_retype_anonymous_field(
47
carrier.iterator_field, "for_iterator", iterator_type)
48
si
49
50
// Frame fields for the two values an await has to carry across
51
// a suspend: whatever was already on the stack to its left, and
52
// its own result. Allocated here for the same reason the loop
53
// iterator above is — a field first declared while the body is
54
// emitted is numbered by nothing.
55
//
56
// Declared once. A body re-walk sees the field already there
57
// and retypes it, rather than allocating a second and leaving
58
// the first as a member nothing reads.
59
//
60
// No-op outside a state machine.
61
_declare_spill_field(spill: Trees.Expressions.SPILL, type: Type) is
62
if let frame = _enclosing_state_machine_frame() then
63
spill.spill_field =
64
frame.declare_or_retype_anonymous_field(
65
spill.spill_field, "spill", type)
66
fi
67
si
68
69
// The frame field a composite value expression routes its result
70
// through when its body suspends. Recording one is what tells IL
71
// generation to spill rather than capture, so the conditions here
72
// are the whole decision.
73
//
74
// Allocated here for the same reason the fields above are: the
75
// emitter runs after the rows the assembly contains have been
76
// fixed, so a field first declared while a body is emitted is
77
// numbered by nothing and the assembly loads and then dies
78
// looking for it.
79
_declare_composite_spill_field(node: Trees.Node, value: Value?) is
80
let frame = _enclosing_state_machine_frame()
81
82
// A field recorded by an earlier walk of the same body
83
// serves again, retyped below with whatever this walk
84
// knows. One belonging to a frame that has since been
85
// dropped and rebuilt is a reference into nothing, and is
86
// forgotten whether or not this walk records another.
87
let existing mut = _composite_spill_state.get(node)
88
89
if existing? /\ existing.owner != frame then
90
_composite_spill_state.remove(node)
91
92
existing = null
93
fi
94
95
if !value? \/ !isa IR.Values.BLOCK(value) then
96
return
97
fi
98
99
// A void result carries no data across the join, and
100
// declaring a void-typed field would itself produce invalid
101
// IL. Capture mode handles it.
102
let type = value.type
103
104
if type.is_void then
105
return
106
fi
107
108
if !_contains_suspend(node) then
109
return
110
fi
111
112
if frame? then
113
_composite_spill_state.set(
114
node,
115
frame.declare_or_retype_anonymous_field(existing, "spill", type))
116
fi
117
si
118
119
_contains_suspend(node: Trees.Node) -> bool => (
120
let scanner = CONTAINS_SUSPEND_SCANNER()
121
node.walk(scanner)
122
scanner.found
123
)
124
125
_declare_await_result_field(`await: Trees.Expressions.AWAIT, type: Type) is
126
if let frame = _enclosing_state_machine_frame() then
127
`await.result_field =
128
frame.declare_or_retype_anonymous_field(
129
`await.result_field, "spill", type)
130
fi
131
si
132
133
// The awaiter an `await` holds while suspended. Its type is
134
// settled by resolving the operand's awaiter pattern, which
135
// happens here, so the field is allocated here too.
136
_declare_awaiter_field(`await: Trees.Expressions.AWAIT, awaiter_type: Type) is
137
if let frame: Semantic.Symbols.ASYNC_STATE_MACHINE_FRAME =
138
_enclosing_state_machine_frame()
139
then
140
`await.awaiter_field =
141
frame.declare_or_retype_awaiter_field(
142
`await.awaiter_field, awaiter_type)
143
fi
144
si
145
146
_declare_lambda_state_machine_frame(function: Trees.Expressions.FUNCTION) is
147
let closure = cast Semantic.Symbols.Function?(function.scope)
148
149
if !closure? then
150
return
151
fi
152
153
if let state_machine = Semantic.Symbols.state_machine_for(closure) then
154
if let frame = state_machine.frame then
155
frame.declare()
156
_register_lambda_pattern_fields(function, closure, frame)
157
fi
158
elif let async_state_machine = Semantic.Symbols.async_state_machine_for(closure) then
159
if let frame = async_state_machine.frame then
160
frame.declare()
161
_register_lambda_pattern_fields(function, closure, frame)
162
fi
163
fi
164
si
165
166
// A destructured lambda parameter's leaves are body locals, so
167
// inside a generator or async closure each one needs a frame
168
// field, exactly as `let`-bound names get one: MoveNext routes
169
// their loads and stores through it. The aggregate stays an
170
// argument - `frame.declare()` above wires its field from the
171
// closure's argument names. No-op for simple parameters and
172
// for closures with no state machine.
173
_register_lambda_pattern_fields(
174
function: Trees.Expressions.FUNCTION,
175
closure: Semantic.Symbols.Function,
176
frame: Semantic.Symbols.STATE_MACHINE_FRAME_BASE
177
) is
178
for a in function.arguments.expressions do
179
let argument = cast Trees.Expressions.VARIABLE?(a)
180
181
if !argument? \/ !argument.left? then
182
continue
183
fi
184
185
let names_into = Collections.LIST[Trees.Identifiers.Identifier]()
186
argument.left.get_names_into(names_into)
187
188
for name in names_into do
189
let symbol = closure.find_direct(name.name)
190
191
if let local: Semantic.Symbols.LOCAL_VARIABLE = symbol then
192
frame.declare_local_field(local)
193
fi
194
od
195
od
196
si
197
198
_enclosing_state_machine_frame() -> Semantic.Symbols.STATE_MACHINE_FRAME_BASE? is
199
let function = current_function
200
201
if !function? then
202
return null
203
fi
204
205
return _state_machine_frame_for(function)
206
si
207
208
// The frame of whichever kind of state machine encloses
209
// `function`, or null when none does. Generator and async are
210
// mutually exclusive — declare-symbols rejects a function that
211
// is both.
212
_state_machine_frame_for(
213
function: Semantic.Symbols.Function
214
) -> Semantic.Symbols.STATE_MACHINE_FRAME_BASE? is
215
if let state_machine = Semantic.Symbols.state_machine_for(function) then
216
return state_machine.frame
217
fi
218
219
if let async_state_machine = Semantic.Symbols.async_state_machine_for(function) then
220
return async_state_machine.frame
221
fi
222
223
return null
224
si
225
226
// Iterate the names on a variable-left pattern and, when the
227
// enclosing function is a generator, register each LOCAL_VARIABLE
228
// with the state-machine frame. LOCAL_ARGUMENTs are filtered out
229
// — those are already wired by `frame.declare()` at function
230
// entry — and non-generator functions are a no-op. Public so
231
// COMPILE_CONDITIONALS can register the names an `if let` /
232
// `while let` / case-when pattern binds, which never pass
233
// through the plain-`let` visitors below.
234
declare_state_machine_local_fields(left: Trees.Variables.VariableLeft) is
235
236
let function = current_function
237
238
if !function? then
239
return
240
fi
241
242
// Try generator first, then async — they're mutually
243
// exclusive (declare-symbols enforces, see the
244
// generator-and-async-not-allowed diagnostic).
245
let state_machine = Semantic.Symbols.state_machine_for(function)
246
let async_state_machine = Semantic.Symbols.async_state_machine_for(function)
247
248
let names_into = Collections.LIST[Trees.Identifiers.Identifier]()
249
left.get_names_into(names_into)
250
251
if state_machine? /\ state_machine.frame? then
252
if let frame = state_machine.frame then
253
for name in names_into do
254
let symbol = find(name)
255
if let local: Semantic.Symbols.LOCAL_VARIABLE = symbol then
256
frame.declare_local_field(local)
257
fi
258
od
259
fi
260
elif async_state_machine? /\ async_state_machine.frame? then
261
if let frame = async_state_machine.frame then
262
for name in names_into do
263
let symbol = find(name)
264
if let local: Semantic.Symbols.LOCAL_VARIABLE = symbol then
265
frame.declare_local_field(local)
266
fi
267
od
268
fi
269
fi
270
si
271
272
si
273
si