Skip to content
← Back

src/syntax/process/access_path.ghul

1
namespace Syntax.Process is
2
use Semantic.Symbols.Symbol;
3
use Semantic.Symbols.Variable;
4
5
// A syntactic access path for flow-sensitive presence tracking:
6
// a root local variable or field plus a chain of instance members
7
// read from it (`receiver.prop`, `receiver.outer.inner`). Every
8
// hop is a field or a property whose getter is proven store-free
9
// — never a call, an indexer or `?.` — so under an unchanged heap
10
// the path re-reads to the same presence answer. Two occurrences
11
// of the same written path resolve through the same root Variable
12
// and canonicalise every member Symbol to its unspecialised
13
// template via `root_specialized_from`, so structural equality
14
// keys presence facts on the path itself even when the receiver
15
// type is a generic instantiation (each `find_member` call on a
16
// GENERIC re-specialises via `memberwise_clone` and produces a
17
// fresh Symbol instance, which would otherwise defeat identity
18
// comparison).
19
//
20
// Fact lifetime is managed by the flow transfers: every path fact
21
// dies at a possibly-storing call; a getter-bearing path dies at
22
// any heap store; a fields-only path dies when its root is
23
// reassigned or when any store targets a field symbol the path
24
// reads through — on any receiver, so aliased receivers are
25
// covered. See NARROWING_FLOW.
26
class ACCESS_PATH is
27
root: Variable public;
28
members: Collections.LIST[Symbol] public;
29
30
init(root: Variable, members: Collections.LIST[Symbol]) is
31
self.root = root;
32
self.members = Collections.LIST[Symbol]();
33
34
for m in members do
35
self.members.add(_canonical(m));
36
od
37
si
38
39
// Canonical form of a member Symbol: the unspecialised
40
// template if the Symbol is a specialisation, else the
41
// Symbol itself. Two lookups of the same member on the same
42
// generic receiver produce distinct specialised Symbols but
43
// share this canonical form.
44
_canonical(m: Symbol) -> Symbol static => m.root_specialized_from;
45
46
// True iff any hop reads through a property getter. A store
47
// anywhere in the heap can change what a getter returns, so
48
// these paths cannot survive a heap store; fields-only paths
49
// can — a store to an unrelated field cannot alter them.
50
has_getter_hop: bool is
51
for m in members do
52
if isa Semantic.Symbols.Property(m) then
53
return true;
54
fi
55
od
56
57
return false;
58
si
59
60
// True iff the path reads through `member` at any hop — a
61
// store to that member on any receiver may change what this
62
// path holds. The incoming symbol is canonicalised so a
63
// specialised member matches a canonical hop.
64
contains_member(member: Symbol?) -> bool is
65
if !member? then
66
return false;
67
fi
68
69
let target = _canonical(member);
70
71
for m in members do
72
if m == target then
73
return true;
74
fi
75
od
76
77
return false;
78
si
79
80
equals(other: object?) -> bool is
81
let o = cast ACCESS_PATH?(other);
82
83
if !o? \/ o.root != root \/ o.members.count != members.count then
84
return false;
85
fi
86
87
for i in 0..members.count do
88
if o.members[i] != members[i] then
89
return false;
90
fi
91
od
92
93
return true;
94
si
95
96
get_hash_code() -> int is
97
let h mut = root.get_hash_code();
98
99
for m in members do
100
h = h * 31 + m.get_hash_code();
101
od
102
103
return h;
104
si
105
106
to_string() -> string is
107
let buffer = System.Text.StringBuilder();
108
buffer.append(root.name);
109
110
for m in members do
111
buffer.append(".");
112
buffer.append(m.name);
113
od
114
115
return buffer.to_string();
116
si
117
si
118
si