From d2ac11ce5f4da057f3075ceb1c081668e5a883bc Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Thu, 13 Dec 2018 15:53:58 +0900 Subject: [PATCH 1/6] Cleanups --- src/librustc_mir/build/matches/mod.rs | 10 +++++----- src/librustc_mir/build/matches/simplify.rs | 18 ++++++++--------- src/librustc_mir/build/matches/test.rs | 23 ++++++++++------------ 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 7e7c0b15555f8..97d983ed9e163 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -100,7 +100,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::(); let pre_binding_blocks: Vec<_> = (0..=candidate_count) .map(|_| self.cfg.start_new_block()) .collect(); @@ -337,7 +337,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 +359,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!( @@ -745,7 +745,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 +1035,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 diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 328b330f762dc..b9fd4f0e0b60b 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -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. } } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 5d9cb014f5821..a95804e05c906 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -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); @@ -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 => { From 9daa823896402cde563a53934e821609783af835 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Thu, 13 Dec 2018 22:35:54 +0900 Subject: [PATCH 2/6] Improve `match` MIR generation for ranges Makes testing a range rule out ranges/constant covered by the range that is being tested --- src/librustc_mir/build/matches/test.rs | 120 +++++++++++++++++++++++- src/librustc_mir/hair/pattern/mod.rs | 9 +- src/test/run-pass/mir/mir_match_test.rs | 83 ++++++++++++++++ 3 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 src/test/run-pass/mir/mir_match_test.rs diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index a95804e05c906..77db74685cd2f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -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}; @@ -136,7 +137,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Variant { .. } => { panic!("you should have called add_variants_to_switch instead!"); } - PatternKind::Range { .. } | + PatternKind::Range { ty, lo, hi, end } => { + indices + .keys() + .all(|value| { + !self + .const_range_contains(ty, lo, hi, end, value) + .unwrap_or(true) + }) + } PatternKind::Slice { .. } | PatternKind::Array { .. } | PatternKind::Wild | @@ -529,6 +538,28 @@ 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 { ty, lo, hi, end }) => { + let not_contained = indices + .keys() + .all(|value| { + !self + .const_range_contains(ty, lo, hi, end, value) + .unwrap_or(true) + }); + + if not_contained { + // No 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, @@ -607,8 +638,70 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } + (&TestKind::Range { + lo: test_lo, hi: test_hi, ty: test_ty, end: test_end, + }, &PatternKind::Range { + lo: pat_lo, hi: pat_hi, ty: _, end: pat_end, + }) => { + if (test_lo, test_hi, test_end) == (pat_lo, pat_hi, pat_end) { + 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 { + lo, hi, ty, end + }, &PatternKind::Constant { + ref value + }) => { + if self.const_range_contains(ty, lo, hi, end, 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. // @@ -719,6 +812,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { "simplifyable pattern found: {:?}", match_pair.pattern) } + + fn const_range_contains( + &self, + ty: Ty<'tcx>, + lo: &'tcx ty::Const<'tcx>, + hi: &'tcx ty::Const<'tcx>, + end: RangeEnd, + value: &'tcx ty::Const<'tcx>, + ) -> Option { + use std::cmp::Ordering::*; + + let param_env = ty::ParamEnv::empty().and(ty); + let tcx = self.hir.tcx(); + + let a = compare_const_vals(tcx, lo, value, param_env)?; + let b = compare_const_vals(tcx, value, hi, param_env)?; + + match (b, end) { + (Less, _) | + (Equal, RangeEnd::Included) if a != Greater => Some(true), + _ => Some(false), + } + } } fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool { diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index d695a64f62a08..864f242a304e0 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -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}; @@ -1210,8 +1210,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 +1233,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)) { diff --git a/src/test/run-pass/mir/mir_match_test.rs b/src/test/run-pass/mir/mir_match_test.rs new file mode 100644 index 0000000000000..1f96d6737e0af --- /dev/null +++ b/src/test/run-pass/mir/mir_match_test.rs @@ -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); +} From 2c1b1c26f49afcca779ec3d6d20e0cb4cbbad332 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Sat, 15 Dec 2018 22:44:39 +0900 Subject: [PATCH 3/6] Factor out --- src/librustc_mir/build/matches/test.rs | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 77db74685cd2f..0abbfc540e63c 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -138,13 +138,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { panic!("you should have called add_variants_to_switch instead!"); } PatternKind::Range { ty, lo, hi, end } => { - indices - .keys() - .all(|value| { - !self - .const_range_contains(ty, lo, hi, end, value) - .unwrap_or(true) - }) + // Check that none of the switch values are in the range. + self.values_not_contained_in_range(ty, lo, hi, end, indices) + .unwrap_or(false) } PatternKind::Slice { .. } | PatternKind::Array { .. } | @@ -541,16 +537,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { (&TestKind::SwitchInt { switch_ty: _, ref options, ref indices }, &PatternKind::Range { ty, lo, hi, end }) => { - let not_contained = indices - .keys() - .all(|value| { - !self - .const_range_contains(ty, lo, hi, end, value) - .unwrap_or(true) - }); + let not_contained = self + .values_not_contained_in_range(ty, lo, hi, end, indices) + .unwrap_or(false); if not_contained { - // No values are contained in the pattern range, + // 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()); @@ -835,6 +827,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { _ => Some(false), } } + + fn values_not_contained_in_range( + &self, + ty: Ty<'tcx>, + lo: &'tcx ty::Const<'tcx>, + hi: &'tcx ty::Const<'tcx>, + end: RangeEnd, + indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>, + ) -> Option { + for val in indices.keys() { + if self.const_range_contains(ty, lo, hi, end, val)? { + return Some(false); + } + } + + Some(true) + } } fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool { From 059bbd962ec03cbbe8da6ff7ca5d23bb2ffdbfee Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Sat, 15 Dec 2018 23:04:23 +0900 Subject: [PATCH 4/6] Add common struct for range --- src/librustc_mir/build/matches/mod.rs | 8 +-- src/librustc_mir/build/matches/simplify.rs | 2 +- src/librustc_mir/build/matches/test.rs | 66 ++++++++-------------- src/librustc_mir/hair/mod.rs | 2 +- src/librustc_mir/hair/pattern/_match.rs | 12 ++-- src/librustc_mir/hair/pattern/mod.rs | 27 +++++---- 6 files changed, 48 insertions(+), 69 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 97d983ed9e163..4d61bf8dae681 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -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; @@ -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 { diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index b9fd4f0e0b60b..0ce642838707e 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -107,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))) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 0abbfc540e63c..c8dec6d0b9764 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -72,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), } } @@ -137,9 +132,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Variant { .. } => { panic!("you should have called add_variants_to_switch instead!"); } - PatternKind::Range { ty, lo, hi, end } => { + PatternKind::Range(range) => { // Check that none of the switch values are in the range. - self.values_not_contained_in_range(ty, lo, hi, end, indices) + self.values_not_contained_in_range(range, indices) .unwrap_or(false) } PatternKind::Slice { .. } | @@ -381,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()); @@ -536,9 +531,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } (&TestKind::SwitchInt { switch_ty: _, ref options, ref indices }, - &PatternKind::Range { ty, lo, hi, end }) => { + &PatternKind::Range(range)) => { let not_contained = self - .values_not_contained_in_range(ty, lo, hi, end, indices) + .values_not_contained_in_range(range, indices) .unwrap_or(false); if not_contained { @@ -630,12 +625,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - (&TestKind::Range { - lo: test_lo, hi: test_hi, ty: test_ty, end: test_end, - }, &PatternKind::Range { - lo: pat_lo, hi: pat_hi, ty: _, end: pat_end, - }) => { - if (test_lo, test_hi, test_end) == (pat_lo, pat_hi, pat_end) { + (&TestKind::Range(test), + &PatternKind::Range(pat)) => { + if test == pat { resulting_candidates[0] .push(self.candidate_without_match_pair( match_pair_index, @@ -648,13 +640,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { use std::cmp::Ordering::*; use rustc::hir::RangeEnd::*; - let param_env = ty::ParamEnv::empty().and(test_ty); + 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)?; + 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) { + match (test.end, pat.end, lo, hi) { // pat < test (_, _, Greater, _) | (_, Excluded, Equal, _) | @@ -675,12 +667,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - (&TestKind::Range { - lo, hi, ty, end - }, &PatternKind::Constant { - ref value - }) => { - if self.const_range_contains(ty, lo, hi, end, value) == Some(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()); @@ -807,21 +795,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn const_range_contains( &self, - ty: Ty<'tcx>, - lo: &'tcx ty::Const<'tcx>, - hi: &'tcx ty::Const<'tcx>, - end: RangeEnd, + range: PatternRange<'tcx>, value: &'tcx ty::Const<'tcx>, ) -> Option { use std::cmp::Ordering::*; - let param_env = ty::ParamEnv::empty().and(ty); + let param_env = ty::ParamEnv::empty().and(range.ty); let tcx = self.hir.tcx(); - let a = compare_const_vals(tcx, lo, value, param_env)?; - let b = compare_const_vals(tcx, value, hi, param_env)?; + let a = compare_const_vals(tcx, range.lo, value, param_env)?; + let b = compare_const_vals(tcx, value, range.hi, param_env)?; - match (b, end) { + match (b, range.end) { (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true), _ => Some(false), @@ -830,14 +815,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn values_not_contained_in_range( &self, - ty: Ty<'tcx>, - lo: &'tcx ty::Const<'tcx>, - hi: &'tcx ty::Const<'tcx>, - end: RangeEnd, + range: PatternRange<'tcx>, indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>, ) -> Option { for val in indices.keys() { - if self.const_range_contains(ty, lo, hi, end, val)? { + if self.const_range_contains(range, val)? { return Some(false); } } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index e604b118eacf1..b254fce4b7684 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -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; diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 4c77350f10ecd..c0bfee803ef8e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -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; @@ -554,12 +554,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, } } @@ -820,7 +820,7 @@ impl<'tcx> IntRange<'tcx> { -> Option> { 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, @@ -1259,7 +1259,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(), @@ -1556,7 +1556,7 @@ fn constructor_covered_by_range<'a, 'tcx>( ) -> Result { 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); diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 864f242a304e0..b014a76a7393f 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -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, From 0aab437f3ab6a9aa03dd8cc9a7788a31ad1d969e Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Mon, 17 Dec 2018 16:53:22 +0900 Subject: [PATCH 5/6] Add MIR test --- src/test/mir-opt/match_test.rs | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/mir-opt/match_test.rs diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs new file mode 100644 index 0000000000000..025b02baef955 --- /dev/null +++ b/src/test/mir-opt/match_test.rs @@ -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 \ No newline at end of file From d66a55e4de9604c5e4f56c0756a747990ae0bc4b Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Mon, 17 Dec 2018 17:10:49 +0900 Subject: [PATCH 6/6] tidy --- src/test/mir-opt/match_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index 025b02baef955..9bfb728e13461 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -82,4 +82,4 @@ fn main() { // ... // return; // } -// END rustc.main.SimplifyCfg-initial.after.mir \ No newline at end of file +// END rustc.main.SimplifyCfg-initial.after.mir