diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 7e7c0b15555f8..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; @@ -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::(); 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 diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 328b330f762dc..0ce642838707e 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. } } } @@ -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))) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 5d9cb014f5821..c8dec6d0b9764 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}; @@ -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 { + 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 { + 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 { 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 d695a64f62a08..b014a76a7393f 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}; @@ -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)) { diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs new file mode 100644 index 0000000000000..9bfb728e13461 --- /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 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); +}