mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Auto merge of #56810 - sinkuu:build_match, r=oli-obk
Improve MIR match generation for ranges Improves MIR match generation to rule out ranges/values distinct from the range that has been tested. e.g., for this code: ```rust match x { 0..=5 if b => 0, 6..=10 => 1, _ => 2, } ``` MIR (before): ```rust bb0: { ...; _4 = Le(const 0i32, _1); switchInt(move _4) -> [false: bb6, otherwise: bb5]; } bb1: { _3 = const 0i32; goto -> bb8; } bb2: { _6 = _2; switchInt(move _6) -> [false: bb6, otherwise: bb1]; } // If `!b`, jumps to test if `6 <= x <= 10`. bb3: { _3 = const 1i32; goto -> bb8; } bb4: { _3 = const 2i32; goto -> bb8; } bb5: { _5 = Le(_1, const 5i32); switchInt(move _5) -> [false: bb6, otherwise: bb2]; } bb6: { _7 = Le(const 6i32, _1); switchInt(move _7) -> [false: bb4, otherwise: bb7]; } bb7: { _8 = Le(_1, const 10i32); switchInt(move _8) -> [false: bb4, otherwise: bb3]; } ``` MIR (after): ```rust bb0: { ...; _4 = Le(const 0i32, _1); switchInt(move _4) -> [false: bb5, otherwise: bb6]; } bb1: { _3 = const 0i32; goto -> bb8; } bb2: { _6 = _2; switchInt(move _6) -> [false: bb4, otherwise: bb1]; } // If `!b`, jumps to `_ =>` arm. bb3: { _3 = const 1i32; goto -> bb8; } bb4: { _3 = const 2i32; goto -> bb8; } bb5: { _7 = Le(const 6i32, _1); switchInt(move _7) -> [false: bb4, otherwise: bb7]; } bb6: { _5 = Le(_1, const 5i32); switchInt(move _5) -> [false: bb5, otherwise: bb2]; } bb7: { _8 = Le(_1, const 10i32); switchInt(move _8) -> [false: bb4, otherwise: bb3]; } ``` cc #29623
This commit is contained in:
commit
54f3cd6873
@ -19,7 +19,6 @@ use build::{BlockAnd, BlockAndExtension, Builder};
|
||||
use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
|
||||
use hair::*;
|
||||
use hair::pattern::PatternTypeProjections;
|
||||
use rustc::hir;
|
||||
use rustc::mir::*;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
@ -100,7 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
.collect();
|
||||
|
||||
// create binding start block for link them by false edges
|
||||
let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len());
|
||||
let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::<usize>();
|
||||
let pre_binding_blocks: Vec<_> = (0..=candidate_count)
|
||||
.map(|_| self.cfg.start_new_block())
|
||||
.collect();
|
||||
@ -337,7 +336,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
||||
pub fn place_into_pattern(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
block: BasicBlock,
|
||||
irrefutable_pat: Pattern<'tcx>,
|
||||
initializer: &Place<'tcx>,
|
||||
set_match_place: bool,
|
||||
@ -359,7 +358,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
||||
// Simplify the candidate. Since the pattern is irrefutable, this should
|
||||
// always convert all match-pairs into bindings.
|
||||
unpack!(block = self.simplify_candidate(block, &mut candidate));
|
||||
self.simplify_candidate(&mut candidate);
|
||||
|
||||
if !candidate.match_pairs.is_empty() {
|
||||
span_bug!(
|
||||
@ -681,12 +680,7 @@ enum TestKind<'tcx> {
|
||||
},
|
||||
|
||||
// test whether the value falls within an inclusive or exclusive range
|
||||
Range {
|
||||
lo: &'tcx ty::Const<'tcx>,
|
||||
hi: &'tcx ty::Const<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
end: hir::RangeEnd,
|
||||
},
|
||||
Range(PatternRange<'tcx>),
|
||||
|
||||
// test length of the slice is equal to len
|
||||
Len {
|
||||
@ -745,7 +739,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
// complete, all the match pairs which remain require some
|
||||
// form of test, whether it be a switch or pattern comparison.
|
||||
for candidate in &mut candidates {
|
||||
unpack!(block = self.simplify_candidate(block, candidate));
|
||||
self.simplify_candidate(candidate);
|
||||
}
|
||||
|
||||
// The candidates are sorted by priority. Check to see
|
||||
@ -1035,7 +1029,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
test, match_pair
|
||||
);
|
||||
let target_blocks = self.perform_test(block, &match_pair.place, &test);
|
||||
let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect();
|
||||
let mut target_candidates = vec![vec![]; target_blocks.len()];
|
||||
|
||||
// Sort the candidates into the appropriate vector in
|
||||
// `target_candidates`. Note that at some point we may
|
||||
|
@ -22,10 +22,9 @@
|
||||
//! sort of test: for example, testing which variant an enum is, or
|
||||
//! testing a value against a constant.
|
||||
|
||||
use build::{BlockAnd, BlockAndExtension, Builder};
|
||||
use build::Builder;
|
||||
use build::matches::{Ascription, Binding, MatchPair, Candidate};
|
||||
use hair::*;
|
||||
use rustc::mir::*;
|
||||
use rustc::ty;
|
||||
use rustc::ty::layout::{Integer, IntegerExt, Size};
|
||||
use syntax::attr::{SignedInt, UnsignedInt};
|
||||
@ -35,24 +34,23 @@ use std::mem;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
pub fn simplify_candidate<'pat>(&mut self,
|
||||
block: BasicBlock,
|
||||
candidate: &mut Candidate<'pat, 'tcx>)
|
||||
-> BlockAnd<()> {
|
||||
candidate: &mut Candidate<'pat, 'tcx>) {
|
||||
// repeatedly simplify match pairs until fixed point is reached
|
||||
loop {
|
||||
let match_pairs = mem::replace(&mut candidate.match_pairs, vec![]);
|
||||
let mut progress = match_pairs.len(); // count how many were simplified
|
||||
let mut changed = false;
|
||||
for match_pair in match_pairs {
|
||||
match self.simplify_match_pair(match_pair, candidate) {
|
||||
Ok(()) => {}
|
||||
Ok(()) => {
|
||||
changed = true;
|
||||
}
|
||||
Err(match_pair) => {
|
||||
candidate.match_pairs.push(match_pair);
|
||||
progress -= 1; // this one was not simplified
|
||||
}
|
||||
}
|
||||
}
|
||||
if progress == 0 {
|
||||
return block.unit(); // if we were not able to simplify any, done.
|
||||
if !changed {
|
||||
return; // if we were not able to simplify any, done.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,7 +107,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
Err(match_pair)
|
||||
}
|
||||
|
||||
PatternKind::Range { lo, hi, ty, end } => {
|
||||
PatternKind::Range(PatternRange { lo, hi, ty, end }) => {
|
||||
let range = match ty.sty {
|
||||
ty::Char => {
|
||||
Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32)))
|
||||
|
@ -18,6 +18,7 @@
|
||||
use build::Builder;
|
||||
use build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||
use hair::*;
|
||||
use hair::pattern::compare_const_vals;
|
||||
use rustc_data_structures::bit_set::BitSet;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc::ty::{self, Ty};
|
||||
@ -71,16 +72,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
PatternKind::Range { lo, hi, ty, end } => {
|
||||
assert!(ty == match_pair.pattern.ty);
|
||||
PatternKind::Range(range) => {
|
||||
assert!(range.ty == match_pair.pattern.ty);
|
||||
Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::Range {
|
||||
lo,
|
||||
hi,
|
||||
ty,
|
||||
end,
|
||||
},
|
||||
kind: TestKind::Range(range),
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +132,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
PatternKind::Variant { .. } => {
|
||||
panic!("you should have called add_variants_to_switch instead!");
|
||||
}
|
||||
PatternKind::Range { .. } |
|
||||
PatternKind::Range(range) => {
|
||||
// Check that none of the switch values are in the range.
|
||||
self.values_not_contained_in_range(range, indices)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
PatternKind::Slice { .. } |
|
||||
PatternKind::Array { .. } |
|
||||
PatternKind::Wild |
|
||||
@ -200,20 +200,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
for (idx, discr) in adt_def.discriminants(tcx) {
|
||||
target_blocks.push(if variants.contains(idx) {
|
||||
values.push(discr.val);
|
||||
targets.push(self.cfg.start_new_block());
|
||||
*targets.last().unwrap()
|
||||
let block = self.cfg.start_new_block();
|
||||
targets.push(block);
|
||||
block
|
||||
} else {
|
||||
if otherwise_block.is_none() {
|
||||
otherwise_block = Some(self.cfg.start_new_block());
|
||||
}
|
||||
otherwise_block.unwrap()
|
||||
*otherwise_block
|
||||
.get_or_insert_with(|| self.cfg.start_new_block())
|
||||
});
|
||||
}
|
||||
if let Some(otherwise_block) = otherwise_block {
|
||||
targets.push(otherwise_block);
|
||||
} else {
|
||||
targets.push(self.unreachable_block());
|
||||
}
|
||||
targets.push(
|
||||
otherwise_block
|
||||
.unwrap_or_else(|| self.unreachable_block()),
|
||||
);
|
||||
debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
|
||||
num_enum_variants, values, variants);
|
||||
let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
|
||||
@ -378,7 +376,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
TestKind::Range { ref lo, ref hi, ty, ref end } => {
|
||||
TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => {
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
|
||||
let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
|
||||
@ -490,8 +488,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
// away.)
|
||||
let tested_match_pair = candidate.match_pairs.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, mp)| mp.place == *test_place)
|
||||
.next();
|
||||
.find(|&(_, mp)| mp.place == *test_place);
|
||||
let (match_pair_index, match_pair) = match tested_match_pair {
|
||||
Some(pair) => pair,
|
||||
None => {
|
||||
@ -532,6 +529,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
resulting_candidates[index].push(new_candidate);
|
||||
true
|
||||
}
|
||||
|
||||
(&TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
|
||||
&PatternKind::Range(range)) => {
|
||||
let not_contained = self
|
||||
.values_not_contained_in_range(range, indices)
|
||||
.unwrap_or(false);
|
||||
|
||||
if not_contained {
|
||||
// No switch values are contained in the pattern range,
|
||||
// so the pattern can be matched only if this test fails.
|
||||
let otherwise = options.len();
|
||||
resulting_candidates[otherwise].push(candidate.clone());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
(&TestKind::SwitchInt { .. }, _) => false,
|
||||
|
||||
|
||||
@ -610,8 +625,63 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
(&TestKind::Range(test),
|
||||
&PatternKind::Range(pat)) => {
|
||||
if test == pat {
|
||||
resulting_candidates[0]
|
||||
.push(self.candidate_without_match_pair(
|
||||
match_pair_index,
|
||||
candidate,
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
let no_overlap = (|| {
|
||||
use std::cmp::Ordering::*;
|
||||
use rustc::hir::RangeEnd::*;
|
||||
|
||||
let param_env = ty::ParamEnv::empty().and(test.ty);
|
||||
let tcx = self.hir.tcx();
|
||||
|
||||
let lo = compare_const_vals(tcx, test.lo, pat.hi, param_env)?;
|
||||
let hi = compare_const_vals(tcx, test.hi, pat.lo, param_env)?;
|
||||
|
||||
match (test.end, pat.end, lo, hi) {
|
||||
// pat < test
|
||||
(_, _, Greater, _) |
|
||||
(_, Excluded, Equal, _) |
|
||||
// pat > test
|
||||
(_, _, _, Less) |
|
||||
(Excluded, _, _, Equal) => Some(true),
|
||||
_ => Some(false),
|
||||
}
|
||||
})();
|
||||
|
||||
if no_overlap == Some(true) {
|
||||
// Testing range does not overlap with pattern range,
|
||||
// so the pattern can be matched only if this test fails.
|
||||
resulting_candidates[1].push(candidate.clone());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
(&TestKind::Range(range), &PatternKind::Constant { ref value }) => {
|
||||
if self.const_range_contains(range, value) == Some(false) {
|
||||
// `value` is not contained in the testing range,
|
||||
// so `value` can be matched only if this test fails.
|
||||
resulting_candidates[1].push(candidate.clone());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
(&TestKind::Range { .. }, _) => false,
|
||||
|
||||
|
||||
(&TestKind::Eq { .. }, _) |
|
||||
(&TestKind::Range { .. }, _) |
|
||||
(&TestKind::Len { .. }, _) => {
|
||||
// These are all binary tests.
|
||||
//
|
||||
@ -722,6 +792,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
"simplifyable pattern found: {:?}",
|
||||
match_pair.pattern)
|
||||
}
|
||||
|
||||
fn const_range_contains(
|
||||
&self,
|
||||
range: PatternRange<'tcx>,
|
||||
value: &'tcx ty::Const<'tcx>,
|
||||
) -> Option<bool> {
|
||||
use std::cmp::Ordering::*;
|
||||
|
||||
let param_env = ty::ParamEnv::empty().and(range.ty);
|
||||
let tcx = self.hir.tcx();
|
||||
|
||||
let a = compare_const_vals(tcx, range.lo, value, param_env)?;
|
||||
let b = compare_const_vals(tcx, value, range.hi, param_env)?;
|
||||
|
||||
match (b, range.end) {
|
||||
(Less, _) |
|
||||
(Equal, RangeEnd::Included) if a != Greater => Some(true),
|
||||
_ => Some(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn values_not_contained_in_range(
|
||||
&self,
|
||||
range: PatternRange<'tcx>,
|
||||
indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>,
|
||||
) -> Option<bool> {
|
||||
for val in indices.keys() {
|
||||
if self.const_range_contains(range, val)? {
|
||||
return Some(false);
|
||||
}
|
||||
}
|
||||
|
||||
Some(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
|
||||
|
@ -29,7 +29,7 @@ pub mod cx;
|
||||
mod constant;
|
||||
|
||||
pub mod pattern;
|
||||
pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern};
|
||||
pub use self::pattern::{BindingMode, Pattern, PatternKind, PatternRange, FieldPattern};
|
||||
pub(crate) use self::pattern::{PatternTypeProjection, PatternTypeProjections};
|
||||
|
||||
mod util;
|
||||
|
@ -173,7 +173,7 @@ use self::WitnessPreference::*;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use super::{FieldPattern, Pattern, PatternKind};
|
||||
use super::{FieldPattern, Pattern, PatternKind, PatternRange};
|
||||
use super::{PatternFoldable, PatternFolder, compare_const_vals};
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
@ -604,12 +604,12 @@ impl<'tcx> Witness<'tcx> {
|
||||
_ => {
|
||||
match *ctor {
|
||||
ConstantValue(value) => PatternKind::Constant { value },
|
||||
ConstantRange(lo, hi, ty, end) => PatternKind::Range {
|
||||
ConstantRange(lo, hi, ty, end) => PatternKind::Range(PatternRange {
|
||||
lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)),
|
||||
hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)),
|
||||
ty,
|
||||
end,
|
||||
},
|
||||
}),
|
||||
_ => PatternKind::Wild,
|
||||
}
|
||||
}
|
||||
@ -872,7 +872,7 @@ impl<'tcx> IntRange<'tcx> {
|
||||
-> Option<IntRange<'tcx>> {
|
||||
Self::from_ctor(tcx, &match pat.kind {
|
||||
box PatternKind::Constant { value } => ConstantValue(value),
|
||||
box PatternKind::Range { lo, hi, ty, end } => ConstantRange(
|
||||
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => ConstantRange(
|
||||
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
|
||||
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
|
||||
ty,
|
||||
@ -1311,7 +1311,7 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>,
|
||||
Some(vec![Variant(adt_def.variants[variant_index].did)])
|
||||
}
|
||||
PatternKind::Constant { value } => Some(vec![ConstantValue(value)]),
|
||||
PatternKind::Range { lo, hi, ty, end } =>
|
||||
PatternKind::Range(PatternRange { lo, hi, ty, end }) =>
|
||||
Some(vec![ConstantRange(
|
||||
lo.to_bits(cx.tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
|
||||
hi.to_bits(cx.tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
|
||||
@ -1642,7 +1642,7 @@ fn constructor_covered_by_range<'a, 'tcx>(
|
||||
) -> Result<bool, ErrorReported> {
|
||||
let (from, to, end, ty) = match pat.kind {
|
||||
box PatternKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
|
||||
box PatternKind::Range { lo, hi, ty, end } => (lo, hi, end, ty),
|
||||
box PatternKind::Range(PatternRange { lo, hi, end, ty }) => (lo, hi, end, ty),
|
||||
_ => bug!("`constructor_covered_by_range` called with {:?}", pat),
|
||||
};
|
||||
trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty);
|
||||
|
@ -24,7 +24,7 @@ use hair::constant::*;
|
||||
use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
|
||||
use rustc::mir::{ProjectionElem, UserTypeAnnotation, UserTypeProjection, UserTypeProjections};
|
||||
use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
|
||||
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty};
|
||||
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, Lift};
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
use rustc::hir::{self, PatKind, RangeEnd};
|
||||
@ -219,12 +219,7 @@ pub enum PatternKind<'tcx> {
|
||||
value: &'tcx ty::Const<'tcx>,
|
||||
},
|
||||
|
||||
Range {
|
||||
lo: &'tcx ty::Const<'tcx>,
|
||||
hi: &'tcx ty::Const<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
end: RangeEnd,
|
||||
},
|
||||
Range(PatternRange<'tcx>),
|
||||
|
||||
/// matches against a slice, checking the length and extracting elements.
|
||||
/// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
|
||||
@ -243,6 +238,14 @@ pub enum PatternKind<'tcx> {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PatternRange<'tcx> {
|
||||
pub lo: &'tcx ty::Const<'tcx>,
|
||||
pub hi: &'tcx ty::Const<'tcx>,
|
||||
pub ty: Ty<'tcx>,
|
||||
pub end: RangeEnd,
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self.kind {
|
||||
@ -354,7 +357,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||
PatternKind::Constant { value } => {
|
||||
fmt_const_val(f, value)
|
||||
}
|
||||
PatternKind::Range { lo, hi, ty: _, end } => {
|
||||
PatternKind::Range(PatternRange { lo, hi, ty: _, end }) => {
|
||||
fmt_const_val(f, lo)?;
|
||||
match end {
|
||||
RangeEnd::Included => write!(f, "..=")?,
|
||||
@ -483,7 +486,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
||||
);
|
||||
match (end, cmp) {
|
||||
(RangeEnd::Excluded, Some(Ordering::Less)) =>
|
||||
PatternKind::Range { lo, hi, ty, end },
|
||||
PatternKind::Range(PatternRange { lo, hi, ty, end }),
|
||||
(RangeEnd::Excluded, _) => {
|
||||
span_err!(
|
||||
self.tcx.sess,
|
||||
@ -497,7 +500,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
||||
PatternKind::Constant { value: lo }
|
||||
}
|
||||
(RangeEnd::Included, Some(Ordering::Less)) => {
|
||||
PatternKind::Range { lo, hi, ty, end }
|
||||
PatternKind::Range(PatternRange { lo, hi, ty, end })
|
||||
}
|
||||
(RangeEnd::Included, _) => {
|
||||
let mut err = struct_span_err!(
|
||||
@ -1177,17 +1180,17 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
|
||||
} => PatternKind::Constant {
|
||||
value: value.fold_with(folder)
|
||||
},
|
||||
PatternKind::Range {
|
||||
PatternKind::Range(PatternRange {
|
||||
lo,
|
||||
hi,
|
||||
ty,
|
||||
end,
|
||||
} => PatternKind::Range {
|
||||
}) => PatternKind::Range(PatternRange {
|
||||
lo: lo.fold_with(folder),
|
||||
hi: hi.fold_with(folder),
|
||||
ty: ty.fold_with(folder),
|
||||
end,
|
||||
},
|
||||
}),
|
||||
PatternKind::Slice {
|
||||
ref prefix,
|
||||
ref slice,
|
||||
@ -1210,8 +1213,8 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_const_vals<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
pub fn compare_const_vals<'a, 'gcx, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
a: &'tcx ty::Const<'tcx>,
|
||||
b: &'tcx ty::Const<'tcx>,
|
||||
ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
@ -1233,6 +1236,9 @@ pub fn compare_const_vals<'a, 'tcx>(
|
||||
return fallback();
|
||||
}
|
||||
|
||||
let tcx = tcx.global_tcx();
|
||||
let (a, b, ty) = (a, b, ty).lift_to_tcx(tcx).unwrap();
|
||||
|
||||
// FIXME: This should use assert_bits(ty) instead of use_bits
|
||||
// but triggers possibly bugs due to mismatching of arrays and slices
|
||||
if let (Some(a), Some(b)) = (a.to_bits(tcx, ty), b.to_bits(tcx, ty)) {
|
||||
|
85
src/test/mir-opt/match_test.rs
Normal file
85
src/test/mir-opt/match_test.rs
Normal file
@ -0,0 +1,85 @@
|
||||
// Make sure redundant testing paths in `match` expressions are sorted out.
|
||||
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
fn main() {
|
||||
let x = 3;
|
||||
let b = true;
|
||||
|
||||
// When `(0..=10).contains(x) && !b`, we should jump to the last arm
|
||||
// without testing two other candidates.
|
||||
match x {
|
||||
0..10 if b => 0,
|
||||
10..=20 => 1,
|
||||
-1 => 2,
|
||||
_ => 3,
|
||||
};
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.SimplifyCfg-initial.after.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _4 = Le(const 0i32, _1);
|
||||
// switchInt(move _4) -> [false: bb10, otherwise: bb11];
|
||||
// }
|
||||
// bb1: {
|
||||
// _3 = const 0i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb2: {
|
||||
// _3 = const 1i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb3: {
|
||||
// _3 = const 2i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb4: {
|
||||
// _3 = const 3i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb5: {
|
||||
// falseEdges -> [real: bb12, imaginary: bb6];
|
||||
// }
|
||||
// bb6: {
|
||||
// falseEdges -> [real: bb2, imaginary: bb7];
|
||||
// }
|
||||
// bb7: {
|
||||
// falseEdges -> [real: bb3, imaginary: bb8];
|
||||
// }
|
||||
// bb8: {
|
||||
// falseEdges -> [real: bb4, imaginary: bb9];
|
||||
// }
|
||||
// bb9: {
|
||||
// unreachable;
|
||||
// }
|
||||
// bb10: {
|
||||
// _7 = Le(const 10i32, _1);
|
||||
// switchInt(move _7) -> [false: bb14, otherwise: bb15];
|
||||
// }
|
||||
// bb11: {
|
||||
// _5 = Lt(_1, const 10i32);
|
||||
// switchInt(move _5) -> [false: bb10, otherwise: bb5];
|
||||
// }
|
||||
// bb12: {
|
||||
// StorageLive(_6);
|
||||
// _6 = _2;
|
||||
// switchInt(move _6) -> [false: bb13, otherwise: bb1];
|
||||
// }
|
||||
// bb13: {
|
||||
// falseEdges -> [real: bb8, imaginary: bb6];
|
||||
// }
|
||||
// bb14: {
|
||||
// switchInt(_1) -> [-1i32: bb7, otherwise: bb8];
|
||||
// }
|
||||
// bb15: {
|
||||
// _8 = Le(_1, const 20i32);
|
||||
// switchInt(move _8) -> [false: bb14, otherwise: bb6];
|
||||
// }
|
||||
// bb16: {
|
||||
// StorageDead(_6);
|
||||
// ...
|
||||
// return;
|
||||
// }
|
||||
// END rustc.main.SimplifyCfg-initial.after.mir
|
83
src/test/run-pass/mir/mir_match_test.rs
Normal file
83
src/test/run-pass/mir/mir_match_test.rs
Normal file
@ -0,0 +1,83 @@
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
// run-pass
|
||||
|
||||
fn main() {
|
||||
let incl_range = |x, b| {
|
||||
match x {
|
||||
0..=5 if b => 0,
|
||||
5..=10 if b => 1,
|
||||
1..=4 if !b => 2,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(incl_range(3, false), 2);
|
||||
assert_eq!(incl_range(3, true), 0);
|
||||
assert_eq!(incl_range(5, false), 3);
|
||||
assert_eq!(incl_range(5, true), 0);
|
||||
|
||||
let excl_range = |x, b| {
|
||||
match x {
|
||||
0..5 if b => 0,
|
||||
5..10 if b => 1,
|
||||
1..4 if !b => 2,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(excl_range(3, false), 2);
|
||||
assert_eq!(excl_range(3, true), 0);
|
||||
assert_eq!(excl_range(5, false), 3);
|
||||
assert_eq!(excl_range(5, true), 1);
|
||||
|
||||
let incl_range_vs_const = |x, b| {
|
||||
match x {
|
||||
0..=5 if b => 0,
|
||||
7 => 1,
|
||||
3 => 2,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(incl_range_vs_const(5, false), 3);
|
||||
assert_eq!(incl_range_vs_const(5, true), 0);
|
||||
assert_eq!(incl_range_vs_const(3, false), 2);
|
||||
assert_eq!(incl_range_vs_const(3, true), 0);
|
||||
assert_eq!(incl_range_vs_const(7, false), 1);
|
||||
assert_eq!(incl_range_vs_const(7, true), 1);
|
||||
|
||||
let excl_range_vs_const = |x, b| {
|
||||
match x {
|
||||
0..5 if b => 0,
|
||||
7 => 1,
|
||||
3 => 2,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(excl_range_vs_const(5, false), 3);
|
||||
assert_eq!(excl_range_vs_const(5, true), 3);
|
||||
assert_eq!(excl_range_vs_const(3, false), 2);
|
||||
assert_eq!(excl_range_vs_const(3, true), 0);
|
||||
assert_eq!(excl_range_vs_const(7, false), 1);
|
||||
assert_eq!(excl_range_vs_const(7, true), 1);
|
||||
|
||||
let const_vs_incl_range = |x, b| {
|
||||
match x {
|
||||
3 if b => 0,
|
||||
5..=7 => 2,
|
||||
1..=4 => 1,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(const_vs_incl_range(3, false), 1);
|
||||
assert_eq!(const_vs_incl_range(3, true), 0);
|
||||
|
||||
let const_vs_excl_range = |x, b| {
|
||||
match x {
|
||||
3 if b => 0,
|
||||
5..7 => 2,
|
||||
1..4 => 1,
|
||||
_ => 3,
|
||||
}
|
||||
};
|
||||
assert_eq!(const_vs_excl_range(3, false), 1);
|
||||
assert_eq!(const_vs_excl_range(3, true), 0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user