Auto merge of #77908 - bugadani:obl-forest, r=nnethercote

Try to make ObligationForest more efficient

This PR tries to decrease the number of allocations in ObligationForest, as well as moves some cold path code to an uninlined function.
This commit is contained in:
bors 2020-10-19 15:14:15 +00:00
commit f90e617305
3 changed files with 329 additions and 356 deletions

View File

@ -149,8 +149,8 @@ pub struct ObligationForest<O: ForestObligation> {
/// comments in `process_obligation` for details. /// comments in `process_obligation` for details.
active_cache: FxHashMap<O::CacheKey, usize>, active_cache: FxHashMap<O::CacheKey, usize>,
/// A vector reused in compress(), to avoid allocating new vectors. /// A vector reused in compress() and find_cycles_from_node(), to avoid allocating new vectors.
node_rewrites: Vec<usize>, reused_node_vec: Vec<usize>,
obligation_tree_id_generator: ObligationTreeIdGenerator, obligation_tree_id_generator: ObligationTreeIdGenerator,
@ -251,12 +251,22 @@ enum NodeState {
Error, Error,
} }
/// This trait allows us to have two different Outcome types:
/// - the normal one that does as little as possible
/// - one for tests that does some additional work and checking
pub trait OutcomeTrait {
type Error;
type Obligation;
fn new() -> Self;
fn mark_not_stalled(&mut self);
fn is_stalled(&self) -> bool;
fn record_completed(&mut self, outcome: &Self::Obligation);
fn record_error(&mut self, error: Self::Error);
}
#[derive(Debug)] #[derive(Debug)]
pub struct Outcome<O, E> { pub struct Outcome<O, E> {
/// Obligations that were completely evaluated, including all
/// (transitive) subobligations. Only computed if requested.
pub completed: Option<Vec<O>>,
/// Backtrace of obligations that were found to be in error. /// Backtrace of obligations that were found to be in error.
pub errors: Vec<Error<O, E>>, pub errors: Vec<Error<O, E>>,
@ -269,12 +279,29 @@ pub struct Outcome<O, E> {
pub stalled: bool, pub stalled: bool,
} }
/// Should `process_obligations` compute the `Outcome::completed` field of its impl<O, E> OutcomeTrait for Outcome<O, E> {
/// result? type Error = Error<O, E>;
#[derive(PartialEq)] type Obligation = O;
pub enum DoCompleted {
No, fn new() -> Self {
Yes, Self { stalled: true, errors: vec![] }
}
fn mark_not_stalled(&mut self) {
self.stalled = false;
}
fn is_stalled(&self) -> bool {
self.stalled
}
fn record_completed(&mut self, _outcome: &Self::Obligation) {
// do nothing
}
fn record_error(&mut self, error: Self::Error) {
self.errors.push(error)
}
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -289,7 +316,7 @@ impl<O: ForestObligation> ObligationForest<O> {
nodes: vec![], nodes: vec![],
done_cache: Default::default(), done_cache: Default::default(),
active_cache: Default::default(), active_cache: Default::default(),
node_rewrites: vec![], reused_node_vec: vec![],
obligation_tree_id_generator: (0..).map(ObligationTreeId), obligation_tree_id_generator: (0..).map(ObligationTreeId),
error_cache: Default::default(), error_cache: Default::default(),
} }
@ -363,8 +390,7 @@ impl<O: ForestObligation> ObligationForest<O> {
.map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) }) .map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) })
.collect(); .collect();
let successful_obligations = self.compress(DoCompleted::Yes); self.compress(|_| assert!(false));
assert!(successful_obligations.unwrap().is_empty());
errors errors
} }
@ -392,16 +418,12 @@ impl<O: ForestObligation> ObligationForest<O> {
/// be called in a loop until `outcome.stalled` is false. /// be called in a loop until `outcome.stalled` is false.
/// ///
/// This _cannot_ be unrolled (presently, at least). /// This _cannot_ be unrolled (presently, at least).
pub fn process_obligations<P>( pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
&mut self,
processor: &mut P,
do_completed: DoCompleted,
) -> Outcome<O, P::Error>
where where
P: ObligationProcessor<Obligation = O>, P: ObligationProcessor<Obligation = O>,
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
{ {
let mut errors = vec![]; let mut outcome = OUT::new();
let mut stalled = true;
// Note that the loop body can append new nodes, and those new nodes // Note that the loop body can append new nodes, and those new nodes
// will then be processed by subsequent iterations of the loop. // will then be processed by subsequent iterations of the loop.
@ -429,7 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
} }
ProcessResult::Changed(children) => { ProcessResult::Changed(children) => {
// We are not (yet) stalled. // We are not (yet) stalled.
stalled = false; outcome.mark_not_stalled();
node.state.set(NodeState::Success); node.state.set(NodeState::Success);
for child in children { for child in children {
@ -442,28 +464,22 @@ impl<O: ForestObligation> ObligationForest<O> {
} }
} }
ProcessResult::Error(err) => { ProcessResult::Error(err) => {
stalled = false; outcome.mark_not_stalled();
errors.push(Error { error: err, backtrace: self.error_at(index) }); outcome.record_error(Error { error: err, backtrace: self.error_at(index) });
} }
} }
index += 1; index += 1;
} }
if stalled {
// There's no need to perform marking, cycle processing and compression when nothing // There's no need to perform marking, cycle processing and compression when nothing
// changed. // changed.
return Outcome { if !outcome.is_stalled() {
completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None },
errors,
stalled,
};
}
self.mark_successes(); self.mark_successes();
self.process_cycles(processor); self.process_cycles(processor);
let completed = self.compress(do_completed); self.compress(|obl| outcome.record_completed(obl));
}
Outcome { completed, errors, stalled } outcome
} }
/// Returns a vector of obligations for `p` and all of its /// Returns a vector of obligations for `p` and all of its
@ -526,7 +542,6 @@ impl<O: ForestObligation> ObligationForest<O> {
let node = &self.nodes[index]; let node = &self.nodes[index];
let state = node.state.get(); let state = node.state.get();
if state == NodeState::Success { if state == NodeState::Success {
node.state.set(NodeState::Waiting);
// This call site is cold. // This call site is cold.
self.uninlined_mark_dependents_as_waiting(node); self.uninlined_mark_dependents_as_waiting(node);
} else { } else {
@ -538,17 +553,18 @@ impl<O: ForestObligation> ObligationForest<O> {
// This never-inlined function is for the cold call site. // This never-inlined function is for the cold call site.
#[inline(never)] #[inline(never)]
fn uninlined_mark_dependents_as_waiting(&self, node: &Node<O>) { fn uninlined_mark_dependents_as_waiting(&self, node: &Node<O>) {
// Mark node Waiting in the cold uninlined code instead of the hot inlined
node.state.set(NodeState::Waiting);
self.inlined_mark_dependents_as_waiting(node) self.inlined_mark_dependents_as_waiting(node)
} }
/// Report cycles between all `Success` nodes, and convert all `Success` /// Report cycles between all `Success` nodes, and convert all `Success`
/// nodes to `Done`. This must be called after `mark_successes`. /// nodes to `Done`. This must be called after `mark_successes`.
fn process_cycles<P>(&self, processor: &mut P) fn process_cycles<P>(&mut self, processor: &mut P)
where where
P: ObligationProcessor<Obligation = O>, P: ObligationProcessor<Obligation = O>,
{ {
let mut stack = vec![]; let mut stack = std::mem::take(&mut self.reused_node_vec);
for (index, node) in self.nodes.iter().enumerate() { for (index, node) in self.nodes.iter().enumerate() {
// For some benchmarks this state test is extremely hot. It's a win // For some benchmarks this state test is extremely hot. It's a win
// to handle the no-op cases immediately to avoid the cost of the // to handle the no-op cases immediately to avoid the cost of the
@ -559,6 +575,7 @@ impl<O: ForestObligation> ObligationForest<O> {
} }
debug_assert!(stack.is_empty()); debug_assert!(stack.is_empty());
self.reused_node_vec = stack;
} }
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize) fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
@ -591,13 +608,12 @@ impl<O: ForestObligation> ObligationForest<O> {
/// indices and hence invalidates any outstanding indices. `process_cycles` /// indices and hence invalidates any outstanding indices. `process_cycles`
/// must be run beforehand to remove any cycles on `Success` nodes. /// must be run beforehand to remove any cycles on `Success` nodes.
#[inline(never)] #[inline(never)]
fn compress(&mut self, do_completed: DoCompleted) -> Option<Vec<O>> { fn compress(&mut self, mut outcome_cb: impl FnMut(&O)) {
let orig_nodes_len = self.nodes.len(); let orig_nodes_len = self.nodes.len();
let mut node_rewrites: Vec<_> = std::mem::take(&mut self.node_rewrites); let mut node_rewrites: Vec<_> = std::mem::take(&mut self.reused_node_vec);
debug_assert!(node_rewrites.is_empty()); debug_assert!(node_rewrites.is_empty());
node_rewrites.extend(0..orig_nodes_len); node_rewrites.extend(0..orig_nodes_len);
let mut dead_nodes = 0; let mut dead_nodes = 0;
let mut removed_done_obligations: Vec<O> = vec![];
// Move removable nodes to the end, preserving the order of the // Move removable nodes to the end, preserving the order of the
// remaining nodes. // remaining nodes.
@ -627,10 +643,8 @@ impl<O: ForestObligation> ObligationForest<O> {
} else { } else {
self.done_cache.insert(node.obligation.as_cache_key().clone()); self.done_cache.insert(node.obligation.as_cache_key().clone());
} }
if do_completed == DoCompleted::Yes {
// Extract the success stories. // Extract the success stories.
removed_done_obligations.push(node.obligation.clone()); outcome_cb(&node.obligation);
}
node_rewrites[index] = orig_nodes_len; node_rewrites[index] = orig_nodes_len;
dead_nodes += 1; dead_nodes += 1;
} }
@ -654,9 +668,7 @@ impl<O: ForestObligation> ObligationForest<O> {
} }
node_rewrites.truncate(0); node_rewrites.truncate(0);
self.node_rewrites = node_rewrites; self.reused_node_vec = node_rewrites;
if do_completed == DoCompleted::Yes { Some(removed_done_obligations) } else { None }
} }
fn apply_rewrites(&mut self, node_rewrites: &[usize]) { fn apply_rewrites(&mut self, node_rewrites: &[usize]) {

View File

@ -17,6 +17,40 @@ struct ClosureObligationProcessor<OF, BF, O, E> {
marker: PhantomData<(O, E)>, marker: PhantomData<(O, E)>,
} }
struct TestOutcome<O, E> {
pub completed: Vec<O>,
pub errors: Vec<Error<O, E>>,
pub stalled: bool,
}
impl<O, E> OutcomeTrait for TestOutcome<O, E>
where
O: Clone,
{
type Error = Error<O, E>;
type Obligation = O;
fn new() -> Self {
Self { errors: vec![], stalled: false, completed: vec![] }
}
fn mark_not_stalled(&mut self) {
self.stalled = false;
}
fn is_stalled(&self) -> bool {
self.stalled
}
fn record_completed(&mut self, outcome: &Self::Obligation) {
self.completed.push(outcome.clone())
}
fn record_error(&mut self, error: Self::Error) {
self.errors.push(error)
}
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn C<OF, BF, O>(of: OF, bf: BF) -> ClosureObligationProcessor<OF, BF, O, &'static str> fn C<OF, BF, O>(of: OF, bf: BF) -> ClosureObligationProcessor<OF, BF, O, &'static str>
where where
@ -65,8 +99,7 @@ fn push_pop() {
// A |-> A.1 // A |-> A.1
// |-> A.2 // |-> A.2
// |-> A.3 // |-> A.3
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"B" => ProcessResult::Error("B is for broken"), "B" => ProcessResult::Error("B is for broken"),
@ -75,10 +108,8 @@ fn push_pop() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok, vec!["C"]);
);
assert_eq!(ok.unwrap(), vec!["C"]);
assert_eq!(err, vec![Error { error: "B is for broken", backtrace: vec!["B"] }]); assert_eq!(err, vec![Error { error: "B is for broken", backtrace: vec!["B"] }]);
// second round: two delays, one success, creating an uneven set of subtasks: // second round: two delays, one success, creating an uneven set of subtasks:
@ -88,8 +119,7 @@ fn push_pop() {
// D |-> D.1 // D |-> D.1
// |-> D.2 // |-> D.2
forest.register_obligation("D"); forest.register_obligation("D");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.1" => ProcessResult::Unchanged, "A.1" => ProcessResult::Unchanged,
"A.2" => ProcessResult::Unchanged, "A.2" => ProcessResult::Unchanged,
@ -99,18 +129,15 @@ fn push_pop() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok, Vec::<&'static str>::new());
);
assert_eq!(ok.unwrap(), Vec::<&'static str>::new());
assert_eq!(err, Vec::new()); assert_eq!(err, Vec::new());
// third round: ok in A.1 but trigger an error in A.2. Check that it // third round: ok in A.1 but trigger an error in A.2. Check that it
// propagates to A, but not D.1 or D.2. // propagates to A, but not D.1 or D.2.
// D |-> D.1 |-> D.1.i // D |-> D.1 |-> D.1.i
// |-> D.2 |-> D.2.i // |-> D.2 |-> D.2.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec![]), "A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Error("A is for apple"), "A.2" => ProcessResult::Error("A is for apple"),
@ -121,27 +148,22 @@ fn push_pop() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["A.1", "A.3", "A.3.i"]); assert_eq!(ok, vec!["A.1", "A.3", "A.3.i"]);
assert_eq!(err, vec![Error { error: "A is for apple", backtrace: vec!["A.2", "A"] }]); assert_eq!(err, vec![Error { error: "A is for apple", backtrace: vec!["A.2", "A"] }]);
// fourth round: error in D.1.i // fourth round: error in D.1.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D.1.i" => ProcessResult::Error("D is for dumb"), "D.1.i" => ProcessResult::Error("D is for dumb"),
"D.2.i" => ProcessResult::Changed(vec![]), "D.2.i" => ProcessResult::Changed(vec![]),
_ => panic!("unexpected obligation {:?}", obligation), _ => panic!("unexpected obligation {:?}", obligation),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["D.2", "D.2.i"]); assert_eq!(ok, vec!["D.2", "D.2.i"]);
assert_eq!(err, vec![Error { error: "D is for dumb", backtrace: vec!["D.1.i", "D.1", "D"] }]); assert_eq!(err, vec![Error { error: "D is for dumb", backtrace: vec!["D.1.i", "D.1", "D"] }]);
@ -160,8 +182,7 @@ fn success_in_grandchildren() {
let mut forest = ObligationForest::new(); let mut forest = ObligationForest::new();
forest.register_obligation("A"); forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" => ProcessResult::Changed(vec![]), "A.1" => ProcessResult::Changed(vec![]),
@ -171,61 +192,50 @@ fn success_in_grandchildren() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["A.1", "A.3"]); assert_eq!(ok, vec!["A.1", "A.3"]);
assert!(err.is_empty()); assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.2.i" => ProcessResult::Unchanged, "A.2.i" => ProcessResult::Unchanged,
"A.2.ii" => ProcessResult::Changed(vec![]), "A.2.ii" => ProcessResult::Changed(vec![]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok, vec!["A.2.ii"]);
);
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
assert!(err.is_empty()); assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]), "A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
"A.2.i.a" => ProcessResult::Unchanged, "A.2.i.a" => ProcessResult::Unchanged,
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert!(ok.is_empty());
);
assert!(ok.unwrap().is_empty());
assert!(err.is_empty()); assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.2.i.a" => ProcessResult::Changed(vec![]), "A.2.i.a" => ProcessResult::Changed(vec![]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]); assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]);
assert!(err.is_empty()); assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = let TestOutcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}), DoCompleted::Yes); forest.process_obligations(&mut C(|_| unreachable!(), |_| {}));
assert!(ok.unwrap().is_empty()); assert!(ok.is_empty());
assert!(err.is_empty()); assert!(err.is_empty());
} }
@ -235,18 +245,15 @@ fn to_errors_no_throw() {
// yields to correct errors (and does not panic, in particular). // yields to correct errors (and does not panic, in particular).
let mut forest = ObligationForest::new(); let mut forest = ObligationForest::new();
forest.register_obligation("A"); forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let errors = forest.to_errors(()); let errors = forest.to_errors(());
assert_eq!(errors[0].backtrace, vec!["A.1", "A"]); assert_eq!(errors[0].backtrace, vec!["A.1", "A"]);
@ -260,22 +267,18 @@ fn diamond() {
// check that diamond dependencies are handled correctly // check that diamond dependencies are handled correctly
let mut forest = ObligationForest::new(); let mut forest = ObligationForest::new();
forest.register_obligation("A"); forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]), "A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
"A.1" | "A.2" => ProcessResult::Unchanged, "A.1" | "A.2" => ProcessResult::Unchanged,
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec!["D"]), "A.1" => ProcessResult::Changed(vec!["D"]),
"A.2" => ProcessResult::Changed(vec!["D"]), "A.2" => ProcessResult::Changed(vec!["D"]),
@ -283,15 +286,12 @@ fn diamond() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let mut d_count = 0; let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D" => { "D" => {
d_count += 1; d_count += 1;
@ -300,11 +300,9 @@ fn diamond() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes,
);
assert_eq!(d_count, 1); assert_eq!(d_count, 1);
let mut ok = ok.unwrap(); let mut ok = ok;
ok.sort(); ok.sort();
assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]); assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
@ -313,22 +311,18 @@ fn diamond() {
assert_eq!(errors.len(), 0); assert_eq!(errors.len(), 0);
forest.register_obligation("A'"); forest.register_obligation("A'");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]), "A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
"A'.1" | "A'.2" => ProcessResult::Unchanged, "A'.1" | "A'.2" => ProcessResult::Unchanged,
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]), "A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
"A'.2" => ProcessResult::Changed(vec!["D'"]), "A'.2" => ProcessResult::Changed(vec!["D'"]),
@ -336,15 +330,12 @@ fn diamond() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let mut d_count = 0; let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D'" => { "D'" => {
d_count += 1; d_count += 1;
@ -353,11 +344,9 @@ fn diamond() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes,
);
assert_eq!(d_count, 1); assert_eq!(d_count, 1);
assert_eq!(ok.unwrap().len(), 0); assert_eq!(ok.len(), 0);
assert_eq!( assert_eq!(
err, err,
vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }] vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }]
@ -375,35 +364,27 @@ fn done_dependency() {
forest.register_obligation("B: Sized"); forest.register_obligation("B: Sized");
forest.register_obligation("C: Sized"); forest.register_obligation("C: Sized");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]), "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]); assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
forest.register_obligation("(A,B,C): Sized"); forest.register_obligation("(A,B,C): Sized");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"(A,B,C): Sized" => { "(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"])
}
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok, vec!["(A,B,C): Sized"]);
);
assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
} }
@ -416,8 +397,7 @@ fn orphan() {
forest.register_obligation("C1"); forest.register_obligation("C1");
forest.register_obligation("C2"); forest.register_obligation("C2");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["D", "E"]), "A" => ProcessResult::Changed(vec!["D", "E"]),
"B" => ProcessResult::Unchanged, "B" => ProcessResult::Unchanged,
@ -427,53 +407,42 @@ fn orphan() {
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, let mut ok = ok;
);
let mut ok = ok.unwrap();
ok.sort(); ok.sort();
assert_eq!(ok, vec!["C1", "C2"]); assert_eq!(ok, vec!["C1", "C2"]);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D" | "E" => ProcessResult::Unchanged, "D" | "E" => ProcessResult::Unchanged,
"B" => ProcessResult::Changed(vec!["D"]), "B" => ProcessResult::Changed(vec!["D"]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D" => ProcessResult::Unchanged, "D" => ProcessResult::Unchanged,
"E" => ProcessResult::Error("E is for error"), "E" => ProcessResult::Error("E is for error"),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error { error: "E is for error", backtrace: vec!["E", "A"] }]); assert_eq!(err, vec![super::Error { error: "E is for error", backtrace: vec!["E", "A"] }]);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"D" => ProcessResult::Error("D is dead"), "D" => ProcessResult::Error("D is dead"),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]); assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]);
let errors = forest.to_errors(()); let errors = forest.to_errors(());
@ -487,35 +456,29 @@ fn simultaneous_register_and_error() {
forest.register_obligation("A"); forest.register_obligation("A");
forest.register_obligation("B"); forest.register_obligation("B");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Error("An error"), "A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]), "B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]); assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
let mut forest = ObligationForest::new(); let mut forest = ObligationForest::new();
forest.register_obligation("B"); forest.register_obligation("B");
forest.register_obligation("A"); forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
&mut C(
|obligation| match *obligation { |obligation| match *obligation {
"A" => ProcessResult::Error("An error"), "A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]), "B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(), _ => unreachable!(),
}, },
|_| {}, |_| {},
), ));
DoCompleted::Yes, assert_eq!(ok.len(), 0);
);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]); assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
} }

View File

@ -1,6 +1,6 @@
use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::infer::{InferCtxt, TyOrConstInferVar};
use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_errors::ErrorReported; use rustc_errors::ErrorReported;
use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation}; use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation};
@ -129,13 +129,11 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
debug!("select: starting another iteration"); debug!("select: starting another iteration");
// Process pending obligations. // Process pending obligations.
let outcome = self.predicates.process_obligations( let outcome: Outcome<_, _> =
&mut FulfillProcessor { self.predicates.process_obligations(&mut FulfillProcessor {
selcx, selcx,
register_region_obligations: self.register_region_obligations, register_region_obligations: self.register_region_obligations,
}, });
DoCompleted::No,
);
debug!("select: outcome={:#?}", outcome); debug!("select: outcome={:#?}", outcome);
// FIXME: if we kept the original cache key, we could mark projection // FIXME: if we kept the original cache key, we could mark projection