diff --git a/src/borrow_tracker/tree_borrows/tree.rs b/src/borrow_tracker/tree_borrows/tree.rs index f045a34729..e45924cad2 100644 --- a/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/borrow_tracker/tree_borrows/tree.rs @@ -395,6 +395,63 @@ impl<'tree> TreeVisitor<'tree> { } Ok(()) } + + // Applies `f_propagate` to every non-child vertex of the tree (ancestors first). + // + // `f_propagate` should follow the following format: for a given `Node` it updates its + // `Permission` depending on the position relative to `start` (given by an + // `AccessRelatedness`). + // It outputs whether the tree traversal for this subree should continue or not. + fn traverse_nonchildren( + mut self, + start: BorTag, + f_propagate: impl Fn(NodeAppArgs<'_>) -> Result, + err_builder: impl Fn(ErrHandlerArgs<'_, InnErr>) -> OutErr, + ) -> Result<(), OutErr> { + let start_idx = self.tag_mapping.get(&start).unwrap(); + let mut stack = + TreeVisitAux { accessed_tag: start_idx, f_propagate, err_builder, stack: Vec::new() }; + { + let mut path_ascend = Vec::new(); + // First climb to the root while recording the path + let mut curr = start_idx; + while let Some(ancestor) = self.nodes.get(curr).unwrap().parent { + path_ascend.push((ancestor, curr)); + curr = ancestor; + } + // Then descend: + // - execute f_propagate on each node + // - record children in visit + while let Some((ancestor, next_in_path)) = path_ascend.pop() { + // Explore ancestors in descending order. + // `next_in_path` is excluded from the recursion because it + // will be the `ancestor` of the next iteration. + // It also needs a different `AccessRelatedness` than the other + // children of `ancestor`. + stack.exec_and_visit( + &mut self, + ancestor, + Some(next_in_path), + AccessRelatedness::StrictChildAccess, + )?; + } + }; + // Up to this point we have never popped from `stack`, hence if the + // path to the root is `root = p(n) <- p(n-1)... <- p(1) <- p(0) = start` + // then now `stack` contains + // `[ ... ]`, + // all of which are for now unexplored. + // This is the starting point of a standard DFS which will thus + // explore all non-ancestors of `start` in the following order: + // - first the unexplored descendants of `parent(start)`; + // - then the unexplored descendants of `parent(parent(start))`; + // ... + // - until finally the unexplored descendants of `root`. + while let Some((tag, rel_pos)) = stack.pop() { + stack.exec_and_visit(&mut self, tag, None, rel_pos)?; + } + Ok(()) + } } impl Tree { @@ -605,6 +662,8 @@ impl<'tcx> Tree { // This is a special access through the entire allocation. // It actually only affects `initialized` locations, so we need // to filter on those before initiating the traversal. + // In addition this implicit access should not be visible to children, + // thus the use of `traverse_nonchildren`. for (perms_range, perms) in self.rperms.iter_mut_all() { let idx = self.tag_mapping.get(&tag).unwrap(); if let Some(p) = perms.get(idx) { @@ -616,7 +675,7 @@ impl<'tcx> Tree { tag_mapping: &self.tag_mapping, perms, } - .traverse_parents_this_children_others( + .traverse_nonchildren( tag, |args| node_app(perms_range.clone(), args), |args| err_handler(perms_range.clone(), args),