@@ -149,8 +149,8 @@ pub struct ObligationForest<O: ForestObligation> {
149
149
/// comments in `process_obligation` for details.
150
150
active_cache : FxHashMap < O :: CacheKey , usize > ,
151
151
152
- /// A vector reused in compress(), to avoid allocating new vectors.
153
- node_rewrites : Vec < usize > ,
152
+ /// A vector reused in compress() and find_cycles_from_node() , to avoid allocating new vectors.
153
+ reused_node_vec : Vec < usize > ,
154
154
155
155
obligation_tree_id_generator : ObligationTreeIdGenerator ,
156
156
@@ -251,12 +251,22 @@ enum NodeState {
251
251
Error ,
252
252
}
253
253
254
+ /// This trait allows us to have two different Outcome types:
255
+ /// - the normal one that does as little as possible
256
+ /// - one for tests that does some additional work and checking
257
+ pub trait OutcomeTrait {
258
+ type Error ;
259
+ type Obligation ;
260
+
261
+ fn new ( ) -> Self ;
262
+ fn mark_not_stalled ( & mut self ) ;
263
+ fn is_stalled ( & self ) -> bool ;
264
+ fn record_completed ( & mut self , outcome : & Self :: Obligation ) ;
265
+ fn record_error ( & mut self , error : Self :: Error ) ;
266
+ }
267
+
254
268
#[ derive( Debug ) ]
255
269
pub struct Outcome < O , E > {
256
- /// Obligations that were completely evaluated, including all
257
- /// (transitive) subobligations. Only computed if requested.
258
- pub completed : Option < Vec < O > > ,
259
-
260
270
/// Backtrace of obligations that were found to be in error.
261
271
pub errors : Vec < Error < O , E > > ,
262
272
@@ -269,12 +279,29 @@ pub struct Outcome<O, E> {
269
279
pub stalled : bool ,
270
280
}
271
281
272
- /// Should `process_obligations` compute the `Outcome::completed` field of its
273
- /// result?
274
- #[ derive( PartialEq ) ]
275
- pub enum DoCompleted {
276
- No ,
277
- Yes ,
282
+ impl < O , E > OutcomeTrait for Outcome < O , E > {
283
+ type Error = Error < O , E > ;
284
+ type Obligation = O ;
285
+
286
+ fn new ( ) -> Self {
287
+ Self { stalled : true , errors : vec ! [ ] }
288
+ }
289
+
290
+ fn mark_not_stalled ( & mut self ) {
291
+ self . stalled = false ;
292
+ }
293
+
294
+ fn is_stalled ( & self ) -> bool {
295
+ self . stalled
296
+ }
297
+
298
+ fn record_completed ( & mut self , _outcome : & Self :: Obligation ) {
299
+ // do nothing
300
+ }
301
+
302
+ fn record_error ( & mut self , error : Self :: Error ) {
303
+ self . errors . push ( error)
304
+ }
278
305
}
279
306
280
307
#[ derive( Debug , PartialEq , Eq ) ]
@@ -289,7 +316,7 @@ impl<O: ForestObligation> ObligationForest<O> {
289
316
nodes : vec ! [ ] ,
290
317
done_cache : Default :: default ( ) ,
291
318
active_cache : Default :: default ( ) ,
292
- node_rewrites : vec ! [ ] ,
319
+ reused_node_vec : vec ! [ ] ,
293
320
obligation_tree_id_generator : ( 0 ..) . map ( ObligationTreeId ) ,
294
321
error_cache : Default :: default ( ) ,
295
322
}
@@ -363,8 +390,7 @@ impl<O: ForestObligation> ObligationForest<O> {
363
390
. map ( |( index, _node) | Error { error : error. clone ( ) , backtrace : self . error_at ( index) } )
364
391
. collect ( ) ;
365
392
366
- let successful_obligations = self . compress ( DoCompleted :: Yes ) ;
367
- assert ! ( successful_obligations. unwrap( ) . is_empty( ) ) ;
393
+ self . compress ( |_| assert ! ( false ) ) ;
368
394
errors
369
395
}
370
396
@@ -392,16 +418,12 @@ impl<O: ForestObligation> ObligationForest<O> {
392
418
/// be called in a loop until `outcome.stalled` is false.
393
419
///
394
420
/// This _cannot_ be unrolled (presently, at least).
395
- pub fn process_obligations < P > (
396
- & mut self ,
397
- processor : & mut P ,
398
- do_completed : DoCompleted ,
399
- ) -> Outcome < O , P :: Error >
421
+ pub fn process_obligations < P , OUT > ( & mut self , processor : & mut P ) -> OUT
400
422
where
401
423
P : ObligationProcessor < Obligation = O > ,
424
+ OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
402
425
{
403
- let mut errors = vec ! [ ] ;
404
- let mut stalled = true ;
426
+ let mut outcome = OUT :: new ( ) ;
405
427
406
428
// Note that the loop body can append new nodes, and those new nodes
407
429
// will then be processed by subsequent iterations of the loop.
@@ -429,7 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
429
451
}
430
452
ProcessResult :: Changed ( children) => {
431
453
// We are not (yet) stalled.
432
- stalled = false ;
454
+ outcome . mark_not_stalled ( ) ;
433
455
node. state . set ( NodeState :: Success ) ;
434
456
435
457
for child in children {
@@ -442,28 +464,22 @@ impl<O: ForestObligation> ObligationForest<O> {
442
464
}
443
465
}
444
466
ProcessResult :: Error ( err) => {
445
- stalled = false ;
446
- errors . push ( Error { error : err, backtrace : self . error_at ( index) } ) ;
467
+ outcome . mark_not_stalled ( ) ;
468
+ outcome . record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
447
469
}
448
470
}
449
471
index += 1 ;
450
472
}
451
473
452
- if stalled {
453
- // There's no need to perform marking, cycle processing and compression when nothing
454
- // changed.
455
- return Outcome {
456
- completed : if do_completed == DoCompleted :: Yes { Some ( vec ! [ ] ) } else { None } ,
457
- errors,
458
- stalled,
459
- } ;
474
+ // There's no need to perform marking, cycle processing and compression when nothing
475
+ // changed.
476
+ if !outcome. is_stalled ( ) {
477
+ self . mark_successes ( ) ;
478
+ self . process_cycles ( processor) ;
479
+ self . compress ( |obl| outcome. record_completed ( obl) ) ;
460
480
}
461
481
462
- self . mark_successes ( ) ;
463
- self . process_cycles ( processor) ;
464
- let completed = self . compress ( do_completed) ;
465
-
466
- Outcome { completed, errors, stalled }
482
+ outcome
467
483
}
468
484
469
485
/// Returns a vector of obligations for `p` and all of its
@@ -526,7 +542,6 @@ impl<O: ForestObligation> ObligationForest<O> {
526
542
let node = & self . nodes [ index] ;
527
543
let state = node. state . get ( ) ;
528
544
if state == NodeState :: Success {
529
- node. state . set ( NodeState :: Waiting ) ;
530
545
// This call site is cold.
531
546
self . uninlined_mark_dependents_as_waiting ( node) ;
532
547
} else {
@@ -538,17 +553,18 @@ impl<O: ForestObligation> ObligationForest<O> {
538
553
// This never-inlined function is for the cold call site.
539
554
#[ inline( never) ]
540
555
fn uninlined_mark_dependents_as_waiting ( & self , node : & Node < O > ) {
556
+ // Mark node Waiting in the cold uninlined code instead of the hot inlined
557
+ node. state . set ( NodeState :: Waiting ) ;
541
558
self . inlined_mark_dependents_as_waiting ( node)
542
559
}
543
560
544
561
/// Report cycles between all `Success` nodes, and convert all `Success`
545
562
/// nodes to `Done`. This must be called after `mark_successes`.
546
- fn process_cycles < P > ( & self , processor : & mut P )
563
+ fn process_cycles < P > ( & mut self , processor : & mut P )
547
564
where
548
565
P : ObligationProcessor < Obligation = O > ,
549
566
{
550
- let mut stack = vec ! [ ] ;
551
-
567
+ let mut stack = std:: mem:: take ( & mut self . reused_node_vec ) ;
552
568
for ( index, node) in self . nodes . iter ( ) . enumerate ( ) {
553
569
// For some benchmarks this state test is extremely hot. It's a win
554
570
// to handle the no-op cases immediately to avoid the cost of the
@@ -559,6 +575,7 @@ impl<O: ForestObligation> ObligationForest<O> {
559
575
}
560
576
561
577
debug_assert ! ( stack. is_empty( ) ) ;
578
+ self . reused_node_vec = stack;
562
579
}
563
580
564
581
fn find_cycles_from_node < P > ( & self , stack : & mut Vec < usize > , processor : & mut P , index : usize )
@@ -591,13 +608,12 @@ impl<O: ForestObligation> ObligationForest<O> {
591
608
/// indices and hence invalidates any outstanding indices. `process_cycles`
592
609
/// must be run beforehand to remove any cycles on `Success` nodes.
593
610
#[ inline( never) ]
594
- fn compress ( & mut self , do_completed : DoCompleted ) -> Option < Vec < O > > {
611
+ fn compress ( & mut self , mut outcome_cb : impl FnMut ( & O ) ) {
595
612
let orig_nodes_len = self . nodes . len ( ) ;
596
- let mut node_rewrites: Vec < _ > = std:: mem:: take ( & mut self . node_rewrites ) ;
613
+ let mut node_rewrites: Vec < _ > = std:: mem:: take ( & mut self . reused_node_vec ) ;
597
614
debug_assert ! ( node_rewrites. is_empty( ) ) ;
598
615
node_rewrites. extend ( 0 ..orig_nodes_len) ;
599
616
let mut dead_nodes = 0 ;
600
- let mut removed_done_obligations: Vec < O > = vec ! [ ] ;
601
617
602
618
// Move removable nodes to the end, preserving the order of the
603
619
// remaining nodes.
@@ -627,10 +643,8 @@ impl<O: ForestObligation> ObligationForest<O> {
627
643
} else {
628
644
self . done_cache . insert ( node. obligation . as_cache_key ( ) . clone ( ) ) ;
629
645
}
630
- if do_completed == DoCompleted :: Yes {
631
- // Extract the success stories.
632
- removed_done_obligations. push ( node. obligation . clone ( ) ) ;
633
- }
646
+ // Extract the success stories.
647
+ outcome_cb ( & node. obligation ) ;
634
648
node_rewrites[ index] = orig_nodes_len;
635
649
dead_nodes += 1 ;
636
650
}
@@ -654,9 +668,7 @@ impl<O: ForestObligation> ObligationForest<O> {
654
668
}
655
669
656
670
node_rewrites. truncate ( 0 ) ;
657
- self . node_rewrites = node_rewrites;
658
-
659
- if do_completed == DoCompleted :: Yes { Some ( removed_done_obligations) } else { None }
671
+ self . reused_node_vec = node_rewrites;
660
672
}
661
673
662
674
fn apply_rewrites ( & mut self , node_rewrites : & [ usize ] ) {
0 commit comments