Fix a subtle regression
Before, the SwitchInt cases were computed in two passes: if the first pass accepted e.g. 0..=5 and then 1, the second pass would not accept 0..=5 anymore because 1 would be listed in the SwitchInt options. Now there's a single pass, so if we sort 0..=5 we must take care to not sort a subsequent 1.
This commit is contained in:
parent
edea739292
commit
d46ff6415c
compiler/rustc_mir_build/src/build/matches
tests/ui/pattern/usefulness/integer-ranges
@ -1091,6 +1091,12 @@ enum TestCase<'pat, 'tcx> {
|
|||||||
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'pat, 'tcx> TestCase<'pat, 'tcx> {
|
||||||
|
fn as_range(&self) -> Option<&'pat PatRange<'tcx>> {
|
||||||
|
if let Self::Range(v) = self { Some(*v) } else { None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct MatchPair<'pat, 'tcx> {
|
pub(crate) struct MatchPair<'pat, 'tcx> {
|
||||||
/// This place...
|
/// This place...
|
||||||
|
@ -510,9 +510,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
(TestKind::SwitchInt, &TestCase::Constant { value })
|
(TestKind::SwitchInt, &TestCase::Constant { value })
|
||||||
if is_switch_ty(match_pair.pattern.ty) =>
|
if is_switch_ty(match_pair.pattern.ty) =>
|
||||||
{
|
{
|
||||||
fully_matched = true;
|
// Beware: there might be some ranges sorted into the failure case; we must not add
|
||||||
let bits = value.eval_bits(self.tcx, self.param_env);
|
// a success case that could be matched by one of these ranges.
|
||||||
Some(TestBranch::Constant(value, bits))
|
let is_covering_range = |test_case: &TestCase<'_, 'tcx>| {
|
||||||
|
test_case.as_range().is_some_and(|range| {
|
||||||
|
matches!(range.contains(value, self.tcx, self.param_env), None | Some(true))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| {
|
||||||
|
candidate
|
||||||
|
.match_pairs
|
||||||
|
.iter()
|
||||||
|
.any(|mp| mp.place == *test_place && is_covering_range(&mp.test_case))
|
||||||
|
};
|
||||||
|
if sorted_candidates
|
||||||
|
.get(&TestBranch::Failure)
|
||||||
|
.is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate))
|
||||||
|
{
|
||||||
|
fully_matched = false;
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
fully_matched = true;
|
||||||
|
let bits = value.eval_bits(self.tcx, self.param_env);
|
||||||
|
Some(TestBranch::Constant(value, bits))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(TestKind::SwitchInt, TestCase::Range(range)) => {
|
(TestKind::SwitchInt, TestCase::Range(range)) => {
|
||||||
fully_matched = false;
|
fully_matched = false;
|
||||||
|
14
tests/ui/pattern/usefulness/integer-ranges/regression-switchint-sorting-with-ranges.rs
Normal file
14
tests/ui/pattern/usefulness/integer-ranges/regression-switchint-sorting-with-ranges.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//
|
||||||
|
// Regression test for match lowering to MIR: when gathering candidates, by the time we get to the
|
||||||
|
// range we know the range will only match on the failure case of the switchint. Hence we mustn't
|
||||||
|
// add the `1` to the switchint or the range would be incorrectly sorted.
|
||||||
|
#![allow(unreachable_patterns)]
|
||||||
|
fn main() {
|
||||||
|
match 1 {
|
||||||
|
10 => unreachable!(),
|
||||||
|
0..=5 => {}
|
||||||
|
1 => unreachable!(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user