@@ -128,21 +128,19 @@ type ObligationTreeIdGenerator =
128128 :: std:: iter:: Map < :: std:: ops:: RangeFrom < usize > , fn ( usize ) -> ObligationTreeId > ;
129129
130130pub struct ObligationForest < O : ForestObligation > {
131- /// The list of obligations. In between calls to
132- /// `process_obligations`, this list only contains nodes in the
133- /// `Pending` or `Success` state (with a non-zero number of
134- /// incomplete children). During processing, some of those nodes
135- /// may be changed to the error state, or we may find that they
136- /// are completed (That is, `num_incomplete_children` drops to 0).
137- /// At the end of processing, those nodes will be removed by a
138- /// call to `compress`.
131+ /// The list of obligations. In between calls to `process_obligations`,
132+ /// this list only contains nodes in the `Pending` or `Success` state.
139133 ///
140134 /// `usize` indices are used here and throughout this module, rather than
141- /// `rustc_index::newtype_index!` indices, because this code is hot enough that the
142- /// `u32`-to-`usize` conversions that would be required are significant,
143- /// and space considerations are not important.
135+ /// `rustc_index::newtype_index!` indices, because this code is hot enough
136+ /// that the `u32`-to-`usize` conversions that would be required are
137+ /// significant, and space considerations are not important.
144138 nodes : Vec < Node < O > > ,
145139
140+ /// The process generation is 1 on the first call to `process_obligations`,
141+ /// 2 on the second call, etc.
142+ gen : u32 ,
143+
146144 /// A cache of predicates that have been successfully completed.
147145 done_cache : FxHashSet < O :: Predicate > ,
148146
@@ -211,31 +209,61 @@ impl<O> Node<O> {
211209/// represents the current state of processing for the obligation (of
212210/// type `O`) associated with this node.
213211///
214- /// Outside of ObligationForest methods, nodes should be either Pending
215- /// or Waiting.
212+ /// The non-`Error` state transitions are as follows.
213+ /// ```
214+ /// (Pre-creation)
215+ /// |
216+ /// | register_obligation_at() (called by process_obligations() and
217+ /// v from outside the crate)
218+ /// Pending
219+ /// |
220+ /// | process_obligations()
221+ /// v
222+ /// Success(not_waiting())
223+ /// | |
224+ /// | | mark_still_waiting_nodes()
225+ /// | v
226+ /// | Success(still_waiting())
227+ /// | |
228+ /// | | compress()
229+ /// v v
230+ /// (Removed)
231+ /// ```
232+ /// The `Error` state can be introduced in several places, via `error_at()`.
233+ ///
234+ /// Outside of `ObligationForest` methods, nodes should be either `Pending` or
235+ /// `Success`.
216236#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
217237enum NodeState {
218- /// Obligations for which selection had not yet returned a
219- /// non-ambiguous result .
238+ /// This obligation has not yet been selected successfully. Cannot have
239+ /// subobligations .
220240 Pending ,
221241
222- /// This obligation was selected successfully, but may or
223- /// may not have subobligations.
224- Success ,
225-
226- /// This obligation was selected successfully, but it has
227- /// a pending subobligation.
228- Waiting ,
229-
230- /// This obligation, along with its subobligations, are complete,
231- /// and will be removed in the next collection.
232- Done ,
242+ /// This obligation was selected successfully, but it may be waiting on one
243+ /// or more pending subobligations, as indicated by the `WaitingState`.
244+ Success ( WaitingState ) ,
233245
234- /// This obligation was resolved to an error. Error nodes are
235- /// removed from the vector by the compression step.
246+ /// This obligation was resolved to an error. It will be removed by the
247+ /// next compression step.
236248 Error ,
237249}
238250
251+ /// Indicates when a `Success` node was last (if ever) waiting on one or more
252+ /// `Pending` nodes. The notion of "when" comes from `ObligationForest::gen`.
253+ /// - 0: "Not waiting". This is a special value, set by `process_obligation`,
254+ /// and usable because generation counting starts at 1.
255+ /// - 1..ObligationForest::gen: "Was waiting" in a previous generation, but
256+ /// waiting no longer. In other words, finished.
257+ /// - ObligationForest::gen: "Still waiting" in this generation.
258+ ///
259+ /// Things to note about this encoding:
260+ /// - Every time `ObligationForest::gen` is incremented, all the "still
261+ /// waiting" nodes automatically become "was waiting".
262+ /// - `ObligationForest::is_still_waiting` is very cheap.
263+ ///
264+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , PartialOrd ) ]
265+ struct WaitingState ( u32 ) ;
266+
239267#[ derive( Debug ) ]
240268pub struct Outcome < O , E > {
241269 /// Obligations that were completely evaluated, including all
@@ -272,6 +300,7 @@ impl<O: ForestObligation> ObligationForest<O> {
272300 pub fn new ( ) -> ObligationForest < O > {
273301 ObligationForest {
274302 nodes : vec ! [ ] ,
303+ gen : 0 ,
275304 done_cache : Default :: default ( ) ,
276305 active_cache : Default :: default ( ) ,
277306 node_rewrites : RefCell :: new ( vec ! [ ] ) ,
@@ -382,6 +411,18 @@ impl<O: ForestObligation> ObligationForest<O> {
382411 . insert ( node. obligation . as_predicate ( ) . clone ( ) ) ;
383412 }
384413
414+ fn not_waiting ( ) -> WaitingState {
415+ WaitingState ( 0 )
416+ }
417+
418+ fn still_waiting ( & self ) -> WaitingState {
419+ WaitingState ( self . gen )
420+ }
421+
422+ fn is_still_waiting ( & self , waiting : WaitingState ) -> bool {
423+ waiting. 0 == self . gen
424+ }
425+
385426 /// Performs a pass through the obligation list. This must
386427 /// be called in a loop until `outcome.stalled` is false.
387428 ///
@@ -392,6 +433,8 @@ impl<O: ForestObligation> ObligationForest<O> {
392433 {
393434 debug ! ( "process_obligations(len={})" , self . nodes. len( ) ) ;
394435
436+ self . gen += 1 ;
437+
395438 let mut errors = vec ! [ ] ;
396439 let mut stalled = true ;
397440
@@ -429,7 +472,7 @@ impl<O: ForestObligation> ObligationForest<O> {
429472 ProcessResult :: Changed ( children) => {
430473 // We are not (yet) stalled.
431474 stalled = false ;
432- node. state . set ( NodeState :: Success ) ;
475+ node. state . set ( NodeState :: Success ( Self :: not_waiting ( ) ) ) ;
433476
434477 for child in children {
435478 let st = self . register_obligation_at (
@@ -464,7 +507,7 @@ impl<O: ForestObligation> ObligationForest<O> {
464507 } ;
465508 }
466509
467- self . mark_as_waiting ( ) ;
510+ self . mark_still_waiting_nodes ( ) ;
468511 self . process_cycles ( processor) ;
469512 let completed = self . compress ( do_completed) ;
470513
@@ -477,10 +520,8 @@ impl<O: ForestObligation> ObligationForest<O> {
477520 }
478521 }
479522
480- /// Mark all `NodeState::Success` nodes as `NodeState::Done` and
481- /// report all cycles between them. This should be called
482- /// after `mark_as_waiting` marks all nodes with pending
483- /// subobligations as NodeState::Waiting.
523+ /// Report cycles between all `Success` nodes that aren't still waiting.
524+ /// This must be called after `mark_still_waiting_nodes`.
484525 fn process_cycles < P > ( & self , processor : & mut P )
485526 where P : ObligationProcessor < Obligation =O >
486527 {
@@ -489,11 +530,13 @@ impl<O: ForestObligation> ObligationForest<O> {
489530 debug ! ( "process_cycles()" ) ;
490531
491532 for ( index, node) in self . nodes . iter ( ) . enumerate ( ) {
492- // For some benchmarks this state test is extremely
493- // hot. It's a win to handle the no-op cases immediately to avoid
494- // the cost of the function call.
495- if node. state . get ( ) == NodeState :: Success {
496- self . find_cycles_from_node ( & mut stack, processor, index) ;
533+ // For some benchmarks this state test is extremely hot. It's a win
534+ // to handle the no-op cases immediately to avoid the cost of the
535+ // function call.
536+ if let NodeState :: Success ( waiting) = node. state . get ( ) {
537+ if !self . is_still_waiting ( waiting) {
538+ self . find_cycles_from_node ( & mut stack, processor, index) ;
539+ }
497540 }
498541 }
499542
@@ -506,22 +549,23 @@ impl<O: ForestObligation> ObligationForest<O> {
506549 where P : ObligationProcessor < Obligation =O >
507550 {
508551 let node = & self . nodes [ index] ;
509- if node. state . get ( ) == NodeState :: Success {
510- match stack. iter ( ) . rposition ( |& n| n == index) {
511- None => {
512- stack. push ( index) ;
513- for & index in node. dependents . iter ( ) {
514- self . find_cycles_from_node ( stack, processor, index) ;
552+ if let NodeState :: Success ( waiting) = node. state . get ( ) {
553+ if !self . is_still_waiting ( waiting) {
554+ match stack. iter ( ) . rposition ( |& n| n == index) {
555+ None => {
556+ stack. push ( index) ;
557+ for & index in node. dependents . iter ( ) {
558+ self . find_cycles_from_node ( stack, processor, index) ;
559+ }
560+ stack. pop ( ) ;
561+ }
562+ Some ( rpos) => {
563+ // Cycle detected.
564+ processor. process_backedge (
565+ stack[ rpos..] . iter ( ) . map ( GetObligation ( & self . nodes ) ) ,
566+ PhantomData
567+ ) ;
515568 }
516- stack. pop ( ) ;
517- node. state . set ( NodeState :: Done ) ;
518- }
519- Some ( rpos) => {
520- // Cycle detected.
521- processor. process_backedge (
522- stack[ rpos..] . iter ( ) . map ( GetObligation ( & self . nodes ) ) ,
523- PhantomData
524- ) ;
525569 }
526570 }
527571 }
@@ -562,62 +606,52 @@ impl<O: ForestObligation> ObligationForest<O> {
562606
563607 // This always-inlined function is for the hot call site.
564608 #[ inline( always) ]
565- fn inlined_mark_neighbors_as_waiting_from ( & self , node : & Node < O > ) {
609+ fn inlined_mark_dependents_as_still_waiting ( & self , node : & Node < O > ) {
566610 for & index in node. dependents . iter ( ) {
567611 let node = & self . nodes [ index] ;
568- match node. state . get ( ) {
569- NodeState :: Waiting | NodeState :: Error => { }
570- NodeState :: Success => {
571- node. state . set ( NodeState :: Waiting ) ;
612+ if let NodeState :: Success ( waiting) = node. state . get ( ) {
613+ if !self . is_still_waiting ( waiting) {
614+ node. state . set ( NodeState :: Success ( self . still_waiting ( ) ) ) ;
572615 // This call site is cold.
573- self . uninlined_mark_neighbors_as_waiting_from ( node) ;
574- }
575- NodeState :: Pending | NodeState :: Done => {
576- // This call site is cold.
577- self . uninlined_mark_neighbors_as_waiting_from ( node) ;
616+ self . uninlined_mark_dependents_as_still_waiting ( node) ;
578617 }
579618 }
580619 }
581620 }
582621
583622 // This never-inlined function is for the cold call site.
584623 #[ inline( never) ]
585- fn uninlined_mark_neighbors_as_waiting_from ( & self , node : & Node < O > ) {
586- self . inlined_mark_neighbors_as_waiting_from ( node)
624+ fn uninlined_mark_dependents_as_still_waiting ( & self , node : & Node < O > ) {
625+ self . inlined_mark_dependents_as_still_waiting ( node)
587626 }
588627
589- /// Marks all nodes that depend on a pending node as `NodeState::Waiting`.
590- fn mark_as_waiting ( & self ) {
591- for node in & self . nodes {
592- if node. state . get ( ) == NodeState :: Waiting {
593- node. state . set ( NodeState :: Success ) ;
594- }
595- }
596-
628+ /// Mark all `Success` nodes that depend on a pending node as still
629+ /// waiting. Upon completion, any `Success` nodes that aren't still waiting
630+ /// can be removed by `compress`.
631+ fn mark_still_waiting_nodes ( & self ) {
597632 for node in & self . nodes {
598633 if node. state . get ( ) == NodeState :: Pending {
599634 // This call site is hot.
600- self . inlined_mark_neighbors_as_waiting_from ( node) ;
635+ self . inlined_mark_dependents_as_still_waiting ( node) ;
601636 }
602637 }
603638 }
604639
605640 /// Compresses the vector, removing all popped nodes. This adjusts the
606- /// indices and hence invalidates any outstanding indices.
607- ///
608- /// Beforehand, all nodes must be marked as `Done` and no cycles
609- /// on these nodes may be present. This is done by e.g., `process_cycles`.
641+ /// indices and hence invalidates any outstanding indices. `process_cycles`
642+ /// must be run beforehand to remove any cycles on not-still-waiting
643+ /// `Success` nodes.
610644 #[ inline( never) ]
611645 fn compress ( & mut self , do_completed : DoCompleted ) -> Option < Vec < O > > {
612646 let orig_nodes_len = self . nodes . len ( ) ;
613647 let mut node_rewrites: Vec < _ > = self . node_rewrites . replace ( vec ! [ ] ) ;
614648 debug_assert ! ( node_rewrites. is_empty( ) ) ;
615649 node_rewrites. extend ( 0 ..orig_nodes_len) ;
616650 let mut dead_nodes = 0 ;
617- let mut removed_done_obligations : Vec < O > = vec ! [ ] ;
651+ let mut removed_success_obligations : Vec < O > = vec ! [ ] ;
618652
619- // Now move all Done/Error nodes to the end, preserving the order of
620- // the Pending/Waiting nodes.
653+ // Move removable nodes to the end, preserving the order of the
654+ // remaining nodes.
621655 //
622656 // LOOP INVARIANT:
623657 // self.nodes[0..index - dead_nodes] are the first remaining nodes
@@ -626,13 +660,19 @@ impl<O: ForestObligation> ObligationForest<O> {
626660 for index in 0 ..orig_nodes_len {
627661 let node = & self . nodes [ index] ;
628662 match node. state . get ( ) {
629- NodeState :: Pending | NodeState :: Waiting => {
663+ NodeState :: Pending => {
664+ if dead_nodes > 0 {
665+ self . nodes . swap ( index, index - dead_nodes) ;
666+ node_rewrites[ index] -= dead_nodes;
667+ }
668+ }
669+ NodeState :: Success ( waiting) if self . is_still_waiting ( waiting) => {
630670 if dead_nodes > 0 {
631671 self . nodes . swap ( index, index - dead_nodes) ;
632672 node_rewrites[ index] -= dead_nodes;
633673 }
634674 }
635- NodeState :: Done => {
675+ NodeState :: Success ( _ ) => {
636676 // This lookup can fail because the contents of
637677 // `self.active_cache` are not guaranteed to match those of
638678 // `self.nodes`. See the comment in `process_obligation`
@@ -646,7 +686,7 @@ impl<O: ForestObligation> ObligationForest<O> {
646686 }
647687 if do_completed == DoCompleted :: Yes {
648688 // Extract the success stories.
649- removed_done_obligations . push ( node. obligation . clone ( ) ) ;
689+ removed_success_obligations . push ( node. obligation . clone ( ) ) ;
650690 }
651691 node_rewrites[ index] = orig_nodes_len;
652692 dead_nodes += 1 ;
@@ -660,7 +700,6 @@ impl<O: ForestObligation> ObligationForest<O> {
660700 node_rewrites[ index] = orig_nodes_len;
661701 dead_nodes += 1 ;
662702 }
663- NodeState :: Success => unreachable ! ( )
664703 }
665704 }
666705
@@ -674,7 +713,7 @@ impl<O: ForestObligation> ObligationForest<O> {
674713 self . node_rewrites . replace ( node_rewrites) ;
675714
676715 if do_completed == DoCompleted :: Yes {
677- Some ( removed_done_obligations )
716+ Some ( removed_success_obligations )
678717 } else {
679718 None
680719 }
0 commit comments