Appearance
| 1 | namespace Semantic.Symbols is | |
| 2 | use IO.Std; | |
| 3 | ||
| 4 | use System.Text.StringBuilder; | |
| 5 | ||
| 6 | use IoC; | |
| 7 | use Source; | |
| 8 | ||
| 9 | use Types.Type; | |
| 10 | ||
| 11 | use Ghul.Pipes; | |
| 12 | ||
| 13 | // Shared frame-class machinery for both generator and async | |
| 14 | // state machines: type-parameter capture + substitution, lazily- | |
| 15 | // allocated locals/anonymous fields, and the outer-self-type | |
| 16 | // helper. Subclasses add their protocol-specific fields and | |
| 17 | // ancestors. | |
| 18 | class STATE_MACHINE_FRAME_BASE: Classy is | |
| 19 | _owning_function: Function; | |
| 20 | _local_fields: Collections.LIST[Field]; | |
| 21 | _next_local_id: int; | |
| 22 | _function_to_class_type_map: Collections.MAP[string, Type]; | |
| 23 | ||
| 24 | owning_function: Function => _owning_function; | |
| 25 | local_fields: Collections.Iterable[Field] => _local_fields; | |
| 26 | function_to_class_type_map: Collections.Map[string, Type] => _function_to_class_type_map; | |
| 27 | ||
| 28 | init( | |
| 29 | location: LOCATION, | |
| 30 | span: LOCATION, | |
| 31 | owner_owner: Scope, | |
| 32 | name: string, | |
| 33 | owner: Scope, | |
| 34 | owning_function: Function | |
| 35 | ) is | |
| 36 | super.init( | |
| 37 | location, | |
| 38 | span, | |
| 39 | owner_owner, | |
| 40 | name, | |
| 41 | System.Array.empty[string](), | |
| 42 | owner | |
| 43 | ); | |
| 44 | ||
| 45 | _owning_function = owning_function; | |
| 46 | _local_fields = Collections.LIST[Field](); | |
| 47 | _function_to_class_type_map = Collections.MAP[string, Type](); | |
| 48 | si | |
| 49 | ||
| 50 | // Declare class-level type parameters mirroring those visible | |
| 51 | // inside the owning function: enclosing class's first, then | |
| 52 | // function's own. Order must match | |
| 53 | // STATE_MACHINE_TYPE_PARAMS.walk_install / | |
| 54 | // construction_type_arguments so `!N` indices line up at IL. | |
| 55 | declare_captured_type_params(listener: SymbolDefinitionListener) -> Collections.LIST[string] is | |
| 56 | let captured_names = Collections.LIST[string](); | |
| 57 | let i mut = 0; | |
| 58 | ||
| 59 | if let owner_classy: Classy = _owning_function.owner /\ owner_classy.is_generic then | |
| 60 | for name in owner_classy.argument_names do | |
| 61 | let class_type_arg = declare_type(LOCATION.internal, name, i, listener); | |
| 62 | _function_to_class_type_map[name] = cast Types.Typed?(class_type_arg)!.type!; | |
| 63 | captured_names.add(name); | |
| 64 | i = i + 1; | |
| 65 | od | |
| 66 | fi | |
| 67 | ||
| 68 | if _owning_function.is_generic then | |
| 69 | for name in _owning_function.generic_argument_names do | |
| 70 | let class_type_arg = declare_type(LOCATION.internal, name, i, listener); | |
| 71 | _function_to_class_type_map[name] = cast Types.Typed?(class_type_arg)!.type!; | |
| 72 | captured_names.add(name); | |
| 73 | i = i + 1; | |
| 74 | od | |
| 75 | fi | |
| 76 | ||
| 77 | if captured_names.count > 0 then | |
| 78 | argument_names = captured_names; | |
| 79 | fi | |
| 80 | ||
| 81 | return captured_names; | |
| 82 | si | |
| 83 | ||
| 84 | // Type for the `$outer_self` field + ctor argument, taking each | |
| 85 | // of a generic outer class's arguments from the frame's mirrored | |
| 86 | // class-T's. | |
| 87 | outer_self_type(outer_classy: Classy) -> Type => | |
| 88 | SELF_CAPTURE_TYPE((classy, name) => _mirrored_class_type(name)).of(outer_classy) ?? outer_classy.type!; | |
| 89 | ||
| 90 | _mirrored_class_type(name: string) -> Type? => | |
| 91 | if _function_to_class_type_map.contains_key(name) then | |
| 92 | _function_to_class_type_map[name] | |
| 93 | else | |
| 94 | null | |
| 95 | fi; | |
| 96 | ||
| 97 | // Frame field for a body local. Idempotent — same Variable | |
| 98 | // gets the same field — but refreshes the field's type on | |
| 99 | // every call so iterative-inference narrowing of | |
| 100 | // `local.storage_type` flows into the IL signature. | |
| 101 | declare_local_field(local: Variable) -> Field is | |
| 102 | let existing = local.state_machine_field; | |
| 103 | ||
| 104 | if existing? then | |
| 105 | existing.set_type(_class_relative(local.storage_type!)); | |
| 106 | return existing; | |
| 107 | fi | |
| 108 | ||
| 109 | let id = _next_local_id; | |
| 110 | _next_local_id = _next_local_id + 1; | |
| 111 | ||
| 112 | let listener = IoC.CONTAINER.instance.symbol_definition_locations; | |
| 113 | ||
| 114 | let `field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "$local_{local.name}_{id}"); | |
| 115 | `field.set_type(_class_relative(local.storage_type!)); | |
| 116 | declare(LOCATION.internal, `field, listener); | |
| 117 | ||
| 118 | _local_fields.add(`field); | |
| 119 | ||
| 120 | local.state_machine_field = `field; | |
| 121 | ||
| 122 | return `field; | |
| 123 | si | |
| 124 | ||
| 125 | // Anonymous frame field for a synthesised value with no | |
| 126 | // user-named source symbol (the FOR iterator, async spill | |
| 127 | // slots). Each call produces a fresh field. | |
| 128 | declare_anonymous_field(prefix: string, type: Type) -> Field is | |
| 129 | let id = _next_local_id; | |
| 130 | _next_local_id = _next_local_id + 1; | |
| 131 | ||
| 132 | let listener = IoC.CONTAINER.instance.symbol_definition_locations; | |
| 133 | ||
| 134 | let `field = Symbols.INSTANCE_FIELD(LOCATION.internal, self, "${prefix}_{id}"); | |
| 135 | `field.set_type(_class_relative(type)); | |
| 136 | declare(LOCATION.internal, `field, listener); | |
| 137 | ||
| 138 | _local_fields.add(`field); | |
| 139 | ||
| 140 | return `field; | |
| 141 | si | |
| 142 | ||
| 143 | // Bump the shared counter without creating a field — for | |
| 144 | // subclasses that name fields outside the standard prefixes | |
| 145 | // (e.g. async's `$awaiter_N`). | |
| 146 | next_local_id() -> int is | |
| 147 | let id = _next_local_id; | |
| 148 | _next_local_id = _next_local_id + 1; | |
| 149 | return id; | |
| 150 | si | |
| 151 | ||
| 152 | // Mirrors Classy.FRAME.set_type_arguments — base-level Classy | |
| 153 | // doesn't define one. | |
| 154 | populate_argument_names(arguments: Collections.Iterable[Symbol]) is | |
| 155 | argument_names = arguments |> map(a => a.name) |> collect(); | |
| 156 | si | |
| 157 | ||
| 158 | // Override Classy's default declare_type (which errors out) | |
| 159 | // so generic state machines can install their type | |
| 160 | // parameters, matching the CLASS shape. | |
| 161 | declare_type(location: LOCATION, name: string, index: int, symbol_definition_listener: SymbolDefinitionListener?) -> Symbol is | |
| 162 | let result = Symbols.CLASSY_GENERIC_ARGUMENT(location, self, name, index); | |
| 163 | declare(location, result, symbol_definition_listener); | |
| 164 | return result; | |
| 165 | si | |
| 166 | ||
| 167 | // Rewrite function-T references in `t` to the parallel | |
| 168 | // CLASSY_GENERIC_ARGUMENT declared on this frame, so types | |
| 169 | // emit `!N` (class-level) rather than `!!N` (method-level). | |
| 170 | // No-op when the owning function is non-generic. | |
| 171 | _class_relative(t: Type) -> Type is | |
| 172 | if _function_to_class_type_map.count == 0 then | |
| 173 | return t; | |
| 174 | fi | |
| 175 | ||
| 176 | return t.specialize(_function_to_class_type_map); | |
| 177 | si | |
| 178 | ||
| 179 | gen_access(buffer: StringBuilder) is | |
| 180 | buffer.append("private "); | |
| 181 | si | |
| 182 | ||
| 183 | gen_type_prefix(buffer: StringBuilder) is | |
| 184 | buffer.append("class "); | |
| 185 | si | |
| 186 | si | |
| 187 | si |