Skip to content
← Back

src/ir/values/coalesce_load.ghul

1
namespace IR.Values is
2
use Semantic.Types.Type;
3
4
// The result of a coalescing member access `a?.b` or method call
5
// `a?.m(...)`. The receiver is short-circuited to absent on null
6
// (reference-type optional) or HasValue=false (value-type optional
7
// via NULLABLE / MAYBE); otherwise the member value - a field or
8
// property load, or a whole call including argument evaluation -
9
// is produced against the unwrapped receiver.
10
//
11
// Built by COMPILE_ACCESS.build_coalesce_wrap. The construction
12
// wires sub-Values whose gen() composes the final IL inline - no
13
// `if let` tree-rewrite, no semantic-pass spilling.
14
//
15
// Reference-type receiver (T? where T is a class), ref-type
16
// member result:
17
// <recv IL> // [..., recv]
18
// dup // [..., recv, recv]
19
// brfalse <null> // [..., recv]
20
// <member_load IL> // [..., U] (member_load reads 'this' from stack)
21
// br <end>
22
// <null>:
23
// pop
24
// <null_value IL> // ldnull (ref) or default sentinel (value)
25
// <end>:
26
//
27
// Value-type receiver (NULLABLE[T] / MAYBE[T]):
28
// <recv address IL> // [..., &recv]
29
// dup // [..., &recv, &recv]
30
// <presence_test IL> // [..., &recv, bool] (call get_has_value on 'this' addr)
31
// brfalse <null> // [..., &recv]
32
// <value_extract IL> // [..., T] (call get_value on 'this' addr)
33
// <member_load IL> // [..., U]
34
// br <end>
35
// <null>:
36
// pop
37
// <null_value IL>
38
// <end>:
39
//
40
// The null_value is `ldnull` for a reference-type result and a
41
// fresh DEFAULT for a value-type result (DEFAULT hoists a
42
// `.locals init` and pushes the zero value). A void member value
43
// (a coalesced call to a void method) has no null_value - neither
44
// arm leaves a result on the stack.
45
//
46
// A static member reached through `?.` never consumes the tested
47
// receiver: `receiver_consumed` false makes the present arm pop
48
// the receiver (or its dup'd address) before the member value,
49
// and the value-type shape skips the value extract entirely.
50
class COALESCE_LOAD: Value is
51
receiver: Value;
52
presence_test: Value?;
53
value_extract: Value?;
54
member_load: Value;
55
null_value: Value?;
56
_result_type: Type;
57
_receiver_is_value_type: bool;
58
_receiver_consumed: bool;
59
60
type: Type => _result_type;
61
is_lightweight_pure: bool => false;
62
63
receiver_is_value_type: bool => _receiver_is_value_type;
64
65
// Reference-type receiver constructor. `member_load`'s
66
// receiver should be a STACK_TOP - gen() leaves the receiver
67
// on the stack ready for the access op.
68
init(
69
receiver: Value,
70
member_load: Value,
71
null_value: Value?,
72
result_type: Type,
73
receiver_consumed: bool
74
) is
75
super.init();
76
77
self.receiver = receiver;
78
self.member_load = member_load;
79
self.null_value = null_value;
80
self._result_type = result_type;
81
self._receiver_is_value_type = false;
82
self._receiver_consumed = receiver_consumed;
83
si
84
85
// Value-type receiver constructor. `receiver`'s address is
86
// pushed once, dup'd, and used by `presence_test` (HasValue)
87
// and `value_extract` (Value). Both receivers should consume
88
// the address from the stack - built with a STACK_TOP_ADDRESS
89
// standing in for 'this'. `value_extract` is null when the
90
// member value doesn't consume the receiver.
91
init(
92
receiver: Value,
93
presence_test: Value,
94
value_extract: Value?,
95
member_load: Value,
96
null_value: Value?,
97
result_type: Type,
98
receiver_consumed: bool
99
) is
100
super.init();
101
102
self.receiver = receiver;
103
self.presence_test = presence_test;
104
self.value_extract = value_extract;
105
self.member_load = member_load;
106
self.null_value = null_value;
107
self._result_type = result_type;
108
self._receiver_is_value_type = true;
109
self._receiver_consumed = receiver_consumed;
110
si
111
112
gen(context: IR.CONTEXT) is
113
let null_label = IR.LABEL();
114
let end_label = IR.LABEL();
115
116
if _receiver_is_value_type then
117
receiver.gen_address(context);
118
context.write_line("dup");
119
gen(presence_test!, context);
120
context.write_line("brfalse {null_label}");
121
122
if _receiver_consumed then
123
gen(value_extract!, context);
124
else
125
context.write_line("pop");
126
fi
127
else
128
gen(receiver, context);
129
context.write_line("dup");
130
context.write_line("brfalse {null_label}");
131
132
if !_receiver_consumed then
133
context.write_line("pop");
134
fi
135
fi
136
137
gen(member_load, context);
138
context.write_line("br {end_label}");
139
context.write_line("{null_label}:");
140
context.write_line("pop");
141
142
if null_value? then
143
gen(null_value!, context);
144
fi
145
146
context.write_line("{end_label}:");
147
si
148
149
to_string() -> string =>
150
"coalesce-load:[{type}]({receiver},{member_load})";
151
si
152
153
// A no-op Value that types as `T` and stands in for "the value
154
// (reference or struct) already on the IL stack". Embedded as
155
// the `from` of a `Load.INSTANCE_FIELD` / property-getter CALL
156
// when an outer composite (COALESCE_LOAD) has already pushed
157
// the receiver - letting the inner load emit only its trailing
158
// access op.
159
class STACK_TOP: Value is
160
_type: Type?;
161
162
type: Type? => _type;
163
is_value_type: bool => _type? /\ _type.is_value_type;
164
is_lightweight_pure: bool => true;
165
166
init(type: Type) is
167
super.init();
168
self._type = type;
169
si
170
171
gen(context: IR.CONTEXT) is
172
si
173
174
to_string() -> string =>
175
"stack-top:[{type}]";
176
si
177
178
// Like STACK_TOP but reports `has_address` - the IL stack
179
// already holds a managed pointer (e.g. the dup'd address of a
180
// value-type optional receiver). The struct-aware load paths
181
// gate on `has_address`, so an `INSTANCE_FIELD` / property
182
// built against this stands in for the implicit `ADDRESS` step
183
// and emits only its trailing op.
184
class STACK_TOP_ADDRESS: Value is
185
_type: Type?;
186
187
type: Type? => _type;
188
is_value_type: bool => _type? /\ _type.is_value_type;
189
has_address: bool => true;
190
is_lightweight_pure: bool => true;
191
192
init(type: Type) is
193
super.init();
194
self._type = type;
195
si
196
197
gen(context: IR.CONTEXT) is
198
si
199
200
gen_address(context: IR.CONTEXT) is
201
si
202
203
to_string() -> string =>
204
"stack-top-address:[{type}]";
205
si
206
si