@@ -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,67 @@ 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 IntoIterator for CoverageSuccessors < ' _ > {
354
+ type Item = BasicBlock ;
355
+ type IntoIter = impl DoubleEndedIterator < Item = Self :: Item > ;
356
+
357
+ fn into_iter ( self ) -> Self :: IntoIter {
358
+ match self {
359
+ Self :: Chainable ( bb) => Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) ) ,
360
+ Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
361
+ }
362
+ }
363
+ }
364
+
352
365
// Returns the subset of a block's successors that are relevant to the coverage
353
366
// graph, i.e. those that do not represent unwinds or false edges.
354
367
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
355
368
// `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)
369
+ fn bcb_filtered_successors < ' a , ' tcx > ( terminator : & ' a Terminator < ' tcx > ) -> CoverageSuccessors < ' a > {
370
+ use TerminatorKind :: * ;
371
+ match terminator. kind {
372
+ // A switch terminator can have many coverage-relevant successors.
373
+ // (If there is exactly one successor, we still treat it as not chainable.)
374
+ SwitchInt { ref targets, .. } => CoverageSuccessors :: NotChainable ( targets. all_targets ( ) ) ,
375
+
376
+ // A yield terminator has exactly 1 successor, but should not be chained,
377
+ // because its resume edge has a different execution count.
378
+ Yield { ref resume, .. } => CoverageSuccessors :: NotChainable ( std:: slice:: from_ref ( resume) ) ,
379
+
380
+ // These terminators have exactly one coverage-relevant successor,
381
+ // and can be chained into it.
382
+ Assert { target, .. }
383
+ | Drop { target, .. }
384
+ | FalseEdge { real_target : target, .. }
385
+ | FalseUnwind { real_target : target, .. }
386
+ | Goto { target } => CoverageSuccessors :: Chainable ( target) ,
387
+
388
+ // These terminators can normally be chained, except when they have no
389
+ // successor because they are known to diverge.
390
+ Call { target : maybe_target, .. } | InlineAsm { destination : maybe_target, .. } => {
391
+ match maybe_target {
392
+ Some ( target) => CoverageSuccessors :: Chainable ( target) ,
393
+ None => CoverageSuccessors :: NotChainable ( & [ ] ) ,
394
+ }
395
+ }
396
+
397
+ // These terminators have no coverage-relevant successors.
398
+ CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate ( _) => {
399
+ CoverageSuccessors :: NotChainable ( & [ ] )
400
+ }
401
+ }
368
402
}
369
403
370
404
/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
@@ -542,7 +576,7 @@ fn short_circuit_preorder<'a, 'tcx, F, Iter>(
542
576
) -> impl Iterator < Item = BasicBlock > + Captures < ' a > + Captures < ' tcx >
543
577
where
544
578
F : Fn ( BasicBlock ) -> Iter ,
545
- Iter : Iterator < Item = BasicBlock > ,
579
+ Iter : IntoIterator < Item = BasicBlock > ,
546
580
{
547
581
let mut visited = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
548
582
let mut worklist = vec ! [ mir:: START_BLOCK ] ;
0 commit comments