diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 84a3923c7b2..e30d6819d04 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -313,10 +313,11 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { ( &ty::Ref(_, rty, _), &PatKind::Constant { - value: Const { - val: ty::ConstKind::Value(val), - ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } - }, + value: + Const { + val: ty::ConstKind::Value(val), + ty: ty::TyS { kind: ty::Ref(_, crty, _), .. }, + }, }, ) => Pat { ty: pat.ty, @@ -328,7 +329,7 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { kind: box PatKind::Constant { value: self.tcx.mk_const(Const { val: ty::ConstKind::Value( - self.fold_const_value_deref(*val, rty, crty) + self.fold_const_value_deref(*val, rty, crty), ), ty: rty, }), @@ -581,7 +582,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] enum Constructor<'tcx> { /// The constructor of all patterns that don't vary by constructor, /// e.g., struct patterns and fixed-length arrays. @@ -589,9 +590,11 @@ enum Constructor<'tcx> { /// Enum variants. Variant(DefId), /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>, Span), - /// Ranges of literal values (`2..=5` and `2..5`). - ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), + ConstantValue(&'tcx ty::Const<'tcx>), + /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). + IntRange(IntRange<'tcx>), + /// Ranges of floating-point literal values (`2.0..=5.2`). + FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), /// Array patterns of length `n`. FixedLenSlice(u64), /// Slice patterns. Captures any array constructor of `length >= i + j`. @@ -600,28 +603,6 @@ enum Constructor<'tcx> { NonExhaustive, } -// Ignore spans when comparing, they don't carry semantic information as they are only for lints. -impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Constructor::Single, Constructor::Single) => true, - (Constructor::NonExhaustive, Constructor::NonExhaustive) => true, - (Constructor::Variant(a), Constructor::Variant(b)) => a == b, - (Constructor::ConstantValue(a, _), Constructor::ConstantValue(b, _)) => a == b, - ( - Constructor::ConstantRange(a_start, a_end, a_ty, a_range_end, _), - Constructor::ConstantRange(b_start, b_end, b_ty, b_range_end, _), - ) => a_start == b_start && a_end == b_end && a_ty == b_ty && a_range_end == b_range_end, - (Constructor::FixedLenSlice(a), Constructor::FixedLenSlice(b)) => a == b, - ( - Constructor::VarLenSlice(a_prefix, a_suffix), - Constructor::VarLenSlice(b_prefix, b_suffix), - ) => a_prefix == b_prefix && a_suffix == b_suffix, - _ => false, - } - } -} - impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { @@ -641,48 +622,20 @@ impl<'tcx> Constructor<'tcx> { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c, _) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), + ConstantValue(c) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } - fn display(&self, tcx: TyCtxt<'tcx>) -> String { - match self { - Constructor::ConstantValue(val, _) => format!("{}", val), - Constructor::ConstantRange(lo, hi, ty, range_end, _) => { - // Get the right sign on the output: - let ty = ty::ParamEnv::empty().and(*ty); - format!( - "{}{}{}", - ty::Const::from_bits(tcx, *lo, ty), - range_end, - ty::Const::from_bits(tcx, *hi, ty), - ) - } - Constructor::FixedLenSlice(val) => format!("[{}]", val), - Constructor::VarLenSlice(prefix, suffix) => format!("[{}, .., {}]", prefix, suffix), - _ => bug!("bad constructor being displayed: `{:?}", self), - } - } - // Returns the set of constructors covered by `self` but not by // anything in `other_ctors`. - fn subtract_ctors( - &self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - other_ctors: &Vec>, - ) -> Vec> { - match *self { + fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { + match self { // Those constructors can only match themselves. - Single | Variant(_) => { - if other_ctors.iter().any(|c| c == self) { - vec![] - } else { - vec![self.clone()] - } + Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { + if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } - FixedLenSlice(self_len) => { + &FixedLenSlice(self_len) => { let overlaps = |c: &Constructor<'_>| match *c { FixedLenSlice(other_len) => other_len == self_len, VarLenSlice(prefix, suffix) => prefix + suffix <= self_len, @@ -753,32 +706,29 @@ impl<'tcx> Constructor<'tcx> { remaining_ctors } - ConstantRange(..) | ConstantValue(..) => { - let mut remaining_ctors = vec![self.clone()]; + IntRange(self_range) => { + let mut remaining_ranges = vec![self_range.clone()]; for other_ctor in other_ctors { - if other_ctor == self { - // If a constructor appears in a `match` arm, we can - // eliminate it straight away. - remaining_ctors = vec![] - } else if let Some(interval) = IntRange::from_ctor(tcx, param_env, other_ctor) { - // Refine the required constructors for the type by subtracting - // the range defined by the current constructor pattern. - remaining_ctors = interval.subtract_from(tcx, param_env, remaining_ctors); - } + if let IntRange(other_range) = other_ctor { + if other_range == self_range { + // If the `self` range appears directly in a `match` arm, we can + // eliminate it straight away. + remaining_ranges = vec![]; + } else { + // Otherwise explicitely compute the remaining ranges. + remaining_ranges = other_range.subtract_from(remaining_ranges); + } - // If the constructor patterns that have been considered so far - // already cover the entire range of values, then we know the - // constructor is not missing, and we can move on to the next one. - if remaining_ctors.is_empty() { - break; + // If the ranges that have been considered so far already cover the entire + // range of values, we can return early. + if remaining_ranges.is_empty() { + break; + } } } - // If a constructor has not been matched, then it is missing. - // We add `remaining_ctors` instead of `self`, because then we can - // provide more detailed error information about precisely which - // ranges have been omitted. - remaining_ctors + // Convert the ranges back into constructors. + remaining_ranges.into_iter().map(IntRange).collect() } // This constructor is never covered by anything else NonExhaustive => vec![NonExhaustive], @@ -786,6 +736,8 @@ impl<'tcx> Constructor<'tcx> { } /// This returns one wildcard pattern for each argument to this constructor. + /// + /// This must be consistent with `apply`, `specialize_one_pattern`, and `arity`. fn wildcard_subpatterns<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, @@ -853,7 +805,7 @@ impl<'tcx> Constructor<'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, - ConstantValue(..) | ConstantRange(..) | NonExhaustive => vec![], + ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => vec![], } } @@ -862,6 +814,8 @@ impl<'tcx> Constructor<'tcx> { /// /// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3. /// A struct pattern's arity is the number of fields it contains, etc. + /// + /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `apply`. fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 { debug!("Constructor::arity({:#?}, {:?})", self, ty); match self { @@ -876,13 +830,15 @@ impl<'tcx> Constructor<'tcx> { }, FixedLenSlice(length) => *length, VarLenSlice(prefix, suffix) => prefix + suffix, - ConstantValue(..) | ConstantRange(..) | NonExhaustive => 0, + ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0, } } /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` /// must have as many elements as this constructor's arity. /// + /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `arity`. + /// /// Examples: /// `self`: `Constructor::Single` /// `ty`: `(u32, u32, u32)` @@ -937,12 +893,9 @@ impl<'tcx> Constructor<'tcx> { let wild = Pat::wildcard_from_ty(ty); PatKind::Slice { prefix, slice: Some(wild), suffix } } - &ConstantValue(value, _) => PatKind::Constant { value }, - &ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { - 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)), - end, - }), + &ConstantValue(value) => PatKind::Constant { value }, + &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, }; @@ -1139,11 +1092,17 @@ fn all_constructors<'a, 'tcx>( pcx: PatCtxt<'tcx>, ) -> Vec> { debug!("all_constructors({:?})", pcx.ty); + let make_range = |start, end| { + IntRange( + // `unwrap()` is ok because we know the type is an integer. + IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span) + .unwrap(), + ) + }; match pcx.ty.kind { - ty::Bool => [true, false] - .iter() - .map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span)) - .collect(), + ty::Bool => { + [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() + } ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![FixedLenSlice(len)] } @@ -1202,20 +1161,8 @@ fn all_constructors<'a, 'tcx>( ty::Char => { vec![ // The valid Unicode Scalar Value ranges. - ConstantRange( - '\u{0000}' as u128, - '\u{D7FF}' as u128, - cx.tcx.types.char, - RangeEnd::Included, - pcx.span, - ), - ConstantRange( - '\u{E000}' as u128, - '\u{10FFFF}' as u128, - cx.tcx.types.char, - RangeEnd::Included, - pcx.span, - ), + make_range('\u{0000}' as u128, '\u{D7FF}' as u128), + make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), ] } ty::Int(_) | ty::Uint(_) @@ -1231,12 +1178,12 @@ fn all_constructors<'a, 'tcx>( let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - vec![ConstantRange(min, max, pcx.ty, RangeEnd::Included, pcx.span)] + vec![make_range(min, max)] } ty::Uint(uty) => { let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); - vec![ConstantRange(0, max, pcx.ty, RangeEnd::Included, pcx.span)] + vec![make_range(0, max)] } _ => { if cx.is_uninhabited(pcx.ty) { @@ -1274,6 +1221,20 @@ impl<'tcx> IntRange<'tcx> { } } + fn is_singleton(&self) -> bool { + self.range.start() == self.range.end() + } + + fn boundaries(&self) -> (u128, u128) { + (*self.range.start(), *self.range.end()) + } + + /// Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` feature + /// is enabled. + fn treat_exhaustively(&self, tcx: TyCtxt<'tcx>) -> bool { + !self.ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching + } + #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match ty.kind { @@ -1296,9 +1257,9 @@ impl<'tcx> IntRange<'tcx> { ) -> Option> { if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { let ty = value.ty; - let val = if let ty::ConstKind::Value(ConstValue::Scalar( - Scalar::Raw { data, size } - )) = value.val { + let val = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, size })) = + value.val + { // For this specific pattern we can skip a lot of effort and go // straight to the result, after doing a bit of checking. (We // could remove this branch and just use the next branch, which @@ -1332,57 +1293,25 @@ impl<'tcx> IntRange<'tcx> { // which makes the interval arithmetic simpler. let bias = IntRange::signed_bias(tcx, ty); let (lo, hi) = (lo ^ bias, hi ^ bias); - // Make sure the interval is well-formed. - if lo > hi || lo == hi && *end == RangeEnd::Excluded { - None - } else { - let offset = (*end == RangeEnd::Excluded) as u128; - Some(IntRange { range: lo..=(hi - offset), ty, span }) + let offset = (*end == RangeEnd::Excluded) as u128; + if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { + // This should have been caught earlier by E0030. + bug!("malformed range pattern: {}..={}", lo, (hi - offset)); } + Some(IntRange { range: lo..=(hi - offset), ty, span }) } else { None } } - fn from_ctor( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - ) -> Option> { - // Floating-point ranges are permitted and we don't want - // to consider them when constructing integer ranges. - match ctor { - ConstantRange(lo, hi, ty, end, span) => Self::from_range(tcx, *lo, *hi, ty, end, *span), - ConstantValue(val, span) => Self::from_const(tcx, param_env, val, *span), - _ => None, - } - } - fn from_pat( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - mut pat: &Pat<'tcx>, + pat: &Pat<'tcx>, ) -> Option> { - loop { - match pat.kind { - box PatKind::Constant { value } => { - return Self::from_const(tcx, param_env, value, pat.span); - } - box PatKind::Range(PatRange { lo, hi, end }) => { - return Self::from_range( - tcx, - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - &lo.ty, - &end, - pat.span, - ); - } - box PatKind::AscribeUserType { ref subpattern, .. } => { - pat = subpattern; - } - _ => return None, - } + match pat_constructor(tcx, param_env, pat)? { + IntRange(range) => Some(range), + _ => None, } } @@ -1397,83 +1326,53 @@ impl<'tcx> IntRange<'tcx> { } } - /// Converts a `RangeInclusive` to a `ConstantValue` or inclusive `ConstantRange`. - fn range_to_ctor( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - r: RangeInclusive, - span: Span, - ) -> Constructor<'tcx> { - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = r.into_inner(); - if lo == hi { - let ty = ty::ParamEnv::empty().and(ty); - ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty), span) - } else { - ConstantRange(lo ^ bias, hi ^ bias, ty, RangeEnd::Included, span) - } - } - /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted /// by the values covered by `self`: i.e., `ranges \ self` (in set notation). - fn subtract_from( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ranges: Vec>, - ) -> Vec> { - let ranges = ranges - .into_iter() - .filter_map(|r| IntRange::from_ctor(tcx, param_env, &r).map(|i| i.range)); + fn subtract_from(&self, ranges: Vec>) -> Vec> { let mut remaining_ranges = vec![]; let ty = self.ty; - let (lo, hi) = self.range.into_inner(); + let span = self.span; + let (lo, hi) = self.boundaries(); for subrange in ranges { - let (subrange_lo, subrange_hi) = subrange.into_inner(); + let (subrange_lo, subrange_hi) = subrange.range.into_inner(); if lo > subrange_hi || subrange_lo > hi { // The pattern doesn't intersect with the subrange at all, // so the subrange remains untouched. - remaining_ranges.push(Self::range_to_ctor( - tcx, - ty, - subrange_lo..=subrange_hi, - self.span, - )); + remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span }); } else { if lo > subrange_lo { // The pattern intersects an upper section of the // subrange, so a lower section will remain. - remaining_ranges.push(Self::range_to_ctor( - tcx, - ty, - subrange_lo..=(lo - 1), - self.span, - )); + remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span }); } if hi < subrange_hi { // The pattern intersects a lower section of the // subrange, so an upper section will remain. - remaining_ranges.push(Self::range_to_ctor( - tcx, - ty, - (hi + 1)..=subrange_hi, - self.span, - )); + remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span }); } } } remaining_ranges } - fn intersection(&self, other: &Self) -> Option { + fn is_subrange(&self, other: &Self) -> bool { + other.range.start() <= self.range.start() && self.range.end() <= other.range.end() + } + + fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option { let ty = self.ty; - let (lo, hi) = (*self.range.start(), *self.range.end()); - let (other_lo, other_hi) = (*other.range.start(), *other.range.end()); - if lo <= other_hi && other_lo <= hi { - let span = other.span; - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) + let (lo, hi) = self.boundaries(); + let (other_lo, other_hi) = other.boundaries(); + if self.treat_exhaustively(tcx) { + if lo <= other_hi && other_lo <= hi { + let span = other.span; + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) + } else { + None + } } else { - None + // If the range should not be treated exhaustively, fallback to checking for inclusion. + if self.is_subrange(other) { Some(self.clone()) } else { None } } } @@ -1489,28 +1388,48 @@ impl<'tcx> IntRange<'tcx> { // `true` in the following cases: // 1 ------- // 1 ------- // 2 -------- // 2 ------- - let (lo, hi) = (*self.range.start(), *self.range.end()); - let (other_lo, other_hi) = (*other.range.start(), *other.range.end()); + let (lo, hi) = self.boundaries(); + let (other_lo, other_hi) = other.boundaries(); (lo == other_hi || hi == other_lo) } + + fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> { + let (lo, hi) = self.boundaries(); + + let bias = IntRange::signed_bias(tcx, self.ty); + let (lo, hi) = (lo ^ bias, hi ^ bias); + + let ty = ty::ParamEnv::empty().and(self.ty); + let lo_const = ty::Const::from_bits(tcx, lo, ty); + let hi_const = ty::Const::from_bits(tcx, hi, ty); + + let kind = if lo == hi { + PatKind::Constant { value: lo_const } + } else { + PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + }; + + // This is a brand new pattern, so we don't reuse `self.span`. + Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) } + } +} + +/// Ignore spans when comparing, they don't carry semantic information as they are only for lints. +impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> { + fn eq(&self, other: &Self) -> bool { + self.range == other.range && self.ty == other.ty + } } // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. struct MissingConstructors<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, } impl<'tcx> MissingConstructors<'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - all_ctors: Vec>, - used_ctors: Vec>, - ) -> Self { - MissingConstructors { tcx, param_env, all_ctors, used_ctors } + fn new(all_ctors: Vec>, used_ctors: Vec>) -> Self { + MissingConstructors { all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -1528,9 +1447,7 @@ impl<'tcx> MissingConstructors<'tcx> { /// Iterate over all_ctors \ used_ctors fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { - self.all_ctors.iter().flat_map(move |req_ctor| { - req_ctor.subtract_ctors(self.tcx, self.param_env, &self.used_ctors) - }) + self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors)) } } @@ -1619,7 +1536,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructor) = pat_constructor(cx, v.head(), pcx) { + if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { debug!("is_useful - expanding constructor: {:#?}", constructor); split_grouped_constructors( cx.tcx, @@ -1638,7 +1555,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful - expanding wildcard"); let used_ctors: Vec> = - matrix.heads().filter_map(|p| pat_constructor(cx, p, pcx)).collect(); + matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect(); debug!("used_ctors = {:#?}", used_ctors); // `all_ctors` are all the constructors for the given type, which // should all be represented (or caught with the wild pattern `_`). @@ -1656,7 +1573,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // Missing constructors are those that are not matched by any non-wildcard patterns in the // current column. We only fully construct them on-demand, because they're rarely used and // can be big. - let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); + let missing_ctors = MissingConstructors::new(all_ctors, used_ctors); debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); @@ -1741,28 +1658,44 @@ fn is_useful_specialized<'p, 'a, 'tcx>( /// Determines the constructor that the given pattern can be specialized to. /// Returns `None` in case of a catch-all, which can't be specialized. fn pat_constructor<'tcx>( - cx: &mut MatchCheckCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, - pcx: PatCtxt<'tcx>, ) -> Option> { match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => pat_constructor(cx, subpattern, pcx), + PatKind::AscribeUserType { ref subpattern, .. } => { + pat_constructor(tcx, param_env, subpattern) + } PatKind::Binding { .. } | PatKind::Wild => None, PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single), PatKind::Variant { adt_def, variant_index, .. } => { Some(Variant(adt_def.variants[variant_index].def_id)) } - PatKind::Constant { value } => Some(ConstantValue(value, pat.span)), - PatKind::Range(PatRange { lo, hi, end }) => Some(ConstantRange( - lo.eval_bits(cx.tcx, cx.param_env, lo.ty), - hi.eval_bits(cx.tcx, cx.param_env, hi.ty), - lo.ty, - end, - pat.span, - )), - PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => Some(FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))), - _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), + PatKind::Constant { value } => { + if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { + Some(IntRange(int_range)) + } else { + Some(ConstantValue(value)) + } + } + PatKind::Range(PatRange { lo, hi, end }) => { + let ty = lo.ty; + if let Some(int_range) = IntRange::from_range( + tcx, + lo.eval_bits(tcx, param_env, lo.ty), + hi.eval_bits(tcx, param_env, hi.ty), + ty, + &end, + pat.span, + ) { + Some(IntRange(int_range)) + } else { + Some(FloatRange(lo, hi, end)) + } + } + PatKind::Array { .. } => match pat.ty.kind { + ty::Array(_, length) => Some(FixedLenSlice(length.eval_usize(tcx, param_env))), + _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let prefix = prefix.len() as u64; @@ -1851,21 +1784,6 @@ fn slice_pat_covered_by_const<'tcx>( Ok(true) } -// Whether to evaluate a constructor using exhaustive integer matching. This is true if the -// constructor is a range or constant with an integer type. -fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) -> bool { - let ty = match ctor { - ConstantValue(value, _) => value.ty, - ConstantRange(_, _, ty, _, _) => ty, - _ => return false, - }; - if let ty::Char | ty::Int(_) | ty::Uint(_) = ty.kind { - !ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching - } else { - false - } -} - /// For exhaustive integer matching, some constructors are grouped within other constructors /// (namely integer typed values are grouped within ranges). However, when specialising these /// constructors, we want to be specialising for the underlying constructors (the integers), not @@ -1917,11 +1835,13 @@ fn split_grouped_constructors<'p, 'tcx>( for ctor in ctors.into_iter() { match ctor { - ConstantRange(..) if should_treat_range_exhaustively(tcx, &ctor) => { - // We only care about finding all the subranges within the range of the constructor - // range. Anything else is irrelevant, because it is guaranteed to result in - // `NotUseful`, which is the default case anyway, and can be ignored. - let ctor_range = IntRange::from_ctor(tcx, param_env, &ctor).unwrap(); + IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => { + // Fast-track if the range is trivial. In particular, don't do the overlapping + // ranges check. + if ctor_range.is_singleton() { + split_ctors.push(IntRange(ctor_range)); + continue; + } /// Represents a border between 2 integers. Because the intervals spanning borders /// must be able to cover every integer, we need to be able to represent @@ -1955,7 +1875,7 @@ fn split_grouped_constructors<'p, 'tcx>( IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) }) .flat_map(|(range, row_len)| { - let intersection = ctor_range.intersection(&range); + let intersection = ctor_range.intersection(tcx, &range); let should_lint = ctor_range.suspicious_intersection(&range); if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { // FIXME: for now, only check for overlapping ranges on simple range @@ -1980,23 +1900,24 @@ fn split_grouped_constructors<'p, 'tcx>( // We're going to iterate through every adjacent pair of borders, making sure that // each represents an interval of nonnegative length, and convert each such // interval into a constructor. - for IntRange { range, .. } in - borders.windows(2).filter_map(|window| match (window[0], window[1]) { - (Border::JustBefore(n), Border::JustBefore(m)) => { - if n < m { - Some(IntRange { range: n..=(m - 1), ty, span }) - } else { - None + split_ctors.extend( + borders + .windows(2) + .filter_map(|window| match (window[0], window[1]) { + (Border::JustBefore(n), Border::JustBefore(m)) => { + if n < m { + Some(IntRange { range: n..=(m - 1), ty, span }) + } else { + None + } } - } - (Border::JustBefore(n), Border::AfterMax) => { - Some(IntRange { range: n..=u128::MAX, ty, span }) - } - (Border::AfterMax, _) => None, - }) - { - split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); - } + (Border::JustBefore(n), Border::AfterMax) => { + Some(IntRange { range: n..=u128::MAX, ty, span }) + } + (Border::AfterMax, _) => None, + }) + .map(IntRange), + ); } VarLenSlice(self_prefix, self_suffix) => { // The exhaustiveness-checking paper does not include any details on @@ -2076,10 +1997,10 @@ fn split_grouped_constructors<'p, 'tcx>( max_fixed_len = cmp::max(max_fixed_len, n.eval_usize(tcx, param_env)) } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), - ty::Slice(_)) => { - max_fixed_len = cmp::max(max_fixed_len, (end - start) as u64) - } + ( + ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), + ty::Slice(_), + ) => max_fixed_len = cmp::max(max_fixed_len, (end - start) as u64), _ => {} } } @@ -2143,7 +2064,7 @@ fn lint_overlapping_patterns( int_range.span, &format!( "this range overlaps on `{}`", - IntRange::range_to_ctor(tcx, ty, int_range.range, DUMMY_SP).display(tcx), + IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), ), ); } @@ -2156,57 +2077,28 @@ fn constructor_covered_by_range<'tcx>( param_env: ty::ParamEnv<'tcx>, ctor: &Constructor<'tcx>, pat: &Pat<'tcx>, -) -> Result { - let (from, to, end, ty) = match pat.kind { - box PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - box PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), +) -> Option<()> { + if let Single = ctor { + return Some(()); + } + + let (pat_from, pat_to, pat_end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), _ => bug!("`constructor_covered_by_range` called with {:?}", pat), }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); - let cmp_from = |c_from| { - compare_const_vals(tcx, c_from, from, param_env, ty).map(|res| res != Ordering::Less) + let (ctor_from, ctor_to, ctor_end) = match *ctor { + ConstantValue(value) => (value, value, RangeEnd::Included), + FloatRange(from, to, ctor_end) => (from, to, ctor_end), + _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), }; - let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); - macro_rules! some_or_ok { - ($e:expr) => { - match $e { - Some(to) => to, - None => return Ok(false), // not char or int - } - }; - } - match *ctor { - ConstantValue(value, _) => { - let to = some_or_ok!(cmp_to(value)); - let end = - (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(some_or_ok!(cmp_from(value)) && end) - } - ConstantRange(from, to, ty, RangeEnd::Included, _) => { - let to = - some_or_ok!(cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty),))); - let end = - (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(some_or_ok!(cmp_from(ty::Const::from_bits( - tcx, - from, - ty::ParamEnv::empty().and(ty), - ))) && end) - } - ConstantRange(from, to, ty, RangeEnd::Excluded, _) => { - let to = - some_or_ok!(cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty)))); - let end = - (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); - Ok(some_or_ok!(cmp_from(ty::Const::from_bits( - tcx, - from, - ty::ParamEnv::empty().and(ty) - ))) && end) - } - Single => Ok(true), - _ => bug!(), - } + trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); + + let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; + let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; + let intersects = (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); + if intersects { Some(()) } else { None } } fn patterns_for_variant<'p, 'a: 'p, 'tcx>( @@ -2336,15 +2228,12 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( // If the constructor is a: // - Single value: add a row if the pattern contains the constructor. // - Range: add a row if the constructor intersects the pattern. - if should_treat_range_exhaustively(cx.tcx, constructor) { - match ( - IntRange::from_ctor(cx.tcx, cx.param_env, constructor), - IntRange::from_pat(cx.tcx, cx.param_env, pat), - ) { - (Some(ctor), Some(pat)) => ctor.intersection(&pat).map(|_| { - let (pat_lo, pat_hi) = pat.range.into_inner(); - let (ctor_lo, ctor_hi) = ctor.range.into_inner(); - assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); + if let IntRange(ctor) = constructor { + match IntRange::from_pat(cx.tcx, cx.param_env, pat) { + Some(pat) => ctor.intersection(cx.tcx, &pat).map(|_| { + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); PatStack::default() }), _ => None, @@ -2355,10 +2244,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( // by `IntRange`. For these cases, the constructor may not be a // range so intersection actually devolves into being covered // by the pattern. - match constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat) { - Ok(true) => Some(PatStack::default()), - Ok(false) | Err(ErrorReported) => None, - } + constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat) + .map(|()| PatStack::default()) } } @@ -2388,7 +2275,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( None } } - ConstantValue(cv, _) => { + ConstantValue(cv) => { match slice_pat_covered_by_const( cx.tcx, pat.span, diff --git a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs index 59f74919897..d379dc44bf1 100644 --- a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs +++ b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs @@ -154,4 +154,19 @@ fn main() { match 0u128 { //~ ERROR non-exhaustive patterns 4 ..= u128::MAX => {} } + + const FOO: i32 = 42; + const BAR: &i32 = &42; + match &0 { + &42 => {} + &FOO => {} //~ ERROR unreachable pattern + BAR => {} // Not detected as unreachable because `try_eval_bits` fails on `BAR`. + _ => {} + } + + // Regression test, see https://github.com/rust-lang/rust/pull/66326#issuecomment-552889933 + match &0 { + BAR => {} // ok + _ => {} + } } diff --git a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr index 7a3a36a820c..0fbeb981ea0 100644 --- a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr +++ b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr @@ -118,6 +118,12 @@ LL | match 0u128 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 14 previous errors +error: unreachable pattern + --> $DIR/exhaustive_integer_patterns.rs:162:9 + | +LL | &FOO => {} + | ^^^^ + +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`.