@@ -128,21 +128,19 @@ type ObligationTreeIdGenerator =
128
128
:: std:: iter:: Map < :: std:: ops:: RangeFrom < usize > , fn ( usize ) -> ObligationTreeId > ;
129
129
130
130
pub 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.
139
133
///
140
134
/// `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.
144
138
nodes : Vec < Node < O > > ,
145
139
140
+ /// The process generation is 1 on the first call to `process_obligations`,
141
+ /// 2 on the second call, etc.
142
+ gen : u32 ,
143
+
146
144
/// A cache of predicates that have been successfully completed.
147
145
done_cache : FxHashSet < O :: Predicate > ,
148
146
@@ -211,31 +209,61 @@ impl<O> Node<O> {
211
209
/// represents the current state of processing for the obligation (of
212
210
/// type `O`) associated with this node.
213
211
///
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`.
216
236
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
217
237
enum 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 .
220
240
Pending ,
221
241
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 ) ,
233
245
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.
236
248
Error ,
237
249
}
238
250
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
+
239
267
#[ derive( Debug ) ]
240
268
pub struct Outcome < O , E > {
241
269
/// Obligations that were completely evaluated, including all
@@ -272,6 +300,7 @@ impl<O: ForestObligation> ObligationForest<O> {
272
300
pub fn new ( ) -> ObligationForest < O > {
273
301
ObligationForest {
274
302
nodes : vec ! [ ] ,
303
+ gen : 0 ,
275
304
done_cache : Default :: default ( ) ,
276
305
active_cache : Default :: default ( ) ,
277
306
node_rewrites : RefCell :: new ( vec ! [ ] ) ,
@@ -382,6 +411,18 @@ impl<O: ForestObligation> ObligationForest<O> {
382
411
. insert ( node. obligation . as_predicate ( ) . clone ( ) ) ;
383
412
}
384
413
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
+
385
426
/// Performs a pass through the obligation list. This must
386
427
/// be called in a loop until `outcome.stalled` is false.
387
428
///
@@ -392,6 +433,8 @@ impl<O: ForestObligation> ObligationForest<O> {
392
433
{
393
434
debug ! ( "process_obligations(len={})" , self . nodes. len( ) ) ;
394
435
436
+ self . gen += 1 ;
437
+
395
438
let mut errors = vec ! [ ] ;
396
439
let mut stalled = true ;
397
440
@@ -429,7 +472,7 @@ impl<O: ForestObligation> ObligationForest<O> {
429
472
ProcessResult :: Changed ( children) => {
430
473
// We are not (yet) stalled.
431
474
stalled = false ;
432
- node. state . set ( NodeState :: Success ) ;
475
+ node. state . set ( NodeState :: Success ( Self :: not_waiting ( ) ) ) ;
433
476
434
477
for child in children {
435
478
let st = self . register_obligation_at (
@@ -464,7 +507,7 @@ impl<O: ForestObligation> ObligationForest<O> {
464
507
} ;
465
508
}
466
509
467
- self . mark_as_waiting ( ) ;
510
+ self . mark_still_waiting_nodes ( ) ;
468
511
self . process_cycles ( processor) ;
469
512
let completed = self . compress ( do_completed) ;
470
513
@@ -477,10 +520,8 @@ impl<O: ForestObligation> ObligationForest<O> {
477
520
}
478
521
}
479
522
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`.
484
525
fn process_cycles < P > ( & self , processor : & mut P )
485
526
where P : ObligationProcessor < Obligation =O >
486
527
{
@@ -489,11 +530,13 @@ impl<O: ForestObligation> ObligationForest<O> {
489
530
debug ! ( "process_cycles()" ) ;
490
531
491
532
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
+ }
497
540
}
498
541
}
499
542
@@ -506,22 +549,23 @@ impl<O: ForestObligation> ObligationForest<O> {
506
549
where P : ObligationProcessor < Obligation =O >
507
550
{
508
551
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
+ ) ;
515
568
}
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
- ) ;
525
569
}
526
570
}
527
571
}
@@ -562,62 +606,52 @@ impl<O: ForestObligation> ObligationForest<O> {
562
606
563
607
// This always-inlined function is for the hot call site.
564
608
#[ 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 > ) {
566
610
for & index in node. dependents . iter ( ) {
567
611
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 ( ) ) ) ;
572
615
// 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) ;
578
617
}
579
618
}
580
619
}
581
620
}
582
621
583
622
// This never-inlined function is for the cold call site.
584
623
#[ 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)
587
626
}
588
627
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 ) {
597
632
for node in & self . nodes {
598
633
if node. state . get ( ) == NodeState :: Pending {
599
634
// This call site is hot.
600
- self . inlined_mark_neighbors_as_waiting_from ( node) ;
635
+ self . inlined_mark_dependents_as_still_waiting ( node) ;
601
636
}
602
637
}
603
638
}
604
639
605
640
/// 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.
610
644
#[ inline( never) ]
611
645
fn compress ( & mut self , do_completed : DoCompleted ) -> Option < Vec < O > > {
612
646
let orig_nodes_len = self . nodes . len ( ) ;
613
647
let mut node_rewrites: Vec < _ > = self . node_rewrites . replace ( vec ! [ ] ) ;
614
648
debug_assert ! ( node_rewrites. is_empty( ) ) ;
615
649
node_rewrites. extend ( 0 ..orig_nodes_len) ;
616
650
let mut dead_nodes = 0 ;
617
- let mut removed_done_obligations : Vec < O > = vec ! [ ] ;
651
+ let mut removed_success_obligations : Vec < O > = vec ! [ ] ;
618
652
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.
621
655
//
622
656
// LOOP INVARIANT:
623
657
// self.nodes[0..index - dead_nodes] are the first remaining nodes
@@ -626,13 +660,19 @@ impl<O: ForestObligation> ObligationForest<O> {
626
660
for index in 0 ..orig_nodes_len {
627
661
let node = & self . nodes [ index] ;
628
662
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) => {
630
670
if dead_nodes > 0 {
631
671
self . nodes . swap ( index, index - dead_nodes) ;
632
672
node_rewrites[ index] -= dead_nodes;
633
673
}
634
674
}
635
- NodeState :: Done => {
675
+ NodeState :: Success ( _ ) => {
636
676
// This lookup can fail because the contents of
637
677
// `self.active_cache` are not guaranteed to match those of
638
678
// `self.nodes`. See the comment in `process_obligation`
@@ -646,7 +686,7 @@ impl<O: ForestObligation> ObligationForest<O> {
646
686
}
647
687
if do_completed == DoCompleted :: Yes {
648
688
// Extract the success stories.
649
- removed_done_obligations . push ( node. obligation . clone ( ) ) ;
689
+ removed_success_obligations . push ( node. obligation . clone ( ) ) ;
650
690
}
651
691
node_rewrites[ index] = orig_nodes_len;
652
692
dead_nodes += 1 ;
@@ -660,7 +700,6 @@ impl<O: ForestObligation> ObligationForest<O> {
660
700
node_rewrites[ index] = orig_nodes_len;
661
701
dead_nodes += 1 ;
662
702
}
663
- NodeState :: Success => unreachable ! ( )
664
703
}
665
704
}
666
705
@@ -674,7 +713,7 @@ impl<O: ForestObligation> ObligationForest<O> {
674
713
self . node_rewrites . replace ( node_rewrites) ;
675
714
676
715
if do_completed == DoCompleted :: Yes {
677
- Some ( removed_done_obligations )
716
+ Some ( removed_success_obligations )
678
717
} else {
679
718
None
680
719
}
0 commit comments