Skip to content

Commit a50e842

Browse files
author
zhuyunxing
committed
coverage. Generate normal coverage coverage when mcdc encounters irrefutable patterns
1 parent 666b4e1 commit a50e842

File tree

5 files changed

+705
-36
lines changed

5 files changed

+705
-36
lines changed

compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs

+56-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{BTreeMap, BTreeSet, VecDeque};
1+
use std::collections::{BTreeMap, VecDeque};
22

33
use rustc_middle::mir::coverage::{
44
BlockMarkerId, ConditionId, ConditionInfo, MCDCBranchSpan, MCDCDecisionSpan,
@@ -198,7 +198,6 @@ impl PatternDecisionCtx {
198198
let entry_span = targets.first().expect("targets mut be non empty").0;
199199

200200
let mut false_next_arm = None;
201-
202201
// Tranverse conditions in reverse order.
203202
while let Some((span, matched_block)) = targets.pop() {
204203
// LLVM whether the decision dominates the condition by checking if span of the decision cover that of the condition,
@@ -246,16 +245,22 @@ impl PatternDecisionCtx {
246245
// to the unique test block of `IpAddr::V4(_) | IpAddr::V6(_)`. Thus the test block cannot find its direct
247246
// predecessors, which are not matched blocks of any patterns, at `visit_matching_arms`
248247
// (in fact they were not connected at that moment).
249-
fn link_condition_gaps(&mut self, cfg: &mut CFG<'_>) {
248+
fn link_condition_gaps(&mut self, entry_block: BasicBlock, cfg: &mut CFG<'_>) {
250249
if self.start_tests.len() < 2 {
251250
return;
252251
}
253-
let mut linked_starts = BTreeSet::new();
252+
254253
for (&end, span) in &self.end_tests {
255254
let mut successor = Some(end);
256255
while let Some(block) = successor.take() {
257-
let next_blocks =
258-
cfg.block_data(block).terminator().successors().collect::<Vec<_>>();
256+
let Some(next_blocks) = cfg
257+
.block_data(block)
258+
.terminator
259+
.as_ref()
260+
.map(|term| term.successors().collect::<Vec<_>>())
261+
else {
262+
break;
263+
};
259264
match next_blocks.as_slice() {
260265
&[unique_successor] => {
261266
if let Some(&next_span) = self.start_tests.get(&unique_successor) {
@@ -264,7 +269,6 @@ impl PatternDecisionCtx {
264269
.get_mut(span)
265270
.expect("end tests must be recorded");
266271
end_record.true_next_pattern = Some(next_span);
267-
linked_starts.insert(unique_successor);
268272
} else {
269273
successor = Some(unique_successor);
270274
}
@@ -274,19 +278,29 @@ impl PatternDecisionCtx {
274278
}
275279
}
276280

277-
self.start_tests.retain(|block, _| !linked_starts.contains(block));
278-
281+
// There might be unreached arms in match guard. E.g
282+
// ```rust
283+
// match pattern {
284+
// (A | B, C | D) => {},
285+
// (B, D) => {},
286+
// _ => {}
287+
// }
288+
// ```
289+
// Clearly the arm `(B, D)` is covered by the first arm so it should be ureached.
290+
// Such arms can cause unresolved start tests here, just ignore them.
291+
self.start_tests.retain(|block, _| *block == entry_block);
292+
// In case no block in `start_tests` is `entry_block`.
279293
assert_eq!(self.start_tests.len(), 1, "still some gaps exist in mcdc pattern decision");
280294
}
281295

282296
fn finish(
283297
mut self,
284298
cfg: &mut CFG<'_>,
285-
matching_block: BasicBlock,
286-
failure_block: BasicBlock,
299+
entry_block: BasicBlock,
300+
end_blocks: Option<(BasicBlock, BasicBlock)>,
287301
mut inject_block_marker: impl FnMut(&mut CFG<'_>, Span, BasicBlock) -> BlockMarkerId,
288-
) -> (MCDCDecisionSpan, Vec<MCDCBranchSpan>) {
289-
self.link_condition_gaps(cfg);
302+
) -> (Option<MCDCDecisionSpan>, Vec<MCDCBranchSpan>) {
303+
self.link_condition_gaps(entry_block, cfg);
290304

291305
let Self { decision_span, decision_depth, mut matching_arms, start_tests, end_tests: _ } =
292306
self;
@@ -295,13 +309,6 @@ impl PatternDecisionCtx {
295309
blocks.into_iter().map(|&block| inject_block_marker(cfg, span, block)).collect()
296310
};
297311

298-
let decision = MCDCDecisionSpan {
299-
span: decision_span,
300-
decision_depth,
301-
conditions_num: matching_arms.len(),
302-
end_markers: into_block_markers(decision_span, &[matching_block, failure_block]),
303-
};
304-
305312
let mut branch_spans = vec![];
306313

307314
// LLVM implementation requires the entry condition should be assigned id 1.
@@ -357,7 +364,7 @@ impl PatternDecisionCtx {
357364
true_markers: into_block_markers(span, &matched_blocks),
358365
});
359366
}
360-
assert_eq!(branch_spans.len(), decision.conditions_num, "conditions num is not correct");
367+
361368
branch_spans.sort_by(|lhs, rhs| {
362369
if lhs.span.contains(rhs.span) {
363370
std::cmp::Ordering::Less
@@ -381,6 +388,15 @@ impl PatternDecisionCtx {
381388
branch_spans[idx].span = span;
382389
}
383390

391+
// No `end_blocks` means it's irrefutable pattern matching. In such cases do not generate mcdc info because the decision
392+
// never fails. Instead we generate normal branch coverage for it.
393+
let decision = end_blocks.map(|(matching, failure)| MCDCDecisionSpan {
394+
span: decision_span,
395+
decision_depth,
396+
conditions_num: branch_spans.len(),
397+
end_markers: into_block_markers(decision_span, &[matching, failure]),
398+
});
399+
384400
(decision, branch_spans)
385401
}
386402
}
@@ -481,8 +497,8 @@ impl MCDCInfoBuilder {
481497
&mut self,
482498
cfg: &mut CFG<'_>,
483499
tcx: TyCtxt<'_>,
484-
matching_block: BasicBlock,
485-
failure_block: BasicBlock,
500+
entry_block: BasicBlock,
501+
end_blocks: Option<(BasicBlock, BasicBlock)>,
486502
inject_block_marker: impl FnMut(&mut CFG<'_>, Span, BasicBlock) -> BlockMarkerId,
487503
) {
488504
if !self
@@ -497,15 +513,24 @@ impl MCDCInfoBuilder {
497513
};
498514

499515
let (decision, conditions) =
500-
decision_ctx.finish(cfg, matching_block, failure_block, inject_block_marker);
501-
self.append_mcdc_info(tcx, decision, conditions);
516+
decision_ctx.finish(cfg, entry_block, end_blocks, inject_block_marker);
517+
if let Some(decision) = decision {
518+
self.append_mcdc_info(tcx, decision, conditions);
519+
} else {
520+
self.append_normal_branches(conditions);
521+
}
522+
}
523+
524+
fn append_normal_branches(&mut self, mut conditions: Vec<MCDCBranchSpan>) {
525+
conditions.iter_mut().for_each(|branch| branch.condition_info = None);
526+
self.branch_spans.extend(conditions);
502527
}
503528

504529
fn append_mcdc_info(
505530
&mut self,
506531
tcx: TyCtxt<'_>,
507532
decision: MCDCDecisionSpan,
508-
mut conditions: Vec<MCDCBranchSpan>,
533+
conditions: Vec<MCDCBranchSpan>,
509534
) {
510535
// take_condition() returns Some for decision_result when the decision stack
511536
// is empty, i.e. when all the conditions of the decision were instrumented,
@@ -521,9 +546,7 @@ impl MCDCInfoBuilder {
521546
// MCDC is equivalent to normal branch coverage if number of conditions is less than 1, so ignore these decisions.
522547
// See comment of `MAX_CONDITIONS_NUM_IN_DECISION` for why decisions with oversized conditions are ignored.
523548
_ => {
524-
// Generate normal branch coverage mappings in such cases.
525-
conditions.iter_mut().for_each(|branch| branch.condition_info = None);
526-
self.branch_spans.extend(conditions);
549+
self.append_normal_branches(conditions);
527550
if decision.conditions_num > MAX_CONDITIONS_NUM_IN_DECISION {
528551
tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
529552
span: decision.span,
@@ -590,8 +613,8 @@ impl Builder<'_, '_> {
590613

591614
pub(crate) fn mcdc_finish_pattern_matching_decision(
592615
&mut self,
593-
matching_block: BasicBlock,
594-
failure_block: BasicBlock,
616+
entry_block: BasicBlock,
617+
end_blocks: Option<(BasicBlock, BasicBlock)>,
595618
) {
596619
if let Some(branch_info) = self.coverage_branch_info.as_mut()
597620
&& let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
@@ -606,8 +629,8 @@ impl Builder<'_, '_> {
606629
mcdc_info.finish_pattern_decision(
607630
&mut self.cfg,
608631
self.tcx,
609-
matching_block,
610-
failure_block,
632+
entry_block,
633+
end_blocks,
611634
inject_block_marker,
612635
);
613636
}

compiler/rustc_mir_build/src/build/matches/mod.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
310310

311311
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
312312

313+
self.mcdc_prepare_pattern_matching_decision(match_start_span);
314+
313315
let fake_borrow_temps = self.lower_match_tree(
314316
block,
315317
scrutinee_span,
@@ -319,6 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
319321
&mut candidates,
320322
);
321323

324+
self.mcdc_finish_pattern_matching_decision(block, None);
325+
322326
self.lower_match_arms(
323327
destination,
324328
scrutinee_place,
@@ -470,7 +474,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
470474
.into_iter()
471475
.map(|(arm, candidate)| {
472476
debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
473-
474477
let arm_source_info = self.source_info(arm.span);
475478
let arm_scope = (arm.scope, arm_source_info);
476479
let match_scope = self.local_scope();
@@ -2037,7 +2040,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20372040
false,
20382041
);
20392042

2040-
self.mcdc_finish_pattern_matching_decision(post_guard_block, otherwise_post_guard_block);
2043+
self.mcdc_finish_pattern_matching_decision(
2044+
block,
2045+
Some((post_guard_block, otherwise_post_guard_block)),
2046+
);
20412047
// If branch coverage is enabled, record this branch.
20422048
self.visit_coverage_conditional_let(
20432049
pat,
@@ -2541,7 +2547,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
25412547
true,
25422548
);
25432549

2544-
this.mcdc_finish_pattern_matching_decision(matching, failure);
2550+
this.mcdc_finish_pattern_matching_decision(block, Some((matching, failure)));
25452551
// If branch coverage is enabled, record this branch.
25462552
this.visit_coverage_conditional_let(pattern, block, matching, failure);
25472553

0 commit comments

Comments
 (0)