about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/mod.rs97
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/tests.rs559
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs10
3 files changed, 319 insertions, 347 deletions
diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
index aeb926bca3e..a5b2df1da5d 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
@@ -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]) {
diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs
index 01652465eea..371c62c063f 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs
@@ -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"] }]);
 }
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 97dd180b27b..88424d449ed 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -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