Skip to content

Commit 418ae3e

Browse files
authored
Rollup merge of rust-lang#118852 - Zalathar:no-spans, r=cjgillot
coverage: Skip instrumenting a function if no spans were extracted from MIR The immediate symptoms of rust-lang#118643 were fixed by rust-lang#118666, but some users reported that their builds now encounter another coverage-related ICE: ``` error: internal compiler error: compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs:98:17: A used function should have had coverage mapping data but did not: (...) ``` I was able to reproduce at least one cause of this error: if no relevant spans could be extracted from a function, but the function contains `CoverageKind::SpanMarker` statements, then codegen still thinks the function is instrumented and complains about the fact that it has no coverage spans. This PR prevents that from happening in two ways: - If we didn't extract any relevant spans from MIR, skip instrumenting the entire function and don't create a `FunctionCoverateInfo` for it. - If coverage codegen sees a `CoverageKind::SpanMarker` statement, skip it early and avoid creating `func_coverage`. --- Fixes rust-lang#118850.
2 parents 8681e07 + e0de143 commit 418ae3e

File tree

6 files changed

+93
-7
lines changed

6 files changed

+93
-7
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
8585

8686
let bx = self;
8787

88+
match coverage.kind {
89+
// Marker statements have no effect during codegen,
90+
// so return early and don't create `func_coverage`.
91+
CoverageKind::SpanMarker => return,
92+
// Match exhaustively to ensure that newly-added kinds are classified correctly.
93+
CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {}
94+
}
95+
8896
let Some(function_coverage_info) =
8997
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
9098
else {
@@ -100,9 +108,9 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
100108

101109
let Coverage { kind } = coverage;
102110
match *kind {
103-
// Span markers are only meaningful during MIR instrumentation,
104-
// and have no effect during codegen.
105-
CoverageKind::SpanMarker => {}
111+
CoverageKind::SpanMarker => unreachable!(
112+
"unexpected marker statement {kind:?} should have caused an early return"
113+
),
106114
CoverageKind::CounterIncrement { id } => {
107115
func_coverage.mark_counter_id_seen(id);
108116
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,

compiler/rustc_mir_transform/src/coverage/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
9999
fn inject_counters(&'a mut self) {
100100
////////////////////////////////////////////////////
101101
// Compute coverage spans from the `CoverageGraph`.
102-
let coverage_spans = CoverageSpans::generate_coverage_spans(
102+
let Some(coverage_spans) = CoverageSpans::generate_coverage_spans(
103103
self.mir_body,
104104
self.fn_sig_span,
105105
self.body_span,
106106
&self.basic_coverage_blocks,
107-
);
107+
) else {
108+
// No relevant spans were found in MIR, so skip instrumenting this function.
109+
return;
110+
};
108111

109112
////////////////////////////////////////////////////
110113
// Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure

compiler/rustc_mir_transform/src/coverage/spans.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,34 @@ pub(super) struct CoverageSpans {
1515
}
1616

1717
impl CoverageSpans {
18+
/// Extracts coverage-relevant spans from MIR, and associates them with
19+
/// their corresponding BCBs.
20+
///
21+
/// Returns `None` if no coverage-relevant spans could be extracted.
1822
pub(super) fn generate_coverage_spans(
1923
mir_body: &mir::Body<'_>,
2024
fn_sig_span: Span,
2125
body_span: Span,
2226
basic_coverage_blocks: &CoverageGraph,
23-
) -> Self {
27+
) -> Option<Self> {
2428
let coverage_spans = CoverageSpansGenerator::generate_coverage_spans(
2529
mir_body,
2630
fn_sig_span,
2731
body_span,
2832
basic_coverage_blocks,
2933
);
3034

35+
if coverage_spans.is_empty() {
36+
return None;
37+
}
38+
3139
// Group the coverage spans by BCB, with the BCBs in sorted order.
3240
let mut bcb_to_spans = IndexVec::from_elem_n(Vec::new(), basic_coverage_blocks.num_nodes());
3341
for CoverageSpan { bcb, span, .. } in coverage_spans {
3442
bcb_to_spans[bcb].push(span);
3543
}
3644

37-
Self { bcb_to_spans }
45+
Some(Self { bcb_to_spans })
3846
}
3947

4048
pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool {
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Function name: no_spans_if_not::main
2+
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 01, 02, 02]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 0
6+
Number of file 0 mappings: 1
7+
- Code(Counter(0)) at (prev + 11, 1) to (start + 2, 2)
8+
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
LL| |// edition: 2021
2+
LL| |
3+
LL| |// If the span extractor can't find any relevant spans for a function,
4+
LL| |// but the function contains coverage span-marker statements (e.g. inserted
5+
LL| |// for `if !`), coverage codegen may think that it is instrumented and
6+
LL| |// consequently complain that it has no spans.
7+
LL| |//
8+
LL| |// Regression test for <https://github.com/rust-lang/rust/issues/118850>,
9+
LL| |// "A used function should have had coverage mapping data but did not".
10+
LL| |
11+
LL| 1|fn main() {
12+
LL| 1| affected_function();
13+
LL| 1|}
14+
LL| |
15+
LL| |macro_rules! macro_that_defines_a_function {
16+
LL| | (fn $name:ident () $body:tt) => {
17+
LL| | fn $name () $body
18+
LL| | }
19+
LL| |}
20+
LL| |
21+
LL| |macro_that_defines_a_function! {
22+
LL| | fn affected_function() {
23+
LL| | if !false {
24+
LL| | ()
25+
LL| | } else {
26+
LL| | ()
27+
LL| | }
28+
LL| | }
29+
LL| |}
30+

tests/coverage/no_spans_if_not.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// edition: 2021
2+
3+
// If the span extractor can't find any relevant spans for a function,
4+
// but the function contains coverage span-marker statements (e.g. inserted
5+
// for `if !`), coverage codegen may think that it is instrumented and
6+
// consequently complain that it has no spans.
7+
//
8+
// Regression test for <https://github.com/rust-lang/rust/issues/118850>,
9+
// "A used function should have had coverage mapping data but did not".
10+
11+
fn main() {
12+
affected_function();
13+
}
14+
15+
macro_rules! macro_that_defines_a_function {
16+
(fn $name:ident () $body:tt) => {
17+
fn $name () $body
18+
}
19+
}
20+
21+
macro_that_defines_a_function! {
22+
fn affected_function() {
23+
if !false {
24+
()
25+
} else {
26+
()
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)