Skip to content
← Back

src/syntax/trees/definitions/variables/variable.ghul

1
// FIXME: correct this namespace:
2
namespace Syntax.Trees.Variables is
3
4
use Source
5
6
use Ghul.Pipes
7
8
class VARIABLE: Trees.Definitions.Definition, ScopeCarrier is
9
scope: Semantic.Scope? public
10
11
left: VariableLeft
12
// Null on a destructure-pattern variable — only a SIMPLE_VARIABLE_LEFT
13
// has a single name. Callers that work only on simple variables can
14
// check `is_simple_name` first; callers that need to iterate every
15
// bound identifier should use `names`.
16
name: Identifiers.Identifier? => left.name
17
18
// every concrete VariableLeft subclass overrides names to non-null
19
names: Collections.Iterator[Identifiers.Identifier] => left.names!
20
21
type_expression: TypeExpressions.TypeExpression
22
initializer: Expressions.Expression?
23
is_static: bool
24
is_explicit_type: bool
25
is_variable: bool => true
26
want_dispose: bool
27
is_mutable_marked: bool
28
// Set on the binding of an `if let` arm so the compile pass can
29
// treat the initializer's optionality as already-checked: a
30
// refutable destructure unwraps `T?` to `T` before resolving
31
// element members.
32
is_refutable: bool
33
// Set on every formal-argument VARIABLE. A formal argument's
34
// initializer is its declared default value (consumed by
35
// callers), not code to run at function entry.
36
is_argument: bool
37
// Set on a VARIABLE that represents the `..` splice marker in
38
// a secondary `init` formal-argument list. Carries only its
39
// location; the rewrite pass expands it into the surrounding
40
// class's primary parameters before any downstream phase
41
// observes it.
42
is_splice: bool
43
44
// Set on the variables of a `let` that is a direct top-level
45
// statement of a file's synthesised entry point. Declare-members
46
// declares these into the file's namespace as top-level variables
47
// (static fields on the globals container) instead of as locals
48
// of the entry, so sibling global functions can read them.
49
is_top_level: bool public
50
51
// Set on a variant field VARIABLE inserted by expanding a `..`
52
// splice against the enclosing union's primary-constructor
53
// parameters. The variant's synthesised init forwards these
54
// names to super.init(...) and skips the self.<field>=
55
// assignment the union base already performs.
56
is_inherited_primary: bool
57
58
// The union's own member an inherited field is read through.
59
// It differs from the parameter's name where the header
60
// declares the parameter private, which captures it under an
61
// underscore.
62
inherited_member_name: string?
63
64
// Set on a VARIABLE synthesised as the backing field of an
65
// auto-property. It carries the property's own source location
66
// (so hover and go-to-definition on the backing field still work)
67
// rather than LOCATION.internal, so declare-members needs this to
68
// mark the resulting symbol synthesised.
69
is_synthesized: bool
70
71
// Populated only for primary-ctor parameters. Carries the
72
// trailing modifier suffixes (`public` / `field` / `init`) that
73
// describe the auto-generated body field/property the rewrite
74
// pass will synthesise for this parameter. Null in every other
75
// variable-parsing context.
76
modifiers: Modifiers.LIST? public
77
78
// Attribute pragmas (`@Foo() name: T`) written directly before a
79
// formal-argument VARIABLE — only ever populated when the parser
80
// is inside a function/method parameter list (see
81
// CONTEXT.in_formal_arguments); null everywhere else.
82
pragmas: Collections.LIST[Pragmas.PRAGMA]? public
83
84
init(
85
location: LOCATION,
86
left: VariableLeft,
87
type_expression: TypeExpressions.TypeExpression,
88
is_static: bool,
89
is_explicit_type: bool,
90
initializer: Expressions.Expression?
91
) is
92
super.init(location)
93
94
95
self.left = left
96
self.type_expression = type_expression
97
self.is_static = is_static
98
self.is_explicit_type = is_explicit_type
99
self.initializer = initializer
100
si
101
102
init(
103
location: LOCATION,
104
name: Identifiers.Identifier,
105
type_expression: TypeExpressions.TypeExpression,
106
is_static: bool,
107
is_explicit_type: bool,
108
initializer: Expressions.Expression?
109
) is
110
init(
111
location,
112
SIMPLE_VARIABLE_LEFT(location, name),
113
type_expression,
114
is_static,
115
is_explicit_type,
116
initializer
117
)
118
si
119
120
set_type_expression(type_expression: TypeExpressions.TypeExpression) is
121
self.type_expression = type_expression
122
si
123
124
mark_want_dispose() is
125
want_dispose = true
126
si
127
128
mark_mutable() is
129
is_mutable_marked = true
130
si
131
132
mark_refutable() is
133
is_refutable = true
134
si
135
136
mark_argument() is
137
is_argument = true
138
si
139
140
mark_splice() is
141
is_splice = true
142
si
143
144
mark_inherited_primary(member_name: string) is
145
is_inherited_primary = true
146
inherited_member_name = member_name
147
si
148
149
mark_synthesized() is
150
is_synthesized = true
151
si
152
153
set_modifiers(modifiers: Modifiers.LIST) is
154
self.modifiers = modifiers
155
si
156
157
set_pragmas(pragmas: Collections.LIST[Pragmas.PRAGMA]) is
158
self.pragmas = pragmas
159
si
160
161
copy() -> VARIABLE is
162
assert initializer == null else "cannot copy a variable node with non null initializer"
163
164
let result =
165
VARIABLE(
166
location,
167
left.copy(),
168
type_expression.copy(),
169
is_static,
170
is_explicit_type,
171
null
172
)
173
174
if let self_pragmas = pragmas then
175
result.set_pragmas(self_pragmas)
176
fi
177
178
return result
179
si
180
181
accept(visitor: Visitor) is
182
visitor.visit(self)
183
si
184
185
walk(visitor: Visitor) is
186
// Inherited-primary entries in a variant's field list
187
// exist purely to feed init synthesis (the variant's
188
// init forwards them to super.init). The union base
189
// owns the storage and visits all the symbol-side
190
// processing (declare_symbols, resolve types, etc.)
191
// through the union's own field declarations. Walking
192
// them at the variant level would double-process the
193
// same name and trip "set type twice" / shadow checks.
194
if is_inherited_primary then
195
return
196
fi
197
if !visitor.pre(self) then
198
if pragmas? then
199
for pragma in pragmas do
200
pragma.walk(visitor)
201
od
202
fi
203
left.walk(visitor)
204
type_expression.walk(visitor)
205
if initializer? then
206
initializer.walk(visitor)
207
fi
208
fi
209
accept(visitor)
210
si
211
si
212
213
class VariableLeft: Trees.Node is
214
is_simple_name: bool => false
215
name: Identifiers.Identifier? => null
216
names: Collections.Iterator[Identifiers.Identifier]? => null
217
elements: Collections.List[VariableLeft]? => null
218
219
// Per-element type ascription. Allowed on any element of a
220
// destructure pattern, at any nesting depth. In an ordinary
221
// `let`, this is a static type assertion on the bound slot;
222
// in an `if let` arm (parent VARIABLE.is_refutable), it is
223
// additionally a runtime narrowing test on that element.
224
type_expression: TypeExpressions.TypeExpression?
225
226
// Set on every element of a by-name destructure group:
227
// `(local = field, …) = source` records `field` here so the
228
// resolver pulls `source.field` rather than the positional
229
// slot. Null on positional-group elements. The parser
230
// enforces all-or-nothing per `(...)` group; mixing is a
231
// parse error.
232
source_field_name: Identifiers.Identifier? public
233
234
set_source_field_name(name: Identifiers.Identifier) is
235
self.source_field_name = name
236
si
237
238
// Compile-expressions output for this node lives in the pass's
239
// own VARIABLE_LEFT_STATE_STORE, keyed by this node, not here.
240
241
// Propagated from the parent VARIABLE for `if let` arms.
242
is_refutable: bool public
243
244
// Propagated from the parent VARIABLE when it's a formal
245
// argument. A destructured formal argument has no initializer
246
// to derive a value from - the leaves' types are assigned
247
// directly from the parameter's aggregate type by resolve-
248
// explicit-types, and generate-il sources the unpack from the
249
// synthesised parameter symbol rather than a walked value - so
250
// compile-expressions' usual initializer-driven handling of a
251
// destructure pattern doesn't apply and skips this subtree.
252
is_argument_left: bool public
253
254
init(location: LOCATION) is
255
super.init(location)
256
si
257
258
set_type_expression(type_expression: TypeExpressions.TypeExpression) is
259
self.type_expression = type_expression
260
si
261
262
// Propagate `is_refutable` to every leaf of a destructure tree
263
// so a `LITERAL_VARIABLE_LEFT` nested inside a tuple destructure
264
// (`if let (1, y) = pair`) knows it sits in a refutable
265
// context and is not a silent no-op.
266
mark_refutable_recursive() is
267
is_refutable = true
268
si
269
270
// Propagate `is_argument_left` to every leaf, same shape as
271
// mark_refutable_recursive above.
272
mark_argument_recursive() is
273
is_argument_left = true
274
si
275
276
// True when this leaf's own `: T` ascription (if any) performs
277
// a runtime test that can genuinely fail — a reference-type
278
// narrow (an isinst test), or a value-type ascription that is
279
// itself option-shaped (`T?` / NULLABLE[T], tested via
280
// presence). A non-optional value-type ascription (`i: int`)
281
// can never fail, so it contributes no refutability — the
282
// same reasoning `PATTERN_CHECKER.should_emit_value_type_narrow_error`
283
// applies to the top-level ascription.
284
_ascription_is_refutable: bool is
285
let ascribed = type_expression?.type
286
287
if !ascribed? then
288
return false
289
fi
290
291
if !ascribed.is_value_type then
292
return true
293
fi
294
295
return ascribed.find_member("has_value")? /\ ascribed.find_member("value")?
296
si
297
298
// True when this leaf, or any leaf nested inside it, carries
299
// its own runtime refutability — a literal/null/enum-member
300
// leaf, or a genuinely-refutable `: T` ascription — independent
301
// of whether the top-level scrutinee itself is optional. An
302
// `if let` pattern needs at least one of these somewhere in
303
// its shape, or the arm can never fail to match.
304
// DESTRUCTURING_VARIABLE_LEFT additionally recurses into its
305
// elements.
306
has_intrinsic_refutability: bool => _ascription_is_refutable
307
308
// True when this node, or any nested destructure group at any
309
// depth, is a by-name group (source_field_name set on its own
310
// first element - the parser enforces all-or-nothing per
311
// group, so checking the first element is enough for that
312
// group). Used to reject named destructuring anywhere in a
313
// formal-argument pattern, not just at its outermost level.
314
has_named_group: bool => false
315
316
get_names_into(into: Collections.MutableList[Identifiers.Identifier])
317
318
copy() -> VariableLeft
319
320
copy_base_values_from(other: VariableLeft) is
321
if let other_type_expression = other.type_expression then
322
type_expression = other_type_expression.copy()
323
fi
324
325
if let other_source_field_name = other.source_field_name then
326
source_field_name = other_source_field_name.copy()
327
fi
328
si
329
si
330
331
class SIMPLE_VARIABLE_LEFT: VariableLeft is
332
name: Identifiers.Identifier
333
is_simple_name: bool => true
334
335
elements: Collections.List[VariableLeft] => System.Array.empty`[VariableLeft]()
336
names: Collections.Iterator[Identifiers.Identifier] => [name].iterator
337
338
init(
339
location: LOCATION,
340
name: Identifiers.Identifier
341
) is
342
super.init(location)
343
344
self.name = name
345
si
346
347
get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is
348
into.add(name)
349
si
350
351
accept(visitor: Visitor) is
352
visitor.visit(self)
353
si
354
355
walk(visitor: Visitor) is
356
if !visitor.pre(self) then
357
name.walk(visitor)
358
359
if let self.type_expression? then
360
type_expression.walk(visitor)
361
fi
362
fi
363
364
accept(visitor)
365
si
366
367
copy() -> VariableLeft is
368
let result = SIMPLE_VARIABLE_LEFT(location, name)
369
result.copy_base_values_from(self)
370
return result
371
si
372
si
373
374
// A literal / `null` / enum-member leaf inside a destructure
375
// pattern. Has no binding name — at runtime, the source's
376
// corresponding element is value-equality tested against the
377
// wrapped expression, and the arm only matches when the test
378
// succeeds. Visible only on the LHS of refutable bindings
379
// (`if let` / `case`-when patterns); a plain `let` never has
380
// a refutable element.
381
class LITERAL_VARIABLE_LEFT: VariableLeft is
382
expression: Expressions.Expression
383
384
// Whether the source wrote the `~` match marker. It changes
385
// nothing about what the leaf does — a bare name only reaches
386
// this node by being marked, and every other leaf form matches
387
// with or without it — but the formatter has to write back
388
// what it read.
389
is_marked: bool
390
391
is_simple_name: bool => false
392
elements: Collections.List[VariableLeft] => System.Array.empty`[VariableLeft]()
393
names: Collections.Iterator[Identifiers.Identifier] => System.Array.empty`[Identifiers.Identifier]().iterator
394
395
// A literal leaf is itself a runtime equality test, so it
396
// always provides refutability regardless of any ascription.
397
has_intrinsic_refutability: bool => true
398
399
init(location: LOCATION, expression: Expressions.Expression, is_marked: bool) is
400
super.init(location)
401
402
self.expression = expression
403
self.is_marked = is_marked
404
si
405
406
get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is
407
si
408
409
accept(visitor: Visitor) is
410
visitor.visit(self)
411
si
412
413
walk(visitor: Visitor) is
414
if !visitor.pre(self) then
415
expression.walk(visitor)
416
fi
417
418
accept(visitor)
419
si
420
421
copy() -> VariableLeft is
422
let result = LITERAL_VARIABLE_LEFT(location, expression, is_marked)
423
result.copy_base_values_from(self)
424
return result
425
si
426
si
427
428
class DESTRUCTURING_VARIABLE_LEFT: VariableLeft is
429
elements: Collections.List[VariableLeft]
430
431
names: Collections.Iterator[Identifiers.Identifier] => _flatten_names()
432
433
_flatten_names() -> Ghul.Pipes.Pipe[Identifiers.Identifier] is
434
for element in elements do
435
let element_names = element.names
436
437
if element_names? then
438
for n in element_names do
439
yield n
440
od
441
fi
442
od
443
si
444
445
get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is
446
for e in elements do
447
e.get_names_into(into)
448
od
449
si
450
451
mark_refutable_recursive() is
452
is_refutable = true
453
for e in elements do
454
e.mark_refutable_recursive()
455
od
456
si
457
458
// Own ascription (a nested group's `: T`), if it is itself
459
// refutable, or any element's, at any depth.
460
has_intrinsic_refutability: bool =>
461
_ascription_is_refutable \/ (elements |> any(e => e.has_intrinsic_refutability))
462
463
mark_argument_recursive() is
464
is_argument_left = true
465
for e in elements do
466
e.mark_argument_recursive()
467
od
468
si
469
470
has_named_group: bool =>
471
(elements.count > 0 /\ elements[0].source_field_name?) \/
472
elements |> any(e => e.has_named_group)
473
474
init(location: LOCATION, elements: Collections.List[VariableLeft]) is
475
super.init(location)
476
477
478
self.elements = elements
479
si
480
481
accept(visitor: Visitor) is
482
visitor.visit(self)
483
si
484
485
walk(visitor: Visitor) is
486
if !visitor.pre(self) then
487
for e in elements do
488
e.walk(visitor)
489
od
490
491
if let self.type_expression? then
492
type_expression.walk(visitor)
493
fi
494
fi
495
accept(visitor)
496
si
497
498
copy() -> VariableLeft is
499
let new_elements = Collections.LIST[VariableLeft](elements.count)
500
501
for e in elements do
502
new_elements.add(e.copy())
503
od
504
505
let result = DESTRUCTURING_VARIABLE_LEFT(location, new_elements)
506
result.copy_base_values_from(self)
507
return result
508
si
509
si
510
si