Auto merge of #3833 - JoJoDeveloping:tb-fix-stack-overflow, r=RalfJung · rust-lang/rust@8b10bda (original) (raw)

`@@ -10,7 +10,7 @@

`

10

10

`//! and the relative position of the access;

`

11

11

`` //! - idempotency properties asserted in perms.rs (for optimizations)

``

12

12

``

13

``

`-

use std::fmt;

`

``

13

`+

use std::{fmt, mem};

`

14

14

``

15

15

`use smallvec::SmallVec;

`

16

16

``

`@@ -699,18 +699,24 @@ impl<'tcx> Tree {

`

699

699

`/// Integration with the BorTag garbage collector

`

700

700

`impl Tree {

`

701

701

`pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet) {

`

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);

`

704

703

`// Right after the GC runs is a good moment to check if we can

`

705

704

`// merge some adjacent ranges that were made equal by the removal of some

`

706

705

`// tags (this does not necessarily mean that they have identical internal representations,

`

707

706

`` // see the PartialEq impl for UniValMap)

``

708

707

`self.rperms.merge_adjacent_thorough();

`

709

708

`}

`

710

709

``

``

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) -> bool {

`

``

713

`+

let node = self.nodes.get(idx).unwrap();

`

``

714

`+

node.children.is_empty() && !live.contains(&node.tag)

`

``

715

`+

}

`

``

716

+

711

717

`/// 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.

`

714

720

`///

`

715

721

`/// NOTE: This leaves in the middle of the tree tags that are unreachable but have

`

716

722

`/// reachable children. There is a potential for compacting the tree by reassigning

`

`@@ -721,42 +727,60 @@ impl Tree {

`

721

727

`` /// child: Reserved. This tree can exist. If we blindly delete parent and reassign

``

722

728

`` /// child to be a direct child of root then Writes to child are now permitted

``

723

729

`` /// whereas they were not when parent was still there.

``

724

``

`-

fn keep_only_needed(&mut self, idx: UniIndex, live: &FxHashSet) -> 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) {

`

``

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;

`

753

783

`}

`

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

`

760

784

`}

`

761

785

`}

`

762

786

`}

`