Skip to content
← Back

src/syntax/process/access_path.ghul

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