Skip to content
← Back

src/semantic/symbols/state_machine_frame_base.ghul

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