1010//!   and the relative position of the access; 
1111//! - idempotency properties asserted in `perms.rs` (for optimizations) 
1212
13- use  std:: fmt; 
13+ use  std:: { fmt,  mem } ; 
1414
1515use  smallvec:: SmallVec ; 
1616
@@ -699,18 +699,24 @@ impl<'tcx> Tree {
699699/// Integration with the BorTag garbage collector 
700700impl  Tree  { 
701701    pub  fn  remove_unreachable_tags ( & mut  self ,  live_tags :  & FxHashSet < BorTag > )  { 
702-         let  root_is_needed = self . keep_only_needed ( self . root ,  live_tags) ;  // root can't be removed 
703-         assert ! ( root_is_needed) ; 
702+         self . remove_useless_children ( self . root ,  live_tags) ; 
704703        // Right after the GC runs is a good moment to check if we can 
705704        // merge some adjacent ranges that were made equal by the removal of some 
706705        // tags (this does not necessarily mean that they have identical internal representations, 
707706        // see the `PartialEq` impl for `UniValMap`) 
708707        self . rperms . merge_adjacent_thorough ( ) ; 
709708    } 
710709
710+     /// Checks if a node is useless and should be GC'ed. 
711+      /// A node is useless if it has no children and also the tag is no longer live. 
712+      fn  is_useless ( & self ,  idx :  UniIndex ,  live :  & FxHashSet < BorTag > )  -> bool  { 
713+         let  node = self . nodes . get ( idx) . unwrap ( ) ; 
714+         node. children . is_empty ( )  && !live. contains ( & node. tag ) 
715+     } 
716+ 
711717    /// Traverses the entire tree looking for useless tags. 
712-      /// Returns true iff  the tag it was called on is still live or has live children,  
713-      /// and removes from  the tree all tags that have no live children . 
718+      /// Removes from  the tree all useless child nodes of root.  
719+      /// It will not delete  the root itself . 
714720     /// 
715721     /// NOTE: This leaves in the middle of the tree tags that are unreachable but have 
716722     /// reachable children. There is a potential for compacting the tree by reassigning 
@@ -721,42 +727,60 @@ impl Tree {
721727     /// `child: Reserved`. This tree can exist. If we blindly delete `parent` and reassign 
722728     /// `child` to be a direct child of `root` then Writes to `child` are now permitted 
723729     /// whereas they were not when `parent` was still there. 
724-      fn  keep_only_needed ( & mut  self ,  idx :  UniIndex ,  live :  & FxHashSet < BorTag > )  -> bool  { 
725-         let  node = self . nodes . get ( idx) . unwrap ( ) ; 
726-         // FIXME: this function does a lot of cloning, a 2-pass approach is possibly 
727-         // more efficient. It could consist of 
728-         // 1. traverse the Tree, collect all useless tags in a Vec 
729-         // 2. traverse the Vec, remove all tags previously selected 
730-         // Bench it. 
731-         let  children:  SmallVec < _ >  = node
732-             . children 
733-             . clone ( ) 
734-             . into_iter ( ) 
735-             . filter ( |child| self . keep_only_needed ( * child,  live) ) 
736-             . collect ( ) ; 
737-         let  no_children = children. is_empty ( ) ; 
738-         let  node = self . nodes . get_mut ( idx) . unwrap ( ) ; 
739-         node. children  = children; 
740-         if  !live. contains ( & node. tag )  && no_children { 
741-             // All of the children and this node are unreachable, delete this tag 
742-             // from the tree (the children have already been deleted by recursive 
743-             // calls). 
744-             // Due to the API of UniMap we must absolutely call 
745-             // `UniValMap::remove` for the key of this tag on *all* maps that used it 
746-             // (which are `self.nodes` and every range of `self.rperms`) 
747-             // before we can safely apply `UniValMap::forget` to truly remove 
748-             // the tag from the mapping. 
749-             let  tag = node. tag ; 
750-             self . nodes . remove ( idx) ; 
751-             for  ( _perms_range,  perms)  in  self . rperms . iter_mut_all ( )  { 
752-                 perms. remove ( idx) ; 
730+      fn  remove_useless_children ( & mut  self ,  root :  UniIndex ,  live :  & FxHashSet < BorTag > )  { 
731+         // To avoid stack overflows, we roll our own stack. 
732+         // Each element in the stack consists of the current tag, and the number of the 
733+         // next child to be processed. 
734+ 
735+         // The other functions are written using the `TreeVisitorStack`, but that does not work here 
736+         // since we need to 1) do a post-traversal and 2) remove nodes from the tree. 
737+         // Since we do a post-traversal (by deleting nodes only after handling all children), 
738+         // we also need to be a bit smarter than "pop node, push all children." 
739+         let  mut  stack = vec ! [ ( root,  0 ) ] ; 
740+         while  let  Some ( ( tag,  nth_child) )  = stack. last_mut ( )  { 
741+             let  node = self . nodes . get ( * tag) . unwrap ( ) ; 
742+             if  * nth_child < node. children . len ( )  { 
743+                 // Visit the child by pushing it to the stack. 
744+                 // Also increase `nth_child` so that when we come back to the `tag` node, we 
745+                 // look at the next child. 
746+                 let  next_child = node. children [ * nth_child] ; 
747+                 * nth_child += 1 ; 
748+                 stack. push ( ( next_child,  0 ) ) ; 
749+                 continue ; 
750+             }  else  { 
751+                 // We have processed all children of `node`, so now it is time to process `node` itself. 
752+                 // First, get the current children of `node`. To appease the borrow checker, 
753+                 // we have to temporarily move the list out of the node, and then put the 
754+                 // list of remaining children back in. 
755+                 let  mut  children_of_node =
756+                     mem:: take ( & mut  self . nodes . get_mut ( * tag) . unwrap ( ) . children ) ; 
757+                 // Remove all useless children, and save them for later. 
758+                 // The closure needs `&self` and the loop below needs `&mut self`, so we need to `collect` 
759+                 // in to a temporary list. 
760+                 let  to_remove:  Vec < _ >  =
761+                     children_of_node. drain_filter ( |x| self . is_useless ( * x,  live) ) . collect ( ) ; 
762+                 // Put back the now-filtered vector. 
763+                 self . nodes . get_mut ( * tag) . unwrap ( ) . children  = children_of_node; 
764+                 // Now, all that is left is unregistering the children saved in `to_remove`. 
765+                 for  idx in  to_remove { 
766+                     // Note: In the rest of this comment, "this node" refers to `idx`. 
767+                     // This node has no more children (if there were any, they have already been removed). 
768+                     // It is also unreachable as determined by the GC, so we can remove it everywhere. 
769+                     // Due to the API of UniMap we must make sure to call 
770+                     // `UniValMap::remove` for the key of this node on *all* maps that used it 
771+                     // (which are `self.nodes` and every range of `self.rperms`) 
772+                     // before we can safely apply `UniKeyMap::remove` to truly remove 
773+                     // this tag from the `tag_mapping`. 
774+                     let  node = self . nodes . remove ( idx) . unwrap ( ) ; 
775+                     for  ( _perms_range,  perms)  in  self . rperms . iter_mut_all ( )  { 
776+                         perms. remove ( idx) ; 
777+                     } 
778+                     self . tag_mapping . remove ( & node. tag ) ; 
779+                 } 
780+                 // We are done, the parent can continue. 
781+                 stack. pop ( ) ; 
782+                 continue ; 
753783            } 
754-             self . tag_mapping . remove ( & tag) ; 
755-             // The tag has been deleted, inform the caller 
756-             false 
757-         }  else  { 
758-             // The tag is still live or has live children, it must be kept 
759-             true 
760784        } 
761785    } 
762786} 
0 commit comments