@@ -216,6 +216,9 @@ pub struct ObligationForest<O: ForestObligation> {
216216 /// dropping this forest.
217217 ///
218218 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 ,
219222 /// Reusable vector for storing unblocked nodes whose watch should be removed.
220223 temp_unblocked_nodes : Vec < O :: Variable > ,
221224}
@@ -448,6 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
448451 stalled_on_unknown : Default :: default ( ) ,
449452 temp_unblocked_nodes : Default :: default ( ) ,
450453 watcher_offset : None ,
454+ done : false ,
451455 }
452456 }
453457
@@ -459,6 +463,7 @@ impl<O: ForestObligation> ObligationForest<O> {
459463
460464 /// Removes the watcher_offset, allowing it to be deregistered
461465 pub fn take_watcher_offset ( & mut self ) -> Option < O :: WatcherOffset > {
466+ self . done = true ;
462467 self . watcher_offset . take ( )
463468 }
464469
@@ -562,6 +567,7 @@ impl<O: ForestObligation> ObligationForest<O> {
562567 let errors = self
563568 . pending_nodes
564569 . iter ( )
570+ . filter ( |& & index| self . nodes [ index] . state . get ( ) == NodeState :: Pending )
565571 . map ( |& index| Error { error : error. clone ( ) , backtrace : self . error_at ( index) } )
566572 . collect ( ) ;
567573
@@ -574,7 +580,14 @@ impl<O: ForestObligation> ObligationForest<O> {
574580 where
575581 F : Fn ( & O ) -> P ,
576582 {
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 ( )
578591 }
579592
580593 fn insert_into_error_cache ( & mut self , index : NodeIndex ) {
@@ -595,107 +608,113 @@ impl<O: ForestObligation> ObligationForest<O> {
595608 OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
596609 {
597610 if self . watcher_offset . is_none ( ) {
611+ assert ! ( !self . done) ;
598612 self . watcher_offset = Some ( processor. register_variable_watcher ( ) ) ;
599613 }
600614 let mut errors = vec ! [ ] ;
601615 let mut stalled = true ;
602616
603617 self . unblock_nodes ( processor) ;
604618
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+ }
616633
617- let node = & mut self . nodes [ index] ;
634+ let node = & mut self . nodes [ index] ;
618635
619- if node. state . get ( ) != NodeState :: Pending {
620- continue ;
621- }
636+ if node. state . get ( ) != NodeState :: Pending {
637+ continue ;
638+ }
622639
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+ }
640658 }
641659 }
642660 }
643661 }
644- }
645662
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+ }
657674
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+ }
677695 }
696+ // No change in state.
678697 }
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+ }
693712 }
694713 }
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+ }
699718 }
700719 }
701720 }
0 commit comments