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