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.
active_cache: FxHashMap<O::CacheKey, usize>,
/// A vector reused in compress(), to avoid allocating new vectors.
node_rewrites: Vec<usize>,
/// A vector reused in compress() and find_cycles_from_node(), to avoid allocating new vectors.
reused_node_vec: Vec<usize>,
obligation_tree_id_generator: ObligationTreeIdGenerator,
@ -251,12 +251,22 @@ enum NodeState {
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)]
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.
pub errors: Vec<Error<O, E>>,
@ -269,12 +279,29 @@ pub struct Outcome<O, E> {
pub stalled: bool,
}
/// Should `process_obligations` compute the `Outcome::completed` field of its
/// result?
#[derive(PartialEq)]
pub enum DoCompleted {
No,
Yes,
impl<O, E> OutcomeTrait for Outcome<O, E> {
type Error = Error<O, E>;
type Obligation = O;
fn new() -> Self {
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)]
@ -289,7 +316,7 @@ impl<O: ForestObligation> ObligationForest<O> {
nodes: vec![],
done_cache: Default::default(),
active_cache: Default::default(),
node_rewrites: vec![],
reused_node_vec: vec![],
obligation_tree_id_generator: (0..).map(ObligationTreeId),
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) })
.collect();
let successful_obligations = self.compress(DoCompleted::Yes);
assert!(successful_obligations.unwrap().is_empty());
self.compress(|_| assert!(false));
errors
}
@ -392,16 +418,12 @@ impl<O: ForestObligation> ObligationForest<O> {
/// 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,
do_completed: DoCompleted,
) -> Outcome<O, P::Error>
pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
where
P: ObligationProcessor<Obligation = O>,
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
{
let mut errors = vec![];
let mut stalled = true;
let mut outcome = OUT::new();
// Note that the loop body can append new nodes, and those new nodes
// will then be processed by subsequent iterations of the loop.
@ -429,7 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
}
ProcessResult::Changed(children) => {
// We are not (yet) stalled.
stalled = false;
outcome.mark_not_stalled();
node.state.set(NodeState::Success);
for child in children {
@ -442,28 +464,22 @@ impl<O: ForestObligation> ObligationForest<O> {
}
}
ProcessResult::Error(err) => {
stalled = false;
errors.push(Error { error: err, backtrace: self.error_at(index) });
outcome.mark_not_stalled();
outcome.record_error(Error { error: err, backtrace: self.error_at(index) });
}
}
index += 1;
}
if stalled {
// There's no need to perform marking, cycle processing and compression when nothing
// changed.
return Outcome {
completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None },
errors,
stalled,
};
// There's no need to perform marking, cycle processing and compression when nothing
// changed.
if !outcome.is_stalled() {
self.mark_successes();
self.process_cycles(processor);
self.compress(|obl| outcome.record_completed(obl));
}
self.mark_successes();
self.process_cycles(processor);
let completed = self.compress(do_completed);
Outcome { completed, errors, stalled }
outcome
}
/// 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 state = node.state.get();
if state == NodeState::Success {
node.state.set(NodeState::Waiting);
// This call site is cold.
self.uninlined_mark_dependents_as_waiting(node);
} else {
@ -538,17 +553,18 @@ impl<O: ForestObligation> ObligationForest<O> {
// This never-inlined function is for the cold call site.
#[inline(never)]
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)
}
/// Report cycles between all `Success` nodes, and convert all `Success`
/// 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
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 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
@ -559,6 +575,7 @@ impl<O: ForestObligation> ObligationForest<O> {
}
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)
@ -591,13 +608,12 @@ impl<O: ForestObligation> ObligationForest<O> {
/// indices and hence invalidates any outstanding indices. `process_cycles`
/// must be run beforehand to remove any cycles on `Success` nodes.
#[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 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());
node_rewrites.extend(0..orig_nodes_len);
let mut dead_nodes = 0;
let mut removed_done_obligations: Vec<O> = vec![];
// Move removable nodes to the end, preserving the order of the
// remaining nodes.
@ -627,10 +643,8 @@ impl<O: ForestObligation> ObligationForest<O> {
} else {
self.done_cache.insert(node.obligation.as_cache_key().clone());
}
if do_completed == DoCompleted::Yes {
// Extract the success stories.
removed_done_obligations.push(node.obligation.clone());
}
// Extract the success stories.
outcome_cb(&node.obligation);
node_rewrites[index] = orig_nodes_len;
dead_nodes += 1;
}
@ -654,9 +668,7 @@ impl<O: ForestObligation> ObligationForest<O> {
}
node_rewrites.truncate(0);
self.node_rewrites = node_rewrites;
if do_completed == DoCompleted::Yes { Some(removed_done_obligations) } else { None }
self.reused_node_vec = node_rewrites;
}
fn apply_rewrites(&mut self, node_rewrites: &[usize]) {

View File

@ -17,6 +17,40 @@ struct ClosureObligationProcessor<OF, BF, 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)]
fn C<OF, BF, O>(of: OF, bf: BF) -> ClosureObligationProcessor<OF, BF, O, &'static str>
where
@ -65,20 +99,17 @@ fn push_pop() {
// A |-> A.1
// |-> A.2
// |-> A.3
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"B" => ProcessResult::Error("B is for broken"),
"C" => ProcessResult::Changed(vec![]),
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap(), vec!["C"]);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"B" => ProcessResult::Error("B is for broken"),
"C" => ProcessResult::Changed(vec![]),
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok, vec!["C"]);
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:
@ -88,60 +119,51 @@ fn push_pop() {
// D |-> D.1
// |-> D.2
forest.register_obligation("D");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Unchanged,
"A.2" => ProcessResult::Unchanged,
"A.3" => ProcessResult::Changed(vec!["A.3.i"]),
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
"A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap(), Vec::<&'static str>::new());
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Unchanged,
"A.2" => ProcessResult::Unchanged,
"A.3" => ProcessResult::Changed(vec!["A.3.i"]),
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
"A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok, Vec::<&'static str>::new());
assert_eq!(err, Vec::new());
// 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.
// D |-> D.1 |-> D.1.i
// |-> D.2 |-> D.2.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Error("A is for apple"),
"A.3.i" => ProcessResult::Changed(vec![]),
"D.1" => ProcessResult::Changed(vec!["D.1.i"]),
"D.2" => ProcessResult::Changed(vec!["D.2.i"]),
"D.1.i" | "D.2.i" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Error("A is for apple"),
"A.3.i" => ProcessResult::Changed(vec![]),
"D.1" => ProcessResult::Changed(vec!["D.1.i"]),
"D.2" => ProcessResult::Changed(vec!["D.2.i"]),
"D.1.i" | "D.2.i" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
let mut ok = ok;
ok.sort();
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"] }]);
// fourth round: error in D.1.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D.1.i" => ProcessResult::Error("D is for dumb"),
"D.2.i" => ProcessResult::Changed(vec![]),
_ => panic!("unexpected obligation {:?}", obligation),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D.1.i" => ProcessResult::Error("D is for dumb"),
"D.2.i" => ProcessResult::Changed(vec![]),
_ => panic!("unexpected obligation {:?}", obligation),
},
|_| {},
));
let mut ok = ok;
ok.sort();
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"] }]);
@ -160,72 +182,60 @@ fn success_in_grandchildren() {
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
"A.3" => ProcessResult::Changed(vec![]),
"A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
"A.3" => ProcessResult::Changed(vec![]),
"A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
let mut ok = ok;
ok.sort();
assert_eq!(ok, vec!["A.1", "A.3"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.2.i" => ProcessResult::Unchanged,
"A.2.ii" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.2.i" => ProcessResult::Unchanged,
"A.2.ii" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok, vec!["A.2.ii"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
"A.2.i.a" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert!(ok.unwrap().is_empty());
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
"A.2.i.a" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert!(ok.is_empty());
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.2.i.a" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.2.i.a" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
));
let mut ok = ok;
ok.sort();
assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}), DoCompleted::Yes);
let TestOutcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}));
assert!(ok.unwrap().is_empty());
assert!(ok.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).
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let errors = forest.to_errors(());
assert_eq!(errors[0].backtrace, vec!["A.1", "A"]);
@ -260,51 +267,42 @@ fn diamond() {
// check that diamond dependencies are handled correctly
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
"A.1" | "A.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
"A.1" | "A.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec!["D"]),
"A.2" => ProcessResult::Changed(vec!["D"]),
"D" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A.1" => ProcessResult::Changed(vec!["D"]),
"A.2" => ProcessResult::Changed(vec!["D"]),
"D" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D" => {
d_count += 1;
ProcessResult::Changed(vec![])
}
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D" => {
d_count += 1;
ProcessResult::Changed(vec![])
}
_ => unreachable!(),
},
|_| {},
));
assert_eq!(d_count, 1);
let mut ok = ok.unwrap();
let mut ok = ok;
ok.sort();
assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]);
assert_eq!(err.len(), 0);
@ -313,51 +311,42 @@ fn diamond() {
assert_eq!(errors.len(), 0);
forest.register_obligation("A'");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
"A'.1" | "A'.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
"A'.1" | "A'.2" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
"A'.2" => ProcessResult::Changed(vec!["D'"]),
"D'" | "A'" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
"A'.2" => ProcessResult::Changed(vec!["D'"]),
"D'" | "A'" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D'" => {
d_count += 1;
ProcessResult::Error("operation failed")
}
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D'" => {
d_count += 1;
ProcessResult::Error("operation failed")
}
_ => unreachable!(),
},
|_| {},
));
assert_eq!(d_count, 1);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(ok.len(), 0);
assert_eq!(
err,
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("C: Sized");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
},
|_| {},
));
let mut ok = ok;
ok.sort();
assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]);
assert_eq!(err.len(), 0);
forest.register_obligation("(A,B,C): Sized");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"(A,B,C): Sized" => {
ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"])
}
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok, vec!["(A,B,C): Sized"]);
assert_eq!(err.len(), 0);
}
@ -416,64 +397,52 @@ fn orphan() {
forest.register_obligation("C1");
forest.register_obligation("C2");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["D", "E"]),
"B" => ProcessResult::Unchanged,
"C1" => ProcessResult::Changed(vec![]),
"C2" => ProcessResult::Changed(vec![]),
"D" | "E" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
let mut ok = ok.unwrap();
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Changed(vec!["D", "E"]),
"B" => ProcessResult::Unchanged,
"C1" => ProcessResult::Changed(vec![]),
"C2" => ProcessResult::Changed(vec![]),
"D" | "E" => ProcessResult::Unchanged,
_ => unreachable!(),
},
|_| {},
));
let mut ok = ok;
ok.sort();
assert_eq!(ok, vec!["C1", "C2"]);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D" | "E" => ProcessResult::Unchanged,
"B" => ProcessResult::Changed(vec!["D"]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D" | "E" => ProcessResult::Unchanged,
"B" => ProcessResult::Changed(vec!["D"]),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D" => ProcessResult::Unchanged,
"E" => ProcessResult::Error("E is for error"),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D" => ProcessResult::Unchanged,
"E" => ProcessResult::Error("E is for error"),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err, vec![super::Error { error: "E is for error", backtrace: vec!["E", "A"] }]);
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"D" => ProcessResult::Error("D is dead"),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"D" => ProcessResult::Error("D is dead"),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]);
let errors = forest.to_errors(());
@ -487,35 +456,29 @@ fn simultaneous_register_and_error() {
forest.register_obligation("A");
forest.register_obligation("B");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
let mut forest = ObligationForest::new();
forest.register_obligation("B");
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
},
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap().len(), 0);
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|obligation| match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
},
|_| {},
));
assert_eq!(ok.len(), 0);
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
}

View File

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