4242//! now considered to be in error.
4343//!
4444//! When the call to `process_obligations` completes, you get back an `Outcome`,
45- //! which includes three bits of information:
45+ //! which includes two bits of information:
4646//!
4747//! - `completed`: a list of obligations where processing was fully
4848//! completed without error (meaning that all transitive subobligations
5353//! all the obligations in `C` have been found completed.
5454//! - `errors`: a list of errors that occurred and associated backtraces
5555//! at the time of error, which can be used to give context to the user.
56- //! - `stalled`: if true, then none of the existing obligations were
57- //! *shallowly successful* (that is, no callback returned `Changed(_)`).
58- //! This implies that all obligations were either errors or returned an
59- //! ambiguous result, which means that any further calls to
60- //! `process_obligations` would simply yield back further ambiguous
61- //! results. This is used by the `FulfillmentContext` to decide when it
62- //! has reached a steady state.
56+ //!
57+ //! Upon completion, none of the existing obligations were *shallowly
58+ //! successful* (that is, no callback returned `Changed(_)`). This implies that
59+ //! all obligations were either errors or returned an ambiguous result.
6360//!
6461//! ### Implementation details
6562//!
@@ -260,8 +257,6 @@ pub trait OutcomeTrait {
260257 type Obligation ;
261258
262259 fn new ( ) -> Self ;
263- fn mark_not_stalled ( & mut self ) ;
264- fn is_stalled ( & self ) -> bool ;
265260 fn record_completed ( & mut self , outcome : & Self :: Obligation ) ;
266261 fn record_error ( & mut self , error : Self :: Error ) ;
267262}
@@ -270,30 +265,14 @@ pub trait OutcomeTrait {
270265pub struct Outcome < O , E > {
271266 /// Backtrace of obligations that were found to be in error.
272267 pub errors : Vec < Error < O , E > > ,
273-
274- /// If true, then we saw no successful obligations, which means
275- /// there is no point in further iteration. This is based on the
276- /// assumption that when trait matching returns `Error` or
277- /// `Unchanged`, those results do not affect environmental
278- /// inference state. (Note that if we invoke `process_obligations`
279- /// with no pending obligations, stalled will be true.)
280- pub stalled : bool ,
281268}
282269
283270impl < O , E > OutcomeTrait for Outcome < O , E > {
284271 type Error = Error < O , E > ;
285272 type Obligation = O ;
286273
287274 fn new ( ) -> Self {
288- Self { stalled : true , errors : vec ! [ ] }
289- }
290-
291- fn mark_not_stalled ( & mut self ) {
292- self . stalled = false ;
293- }
294-
295- fn is_stalled ( & self ) -> bool {
296- self . stalled
275+ Self { errors : vec ! [ ] }
297276 }
298277
299278 fn record_completed ( & mut self , _outcome : & Self :: Obligation ) {
@@ -415,10 +394,7 @@ impl<O: ForestObligation> ObligationForest<O> {
415394 . insert ( node. obligation . as_cache_key ( ) ) ;
416395 }
417396
418- /// Performs a pass through the obligation list. This must
419- /// be called in a loop until `outcome.stalled` is false.
420- ///
421- /// This _cannot_ be unrolled (presently, at least).
397+ /// Performs a fixpoint computation over the obligation list.
422398 #[ inline( never) ]
423399 pub fn process_obligations < P , OUT > ( & mut self , processor : & mut P ) -> OUT
424400 where
@@ -427,55 +403,66 @@ impl<O: ForestObligation> ObligationForest<O> {
427403 {
428404 let mut outcome = OUT :: new ( ) ;
429405
430- // Note that the loop body can append new nodes, and those new nodes
431- // will then be processed by subsequent iterations of the loop.
432- //
433- // We can't use an iterator for the loop because `self.nodes` is
434- // appended to and the borrow checker would complain. We also can't use
435- // `for index in 0..self.nodes.len() { ... }` because the range would
436- // be computed with the initial length, and we would miss the appended
437- // nodes. Therefore we use a `while` loop.
438- let mut index = 0 ;
439- while let Some ( node) = self . nodes . get_mut ( index) {
440- // `processor.process_obligation` can modify the predicate within
441- // `node.obligation`, and that predicate is the key used for
442- // `self.active_cache`. This means that `self.active_cache` can get
443- // out of sync with `nodes`. It's not very common, but it does
444- // happen, and code in `compress` has to allow for it.
445- if node. state . get ( ) != NodeState :: Pending {
446- index += 1 ;
447- continue ;
448- }
449-
450- match processor. process_obligation ( & mut node. obligation ) {
451- ProcessResult :: Unchanged => {
452- // No change in state.
406+ // Fixpoint computation: we repeat until the inner loop stalls.
407+ loop {
408+ let mut has_changed = false ;
409+
410+ // Note that the loop body can append new nodes, and those new nodes
411+ // will then be processed by subsequent iterations of the loop.
412+ //
413+ // We can't use an iterator for the loop because `self.nodes` is
414+ // appended to and the borrow checker would complain. We also can't use
415+ // `for index in 0..self.nodes.len() { ... }` because the range would
416+ // be computed with the initial length, and we would miss the appended
417+ // nodes. Therefore we use a `while` loop.
418+ let mut index = 0 ;
419+ while let Some ( node) = self . nodes . get_mut ( index) {
420+ // `processor.process_obligation` can modify the predicate within
421+ // `node.obligation`, and that predicate is the key used for
422+ // `self.active_cache`. This means that `self.active_cache` can get
423+ // out of sync with `nodes`. It's not very common, but it does
424+ // happen, and code in `compress` has to allow for it.
425+ if node. state . get ( ) != NodeState :: Pending {
426+ index += 1 ;
427+ continue ;
453428 }
454- ProcessResult :: Changed ( children) => {
455- // We are not (yet) stalled.
456- outcome. mark_not_stalled ( ) ;
457- node. state . set ( NodeState :: Success ) ;
458-
459- for child in children {
460- let st = self . register_obligation_at ( child, Some ( index) ) ;
461- if let Err ( ( ) ) = st {
462- // Error already reported - propagate it
463- // to our node.
464- self . error_at ( index) ;
429+
430+ match processor. process_obligation ( & mut node. obligation ) {
431+ ProcessResult :: Unchanged => {
432+ // No change in state.
433+ }
434+ ProcessResult :: Changed ( children) => {
435+ // We are not (yet) stalled.
436+ has_changed = true ;
437+ node. state . set ( NodeState :: Success ) ;
438+
439+ for child in children {
440+ let st = self . register_obligation_at ( child, Some ( index) ) ;
441+ if let Err ( ( ) ) = st {
442+ // Error already reported - propagate it
443+ // to our node.
444+ self . error_at ( index) ;
445+ }
465446 }
466447 }
448+ ProcessResult :: Error ( err) => {
449+ has_changed = true ;
450+ outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
451+ }
467452 }
468- ProcessResult :: Error ( err) => {
469- outcome. mark_not_stalled ( ) ;
470- outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
471- }
453+ index += 1 ;
454+ }
455+
456+ // If unchanged, then we saw no successful obligations, which means
457+ // there is no point in further iteration. This is based on the
458+ // assumption that when trait matching returns `Error` or
459+ // `Unchanged`, those results do not affect environmental inference
460+ // state. (Note that this will occur if we invoke
461+ // `process_obligations` with no pending obligations.)
462+ if !has_changed {
463+ break ;
472464 }
473- index += 1 ;
474- }
475465
476- // There's no need to perform marking, cycle processing and compression when nothing
477- // changed.
478- if !outcome. is_stalled ( ) {
479466 self . mark_successes ( ) ;
480467 self . process_cycles ( processor) ;
481468 self . compress ( |obl| outcome. record_completed ( obl) ) ;
0 commit comments