Skip to content

Commit 457f94c

Browse files
authored
Rollup merge of rust-lang#121908 - Nadrieril:dynamic-variant-collection, r=matthewjasper
match lowering: don't collect test alternatives ahead of time I'm very happy with this one. Before this, when sorting candidates into the possible test branches, we manually computed `usize` indices to determine in which branch each candidate goes. To make this work we had a first pass that collected the possible alternatives we'd have to deal with, and a second pass that actually sorts the candidates. In this PR, I replace `usize` indices with a dedicated enum. This makes `sort_candidates` easier to follow, and we don't need the first pass anymore. r? `@matthewjasper`
2 parents 494b170 + d46ff64 commit 457f94c

9 files changed

+206
-274
lines changed

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

+59-60
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rustc_data_structures::{
1414
fx::{FxHashSet, FxIndexMap, FxIndexSet},
1515
stack::ensure_sufficient_stack,
1616
};
17-
use rustc_index::bit_set::BitSet;
1817
use rustc_middle::middle::region;
1918
use rustc_middle::mir::{self, *};
2019
use rustc_middle::thir::{self, *};
@@ -1084,6 +1083,12 @@ enum TestCase<'pat, 'tcx> {
10841083
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
10851084
}
10861085

1086+
impl<'pat, 'tcx> TestCase<'pat, 'tcx> {
1087+
fn as_range(&self) -> Option<&'pat PatRange<'tcx>> {
1088+
if let Self::Range(v) = self { Some(*v) } else { None }
1089+
}
1090+
}
1091+
10871092
#[derive(Debug, Clone)]
10881093
pub(crate) struct MatchPair<'pat, 'tcx> {
10891094
/// This place...
@@ -1108,19 +1113,10 @@ enum TestKind<'tcx> {
11081113
Switch {
11091114
/// The enum type being tested.
11101115
adt_def: ty::AdtDef<'tcx>,
1111-
/// The set of variants that we should create a branch for. We also
1112-
/// create an additional "otherwise" case.
1113-
variants: BitSet<VariantIdx>,
11141116
},
11151117

11161118
/// Test what value an integer or `char` has.
1117-
SwitchInt {
1118-
/// The (ordered) set of values that we test for.
1119-
///
1120-
/// We create a branch to each of the values in `options`, as well as an "otherwise" branch
1121-
/// for all other values, even in the (rare) case that `options` is exhaustive.
1122-
options: FxIndexMap<Const<'tcx>, u128>,
1123-
},
1119+
SwitchInt,
11241120

11251121
/// Test what value a `bool` has.
11261122
If,
@@ -1152,6 +1148,25 @@ pub(crate) struct Test<'tcx> {
11521148
kind: TestKind<'tcx>,
11531149
}
11541150

1151+
/// The branch to be taken after a test.
1152+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1153+
enum TestBranch<'tcx> {
1154+
/// Success branch, used for tests with two possible outcomes.
1155+
Success,
1156+
/// Branch corresponding to this constant.
1157+
Constant(Const<'tcx>, u128),
1158+
/// Branch corresponding to this variant.
1159+
Variant(VariantIdx),
1160+
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
1161+
Failure,
1162+
}
1163+
1164+
impl<'tcx> TestBranch<'tcx> {
1165+
fn as_constant(&self) -> Option<&Const<'tcx>> {
1166+
if let Self::Constant(v, _) = self { Some(v) } else { None }
1167+
}
1168+
}
1169+
11551170
/// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether
11561171
/// a match arm has a guard expression attached to it.
11571172
#[derive(Copy, Clone, Debug)]
@@ -1561,30 +1576,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15611576
) -> (PlaceBuilder<'tcx>, Test<'tcx>) {
15621577
// Extract the match-pair from the highest priority candidate
15631578
let match_pair = &candidates.first().unwrap().match_pairs[0];
1564-
let mut test = self.test(match_pair);
1579+
let test = self.test(match_pair);
15651580
let match_place = match_pair.place.clone();
1566-
15671581
debug!(?test, ?match_pair);
1568-
// Most of the time, the test to perform is simply a function of the main candidate; but for
1569-
// a test like SwitchInt, we may want to add cases based on the candidates that are
1570-
// available
1571-
match test.kind {
1572-
TestKind::SwitchInt { ref mut options } => {
1573-
for candidate in candidates.iter() {
1574-
if !self.add_cases_to_switch(&match_place, candidate, options) {
1575-
break;
1576-
}
1577-
}
1578-
}
1579-
TestKind::Switch { adt_def: _, ref mut variants } => {
1580-
for candidate in candidates.iter() {
1581-
if !self.add_variants_to_switch(&match_place, candidate, variants) {
1582-
break;
1583-
}
1584-
}
1585-
}
1586-
_ => {}
1587-
}
15881582

15891583
(match_place, test)
15901584
}
@@ -1627,23 +1621,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16271621
match_place: &PlaceBuilder<'tcx>,
16281622
test: &Test<'tcx>,
16291623
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
1630-
) -> (&'b mut [&'c mut Candidate<'pat, 'tcx>], Vec<Vec<&'b mut Candidate<'pat, 'tcx>>>) {
1631-
// For each of the N possible outcomes, create a (initially empty) vector of candidates.
1632-
// Those are the candidates that apply if the test has that particular outcome.
1633-
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
1634-
target_candidates.resize_with(test.targets(), Default::default);
1624+
) -> (
1625+
&'b mut [&'c mut Candidate<'pat, 'tcx>],
1626+
FxIndexMap<TestBranch<'tcx>, Vec<&'b mut Candidate<'pat, 'tcx>>>,
1627+
) {
1628+
// For each of the possible outcomes, collect vector of candidates that apply if the test
1629+
// has that particular outcome.
1630+
let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_, '_>>> = Default::default();
16351631

16361632
let total_candidate_count = candidates.len();
16371633

16381634
// Sort the candidates into the appropriate vector in `target_candidates`. Note that at some
16391635
// point we may encounter a candidate where the test is not relevant; at that point, we stop
16401636
// sorting.
16411637
while let Some(candidate) = candidates.first_mut() {
1642-
let Some(idx) = self.sort_candidate(&match_place, &test, candidate) else {
1638+
let Some(branch) =
1639+
self.sort_candidate(&match_place, test, candidate, &target_candidates)
1640+
else {
16431641
break;
16441642
};
16451643
let (candidate, rest) = candidates.split_first_mut().unwrap();
1646-
target_candidates[idx].push(candidate);
1644+
target_candidates.entry(branch).or_insert_with(Vec::new).push(candidate);
16471645
candidates = rest;
16481646
}
16491647

@@ -1784,31 +1782,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17841782
otherwise_block
17851783
};
17861784

1787-
// For each outcome of test, process the candidates that still
1788-
// apply. Collect a list of blocks where control flow will
1789-
// branch if one of the `target_candidate` sets is not
1790-
// exhaustive.
1791-
let target_blocks: Vec<_> = target_candidates
1785+
// For each outcome of test, process the candidates that still apply.
1786+
let target_blocks: FxIndexMap<_, _> = target_candidates
17921787
.into_iter()
1793-
.map(|mut candidates| {
1794-
if !candidates.is_empty() {
1795-
let candidate_start = self.cfg.start_new_block();
1796-
self.match_candidates(
1797-
span,
1798-
scrutinee_span,
1799-
candidate_start,
1800-
remainder_start,
1801-
&mut *candidates,
1802-
);
1803-
candidate_start
1804-
} else {
1805-
remainder_start
1806-
}
1788+
.map(|(branch, mut candidates)| {
1789+
let candidate_start = self.cfg.start_new_block();
1790+
self.match_candidates(
1791+
span,
1792+
scrutinee_span,
1793+
candidate_start,
1794+
remainder_start,
1795+
&mut *candidates,
1796+
);
1797+
(branch, candidate_start)
18071798
})
18081799
.collect();
18091800

18101801
// Perform the test, branching to one of N blocks.
1811-
self.perform_test(span, scrutinee_span, start_block, &match_place, &test, target_blocks);
1802+
self.perform_test(
1803+
span,
1804+
scrutinee_span,
1805+
start_block,
1806+
remainder_start,
1807+
&match_place,
1808+
&test,
1809+
target_blocks,
1810+
);
18121811
}
18131812

18141813
/// Determine the fake borrows that are needed from a set of places that

0 commit comments

Comments
 (0)