@@ -151,9 +151,8 @@ pub struct ObligationForest<O: ForestObligation> {
151
151
/// comments in `process_obligation` for details.
152
152
active_cache : FxHashMap < O :: Predicate , usize > ,
153
153
154
- /// A scratch vector reused in various operations, to avoid allocating new
155
- /// vectors.
156
- scratch : RefCell < Vec < usize > > ,
154
+ /// A vector reused in compress(), to avoid allocating new vectors.
155
+ node_rewrites : RefCell < Vec < usize > > ,
157
156
158
157
obligation_tree_id_generator : ObligationTreeIdGenerator ,
159
158
@@ -235,10 +234,6 @@ enum NodeState {
235
234
/// This obligation was resolved to an error. Error nodes are
236
235
/// removed from the vector by the compression step.
237
236
Error ,
238
-
239
- /// This is a temporary state used in DFS loops to detect cycles,
240
- /// it should not exist outside of these DFSes.
241
- OnDfsStack ,
242
237
}
243
238
244
239
#[ derive( Debug ) ]
@@ -279,7 +274,7 @@ impl<O: ForestObligation> ObligationForest<O> {
279
274
nodes : vec ! [ ] ,
280
275
done_cache : Default :: default ( ) ,
281
276
active_cache : Default :: default ( ) ,
282
- scratch : RefCell :: new ( vec ! [ ] ) ,
277
+ node_rewrites : RefCell :: new ( vec ! [ ] ) ,
283
278
obligation_tree_id_generator : ( 0 ..) . map ( ObligationTreeId ) ,
284
279
error_cache : Default :: default ( ) ,
285
280
}
@@ -305,9 +300,10 @@ impl<O: ForestObligation> ObligationForest<O> {
305
300
306
301
match self . active_cache . entry ( obligation. as_predicate ( ) . clone ( ) ) {
307
302
Entry :: Occupied ( o) => {
303
+ let index = * o. get ( ) ;
308
304
debug ! ( "register_obligation_at({:?}, {:?}) - duplicate of {:?}!" ,
309
- obligation, parent, o . get ( ) ) ;
310
- let node = & mut self . nodes [ * o . get ( ) ] ;
305
+ obligation, parent, index ) ;
306
+ let node = & mut self . nodes [ index ] ;
311
307
if let Some ( parent_index) = parent {
312
308
// If the node is already in `active_cache`, it has already
313
309
// had its chance to be marked with a parent. So if it's
@@ -342,7 +338,8 @@ impl<O: ForestObligation> ObligationForest<O> {
342
338
if already_failed {
343
339
Err ( ( ) )
344
340
} else {
345
- v. insert ( self . nodes . len ( ) ) ;
341
+ let new_index = self . nodes . len ( ) ;
342
+ v. insert ( new_index) ;
346
343
self . nodes . push ( Node :: new ( parent, obligation, obligation_tree_id) ) ;
347
344
Ok ( ( ) )
348
345
}
@@ -352,15 +349,16 @@ impl<O: ForestObligation> ObligationForest<O> {
352
349
353
350
/// Converts all remaining obligations to the given error.
354
351
pub fn to_errors < E : Clone > ( & mut self , error : E ) -> Vec < Error < O , E > > {
355
- let mut errors = vec ! [ ] ;
356
- for ( index , node) in self . nodes . iter ( ) . enumerate ( ) {
357
- if let NodeState :: Pending = node . state . get ( ) {
358
- errors . push ( Error {
352
+ let errors = self . nodes . iter ( ) . enumerate ( )
353
+ . filter ( | ( _index , node) | node . state . get ( ) == NodeState :: Pending )
354
+ . map ( | ( index , _node ) | {
355
+ Error {
359
356
error : error. clone ( ) ,
360
357
backtrace : self . error_at ( index) ,
361
- } ) ;
362
- }
363
- }
358
+ }
359
+ } )
360
+ . collect ( ) ;
361
+
364
362
let successful_obligations = self . compress ( DoCompleted :: Yes ) ;
365
363
assert ! ( successful_obligations. unwrap( ) . is_empty( ) ) ;
366
364
errors
@@ -370,15 +368,14 @@ impl<O: ForestObligation> ObligationForest<O> {
370
368
pub fn map_pending_obligations < P , F > ( & self , f : F ) -> Vec < P >
371
369
where F : Fn ( & O ) -> P
372
370
{
373
- self . nodes
374
- . iter ( )
375
- . filter ( |n| n. state . get ( ) == NodeState :: Pending )
376
- . map ( |n| f ( & n. obligation ) )
371
+ self . nodes . iter ( )
372
+ . filter ( |node| node. state . get ( ) == NodeState :: Pending )
373
+ . map ( |node| f ( & node. obligation ) )
377
374
. collect ( )
378
375
}
379
376
380
- fn insert_into_error_cache ( & mut self , node_index : usize ) {
381
- let node = & self . nodes [ node_index ] ;
377
+ fn insert_into_error_cache ( & mut self , index : usize ) {
378
+ let node = & self . nodes [ index ] ;
382
379
self . error_cache
383
380
. entry ( node. obligation_tree_id )
384
381
. or_default ( )
@@ -408,10 +405,10 @@ impl<O: ForestObligation> ObligationForest<O> {
408
405
// `self.active_cache`. This means that `self.active_cache` can get
409
406
// out of sync with `nodes`. It's not very common, but it does
410
407
// happen, and code in `compress` has to allow for it.
411
- let result = match node. state . get ( ) {
412
- NodeState :: Pending => processor . process_obligation ( & mut node . obligation ) ,
413
- _ => continue
414
- } ;
408
+ if node. state . get ( ) != NodeState :: Pending {
409
+ continue ;
410
+ }
411
+ let result = processor . process_obligation ( & mut node . obligation ) ;
415
412
416
413
debug ! ( "process_obligations: node {} got result {:?}" , index, result) ;
417
414
@@ -476,64 +473,53 @@ impl<O: ForestObligation> ObligationForest<O> {
476
473
fn process_cycles < P > ( & self , processor : & mut P )
477
474
where P : ObligationProcessor < Obligation =O >
478
475
{
479
- let mut stack = self . scratch . replace ( vec ! [ ] ) ;
480
- debug_assert ! ( stack. is_empty( ) ) ;
476
+ let mut stack = vec ! [ ] ;
481
477
482
478
debug ! ( "process_cycles()" ) ;
483
479
484
480
for ( index, node) in self . nodes . iter ( ) . enumerate ( ) {
485
481
// For some benchmarks this state test is extremely
486
482
// hot. It's a win to handle the no-op cases immediately to avoid
487
483
// the cost of the function call.
488
- match node. state . get ( ) {
489
- // Match arms are in order of frequency. Pending, Success and
490
- // Waiting dominate; the others are rare.
491
- NodeState :: Pending => { } ,
492
- NodeState :: Success => self . find_cycles_from_node ( & mut stack, processor, index) ,
493
- NodeState :: Waiting | NodeState :: Done | NodeState :: Error => { } ,
494
- NodeState :: OnDfsStack => self . find_cycles_from_node ( & mut stack, processor, index) ,
484
+ if node. state . get ( ) == NodeState :: Success {
485
+ self . find_cycles_from_node ( & mut stack, processor, index) ;
495
486
}
496
487
}
497
488
498
489
debug ! ( "process_cycles: complete" ) ;
499
490
500
491
debug_assert ! ( stack. is_empty( ) ) ;
501
- self . scratch . replace ( stack) ;
502
492
}
503
493
504
494
fn find_cycles_from_node < P > ( & self , stack : & mut Vec < usize > , processor : & mut P , index : usize )
505
495
where P : ObligationProcessor < Obligation =O >
506
496
{
507
497
let node = & self . nodes [ index] ;
508
- match node. state . get ( ) {
509
- NodeState :: OnDfsStack => {
510
- let rpos = stack. iter ( ) . rposition ( |& n| n == index) . unwrap ( ) ;
511
- processor. process_backedge ( stack[ rpos..] . iter ( ) . map ( GetObligation ( & self . nodes ) ) ,
512
- PhantomData ) ;
513
- }
514
- NodeState :: Success => {
515
- node. state . set ( NodeState :: OnDfsStack ) ;
516
- stack. push ( index) ;
517
- for & index in node. dependents . iter ( ) {
518
- self . find_cycles_from_node ( stack, processor, index) ;
498
+ if node. state . get ( ) == NodeState :: Success {
499
+ match stack. iter ( ) . rposition ( |& n| n == index) {
500
+ None => {
501
+ stack. push ( index) ;
502
+ for & index in node. dependents . iter ( ) {
503
+ self . find_cycles_from_node ( stack, processor, index) ;
504
+ }
505
+ stack. pop ( ) ;
506
+ node. state . set ( NodeState :: Done ) ;
507
+ }
508
+ Some ( rpos) => {
509
+ // Cycle detected.
510
+ processor. process_backedge (
511
+ stack[ rpos..] . iter ( ) . map ( GetObligation ( & self . nodes ) ) ,
512
+ PhantomData
513
+ ) ;
519
514
}
520
- stack. pop ( ) ;
521
- node. state . set ( NodeState :: Done ) ;
522
- } ,
523
- NodeState :: Waiting | NodeState :: Pending => {
524
- // This node is still reachable from some pending node. We
525
- // will get to it when they are all processed.
526
- }
527
- NodeState :: Done | NodeState :: Error => {
528
- // Already processed that node.
529
515
}
530
- } ;
516
+ }
531
517
}
532
518
533
519
/// Returns a vector of obligations for `p` and all of its
534
520
/// ancestors, putting them into the error state in the process.
535
521
fn error_at ( & self , mut index : usize ) -> Vec < O > {
536
- let mut error_stack = self . scratch . replace ( vec ! [ ] ) ;
522
+ let mut error_stack: Vec < usize > = vec ! [ ] ;
537
523
let mut trace = vec ! [ ] ;
538
524
539
525
loop {
@@ -554,23 +540,32 @@ impl<O: ForestObligation> ObligationForest<O> {
554
540
555
541
while let Some ( index) = error_stack. pop ( ) {
556
542
let node = & self . nodes [ index] ;
557
- match node. state . get ( ) {
558
- NodeState :: Error => continue ,
559
- _ => node. state . set ( NodeState :: Error ) ,
543
+ if node. state . get ( ) != NodeState :: Error {
544
+ node . state . set ( NodeState :: Error ) ;
545
+ error_stack . extend ( node. dependents . iter ( ) ) ;
560
546
}
561
-
562
- error_stack. extend ( node. dependents . iter ( ) ) ;
563
547
}
564
548
565
- self . scratch . replace ( error_stack) ;
566
549
trace
567
550
}
568
551
569
552
// This always-inlined function is for the hot call site.
570
553
#[ inline( always) ]
571
554
fn inlined_mark_neighbors_as_waiting_from ( & self , node : & Node < O > ) {
572
555
for & index in node. dependents . iter ( ) {
573
- self . mark_as_waiting_from ( & self . nodes [ index] ) ;
556
+ let node = & self . nodes [ index] ;
557
+ match node. state . get ( ) {
558
+ NodeState :: Waiting | NodeState :: Error => { }
559
+ NodeState :: Success => {
560
+ node. state . set ( NodeState :: Waiting ) ;
561
+ // This call site is cold.
562
+ self . uninlined_mark_neighbors_as_waiting_from ( node) ;
563
+ }
564
+ NodeState :: Pending | NodeState :: Done => {
565
+ // This call site is cold.
566
+ self . uninlined_mark_neighbors_as_waiting_from ( node) ;
567
+ }
568
+ }
574
569
}
575
570
}
576
571
@@ -596,37 +591,28 @@ impl<O: ForestObligation> ObligationForest<O> {
596
591
}
597
592
}
598
593
599
- fn mark_as_waiting_from ( & self , node : & Node < O > ) {
600
- match node. state . get ( ) {
601
- NodeState :: Waiting | NodeState :: Error | NodeState :: OnDfsStack => return ,
602
- NodeState :: Success => node. state . set ( NodeState :: Waiting ) ,
603
- NodeState :: Pending | NodeState :: Done => { } ,
604
- }
605
-
606
- // This call site is cold.
607
- self . uninlined_mark_neighbors_as_waiting_from ( node) ;
608
- }
609
-
610
- /// Compresses the vector, removing all popped nodes. This adjusts
611
- /// the indices and hence invalidates any outstanding
612
- /// indices. Cannot be used during a transaction.
594
+ /// Compresses the vector, removing all popped nodes. This adjusts the
595
+ /// indices and hence invalidates any outstanding indices.
613
596
///
614
597
/// Beforehand, all nodes must be marked as `Done` and no cycles
615
598
/// on these nodes may be present. This is done by e.g., `process_cycles`.
616
599
#[ inline( never) ]
617
600
fn compress ( & mut self , do_completed : DoCompleted ) -> Option < Vec < O > > {
618
- let nodes_len = self . nodes . len ( ) ;
619
- let mut node_rewrites: Vec < _ > = self . scratch . replace ( vec ! [ ] ) ;
620
- node_rewrites. extend ( 0 ..nodes_len) ;
601
+ let orig_nodes_len = self . nodes . len ( ) ;
602
+ let mut node_rewrites: Vec < _ > = self . node_rewrites . replace ( vec ! [ ] ) ;
603
+ debug_assert ! ( node_rewrites. is_empty( ) ) ;
604
+ node_rewrites. extend ( 0 ..orig_nodes_len) ;
621
605
let mut dead_nodes = 0 ;
606
+ let mut removed_done_obligations: Vec < O > = vec ! [ ] ;
622
607
623
- // Now move all popped nodes to the end. Try to keep the order.
608
+ // Now move all Done/Error nodes to the end, preserving the order of
609
+ // the Pending/Waiting nodes.
624
610
//
625
611
// LOOP INVARIANT:
626
612
// self.nodes[0..index - dead_nodes] are the first remaining nodes
627
613
// self.nodes[index - dead_nodes..index] are all dead
628
614
// self.nodes[index..] are unchanged
629
- for index in 0 ..self . nodes . len ( ) {
615
+ for index in 0 ..orig_nodes_len {
630
616
let node = & self . nodes [ index] ;
631
617
match node. state . get ( ) {
632
618
NodeState :: Pending | NodeState :: Waiting => {
@@ -637,7 +623,7 @@ impl<O: ForestObligation> ObligationForest<O> {
637
623
}
638
624
NodeState :: Done => {
639
625
// This lookup can fail because the contents of
640
- // `self.active_cache` is not guaranteed to match those of
626
+ // `self.active_cache` are not guaranteed to match those of
641
627
// `self.nodes`. See the comment in `process_obligation`
642
628
// for more details.
643
629
if let Some ( ( predicate, _) ) =
@@ -647,61 +633,50 @@ impl<O: ForestObligation> ObligationForest<O> {
647
633
} else {
648
634
self . done_cache . insert ( node. obligation . as_predicate ( ) . clone ( ) ) ;
649
635
}
650
- node_rewrites[ index] = nodes_len;
636
+ if do_completed == DoCompleted :: Yes {
637
+ // Extract the success stories.
638
+ removed_done_obligations. push ( node. obligation . clone ( ) ) ;
639
+ }
640
+ node_rewrites[ index] = orig_nodes_len;
651
641
dead_nodes += 1 ;
652
642
}
653
643
NodeState :: Error => {
654
644
// We *intentionally* remove the node from the cache at this point. Otherwise
655
645
// tests must come up with a different type on every type error they
656
646
// check against.
657
647
self . active_cache . remove ( node. obligation . as_predicate ( ) ) ;
658
- node_rewrites[ index] = nodes_len;
659
- dead_nodes += 1 ;
660
648
self . insert_into_error_cache ( index) ;
649
+ node_rewrites[ index] = orig_nodes_len;
650
+ dead_nodes += 1 ;
661
651
}
662
- NodeState :: OnDfsStack | NodeState :: Success => unreachable ! ( )
652
+ NodeState :: Success => unreachable ! ( )
663
653
}
664
654
}
665
655
666
- // No compression needed.
667
- if dead_nodes == 0 {
668
- node_rewrites. truncate ( 0 ) ;
669
- self . scratch . replace ( node_rewrites) ;
670
- return if do_completed == DoCompleted :: Yes { Some ( vec ! [ ] ) } else { None } ;
656
+ if dead_nodes > 0 {
657
+ // Remove the dead nodes and rewrite indices.
658
+ self . nodes . truncate ( orig_nodes_len - dead_nodes) ;
659
+ self . apply_rewrites ( & node_rewrites) ;
671
660
}
672
661
673
- // Pop off all the nodes we killed and extract the success stories.
674
- let successful = if do_completed == DoCompleted :: Yes {
675
- Some ( ( 0 ..dead_nodes)
676
- . map ( |_| self . nodes . pop ( ) . unwrap ( ) )
677
- . flat_map ( |node| {
678
- match node. state . get ( ) {
679
- NodeState :: Error => None ,
680
- NodeState :: Done => Some ( node. obligation ) ,
681
- _ => unreachable ! ( )
682
- }
683
- } )
684
- . collect ( ) )
685
- } else {
686
- self . nodes . truncate ( self . nodes . len ( ) - dead_nodes) ;
687
- None
688
- } ;
689
- self . apply_rewrites ( & node_rewrites) ;
690
-
691
662
node_rewrites. truncate ( 0 ) ;
692
- self . scratch . replace ( node_rewrites) ;
663
+ self . node_rewrites . replace ( node_rewrites) ;
693
664
694
- successful
665
+ if do_completed == DoCompleted :: Yes {
666
+ Some ( removed_done_obligations)
667
+ } else {
668
+ None
669
+ }
695
670
}
696
671
697
672
fn apply_rewrites ( & mut self , node_rewrites : & [ usize ] ) {
698
- let nodes_len = node_rewrites. len ( ) ;
673
+ let orig_nodes_len = node_rewrites. len ( ) ;
699
674
700
675
for node in & mut self . nodes {
701
676
let mut i = 0 ;
702
677
while i < node. dependents . len ( ) {
703
678
let new_index = node_rewrites[ node. dependents [ i] ] ;
704
- if new_index >= nodes_len {
679
+ if new_index >= orig_nodes_len {
705
680
node. dependents . swap_remove ( i) ;
706
681
if i == 0 && node. has_parent {
707
682
// We just removed the parent.
@@ -718,7 +693,7 @@ impl<O: ForestObligation> ObligationForest<O> {
718
693
// removal of nodes within `compress` can fail. See above.
719
694
self . active_cache . retain ( |_predicate, index| {
720
695
let new_index = node_rewrites[ * index] ;
721
- if new_index >= nodes_len {
696
+ if new_index >= orig_nodes_len {
722
697
false
723
698
} else {
724
699
* index = new_index;
0 commit comments