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:
bors 2019-11-15 23:28:50 +00:00
commit 82161cda33
3 changed files with 255 additions and 347 deletions

View file

@ -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,

View file

@ -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
_ => {}
}
}

View file

@ -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`.