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