Auto merge of #66326 - Nadrieril:refactor-intrange, r=varkor
Refactor integer range handling in the usefulness algorithm Integer range handling had accumulated a lot of debt. This cleans up a lot of it. In particular this: - removes unnecessary conversions between `Const` and `u128`, and between `Constructor` and `IntRange` - clearly distinguishes between on the one hand ranges of integers that may or may not be matched exhaustively, and on the other hand ranges of non-integers that are never matched exhaustively and are compared using Const-based shenanigans - cleans up some overly complicated code paths - generally tries to be more idiomatic. As a nice side-effect, I measured a 10% perf increase on `unicode_normalization`. There's one thing that I feel remains to clean up: the [overlapping range check](https://github.com/rust-lang/rust/pull/64007), which is currently quite ad-hoc. But that is intricate enough that I'm leaving it out of this PR. There's also one little thing I'm not sure I understand: can `try_eval_bits` fail for an integer constant value in that code ? What would that mean, and how do I construct a test case for this possibility ?
This commit is contained in:
commit
82161cda33
3 changed files with 255 additions and 347 deletions
|
@ -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<Constructor<'tcx>>,
|
||||
) -> Vec<Constructor<'tcx>> {
|
||||
match *self {
|
||||
fn subtract_ctors(&self, other_ctors: &Vec<Constructor<'tcx>>) -> Vec<Constructor<'tcx>> {
|
||||
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<Constructor<'tcx>> {
|
||||
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<IntRange<'tcx>> {
|
||||
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<IntRange<'tcx>> {
|
||||
// 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<IntRange<'tcx>> {
|
||||
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<u128>,
|
||||
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<Constructor<'tcx>>,
|
||||
) -> Vec<Constructor<'tcx>> {
|
||||
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<IntRange<'tcx>>) -> Vec<IntRange<'tcx>> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
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<Constructor<'tcx>>,
|
||||
used_ctors: Vec<Constructor<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> MissingConstructors<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
all_ctors: Vec<Constructor<'tcx>>,
|
||||
used_ctors: Vec<Constructor<'tcx>>,
|
||||
) -> Self {
|
||||
MissingConstructors { tcx, param_env, all_ctors, used_ctors }
|
||||
fn new(all_ctors: Vec<Constructor<'tcx>>, used_ctors: Vec<Constructor<'tcx>>) -> Self {
|
||||
MissingConstructors { all_ctors, used_ctors }
|
||||
}
|
||||
|
||||
fn into_inner(self) -> (Vec<Constructor<'tcx>>, Vec<Constructor<'tcx>>) {
|
||||
|
@ -1528,9 +1447,7 @@ impl<'tcx> MissingConstructors<'tcx> {
|
|||
|
||||
/// Iterate over all_ctors \ used_ctors
|
||||
fn iter<'a>(&'a self) -> impl Iterator<Item = Constructor<'tcx>> + 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<Constructor<'_>> =
|
||||
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<Constructor<'tcx>> {
|
||||
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<bool, ErrorReported> {
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
Loading…
Add table
Reference in a new issue