@@ -39,6 +39,7 @@ impl CoverageGraph {
39
39
let bcb_data = & bcbs[ bcb] ;
40
40
let mut bcb_successors = Vec :: new ( ) ;
41
41
for successor in bcb_filtered_successors ( mir_body[ bcb_data. last_bb ( ) ] . terminator ( ) )
42
+ . into_iter ( )
42
43
. filter_map ( |successor_bb| bb_to_bcb[ successor_bb] )
43
44
{
44
45
if !seen[ successor] {
@@ -122,11 +123,8 @@ impl CoverageGraph {
122
123
123
124
let term = mir_body[ bb] . terminator ( ) ;
124
125
125
- match term. kind {
126
- TerminatorKind :: Return { .. }
127
- | TerminatorKind :: UnwindTerminate ( _)
128
- | TerminatorKind :: Yield { .. }
129
- | TerminatorKind :: SwitchInt { .. } => {
126
+ match bcb_filtered_successors ( term) {
127
+ CoverageSuccessors :: NotChainable ( _) => {
130
128
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
131
129
// current sequence of `basic_blocks` gathered to this point, as a new
132
130
// `BasicCoverageBlockData`.
@@ -153,16 +151,7 @@ impl CoverageGraph {
153
151
// for a coverage region containing the `Terminator` that began the panic. This
154
152
// is as intended. (See Issue #78544 for a possible future option to support
155
153
// coverage in test programs that panic.)
156
- TerminatorKind :: Goto { .. }
157
- | TerminatorKind :: UnwindResume
158
- | TerminatorKind :: Unreachable
159
- | TerminatorKind :: Drop { .. }
160
- | TerminatorKind :: Call { .. }
161
- | TerminatorKind :: CoroutineDrop
162
- | TerminatorKind :: Assert { .. }
163
- | TerminatorKind :: FalseEdge { .. }
164
- | TerminatorKind :: FalseUnwind { .. }
165
- | TerminatorKind :: InlineAsm { .. } => { }
154
+ CoverageSuccessors :: Chainable ( _) => { }
166
155
}
167
156
}
168
157
@@ -349,22 +338,73 @@ impl BasicCoverageBlockData {
349
338
}
350
339
}
351
340
341
+ /// Holds the coverage-relevant successors of a basic block's terminator, and
342
+ /// indicates whether that block can potentially be combined into the same BCB
343
+ /// as its sole successor.
344
+ #[ derive( Clone , Copy , Debug ) ]
345
+ enum CoverageSuccessors < ' a > {
346
+ /// The terminator has exactly one straight-line successor, so its block can
347
+ /// potentially be combined into the same BCB as that successor.
348
+ Chainable ( BasicBlock ) ,
349
+ /// The block cannot be combined into the same BCB as its successor(s).
350
+ NotChainable ( & ' a [ BasicBlock ] ) ,
351
+ }
352
+
353
+ impl CoverageSuccessors < ' _ > {
354
+ fn maybe_chainable ( maybe_target : Option < BasicBlock > ) -> Self {
355
+ match maybe_target {
356
+ Some ( target) => Self :: Chainable ( target) ,
357
+ None => Self :: NotChainable ( & [ ] ) ,
358
+ }
359
+ }
360
+ }
361
+
362
+ impl IntoIterator for CoverageSuccessors < ' _ > {
363
+ type Item = BasicBlock ;
364
+ type IntoIter = impl DoubleEndedIterator < Item = Self :: Item > ;
365
+
366
+ fn into_iter ( self ) -> Self :: IntoIter {
367
+ match self {
368
+ Self :: Chainable ( bb) => Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) ) ,
369
+ Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
370
+ }
371
+ }
372
+ }
373
+
352
374
// Returns the subset of a block's successors that are relevant to the coverage
353
375
// graph, i.e. those that do not represent unwinds or false edges.
354
376
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
355
377
// `catch_unwind()` handlers.
356
- fn bcb_filtered_successors < ' a , ' tcx > (
357
- terminator : & ' a Terminator < ' tcx > ,
358
- ) -> impl Iterator < Item = BasicBlock > + Captures < ' a > + Captures < ' tcx > {
359
- let take_n_successors = match terminator. kind {
360
- // SwitchInt successors are never unwinds, so all of them should be traversed.
361
- TerminatorKind :: SwitchInt { .. } => usize:: MAX ,
362
- // For all other kinds, return only the first successor (if any), ignoring any
363
- // unwind successors.
364
- _ => 1 ,
365
- } ;
366
-
367
- terminator. successors ( ) . take ( take_n_successors)
378
+ fn bcb_filtered_successors < ' a , ' tcx > ( terminator : & ' a Terminator < ' tcx > ) -> CoverageSuccessors < ' a > {
379
+ use TerminatorKind :: * ;
380
+ match terminator. kind {
381
+ // A switch terminator can have many coverage-relevant successors.
382
+ // (If there is exactly one successor, we still treat it as not chainable.)
383
+ SwitchInt { ref targets, .. } => CoverageSuccessors :: NotChainable ( targets. all_targets ( ) ) ,
384
+
385
+ // A yield terminator has exactly 1 successor, but should not be chained,
386
+ // because its resume edge has a different execution count.
387
+ Yield { ref resume, .. } => CoverageSuccessors :: NotChainable ( std:: slice:: from_ref ( resume) ) ,
388
+
389
+ // These terminators have exactly one coverage-relevant successor,
390
+ // and can be chained into it.
391
+ Assert { target, .. }
392
+ | Drop { target, .. }
393
+ | FalseEdge { real_target : target, .. }
394
+ | FalseUnwind { real_target : target, .. }
395
+ | Goto { target } => CoverageSuccessors :: Chainable ( target) ,
396
+
397
+ // These terminators can normally be chained, except when they have no
398
+ // successor because they are known to diverge.
399
+ Call { target : maybe_target, .. } | InlineAsm { destination : maybe_target, .. } => {
400
+ CoverageSuccessors :: maybe_chainable ( maybe_target)
401
+ }
402
+
403
+ // These terminators have no coverage-relevant successors.
404
+ CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate ( _) => {
405
+ CoverageSuccessors :: NotChainable ( & [ ] )
406
+ }
407
+ }
368
408
}
369
409
370
410
/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
@@ -542,7 +582,7 @@ fn short_circuit_preorder<'a, 'tcx, F, Iter>(
542
582
) -> impl Iterator < Item = BasicBlock > + Captures < ' a > + Captures < ' tcx >
543
583
where
544
584
F : Fn ( BasicBlock ) -> Iter ,
545
- Iter : Iterator < Item = BasicBlock > ,
585
+ Iter : IntoIterator < Item = BasicBlock > ,
546
586
{
547
587
let mut visited = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
548
588
let mut worklist = vec ! [ mir:: START_BLOCK ] ;
0 commit comments