1
Fork 0

Turn Outcome into an opaque type to remove some runtime checks

This commit is contained in:
Dániel Buga 2020-10-14 02:46:21 +02:00
parent 5f11e71721
commit 8c7a8a62dd
3 changed files with 319 additions and 347 deletions

View file

@ -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)]
@ -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. if !outcome.is_stalled() {
return Outcome { self.mark_successes();
completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None }, self.process_cycles(processor);
errors, self.compress(|obl| outcome.record_completed(obl));
stalled,
};
} }
self.mark_successes(); outcome
self.process_cycles(processor);
let completed = self.compress(do_completed);
Outcome { completed, errors, stalled }
} }
/// Returns a vector of obligations for `p` and all of its /// Returns a vector of obligations for `p` and all of its
@ -592,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.reused_node_vec); 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.
@ -628,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. outcome_cb(&node.obligation);
removed_done_obligations.push(node.obligation.clone());
}
node_rewrites[index] = orig_nodes_len; node_rewrites[index] = orig_nodes_len;
dead_nodes += 1; dead_nodes += 1;
} }
@ -656,8 +669,6 @@ impl<O: ForestObligation> ObligationForest<O> {
node_rewrites.truncate(0); node_rewrites.truncate(0);
self.reused_node_vec = 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,20 +99,17 @@ 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"), "C" => ProcessResult::Changed(vec![]),
"C" => ProcessResult::Changed(vec![]), "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok, vec!["C"]);
DoCompleted::Yes,
);
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,60 +119,51 @@ 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, "A.3" => ProcessResult::Changed(vec!["A.3.i"]),
"A.3" => ProcessResult::Changed(vec!["A.3.i"]), "D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]), "A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
"A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok, Vec::<&'static str>::new());
DoCompleted::Yes,
);
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"), "A.3.i" => ProcessResult::Changed(vec![]),
"A.3.i" => ProcessResult::Changed(vec![]), "D.1" => ProcessResult::Changed(vec!["D.1.i"]),
"D.1" => ProcessResult::Changed(vec!["D.1.i"]), "D.2" => ProcessResult::Changed(vec!["D.2.i"]),
"D.2" => ProcessResult::Changed(vec!["D.2.i"]), "D.1.i" | "D.2.i" => ProcessResult::Unchanged,
"D.1.i" | "D.2.i" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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,72 +182,60 @@ 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![]), "A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]), "A.3" => ProcessResult::Changed(vec![]),
"A.3" => ProcessResult::Changed(vec![]), "A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
"A.2.i" | "A.2.ii" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok, vec!["A.2.ii"]);
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert!(ok.is_empty());
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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,51 +267,42 @@ 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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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"]), "D" => ProcessResult::Unchanged,
"D" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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; ProcessResult::Changed(vec![])
ProcessResult::Changed(vec![]) }
} _ => 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,51 +311,42 @@ 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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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'"]), "D'" | "A'" => ProcessResult::Unchanged,
"D'" | "A'" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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; ProcessResult::Error("operation failed")
ProcessResult::Error("operation failed") }
} _ => 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!(), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
"(A,B,C): Sized" => { _ => unreachable!(),
ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]) },
} |_| {},
_ => unreachable!(), ));
}, assert_eq!(ok, vec!["(A,B,C): Sized"]);
|_| {},
),
DoCompleted::Yes,
);
assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]);
assert_eq!(err.len(), 0); assert_eq!(err.len(), 0);
} }
@ -416,64 +397,52 @@ 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, "C1" => ProcessResult::Changed(vec![]),
"C1" => ProcessResult::Changed(vec![]), "C2" => ProcessResult::Changed(vec![]),
"C2" => ProcessResult::Changed(vec![]), "D" | "E" => ProcessResult::Unchanged,
"D" | "E" => ProcessResult::Unchanged, _ => unreachable!(),
_ => unreachable!(), },
}, |_| {},
|_| {}, ));
), let mut ok = ok;
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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!(), },
}, |_| {},
|_| {}, ));
), assert_eq!(ok.len(), 0);
DoCompleted::Yes,
);
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