Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use System.Reflection.Metadata.ILOpCode | |
| 3 | use IO.Std | |
| 4 | ||
| 5 | use System.Text.StringBuilder | |
| 6 | ||
| 7 | use Logging | |
| 8 | use Trees | |
| 9 | use Source | |
| 10 | ||
| 11 | use IR | |
| 12 | use IR.Values | |
| 13 | ||
| 14 | use Ghul.Pipes | |
| 15 | ||
| 16 | ||
| 17 | // Function and closure IL: plain function pre/visit and SRM emission, closure walking and | |
| 18 | // the async-closure prologue. | |
| 19 | partial GENERATE_IL is | |
| 20 | pre(function: Definitions.FUNCTION) -> bool is | |
| 21 | let symbol = function_for(function)! | |
| 22 | ||
| 23 | // Generator branch: a `*_GENERATOR_*` function expands | |
| 24 | // into TWO IL methods (the outer stub returning a fresh | |
| 25 | // state-machine instance + the state machine class with | |
| 26 | // its MoveNext / get_Current / GetEnumerator / Dispose / | |
| 27 | // Reset implementations). The split is handled by the | |
| 28 | // gen-generator-function path so the rest of generate_il | |
| 29 | // doesn't need to know. | |
| 30 | // | |
| 31 | // Both branches come before ordinary function emission, | |
| 32 | // because a state machine is a shape rather than a body: | |
| 33 | // the expansion has to happen before any of the pieces it | |
| 34 | // produces can be emitted. | |
| 35 | let state_machine = Semantic.Symbols.state_machine_for(cast Semantic.Symbols.Function?(symbol)!) | |
| 36 | ||
| 37 | if state_machine? then | |
| 38 | return _pre_generator_function(function, cast Semantic.Symbols.Function?(symbol)!, state_machine) | |
| 39 | fi | |
| 40 | ||
| 41 | // Async branch (parallel to the generator branch above). | |
| 42 | let async_sm = Semantic.Symbols.async_state_machine_for(cast Semantic.Symbols.Function?(symbol)!) | |
| 43 | ||
| 44 | if async_sm? then | |
| 45 | return _pre_async_function(function, cast Semantic.Symbols.Function?(symbol)!, async_sm) | |
| 46 | fi | |
| 47 | ||
| 48 | return _pre_function_srm(function, cast Semantic.Symbols.Function?(symbol)!) | |
| 49 | si | |
| 50 | ||
| 51 | // Emission for every function the assembly defines. The | |
| 52 | // structure walk has already | |
| 53 | // reserved this function's MethodDef row and will write it once | |
| 54 | // the walk is done, so nothing here touches metadata: the body | |
| 55 | // is encoded into its own buffer and its offset left against | |
| 56 | // the symbol. | |
| 57 | _pre_function_srm(function: Definitions.FUNCTION, symbol: Semantic.Symbols.Function) -> bool is | |
| 58 | // A declaration with no body - an abstract method, or a | |
| 59 | // trait member left to its implementors - has nothing to | |
| 60 | // encode, and its row is marked abstract rather than | |
| 61 | // pointed at an empty body. | |
| 62 | if symbol.is_abstract then | |
| 63 | return true | |
| 64 | fi | |
| 65 | ||
| 66 | // A method that calls into a shared library has no body of | |
| 67 | // its own: the runtime builds the call from the import its | |
| 68 | // row names. | |
| 69 | if symbol.pinvoke? then | |
| 70 | return true | |
| 71 | fi | |
| 72 | ||
| 73 | enter_scope(function) | |
| 74 | ||
| 75 | _context.current_srm_body_emitter = IR.Emitter.SRM_METHOD_BODY_EMITTER() | |
| 76 | ||
| 77 | // Which function the assembly enters at was settled by | |
| 78 | // select-entry-point, before any IL was written; this hands | |
| 79 | // the choice to the emitter as the walk reaches it. | |
| 80 | symbol.gen_entrypoint(_context) | |
| 81 | ||
| 82 | enter_block() | |
| 83 | ||
| 84 | return false | |
| 85 | si | |
| 86 | ||
| 87 | // The tail of `visit(function)` below. | |
| 88 | _visit_function_srm(function: Definitions.FUNCTION, symbol: Semantic.Symbols.Function) is | |
| 89 | if System.Environment.get_environment_variable("GHUL_EMIT_TRACE")? then | |
| 90 | IO.Std.error.write_line("emit: {symbol.qualified_name} {symbol.location}") | |
| 91 | fi | |
| 92 | ||
| 93 | let body_emitter = _context.current_srm_body_emitter! | |
| 94 | let assembly_emitter = _context.srm_assembly_emitter | |
| 95 | ||
| 96 | // A body-less method that overrides an implemented one has | |
| 97 | // a slot to fill and nothing to fill it with, so the body | |
| 98 | // is a throw naming the method rather than a load of the | |
| 99 | // return type's default. | |
| 100 | let throws_unimplemented = symbol.throws_unimplemented | |
| 101 | ||
| 102 | if throws_unimplemented then | |
| 103 | add(Values.Literal.STRING(symbol.qualified_name, _innate_symbol_lookup.get_string_type())) | |
| 104 | add(Values.NEW_NOT_IMPLEMENTED_EXCEPTION()) | |
| 105 | add(Values.INSTRUCTION(System.Reflection.Metadata.ILOpCode.THROW)) | |
| 106 | fi | |
| 107 | ||
| 108 | // An implicit tail return delivers the body list's captured | |
| 109 | // value - the whole body IL ending in the tail's value - boxed | |
| 110 | // against the declared return type, in place of the default | |
| 111 | // value loaded below. | |
| 112 | let tail = if throws_unimplemented then null else _function_body_tail(function.body) fi | |
| 113 | ||
| 114 | if !tail? then | |
| 115 | current_block.gen(_context) | |
| 116 | else | |
| 117 | add(_boxer.box_if_needed(tail, symbol.return_type!)) | |
| 118 | current_block.gen(_context) | |
| 119 | fi | |
| 120 | ||
| 121 | leave_block() | |
| 122 | ||
| 123 | // A non-void function ends with a default value loaded | |
| 124 | // ahead of the trailing `ret`. The | |
| 125 | // `ret` is unreachable whenever every path already | |
| 126 | // returned, but unreachable is not the same as unverified: | |
| 127 | // a method containing a protected region is checked whole, | |
| 128 | // so a bare `ret` where a value is due is rejected outright | |
| 129 | // rather than pruned. | |
| 130 | // | |
| 131 | // A body that delivered a tail value already leaves exactly | |
| 132 | // that value on the stack, so no default load follows it. | |
| 133 | // A machine-less result-less task-like return completes | |
| 134 | // here instead of loading the default: its fall-through | |
| 135 | // owes a handle, and the builder produces one. | |
| 136 | if !tail? /\ !throws_unimplemented then | |
| 137 | if let return_type = symbol.return_type then | |
| 138 | if return_type.compare(_innate_symbol_lookup.get_void_type()) != Semantic.Types.MATCH.SAME then | |
| 139 | if let completion = _builder_task_like_completion(symbol, return_type) then | |
| 140 | completion.gen(_context) | |
| 141 | else | |
| 142 | Values.DEFAULT_RETURN(return_type).gen(_context) | |
| 143 | fi | |
| 144 | fi | |
| 145 | fi | |
| 146 | fi | |
| 147 | ||
| 148 | body_emitter.ret() | |
| 149 | ||
| 150 | assembly_emitter.handles.set_body_offset(symbol, body_emitter.flush(assembly_emitter)) | |
| 151 | assembly_emitter.handles.set_il_outputs(symbol, body_emitter.il_outputs) | |
| 152 | ||
| 153 | _context.current_srm_body_emitter = null | |
| 154 | ||
| 155 | leave_scope(function) | |
| 156 | si | |
| 157 | ||
| 158 | // The completion value a machine-less body owes for a bare | |
| 159 | // `return;` or a fall-through: the handle produced by driving | |
| 160 | // the task-like's own builder. Which return types complete this | |
| 161 | // way is TASK_CONVERSION's to say - asking it rather than | |
| 162 | // restating it keeps emission and the semantic side, which | |
| 163 | // suppresses the definite-return warning on the same answer, | |
| 164 | // from drifting apart. A state machine's own trailer completes | |
| 165 | // through the builder field, so a body with one never asks. | |
| 166 | _builder_task_like_completion( | |
| 167 | function_symbol: Semantic.Symbols.Function, | |
| 168 | return_type: Semantic.Types.Type? | |
| 169 | ) -> IR.Values.Value? is | |
| 170 | if | |
| 171 | Semantic.Symbols.async_state_machine_for(function_symbol)? \/ | |
| 172 | Semantic.Symbols.state_machine_for(function_symbol)? | |
| 173 | then | |
| 174 | return null | |
| 175 | fi | |
| 176 | ||
| 177 | if let task_like = _task_conversion.builder_completion_for(return_type) then | |
| 178 | return IR.Values.COMPLETED_TASK_LIKE(return_type!, task_like.builder_type) | |
| 179 | fi | |
| 180 | ||
| 181 | return null | |
| 182 | si | |
| 183 | ||
| 184 | // The captured value of a function body whose final statement is | |
| 185 | // an implicit tail return - the whole body IL, ending in the | |
| 186 | // tail's value - or null when the body delivers nothing | |
| 187 | // (terminated tails, empty bodies, void bodies, generators). | |
| 188 | _function_body_tail(body: Trees.Bodies.Body?) -> IR.Values.Value? is | |
| 189 | if let block = cast Trees.Bodies.BLOCK?(body) then | |
| 190 | if let v = block.statements.compile_expressions_state.value then | |
| 191 | return v | |
| 192 | fi | |
| 193 | fi | |
| 194 | ||
| 195 | return null | |
| 196 | si | |
| 197 | ||
| 198 | // Deliver what an async body produces to its state machine's | |
| 199 | // result, before the leave to the success label whose trailer | |
| 200 | // reads that field to complete the builder. | |
| 201 | _gen_async_body_result( | |
| 202 | body: Trees.Bodies.Body?, | |
| 203 | frame: Semantic.Symbols.ASYNC_STATE_MACHINE_FRAME | |
| 204 | ) is | |
| 205 | if let result_field = frame.result_field, class_result_type = frame.class_result_type then | |
| 206 | if let value = _async_body_value(body) then | |
| 207 | add(_build_frame_field_store( | |
| 208 | frame, | |
| 209 | result_field, | |
| 210 | _boxer.box_if_needed(value, class_result_type) | |
| 211 | )) | |
| 212 | fi | |
| 213 | ||
| 214 | return | |
| 215 | fi | |
| 216 | ||
| 217 | // A result-less machine has nowhere to put a value, so an | |
| 218 | // expression body is emitted for its effects and whatever it | |
| 219 | // leaves standing is dropped. A block body's statements have | |
| 220 | // already emitted themselves. | |
| 221 | if let expression_body = cast Trees.Bodies.EXPRESSION?(body) then | |
| 222 | if let value = expression_body.expression.value then | |
| 223 | add(value) | |
| 224 | ||
| 225 | if let value_type = value.type then | |
| 226 | if !value_type.is_void then | |
| 227 | add(Values.INSTRUCTION(ILOpCode.POP)) | |
| 228 | fi | |
| 229 | fi | |
| 230 | fi | |
| 231 | fi | |
| 232 | si | |
| 233 | ||
| 234 | // What an async body leaves for the result field. A block body | |
| 235 | // leaves its tail; an `=> body` leaves its expression, whose own | |
| 236 | // visitor emits nothing when the machine has a result field so | |
| 237 | // the value arrives here once. | |
| 238 | _async_body_value(body: Trees.Bodies.Body?) -> IR.Values.Value? is | |
| 239 | if let expression_body = cast Trees.Bodies.EXPRESSION?(body) then | |
| 240 | return expression_body.expression.value | |
| 241 | fi | |
| 242 | ||
| 243 | return _function_body_tail(body) | |
| 244 | si | |
| 245 | ||
| 246 | visit(function: Definitions.FUNCTION) is | |
| 247 | let symbol = symbol_for(function) | |
| 248 | ||
| 249 | let state_machine = Semantic.Symbols.state_machine_for(cast Semantic.Symbols.Function?(symbol)!) | |
| 250 | ||
| 251 | if state_machine? then | |
| 252 | _visit_generator_function(function, cast Semantic.Symbols.Function?(symbol)!, state_machine) | |
| 253 | ||
| 254 | return | |
| 255 | fi | |
| 256 | ||
| 257 | let async_sm = Semantic.Symbols.async_state_machine_for(cast Semantic.Symbols.Function?(symbol)!) | |
| 258 | ||
| 259 | if async_sm? then | |
| 260 | // _pre_async_function already emitted the outer | |
| 261 | // method and stashed the move_next block onto | |
| 262 | // _pending_async_state_machines. Nothing else to | |
| 263 | // do at AST visit time. | |
| 264 | return | |
| 265 | fi | |
| 266 | ||
| 267 | let symbol_function = cast Semantic.Symbols.Function?(symbol)! | |
| 268 | ||
| 269 | if !symbol_function.is_abstract /\ !symbol_function.pinvoke? then | |
| 270 | _visit_function_srm(function, symbol_function) | |
| 271 | fi | |
| 272 | si | |
| 273 | ||
| 274 | pre(function: Trees.Expressions.FUNCTION) -> bool is | |
| 275 | super.pre(function) | |
| 276 | ||
| 277 | let closure = cast Semantic.Symbols.Closure?(scope_for(function)) | |
| 278 | ||
| 279 | if !closure? then | |
| 280 | return true | |
| 281 | fi | |
| 282 | ||
| 283 | closure.map_type_arguments() | |
| 284 | ||
| 285 | return true | |
| 286 | si | |
| 287 | ||
| 288 | // A function literal's body is a method of its own, so the loops | |
| 289 | // and protected regions it is written inside are not ones it can | |
| 290 | // branch out of: a return in it leaves its own method, whatever | |
| 291 | // try, disposal region or loop encloses the literal. | |
| 292 | visit(function: Trees.Expressions.FUNCTION) is | |
| 293 | let enclosing_loops = _loops | |
| 294 | ||
| 295 | _loops = LOOP_LABEL_STACK() | |
| 296 | ||
| 297 | _visit_function_literal(function) | |
| 298 | ||
| 299 | _loops = enclosing_loops | |
| 300 | si | |
| 301 | ||
| 302 | _visit_function_literal(function: Trees.Expressions.FUNCTION) is | |
| 303 | let closure = cast Semantic.Symbols.Closure?(scope_for(function))! | |
| 304 | ||
| 305 | // Async closure dispatch (parallel to the named-function | |
| 306 | // path in pre(Definitions.FUNCTION)). The lambda's $anon | |
| 307 | // method body becomes a state-machine launch sequence | |
| 308 | // (Create + MoveNext + return Task); the lambda body | |
| 309 | // walks into a separate move_next_block; the SM class is | |
| 310 | // queued for emission at class/namespace tail. | |
| 311 | let async_sm = Semantic.Symbols.async_state_machine_for(closure) | |
| 312 | if async_sm? then | |
| 313 | _emit_async_closure(function, closure, async_sm) | |
| 314 | super.visit(function) | |
| 315 | closure.unmap_type_arguments() | |
| 316 | return | |
| 317 | fi | |
| 318 | ||
| 319 | enter_block() | |
| 320 | ||
| 321 | // Safety net: if a closure's return type resolved to ERROR | |
| 322 | // (e.g. its body referenced a variable that itself never got | |
| 323 | // a type), the consume-side check that should have surfaced | |
| 324 | // a diagnostic missed this path — but `get_il_type()` will | |
| 325 | // throw NotImplementedException as soon as we ask it to emit | |
| 326 | // ERROR. Report a located diagnostic at the function's site | |
| 327 | // and skip IL emission for this body. The function symbol | |
| 328 | // itself remains valid; any caller will already have its | |
| 329 | // own ERROR-typed result and will be handled by its own | |
| 330 | // visit. (See error.ghul:30-34 for the underlying rule.) | |
| 331 | if closure.return_type? /\ closure.return_type.is_error then | |
| 332 | _logger.error(function.location, "cannot infer return type here") | |
| 333 | leave_block() | |
| 334 | super.visit(function) | |
| 335 | closure.unmap_type_arguments() | |
| 336 | return | |
| 337 | fi | |
| 338 | ||
| 339 | // The closure's body is encoded into a method body of its | |
| 340 | // own. The enclosing method is mid-encode, so its emitter | |
| 341 | // is set aside for the duration and restored after: the | |
| 342 | // two bodies are separate, and the instructions of one | |
| 343 | // must not land in the other. | |
| 344 | let assembly_emitter = _context.srm_assembly_emitter | |
| 345 | let enclosing = _context.current_srm_body_emitter | |
| 346 | let enclosing_member = assembly_emitter.current_emission_member | |
| 347 | let body_emitter = IR.Emitter.SRM_METHOD_BODY_EMITTER() | |
| 348 | ||
| 349 | _context.current_srm_body_emitter = body_emitter | |
| 350 | ||
| 351 | // Instructions in this body name types in the closure's terms, | |
| 352 | // and a TypeSpec they reference is built from here rather than | |
| 353 | // from the closure's own signature, so the emitter is told | |
| 354 | // which member it is writing into. | |
| 355 | assembly_emitter.current_emission_member = closure | |
| 356 | ||
| 357 | _gen_closure_destructure_prologue(function, closure) | |
| 358 | ||
| 359 | function.body.walk(self) | |
| 360 | ||
| 361 | // An implicit tail return delivers the body list's captured | |
| 362 | // value boxed against the closure's return type, in place of | |
| 363 | // the default value a non-void fall-off loads. | |
| 364 | let closure_tail = _function_body_tail(function.body) | |
| 365 | ||
| 366 | if let ct = closure_tail, crt = closure.return_type then | |
| 367 | add(_boxer.box_if_needed(ct, crt)) | |
| 368 | elif | |
| 369 | closure.return_type? /\ | |
| 370 | closure.return_type.compare(_innate_symbol_lookup.get_void_type()) != Semantic.Types.MATCH.SAME | |
| 371 | then | |
| 372 | if let completion = _builder_task_like_completion(closure, closure.return_type) then | |
| 373 | add(completion) | |
| 374 | else | |
| 375 | add(Values.DEFAULT_RETURN(closure.return_type!)) | |
| 376 | fi | |
| 377 | fi | |
| 378 | ||
| 379 | add(Values.RET()) | |
| 380 | ||
| 381 | current_block.gen(_context) | |
| 382 | ||
| 383 | leave_block() | |
| 384 | ||
| 385 | assembly_emitter.handles.set_body_offset( | |
| 386 | closure, body_emitter.flush(assembly_emitter)) | |
| 387 | assembly_emitter.handles.set_il_outputs( | |
| 388 | closure, body_emitter.il_outputs) | |
| 389 | ||
| 390 | _context.current_srm_body_emitter = enclosing | |
| 391 | assembly_emitter.current_emission_member = enclosing_member | |
| 392 | ||
| 393 | super.visit(function) | |
| 394 | ||
| 395 | closure.unmap_type_arguments() | |
| 396 | si | |
| 397 | ||
| 398 | // Declare and fill the names bound by any destructured lambda | |
| 399 | // parameter, before the body that reads them runs. The | |
| 400 | // parameter itself is one physical argument holding the | |
| 401 | // aggregate; its leaves are ordinary locals, so they need both | |
| 402 | // a `.locals init` slot and the unpacking stores - the same | |
| 403 | // pair `pre(Variables.VARIABLE)` and `visit(Variables.VARIABLE)` | |
| 404 | // emit for a named function's destructured formal argument. | |
| 405 | _gen_closure_destructure_prologue( | |
| 406 | function: Trees.Expressions.FUNCTION, | |
| 407 | closure: Semantic.Symbols.Closure | |
| 408 | ) is | |
| 409 | _gen_packed_parameter_prologue(function.location, closure) | |
| 410 | ||
| 411 | for a in function.arguments.expressions do | |
| 412 | let argument = cast Trees.Expressions.VARIABLE?(a) | |
| 413 | ||
| 414 | if !argument? \/ !argument.left? then | |
| 415 | continue | |
| 416 | fi | |
| 417 | ||
| 418 | let left = argument.left | |
| 419 | ||
| 420 | for name in left.names! do | |
| 421 | _declare_local(closure.find_direct(name.name)) | |
| 422 | od | |
| 423 | ||
| 424 | let group_symbol = closure.find_direct(argument.name.name) | |
| 425 | ||
| 426 | if group_symbol? then | |
| 427 | gen_destructuring_initialize(left, group_symbol.load(argument.location, null, _symbol_loader)) | |
| 428 | fi | |
| 429 | od | |
| 430 | si | |
| 431 | ||
| 432 | // A literal written with an argument pack spread out is emitted | |
| 433 | // taking the pack's tuple. Each parameter it declared is a local, | |
| 434 | // filled from the tuple before the body that reads it runs. The | |
| 435 | // pack binds to anything that destructures by position, so the | |
| 436 | // elements come from the tuple's members or from a `deconstruct` | |
| 437 | // call, as they do for a destructuring `let`. | |
| 438 | _gen_packed_parameter_prologue(location: Source.LOCATION, closure: Semantic.Symbols.Closure) is | |
| 439 | let packed = closure.packed_parameters | |
| 440 | let group = closure.pack_group | |
| 441 | ||
| 442 | if !packed? \/ !group? then | |
| 443 | return | |
| 444 | fi | |
| 445 | ||
| 446 | for parameter in packed do | |
| 447 | current_block.add(Values.DECLARE_LOCAL(IR.Values.packed_parameter_local_for(parameter)!, parameter.type!)) | |
| 448 | od | |
| 449 | ||
| 450 | let strategy = DESTRUCTURE_RESOLVER.resolve_strategy(group.type, packed.count) | |
| 451 | ||
| 452 | let elements = Collections.LIST[Value]() | |
| 453 | ||
| 454 | if strategy.is_deconstruct then | |
| 455 | let deconstruct = strategy.deconstruct_function! | |
| 456 | let call_arguments = Collections.LIST[Value]() | |
| 457 | ||
| 458 | for i in 0..deconstruct.arguments.count do | |
| 459 | let ref_type = deconstruct.arguments[i] | |
| 460 | let element_type = ref_type.get_element_type() | |
| 461 | ||
| 462 | assert element_type? else "deconstruct argument type has no element type" | |
| 463 | ||
| 464 | let element = IR.TEMP(current_block, "pack_element", i, element_type) | |
| 465 | ||
| 466 | elements.add(element.load()) | |
| 467 | call_arguments.add(IR.Values.ADDRESS(element.load(), ref_type)) | |
| 468 | od | |
| 469 | ||
| 470 | add( | |
| 471 | deconstruct.call( | |
| 472 | location, | |
| 473 | group.load(location, null, _symbol_loader), | |
| 474 | call_arguments, | |
| 475 | null, | |
| 476 | _function_caller | |
| 477 | ) | |
| 478 | ) | |
| 479 | else | |
| 480 | for i in 0..packed.count do | |
| 481 | elements.add(strategy.members[i]!.load(location, group.load(location, null, _symbol_loader), _symbol_loader)) | |
| 482 | od | |
| 483 | fi | |
| 484 | ||
| 485 | assert elements.count == packed.count else "argument pack of {packed.count} parameters has {elements.count} elements" | |
| 486 | ||
| 487 | for i in 0..packed.count do | |
| 488 | add(Values.Store.LOCAL_ARGUMENT(packed[i], _boxer.box_if_needed(elements[i], packed[i].type!))) | |
| 489 | od | |
| 490 | si | |
| 491 | ||
| 492 | // Unpack a named function's destructured formal arguments into | |
| 493 | // their leaf frame fields. The plain path needs no call to this: | |
| 494 | // its AST walk reaches each formal's Variables.VARIABLE and | |
| 495 | // `visit(Variables.VARIABLE)` emits the same stores at method | |
| 496 | // entry. The generator and async paths drive their own walk into | |
| 497 | // MoveNext and never reach those nodes, so they emit this first - | |
| 498 | // placed after the state dispatch, so a resumed MoveNext branches | |
| 499 | // straight to its await label and the unpack runs once, on first | |
| 500 | // entry. The aggregate loads through its `$arg_*` frame field; | |
| 501 | // compile-expressions registered every leaf's field. | |
| 502 | _gen_destructured_formal_prologue(function: Definitions.FUNCTION) is | |
| 503 | for v in function.arguments do | |
| 504 | if v.is_argument /\ !v.left.is_simple_name then | |
| 505 | let group_symbol = symbol_for(v) | |
| 506 | ||
| 507 | if group_symbol? then | |
| 508 | gen_destructuring_initialize(v.left, group_symbol.load(v.location, null, _symbol_loader)) | |
| 509 | fi | |
| 510 | fi | |
| 511 | od | |
| 512 | si | |
| 513 | ||
| 514 | // Async closure: the lambda's $anon body becomes a builder- | |
| 515 | // create / Start / return-Task launch sequence; the user body | |
| 516 | // walks into a separate move_next_block; the SM class is | |
| 517 | // queued for class-tail emission alongside named-function SMs. | |
| 518 | _emit_async_closure( | |
| 519 | function: Trees.Expressions.FUNCTION, | |
| 520 | closure: Semantic.Symbols.Closure, | |
| 521 | async_state_machine: Semantic.Symbols.ASYNC_STATE_MACHINE | |
| 522 | ) is | |
| 523 | let frame = async_state_machine.frame | |
| 524 | ||
| 525 | if !frame? then | |
| 526 | _logger.error(function.location, "async function must return a task-like type — one carrying AsyncMethodBuilderAttribute, such as Tasks.TASK or Tasks.ValueTask") | |
| 527 | return | |
| 528 | fi | |
| 529 | ||
| 530 | frame.declare() | |
| 531 | ||
| 532 | // Body walk before launch IL — populates the frame's | |
| 533 | // await labels / locals / awaiters before launch | |
| 534 | // references any of them. | |
| 535 | async_state_machine.install_body_emission_overrides() | |
| 536 | ||
| 537 | enter_scope(function) | |
| 538 | ||
| 539 | let move_next_block = Values.BLOCK() | |
| 540 | ||
| 541 | enter_block(move_next_block) | |
| 542 | ||
| 543 | add(Values.MAX_STACK(64)) | |
| 544 | ||
| 545 | // Same V_state caching + per-region dispatch pattern as | |
| 546 | // `_pre_async_function` (see its commentary). Awaits in | |
| 547 | // the closure body register with the outer holder unless | |
| 548 | // an inner `.try` pushes its own. | |
| 549 | let outer_dispatch_holder = | |
| 550 | _open_state_machine_entry(frame, frame.state_field, ".async_state") | |
| 551 | ||
| 552 | let success_label = IR.LABEL() | |
| 553 | let end_label = IR.LABEL() | |
| 554 | ||
| 555 | let prev_async_sm = _current_async_state_machine | |
| 556 | let prev_success = _current_async_success_label | |
| 557 | let prev_end = _current_async_end_label | |
| 558 | _current_async_state_machine = async_state_machine | |
| 559 | _current_async_success_label = success_label | |
| 560 | _current_async_end_label = end_label | |
| 561 | ||
| 562 | // The plain-closure path emits this before its body walk | |
| 563 | // too - here into MoveNext, after the state dispatch, where | |
| 564 | // the leaves' loads route through the frame fields | |
| 565 | // `_register_lambda_pattern_fields` registered. | |
| 566 | _gen_closure_destructure_prologue(function, closure) | |
| 567 | ||
| 568 | function.body.walk(self) | |
| 569 | ||
| 570 | // What the body produces goes into the frame's result before | |
| 571 | // leaving to success, the same as a named function's state | |
| 572 | // machine - the trailer reads that field to complete the | |
| 573 | // builder, so a body that never reaches it completes with the | |
| 574 | // result type's default. | |
| 575 | _gen_async_body_result(function.body, frame) | |
| 576 | ||
| 577 | add(Values.BRANCH_TO(ILOpCode.LEAVE, success_label)) | |
| 578 | ||
| 579 | _current_async_state_machine = prev_async_sm | |
| 580 | _current_async_success_label = prev_success | |
| 581 | _current_async_end_label = prev_end | |
| 582 | ||
| 583 | _pop_async_dispatch_holder(outer_dispatch_holder) | |
| 584 | ||
| 585 | leave_block() | |
| 586 | leave_scope(function) | |
| 587 | ||
| 588 | async_state_machine.uninstall_body_emission_overrides() | |
| 589 | ||
| 590 | // --- Build the builder-launch sequence --- | |
| 591 | enter_block() | |
| 592 | add(Values.MAX_STACK(64)) | |
| 593 | ||
| 594 | let construction_type_args = async_state_machine.get_construction_type_arguments() | |
| 595 | ||
| 596 | let constructor = | |
| 597 | _specialized_constructor( | |
| 598 | frame, frame.constructor, construction_type_args, function.location) | |
| 599 | ||
| 600 | let state_machine_type = | |
| 601 | _specialized_frame_type(frame, construction_type_args, function.location) ?? frame.type! | |
| 602 | ||
| 603 | // Ctor args: instance closures prepend ldarg.0 (the | |
| 604 | // closure-frame instance, which holds captures), then | |
| 605 | // each user-declared lambda parameter via ldarg '<name>'. | |
| 606 | let ctor_args_block = Values.BLOCK() | |
| 607 | ||
| 608 | // As above: the closure's receiver, when it has one, takes | |
| 609 | // slot 0 and its declared parameters follow. | |
| 610 | let slot mut = if closure.is_emitted_with_receiver then 1 else 0 fi | |
| 611 | ||
| 612 | if frame.outer_self_field? then | |
| 613 | ctor_args_block.add(Values.Load.ARGUMENT_SLOT(0, "ldarg.0")) | |
| 614 | fi | |
| 615 | for arg_name in closure.argument_names do | |
| 616 | ctor_args_block.add(Values.Load.ARGUMENT_SLOT(slot, "ldarg '{arg_name}'")) | |
| 617 | slot = slot + 1 | |
| 618 | od | |
| 619 | ctor_args_block.close() | |
| 620 | ||
| 621 | let task_type = closure.return_type! | |
| 622 | ||
| 623 | let launch = IR.Values.ASYNC_METHOD_LAUNCH( | |
| 624 | task_type, | |
| 625 | state_machine_type, | |
| 626 | constructor, | |
| 627 | ctor_args_block, | |
| 628 | frame.state_field, | |
| 629 | frame.builder_field!, | |
| 630 | ".sm" | |
| 631 | ) | |
| 632 | ||
| 633 | add(launch) | |
| 634 | add(Values.RET()) | |
| 635 | ||
| 636 | // The launch is the closure's own method body, not part of | |
| 637 | // whatever is being emitted around the literal, so it needs | |
| 638 | // a body emitter of its own or it lands in the enclosing | |
| 639 | // method. | |
| 640 | let assembly_emitter = _context.srm_assembly_emitter | |
| 641 | let enclosing = _context.current_srm_body_emitter | |
| 642 | let body = IR.Emitter.SRM_METHOD_BODY_EMITTER() | |
| 643 | ||
| 644 | _context.current_srm_body_emitter = body | |
| 645 | ||
| 646 | try | |
| 647 | current_block.gen(_context) | |
| 648 | ||
| 649 | assembly_emitter.handles.set_body_offset( | |
| 650 | closure, body.flush(assembly_emitter)) | |
| 651 | assembly_emitter.handles.set_il_outputs( | |
| 652 | closure, body.il_outputs) | |
| 653 | finally | |
| 654 | _context.current_srm_body_emitter = enclosing | |
| 655 | yrt | |
| 656 | ||
| 657 | leave_block() | |
| 658 | ||
| 659 | _pending_async_state_machines.add( | |
| 660 | PENDING_ASYNC_STATE_MACHINE( | |
| 661 | async_state_machine, | |
| 662 | move_next_block, | |
| 663 | success_label, | |
| 664 | end_label | |
| 665 | ) | |
| 666 | ) | |
| 667 | si | |
| 668 | ||
| 669 | si | |
| 670 | si |