11use std:: cmp:: Ordering ;
22use std:: collections:: VecDeque ;
3- use std:: iter;
43use std:: ops:: { Index , IndexMut } ;
4+ use std:: { iter, slice} ;
55
66use rustc_data_structures:: captures:: Captures ;
77use rustc_data_structures:: fx:: FxHashSet ;
@@ -389,34 +389,26 @@ impl BasicCoverageBlockData {
389389/// indicates whether that block can potentially be combined into the same BCB
390390/// as its sole successor.
391391#[ derive( Clone , Copy , Debug ) ]
392- enum CoverageSuccessors < ' a > {
393- /// The terminator has exactly one straight-line successor, so its block can
394- /// potentially be combined into the same BCB as that successor.
395- Chainable ( BasicBlock ) ,
396- /// The block cannot be combined into the same BCB as its successor(s).
397- NotChainable ( & ' a [ BasicBlock ] ) ,
398- /// Yield terminators are not chainable, and their execution count can also
399- /// differ from the execution count of their out-edge.
400- Yield ( BasicBlock ) ,
392+ struct CoverageSuccessors < ' a > {
393+ /// Coverage-relevant successors of the corresponding terminator.
394+ /// There might be 0, 1, or multiple targets.
395+ targets : & ' a [ BasicBlock ] ,
396+ /// `Yield` terminators are not chainable, because their sole out-edge is
397+ /// only followed if/when the generator is resumed after the yield.
398+ is_yield : bool ,
401399}
402400
403401impl CoverageSuccessors < ' _ > {
404402 fn is_chainable ( & self ) -> bool {
405- match self {
406- Self :: Chainable ( _) => true ,
407- Self :: NotChainable ( _) => false ,
408- Self :: Yield ( _) => false ,
409- }
403+ // If a terminator is out-summable and has exactly one out-edge, then
404+ // it is eligible to be chained into its successor block.
405+ self . is_out_summable ( ) && self . targets . len ( ) == 1
410406 }
411407
412408 /// Returns true if the terminator itself is assumed to have the same
413409 /// execution count as the sum of its out-edges (assuming no panics).
414410 fn is_out_summable ( & self ) -> bool {
415- match self {
416- Self :: Chainable ( _) => true ,
417- Self :: NotChainable ( _) => true ,
418- Self :: Yield ( _) => false ,
419- }
411+ !self . is_yield && !self . targets . is_empty ( )
420412 }
421413}
422414
@@ -425,12 +417,7 @@ impl IntoIterator for CoverageSuccessors<'_> {
425417 type IntoIter = impl DoubleEndedIterator < Item = Self :: Item > ;
426418
427419 fn into_iter ( self ) -> Self :: IntoIter {
428- match self {
429- Self :: Chainable ( bb) | Self :: Yield ( bb) => {
430- Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) )
431- }
432- Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
433- }
420+ self . targets . iter ( ) . copied ( )
434421 }
435422}
436423
@@ -440,48 +427,44 @@ impl IntoIterator for CoverageSuccessors<'_> {
440427// `catch_unwind()` handlers.
441428fn bcb_filtered_successors < ' a , ' tcx > ( terminator : & ' a Terminator < ' tcx > ) -> CoverageSuccessors < ' a > {
442429 use TerminatorKind :: * ;
443- match terminator. kind {
430+ let mut is_yield = false ;
431+ let targets = match & terminator. kind {
444432 // A switch terminator can have many coverage-relevant successors.
445- // (If there is exactly one successor, we still treat it as not chainable.)
446- SwitchInt { ref targets, .. } => CoverageSuccessors :: NotChainable ( targets. all_targets ( ) ) ,
433+ SwitchInt { targets, .. } => targets. all_targets ( ) ,
447434
448435 // A yield terminator has exactly 1 successor, but should not be chained,
449436 // because its resume edge has a different execution count.
450- Yield { resume, .. } => CoverageSuccessors :: Yield ( resume) ,
437+ Yield { resume, .. } => {
438+ is_yield = true ;
439+ slice:: from_ref ( resume)
440+ }
451441
452442 // These terminators have exactly one coverage-relevant successor,
453443 // and can be chained into it.
454444 Assert { target, .. }
455445 | Drop { target, .. }
456446 | FalseEdge { real_target : target, .. }
457447 | FalseUnwind { real_target : target, .. }
458- | Goto { target } => CoverageSuccessors :: Chainable ( target) ,
448+ | Goto { target } => slice :: from_ref ( target) ,
459449
460450 // A call terminator can normally be chained, except when it has no
461451 // successor because it is known to diverge.
462- Call { target : maybe_target, .. } => match maybe_target {
463- Some ( target) => CoverageSuccessors :: Chainable ( target) ,
464- None => CoverageSuccessors :: NotChainable ( & [ ] ) ,
465- } ,
452+ Call { target : maybe_target, .. } => maybe_target. as_slice ( ) ,
466453
467454 // An inline asm terminator can normally be chained, except when it
468455 // diverges or uses asm goto.
469- InlineAsm { ref targets, .. } => {
470- if let [ target] = targets[ ..] {
471- CoverageSuccessors :: Chainable ( target)
472- } else {
473- CoverageSuccessors :: NotChainable ( targets)
474- }
475- }
456+ InlineAsm { targets, .. } => & targets,
476457
477458 // These terminators have no coverage-relevant successors.
478459 CoroutineDrop
479460 | Return
480461 | TailCall { .. }
481462 | Unreachable
482463 | UnwindResume
483- | UnwindTerminate ( _) => CoverageSuccessors :: NotChainable ( & [ ] ) ,
484- }
464+ | UnwindTerminate ( _) => & [ ] ,
465+ } ;
466+
467+ CoverageSuccessors { targets, is_yield }
485468}
486469
487470/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
0 commit comments