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//!
@@ -99,6 +96,8 @@ pub trait ObligationProcessor {
9996 type Obligation : ForestObligation ;
10097 type Error : Debug ;
10198
99+ fn needs_process_obligation ( & self , obligation : & Self :: Obligation ) -> bool ;
100+
102101 fn process_obligation (
103102 & mut self ,
104103 obligation : & mut Self :: Obligation ,
@@ -146,7 +145,7 @@ pub struct ObligationForest<O: ForestObligation> {
146145
147146 /// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
148147 /// its contents are not guaranteed to match those of `nodes`. See the
149- /// comments in [ `Self::process_obligation` for details.
148+ /// comments in `Self::process_obligation` for details.
150149 active_cache : FxHashMap < O :: CacheKey , usize > ,
151150
152151 /// A vector reused in [Self::compress()] and [Self::find_cycles_from_node()],
@@ -260,8 +259,6 @@ pub trait OutcomeTrait {
260259 type Obligation ;
261260
262261 fn new ( ) -> Self ;
263- fn mark_not_stalled ( & mut self ) ;
264- fn is_stalled ( & self ) -> bool ;
265262 fn record_completed ( & mut self , outcome : & Self :: Obligation ) ;
266263 fn record_error ( & mut self , error : Self :: Error ) ;
267264}
@@ -270,30 +267,14 @@ pub trait OutcomeTrait {
270267pub struct Outcome < O , E > {
271268 /// Backtrace of obligations that were found to be in error.
272269 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 ,
281270}
282271
283272impl < O , E > OutcomeTrait for Outcome < O , E > {
284273 type Error = Error < O , E > ;
285274 type Obligation = O ;
286275
287276 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
277+ Self { errors : vec ! [ ] }
297278 }
298279
299280 fn record_completed ( & mut self , _outcome : & Self :: Obligation ) {
@@ -415,10 +396,7 @@ impl<O: ForestObligation> ObligationForest<O> {
415396 . insert ( node. obligation . as_cache_key ( ) ) ;
416397 }
417398
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).
399+ /// Performs a fixpoint computation over the obligation list.
422400 #[ inline( never) ]
423401 pub fn process_obligations < P , OUT > ( & mut self , processor : & mut P ) -> OUT
424402 where
@@ -427,55 +405,69 @@ impl<O: ForestObligation> ObligationForest<O> {
427405 {
428406 let mut outcome = OUT :: new ( ) ;
429407
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.
408+ // Fixpoint computation: we repeat until the inner loop stalls.
409+ loop {
410+ let mut has_changed = false ;
411+
412+ // Note that the loop body can append new nodes, and those new nodes
413+ // will then be processed by subsequent iterations of the loop.
414+ //
415+ // We can't use an iterator for the loop because `self.nodes` is
416+ // appended to and the borrow checker would complain. We also can't use
417+ // `for index in 0..self.nodes.len() { ... }` because the range would
418+ // be computed with the initial length, and we would miss the appended
419+ // nodes. Therefore we use a `while` loop.
420+ let mut index = 0 ;
421+ while let Some ( node) = self . nodes . get_mut ( index) {
422+ if node. state . get ( ) != NodeState :: Pending
423+ || !processor. needs_process_obligation ( & node. obligation )
424+ {
425+ index += 1 ;
426+ continue ;
453427 }
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) ;
428+
429+ // `processor.process_obligation` can modify the predicate within
430+ // `node.obligation`, and that predicate is the key used for
431+ // `self.active_cache`. This means that `self.active_cache` can get
432+ // out of sync with `nodes`. It's not very common, but it does
433+ // happen, and code in `compress` has to allow for it.
434+
435+ match processor. process_obligation ( & mut node. obligation ) {
436+ ProcessResult :: Unchanged => {
437+ // No change in state.
438+ }
439+ ProcessResult :: Changed ( children) => {
440+ // We are not (yet) stalled.
441+ has_changed = true ;
442+ node. state . set ( NodeState :: Success ) ;
443+
444+ for child in children {
445+ let st = self . register_obligation_at ( child, Some ( index) ) ;
446+ if let Err ( ( ) ) = st {
447+ // Error already reported - propagate it
448+ // to our node.
449+ self . error_at ( index) ;
450+ }
465451 }
466452 }
453+ ProcessResult :: Error ( err) => {
454+ has_changed = true ;
455+ outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
456+ }
467457 }
468- ProcessResult :: Error ( err) => {
469- outcome. mark_not_stalled ( ) ;
470- outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
471- }
458+ index += 1 ;
459+ }
460+
461+ // If unchanged, then we saw no successful obligations, which means
462+ // there is no point in further iteration. This is based on the
463+ // assumption that when trait matching returns `Error` or
464+ // `Unchanged`, those results do not affect environmental inference
465+ // state. (Note that this will occur if we invoke
466+ // `process_obligations` with no pending obligations.)
467+ if !has_changed {
468+ break ;
472469 }
473- index += 1 ;
474- }
475470
476- // There's no need to perform marking, cycle processing and compression when nothing
477- // changed.
478- if !outcome. is_stalled ( ) {
479471 self . mark_successes ( ) ;
480472 self . process_cycles ( processor) ;
481473 self . compress ( |obl| outcome. record_completed ( obl) ) ;
@@ -634,17 +626,14 @@ impl<O: ForestObligation> ObligationForest<O> {
634626 }
635627 }
636628 NodeState :: Done => {
637- // This lookup can fail because the contents of
629+ // The removal lookup might fail because the contents of
638630 // `self.active_cache` are not guaranteed to match those of
639631 // `self.nodes`. See the comment in `process_obligation`
640632 // for more details.
641- if let Some ( ( predicate, _) ) =
642- self . active_cache . remove_entry ( & node. obligation . as_cache_key ( ) )
643- {
644- self . done_cache . insert ( predicate) ;
645- } else {
646- self . done_cache . insert ( node. obligation . as_cache_key ( ) . clone ( ) ) ;
647- }
633+ let cache_key = node. obligation . as_cache_key ( ) ;
634+ self . active_cache . remove ( & cache_key) ;
635+ self . done_cache . insert ( cache_key) ;
636+
648637 // Extract the success stories.
649638 outcome_cb ( & node. obligation ) ;
650639 node_rewrites[ index] = orig_nodes_len;
0 commit comments