1use crate::{
5 paths::{self, Path, PathSlice},
6 references::*,
7};
8use mirai_annotations::{debug_checked_postcondition, debug_checked_precondition};
9use std::collections::{BTreeMap, BTreeSet};
10
11#[derive(Clone, Debug, Default, PartialEq)]
16pub struct BorrowGraph<Loc: Copy, Lbl: Clone + Ord>(BTreeMap<RefID, Ref<Loc, Lbl>>);
17
18impl<Loc: Copy, Lbl: Clone + Ord> BorrowGraph<Loc, Lbl> {
23 #[allow(clippy::new_without_default)]
25 pub fn new() -> Self {
26 Self(BTreeMap::new())
27 }
28
29 pub fn is_mutable(&self, id: RefID) -> bool {
31 self.0.get(&id).unwrap().mutable
32 }
33
34 pub fn new_ref(&mut self, id: RefID, mutable: bool) {
37 assert!(
38 self.0.insert(id, Ref::new(mutable)).is_none(),
39 "ref {} exists",
40 id.0
41 )
42 }
43
44 pub fn borrowed_by(
50 &self,
51 id: RefID,
52 ) -> (BTreeMap<RefID, Loc>, BTreeMap<Lbl, BTreeMap<RefID, Loc>>) {
53 let borrowed_by = &self.0.get(&id).unwrap().borrowed_by;
54 let mut full_borrows: BTreeMap<RefID, Loc> = BTreeMap::new();
55 let mut field_borrows: BTreeMap<Lbl, BTreeMap<RefID, Loc>> = BTreeMap::new();
56 for (borrower, edges) in &borrowed_by.0 {
57 let borrower = *borrower;
58 for edge in edges {
59 match edge.path.get(0) {
60 None => full_borrows.insert(borrower, edge.loc),
61 Some(f) => field_borrows
62 .entry(f.clone())
63 .or_insert_with(BTreeMap::new)
64 .insert(borrower, edge.loc),
65 };
66 }
67 }
68 (full_borrows, field_borrows)
69 }
70
71 pub fn between_edges(&self, parent: RefID, child: RefID) -> Vec<(Loc, Path<Lbl>, bool)> {
73 let edges = &self.0.get(&parent).unwrap().borrowed_by.0[&child];
74 edges
75 .iter()
76 .map(|edge| (edge.loc, edge.path.clone(), edge.strong))
77 .collect()
78 }
79
80 pub fn out_edges(&self, id: RefID) -> Vec<(Loc, Path<Lbl>, bool, RefID)> {
82 let mut returned_edges = vec![];
83 let borrowed_by = &self.0.get(&id).unwrap().borrowed_by;
84 for (borrower, edges) in &borrowed_by.0 {
85 let borrower = *borrower;
86 for edge in edges {
87 returned_edges.push((edge.loc, edge.path.clone(), edge.strong, borrower));
88 }
89 }
90 returned_edges
91 }
92
93 pub fn in_edges(&self, id: RefID) -> Vec<(Loc, RefID, Path<Lbl>, bool)> {
95 let mut returned_edges = vec![];
96 let borrows_from = &self.0.get(&id).unwrap().borrows_from;
97 for src in borrows_from {
98 for edge in self.between_edges(*src, id) {
99 returned_edges.push((edge.0, *src, edge.1, edge.2));
100 }
101 }
102 returned_edges
103 }
104
105 pub fn add_strong_borrow(&mut self, loc: Loc, parent_id: RefID, child_id: RefID) {
111 self.factor(parent_id, loc, vec![], child_id)
112 }
113
114 pub fn add_strong_field_borrow(
116 &mut self,
117 loc: Loc,
118 parent_id: RefID,
119 field: Lbl,
120 child_id: RefID,
121 ) {
122 self.factor(parent_id, loc, vec![field], child_id)
123 }
124
125 pub fn add_weak_borrow(&mut self, loc: Loc, parent_id: RefID, child_id: RefID) {
128 self.add_path(parent_id, loc, false, vec![], child_id)
129 }
130
131 pub fn add_weak_field_borrow(
134 &mut self,
135 loc: Loc,
136 parent_id: RefID,
137 field: Lbl,
138 child_id: RefID,
139 ) {
140 self.add_path(parent_id, loc, false, vec![field], child_id)
141 }
142
143 fn add_edge(&mut self, parent_id: RefID, edge: BorrowEdge<Loc, Lbl>, child_id: RefID) {
144 assert!(parent_id != child_id);
145 let parent = self.0.get_mut(&parent_id).unwrap();
146 parent
147 .borrowed_by
148 .0
149 .entry(child_id)
150 .or_insert_with(BTreeSet::new)
151 .insert(edge);
152 let child = self.0.get_mut(&child_id).unwrap();
153 child.borrows_from.insert(parent_id);
154 }
155
156 fn add_path(
157 &mut self,
158 parent_id: RefID,
159 loc: Loc,
160 strong: bool,
161 path: Path<Lbl>,
162 child_id: RefID,
163 ) {
164 let edge = BorrowEdge { strong, path, loc };
165 self.add_edge(parent_id, edge, child_id)
166 }
167
168 fn factor(&mut self, parent_id: RefID, loc: Loc, path: Path<Lbl>, intermediate_id: RefID) {
169 debug_checked_precondition!(self.check_invariant());
170 let parent = self.0.get_mut(&parent_id).unwrap();
171 let mut needs_factored = vec![];
172 for (child_id, parent_to_child_edges) in &parent.borrowed_by.0 {
173 for parent_to_child_edge in parent_to_child_edges {
174 if paths::leq(&path, &parent_to_child_edge.path) {
175 let factored_edge = (*child_id, parent_to_child_edge.clone());
176 needs_factored.push(factored_edge);
177 }
178 }
179 }
180
181 let mut cleanup_ids = BTreeSet::new();
182 for (child_id, parent_to_child_edge) in &needs_factored {
183 let parent_to_child_edges = parent.borrowed_by.0.get_mut(child_id).unwrap();
184 assert!(parent_to_child_edges.remove(parent_to_child_edge));
185 if parent_to_child_edges.is_empty() {
186 assert!(parent.borrowed_by.0.remove(child_id).is_some());
187 cleanup_ids.insert(child_id);
188 }
189 }
190
191 for child_id in cleanup_ids {
192 assert!(self
193 .0
194 .get_mut(child_id)
195 .unwrap()
196 .borrows_from
197 .remove(&parent_id));
198 }
199
200 for (child_id, parent_to_child_edge) in needs_factored {
201 let (_, intermediate_to_child_suffix) = paths::factor(&path, parent_to_child_edge.path);
202 self.add_path(
203 intermediate_id,
204 parent_to_child_edge.loc,
205 parent_to_child_edge.strong,
206 intermediate_to_child_suffix,
207 child_id,
208 )
209 }
210 self.add_path(
211 parent_id,
212 loc,
213 true,
214 path,
215 intermediate_id,
216 );
217 debug_checked_postcondition!(self.check_invariant());
218 }
219
220 pub fn release(&mut self, id: RefID) {
228 debug_checked_precondition!(self.check_invariant());
229 let Ref {
230 borrowed_by,
231 borrows_from,
232 ..
233 } = self.0.remove(&id).unwrap();
234 for parent_ref_id in borrows_from.into_iter() {
235 let parent = self.0.get_mut(&parent_ref_id).unwrap();
236 let parent_edges = parent.borrowed_by.0.remove(&id).unwrap();
237 for parent_edge in parent_edges {
238 for (child_ref_id, child_edges) in &borrowed_by.0 {
239 for child_edge in child_edges {
240 self.splice_out_intermediate(
241 parent_ref_id,
242 &parent_edge,
243 *child_ref_id,
244 child_edge,
245 )
246 }
247 }
248 }
249 }
250 for child_ref_id in borrowed_by.0.keys() {
251 let child = self.0.get_mut(&child_ref_id).unwrap();
252 child.borrows_from.remove(&id);
253 }
254 debug_checked_postcondition!(self.check_invariant());
255 }
256
257 fn splice_out_intermediate(
258 &mut self,
259 parent_id: RefID,
260 parent_to_intermediate: &BorrowEdge<Loc, Lbl>,
261 child_id: RefID,
262 intermediate_to_child: &BorrowEdge<Loc, Lbl>,
263 ) {
264 if parent_id == child_id {
266 return;
267 }
268
269 let path = if parent_to_intermediate.strong {
270 paths::append(&parent_to_intermediate.path, &intermediate_to_child.path)
271 } else {
272 parent_to_intermediate.path.clone()
273 };
274 let strong = parent_to_intermediate.strong && intermediate_to_child.strong;
275 let loc = intermediate_to_child.loc;
276 let parent_to_child = BorrowEdge { strong, path, loc };
277 self.add_edge(parent_id, parent_to_child, child_id)
278 }
279
280 pub fn leq(&self, other: &Self) -> bool {
286 self.unmatched_edges(other).is_empty()
287 }
288
289 fn unmatched_edges(&self, other: &Self) -> BTreeMap<RefID, BorrowEdges<Loc, Lbl>> {
290 let mut unmatched_edges = BTreeMap::new();
291 for (parent_id, other_ref) in &other.0 {
292 let self_ref = &self.0[parent_id];
293 let self_borrowed_by = &self_ref.borrowed_by.0;
294 for (child_id, other_edges) in &other_ref.borrowed_by.0 {
295 for other_edge in other_edges {
296 let found_match = self_borrowed_by
297 .get(child_id)
298 .map(|parent_to_child| {
299 parent_to_child
300 .iter()
301 .any(|self_edge| self_edge.leq(other_edge))
302 })
303 .unwrap_or(false);
304 if !found_match {
305 assert!(parent_id != child_id);
306 unmatched_edges
307 .entry(*parent_id)
308 .or_insert_with(BorrowEdges::new)
309 .0
310 .entry(*child_id)
311 .or_insert_with(BTreeSet::new)
312 .insert(other_edge.clone());
313 }
314 }
315 }
316 }
317 unmatched_edges
318 }
319
320 pub fn remap_refs(&mut self, id_map: &BTreeMap<RefID, RefID>) {
327 debug_checked_precondition!(self.check_invariant());
328 for info in self.0.values_mut() {
329 info.remap_refs(id_map);
330 }
331 for (old, new) in id_map {
332 if let Some(info) = self.0.remove(old) {
333 self.0.insert(*new, info);
334 }
335 }
336 debug_checked_postcondition!(self.check_invariant());
337 }
338
339 pub fn join(&self, other: &Self) -> Self {
347 debug_checked_precondition!(self.check_invariant());
348 debug_checked_precondition!(other.check_invariant());
349 debug_checked_precondition!(self.0.keys().all(|id| other.0.contains_key(id)));
350 debug_checked_precondition!(other.0.keys().all(|id| self.0.contains_key(id)));
351
352 let mut joined = self.clone();
353 for (parent_id, unmatched_borrowed_by) in self.unmatched_edges(other) {
354 for (child_id, unmatched_edges) in unmatched_borrowed_by.0 {
355 for unmatched_edge in unmatched_edges {
356 joined.add_edge(parent_id, unmatched_edge, child_id);
357 }
358 }
359 }
360 debug_checked_postcondition!(joined.check_invariant());
361 joined
362 }
363
364 fn check_invariant(&self) -> bool {
369 self.id_consistency() && self.edge_consistency() && self.no_self_loops()
370 }
371
372 fn id_consistency(&self) -> bool {
375 let contains_id = |id| self.0.contains_key(id);
376 self.0.values().all(|r| {
377 r.borrowed_by.0.keys().all(contains_id) && r.borrows_from.iter().all(contains_id)
378 })
379 }
380
381 fn edge_consistency(&self) -> bool {
385 let parent_to_child_consistency =
386 |cur_parent, child| self.0[child].borrows_from.contains(cur_parent);
387 let child_to_parent_consistency =
388 |cur_child, parent| self.0[parent].borrowed_by.0.contains_key(cur_child);
389 self.0.iter().all(|(id, r)| {
390 r.borrowed_by
391 .0
392 .keys()
393 .all(|c| parent_to_child_consistency(id, c))
394 && r.borrows_from
395 .iter()
396 .all(|p| child_to_parent_consistency(id, p))
397 })
398 }
399
400 fn no_self_loops(&self) -> bool {
402 self.0.iter().all(|(id, r)| {
403 r.borrowed_by.0.keys().all(|to_id| id != to_id)
404 && r.borrows_from.iter().all(|from_id| id != from_id)
405 })
406 }
407
408 pub fn contains_id(&self, ref_id: RefID) -> bool {
414 self.0.contains_key(&ref_id)
415 }
416
417 pub fn all_refs(&self) -> BTreeSet<RefID> {
419 self.0.keys().cloned().collect()
420 }
421
422 #[allow(dead_code)]
424 pub fn display(&self)
425 where
426 Lbl: std::fmt::Display,
427 {
428 fn path_to_string<Lbl: std::fmt::Display>(p: &PathSlice<Lbl>) -> String {
429 p.iter()
430 .map(|l| l.to_string())
431 .collect::<Vec<_>>()
432 .join(".")
433 }
434
435 for (id, ref_info) in &self.0 {
436 if ref_info.borrowed_by.0.is_empty() && ref_info.borrows_from.is_empty() {
437 println!("{}", id.0);
438 }
439 for (borrower, edges) in &ref_info.borrowed_by.0 {
440 for edge in edges {
441 let edisp = if edge.strong { "=" } else { "-" };
442 println!(
443 "{} {}{}{}> {}",
444 id.0,
445 edisp,
446 path_to_string(&edge.path),
447 edisp,
448 borrower.0,
449 );
450 }
451 }
452 for parent in &ref_info.borrows_from {
453 println!("{} <- {}", parent.0, id.0);
454 }
455 }
456 }
457}