fn save_unreachable_coverage(
    basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
    source_scopes: &IndexVec<SourceScope, SourceScopeData<'_>>,
    first_dead_block: usize
)
Expand description

Some MIR transforms can determine at compile time that a sequences of statements will never be executed, so they can be dropped from the MIR. For example, an if or else block that is guaranteed to never be executed because its condition can be evaluated at compile time, such as by const evaluation: if false { ... }.

Those statements are bypassed by redirecting paths in the CFG around the dead blocks; but with -C instrument-coverage, the dead blocks usually include Coverage statements representing the Rust source code regions to be counted at runtime. Without these Coverage statements, the regions are lost, and the Rust source code will show no coverage information.

What we want to show in a coverage report is the dead code with coverage counts of 0. To do this, we need to save the code regions, by injecting Unreachable coverage statements. These are non-executable statements whose code regions are still recorded in the coverage map, representing regions with 0 executions.

If there are no live Counter Coverage statements remaining, we remove Coverage statements along with the dead blocks. Since at least one counter per function is required by LLVM (and necessary, to add the function_hash to the counter’s call to the LLVM intrinsic instrprof.increment()).

The generator::StateTransform MIR pass and MIR inlining can create atypical conditions, where all live Counters are dropped from the MIR.

With MIR inlining we can have coverage counters belonging to different instances in a single body, so the strategy described above is applied to coverage counters from each instance individually.