@@ -104,6 +104,11 @@ pub trait ObligationProcessor {
104
104
type Obligation : ForestObligation ;
105
105
type Error : Debug ;
106
106
107
+ fn checked_process_obligation (
108
+ & mut self ,
109
+ obligation : & mut Self :: Obligation ,
110
+ ) -> ProcessResult < Self :: Obligation , Self :: Error > ;
111
+
107
112
fn process_obligation (
108
113
& mut self ,
109
114
obligation : & mut Self :: Obligation ,
@@ -383,49 +388,18 @@ enum NodeState {
383
388
Error ,
384
389
}
385
390
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
-
400
391
#[ derive( Debug ) ]
401
392
pub struct Outcome < O , E > {
402
393
/// Backtrace of obligations that were found to be in error.
403
394
pub errors : Vec < Error < O , E > > ,
404
395
}
405
396
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 ,
429
403
}
430
404
431
405
#[ derive( Debug , PartialEq , Eq ) ]
@@ -602,14 +576,18 @@ impl<O: ForestObligation> ObligationForest<O> {
602
576
/// be called in a loop until `outcome.stalled` is false.
603
577
///
604
578
/// 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 >
606
580
where
607
581
P : ObligationProcessor < Obligation = O > ,
608
- OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
609
582
{
610
583
if self . watcher_offset . is_none ( ) {
611
584
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
+ }
613
591
}
614
592
let mut errors = vec ! [ ] ;
615
593
let mut stalled = true ;
@@ -713,6 +691,7 @@ impl<O: ForestObligation> ObligationForest<O> {
713
691
}
714
692
}
715
693
ProcessResult :: Error ( err) => {
694
+ made_progress_this_iteration = true ;
716
695
stalled = false ;
717
696
errors. push ( Error { error : err, backtrace : self . error_at ( index) } ) ;
718
697
}
@@ -732,10 +711,127 @@ impl<O: ForestObligation> ObligationForest<O> {
732
711
self . mark_successes ( ) ;
733
712
self . process_cycles ( processor) ;
734
713
let completed = self . compress ( do_completed) ;
735
-
736
714
Outcome { completed, errors }
737
715
}
738
716
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
+
739
835
/// Checks which nodes have been unblocked since the last time this was called. All nodes that
740
836
/// were unblocked are added to the `unblocked` queue and all watches associated with the
741
837
/// variables blocking those nodes are deregistered (since they are now instantiated, they will
0 commit comments