Skip to content
← Back

src/semantic/symbols/variable.ghul

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 Types.Type
11
12
use IR.Values.Value
13
use IR.Values.DUMMY
14
15
class Variable: Symbol, Types.SettableTyped abstract is
16
type: Type?
17
set_type(value: Type) is type = value; si
18
19
short_description: string => "{name}: {if type? then type.short_description else "?" fi}"
20
21
// Concrete Variable / Field describe overrides delegate
22
// straight through to Symbol._describe_typed with the live
23
// declared type — the dual `declared → narrowed` rule lives
24
// there, shared with Property so a narrowed member access
25
// hovers the same shape as a narrowed local.
26
27
symbol_kind: SymbolKind => SymbolKind.VARIABLE
28
completion_kind: CompletionKind => CompletionKind.VARIABLE
29
30
is_defined: bool public
31
is_variable: bool => true
32
is_assigned: bool public
33
is_reassigned: bool public
34
is_mutable_marked: bool public
35
is_captured: bool public
36
is_disposed: bool public
37
38
// Set by the `mark-boxed-locals` analysis pass when a
39
// local is both captured by a closure and reassigned.
40
// Storage becomes `Ghul.BOX[type]`; reads/writes
41
// dispatch through the box's `.value` field; the closure
42
// body and the enclosing scope share one heap cell. A
43
// flag rather than a subclass so symbol identity stays
44
// stable across the analysis-pass marking — IDE caches
45
// (`SYMBOL_DEFINITION_LOCATIONS`, `SymbolUseListener`)
46
// hold direct pointers from declare-symbols time and
47
// mustn't be invalidated. See
48
// `docs/claude/boxed-captured-mutables.md`.
49
is_boxed: bool public
50
51
// Set by `mark-boxed-locals` when the local is assigned
52
// from inside a nested function literal. Such a local
53
// shares one heap cell with the closure body, so invoking
54
// the closure rewrites it behind the enclosing scope's
55
// back, and flow narrowing forms no facts on it. A local
56
// the enclosing scope alone assigns is not marked: those
57
// writes are on the path the narrowing walk already sees.
58
//
59
// Derived on every build, unlike `is_boxed`, which asks the
60
// same question of the *storage strategy* and is only worth
61
// answering once the IR is lowered to IL.
62
is_closure_assigned: bool public
63
64
// When this variable lives on the synthesised state-machine
65
// frame of a generator function, this points at the matching
66
// `Field` on that class. Load/Store IR for the variable then
67
// routes through `ldarg.0; ldfld/stfld <state_machine_field>`
68
// instead of `ldarg`/`ldloc`/`starg`/`stloc`. Populated by
69
// STATE_MACHINE_FRAME for parameters at declare-time and
70
// for body locals by generate_il during the body walk. Null
71
// for variables in plain (non-generator) functions.
72
state_machine_field: Field? public
73
74
// The actual IL slot type for this Variable. Defaults to
75
// `type`; switches to `Ghul.BOX[type]` when `is_boxed`.
76
// Queried at IL emission time and by
77
// `closure.find_or_add_capture` when declaring the
78
// frame field. User-visible queries (HOVER, completion,
79
// type-checking) continue to use `type` directly so the
80
// box is invisible above IL level.
81
storage_type: Type? =>
82
if is_boxed /\ type? then
83
IoC.CONTAINER.instance.innate_symbol_lookup.get_box_type(type!)
84
else
85
type
86
fi
87
88
// While `type` is an INFERRED_VARIABLE_TYPE placeholder,
89
// a Variable holds constraints in three shapes — all
90
// accumulated by the body-retry walk and consulted by
91
// `try_get_inferred_type` when collapsing to a resolved
92
// type.
93
//
94
// `_lub_map` — lower-bound type candidates. Records
95
// "this placeholder IS-A T" from sites that produce a
96
// concrete value typed T to be held in the placeholder
97
// (assignment RHS `v = expr`, lambda call-site actual
98
// passed to a placeholder formal). The LUB-map collapses
99
// these to a single widest-needed candidate type — the
100
// classical least-upper-bound operation.
101
//
102
// `_upper_bounds` — upper-bound type candidates. Records
103
// "this placeholder MUST FIT INTO T" from sites that
104
// consume the placeholder via a concretely-typed slot
105
// (passing the placeholder as an argument to `g(a: T)`).
106
// These are validated against the LUB candidate but
107
// never widen it. Distinct from `_lub_map` so that an
108
// upper-bound `object` (e.g. from `g(v: object)`) does
109
// not dominate the LUB and force v→object when an
110
// assignment said v=bool.
111
//
112
// `_constraints` — operation/structural constraints.
113
// Records "this placeholder is consumed by `.foo`" /
114
// "this placeholder is called with (int)" etc. from
115
// sites that exercise the placeholder without
116
// producing a candidate type. The LUB candidate is
117
// accepted only if every accumulated constraint
118
// discharges against it.
119
//
120
// All three are lazily constructed; null when nothing
121
// has been recorded yet.
122
123
_lub_map: Semantic.LEAST_UPPER_BOUND_MAP?
124
125
// A `null` lower bound names no type of its own: it says only
126
// that the type the other bounds settle on must hold absence.
127
_seen_null: bool
128
129
has_seen_null: bool => _seen_null
130
131
// Set for a `mut` local with an initializer: its type is the join
132
// of the initializer and every value assigned to it, so assignments
133
// keep recording lower bounds after the type first settles.
134
joins_assignments: bool public
135
136
lower_bounds: Collections.List[Type] =>
137
if _lub_map? then _lub_map.types else Collections.LIST[Type]() fi
138
139
// True when the type-bound LUB has at least one
140
// candidate. Callers gate speculative match propagation (the
141
// call-site-synthesized function-type-shape constraint
142
// used by mutual-recursion inference) on this to avoid
143
// polluting the LUB with a synthesised shape when an
144
// explicit assignment already supplied a candidate —
145
// the synthesised arg types come from the call site and
146
// may not match the assigned shape, leaving the per-
147
// position merge unable to fold the two entries.
148
has_lub_candidate: bool => _lub_map? /\ _lub_map.types.count > 0
149
150
// Linear list with `matches`-based dedup. A SET would need
151
// Type to override `equals`/`get_hash_code` (it doesn't, and
152
// `matches` isn't an equivalence relation so couldn't back a
153
// SET anyway). In practice this list is tiny (0-2 entries) so
154
// a linear contains-check is cheap.
155
_upper_bounds: Collections.LIST[Type]?
156
157
// Stored as a SET to deduplicate constraints emitted
158
// from multiple equivalent operation sites (e.g. two
159
// `.foo` accesses on the same placeholder collapse to a
160
// single MEMBER_CONSTRAINT("foo")). Constraint subclasses
161
// override `equals` and `get_hash_code` to make that work.
162
_constraints: Collections.SET[Semantic.Constraint]?
163
164
init(location: LOCATION, owner: Scope, name: string) is
165
super.init(location, owner, name)
166
si
167
168
// Records a lower-bound type candidate ("placeholder IS-A
169
// this type") into `_lub_map`. Returns true if the bound
170
// was actually added (i.e. the symbol is unresolved AND
171
// the bound carries information). Callers use this to
172
// signal progress to the retry loop via
173
// _logger.mark_consumed_any so the body retry kicks in
174
// even for cases where the body walk itself didn't fire
175
// mark_consumed_any (e.g. member access on placeholder
176
// receiver poisons silently to ERROR without consuming
177
// the receiver).
178
add_lower_bound(bound: Type?) -> bool is
179
// Skip only once the symbol's type is fully settled
180
// (no placeholders, no ERROR). Provisional composites
181
// — e.g. a `Func[List[int], INFERRED_RETURN_TYPE]`
182
// recorded on an early iter before the lambda's return
183
// resolved — still need refining; without further LUB
184
// entries the per-position merge has nothing to fold
185
// the resolved arity into. The earlier `!is_sentinel`
186
// gate stopped accepting refinement constraints in
187
// exactly the case where they were most needed (and
188
// produced the survey §4.24 / fuzz finding 04 IL
189
// placeholder leak).
190
if type? /\ type.is_settled /\ !joins_assignments then
191
return false
192
fi
193
194
if !bound? \/ bound.is_sentinel then
195
return false
196
fi
197
198
// A composite with ERROR inside says nothing about the element
199
// that failed, and a lower bound is never withdrawn, so it would
200
// outlive the walk that produced it and spoil the join.
201
if Types.ERROR_ELEMENT.within(bound) then
202
return false
203
fi
204
205
// A local that joins its assignments sees the same bounds on
206
// every walk; only one it has not seen is progress.
207
if joins_assignments /\ _lub_map? then
208
for existing in _lub_map.types do
209
if existing.is_equivalent_to(bound) then
210
return false
211
fi
212
od
213
fi
214
215
if bound.is_null then
216
if bound.is_error \/ _seen_null then
217
return false
218
fi
219
220
_seen_null = true
221
return true
222
fi
223
224
if !_lub_map? then
225
_lub_map = Semantic.LEAST_UPPER_BOUND_MAP()
226
fi
227
228
_lub_map.add(bound)
229
return true
230
si
231
232
// Re-entrancy latch for try_get_inferred_type. Resolving a
233
// candidate composite recurses into the origins of any
234
// placeholders it carries; two unresolved variables whose
235
// candidates reference each other would recurse forever.
236
// Answering null on re-entry treats the cycle as
237
// still-unresolved, which is what it is.
238
_resolving_inferred_type: bool
239
240
try_get_inferred_type() -> Type? is
241
if _resolving_inferred_type then
242
return null
243
fi
244
245
_resolving_inferred_type = true
246
let result = _try_get_inferred_type()
247
_resolving_inferred_type = false
248
249
return result
250
si
251
252
_try_get_inferred_type() -> Type? is
253
let candidate: Type? mut = null
254
255
if _lub_map? then
256
candidate = _lub_map.get_result()
257
fi
258
259
// No lower-bound candidate — fall back to upper bounds.
260
// Single upper bound: return it (preserves the bare
261
// "only signal is g(v: object)" case as v->object).
262
// Multiple upper bounds: keep deferred — no general
263
// narrowest-of-uppers heuristic yet. Returning null
264
// here leaves the placeholder unresolved and lets the
265
// existing "cannot infer" diagnostic surface if no
266
// further info appears.
267
if !candidate? then
268
// A bound over another function's type parameter names
269
// no type the placeholder could take, so it is left to
270
// the lower bounds a later walk records. A type parameter
271
// of the function this variable is declared in is a
272
// definite type here.
273
let uppers = _upper_bounds
274
275
if uppers? /\ uppers.count == 1 /\ !uppers[0].has_function_generic_argument_foreign_to(owner) then
276
candidate = uppers[0]
277
else
278
return null
279
fi
280
fi
281
282
// The candidate may be a composite that captured other
283
// variables' placeholders before their origins settled
284
// (a lambda type recorded at a call site, a tuple LUB
285
// entry). Nothing rewrites the stored composite when
286
// those origins settle, so collapse the settled slots
287
// here - the chokepoint every consumer reads through -
288
// rather than letting the stale placeholder propagate
289
// into committed types and eventually IL.
290
candidate = SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(candidate)
291
292
if _seen_null /\ !candidate.is_optional then
293
if candidate.is_type_variable then
294
return null
295
elif candidate.is_value_type then
296
candidate = IoC.CONTAINER.instance.innate_symbol_lookup.get_optional_type(candidate)
297
else
298
candidate = candidate.as_optional()
299
fi
300
fi
301
302
// Upper-bound validation: the chosen candidate must
303
// be assignable to every recorded upper bound. If
304
// not, the placeholder is being asked to be both
305
// wider (assignment / lower bound) and narrower
306
// (passed to a too-narrow slot). Return null so the
307
// placeholder stays unresolved; downstream
308
// assignability errors surface at the offending sites.
309
if _upper_bounds? then
310
for upper in _upper_bounds do
311
// A bound over a function's type parameter - the
312
// `Iterable[T]` of a `count[T]` the placeholder was
313
// passed to - accepts whatever that parameter binds
314
// to, and no candidate is assignable to the unbound
315
// parameter itself, so it cannot reject a settled
316
// candidate. A candidate still holding placeholders
317
// stays held back, for a later walk to settle.
318
if upper.contains_function_generic_argument /\ candidate.is_settled then
319
continue
320
fi
321
322
if !upper.is_assignable_from(candidate) then
323
return null
324
fi
325
od
326
fi
327
328
// Operation-side filter: the candidate is only
329
// valid if every accumulated constraint discharges
330
// against it. A rejection here means the LUB picked
331
// a type that doesn't expose an operation the user's
332
// code performs on the placeholder — return null so
333
// the placeholder stays unresolved and the retry
334
// loop has another iteration to accumulate more
335
// information. If no candidate ever discharges, the
336
// slot stays unresolved and the non-convergence sweep
337
// reports it as cannot infer type here rather than
338
// silently producing bad IL.
339
if _constraints? then
340
for c in _constraints do
341
if !c.try_discharge(candidate) then
342
return null
343
fi
344
od
345
fi
346
347
return candidate
348
si
349
350
// Record an upper bound on this placeholder's eventual
351
// type. The chosen LUB candidate (from lower bounds)
352
// must be assignable to every upper bound to be accepted
353
// by `try_get_inferred_type`.
354
//
355
// Returns true iff the bound carries new information —
356
// wasn't already recorded by `matches`. The
357
// retry loop uses this signal via `mark_consumed_any`.
358
//
359
// Skipped when the placeholder already has a concrete
360
// resolved type (constraints accumulate only while
361
// unresolved) and when the bound is itself a sentinel
362
// or type variable (no information).
363
add_upper_bound(bound: Type?) -> bool is
364
if type? /\ !type.is_sentinel then
365
return false
366
fi
367
368
if !bound? \/ bound.is_sentinel \/ bound.is_type_variable then
369
return false
370
fi
371
372
let upper_bounds mut = _upper_bounds
373
374
if !upper_bounds? then
375
upper_bounds = Collections.LIST[Type]()
376
_upper_bounds = upper_bounds
377
else
378
for existing in upper_bounds do
379
if existing.matches(bound) then
380
return false
381
fi
382
od
383
fi
384
385
upper_bounds.add(bound)
386
return true
387
si
388
389
// Record an operation/structural constraint against this
390
// placeholder origin. Returns true iff the constraint
391
// carries information that wasn't already recorded — i.e.
392
// wasn't already in the set keyed on its `equals` /
393
// `get_hash_code`. The retry loop uses this signal to
394
// drive `_logger.mark_consumed_any`.
395
//
396
// Like `add_lower_bound`, skipped only when the symbol's
397
// type is fully settled — provisional composites are
398
// still legitimately refining and should keep accumulating
399
// operation evidence too.
400
add_constraint(constraint: Semantic.Constraint?) -> bool is
401
if type? /\ type.is_settled then
402
return false
403
fi
404
405
if !constraint? then
406
return false
407
fi
408
409
if !_constraints? then
410
_constraints = Collections.SET[Semantic.Constraint]()
411
elif _constraints.contains(constraint) then
412
return false
413
fi
414
415
_constraints.add(constraint)
416
return true
417
si
418
419
specialize(type_map: Collections.Map[Symbol,Type], owner: GENERIC) -> Symbol is
420
let result = cast Variable?(memberwise_clone())!
421
422
result.specialized_from = self
423
424
if type? then
425
let specialized_type = type.specialize(type_map)
426
427
result.type = specialized_type
428
fi
429
430
result.owner = owner
431
432
return result
433
si
434
si
435
436
// Minimal Variable subclass used as the origin symbol for an
437
// unbound owner type-arg placeholder at a constructor expression.
438
// Carries the inherited `_lub_map` accumulator so overload back-
439
// feed can push concrete types from downstream usage. Never
440
// appears in IL — it's an inference-time-only sentinel that
441
// identifies "the T slot of this specific construction".
442
class INFERRED_TYPE_ARG_ORIGIN: Variable is
443
// True when the slot this phantom stands for is an argument
444
// pack: a value called through the phantom takes the call's
445
// arguments as the tuple the pack binds to.
446
is_argument_pack: bool public
447
448
init(location: LOCATION, owner: Scope, name: string) is
449
super.init(location, owner, name)
450
si
451
452
si
453
454
// FIXME: pull up common code into a local + argument superclass:
455
class LOCAL_VARIABLE: Variable, Types.SettableTyped is
456
is_local: bool => true
457
458
// Set on the local a nested named function statement declares, so
459
// a reference from above the definition - a call written earlier
460
// in the body, or a sibling function reaching for this one - is
461
// reported as what it is rather than as an undefined variable.
462
is_nested_function: bool public
463
464
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
465
_describe_typed(context, PARTS.literal(name), type)
466
467
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
468
_local_kind()
469
470
_local_kind() -> string =>
471
if is_nested_function then "nested function"
472
elif is_boxed then "captured variable"
473
elif is_captured then "captured value"
474
elif is_disposed then "scoped disposal value"
475
elif is_reassigned then "local variable"
476
else "local value"
477
fi
478
479
init(location: LOCATION, owner: Scope, name: string) is
480
super.init(location, owner, name)
481
482
il_name_override = IoC.CONTAINER.instance.local_id_generator.get_unique_il_name_for(name)
483
si
484
485
define() is
486
is_defined = true
487
si
488
489
check_is_defined(location: LOCATION) is
490
if !is_defined then
491
if is_nested_function then
492
IoC.CONTAINER.instance.logger.error(location, "nested function {name} is used before its definition")
493
else
494
IoC.CONTAINER.instance.logger.error(location, "variable is not defined here")
495
fi
496
fi
497
si
498
499
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
500
assert !from?
501
502
check_is_defined(location)
503
504
return loader.load_local_variable(location, self)
505
si
506
507
load_outer(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
508
assert !from?
509
510
// A closure capturing this variable can only see a meaningful
511
// value if the let-binding has already completed by the time
512
// the closure is created. If is_defined is false the closure
513
// is being constructed inside the variable's own initializer
514
// (or before it), so the slot it captures is null. Without
515
// this check, the silent ERROR-typed reference escapes all
516
// the way to IL emission and crashes Type.gen_type.
517
check_is_defined(location)
518
519
return loader.load_outer_local_variable(location, self)
520
si
521
522
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value is
523
assert !from?
524
525
check_is_defined(location)
526
527
is_assigned = true
528
529
if !is_initialize then
530
is_reassigned = true
531
fi
532
533
return loader.store_local_variable(location, self, value, is_initialize)
534
si
535
536
// Declaring a captured, reassigned local that has no initializer:
537
// there is no value to store, only the empty box to allocate.
538
store_empty_box(location: LOCATION, loader: SYMBOL_LOADER) -> Value is
539
check_is_defined(location)
540
541
is_assigned = true
542
543
return loader.store_empty_boxed_local(location, self)
544
si
545
si
546
547
// FIXME: pull up common code into a local + argument superclass:
548
class LOCAL_ARGUMENT: Variable, Types.SettableTyped is
549
is_argument: bool => true
550
is_local: bool => true
551
552
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
553
_describe_typed(context, PARTS.literal(name), type)
554
555
describe_kind(context: DESCRIBE_CONTEXT) -> string? =>
556
_argument_kind()
557
558
_argument_kind() -> string =>
559
if is_captured then "captured value"
560
elif is_reassigned then "local variable"
561
else "local argument"
562
fi
563
564
init(location: LOCATION, owner: Scope, name: string) is
565
super.init(location, owner, name)
566
si
567
568
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
569
assert !from?
570
return loader.load_local_argument(location, self)
571
si
572
573
load_outer(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
574
assert !from?
575
return loader.load_outer_local_argument(location, self)
576
si
577
578
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value is
579
assert !from?
580
581
is_assigned = true
582
583
if !is_initialize then
584
is_reassigned = true
585
fi
586
587
return loader.store_local_argument(location, self, value, is_initialize)
588
si
589
590
si
591
592
class Field: Variable, Types.SettableTyped abstract is
593
unspecialized_type: Type? public
594
595
// Set on a closure frame's `$recurse` capture: a load of this
596
// field is the enclosing recursive literal referring to
597
// itself, so the value it denotes is the frame's own closure.
598
is_recurse_capture: bool public
599
600
// For a frame field capturing a local variable, the local it
601
// captures — an immutable one can only ever hold its
602
// initializer's value, so a load of the field denotes
603
// whatever that local does.
604
captured_symbol: Symbol? public
605
606
symbol_kind: SymbolKind => SymbolKind.FIELD
607
completion_kind: CompletionKind => CompletionKind.FIELD
608
609
is_private: bool
610
is_field: bool => true
611
is_public_readable: bool => !is_private
612
is_workspace_visible: bool => !is_private
613
614
is_accessible_to(accessor: Classy?) -> bool is
615
if !is_private then
616
return true
617
fi
618
619
let policy = IoC.CONTAINER.instance.build_flags.underscore_access
620
621
if policy == Compiler.UnderscoreAccess.PRIVATE then
622
return is_accessible_to_declaring_type(accessor)
623
elif policy == Compiler.UnderscoreAccess.PROTECTED then
624
// Normalised through unspecialized_symbol so a member
625
// reached through a specialization matches its declaring
626
// type - see is_accessible_to_declaring_type.
627
let o = cast Classy?(owner?.unspecialized_symbol)
628
return accessor? /\ o? /\ o.type? /\ accessor.type? /\ o.type.is_assignable_from(accessor.type)
629
fi
630
631
// LEGACY: the pre-existing is_public_readable rule remains the gate.
632
return true
633
si
634
635
access_prefix: string =>
636
if !is_private then
637
""
638
elif IoC.CONTAINER.instance.build_flags.underscore_access == Compiler.UnderscoreAccess.PRIVATE then
639
"private "
640
elif IoC.CONTAINER.instance.build_flags.underscore_access == Compiler.UnderscoreAccess.PROTECTED then
641
"protected "
642
else
643
""
644
fi
645
646
// Shared body for the concrete Field kinds. Delegates to
647
// `_describe_typed` so the narrowed / declared dual display
648
// rule stays in one place — a hover on a field whose observed
649
// type differs from its declared shape shows both.
650
_describe_field(context: DESCRIBE_CONTEXT) -> SignaturePart =>
651
_describe_typed(context, PARTS.name(self), type)
652
653
init(location: LOCATION, owner: Scope, name: string) is
654
super.init(location, owner, name)
655
656
self.is_private = name.starts_with('_')
657
si
658
659
specialize(type_map: Collections.Map[Symbol,Type], owner: GENERIC) -> Symbol is
660
let result = cast Field?(super.specialize(type_map, owner))!
661
662
result.unspecialized_type = type!
663
664
return result
665
si
666
667
si
668
669
class GLOBAL_VARIABLE: Field, Types.SettableTyped is
670
// Set by the .NET importer when the symbol is read back from a
671
// referenced assembly: the carrier class the field is a static
672
// member of, which a field reference hangs off. A namespace can
673
// have several carriers, so the carrier is recorded rather than
674
// derived from the namespace.
675
il_carrier: Classy? public
676
677
// Globals live on the synthetic $globals class; declaring-class-private
678
// is meaningless for them. An underscore global variable is
679
// assembly-internal (emitted assembly, hidden from other assemblies)
680
// and freely reachable within the assembly.
681
is_accessible_to(accessor: Classy?) -> bool => true
682
access_prefix: string => ""
683
684
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
685
_describe_field(context)
686
687
init(location: LOCATION, owner: Scope, name: string) is
688
super.init(location, owner, name)
689
si
690
691
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
692
// `from` is the namespace when the variable is named through
693
// it (`demo.count`), and nothing otherwise: a global variable
694
// is reached the same way either way.
695
return loader.load_global_variable(self)
696
si
697
698
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value is
699
return loader.store_global_variable(self, value)
700
si
701
702
// Field definition lives inside `.class 'NS'.'$globals' { ... }` block.
703
si
704
705
// A top-level `let` local promoted to a static field on the globals
706
// container, so global functions in the same file can read it. Declared
707
// by declare-members from a `let` that is a direct top-level statement;
708
// the synthesised entry's own store initialises it, and a bare (non-mut)
709
// `let` stays unassignable everywhere else.
710
class TOP_LEVEL_VARIABLE: GLOBAL_VARIABLE is
711
init(location: LOCATION, owner: Scope, name: string) is
712
super.init(location, owner, name)
713
si
714
715
// "global variable" rather than "top-level variable": the kind is
716
// what it is - namespace-scope mutable state with no accessors -
717
// rather than where it was written, and it is distinct from the
718
// `global property` a declared `name: type` produces, which has a
719
// backing field and accessor functions.
720
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "global variable"
721
722
// A top-level `let` is declared into the namespace, but it is
723
// still written as a statement and takes its value where it is
724
// written, so it carries a local's notion of the point it
725
// becomes readable. The walk already calls this on the `let`'s
726
// left and clears the flag before every walk of that `let`;
727
// without the override both land on the base no-op and the
728
// variable reads as defined nowhere.
729
define() is
730
is_defined = true
731
si
732
733
// A reported read has no type to load - the `let` that gives
734
// the variable one has not been walked yet - so it recovers as
735
// an error-typed value. That is the carrier the rest of
736
// compile-expressions already reads as "diagnosed, do not
737
// report again"; loading the variable itself instead hands
738
// member access, indexing and operator resolution a value with
739
// no type at all, which each of them reports in its own words.
740
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
741
if let earlier = _earlier_submission_variable(location) then
742
return earlier.load(location, from, loader)
743
fi
744
745
if _check_textual_order(location) then
746
return DUMMY(Types.ERROR(), location)
747
fi
748
749
return super.load(location, from, loader)
750
si
751
752
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value is
753
if !is_initialize then
754
if !is_mutable_marked then
755
IoC.CONTAINER.instance.logger.error(location, "top-level value cannot be reassigned")
756
fi
757
758
// Reported or not, the store still stands: an
759
// assignment has a value to write and a field to write
760
// it to, neither of which the diagnostic changes.
761
let _ = _check_textual_order(location)
762
fi
763
764
return super.store(location, from, value, loader, is_initialize)
765
si
766
767
// One step of an interactive session redefines a name by declaring
768
// it again, and the natural way to write the new value is in terms
769
// of the old: `let x = x + 1`. Inside its own initializer this
770
// variable does not exist yet, so where an earlier step's variable
771
// of the same name was imported, that is the one the name means
772
// there. Anywhere but a submission there is no earlier one to
773
// mean, and the read stays the error it is.
774
_earlier_submission_variable(use_location: LOCATION) -> Symbol? is
775
let container = IoC.CONTAINER.instance
776
777
if
778
!container.build_flags.submission_name? \/
779
is_defined \/
780
!container.symbol_table.is_within_top_level_entry \/
781
use_location.file_name !~ location.file_name \/
782
use_location.start < location.start
783
then
784
return null
785
fi
786
787
if let block = container.symbol_table.current_namespace_block then
788
let earlier = block.get_used_symbol(name)
789
790
if earlier? /\ earlier != self then
791
return earlier
792
fi
793
fi
794
795
return null
796
si
797
798
// A `let` among the top-level statements is a statement, and
799
// takes its value in the order the statements run, so it is in
800
// scope from where it is written and no earlier. A read above it
801
// gets the field's default value rather than one the program
802
// assigned, wherever that read is written: a function defined
803
// above the `let` can still be called from below it, so nothing
804
// here makes the read safe, but holding every reader to the
805
// declaration's position is consistent and says what the `let`
806
// was for. A namespace-scope declaration - `count: int;` - is
807
// the way to ask for state the whole file sees regardless of
808
// order.
809
_check_textual_order(use_location: LOCATION) -> bool is
810
if use_location.file_name !~ location.file_name then
811
// Another file's use is not ordered against this
812
// declaration: its statements are a different entry's,
813
// and run in their own order rather than this one's.
814
return false
815
fi
816
817
if use_location.start < location.start then
818
IoC.CONTAINER.instance.logger.error(
819
use_location,
820
"global variable {name} is used before its declaration",
821
location,
822
"{name} is declared here"
823
)
824
825
return true
826
elif
827
IoC.CONTAINER.instance.symbol_table.is_within_top_level_entry /\
828
!is_defined
829
then
830
// A read from inside the `let`'s own initializer, which
831
// is textually after the name it declares and so is not
832
// caught above. `pre_let` clears `is_defined` at the
833
// start of every walk of the `let`, so this holds on a
834
// re-walk as well as on the first. Asked only within the
835
// entry, where that clearing and the matching define()
836
// are what the walk is doing; elsewhere the flag says
837
// nothing about the reading position.
838
IoC.CONTAINER.instance.logger.error(
839
use_location,
840
"variable is not defined here",
841
location,
842
"{name} is declared here"
843
)
844
845
return true
846
fi
847
848
return false
849
si
850
si
851
852
class INSTANCE_FIELD: Field is
853
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
854
_describe_field(context)
855
856
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "{access_prefix}field"
857
858
is_instance: bool => true
859
860
init(location: LOCATION, owner: Scope, name: string) is
861
super.init(location, owner, name)
862
si
863
864
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value =>
865
loader.load_instance_variable(location, from, self)
866
867
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value =>
868
loader.store_instance_variable(location, from, self, value)
869
si
870
871
class VARIANT_FIELD: Field is
872
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
873
_describe_field(context)
874
875
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "variant field"
876
877
is_instance: bool => true
878
879
// it's OK for variant fields to hide symbols in the base union
880
can_hide_inherited: bool => true
881
882
init(location: LOCATION, owner: Scope, name: string) is
883
super.init(location, owner, name)
884
si
885
886
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value =>
887
loader.load_instance_variable(location, from, self)
888
889
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value =>
890
loader.store_instance_variable(location, from, self, value)
891
si
892
893
class STRUCT_FIELD: Field is
894
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
895
_describe_field(context)
896
897
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "{access_prefix}field"
898
899
is_instance: bool => true
900
901
init(location: LOCATION, owner: Scope, name: string) is
902
super.init(location, owner, name)
903
si
904
905
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value =>
906
loader.load_struct_variable(location, from, self)
907
908
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value =>
909
loader.store_struct_variable(location, from, self, value)
910
si
911
912
class STATIC_FIELD: Field is
913
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
914
_describe_field(context)
915
916
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "{access_prefix}class field"
917
918
init(location: LOCATION, owner: Scope, name: string) is
919
super.init(location, owner, name)
920
si
921
922
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value =>
923
loader.load_static_field(self)
924
925
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value =>
926
loader.store_static_field(self, value)
927
928
si
929
930
// An imported compile-time constant. Metadata carries the value
931
// rather than a storage slot - a language that inlines a constant at
932
// each use leaves no field behind for anyone to load - so a read is
933
// the value written out where the read is, and there is nothing for a
934
// write to write to.
935
class CONSTANT_FIELD: Field is
936
// The value as invariant-culture text. Absent when the constant
937
// itself is null, which is the only constant of a reference type
938
// metadata can hold - a case of its own rather than a reserved
939
// spelling, since a constant string can hold any text at all.
940
constant_value: string?
941
942
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
943
_describe_field(context)
944
945
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "{access_prefix}constant"
946
947
init(location: LOCATION, owner: Scope, name: string, constant_value: string?) is
948
super.init(location, owner, name)
949
950
self.constant_value = constant_value
951
si
952
953
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value =>
954
loader.load_constant_field(self)
955
956
store(location: LOCATION, from: Value?, value: Value, loader: SYMBOL_LOADER, is_initialize: bool) -> Value =>
957
loader.store_constant_field(location, self)
958
si
959
si
960