@@ -45,6 +45,11 @@ pub(super) fn compute<T: FactTypes>(
45
45
// `loan_invalidated_at` facts, stored ready for joins
46
46
let loan_invalidated_at =
47
47
iteration. variable :: < ( ( T :: Loan , T :: Point ) , ( ) ) > ( "loan_invalidated_at" ) ;
48
+ loan_invalidated_at. extend (
49
+ ctx. loan_invalidated_at
50
+ . iter ( )
51
+ . map ( |& ( loan, point) | ( ( loan, point) , ( ) ) ) ,
52
+ ) ;
48
53
49
54
// different indices for `subset`.
50
55
let subset_o1p = iteration. variable_indistinct ( "subset_o1p" ) ;
@@ -54,10 +59,32 @@ pub(super) fn compute<T: FactTypes>(
54
59
let origin_contains_loan_on_entry_op =
55
60
iteration. variable_indistinct ( "origin_contains_loan_on_entry_op" ) ;
56
61
57
- // we need `origin_live_on_entry` in both variable and relation forms.
58
- // (respectively, for the regular join and the leapjoin).
62
+ // Unfortunately, we need `origin_live_on_entry` in both variable and relation forms:
63
+ // We need:
64
+ // - `origin_live_on_entry` as a Relation for the leapjoins in rules 3 & 6
65
+ // - `origin_live_on_entry` as a Variable for the join in rule 7
66
+ //
67
+ // The leapjoins use `origin_live_on_entry` as `(Origin, Point)` tuples, while the join uses
68
+ // it as a `((O, P), ())` tuple to filter the `((Origin, Point), Loan)` tuples from
69
+ // `origin_contains_loan_on_entry_op`.
70
+ //
71
+ // The regular join in rule 7 could be turned into a `filter_with` leaper but that would
72
+ // result in a leapjoin with no `extend_*` leapers: a leapjoin that is not well-formed.
73
+ // Doing the filtering via an `extend_with` leaper would be extremely inefficient.
74
+ //
75
+ // Until there's an API in datafrog to handle this use-case better, we do a slightly less
76
+ // inefficient thing of copying the whole static input into a Variable to use a regular
77
+ // join, even though the liveness information can be quite heavy (around 1M tuples
78
+ // on `clap`).
79
+ // This is the Naive variant so this is not a big problem, but needs an
80
+ // explanation.
59
81
let origin_live_on_entry_var =
60
82
iteration. variable :: < ( ( T :: Origin , T :: Point ) , ( ) ) > ( "origin_live_on_entry" ) ;
83
+ origin_live_on_entry_var. extend (
84
+ origin_live_on_entry_rel
85
+ . iter ( )
86
+ . map ( |& ( origin, point) | ( ( origin, point) , ( ) ) ) ,
87
+ ) ;
61
88
62
89
// variable and index to compute the subsets of placeholders
63
90
let subset_placeholder =
@@ -68,19 +95,19 @@ pub(super) fn compute<T: FactTypes>(
68
95
let errors = iteration. variable ( "errors" ) ;
69
96
let subset_errors = iteration. variable :: < ( T :: Origin , T :: Origin , T :: Point ) > ( "subset_errors" ) ;
70
97
71
- // load initial facts.
98
+ // load initial facts:
99
+
100
+ // Rule 1: the initial subsets are the non-transitive `subset_base` static input.
101
+ //
102
+ // subset(Origin1, Origin2, Point) :-
103
+ // subset_base(Origin1, Origin2, Point).
72
104
subset. extend ( ctx. subset_base . iter ( ) ) ;
105
+
106
+ // Rule 4: the issuing origins are the ones initially containing loans.
107
+ //
108
+ // origin_contains_loan_on_entry(Origin, Loan, Point) :-
109
+ // loan_issued_at(Origin, Loan, Point).
73
110
origin_contains_loan_on_entry. extend ( ctx. loan_issued_at . iter ( ) ) ;
74
- loan_invalidated_at. extend (
75
- ctx. loan_invalidated_at
76
- . iter ( )
77
- . map ( |& ( loan, point) | ( ( loan, point) , ( ) ) ) ,
78
- ) ;
79
- origin_live_on_entry_var. extend (
80
- origin_live_on_entry_rel
81
- . iter ( )
82
- . map ( |& ( origin, point) | ( ( origin, point) , ( ) ) ) ,
83
- ) ;
84
111
85
112
// .. and then start iterating rules!
86
113
while iteration. changed ( ) {
@@ -104,7 +131,7 @@ pub(super) fn compute<T: FactTypes>(
104
131
. elements
105
132
. retain ( |& ( origin1, origin2, _) | origin1 != origin2) ;
106
133
107
- // remap fields to re-index by keys.
134
+ // Remap fields to re-index by keys, to prepare the data needed by the rules below .
108
135
subset_o1p. from_map ( & subset, |& ( origin1, origin2, point) | {
109
136
( ( origin1, point) , origin2)
110
137
} ) ;
@@ -121,24 +148,26 @@ pub(super) fn compute<T: FactTypes>(
121
148
( ( origin, point) , loan)
122
149
} ) ;
123
150
124
- // subset(origin1, origin2, point) :-
125
- // subset_base(origin1, origin2, point).
126
- // Already loaded; `subset_base` is static.
151
+ // Rule 1: done above, as part of the static input facts setup.
127
152
128
- // subset(origin1, origin3, point) :-
129
- // subset(origin1, origin2, point),
130
- // subset(origin2, origin3, point).
153
+ // Rule 2: compute the subset transitive closure, at a given point.
154
+ //
155
+ // subset(Origin1, Origin3, Point) :-
156
+ // subset(Origin1, Origin2, Point),
157
+ // subset(Origin2, Origin3, Point).
131
158
subset. from_join (
132
159
& subset_o2p,
133
160
& subset_o1p,
134
161
|& ( _origin2, point) , & origin1, & origin3| ( origin1, origin3, point) ,
135
162
) ;
136
163
137
- // subset(origin1, origin2, point2) :-
138
- // subset(origin1, origin2, point1),
139
- // cfg_edge(point1, point2),
140
- // origin_live_on_entry(origin1, point2),
141
- // origin_live_on_entry(origin2, point2).
164
+ // Rule 3: propagate subsets along the CFG, according to liveness.
165
+ //
166
+ // subset(Origin1, Origin2, Point2) :-
167
+ // subset(Origin1, Origin2, Point1),
168
+ // cfg_edge(Point1, Point2),
169
+ // origin_live_on_entry(Origin1, Point2),
170
+ // origin_live_on_entry(Origin2, Point2).
142
171
subset. from_leapjoin (
143
172
& subset,
144
173
(
@@ -149,24 +178,26 @@ pub(super) fn compute<T: FactTypes>(
149
178
|& ( origin1, origin2, _point1) , & point2| ( origin1, origin2, point2) ,
150
179
) ;
151
180
152
- // origin_contains_loan_on_entry(origin, loan, point) :-
153
- // loan_issued_at(origin, loan, point).
154
- // Already loaded; `loan_issued_at` is static.
181
+ // Rule 4: done above as part of the static input facts setup.
155
182
156
- // origin_contains_loan_on_entry(origin2, loan, point) :-
157
- // origin_contains_loan_on_entry(origin1, loan, point),
158
- // subset(origin1, origin2, point).
183
+ // Rule 5: propagate loans within origins, at a given point, according to subsets.
184
+ //
185
+ // origin_contains_loan_on_entry(Origin2, Loan, Point) :-
186
+ // origin_contains_loan_on_entry(Origin1, Loan, Point),
187
+ // subset(Origin1, Origin2, Point).
159
188
origin_contains_loan_on_entry. from_join (
160
189
& origin_contains_loan_on_entry_op,
161
190
& subset_o1p,
162
191
|& ( _origin1, point) , & loan, & origin2| ( origin2, loan, point) ,
163
192
) ;
164
193
165
- // origin_contains_loan_on_entry(origin, loan, point2) :-
166
- // origin_contains_loan_on_entry(origin, loan, point1),
167
- // !loan_killed_at(loan, point1),
168
- // cfg_edge(point1, point2),
169
- // origin_live_on_entry(origin, point2).
194
+ // Rule 6: propagate loans along the CFG, according to liveness.
195
+ //
196
+ // origin_contains_loan_on_entry(Origin, Loan, Point2) :-
197
+ // origin_contains_loan_on_entry(Origin, Loan, Point1),
198
+ // !loan_killed_at(Loan, Point1),
199
+ // cfg_edge(Point1, Point2),
200
+ // origin_live_on_entry(Origin, Point2).
170
201
origin_contains_loan_on_entry. from_leapjoin (
171
202
& origin_contains_loan_on_entry,
172
203
(
@@ -177,24 +208,31 @@ pub(super) fn compute<T: FactTypes>(
177
208
|& ( origin, loan, _point1) , & point2| ( origin, loan, point2) ,
178
209
) ;
179
210
180
- // loan_live_at(loan, point) :-
181
- // origin_contains_loan_on_entry(origin, loan, point),
182
- // origin_live_on_entry(origin, point).
211
+ // Rule 7: compute whether a loan is live at a given point, i.e. whether it is
212
+ // contained in a live origin at this point.
213
+ //
214
+ // loan_live_at(Loan, Point) :-
215
+ // origin_contains_loan_on_entry(Origin, Loan, Point),
216
+ // origin_live_on_entry(Origin, Point).
183
217
loan_live_at. from_join (
184
218
& origin_contains_loan_on_entry_op,
185
219
& origin_live_on_entry_var,
186
220
|& ( _origin, point) , & loan, _| ( ( loan, point) , ( ) ) ,
187
221
) ;
188
222
189
- // errors(loan, point) :-
190
- // loan_invalidated_at(loan, point),
191
- // loan_live_at(loan, point).
223
+ // Rule 8: compute illegal access errors, i.e. an invalidation of a live loan.
224
+ //
225
+ // errors(Loan, Point) :-
226
+ // loan_invalidated_at(Loan, Point),
227
+ // loan_live_at(Loan, Point).
192
228
errors. from_join (
193
229
& loan_invalidated_at,
194
230
& loan_live_at,
195
231
|& ( loan, point) , _, _| ( loan, point) ,
196
232
) ;
197
233
234
+ // Rule 9: compute the subsets of the placeholder origins, at a given point.
235
+ //
198
236
// subset_placeholder(Origin1, Origin2, Point) :-
199
237
// subset(Origin1, Origin2, Point),
200
238
// placeholder_origin(Origin1).
@@ -210,6 +248,8 @@ pub(super) fn compute<T: FactTypes>(
210
248
|& ( ( origin1, point) , origin2) , _| ( origin1, origin2, point) ,
211
249
) ;
212
250
251
+ // Rule 10: compute the subset transitive closure of placeholder origins.
252
+ //
213
253
// subset_placeholder(Origin1, Origin3, Point) :-
214
254
// subset_placeholder(Origin1, Origin2, Point),
215
255
// subset(Origin2, Origin3, Point).
@@ -219,6 +259,9 @@ pub(super) fn compute<T: FactTypes>(
219
259
|& ( _origin2, point) , & origin1, & origin3| ( origin1, origin3, point) ,
220
260
) ;
221
261
262
+ // Rule 11: compute illegal subset relations errors, i.e. the undeclared subsets
263
+ // between two placeholder origins.
264
+ //
222
265
// subset_errors(Origin1, Origin2, Point) :-
223
266
// subset_placeholder(Origin1, Origin2, Point),
224
267
// placeholder_origin(Origin2),
0 commit comments