Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Function = Semantic.Symbols.Function | |
| 3 | use Symbol = Semantic.Symbols.Symbol | |
| 4 | ||
| 5 | // What one function contributed to the effect solve's inputs, as | |
| 6 | // taken before a partial round drops and re-derives it: copies of | |
| 7 | // every set the solve reads, so the re-derived contribution can be | |
| 8 | // compared member by member. | |
| 9 | class EFFECT_INPUT_SNAPSHOT( | |
| 10 | is_disqualified: bool, | |
| 11 | is_construction_disqualified: bool, | |
| 12 | callees: Collections.SET[Function], | |
| 13 | construction_callees: Collections.SET[Function], | |
| 14 | has_record: bool, | |
| 15 | writes: Collections.SET[Symbol], | |
| 16 | nulling_writes: Collections.SET[Symbol], | |
| 17 | reads: Collections.SET[Symbol], | |
| 18 | forwards_param_to: Collections.SET[Function], | |
| 19 | flags: int | |
| 20 | ) is | |
| 21 | of(facts: STORE_FREE_FACTS, record: FUNCTION_EFFECT_FACTS?) -> EFFECT_INPUT_SNAPSHOT static is | |
| 22 | return EFFECT_INPUT_SNAPSHOT( | |
| 23 | facts.is_disqualified, | |
| 24 | facts.is_construction_disqualified, | |
| 25 | _copy(facts.callees), | |
| 26 | _copy(facts.construction_callees), | |
| 27 | record?, | |
| 28 | if record? then _copy(record.writes) else Collections.SET[Symbol]() fi, | |
| 29 | if record? then _copy(record.nulling_writes) else Collections.SET[Symbol]() fi, | |
| 30 | if record? then _copy(record.reads) else Collections.SET[Symbol]() fi, | |
| 31 | if record? then _copy(record.forwards_param_to) else Collections.SET[Function]() fi, | |
| 32 | if record? then flags_of(record) else 0 fi) | |
| 33 | si | |
| 34 | ||
| 35 | flags_of(record: FUNCTION_EFFECT_FACTS) -> int static is | |
| 36 | let bits = [ | |
| 37 | record.own_stores, record.own_unbounded, record.is_disqualified, | |
| 38 | record.writes_unbounded, record.assigns_optional, record.nulling_unbounded, | |
| 39 | record.writes_elements, record.reads_elements, record.for_auto_property, | |
| 40 | record.openly_dispatchable, record.declared_pure, record.invokes_function_param | |
| 41 | ] | |
| 42 | ||
| 43 | let result mut = 0 | |
| 44 | ||
| 45 | for bit in bits do | |
| 46 | result = result * 2 + if bit then 1 else 0 fi | |
| 47 | od | |
| 48 | ||
| 49 | return result * 65536 + record.callee_count * 256 + record.overrider_count | |
| 50 | si | |
| 51 | ||
| 52 | // Whether the function's contribution after its re-walk is the | |
| 53 | // one captured here. | |
| 54 | matches(facts: STORE_FREE_FACTS, record: FUNCTION_EFFECT_FACTS?) -> bool is | |
| 55 | if facts.is_disqualified != is_disqualified \/ facts.is_construction_disqualified != is_construction_disqualified then | |
| 56 | return false | |
| 57 | fi | |
| 58 | ||
| 59 | if !_same_members(facts.callees, callees) \/ !_same_members(facts.construction_callees, construction_callees) then | |
| 60 | return false | |
| 61 | fi | |
| 62 | ||
| 63 | if record? != has_record then | |
| 64 | return false | |
| 65 | fi | |
| 66 | ||
| 67 | if !record? then | |
| 68 | return true | |
| 69 | fi | |
| 70 | ||
| 71 | return | |
| 72 | _same_members(record.writes, writes) /\ | |
| 73 | _same_members(record.nulling_writes, nulling_writes) /\ | |
| 74 | _same_members(record.reads, reads) /\ | |
| 75 | _same_members(record.forwards_param_to, forwards_param_to) /\ | |
| 76 | flags_of(record) == flags | |
| 77 | si | |
| 78 | ||
| 79 | _copy[T](members: Collections.Iterable[T]) -> Collections.SET[T] static is | |
| 80 | let result = Collections.SET[T]() | |
| 81 | ||
| 82 | for m in members do | |
| 83 | result.add(m) | |
| 84 | od | |
| 85 | ||
| 86 | return result | |
| 87 | si | |
| 88 | ||
| 89 | _same_members[T](members: Collections.Iterable[T], set: Collections.SET[T]) -> bool static is | |
| 90 | let seen = Collections.SET[T]() | |
| 91 | ||
| 92 | for m in members do | |
| 93 | if !set.contains(m) then | |
| 94 | return false | |
| 95 | fi | |
| 96 | ||
| 97 | seen.add(m) | |
| 98 | od | |
| 99 | ||
| 100 | return seen.count == set.count | |
| 101 | si | |
| 102 | si | |
| 103 | ||
| 104 | // Everything a partial round's re-walk of some files can change | |
| 105 | // among the solve's inputs, taken before their facts are dropped. | |
| 106 | class EFFECT_INPUTS_SNAPSHOT( | |
| 107 | functions: Collections.MAP[Function, EFFECT_INPUT_SNAPSHOT], | |
| 108 | passed: PASSED_CONTRIBUTIONS | |
| 109 | ) | |
| 110 | ||
| 111 | partial INFER_STORE_FREE is | |
| 112 | // The contributions of every walked function in `files`, and | |
| 113 | // the function values their call sites pass, so a partial round | |
| 114 | // can tell whether re-deriving them changed any input the solve | |
| 115 | // reads. Taken before the files' facts are dropped. | |
| 116 | snapshot_inputs(files: Collections.Iterable[string]) -> EFFECT_INPUTS_SNAPSHOT is | |
| 117 | let names = Collections.SET[string]() | |
| 118 | ||
| 119 | for file_name in files do | |
| 120 | names.add(file_name) | |
| 121 | od | |
| 122 | ||
| 123 | let result = Collections.MAP[Function, EFFECT_INPUT_SNAPSHOT]() | |
| 124 | ||
| 125 | for entry in _facts do | |
| 126 | if names.contains(entry.key.location.file_name) then | |
| 127 | result[entry.key] = EFFECT_INPUT_SNAPSHOT.of(entry.value, EFFECT_FACTS.record_for(entry.key)) | |
| 128 | fi | |
| 129 | od | |
| 130 | ||
| 131 | return EFFECT_INPUTS_SNAPSHOT(result, EFFECT_FACTS.passed_from(names)) | |
| 132 | si | |
| 133 | ||
| 134 | // The functions whose solve inputs the re-walk of `files` | |
| 135 | // changed from what the snapshot captured: a function walked | |
| 136 | // with a different contribution, one walked for the first time, | |
| 137 | // one no longer walked, and a callee handed different function | |
| 138 | // values from the files' call sites. Empty when the installed | |
| 139 | // relations already answer for these facts. | |
| 140 | changed_inputs( | |
| 141 | files: Collections.Iterable[string], | |
| 142 | snapshot: EFFECT_INPUTS_SNAPSHOT | |
| 143 | ) -> Collections.SET[Function] is | |
| 144 | let names = Collections.SET[string]() | |
| 145 | ||
| 146 | for file_name in files do | |
| 147 | names.add(file_name) | |
| 148 | od | |
| 149 | ||
| 150 | let changed = snapshot.passed.differing_callees(EFFECT_FACTS.passed_from(names)) | |
| 151 | ||
| 152 | for entry in _facts do | |
| 153 | if !names.contains(entry.key.location.file_name) then | |
| 154 | continue | |
| 155 | fi | |
| 156 | ||
| 157 | let before: EFFECT_INPUT_SNAPSHOT mut | |
| 158 | ||
| 159 | if !snapshot.functions.try_get_value(entry.key, before ref) then | |
| 160 | changed.add(entry.key) | |
| 161 | elif !before.matches(entry.value, EFFECT_FACTS.record_for(entry.key)) then | |
| 162 | changed.add(entry.key) | |
| 163 | fi | |
| 164 | od | |
| 165 | ||
| 166 | for function in snapshot.functions.keys do | |
| 167 | if !_facts.contains_key(function) then | |
| 168 | changed.add(function) | |
| 169 | fi | |
| 170 | od | |
| 171 | ||
| 172 | return changed | |
| 173 | si | |
| 174 | si | |
| 175 | si |