Turn Outcome into an opaque type to remove some runtime checks
This commit is contained in:
parent
5f11e71721
commit
8c7a8a62dd
3 changed files with 319 additions and 347 deletions
|
@ -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]) {
|
||||||
|
|
|
@ -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"] }]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue