Appearance
| 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 |