Appearance
| 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 |