diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 34c415987d8c0..3b2eef5a905dd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -37,14 +37,12 @@ use std::ops::RangeInclusive; /// /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. -#[derive(Clone, Debug)] -pub(super) struct IntRange<'tcx> { +#[derive(Clone, Debug, PartialEq, Eq)] +pub(super) struct IntRange { range: RangeInclusive, - ty: Ty<'tcx>, - span: Span, } -impl<'tcx> IntRange<'tcx> { +impl IntRange { #[inline] fn is_integral(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) @@ -59,7 +57,7 @@ impl<'tcx> IntRange<'tcx> { } #[inline] - fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { + fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> { match *ty.kind() { ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), @@ -73,12 +71,11 @@ impl<'tcx> IntRange<'tcx> { } #[inline] - fn from_const( + fn from_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, value: &Const<'tcx>, - span: Span, - ) -> Option> { + ) -> Option { if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { let ty = value.ty; let val = (|| { @@ -95,21 +92,20 @@ impl<'tcx> IntRange<'tcx> { value.try_eval_bits(tcx, param_env, ty) })()?; let val = val ^ bias; - Some(IntRange { range: val..=val, ty, span }) + Some(IntRange { range: val..=val }) } else { None } } #[inline] - fn from_range( + fn from_range<'tcx>( tcx: TyCtxt<'tcx>, lo: u128, hi: u128, ty: Ty<'tcx>, end: &RangeEnd, - span: Span, - ) -> Option> { + ) -> Option { if Self::is_integral(ty) { // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. @@ -120,14 +116,14 @@ impl<'tcx> IntRange<'tcx> { // This should have been caught earlier by E0030. bug!("malformed range pattern: {}..={}", lo, (hi - offset)); } - Some(IntRange { range: lo..=(hi - offset), ty, span }) + Some(IntRange { range: lo..=(hi - offset) }) } else { None } } // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. - fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { + fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { match *ty.kind() { ty::Int(ity) => { let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; @@ -142,12 +138,10 @@ impl<'tcx> IntRange<'tcx> { } fn intersection(&self, other: &Self) -> Option { - let ty = self.ty; let (lo, hi) = self.boundaries(); let (other_lo, other_hi) = other.boundaries(); if lo <= other_hi && other_lo <= hi { - let span = other.span; - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) }) } else { None } @@ -170,15 +164,15 @@ impl<'tcx> IntRange<'tcx> { lo == other_hi || hi == other_lo } - fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> { + fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { let (lo, hi) = self.boundaries(); - let bias = IntRange::signed_bias(tcx, self.ty); + let bias = IntRange::signed_bias(tcx, 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 env = ty::ParamEnv::empty().and(ty); + let lo_const = ty::Const::from_bits(tcx, lo, env); + let hi_const = ty::Const::from_bits(tcx, hi, env); let kind = if lo == hi { PatKind::Constant { value: lo_const } @@ -186,8 +180,7 @@ impl<'tcx> IntRange<'tcx> { 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) } + Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } } /// For exhaustive integer matching, some constructors are grouped within other constructors @@ -222,13 +215,11 @@ impl<'tcx> IntRange<'tcx> { /// boundaries for each interval range, sort them, then create constructors for each new interval /// between every pair of boundary points. (This essentially sums up to performing the intuitive /// merging operation depicted above.) - fn split<'p>( + fn split<'p, 'tcx>( &self, pcx: PatCtxt<'_, 'p, 'tcx>, hir_id: Option, ) -> SmallVec<[Constructor<'tcx>; 1]> { - let ty = pcx.ty; - /// 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 /// 2^128 + 1 such borders. @@ -239,7 +230,7 @@ impl<'tcx> IntRange<'tcx> { } // A function for extracting the borders of an integer interval. - fn range_borders(r: IntRange<'_>) -> impl Iterator { + fn range_borders(r: IntRange) -> impl Iterator { let (lo, hi) = r.range.into_inner(); let from = Border::JustBefore(lo); let to = match hi.checked_add(1) { @@ -257,21 +248,23 @@ impl<'tcx> IntRange<'tcx> { // class lies between 2 borders. let row_borders = pcx .matrix - .head_ctors(pcx.cx) - .filter_map(|ctor| ctor.as_int_range()) - .filter_map(|range| { + .head_ctors_and_spans(pcx.cx) + .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span))) + .filter_map(|(range, span)| { let intersection = self.intersection(&range); let should_lint = self.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 // patterns. Otherwise with the current logic the following is detected // as overlapping: - // match (10u8, true) { - // (0 ..= 125, false) => {} - // (126 ..= 255, false) => {} - // (0 ..= 255, true) => {} - // } - overlaps.push(range.clone()); + // ``` + // match (0u8, true) { + // (0 ..= 125, false) => {} + // (125 ..= 255, true) => {} + // _ => {} + // } + // ``` + overlaps.push((range.clone(), span)); } intersection }) @@ -280,7 +273,7 @@ impl<'tcx> IntRange<'tcx> { let mut borders: Vec<_> = row_borders.chain(self_borders).collect(); borders.sort_unstable(); - self.lint_overlapping_patterns(pcx.cx.tcx, hir_id, ty, overlaps); + self.lint_overlapping_patterns(pcx, hir_id, overlaps); // 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 @@ -298,33 +291,32 @@ impl<'tcx> IntRange<'tcx> { [Border::JustBefore(n), Border::AfterMax] => Some(n..=u128::MAX), [Border::AfterMax, _] => None, }) - .map(|range| IntRange { range, ty, span: pcx.span }) + .map(|range| IntRange { range }) .map(IntRange) .collect() } fn lint_overlapping_patterns( &self, - tcx: TyCtxt<'tcx>, + pcx: PatCtxt<'_, '_, '_>, hir_id: Option, - ty: Ty<'tcx>, - overlaps: Vec>, + overlaps: Vec<(IntRange, Span)>, ) { if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { - tcx.struct_span_lint_hir( + pcx.cx.tcx.struct_span_lint_hir( lint::builtin::OVERLAPPING_PATTERNS, hir_id, - self.span, + pcx.span, |lint| { let mut err = lint.build("multiple patterns covering the same range"); - err.span_label(self.span, "overlapping patterns"); - for int_range in overlaps { + err.span_label(pcx.span, "overlapping patterns"); + for (int_range, span) in overlaps { // Use the real type for user display of the ranges: err.span_label( - int_range.span, + span, &format!( "this range overlaps on `{}`", - IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), + int_range.to_pat(pcx.cx.tcx, pcx.ty), ), ); } @@ -347,13 +339,6 @@ impl<'tcx> IntRange<'tcx> { } } -/// 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 - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum SliceKind { /// Patterns of length `n` (`[x, y]`). @@ -547,7 +532,7 @@ pub(super) enum Constructor<'tcx> { /// Enum variants. Variant(DefId), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). - IntRange(IntRange<'tcx>), + IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. @@ -570,7 +555,7 @@ impl<'tcx> Constructor<'tcx> { matches!(self, Wildcard) } - fn as_int_range(&self) -> Option<&IntRange<'tcx>> { + fn as_int_range(&self) -> Option<&IntRange> { match self { IntRange(range) => Some(range), _ => None, @@ -605,8 +590,7 @@ impl<'tcx> Constructor<'tcx> { Variant(adt_def.variants[variant_index].def_id) } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value, pat.span) - { + if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) { IntRange(int_range) } else { match pat.ty.kind() { @@ -630,7 +614,6 @@ impl<'tcx> Constructor<'tcx> { hi.eval_bits(cx.tcx, cx.param_env, hi.ty), ty, &end, - pat.span, ) { IntRange(int_range) } else { @@ -815,8 +798,7 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec Fields<'p, 'tcx> { }, &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), - IntRange(range) => return range.to_pat(pcx.cx.tcx), + IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty), NonExhaustive => PatKind::Wild, Opaque => bug!("we should not try to apply an opaque constructor"), Wildcard => bug!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index be96d5ae81684..f3e1507b37ae1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -535,14 +535,22 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.patterns.iter().map(|r| r.head()) } - /// Iterate over the first constructor of each row + /// Iterate over the first constructor of each row. pub(super) fn head_ctors<'a>( &'a self, cx: &'a MatchCheckCtxt<'p, 'tcx>, - ) -> impl Iterator> + Captures<'a> + Captures<'p> { + ) -> impl Iterator> + Captures<'p> { self.patterns.iter().map(move |r| r.head_ctor(cx)) } + /// Iterate over the first constructor and the corresponding span of each row. + pub(super) fn head_ctors_and_spans<'a>( + &'a self, + cx: &'a MatchCheckCtxt<'p, 'tcx>, + ) -> impl Iterator, Span)> + Captures<'p> { + self.patterns.iter().map(move |r| (r.head_ctor(cx), r.head().span)) + } + /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize_constructor( &self,