]> git.lizzy.rs Git - rust.git/commitdiff
Add a notion of "per-tree" state
authorNiko Matsakis <niko@alum.mit.edu>
Mon, 1 Feb 2016 17:57:44 +0000 (12:57 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 1 Feb 2016 17:59:49 +0000 (12:59 -0500)
src/librustc/middle/traits/fulfill.rs
src/librustc_data_structures/obligation_forest/README.md
src/librustc_data_structures/obligation_forest/mod.rs
src/librustc_data_structures/obligation_forest/test.rs
src/librustc_data_structures/obligation_forest/tree_index.rs [new file with mode: 0644]

index d28504db585c745b5a4c582b2a5b08b0cf1f0ae8..a656cb3126de7d0fbcf87121288b9c3ec12992a4 100644 (file)
@@ -66,7 +66,7 @@ pub struct FulfillmentContext<'tcx> {
 
     // A list of all obligations that have been registered with this
     // fulfillment context.
-    predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
+    predicates: ObligationForest<PendingPredicateObligation<'tcx>, ()>,
 
     // A set of constraints that regionck must validate. Each
     // constraint has the form `T:'a`, meaning "some type `T` must
@@ -192,7 +192,7 @@ pub fn register_predicate_obligation<'a>(&mut self,
             obligation: obligation,
             stalled_on: vec![]
         };
-        self.predicates.push_root(obligation);
+        self.predicates.push_tree(obligation, ());
     }
 
     pub fn region_obligations(&self,
@@ -278,10 +278,10 @@ fn select<'a>(&mut self,
             let outcome = {
                 let region_obligations = &mut self.region_obligations;
                 self.predicates.process_obligations(
-                    |obligation, backtrace| process_predicate(selcx,
-                                                              obligation,
-                                                              backtrace,
-                                                              region_obligations))
+                    |obligation, _tree, backtrace| process_predicate(selcx,
+                                                                     obligation,
+                                                                     backtrace,
+                                                                     region_obligations))
             };
 
             debug!("select_where_possible: outcome={:?}", outcome);
@@ -405,7 +405,7 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
         pending_obligation.stalled_on = vec![];
     }
 
-    let obligation = &pending_obligation.obligation;
+    let obligation = &mut pending_obligation.obligation;
 
     // If we exceed the recursion limit, take a moment to look for a
     // cycle so we can give a better error report from here, where we
@@ -417,8 +417,16 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
         }
     }
 
+    if obligation.predicate.has_infer_types() {
+        obligation.predicate = selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate);
+    }
+
     match obligation.predicate {
         ty::Predicate::Trait(ref data) => {
+            if selcx.tcx().fulfilled_predicates.borrow().check_duplicate_trait(data) {
+                return Ok(Some(vec![]));
+            }
+
             if coinductive_match(selcx, obligation, data, &backtrace) {
                 return Ok(Some(vec![]));
             }
@@ -426,9 +434,14 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
             let trait_obligation = obligation.with(data.clone());
             match selcx.select(&trait_obligation) {
                 Ok(Some(vtable)) => {
+                    info!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
+                          data, obligation.recursion_depth);
                     Ok(Some(vtable.nested_obligations()))
                 }
                 Ok(None) => {
+                    info!("selecting trait `{:?}` at depth {} yielded Ok(None)",
+                          data, obligation.recursion_depth);
+
                     // This is a bit subtle: for the most part, the
                     // only reason we can fail to make progress on
                     // trait selection is because we don't have enough
@@ -457,6 +470,8 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
                     Ok(None)
                 }
                 Err(selection_err) => {
+                    info!("selecting trait `{:?}` at depth {} yielded Err",
+                          data, obligation.recursion_depth);
                     Err(CodeSelectionError(selection_err))
                 }
             }
@@ -642,18 +657,28 @@ pub fn new(dep_graph: DepGraph) -> GlobalFulfilledPredicates<'tcx> {
 
     pub fn check_duplicate(&self, key: &ty::Predicate<'tcx>) -> bool {
         if let ty::Predicate::Trait(ref data) = *key {
-            // For the global predicate registry, when we find a match, it
-            // may have been computed by some other task, so we want to
-            // add a read from the node corresponding to the predicate
-            // processing to make sure we get the transitive dependencies.
-            if self.set.contains(data) {
-                debug_assert!(data.is_global());
-                self.dep_graph.read(data.dep_node());
-                return true;
-            }
+            self.check_duplicate_trait(data)
+        } else {
+            false
         }
+    }
+
+    pub fn check_duplicate_trait(&self, data: &ty::PolyTraitPredicate<'tcx>) -> bool {
+        // For the global predicate registry, when we find a match, it
+        // may have been computed by some other task, so we want to
+        // add a read from the node corresponding to the predicate
+        // processing to make sure we get the transitive dependencies.
+        if self.set.contains(data) {
+            debug_assert!(data.is_global());
+            self.dep_graph.read(data.dep_node());
+            debug!("check_duplicate: global predicate `{:?}` already proved elsewhere", data);
+
+            info!("check_duplicate_trait hit: `{:?}`", data);
 
-        return false;
+            true
+        } else {
+            false
+        }
     }
 
     fn add_if_global(&mut self, key: &ty::Predicate<'tcx>) {
@@ -663,7 +688,10 @@ fn add_if_global(&mut self, key: &ty::Predicate<'tcx>) {
             // already has the required read edges, so we don't need
             // to add any more edges here.
             if data.is_global() {
-                self.set.insert(data.clone());
+                if self.set.insert(data.clone()) {
+                    debug!("add_if_global: global predicate `{:?}` added", data);
+                    info!("check_duplicate_trait entry: `{:?}`", data);
+                }
             }
         }
     }
index 1ffe07bb43b4e12565d079c817c94aae20a2cf46..d76d7f6ba340e67c9e563594ed084378ce91cf09 100644 (file)
@@ -9,15 +9,18 @@ place).
 `ObligationForest` supports two main public operations (there are a
 few others not discussed here):
 
-1. Add a new root obligation (`push_root`).
+1. Add a new root obligations (`push_tree`).
 2. Process the pending obligations (`process_obligations`).
 
 When a new obligation `N` is added, it becomes the root of an
-obligation tree. This tree is a singleton to start, so `N` is both the
-root and the only leaf. Each time the `process_obligations` method is
-called, it will invoke its callback with every pending obligation (so
-that will include `N`, the first time). The callback shoud process the
-obligation `O` that it is given and return one of three results:
+obligation tree. This tree can also carry some per-tree state `T`,
+which is given at the same time. This tree is a singleton to start, so
+`N` is both the root and the only leaf. Each time the
+`process_obligations` method is called, it will invoke its callback
+with every pending obligation (so that will include `N`, the first
+time). The callback also receives a (mutable) reference to the
+per-tree state `T`. The callback should process the obligation `O`
+that it is given and return one of three results:
 
 - `Ok(None)` -> ambiguous result. Obligation was neither a success
   nor a failure. It is assumed that further attempts to process the
index 0d92a2b158f82496ac10e5e16edde3ccacf23ae0..25a77adba2820b50e6e0db74913aba76fd32178d 100644 (file)
 use std::mem;
 
 mod node_index;
+use self::node_index::NodeIndex;
+
+mod tree_index;
+use self::tree_index::TreeIndex;
+
 
 #[cfg(test)]
 mod test;
 
-pub struct ObligationForest<O> {
+pub struct ObligationForest<O,T> {
     /// The list of obligations. In between calls to
     /// `process_obligations`, this list only contains nodes in the
     /// `Pending` or `Success` state (with a non-zero number of
@@ -37,6 +42,7 @@ pub struct ObligationForest<O> {
     /// at a higher index than its parent. This is needed by the
     /// backtrace iterator (which uses `split_at`).
     nodes: Vec<Node<O>>,
+    trees: Vec<Tree<T>>,
     snapshots: Vec<usize>
 }
 
@@ -44,12 +50,15 @@ pub struct Snapshot {
     len: usize,
 }
 
-pub use self::node_index::NodeIndex;
+struct Tree<T> {
+    root: NodeIndex,
+    state: T,
+}
 
 struct Node<O> {
     state: NodeState<O>,
     parent: Option<NodeIndex>,
-    root: NodeIndex, // points to the root, which may be the current node
+    tree: TreeIndex,
 }
 
 /// The state of one node in some tree within the forest. This
@@ -99,9 +108,10 @@ pub struct Error<O,E> {
     pub backtrace: Vec<O>,
 }
 
-impl<O: Debug> ObligationForest<O> {
-    pub fn new() -> ObligationForest<O> {
+impl<O: Debug, T: Debug> ObligationForest<O, T> {
+    pub fn new() -> ObligationForest<O, T> {
         ObligationForest {
+            trees: vec![],
             nodes: vec![],
             snapshots: vec![]
         }
@@ -114,30 +124,39 @@ pub fn len(&self) -> usize {
     }
 
     pub fn start_snapshot(&mut self) -> Snapshot {
-        self.snapshots.push(self.nodes.len());
+        self.snapshots.push(self.trees.len());
         Snapshot { len: self.snapshots.len() }
     }
 
     pub fn commit_snapshot(&mut self, snapshot: Snapshot) {
         assert_eq!(snapshot.len, self.snapshots.len());
-        let nodes_len = self.snapshots.pop().unwrap();
-        assert!(self.nodes.len() >= nodes_len);
+        let trees_len = self.snapshots.pop().unwrap();
+        assert!(self.trees.len() >= trees_len);
     }
 
     pub fn rollback_snapshot(&mut self, snapshot: Snapshot) {
         // Check that we are obeying stack discipline.
         assert_eq!(snapshot.len, self.snapshots.len());
-        let nodes_len = self.snapshots.pop().unwrap();
+        let trees_len = self.snapshots.pop().unwrap();
 
-        // The only action permitted while in a snapshot is to push
-        // new root obligations. Because no processing will have been
-        // done, those roots should still be in the pending state.
-        debug_assert!(self.nodes[nodes_len..].iter().all(|n| match n.state {
-            NodeState::Pending { .. } => true,
-            _ => false,
-        }));
+        // If nothing happened in snapshot, done.
+        if self.trees.len() == trees_len {
+            return;
+        }
 
-        self.nodes.truncate(nodes_len);
+        // Find root of first tree; because nothing can happen in a
+        // snapshot but pushing trees, all nodes after that should be
+        // roots of other trees as well
+        let first_root_index = self.trees[trees_len].root.get();
+        debug_assert!(
+            self.nodes[first_root_index..]
+                .iter()
+                .zip(first_root_index..)
+                .all(|(root, root_index)| self.trees[root.tree.get()].root.get() == root_index));
+
+        // Pop off tree/root pairs pushed during snapshot.
+        self.trees.truncate(trees_len);
+        self.nodes.truncate(first_root_index);
     }
 
     pub fn in_snapshot(&self) -> bool {
@@ -147,9 +166,11 @@ pub fn in_snapshot(&self) -> bool {
     /// Adds a new tree to the forest.
     ///
     /// This CAN be done during a snapshot.
-    pub fn push_root(&mut self, obligation: O) {
+    pub fn push_tree(&mut self, obligation: O, tree_state: T) {
         let index = NodeIndex::new(self.nodes.len());
-        self.nodes.push(Node::new(index, None, obligation));
+        let tree = TreeIndex::new(self.trees.len());
+        self.trees.push(Tree { root: index, state: tree_state });
+        self.nodes.push(Node::new(tree, None, obligation));
     }
 
     /// Convert all remaining obligations to the given error.
@@ -186,7 +207,7 @@ pub fn pending_obligations(&self) -> Vec<O> where O: Clone {
     ///
     /// This CANNOT be unrolled (presently, at least).
     pub fn process_obligations<E,F>(&mut self, mut action: F) -> Outcome<O,E>
-        where E: Debug, F: FnMut(&mut O, Backtrace<O>) -> Result<Option<Vec<O>>, E>
+        where E: Debug, F: FnMut(&mut O, &mut T, Backtrace<O>) -> Result<Option<Vec<O>>, E>
     {
         debug!("process_obligations(len={})", self.nodes.len());
         assert!(!self.in_snapshot()); // cannot unroll this action
@@ -210,7 +231,7 @@ pub fn process_obligations<E,F>(&mut self, mut action: F) -> Outcome<O,E>
                    index, self.nodes[index].state);
 
             let result = {
-                let parent = self.nodes[index].parent;
+                let Node { tree, parent, .. } = self.nodes[index];
                 let (prefix, suffix) = self.nodes.split_at_mut(index);
                 let backtrace = Backtrace::new(prefix, parent);
                 match suffix[0].state {
@@ -218,7 +239,7 @@ pub fn process_obligations<E,F>(&mut self, mut action: F) -> Outcome<O,E>
                     NodeState::Success { .. } =>
                         continue,
                     NodeState::Pending { ref mut obligation } =>
-                        action(obligation, backtrace),
+                        action(obligation, &mut self.trees[tree.get()].state, backtrace),
                 }
             };
 
@@ -268,11 +289,11 @@ fn success(&mut self, index: usize, children: Vec<O>) {
             self.update_parent(index);
         } else {
             // create child work
-            let root_index = self.nodes[index].root;
+            let tree_index = self.nodes[index].tree;
             let node_index = NodeIndex::new(index);
             self.nodes.extend(
                 children.into_iter()
-                        .map(|o| Node::new(root_index, Some(node_index), o)));
+                        .map(|o| Node::new(tree_index, Some(node_index), o)));
         }
 
         // change state from `Pending` to `Success`, temporarily swapping in `Error`
@@ -311,8 +332,9 @@ fn update_parent(&mut self, child: usize) {
     /// skip the remaining obligations from a tree once some other
     /// node in the tree is found to be in error.
     fn inherit_error(&mut self, child: usize) {
-        let root = self.nodes[child].root.get();
-        if let NodeState::Error = self.nodes[root].state {
+        let tree = self.nodes[child].tree;
+        let root = self.trees[tree.get()].root;
+        if let NodeState::Error = self.nodes[root.get()].state {
             self.nodes[child].state = NodeState::Error;
         }
     }
@@ -353,7 +375,8 @@ fn backtrace(&mut self, mut p: usize) -> Vec<O> {
     /// indices. Cannot be used during a transaction.
     fn compress(&mut self) -> Vec<O> {
         assert!(!self.in_snapshot()); // didn't write code to unroll this action
-        let mut rewrites: Vec<_> = (0..self.nodes.len()).collect();
+        let mut node_rewrites: Vec<_> = (0..self.nodes.len()).collect();
+        let mut tree_rewrites: Vec<_> = (0..self.trees.len()).collect();
 
         // Finish propagating error state. Note that in this case we
         // only have to check immediate parents, rather than all
@@ -366,43 +389,69 @@ fn compress(&mut self) -> Vec<O> {
             }
         }
 
+        // Determine which trees to remove by checking if their root
+        // is popped.
+        let mut dead_trees = 0;
+        let trees_len = self.trees.len();
+        for i in 0..trees_len {
+            let root_node = self.trees[i].root;
+            if self.nodes[root_node.get()].is_popped() {
+                dead_trees += 1;
+            } else if dead_trees > 0 {
+                self.trees.swap(i, i - dead_trees);
+                tree_rewrites[i] -= dead_trees;
+            }
+        }
+
         // Now go through and move all nodes that are either
         // successful or which have an error over into to the end of
         // the list, preserving the relative order of the survivors
         // (which is important for the `inherit_error` logic).
-        let mut dead = 0;
+        let mut dead_nodes = 0;
         for i in 0..nodes_len {
             if self.nodes[i].is_popped() {
-                dead += 1;
-            } else if dead > 0 {
-                self.nodes.swap(i, i - dead);
-                rewrites[i] -= dead;
+                dead_nodes += 1;
+            } else if dead_nodes > 0 {
+                self.nodes.swap(i, i - dead_nodes);
+                node_rewrites[i] -= dead_nodes;
             }
         }
 
+        // No compression needed.
+        if dead_nodes == 0 && dead_trees == 0 {
+            return vec![];
+        }
+
+        // Pop off the trees we killed.
+        self.trees.truncate(trees_len - dead_trees);
+
         // Pop off all the nodes we killed and extract the success
         // stories.
         let successful =
-            (0 .. dead).map(|_| self.nodes.pop().unwrap())
-                       .flat_map(|node| match node.state {
-                           NodeState::Error => None,
-                           NodeState::Pending { .. } => unreachable!(),
-                           NodeState::Success { obligation, num_incomplete_children } => {
-                               assert_eq!(num_incomplete_children, 0);
-                               Some(obligation)
-                           }
-                       })
-                       .collect();
-
-        // Adjust the parent indices, since we compressed things.
+            (0 .. dead_nodes)
+            .map(|_| self.nodes.pop().unwrap())
+            .flat_map(|node| match node.state {
+                NodeState::Error => None,
+                NodeState::Pending { .. } => unreachable!(),
+                NodeState::Success { obligation, num_incomplete_children } => {
+                    assert_eq!(num_incomplete_children, 0);
+                    Some(obligation)
+                }
+            })
+            .collect();
+
+        // Adjust the various indices, since we compressed things.
+        for tree in &mut self.trees {
+            tree.root = NodeIndex::new(node_rewrites[tree.root.get()]);
+        }
         for node in &mut self.nodes {
             if let Some(ref mut index) = node.parent {
-                let new_index = rewrites[index.get()];
-                debug_assert!(new_index < (nodes_len - dead));
+                let new_index = node_rewrites[index.get()];
+                debug_assert!(new_index < (nodes_len - dead_nodes));
                 *index = NodeIndex::new(new_index);
             }
 
-            node.root = NodeIndex::new(rewrites[node.root.get()]);
+            node.tree = TreeIndex::new(tree_rewrites[node.tree.get()]);
         }
 
         successful
@@ -410,11 +459,11 @@ fn compress(&mut self) -> Vec<O> {
 }
 
 impl<O> Node<O> {
-    fn new(root: NodeIndex, parent: Option<NodeIndex>, obligation: O) -> Node<O> {
+    fn new(tree: TreeIndex, parent: Option<NodeIndex>, obligation: O) -> Node<O> {
         Node {
             parent: parent,
             state: NodeState::Pending { obligation: obligation },
-            root: root
+            tree: tree,
         }
     }
 
index 519b282a6a8c7a0f523a1ea81a9886e50572bb6d..9a0a4218d4593d662ad1cee5b6217366abf58ab7 100644 (file)
 #[test]
 fn push_pop() {
     let mut forest = ObligationForest::new();
-    forest.push_root("A");
-    forest.push_root("B");
-    forest.push_root("C");
+    forest.push_tree("A", "A");
+    forest.push_tree("B", "B");
+    forest.push_tree("C", "C");
 
     // first round, B errors out, A has subtasks, and C completes, creating this:
     //      A |-> A.1
     //        |-> A.2
     //        |-> A.3
-    let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
-        match *obligation {
-            "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
-            "B" => Err("B is for broken"),
-            "C" => Ok(Some(vec![])),
-            _ => unreachable!(),
-        }
-    });
+    let Outcome { completed: ok, errors: err, .. } =
+        forest.process_obligations(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
+            match *obligation {
+                "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
+                "B" => Err("B is for broken"),
+                "C" => Ok(Some(vec![])),
+                _ => unreachable!(),
+            }
+        });
     assert_eq!(ok, vec!["C"]);
     assert_eq!(err, vec![Error {error: "B is for broken",
                                 backtrace: vec!["B"]}]);
@@ -39,9 +41,10 @@ fn push_pop() {
     //        |-> A.3 |-> A.3.i
     //      D |-> D.1
     //        |-> D.2
-    forest.push_root("D");
+    forest.push_tree("D", "D");
     let Outcome { completed: ok, errors: err, .. }: Outcome<&'static str, ()> =
-        forest.process_obligations(|obligation, _| {
+        forest.process_obligations(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A.1" => Ok(None),
                 "A.2" => Ok(None),
@@ -58,26 +61,30 @@ fn push_pop() {
     // propagates to A.3.i, but not D.1 or D.2.
     //      D |-> D.1 |-> D.1.i
     //        |-> D.2 |-> D.2.i
-    let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
-        match *obligation {
-            "A.1" => Ok(Some(vec![])),
-            "A.2" => Err("A is for apple"),
-            "D.1" => Ok(Some(vec!["D.1.i"])),
-            "D.2" => Ok(Some(vec!["D.2.i"])),
-            _ => unreachable!(),
-        }
-    });
+    let Outcome { completed: ok, errors: err, .. } =
+        forest.process_obligations(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
+            match *obligation {
+                "A.1" => Ok(Some(vec![])),
+                "A.2" => Err("A is for apple"),
+                "D.1" => Ok(Some(vec!["D.1.i"])),
+                "D.2" => Ok(Some(vec!["D.2.i"])),
+                _ => unreachable!(),
+            }
+        });
     assert_eq!(ok, vec!["A.1"]);
     assert_eq!(err, vec![Error { error: "A is for apple",
                                  backtrace: vec!["A.2", "A"] }]);
 
     // fourth round: error in D.1.i that should propagate to D.2.i
-    let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
-        match *obligation {
-            "D.1.i" => Err("D is for dumb"),
-            _ => panic!("unexpected obligation {:?}", obligation),
-        }
-    });
+    let Outcome { completed: ok, errors: err, .. } =
+        forest.process_obligations(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
+            match *obligation {
+                "D.1.i" => Err("D is for dumb"),
+                _ => panic!("unexpected obligation {:?}", obligation),
+            }
+        });
     assert_eq!(ok, Vec::<&'static str>::new());
     assert_eq!(err, vec![Error { error: "D is for dumb",
                                  backtrace: vec!["D.1.i", "D.1", "D"] }]);
@@ -94,10 +101,11 @@ fn push_pop() {
 #[test]
 fn success_in_grandchildren() {
     let mut forest = ObligationForest::new();
-    forest.push_root("A");
+    forest.push_tree("A", "A");
 
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, _| {
+        forest.process_obligations::<(),_>(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
                 _ => unreachable!(),
@@ -107,7 +115,8 @@ fn success_in_grandchildren() {
     assert!(err.is_empty());
 
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, _| {
+        forest.process_obligations::<(),_>(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A.1" => Ok(Some(vec![])),
                 "A.2" => Ok(Some(vec!["A.2.i", "A.2.ii"])),
@@ -119,7 +128,8 @@ fn success_in_grandchildren() {
     assert!(err.is_empty());
 
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, _| {
+        forest.process_obligations::<(),_>(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A.2.i" => Ok(Some(vec!["A.2.i.a"])),
                 "A.2.ii" => Ok(Some(vec![])),
@@ -130,7 +140,8 @@ fn success_in_grandchildren() {
     assert!(err.is_empty());
 
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, _| {
+        forest.process_obligations::<(),_>(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A.2.i.a" => Ok(Some(vec![])),
                 _ => unreachable!(),
@@ -140,7 +151,7 @@ fn success_in_grandchildren() {
     assert!(err.is_empty());
 
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|_, _| unreachable!());
+        forest.process_obligations::<(),_>(|_, _, _| unreachable!());
     assert!(ok.is_empty());
     assert!(err.is_empty());
 }
@@ -150,9 +161,10 @@ fn to_errors_no_throw() {
     // check that converting multiple children with common parent (A)
     // only yields one of them (and does not panic, in particular).
     let mut forest = ObligationForest::new();
-    forest.push_root("A");
+    forest.push_tree("A", "A");
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, _| {
+        forest.process_obligations::<(),_>(|obligation, tree, _| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             match *obligation {
                 "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
                 _ => unreachable!(),
@@ -168,10 +180,11 @@ fn to_errors_no_throw() {
 fn backtrace() {
     // check that converting multiple children with common parent (A)
     // only yields one of them (and does not panic, in particular).
-    let mut forest: ObligationForest<&'static str> = ObligationForest::new();
-    forest.push_root("A");
+    let mut forest = ObligationForest::new();
+    forest.push_tree("A", "A");
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, mut backtrace| {
+        forest.process_obligations::<(),_>(|obligation, tree, mut backtrace| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             assert!(backtrace.next().is_none());
             match *obligation {
                 "A" => Ok(Some(vec!["A.1"])),
@@ -181,7 +194,8 @@ fn backtrace() {
     assert!(ok.is_empty());
     assert!(err.is_empty());
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, mut backtrace| {
+        forest.process_obligations::<(),_>(|obligation, tree, mut backtrace| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             assert!(backtrace.next().unwrap() == &"A");
             assert!(backtrace.next().is_none());
             match *obligation {
@@ -192,7 +206,8 @@ fn backtrace() {
     assert!(ok.is_empty());
     assert!(err.is_empty());
     let Outcome { completed: ok, errors: err, .. } =
-        forest.process_obligations::<(),_>(|obligation, mut backtrace| {
+        forest.process_obligations::<(),_>(|obligation, tree, mut backtrace| {
+            assert_eq!(obligation.chars().next(), tree.chars().next());
             assert!(backtrace.next().unwrap() == &"A.1");
             assert!(backtrace.next().unwrap() == &"A");
             assert!(backtrace.next().is_none());
diff --git a/src/librustc_data_structures/obligation_forest/tree_index.rs b/src/librustc_data_structures/obligation_forest/tree_index.rs
new file mode 100644 (file)
index 0000000..a9f5483
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::u32;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct TreeIndex {
+    index: u32
+}
+
+impl TreeIndex {
+    pub fn new(value: usize) -> TreeIndex {
+        assert!(value < (u32::MAX as usize));
+        TreeIndex { index: value as u32 }
+    }
+
+    pub fn get(self) -> usize {
+        self.index as usize
+    }
+}
+