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,
}
/// 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)]
@ -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
@ -592,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.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.
@ -628,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;
}
@ -656,8 +669,6 @@ impl<O: ForestObligation> ObligationForest<O> {
node_rewrites.truncate(0);
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]) {

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