blob: c079146edbf42bbe59989fca939aa519fa374662 [file] [log] [blame]
// 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.
//! The `ObligationForest` is a utility data structure used in trait
//! matching to track the set of outstanding obligations (those not
//! yet resolved to success or error). It also tracks the "backtrace"
//! of each pending obligation (why we are trying to figure this out
//! in the first place). See README.md for a general overview of how
//! to use this class.
use fnv::{FnvHashMap, FnvHashSet};
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash;
use std::marker::PhantomData;
mod node_index;
use self::node_index::NodeIndex;
#[cfg(test)]
mod test;
pub trait ForestObligation : Clone + Debug {
type Predicate : Clone + hash::Hash + Eq + Debug;
fn as_predicate(&self) -> &Self::Predicate;
}
pub trait ObligationProcessor {
type Obligation : ForestObligation;
type Error : Debug;
fn process_obligation(&mut self,
obligation: &mut Self::Obligation)
-> Result<Option<Vec<Self::Obligation>>, Self::Error>;
fn process_backedge<'c, I>(&mut self, cycle: I,
_marker: PhantomData<&'c Self::Obligation>)
where I: Clone + Iterator<Item=&'c Self::Obligation>;
}
struct SnapshotData {
node_len: usize,
cache_list_len: usize,
}
pub struct ObligationForest<O: ForestObligation> {
/// 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
/// incomplete children). During processing, some of those nodes
/// may be changed to the error state, or we may find that they
/// are completed (That is, `num_incomplete_children` drops to 0).
/// At the end of processing, those nodes will be removed by a
/// call to `compress`.
///
/// At all times we maintain the invariant that every node appears
/// at a higher index than its parent. This is needed by the
/// backtrace iterator (which uses `split_at`).
nodes: Vec<Node<O>>,
/// A cache of predicates that have been successfully completed.
done_cache: FnvHashSet<O::Predicate>,
/// An cache of the nodes in `nodes`, indexed by predicate.
waiting_cache: FnvHashMap<O::Predicate, NodeIndex>,
/// A list of the obligations added in snapshots, to allow
/// for their removal.
cache_list: Vec<O::Predicate>,
snapshots: Vec<SnapshotData>,
scratch: Option<Vec<usize>>,
}
pub struct Snapshot {
len: usize,
}
#[derive(Debug)]
struct Node<O> {
obligation: O,
state: Cell<NodeState>,
/// Obligations that depend on this obligation for their
/// completion. They must all be in a non-pending state.
dependents: Vec<NodeIndex>,
/// The parent of a node - the original obligation of
/// which it is a subobligation. Except for error reporting,
/// this is just another member of `dependents`.
parent: Option<NodeIndex>,
}
/// The state of one node in some tree within the forest. This
/// represents the current state of processing for the obligation (of
/// type `O`) associated with this node.
///
/// Outside of ObligationForest methods, nodes should be either Pending
/// or Waiting.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum NodeState {
/// Obligations for which selection had not yet returned a
/// non-ambiguous result.
Pending,
/// This obligation was selected successfuly, but may or
/// may not have subobligations.
Success,
/// This obligation was selected sucessfully, but it has
/// a pending subobligation.
Waiting,
/// This obligation, along with its subobligations, are complete,
/// and will be removed in the next collection.
Done,
/// This obligation was resolved to an error. Error nodes are
/// removed from the vector by the compression step.
Error,
/// This is a temporary state used in DFS loops to detect cycles,
/// it should not exist outside of these DFSes.
OnDfsStack,
}
#[derive(Debug)]
pub struct Outcome<O, E> {
/// Obligations that were completely evaluated, including all
/// (transitive) subobligations.
pub completed: Vec<O>,
/// Backtrace of obligations that were found to be in error.
pub errors: Vec<Error<O, E>>,
/// If true, then we saw no successful obligations, which means
/// there is no point in further iteration. This is based on the
/// assumption that when trait matching returns `Err` or
/// `Ok(None)`, those results do not affect environmental
/// inference state. (Note that if we invoke `process_obligations`
/// with no pending obligations, stalled will be true.)
pub stalled: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Error<O, E> {
pub error: E,
pub backtrace: Vec<O>,
}
impl<O: ForestObligation> ObligationForest<O> {
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],
snapshots: vec![],
done_cache: FnvHashSet(),
waiting_cache: FnvHashMap(),
cache_list: vec![],
scratch: Some(vec![]),
}
}
/// Return the total number of nodes in the forest that have not
/// yet been fully resolved.
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn start_snapshot(&mut self) -> Snapshot {
self.snapshots.push(SnapshotData {
node_len: self.nodes.len(),
cache_list_len: self.cache_list.len()
});
Snapshot { len: self.snapshots.len() }
}
pub fn commit_snapshot(&mut self, snapshot: Snapshot) {
assert_eq!(snapshot.len, self.snapshots.len());
let info = self.snapshots.pop().unwrap();
assert!(self.nodes.len() >= info.node_len);
assert!(self.cache_list.len() >= info.cache_list_len);
}
pub fn rollback_snapshot(&mut self, snapshot: Snapshot) {
// Check that we are obeying stack discipline.
assert_eq!(snapshot.len, self.snapshots.len());
let info = self.snapshots.pop().unwrap();
for entry in &self.cache_list[info.cache_list_len..] {
self.done_cache.remove(entry);
self.waiting_cache.remove(entry);
}
self.nodes.truncate(info.node_len);
self.cache_list.truncate(info.cache_list_len);
}
pub fn in_snapshot(&self) -> bool {
!self.snapshots.is_empty()
}
/// Registers an obligation
///
/// This CAN be done in a snapshot
pub fn register_obligation(&mut self, obligation: O) {
// Ignore errors here - there is no guarantee of success.
let _ = self.register_obligation_at(obligation, None);
}
// returns Err(()) if we already know this obligation failed.
fn register_obligation_at(&mut self, obligation: O, parent: Option<NodeIndex>)
-> Result<(), ()>
{
if self.done_cache.contains(obligation.as_predicate()) {
return Ok(())
}
match self.waiting_cache.entry(obligation.as_predicate().clone()) {
Entry::Occupied(o) => {
debug!("register_obligation_at({:?}, {:?}) - duplicate of {:?}!",
obligation, parent, o.get());
if let Some(parent) = parent {
if self.nodes[o.get().get()].dependents.contains(&parent) {
debug!("register_obligation_at({:?}, {:?}) - duplicate subobligation",
obligation, parent);
} else {
self.nodes[o.get().get()].dependents.push(parent);
}
}
if let NodeState::Error = self.nodes[o.get().get()].state.get() {
Err(())
} else {
Ok(())
}
}
Entry::Vacant(v) => {
debug!("register_obligation_at({:?}, {:?}) - ok",
obligation, parent);
v.insert(NodeIndex::new(self.nodes.len()));
self.cache_list.push(obligation.as_predicate().clone());
self.nodes.push(Node::new(parent, obligation));
Ok(())
}
}
}
/// Convert all remaining obligations to the given error.
///
/// This cannot be done during a snapshot.
pub fn to_errors<E: Clone>(&mut self, error: E) -> Vec<Error<O, E>> {
assert!(!self.in_snapshot());
let mut errors = vec![];
for index in 0..self.nodes.len() {
if let NodeState::Pending = self.nodes[index].state.get() {
let backtrace = self.error_at(index);
errors.push(Error {
error: error.clone(),
backtrace: backtrace,
});
}
}
let successful_obligations = self.compress();
assert!(successful_obligations.is_empty());
errors
}
/// Returns the set of obligations that are in a pending state.
pub fn pending_obligations(&self) -> Vec<O>
where O: Clone
{
self.nodes
.iter()
.filter(|n| n.state.get() == NodeState::Pending)
.map(|n| n.obligation.clone())
.collect()
}
/// Perform a pass through the obligation list. This must
/// be called in a loop until `outcome.stalled` is false.
///
/// This CANNOT be unrolled (presently, at least).
pub fn process_obligations<P>(&mut self, processor: &mut P) -> Outcome<O, P::Error>
where P: ObligationProcessor<Obligation=O>
{
debug!("process_obligations(len={})", self.nodes.len());
assert!(!self.in_snapshot()); // cannot unroll this action
let mut errors = vec![];
let mut stalled = true;
for index in 0..self.nodes.len() {
debug!("process_obligations: node {} == {:?}",
index,
self.nodes[index]);
let result = match self.nodes[index] {
Node { state: ref _state, ref mut obligation, .. }
if _state.get() == NodeState::Pending =>
{
processor.process_obligation(obligation)
}
_ => continue
};
debug!("process_obligations: node {} got result {:?}",
index,
result);
match result {
Ok(None) => {
// no change in state
}
Ok(Some(children)) => {
// if we saw a Some(_) result, we are not (yet) stalled
stalled = false;
self.nodes[index].state.set(NodeState::Success);
for child in children {
let st = self.register_obligation_at(
child,
Some(NodeIndex::new(index))
);
if let Err(()) = st {
// error already reported - propagate it
// to our node.
self.error_at(index);
}
}
}
Err(err) => {
let backtrace = self.error_at(index);
errors.push(Error {
error: err,
backtrace: backtrace,
});
}
}
}
self.mark_as_waiting();
self.process_cycles(processor);
// Now we have to compress the result
let completed_obligations = self.compress();
debug!("process_obligations: complete");
Outcome {
completed: completed_obligations,
errors: errors,
stalled: stalled,
}
}
/// Mark all NodeState::Success nodes as NodeState::Done and
/// report all cycles between them. This should be called
/// after `mark_as_waiting` marks all nodes with pending
/// subobligations as NodeState::Waiting.
fn process_cycles<P>(&mut self, processor: &mut P)
where P: ObligationProcessor<Obligation=O>
{
let mut stack = self.scratch.take().unwrap();
for node in 0..self.nodes.len() {
self.find_cycles_from_node(&mut stack, processor, node);
}
self.scratch = Some(stack);
}
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>,
processor: &mut P, index: usize)
where P: ObligationProcessor<Obligation=O>
{
let node = &self.nodes[index];
let state = node.state.get();
match state {
NodeState::OnDfsStack => {
let index =
stack.iter().rposition(|n| *n == index).unwrap();
// I need a Clone closure
#[derive(Clone)]
struct GetObligation<'a, O: 'a>(&'a [Node<O>]);
impl<'a, 'b, O> FnOnce<(&'b usize,)> for GetObligation<'a, O> {
type Output = &'a O;
extern "rust-call" fn call_once(self, args: (&'b usize,)) -> &'a O {
&self.0[*args.0].obligation
}
}
impl<'a, 'b, O> FnMut<(&'b usize,)> for GetObligation<'a, O> {
extern "rust-call" fn call_mut(&mut self, args: (&'b usize,)) -> &'a O {
&self.0[*args.0].obligation
}
}
processor.process_backedge(stack[index..].iter().map(GetObligation(&self.nodes)),
PhantomData);
}
NodeState::Success => {
node.state.set(NodeState::OnDfsStack);
stack.push(index);
if let Some(parent) = node.parent {
self.find_cycles_from_node(stack, processor, parent.get());
}
for dependent in &node.dependents {
self.find_cycles_from_node(stack, processor, dependent.get());
}
stack.pop();
node.state.set(NodeState::Done);
},
NodeState::Waiting | NodeState::Pending => {
// this node is still reachable from some pending node. We
// will get to it when they are all processed.
}
NodeState::Done | NodeState::Error => {
// already processed that node
}
};
}
/// Returns a vector of obligations for `p` and all of its
/// ancestors, putting them into the error state in the process.
fn error_at(&mut self, p: usize) -> Vec<O> {
let mut error_stack = self.scratch.take().unwrap();
let mut trace = vec![];
let mut n = p;
loop {
self.nodes[n].state.set(NodeState::Error);
trace.push(self.nodes[n].obligation.clone());
error_stack.extend(self.nodes[n].dependents.iter().map(|x| x.get()));
// loop to the parent
match self.nodes[n].parent {
Some(q) => n = q.get(),
None => break
}
}
loop {
// non-standard `while let` to bypass #6393
let i = match error_stack.pop() {
Some(i) => i,
None => break
};
let node = &self.nodes[i];
match node.state.get() {
NodeState::Error => continue,
_ => node.state.set(NodeState::Error)
}
error_stack.extend(
node.dependents.iter().cloned().chain(node.parent).map(|x| x.get())
);
}
self.scratch = Some(error_stack);
trace
}
/// Marks all nodes that depend on a pending node as NodeState;:Waiting.
fn mark_as_waiting(&self) {
for node in &self.nodes {
if node.state.get() == NodeState::Waiting {
node.state.set(NodeState::Success);
}
}
for node in &self.nodes {
if node.state.get() == NodeState::Pending {
self.mark_as_waiting_from(node)
}
}
}
fn mark_as_waiting_from(&self, node: &Node<O>) {
match node.state.get() {
NodeState::Pending | NodeState::Done => {},
NodeState::Waiting | NodeState::Error | NodeState::OnDfsStack => return,
NodeState::Success => {
node.state.set(NodeState::Waiting);
}
}
if let Some(parent) = node.parent {
self.mark_as_waiting_from(&self.nodes[parent.get()]);
}
for dependent in &node.dependents {
self.mark_as_waiting_from(&self.nodes[dependent.get()]);
}
}
/// Compresses the vector, removing all popped nodes. This adjusts
/// the indices and hence invalidates any outstanding
/// indices. Cannot be used during a transaction.
///
/// Beforehand, all nodes must be marked as `Done` and no cycles
/// on these nodes may be present. This is done by e.g. `process_cycles`.
#[inline(never)]
fn compress(&mut self) -> Vec<O> {
assert!(!self.in_snapshot()); // didn't write code to unroll this action
let nodes_len = self.nodes.len();
let mut node_rewrites: Vec<_> = self.scratch.take().unwrap();
node_rewrites.extend(0..nodes_len);
let mut dead_nodes = 0;
// Now move all popped nodes to the end. Try to keep the order.
//
// LOOP INVARIANT:
// self.nodes[0..i - dead_nodes] are the first remaining nodes
// self.nodes[i - dead_nodes..i] are all dead
// self.nodes[i..] are unchanged
for i in 0..self.nodes.len() {
match self.nodes[i].state.get() {
NodeState::Done => {
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
// FIXME(HashMap): why can't I get my key back?
self.done_cache.insert(self.nodes[i].obligation.as_predicate().clone());
}
NodeState::Error => {
// We *intentionally* remove the node from the cache at this point. Otherwise
// tests must come up with a different type on every type error they
// check against.
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
}
_ => {}
}
if self.nodes[i].is_popped() {
node_rewrites[i] = nodes_len;
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 {
node_rewrites.truncate(0);
self.scratch = Some(node_rewrites);
return vec![];
}
// Pop off all the nodes we killed and extract the success
// stories.
let successful = (0..dead_nodes)
.map(|_| self.nodes.pop().unwrap())
.flat_map(|node| {
match node.state.get() {
NodeState::Error => None,
NodeState::Done => Some(node.obligation),
_ => unreachable!()
}
})
.collect();
self.apply_rewrites(&node_rewrites);
node_rewrites.truncate(0);
self.scratch = Some(node_rewrites);
successful
}
fn apply_rewrites(&mut self, node_rewrites: &[usize]) {
let nodes_len = node_rewrites.len();
for node in &mut self.nodes {
if let Some(index) = node.parent {
let new_index = node_rewrites[index.get()];
if new_index >= nodes_len {
// parent dead due to error
node.parent = None;
} else {
node.parent = Some(NodeIndex::new(new_index));
}
}
let mut i = 0;
while i < node.dependents.len() {
let new_index = node_rewrites[node.dependents[i].get()];
if new_index >= nodes_len {
node.dependents.swap_remove(i);
} else {
node.dependents[i] = NodeIndex::new(new_index);
i += 1;
}
}
}
let mut kill_list = vec![];
for (predicate, index) in self.waiting_cache.iter_mut() {
let new_index = node_rewrites[index.get()];
if new_index >= nodes_len {
kill_list.push(predicate.clone());
} else {
*index = NodeIndex::new(new_index);
}
}
for predicate in kill_list { self.waiting_cache.remove(&predicate); }
}
}
impl<O> Node<O> {
fn new(parent: Option<NodeIndex>, obligation: O) -> Node<O> {
Node {
obligation: obligation,
parent: parent,
state: Cell::new(NodeState::Pending),
dependents: vec![],
}
}
fn is_popped(&self) -> bool {
match self.state.get() {
NodeState::Pending | NodeState::Waiting => false,
NodeState::Error | NodeState::Done => true,
NodeState::OnDfsStack | NodeState::Success => unreachable!()
}
}
}