Skip to content
← Back

src/syntax/process/infer-effects/infer_store_free_snapshot.ghul

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