@@ -216,6 +216,9 @@ pub struct ObligationForest<O: ForestObligation> {
216
216
/// dropping this forest.
217
217
///
218
218
watcher_offset : Option < O :: WatcherOffset > ,
219
+ /// We do not want to process any further obligations after the offset has been deregistered as that could mean unified variables are lost, leading to typecheck failures.
220
+ /// So we mark this as done and panic if a caller tries to resume processing.
221
+ done : bool ,
219
222
/// Reusable vector for storing unblocked nodes whose watch should be removed.
220
223
temp_unblocked_nodes : Vec < O :: Variable > ,
221
224
}
@@ -448,6 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
448
451
stalled_on_unknown : Default :: default ( ) ,
449
452
temp_unblocked_nodes : Default :: default ( ) ,
450
453
watcher_offset : None ,
454
+ done : false ,
451
455
}
452
456
}
453
457
@@ -459,6 +463,7 @@ impl<O: ForestObligation> ObligationForest<O> {
459
463
460
464
/// Removes the watcher_offset, allowing it to be deregistered
461
465
pub fn take_watcher_offset ( & mut self ) -> Option < O :: WatcherOffset > {
466
+ self . done = true ;
462
467
self . watcher_offset . take ( )
463
468
}
464
469
@@ -562,6 +567,7 @@ impl<O: ForestObligation> ObligationForest<O> {
562
567
let errors = self
563
568
. pending_nodes
564
569
. iter ( )
570
+ . filter ( |& & index| self . nodes [ index] . state . get ( ) == NodeState :: Pending )
565
571
. map ( |& index| Error { error : error. clone ( ) , backtrace : self . error_at ( index) } )
566
572
. collect ( ) ;
567
573
@@ -574,7 +580,14 @@ impl<O: ForestObligation> ObligationForest<O> {
574
580
where
575
581
F : Fn ( & O ) -> P ,
576
582
{
577
- self . pending_nodes . iter ( ) . map ( |& index| f ( & self . nodes [ index] . obligation ) ) . collect ( )
583
+ self . pending_nodes
584
+ . iter ( )
585
+ . filter_map ( |& index| {
586
+ let node = & self . nodes [ index] ;
587
+ if node. state . get ( ) == NodeState :: Pending { Some ( node) } else { None }
588
+ } )
589
+ . map ( |node| f ( & node. obligation ) )
590
+ . collect ( )
578
591
}
579
592
580
593
fn insert_into_error_cache ( & mut self , index : NodeIndex ) {
@@ -595,107 +608,113 @@ impl<O: ForestObligation> ObligationForest<O> {
595
608
OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
596
609
{
597
610
if self . watcher_offset . is_none ( ) {
611
+ assert ! ( !self . done) ;
598
612
self . watcher_offset = Some ( processor. register_variable_watcher ( ) ) ;
599
613
}
600
614
let mut errors = vec ! [ ] ;
601
615
let mut stalled = true ;
602
616
603
617
self . unblock_nodes ( processor) ;
604
618
605
- let nodes = & self . nodes ;
606
- self . unblocked . extend (
607
- self . stalled_on_unknown
608
- . drain ( ..)
609
- . map ( |index| Unblocked { index, order : nodes[ index] . node_number } ) ,
610
- ) ;
611
- while let Some ( Unblocked { index, .. } ) = self . unblocked . pop ( ) {
612
- // Skip any duplicates since we only need to processes the node once
613
- if self . unblocked . peek ( ) . map ( |u| u. index ) == Some ( index) {
614
- continue ;
615
- }
619
+ let mut made_progress_this_iteration = true ;
620
+ while made_progress_this_iteration {
621
+ made_progress_this_iteration = false ;
622
+ let nodes = & self . nodes ;
623
+ self . unblocked . extend (
624
+ self . stalled_on_unknown
625
+ . drain ( ..)
626
+ . map ( |index| Unblocked { index, order : nodes[ index] . node_number } ) ,
627
+ ) ;
628
+ while let Some ( Unblocked { index, .. } ) = self . unblocked . pop ( ) {
629
+ // Skip any duplicates since we only need to processes the node once
630
+ if self . unblocked . peek ( ) . map ( |u| u. index ) == Some ( index) {
631
+ continue ;
632
+ }
616
633
617
- let node = & mut self . nodes [ index] ;
634
+ let node = & mut self . nodes [ index] ;
618
635
619
- if node. state . get ( ) != NodeState :: Pending {
620
- continue ;
621
- }
636
+ if node. state . get ( ) != NodeState :: Pending {
637
+ continue ;
638
+ }
622
639
623
- // One of the variables we stalled on unblocked us. If the node were blocked on other
624
- // variables as well then remove those stalls. If the node is still stalled on one of
625
- // those variables after `process_obligation` it will simply be added back to
626
- // `self.stalled_on`
627
- let stalled_on = node. obligation . stalled_on ( ) ;
628
- if stalled_on. len ( ) > 1 {
629
- for var in stalled_on {
630
- match self . stalled_on . entry ( var. clone ( ) ) {
631
- Entry :: Vacant ( _) => ( ) ,
632
- Entry :: Occupied ( mut entry) => {
633
- let nodes = entry. get_mut ( ) ;
634
- if let Some ( i) = nodes. iter ( ) . position ( |x| * x == index) {
635
- nodes. swap_remove ( i) ;
636
- }
637
- if nodes. is_empty ( ) {
638
- processor. unwatch_variable ( var. clone ( ) ) ;
639
- entry. remove ( ) ;
640
+ // One of the variables we stalled on unblocked us. If the node were blocked on other
641
+ // variables as well then remove those stalls. If the node is still stalled on one of
642
+ // those variables after `process_obligation` it will simply be added back to
643
+ // `self.stalled_on`
644
+ let stalled_on = node. obligation . stalled_on ( ) ;
645
+ if stalled_on. len ( ) > 1 {
646
+ for var in stalled_on {
647
+ match self . stalled_on . entry ( var. clone ( ) ) {
648
+ Entry :: Vacant ( _) => ( ) ,
649
+ Entry :: Occupied ( mut entry) => {
650
+ let nodes = entry. get_mut ( ) ;
651
+ if let Some ( i) = nodes. iter ( ) . position ( |x| * x == index) {
652
+ nodes. swap_remove ( i) ;
653
+ }
654
+ if nodes. is_empty ( ) {
655
+ processor. unwatch_variable ( var. clone ( ) ) ;
656
+ entry. remove ( ) ;
657
+ }
640
658
}
641
659
}
642
660
}
643
661
}
644
- }
645
662
646
- // `processor.process_obligation` can modify the predicate within
647
- // `node.obligation`, and that predicate is the key used for
648
- // `self.active_cache`. This means that `self.active_cache` can get
649
- // out of sync with `nodes`. It's not very common, but it does
650
- // happen, and code in `compress` has to allow for it.
651
- let before = node. obligation . as_cache_key ( ) ;
652
- let result = processor. process_obligation ( & mut node. obligation ) ;
653
- let after = node. obligation . as_cache_key ( ) ;
654
- if before != after {
655
- node. alternative_predicates . push ( before) ;
656
- }
663
+ // `processor.process_obligation` can modify the predicate within
664
+ // `node.obligation`, and that predicate is the key used for
665
+ // `self.active_cache`. This means that `self.active_cache` can get
666
+ // out of sync with `nodes`. It's not very common, but it does
667
+ // happen, and code in `compress` has to allow for it.
668
+ let before = node. obligation . as_cache_key ( ) ;
669
+ let result = processor. process_obligation ( & mut node. obligation ) ;
670
+ let after = node. obligation . as_cache_key ( ) ;
671
+ if before != after {
672
+ node. alternative_predicates . push ( before) ;
673
+ }
657
674
658
- self . unblock_nodes ( processor) ;
659
- let node = & mut self . nodes [ index] ;
660
- match result {
661
- ProcessResult :: Unchanged => {
662
- let stalled_on = node. obligation . stalled_on ( ) ;
663
- if stalled_on. is_empty ( ) {
664
- // We stalled but the variables that caused it are unknown so we run
665
- // `index` again at the next opportunity
666
- self . stalled_on_unknown . push ( index) ;
667
- } else {
668
- // Register every variable that we stalled on
669
- for var in stalled_on {
670
- self . stalled_on
671
- . entry ( var. clone ( ) )
672
- . or_insert_with ( || {
673
- processor. watch_variable ( var. clone ( ) ) ;
674
- Vec :: new ( )
675
- } )
676
- . push ( index) ;
675
+ self . unblock_nodes ( processor) ;
676
+ let node = & mut self . nodes [ index] ;
677
+ match result {
678
+ ProcessResult :: Unchanged => {
679
+ let stalled_on = node. obligation . stalled_on ( ) ;
680
+ if stalled_on. is_empty ( ) {
681
+ // We stalled but the variables that caused it are unknown so we run
682
+ // `index` again at the next opportunity
683
+ self . stalled_on_unknown . push ( index) ;
684
+ } else {
685
+ // Register every variable that we stalled on
686
+ for var in stalled_on {
687
+ self . stalled_on
688
+ . entry ( var. clone ( ) )
689
+ . or_insert_with ( || {
690
+ processor. watch_variable ( var. clone ( ) ) ;
691
+ Vec :: new ( )
692
+ } )
693
+ . push ( index) ;
694
+ }
677
695
}
696
+ // No change in state.
678
697
}
679
- // No change in state.
680
- }
681
- ProcessResult :: Changed ( children ) => {
682
- // We are not (yet) stalled.
683
- stalled = false ;
684
- node . state . set ( NodeState :: Success ) ;
685
- self . success_or_waiting_nodes . push ( index ) ;
686
-
687
- for child in children {
688
- let st = self . register_obligation_at ( child , Some ( index ) ) ;
689
- if let Err ( ( ) ) = st {
690
- // Error already reported - propagate it
691
- // to our node.
692
- self . error_at ( index ) ;
698
+ ProcessResult :: Changed ( children ) => {
699
+ made_progress_this_iteration = true ;
700
+ // We are not (yet) stalled.
701
+ stalled = false ;
702
+ node . state . set ( NodeState :: Success ) ;
703
+ self . success_or_waiting_nodes . push ( index ) ;
704
+
705
+ for child in children {
706
+ let st = self . register_obligation_at ( child , Some ( index ) ) ;
707
+ if let Err ( ( ) ) = st {
708
+ // Error already reported - propagate it
709
+ // to our node.
710
+ self . error_at ( index ) ;
711
+ }
693
712
}
694
713
}
695
- }
696
- ProcessResult :: Error ( err ) => {
697
- stalled = false ;
698
- errors . push ( Error { error : err , backtrace : self . error_at ( index ) } ) ;
714
+ ProcessResult :: Error ( err ) => {
715
+ stalled = false ;
716
+ errors . push ( Error { error : err , backtrace : self . error_at ( index ) } ) ;
717
+ }
699
718
}
700
719
}
701
720
}
0 commit comments