@@ -104,6 +104,11 @@ pub trait ObligationProcessor {
104104 type Obligation : ForestObligation ;
105105 type Error : Debug ;
106106
107+ fn checked_process_obligation (
108+ & mut self ,
109+ obligation : & mut Self :: Obligation ,
110+ ) -> ProcessResult < Self :: Obligation , Self :: Error > ;
111+
107112 fn process_obligation (
108113 & mut self ,
109114 obligation : & mut Self :: Obligation ,
@@ -383,49 +388,18 @@ enum NodeState {
383388 Error ,
384389}
385390
386- /// This trait allows us to have two different Outcome types:
387- /// - the normal one that does as little as possible
388- /// - one for tests that does some additional work and checking
389- pub trait OutcomeTrait {
390- type Error ;
391- type Obligation ;
392-
393- fn new ( ) -> Self ;
394- fn mark_not_stalled ( & mut self ) ;
395- fn is_stalled ( & self ) -> bool ;
396- fn record_completed ( & mut self , outcome : & Self :: Obligation ) ;
397- fn record_error ( & mut self , error : Self :: Error ) ;
398- }
399-
400391#[ derive( Debug ) ]
401392pub struct Outcome < O , E > {
402393 /// Backtrace of obligations that were found to be in error.
403394 pub errors : Vec < Error < O , E > > ,
404395}
405396
406- impl < O , E > OutcomeTrait for Outcome < O , E > {
407- type Error = Error < O , E > ;
408- type Obligation = O ;
409-
410- fn new ( ) -> Self {
411- Self { stalled : true , errors : vec ! [ ] }
412- }
413-
414- fn mark_not_stalled ( & mut self ) {
415- self . stalled = false ;
416- }
417-
418- fn is_stalled ( & self ) -> bool {
419- self . stalled
420- }
421-
422- fn record_completed ( & mut self , _outcome : & Self :: Obligation ) {
423- // do nothing
424- }
425-
426- fn record_error ( & mut self , error : Self :: Error ) {
427- self . errors . push ( error)
428- }
397+ /// Should `process_obligations` compute the `Outcome::completed` field of its
398+ /// result?
399+ #[ derive( PartialEq , Copy , Clone ) ]
400+ pub enum DoCompleted {
401+ No ,
402+ Yes ,
429403}
430404
431405#[ derive( Debug , PartialEq , Eq ) ]
@@ -602,14 +576,18 @@ impl<O: ForestObligation> ObligationForest<O> {
602576 /// be called in a loop until `outcome.stalled` is false.
603577 ///
604578 /// This _cannot_ be unrolled (presently, at least).
605- pub fn process_obligations < P , OUT > ( & mut self , processor : & mut P ) -> OUT
579+ pub fn process_obligations < P > ( & mut self , processor : & mut P ) -> Outcome < O , P :: Error >
606580 where
607581 P : ObligationProcessor < Obligation = O > ,
608- OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
609582 {
610583 if self . watcher_offset . is_none ( ) {
611584 assert ! ( !self . done) ;
612- self . watcher_offset = Some ( processor. register_variable_watcher ( ) ) ;
585+ if self . nodes . len ( ) > 100 {
586+ self . watcher_offset = Some ( processor. register_variable_watcher ( ) ) ;
587+ }
588+ if let Some ( outcome) = self . process_obligations_simple ( processor, do_completed) {
589+ return outcome;
590+ }
613591 }
614592 let mut errors = vec ! [ ] ;
615593 let mut stalled = true ;
@@ -713,6 +691,7 @@ impl<O: ForestObligation> ObligationForest<O> {
713691 }
714692 }
715693 ProcessResult :: Error ( err) => {
694+ made_progress_this_iteration = true ;
716695 stalled = false ;
717696 errors. push ( Error { error : err, backtrace : self . error_at ( index) } ) ;
718697 }
@@ -732,10 +711,127 @@ impl<O: ForestObligation> ObligationForest<O> {
732711 self . mark_successes ( ) ;
733712 self . process_cycles ( processor) ;
734713 let completed = self . compress ( do_completed) ;
735-
736714 Outcome { completed, errors }
737715 }
738716
717+ fn process_obligations_simple < P > (
718+ & mut self ,
719+ processor : & mut P ,
720+ do_completed : DoCompleted ,
721+ ) -> Option < Outcome < O , P :: Error > >
722+ where
723+ P : ObligationProcessor < Obligation = O > ,
724+ {
725+ let mut errors = vec ! [ ] ;
726+ let mut stalled = true ;
727+
728+ // Note that the loop body can append new nodes, and those new nodes
729+ // will then be processed by subsequent iterations of the loop.
730+ //
731+ // We can't use an iterator for the loop because `self.nodes` is
732+ // appended to and the borrow checker would complain. We also can't use
733+ // `for index in 0..self.nodes.len() { ... }` because the range would
734+ // be computed with the initial length, and we would miss the appended
735+ // nodes. Therefore we use a `while` loop.
736+ loop {
737+ let mut i = 0 ;
738+ let mut made_progress_this_iteration = false ;
739+ while let Some ( & index) = self . pending_nodes . get ( i) {
740+ let node = & mut self . nodes [ index] ;
741+ // `processor.process_obligation` can modify the predicate within
742+ // `node.obligation`, and that predicate is the key used for
743+ // `self.active_cache`. This means that `self.active_cache` can get
744+ // out of sync with `nodes`. It's not very common, but it does
745+ // happen, and code in `compress` has to allow for it.
746+ if node. state . get ( ) != NodeState :: Pending {
747+ i += 1 ;
748+ continue ;
749+ }
750+
751+ // `processor.process_obligation` can modify the predicate within
752+ // `node.obligation`, and that predicate is the key used for
753+ // `self.active_cache`. This means that `self.active_cache` can get
754+ // out of sync with `nodes`. It's not very common, but it does
755+ // happen, and code in `compress` has to allow for it.
756+ let before = node. obligation . as_cache_key ( ) ;
757+ let result = processor. checked_process_obligation ( & mut node. obligation ) ;
758+ let after = node. obligation . as_cache_key ( ) ;
759+ if before != after {
760+ node. alternative_predicates . push ( before) ;
761+ }
762+
763+ match result {
764+ ProcessResult :: Unchanged => {
765+ // No change in state.
766+ if self . watcher_offset . is_some ( ) {
767+ let stalled_on = node. obligation . stalled_on ( ) ;
768+ if stalled_on. is_empty ( ) {
769+ // We stalled but the variables that caused it are unknown so we run
770+ // `index` again at the next opportunity
771+ self . stalled_on_unknown . push ( index) ;
772+ } else {
773+ // Register every variable that we stalled on
774+ for var in stalled_on {
775+ self . stalled_on
776+ . entry ( var. clone ( ) )
777+ . or_insert_with ( || {
778+ processor. watch_variable ( var. clone ( ) ) ;
779+ Vec :: new ( )
780+ } )
781+ . push ( index) ;
782+ }
783+ }
784+ }
785+ }
786+ ProcessResult :: Changed ( children) => {
787+ // We are not (yet) stalled.
788+ stalled = false ;
789+ node. state . set ( NodeState :: Success ) ;
790+ made_progress_this_iteration = true ;
791+ self . success_or_waiting_nodes . push ( index) ;
792+
793+ for child in children {
794+ let st = self . register_obligation_at ( child, Some ( index) ) ;
795+ if let Err ( ( ) ) = st {
796+ // Error already reported - propagate it
797+ // to our node.
798+ self . error_at ( index) ;
799+ }
800+ }
801+ }
802+ ProcessResult :: Error ( err) => {
803+ stalled = false ;
804+ errors. push ( Error { error : err, backtrace : self . error_at ( index) } ) ;
805+ }
806+ }
807+ i += 1 ;
808+ }
809+
810+ if stalled {
811+ // There's no need to perform marking, cycle processing and compression when nothing
812+ // changed.
813+ return Some ( Outcome {
814+ completed : if do_completed == DoCompleted :: Yes { Some ( vec ! [ ] ) } else { None } ,
815+ errors,
816+ } ) ;
817+ }
818+
819+ if !made_progress_this_iteration {
820+ break ;
821+ }
822+
823+ if self . watcher_offset . is_some ( ) {
824+ return None ;
825+ }
826+
827+ self . mark_successes ( ) ;
828+ self . process_cycles ( processor) ;
829+ self . compress ( do_completed) ;
830+ }
831+
832+ Some ( Outcome { completed : None , errors } )
833+ }
834+
739835 /// Checks which nodes have been unblocked since the last time this was called. All nodes that
740836 /// were unblocked are added to the `unblocked` queue and all watches associated with the
741837 /// variables blocking those nodes are deregistered (since they are now instantiated, they will
0 commit comments