1
Fork 0

Don't store ty and span in IntRange

We prefer to grab `ty` and `span` from `pcx`. This makes it consistent
with other constructors.
This commit is contained in:
Nadrieril 2020-11-28 22:07:15 +00:00
parent d8983655c1
commit bdd2bdb53b
2 changed files with 56 additions and 66 deletions

View file

@ -37,14 +37,12 @@ use std::ops::RangeInclusive;
/// ///
/// `IntRange` is never used to encode an empty range or a "range" that wraps /// `IntRange` is never used to encode an empty range or a "range" that wraps
/// around the (offset) space: i.e., `range.lo <= range.hi`. /// around the (offset) space: i.e., `range.lo <= range.hi`.
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct IntRange<'tcx> { pub(super) struct IntRange {
range: RangeInclusive<u128>, range: RangeInclusive<u128>,
ty: Ty<'tcx>,
span: Span,
} }
impl<'tcx> IntRange<'tcx> { impl IntRange {
#[inline] #[inline]
fn is_integral(ty: Ty<'_>) -> bool { fn is_integral(ty: Ty<'_>) -> bool {
matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool)
@ -59,7 +57,7 @@ impl<'tcx> IntRange<'tcx> {
} }
#[inline] #[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() { match *ty.kind() {
ty::Bool => Some((Size::from_bytes(1), 0)), ty::Bool => Some((Size::from_bytes(1), 0)),
ty::Char => Some((Size::from_bytes(4), 0)), ty::Char => Some((Size::from_bytes(4), 0)),
@ -73,12 +71,11 @@ impl<'tcx> IntRange<'tcx> {
} }
#[inline] #[inline]
fn from_const( fn from_const<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
value: &Const<'tcx>, value: &Const<'tcx>,
span: Span, ) -> Option<IntRange> {
) -> Option<IntRange<'tcx>> {
if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) {
let ty = value.ty; let ty = value.ty;
let val = (|| { let val = (|| {
@ -95,21 +92,20 @@ impl<'tcx> IntRange<'tcx> {
value.try_eval_bits(tcx, param_env, ty) value.try_eval_bits(tcx, param_env, ty)
})()?; })()?;
let val = val ^ bias; let val = val ^ bias;
Some(IntRange { range: val..=val, ty, span }) Some(IntRange { range: val..=val })
} else { } else {
None None
} }
} }
#[inline] #[inline]
fn from_range( fn from_range<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
lo: u128, lo: u128,
hi: u128, hi: u128,
ty: Ty<'tcx>, ty: Ty<'tcx>,
end: &RangeEnd, end: &RangeEnd,
span: Span, ) -> Option<IntRange> {
) -> Option<IntRange<'tcx>> {
if Self::is_integral(ty) { if Self::is_integral(ty) {
// Perform a shift if the underlying types are signed, // Perform a shift if the underlying types are signed,
// which makes the interval arithmetic simpler. // which makes the interval arithmetic simpler.
@ -120,14 +116,14 @@ impl<'tcx> IntRange<'tcx> {
// This should have been caught earlier by E0030. // This should have been caught earlier by E0030.
bug!("malformed range pattern: {}..={}", lo, (hi - offset)); bug!("malformed range pattern: {}..={}", lo, (hi - offset));
} }
Some(IntRange { range: lo..=(hi - offset), ty, span }) Some(IntRange { range: lo..=(hi - offset) })
} else { } else {
None None
} }
} }
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. // 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() { match *ty.kind() {
ty::Int(ity) => { ty::Int(ity) => {
let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; 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<Self> { fn intersection(&self, other: &Self) -> Option<Self> {
let ty = self.ty;
let (lo, hi) = self.boundaries(); let (lo, hi) = self.boundaries();
let (other_lo, other_hi) = other.boundaries(); let (other_lo, other_hi) = other.boundaries();
if lo <= other_hi && other_lo <= hi { if lo <= other_hi && other_lo <= hi {
let span = other.span; Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span })
} else { } else {
None None
} }
@ -170,15 +164,15 @@ impl<'tcx> IntRange<'tcx> {
lo == other_hi || hi == other_lo 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 (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 (lo, hi) = (lo ^ bias, hi ^ bias);
let ty = ty::ParamEnv::empty().and(self.ty); let env = ty::ParamEnv::empty().and(ty);
let lo_const = ty::Const::from_bits(tcx, lo, ty); let lo_const = ty::Const::from_bits(tcx, lo, env);
let hi_const = ty::Const::from_bits(tcx, hi, ty); let hi_const = ty::Const::from_bits(tcx, hi, env);
let kind = if lo == hi { let kind = if lo == hi {
PatKind::Constant { value: lo_const } 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 }) 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, span: DUMMY_SP, kind: Box::new(kind) }
Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) }
} }
/// For exhaustive integer matching, some constructors are grouped within other constructors /// 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 /// 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 /// between every pair of boundary points. (This essentially sums up to performing the intuitive
/// merging operation depicted above.) /// merging operation depicted above.)
fn split<'p>( fn split<'p, 'tcx>(
&self, &self,
pcx: PatCtxt<'_, 'p, 'tcx>, pcx: PatCtxt<'_, 'p, 'tcx>,
hir_id: Option<HirId>, hir_id: Option<HirId>,
) -> SmallVec<[Constructor<'tcx>; 1]> { ) -> SmallVec<[Constructor<'tcx>; 1]> {
let ty = pcx.ty;
/// Represents a border between 2 integers. Because the intervals spanning borders /// 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 /// must be able to cover every integer, we need to be able to represent
/// 2^128 + 1 such borders. /// 2^128 + 1 such borders.
@ -239,7 +230,7 @@ impl<'tcx> IntRange<'tcx> {
} }
// A function for extracting the borders of an integer interval. // A function for extracting the borders of an integer interval.
fn range_borders(r: IntRange<'_>) -> impl Iterator<Item = Border> { fn range_borders(r: IntRange) -> impl Iterator<Item = Border> {
let (lo, hi) = r.range.into_inner(); let (lo, hi) = r.range.into_inner();
let from = Border::JustBefore(lo); let from = Border::JustBefore(lo);
let to = match hi.checked_add(1) { let to = match hi.checked_add(1) {
@ -257,21 +248,23 @@ impl<'tcx> IntRange<'tcx> {
// class lies between 2 borders. // class lies between 2 borders.
let row_borders = pcx let row_borders = pcx
.matrix .matrix
.head_ctors(pcx.cx) .head_ctors_and_spans(pcx.cx)
.filter_map(|ctor| ctor.as_int_range()) .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span)))
.filter_map(|range| { .filter_map(|(range, span)| {
let intersection = self.intersection(&range); let intersection = self.intersection(&range);
let should_lint = self.suspicious_intersection(&range); let should_lint = self.suspicious_intersection(&range);
if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { if let (Some(range), 1, true) = (&intersection, row_len, should_lint) {
// FIXME: for now, only check for overlapping ranges on simple range // FIXME: for now, only check for overlapping ranges on simple range
// patterns. Otherwise with the current logic the following is detected // patterns. Otherwise with the current logic the following is detected
// as overlapping: // as overlapping:
// match (10u8, true) { // ```
// (0 ..= 125, false) => {} // match (0u8, true) {
// (126 ..= 255, false) => {} // (0 ..= 125, false) => {}
// (0 ..= 255, true) => {} // (125 ..= 255, true) => {}
// } // _ => {}
overlaps.push(range.clone()); // }
// ```
overlaps.push((range.clone(), span));
} }
intersection intersection
}) })
@ -280,7 +273,7 @@ impl<'tcx> IntRange<'tcx> {
let mut borders: Vec<_> = row_borders.chain(self_borders).collect(); let mut borders: Vec<_> = row_borders.chain(self_borders).collect();
borders.sort_unstable(); 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 // 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 // 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::JustBefore(n), Border::AfterMax] => Some(n..=u128::MAX),
[Border::AfterMax, _] => None, [Border::AfterMax, _] => None,
}) })
.map(|range| IntRange { range, ty, span: pcx.span }) .map(|range| IntRange { range })
.map(IntRange) .map(IntRange)
.collect() .collect()
} }
fn lint_overlapping_patterns( fn lint_overlapping_patterns(
&self, &self,
tcx: TyCtxt<'tcx>, pcx: PatCtxt<'_, '_, '_>,
hir_id: Option<HirId>, hir_id: Option<HirId>,
ty: Ty<'tcx>, overlaps: Vec<(IntRange, Span)>,
overlaps: Vec<IntRange<'tcx>>,
) { ) {
if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { 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, lint::builtin::OVERLAPPING_PATTERNS,
hir_id, hir_id,
self.span, pcx.span,
|lint| { |lint| {
let mut err = lint.build("multiple patterns covering the same range"); let mut err = lint.build("multiple patterns covering the same range");
err.span_label(self.span, "overlapping patterns"); err.span_label(pcx.span, "overlapping patterns");
for int_range in overlaps { for (int_range, span) in overlaps {
// Use the real type for user display of the ranges: // Use the real type for user display of the ranges:
err.span_label( err.span_label(
int_range.span, span,
&format!( &format!(
"this range overlaps on `{}`", "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)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SliceKind { enum SliceKind {
/// Patterns of length `n` (`[x, y]`). /// Patterns of length `n` (`[x, y]`).
@ -547,7 +532,7 @@ pub(super) enum Constructor<'tcx> {
/// Enum variants. /// Enum variants.
Variant(DefId), Variant(DefId),
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`). /// 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`). /// Ranges of floating-point literal values (`2.0..=5.2`).
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), 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. /// 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) matches!(self, Wildcard)
} }
fn as_int_range(&self) -> Option<&IntRange<'tcx>> { fn as_int_range(&self) -> Option<&IntRange> {
match self { match self {
IntRange(range) => Some(range), IntRange(range) => Some(range),
_ => None, _ => None,
@ -605,8 +590,7 @@ impl<'tcx> Constructor<'tcx> {
Variant(adt_def.variants[variant_index].def_id) Variant(adt_def.variants[variant_index].def_id)
} }
PatKind::Constant { value } => { 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) IntRange(int_range)
} else { } else {
match pat.ty.kind() { match pat.ty.kind() {
@ -630,7 +614,6 @@ impl<'tcx> Constructor<'tcx> {
hi.eval_bits(cx.tcx, cx.param_env, hi.ty), hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
ty, ty,
&end, &end,
pat.span,
) { ) {
IntRange(int_range) IntRange(int_range)
} else { } else {
@ -815,8 +798,7 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
let make_range = |start, end| { let make_range = |start, end| {
IntRange( IntRange(
// `unwrap()` is ok because we know the type is an integer. // `unwrap()` is ok because we know the type is an integer.
IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span) IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(),
.unwrap(),
) )
}; };
match pcx.ty.kind() { match pcx.ty.kind() {
@ -1221,7 +1203,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
}, },
&Str(value) => PatKind::Constant { value }, &Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), &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, NonExhaustive => PatKind::Wild,
Opaque => bug!("we should not try to apply an opaque constructor"), Opaque => bug!("we should not try to apply an opaque constructor"),
Wildcard => bug!( Wildcard => bug!(

View file

@ -535,14 +535,22 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
self.patterns.iter().map(|r| r.head()) 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>( pub(super) fn head_ctors<'a>(
&'a self, &'a self,
cx: &'a MatchCheckCtxt<'p, 'tcx>, cx: &'a MatchCheckCtxt<'p, 'tcx>,
) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'a> + Captures<'p> { ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> {
self.patterns.iter().map(move |r| r.head_ctor(cx)) 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<Item = (&'a Constructor<'tcx>, 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. /// This computes `S(constructor, self)`. See top of the file for explanations.
fn specialize_constructor( fn specialize_constructor(
&self, &self,