// 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
obligation: obligation,
stalled_on: vec![]
};
- self.predicates.push_root(obligation);
+ self.predicates.push_tree(obligation, ());
}
pub fn region_obligations(&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);
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
}
}
+ 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![]));
}
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
Ok(None)
}
Err(selection_err) => {
+ info!("selecting trait `{:?}` at depth {} yielded Err",
+ data, obligation.recursion_depth);
Err(CodeSelectionError(selection_err))
}
}
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>) {
// 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);
+ }
}
}
}
`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
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
/// 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>
}
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
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![]
}
}
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 {
/// 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.
///
/// 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
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 {
NodeState::Success { .. } =>
continue,
NodeState::Pending { ref mut obligation } =>
- action(obligation, backtrace),
+ action(obligation, &mut self.trees[tree.get()].state, backtrace),
}
};
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`
/// 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;
}
}
/// 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
}
}
+ // 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
}
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,
}
}
#[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"]}]);
// |-> 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),
// 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"] }]);
#[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!(),
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"])),
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![])),
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!(),
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());
}
// 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!(),
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"])),
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 {
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());
--- /dev/null
+// 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
+ }
+}
+