mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #116692 - Nadrieril:half-open-ranges, r=cjgillot
Match usize/isize exhaustively with half-open ranges The long-awaited finale to the saga of [exhaustiveness checking for integers](https://github.com/rust-lang/rust/pull/50912)! ```rust match 0usize { 0.. => {} // exhaustive! } match 0usize { 0..usize::MAX => {} // helpful error message! } ``` Features: - Half-open ranges behave as expected for `usize`/`isize`; - Trying to use `0..usize::MAX` will tell you that `usize::MAX..` is missing and explain why. No more unhelpful "`_` is missing"; - Everything else stays the same. This should unblock https://github.com/rust-lang/rust/issues/37854. Review-wise: - I recommend looking commit-by-commit; - This regresses perf because of the added complexity in `IntRange`; hopefully not too much; - I measured each `#[inline]`, they all help a bit with the perf regression (tho I don't get why); - I did not touch MIR building; I expect there's an easy PR there that would skip unnecessary comparisons when the range is half-open.
This commit is contained in:
commit
7fc6365570
@ -16,17 +16,19 @@ use rustc_hir::RangeEnd;
|
||||
use rustc_index::newtype_index;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::interpret::AllocId;
|
||||
use rustc_middle::mir::interpret::{AllocId, Scalar};
|
||||
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, List, Ty,
|
||||
UpvarArgs,
|
||||
TyCtxt, UpvarArgs,
|
||||
};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx};
|
||||
use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::ops::Index;
|
||||
|
||||
@ -810,12 +812,243 @@ pub enum PatKind<'tcx> {
|
||||
Error(ErrorGuaranteed),
|
||||
}
|
||||
|
||||
/// A range pattern.
|
||||
/// The boundaries must be of the same type and that type must be numeric.
|
||||
#[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
||||
pub struct PatRange<'tcx> {
|
||||
pub lo: mir::Const<'tcx>,
|
||||
pub hi: mir::Const<'tcx>,
|
||||
pub lo: PatRangeBoundary<'tcx>,
|
||||
pub hi: PatRangeBoundary<'tcx>,
|
||||
#[type_visitable(ignore)]
|
||||
pub end: RangeEnd,
|
||||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> PatRange<'tcx> {
|
||||
/// Whether this range covers the full extent of possible values (best-effort, we ignore floats).
|
||||
#[inline]
|
||||
pub fn is_full_range(&self, tcx: TyCtxt<'tcx>) -> Option<bool> {
|
||||
let (min, max, size, bias) = match *self.ty.kind() {
|
||||
ty::Char => (0, std::char::MAX as u128, Size::from_bits(32), 0),
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_int_ty(&tcx, ity).size();
|
||||
let max = size.truncate(u128::MAX);
|
||||
let bias = 1u128 << (size.bits() - 1);
|
||||
(0, max, size, bias)
|
||||
}
|
||||
ty::Uint(uty) => {
|
||||
let size = Integer::from_uint_ty(&tcx, uty).size();
|
||||
let max = size.unsigned_int_max();
|
||||
(0, max, size, 0)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// We want to compare ranges numerically, but the order of the bitwise representation of
|
||||
// signed integers does not match their numeric order. Thus, to correct the ordering, we
|
||||
// need to shift the range of signed integers to correct the comparison. This is achieved by
|
||||
// XORing with a bias (see pattern/deconstruct_pat.rs for another pertinent example of this
|
||||
// pattern).
|
||||
//
|
||||
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
|
||||
let lo_is_min = match self.lo {
|
||||
PatRangeBoundary::NegInfinity => true,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let lo = value.try_to_bits(size).unwrap() ^ bias;
|
||||
lo <= min
|
||||
}
|
||||
PatRangeBoundary::PosInfinity => false,
|
||||
};
|
||||
if lo_is_min {
|
||||
let hi_is_max = match self.hi {
|
||||
PatRangeBoundary::NegInfinity => false,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let hi = value.try_to_bits(size).unwrap() ^ bias;
|
||||
hi > max || hi == max && self.end == RangeEnd::Included
|
||||
}
|
||||
PatRangeBoundary::PosInfinity => true,
|
||||
};
|
||||
if hi_is_max {
|
||||
return Some(true);
|
||||
}
|
||||
}
|
||||
Some(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(
|
||||
&self,
|
||||
value: mir::Const<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Option<bool> {
|
||||
use Ordering::*;
|
||||
debug_assert_eq!(self.ty, value.ty());
|
||||
let ty = self.ty;
|
||||
let value = PatRangeBoundary::Finite(value);
|
||||
// For performance, it's important to only do the second comparison if necessary.
|
||||
Some(
|
||||
match self.lo.compare_with(value, ty, tcx, param_env)? {
|
||||
Less | Equal => true,
|
||||
Greater => false,
|
||||
} && match value.compare_with(self.hi, ty, tcx, param_env)? {
|
||||
Less => true,
|
||||
Equal => self.end == RangeEnd::Included,
|
||||
Greater => false,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn overlaps(
|
||||
&self,
|
||||
other: &Self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Option<bool> {
|
||||
use Ordering::*;
|
||||
debug_assert_eq!(self.ty, other.ty);
|
||||
// For performance, it's important to only do the second comparison if necessary.
|
||||
Some(
|
||||
match other.lo.compare_with(self.hi, self.ty, tcx, param_env)? {
|
||||
Less => true,
|
||||
Equal => self.end == RangeEnd::Included,
|
||||
Greater => false,
|
||||
} && match self.lo.compare_with(other.hi, self.ty, tcx, param_env)? {
|
||||
Less => true,
|
||||
Equal => other.end == RangeEnd::Included,
|
||||
Greater => false,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for PatRange<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let PatRangeBoundary::Finite(value) = &self.lo {
|
||||
write!(f, "{value}")?;
|
||||
}
|
||||
if let PatRangeBoundary::Finite(value) = &self.hi {
|
||||
write!(f, "{}", self.end)?;
|
||||
write!(f, "{value}")?;
|
||||
} else {
|
||||
// `0..` is parsed as an inclusive range, we must display it correctly.
|
||||
write!(f, "..")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A (possibly open) boundary of a range pattern.
|
||||
/// If present, the const must be of a numeric type.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
||||
pub enum PatRangeBoundary<'tcx> {
|
||||
Finite(mir::Const<'tcx>),
|
||||
NegInfinity,
|
||||
PosInfinity,
|
||||
}
|
||||
|
||||
impl<'tcx> PatRangeBoundary<'tcx> {
|
||||
#[inline]
|
||||
pub fn is_finite(self) -> bool {
|
||||
matches!(self, Self::Finite(..))
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
|
||||
match self {
|
||||
Self::Finite(value) => Some(value),
|
||||
Self::NegInfinity | Self::PosInfinity => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
|
||||
match self {
|
||||
Self::Finite(value) => value,
|
||||
Self::NegInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_min_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
Self::PosInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_max_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
|
||||
match self {
|
||||
Self::Finite(value) => value.eval_bits(tcx, param_env),
|
||||
Self::NegInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
|
||||
}
|
||||
Self::PosInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
ty.numeric_min_and_max_as_bits(tcx).unwrap().1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, param_env), level = "debug", ret)]
|
||||
pub fn compare_with(
|
||||
self,
|
||||
other: Self,
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Option<Ordering> {
|
||||
use PatRangeBoundary::*;
|
||||
match (self, other) {
|
||||
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
|
||||
// describe the same range. These two shortcuts are ok, but for the rest we must check
|
||||
// bit values.
|
||||
(PosInfinity, PosInfinity) => return Some(Ordering::Equal),
|
||||
(NegInfinity, NegInfinity) => return Some(Ordering::Equal),
|
||||
|
||||
// This code is hot when compiling matches with many ranges. So we
|
||||
// special-case extraction of evaluated scalars for speed, for types where
|
||||
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
|
||||
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
|
||||
// in this way.
|
||||
(Finite(mir::Const::Ty(a)), Finite(mir::Const::Ty(b)))
|
||||
if matches!(ty.kind(), ty::Uint(_) | ty::Char) =>
|
||||
{
|
||||
return Some(a.kind().cmp(&b.kind()));
|
||||
}
|
||||
(
|
||||
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _)),
|
||||
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _)),
|
||||
) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => return Some(a.cmp(&b)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let a = self.eval_bits(ty, tcx, param_env);
|
||||
let b = other.eval_bits(ty, tcx, param_env);
|
||||
|
||||
match ty.kind() {
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
use rustc_apfloat::Float;
|
||||
let a = rustc_apfloat::ieee::Single::from_bits(a);
|
||||
let b = rustc_apfloat::ieee::Single::from_bits(b);
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
use rustc_apfloat::Float;
|
||||
let a = rustc_apfloat::ieee::Double::from_bits(a);
|
||||
let b = rustc_apfloat::ieee::Double::from_bits(b);
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
ty::Int(ity) => {
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
let size = rustc_target::abi::Integer::from_int_ty(&tcx, *ity).size();
|
||||
let a = size.sign_extend(a) as i128;
|
||||
let b = size.sign_extend(b) as i128;
|
||||
Some(a.cmp(&b))
|
||||
}
|
||||
ty::Uint(_) | ty::Char => Some(a.cmp(&b)),
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for Pat<'tcx> {
|
||||
@ -944,11 +1177,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
|
||||
PatKind::InlineConstant { def: _, ref subpattern } => {
|
||||
write!(f, "{} (from inline const)", subpattern)
|
||||
}
|
||||
PatKind::Range(box PatRange { lo, hi, end }) => {
|
||||
write!(f, "{lo}")?;
|
||||
write!(f, "{end}")?;
|
||||
write!(f, "{hi}")
|
||||
}
|
||||
PatKind::Range(ref range) => write!(f, "{range}"),
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix }
|
||||
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
write!(f, "[")?;
|
||||
|
@ -19,7 +19,7 @@ use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::{Integer, IntegerType, Size};
|
||||
use rustc_target::abi::{Integer, IntegerType, Primitive, Size};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use smallvec::SmallVec;
|
||||
use std::{fmt, iter};
|
||||
@ -919,54 +919,62 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> Ty<'tcx> {
|
||||
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
|
||||
pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
|
||||
match *self.kind() {
|
||||
ty::Bool => Size::from_bytes(1),
|
||||
ty::Char => Size::from_bytes(4),
|
||||
ty::Int(ity) => Integer::from_int_ty(&tcx, ity).size(),
|
||||
ty::Uint(uty) => Integer::from_uint_ty(&tcx, uty).size(),
|
||||
ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx),
|
||||
ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx),
|
||||
_ => bug!("non primitive type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int_size_and_signed(self, tcx: TyCtxt<'tcx>) -> (Size, bool) {
|
||||
let (int, signed) = match *self.kind() {
|
||||
ty::Int(ity) => (Integer::from_int_ty(&tcx, ity), true),
|
||||
ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false),
|
||||
match *self.kind() {
|
||||
ty::Int(ity) => (Integer::from_int_ty(&tcx, ity).size(), true),
|
||||
ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty).size(), false),
|
||||
_ => bug!("non integer discriminant"),
|
||||
};
|
||||
(int.size(), signed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum and maximum values for the given numeric type (including `char`s) or
|
||||
/// returns `None` if the type is not numeric.
|
||||
pub fn numeric_min_and_max_as_bits(self, tcx: TyCtxt<'tcx>) -> Option<(u128, u128)> {
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
Some(match self.kind() {
|
||||
ty::Int(_) | ty::Uint(_) => {
|
||||
let (size, signed) = self.int_size_and_signed(tcx);
|
||||
let min = if signed { size.truncate(size.signed_int_min() as u128) } else { 0 };
|
||||
let max =
|
||||
if signed { size.signed_int_max() as u128 } else { size.unsigned_int_max() };
|
||||
(min, max)
|
||||
}
|
||||
ty::Char => (0, std::char::MAX as u128),
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
((-Single::INFINITY).to_bits(), Single::INFINITY.to_bits())
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
((-Double::INFINITY).to_bits(), Double::INFINITY.to_bits())
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the maximum value for the given numeric type (including `char`s)
|
||||
/// or returns `None` if the type is not numeric.
|
||||
pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
|
||||
let val = match self.kind() {
|
||||
ty::Int(_) | ty::Uint(_) => {
|
||||
let (size, signed) = self.int_size_and_signed(tcx);
|
||||
let val =
|
||||
if signed { size.signed_int_max() as u128 } else { size.unsigned_int_max() };
|
||||
Some(val)
|
||||
}
|
||||
ty::Char => Some(std::char::MAX as u128),
|
||||
ty::Float(fty) => Some(match fty {
|
||||
ty::FloatTy::F32 => rustc_apfloat::ieee::Single::INFINITY.to_bits(),
|
||||
ty::FloatTy::F64 => rustc_apfloat::ieee::Double::INFINITY.to_bits(),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
|
||||
self.numeric_min_and_max_as_bits(tcx)
|
||||
.map(|(_, max)| ty::Const::from_bits(tcx, max, ty::ParamEnv::empty().and(self)))
|
||||
}
|
||||
|
||||
/// Returns the minimum value for the given numeric type (including `char`s)
|
||||
/// or returns `None` if the type is not numeric.
|
||||
pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
|
||||
let val = match self.kind() {
|
||||
ty::Int(_) | ty::Uint(_) => {
|
||||
let (size, signed) = self.int_size_and_signed(tcx);
|
||||
let val = if signed { size.truncate(size.signed_int_min() as u128) } else { 0 };
|
||||
Some(val)
|
||||
}
|
||||
ty::Char => Some(0),
|
||||
ty::Float(fty) => Some(match fty {
|
||||
ty::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(),
|
||||
ty::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
|
||||
self.numeric_min_and_max_as_bits(tcx)
|
||||
.map(|(min, _)| ty::Const::from_bits(tcx, min, ty::ParamEnv::empty().and(self)))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` are *moved* or *copied*
|
||||
|
@ -1035,7 +1035,7 @@ enum TestKind<'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
},
|
||||
|
||||
/// Test whether the value falls within an inclusive or exclusive range
|
||||
/// Test whether the value falls within an inclusive or exclusive range.
|
||||
Range(Box<PatRange<'tcx>>),
|
||||
|
||||
/// Test that the length of the slice is equal to `len`.
|
||||
|
@ -15,11 +15,7 @@
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
|
||||
use crate::build::Builder;
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_target::abi::{Integer, Size};
|
||||
|
||||
use std::mem;
|
||||
|
||||
@ -148,7 +144,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
match_pair: MatchPair<'pat, 'tcx>,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) -> Result<(), MatchPair<'pat, 'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
match match_pair.pattern.kind {
|
||||
PatKind::AscribeUserType {
|
||||
ref subpattern,
|
||||
@ -210,41 +205,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
PatKind::Range(box PatRange { lo, hi, end }) => {
|
||||
let (range, bias) = match *lo.ty().kind() {
|
||||
ty::Char => {
|
||||
(Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
|
||||
}
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_int_ty(&tcx, ity).size();
|
||||
let max = size.truncate(u128::MAX);
|
||||
let bias = 1u128 << (size.bits() - 1);
|
||||
(Some((0, max, size)), bias)
|
||||
}
|
||||
ty::Uint(uty) => {
|
||||
let size = Integer::from_uint_ty(&tcx, uty).size();
|
||||
let max = size.truncate(u128::MAX);
|
||||
(Some((0, max, size)), 0)
|
||||
}
|
||||
_ => (None, 0),
|
||||
};
|
||||
if let Some((min, max, sz)) = range {
|
||||
// We want to compare ranges numerically, but the order of the bitwise
|
||||
// representation of signed integers does not match their numeric order. Thus,
|
||||
// to correct the ordering, we need to shift the range of signed integers to
|
||||
// correct the comparison. This is achieved by XORing with a bias (see
|
||||
// pattern/_match.rs for another pertinent example of this pattern).
|
||||
//
|
||||
// Also, for performance, it's important to only do the second
|
||||
// `try_to_bits` if necessary.
|
||||
let lo = lo.try_to_bits(sz).unwrap() ^ bias;
|
||||
if lo <= min {
|
||||
let hi = hi.try_to_bits(sz).unwrap() ^ bias;
|
||||
if hi > max || hi == max && end == RangeEnd::Included {
|
||||
// Irrefutable pattern match.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
PatKind::Range(ref range) => {
|
||||
if let Some(true) = range.is_full_range(self.tcx) {
|
||||
// Irrefutable pattern match.
|
||||
return Ok(());
|
||||
}
|
||||
Err(match_pair)
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||
use crate::build::Builder;
|
||||
use crate::thir::pattern::compare_const_vals;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::{LangItem, RangeEnd};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
@ -59,8 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
},
|
||||
|
||||
PatKind::Range(ref range) => {
|
||||
assert_eq!(range.lo.ty(), match_pair.pattern.ty);
|
||||
assert_eq!(range.hi.ty(), match_pair.pattern.ty);
|
||||
assert_eq!(range.ty, match_pair.pattern.ty);
|
||||
Test { span: match_pair.pattern.span, kind: TestKind::Range(range.clone()) }
|
||||
}
|
||||
|
||||
@ -309,11 +307,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
TestKind::Range(box PatRange { lo, hi, ref end }) => {
|
||||
TestKind::Range(ref range) => {
|
||||
let lower_bound_success = self.cfg.start_new_block();
|
||||
let target_blocks = make_target_blocks(self);
|
||||
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
// FIXME: skip useless comparison when the range is half-open.
|
||||
let lo = range.lo.to_const(range.ty, self.tcx);
|
||||
let hi = range.hi.to_const(range.ty, self.tcx);
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let val = Operand::Copy(place);
|
||||
@ -330,7 +331,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
lo,
|
||||
val.clone(),
|
||||
);
|
||||
let op = match *end {
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
};
|
||||
@ -698,34 +699,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
(TestKind::Range(test), PatKind::Range(pat)) => {
|
||||
use std::cmp::Ordering::*;
|
||||
|
||||
if test == pat {
|
||||
self.candidate_without_match_pair(match_pair_index, candidate);
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
// For performance, it's important to only do the second
|
||||
// `compare_const_vals` if necessary.
|
||||
let no_overlap = if matches!(
|
||||
(compare_const_vals(self.tcx, test.hi, pat.lo, self.param_env)?, test.end),
|
||||
(Less, _) | (Equal, RangeEnd::Excluded) // test < pat
|
||||
) || matches!(
|
||||
(compare_const_vals(self.tcx, test.lo, pat.hi, self.param_env)?, pat.end),
|
||||
(Greater, _) | (Equal, RangeEnd::Excluded) // test > pat
|
||||
) {
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If the testing range does not overlap with pattern range,
|
||||
// the pattern can be matched only if this test fails.
|
||||
no_overlap
|
||||
if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
|
||||
}
|
||||
|
||||
(TestKind::Range(range), &PatKind::Constant { value }) => {
|
||||
if let Some(false) = self.const_range_contains(&*range, value) {
|
||||
if !range.contains(value, self.tcx, self.param_env)? {
|
||||
// `value` is not contained in the testing range,
|
||||
// so `value` can be matched only if this test fails.
|
||||
Some(1)
|
||||
@ -817,27 +802,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
span_bug!(match_pair.pattern.span, "simplifiable pattern found: {:?}", match_pair.pattern)
|
||||
}
|
||||
|
||||
fn const_range_contains(&self, range: &PatRange<'tcx>, value: Const<'tcx>) -> Option<bool> {
|
||||
use std::cmp::Ordering::*;
|
||||
|
||||
// For performance, it's important to only do the second
|
||||
// `compare_const_vals` if necessary.
|
||||
Some(
|
||||
matches!(compare_const_vals(self.tcx, range.lo, value, self.param_env)?, Less | Equal)
|
||||
&& matches!(
|
||||
(compare_const_vals(self.tcx, value, range.hi, self.param_env)?, range.end),
|
||||
(Less, _) | (Equal, RangeEnd::Included)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn values_not_contained_in_range(
|
||||
&self,
|
||||
range: &PatRange<'tcx>,
|
||||
options: &FxIndexMap<Const<'tcx>, u128>,
|
||||
) -> Option<bool> {
|
||||
for &val in options.keys() {
|
||||
if self.const_range_contains(range, val)? {
|
||||
if range.contains(val, self.tcx, self.param_env)? {
|
||||
return Some(false);
|
||||
}
|
||||
}
|
||||
|
@ -807,13 +807,19 @@ impl<'tcx> Uncovered<'tcx> {
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
witnesses: Vec<WitnessPat<'tcx>>,
|
||||
) -> Self {
|
||||
let witness_1 = witnesses.get(0).unwrap().to_pat(cx);
|
||||
let witness_1 = witnesses.get(0).unwrap().to_diagnostic_pat(cx);
|
||||
Self {
|
||||
span,
|
||||
count: witnesses.len(),
|
||||
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
|
||||
witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
|
||||
witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
|
||||
witness_2: witnesses
|
||||
.get(1)
|
||||
.map(|w| w.to_diagnostic_pat(cx))
|
||||
.unwrap_or_else(|| witness_1.clone()),
|
||||
witness_3: witnesses
|
||||
.get(2)
|
||||
.map(|w| w.to_diagnostic_pat(cx))
|
||||
.unwrap_or_else(|| witness_1.clone()),
|
||||
witness_1,
|
||||
remainder: witnesses.len().saturating_sub(3),
|
||||
}
|
||||
|
@ -703,14 +703,21 @@ fn report_arm_reachability<'p, 'tcx>(
|
||||
}
|
||||
|
||||
fn collect_non_exhaustive_tys<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pat: &WitnessPat<'tcx>,
|
||||
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
|
||||
) {
|
||||
if matches!(pat.ctor(), Constructor::NonExhaustive) {
|
||||
non_exhaustive_tys.insert(pat.ty());
|
||||
}
|
||||
if let Constructor::IntRange(range) = pat.ctor() {
|
||||
if range.is_beyond_boundaries(pat.ty(), tcx) {
|
||||
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
|
||||
non_exhaustive_tys.insert(pat.ty());
|
||||
}
|
||||
}
|
||||
pat.iter_fields()
|
||||
.for_each(|field_pat| collect_non_exhaustive_tys(field_pat, non_exhaustive_tys))
|
||||
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
|
||||
}
|
||||
|
||||
/// Report that a match is not exhaustive.
|
||||
@ -753,7 +760,7 @@ fn non_exhaustive_match<'p, 'tcx>(
|
||||
pattern = if witnesses.len() < 4 {
|
||||
witnesses
|
||||
.iter()
|
||||
.map(|witness| witness.to_pat(cx).to_string())
|
||||
.map(|witness| witness.to_diagnostic_pat(cx).to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" | ")
|
||||
} else {
|
||||
@ -764,16 +771,24 @@ fn non_exhaustive_match<'p, 'tcx>(
|
||||
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
|
||||
err.note(format!("the matched value is of type `{}`", scrut_ty));
|
||||
|
||||
if !is_empty_match && witnesses.len() == 1 {
|
||||
if !is_empty_match {
|
||||
let mut non_exhaustive_tys = FxHashSet::default();
|
||||
collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys);
|
||||
// Look at the first witness.
|
||||
collect_non_exhaustive_tys(cx.tcx, &witnesses[0], &mut non_exhaustive_tys);
|
||||
|
||||
for ty in non_exhaustive_tys {
|
||||
if ty.is_ptr_sized_integral() {
|
||||
err.note(format!(
|
||||
"`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
|
||||
exhaustively",
|
||||
if ty == cx.tcx.types.usize {
|
||||
err.note(format!(
|
||||
"`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
|
||||
exhaustively",
|
||||
));
|
||||
} else if ty == cx.tcx.types.isize {
|
||||
err.note(format!(
|
||||
"`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
|
||||
exhaustively",
|
||||
));
|
||||
}
|
||||
if cx.tcx.sess.is_nightly_build() {
|
||||
err.help(format!(
|
||||
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
|
||||
@ -900,13 +915,13 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
|
||||
witnesses: &[WitnessPat<'tcx>],
|
||||
) -> String {
|
||||
const LIMIT: usize = 3;
|
||||
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_pat(cx).to_string();
|
||||
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_diagnostic_pat(cx).to_string();
|
||||
match witnesses {
|
||||
[] => bug!(),
|
||||
[witness] => format!("`{}`", witness.to_pat(cx)),
|
||||
[witness] => format!("`{}`", witness.to_diagnostic_pat(cx)),
|
||||
[head @ .., tail] if head.len() < LIMIT => {
|
||||
let head: Vec<_> = head.iter().map(pat_to_str).collect();
|
||||
format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
|
||||
format!("`{}` and `{}`", head.join("`, `"), tail.to_diagnostic_pat(cx))
|
||||
}
|
||||
_ => {
|
||||
let (head, tail) = witnesses.split_at(LIMIT);
|
||||
|
@ -46,7 +46,6 @@ use std::cell::Cell;
|
||||
use std::cmp::{self, max, min, Ordering};
|
||||
use std::fmt;
|
||||
use std::iter::once;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
@ -57,13 +56,15 @@ use rustc_hir::RangeEnd;
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
|
||||
use self::Constructor::*;
|
||||
use self::MaybeInfiniteInt::*;
|
||||
use self::SliceKind::*;
|
||||
|
||||
use super::usefulness::{MatchCheckCtxt, PatCtxt};
|
||||
@ -92,65 +93,21 @@ enum Presence {
|
||||
Seen,
|
||||
}
|
||||
|
||||
/// An inclusive interval, used for precise integer exhaustiveness checking.
|
||||
/// `IntRange`s always store a contiguous range. This means that values are
|
||||
/// encoded such that `0` encodes the minimum value for the integer,
|
||||
/// regardless of the signedness.
|
||||
/// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
|
||||
/// This makes comparisons and arithmetic on interval endpoints much more
|
||||
/// straightforward. See `signed_bias` for details.
|
||||
///
|
||||
/// `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, PartialEq, Eq)]
|
||||
pub(crate) struct IntRange {
|
||||
range: RangeInclusive<u128>,
|
||||
/// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
|
||||
/// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
|
||||
/// `255`. See `signed_bias` for details.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum MaybeInfiniteInt {
|
||||
NegInfinity,
|
||||
/// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
|
||||
Finite(u128),
|
||||
/// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range.
|
||||
JustAfterMax,
|
||||
PosInfinity,
|
||||
}
|
||||
|
||||
impl IntRange {
|
||||
#[inline]
|
||||
pub(super) fn is_integral(ty: Ty<'_>) -> bool {
|
||||
matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool)
|
||||
}
|
||||
|
||||
pub(super) fn is_singleton(&self) -> bool {
|
||||
self.range.start() == self.range.end()
|
||||
}
|
||||
|
||||
pub(super) fn boundaries(&self) -> (u128, u128) {
|
||||
(*self.range.start(), *self.range.end())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange {
|
||||
let bias = IntRange::signed_bias(tcx, ty);
|
||||
// Perform a shift if the underlying types are signed,
|
||||
// which makes the interval arithmetic simpler.
|
||||
let val = bits ^ bias;
|
||||
IntRange { range: val..=val }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_range<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
lo: u128,
|
||||
hi: u128,
|
||||
ty: Ty<'tcx>,
|
||||
end: RangeEnd,
|
||||
) -> IntRange {
|
||||
// Perform a shift if the underlying types are signed,
|
||||
// which makes the interval arithmetic simpler.
|
||||
let bias = IntRange::signed_bias(tcx, ty);
|
||||
let (lo, hi) = (lo ^ bias, hi ^ bias);
|
||||
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));
|
||||
}
|
||||
IntRange { range: lo..=(hi - offset) }
|
||||
}
|
||||
|
||||
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
|
||||
impl MaybeInfiniteInt {
|
||||
// The return value of `signed_bias` should be XORed with a value to encode/decode it.
|
||||
fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 {
|
||||
match *ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
@ -161,15 +118,132 @@ impl IntRange {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_finite(tcx: TyCtxt<'_>, ty: Ty<'_>, bits: u128) -> Self {
|
||||
let bias = Self::signed_bias(tcx, ty);
|
||||
// Perform a shift if the underlying types are signed, which makes the interval arithmetic
|
||||
// type-independent.
|
||||
let x = bits ^ bias;
|
||||
Finite(x)
|
||||
}
|
||||
fn from_pat_range_bdy<'tcx>(
|
||||
bdy: PatRangeBoundary<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Self {
|
||||
match bdy {
|
||||
PatRangeBoundary::NegInfinity => NegInfinity,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let bits = value.eval_bits(tcx, param_env);
|
||||
Self::new_finite(tcx, ty, bits)
|
||||
}
|
||||
PatRangeBoundary::PosInfinity => PosInfinity,
|
||||
}
|
||||
}
|
||||
|
||||
/// Used only for diagnostics.
|
||||
/// Note: it is possible to get `isize/usize::MAX+1` here, as explained in the doc for
|
||||
/// [`IntRange::split`]. This cannot be represented as a `Const`, so we represent it with
|
||||
/// `PosInfinity`.
|
||||
fn to_diagnostic_pat_range_bdy<'tcx>(
|
||||
self,
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> PatRangeBoundary<'tcx> {
|
||||
match self {
|
||||
NegInfinity => PatRangeBoundary::NegInfinity,
|
||||
Finite(x) => {
|
||||
let bias = Self::signed_bias(tcx, ty);
|
||||
let bits = x ^ bias;
|
||||
let size = ty.primitive_size(tcx);
|
||||
match Scalar::try_from_uint(bits, size) {
|
||||
Some(scalar) => {
|
||||
let value = mir::Const::from_scalar(tcx, scalar, ty);
|
||||
PatRangeBoundary::Finite(value)
|
||||
}
|
||||
// The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value
|
||||
// for a type, the problem isn't that the value is too small. So it must be too
|
||||
// large.
|
||||
None => PatRangeBoundary::PosInfinity,
|
||||
}
|
||||
}
|
||||
JustAfterMax | PosInfinity => PatRangeBoundary::PosInfinity,
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: this will not turn a finite value into an infinite one or vice-versa.
|
||||
pub(crate) fn minus_one(self) -> Self {
|
||||
match self {
|
||||
Finite(n) => match n.checked_sub(1) {
|
||||
Some(m) => Finite(m),
|
||||
None => bug!(),
|
||||
},
|
||||
JustAfterMax => Finite(u128::MAX),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
/// Note: this will not turn a finite value into an infinite one or vice-versa.
|
||||
pub(crate) fn plus_one(self) -> Self {
|
||||
match self {
|
||||
Finite(n) => match n.checked_add(1) {
|
||||
Some(m) => Finite(m),
|
||||
None => JustAfterMax,
|
||||
},
|
||||
JustAfterMax => bug!(),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An exclusive interval, used for precise integer exhaustiveness checking. `IntRange`s always
|
||||
/// store a contiguous range.
|
||||
///
|
||||
/// `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, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct IntRange {
|
||||
pub(crate) lo: MaybeInfiniteInt, // Must not be `PosInfinity`.
|
||||
pub(crate) hi: MaybeInfiniteInt, // Must not be `NegInfinity`.
|
||||
}
|
||||
|
||||
impl IntRange {
|
||||
#[inline]
|
||||
pub(super) fn is_integral(ty: Ty<'_>) -> bool {
|
||||
matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_))
|
||||
}
|
||||
|
||||
/// Best effort; will not know that e.g. `255u8..` is a singleton.
|
||||
pub(super) fn is_singleton(&self) -> bool {
|
||||
// Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite
|
||||
// to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`.
|
||||
self.lo.plus_one() == self.hi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange {
|
||||
let x = MaybeInfiniteInt::new_finite(tcx, ty, bits);
|
||||
IntRange { lo: x, hi: x.plus_one() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange {
|
||||
if end == RangeEnd::Included {
|
||||
hi = hi.plus_one();
|
||||
}
|
||||
if lo >= hi {
|
||||
// This should have been caught earlier by E0030.
|
||||
bug!("malformed range pattern: {lo:?}..{hi:?}");
|
||||
}
|
||||
IntRange { lo, hi }
|
||||
}
|
||||
|
||||
fn is_subrange(&self, other: &Self) -> bool {
|
||||
other.range.start() <= self.range.start() && self.range.end() <= other.range.end()
|
||||
other.lo <= self.lo && self.hi <= other.hi
|
||||
}
|
||||
|
||||
fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let (lo, hi) = self.boundaries();
|
||||
let (other_lo, other_hi) = other.boundaries();
|
||||
if lo <= other_hi && other_lo <= hi {
|
||||
Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
|
||||
if self.lo < other.hi && other.lo < self.hi {
|
||||
Some(IntRange { lo: max(self.lo, other.lo), hi: min(self.hi, other.hi) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -202,52 +276,45 @@ impl IntRange {
|
||||
/// ```
|
||||
/// where each sequence of dashes is an output range, and dashes outside parentheses are marked
|
||||
/// as `Presence::Missing`.
|
||||
///
|
||||
/// ## `isize`/`usize`
|
||||
///
|
||||
/// Whereas a wildcard of type `i32` stands for the range `i32::MIN..=i32::MAX`, a `usize`
|
||||
/// wildcard stands for `0..PosInfinity` and a `isize` wildcard stands for
|
||||
/// `NegInfinity..PosInfinity`. In other words, as far as `IntRange` is concerned, there are
|
||||
/// values before `isize::MIN` and after `usize::MAX`/`isize::MAX`.
|
||||
/// This is to avoid e.g. `0..(u32::MAX as usize)` from being exhaustive on one architecture and
|
||||
/// not others. See discussions around the `precise_pointer_size_matching` feature for more
|
||||
/// details.
|
||||
///
|
||||
/// These infinities affect splitting subtly: it is possible to get `NegInfinity..0` and
|
||||
/// `usize::MAX+1..PosInfinity` in the output. Diagnostics must be careful to handle these
|
||||
/// fictitious ranges sensibly.
|
||||
fn split(
|
||||
&self,
|
||||
column_ranges: impl Iterator<Item = IntRange>,
|
||||
) -> impl Iterator<Item = (Presence, IntRange)> {
|
||||
/// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
|
||||
/// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum IntBoundary {
|
||||
JustBefore(u128),
|
||||
AfterMax,
|
||||
}
|
||||
|
||||
fn unpack_intrange(range: IntRange) -> [IntBoundary; 2] {
|
||||
use IntBoundary::*;
|
||||
let (lo, hi) = range.boundaries();
|
||||
let lo = JustBefore(lo);
|
||||
let hi = match hi.checked_add(1) {
|
||||
Some(m) => JustBefore(m),
|
||||
None => AfterMax,
|
||||
};
|
||||
[lo, hi]
|
||||
}
|
||||
|
||||
// The boundaries of ranges in `column_ranges` intersected with `self`.
|
||||
// We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
|
||||
// a range and -1 if it ends it. When the count is > 0 between two boundaries, we
|
||||
// are within an input range.
|
||||
let mut boundaries: Vec<(IntBoundary, isize)> = column_ranges
|
||||
let mut boundaries: Vec<(MaybeInfiniteInt, isize)> = column_ranges
|
||||
.filter_map(|r| self.intersection(&r))
|
||||
.map(unpack_intrange)
|
||||
.flat_map(|[lo, hi]| [(lo, 1), (hi, -1)])
|
||||
.flat_map(|r| [(r.lo, 1), (r.hi, -1)])
|
||||
.collect();
|
||||
// We sort by boundary, and for each boundary we sort the "closing parentheses" first. The
|
||||
// order of +1/-1 for a same boundary value is actually irrelevant, because we only look at
|
||||
// the accumulated count between distinct boundary values.
|
||||
boundaries.sort_unstable();
|
||||
|
||||
let [self_start, self_end] = unpack_intrange(self.clone());
|
||||
// Accumulate parenthesis counts.
|
||||
let mut paren_counter = 0isize;
|
||||
// Gather pairs of adjacent boundaries.
|
||||
let mut prev_bdy = self_start;
|
||||
let mut prev_bdy = self.lo;
|
||||
boundaries
|
||||
.into_iter()
|
||||
// End with the end of the range. The count is ignored.
|
||||
.chain(once((self_end, 0)))
|
||||
.chain(once((self.hi, 0)))
|
||||
// List pairs of adjacent boundaries and the count between them.
|
||||
.map(move |(bdy, delta)| {
|
||||
// `delta` affects the count as we cross `bdy`, so the relevant count between
|
||||
@ -261,51 +328,75 @@ impl IntRange {
|
||||
.filter(|&(prev_bdy, _, bdy)| prev_bdy != bdy)
|
||||
// Convert back to ranges.
|
||||
.map(move |(prev_bdy, paren_count, bdy)| {
|
||||
use IntBoundary::*;
|
||||
use Presence::*;
|
||||
let presence = if paren_count > 0 { Seen } else { Unseen };
|
||||
let range = match (prev_bdy, bdy) {
|
||||
(JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
|
||||
(JustBefore(n), AfterMax) => n..=u128::MAX,
|
||||
_ => unreachable!(), // Ruled out by the sorting and filtering we did
|
||||
};
|
||||
(presence, IntRange { range })
|
||||
let range = IntRange { lo: prev_bdy, hi: bdy };
|
||||
(presence, range)
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the range denotes the fictitious values before `isize::MIN` or after
|
||||
/// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist).
|
||||
pub(crate) fn is_beyond_boundaries<'tcx>(&self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
|
||||
ty.is_ptr_sized_integral() && !tcx.features().precise_pointer_size_matching && {
|
||||
// The two invalid ranges are `NegInfinity..isize::MIN` (represented as
|
||||
// `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `to_diagnostic_pat_range_bdy`
|
||||
// converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `self.lo`
|
||||
// otherwise.
|
||||
let lo = self.lo.to_diagnostic_pat_range_bdy(ty, tcx);
|
||||
matches!(lo, PatRangeBoundary::PosInfinity)
|
||||
|| matches!(self.hi, MaybeInfiniteInt::Finite(0))
|
||||
}
|
||||
}
|
||||
/// Only used for displaying the range.
|
||||
pub(super) fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
|
||||
let (lo, hi) = self.boundaries();
|
||||
|
||||
let bias = IntRange::signed_bias(tcx, ty);
|
||||
let (lo, hi) = (lo ^ bias, hi ^ bias);
|
||||
|
||||
let env = ty::ParamEnv::empty().and(ty);
|
||||
let lo_const = mir::Const::from_bits(tcx, lo, env);
|
||||
let hi_const = mir::Const::from_bits(tcx, hi, env);
|
||||
|
||||
let kind = if lo == hi {
|
||||
PatKind::Constant { value: lo_const }
|
||||
pub(super) fn to_diagnostic_pat<'tcx>(&self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Pat<'tcx> {
|
||||
let kind = if matches!((self.lo, self.hi), (NegInfinity, PosInfinity)) {
|
||||
PatKind::Wild
|
||||
} else if self.is_singleton() {
|
||||
let lo = self.lo.to_diagnostic_pat_range_bdy(ty, tcx);
|
||||
let value = lo.as_finite().unwrap();
|
||||
PatKind::Constant { value }
|
||||
} else {
|
||||
PatKind::Range(Box::new(PatRange {
|
||||
lo: lo_const,
|
||||
hi: hi_const,
|
||||
end: RangeEnd::Included,
|
||||
}))
|
||||
// We convert to an inclusive range for diagnostics.
|
||||
let mut end = RangeEnd::Included;
|
||||
let mut lo = self.lo.to_diagnostic_pat_range_bdy(ty, tcx);
|
||||
if matches!(lo, PatRangeBoundary::PosInfinity) {
|
||||
// The only reason to get `PosInfinity` here is the special case where
|
||||
// `to_diagnostic_pat_range_bdy` found `{u,i}size::MAX+1`. So the range denotes the
|
||||
// fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do
|
||||
// this). We show this to the user as `usize::MAX..` which is slightly incorrect but
|
||||
// probably clear enough.
|
||||
let c = ty.numeric_max_val(tcx).unwrap();
|
||||
let value = mir::Const::from_ty_const(c, tcx);
|
||||
lo = PatRangeBoundary::Finite(value);
|
||||
}
|
||||
let hi = if matches!(self.hi, MaybeInfiniteInt::Finite(0)) {
|
||||
// The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
|
||||
end = RangeEnd::Excluded;
|
||||
self.hi
|
||||
} else {
|
||||
self.hi.minus_one()
|
||||
};
|
||||
let hi = hi.to_diagnostic_pat_range_bdy(ty, tcx);
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
|
||||
};
|
||||
|
||||
Pat { ty, span: DUMMY_SP, kind }
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
|
||||
/// would be displayed as such. To render properly, convert to a pattern first.
|
||||
/// Note: this will render signed ranges incorrectly. To render properly, convert to a pattern
|
||||
/// first.
|
||||
impl fmt::Debug for IntRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (lo, hi) = self.boundaries();
|
||||
write!(f, "{lo}")?;
|
||||
write!(f, "{}", RangeEnd::Included)?;
|
||||
write!(f, "{hi}")
|
||||
if let Finite(lo) = self.lo {
|
||||
write!(f, "{lo}")?;
|
||||
}
|
||||
write!(f, "{}", RangeEnd::Excluded)?;
|
||||
if let Finite(hi) = self.hi {
|
||||
write!(f, "{hi}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,6 +631,8 @@ pub(super) enum Constructor<'tcx> {
|
||||
Single,
|
||||
/// Enum variants.
|
||||
Variant(VariantIdx),
|
||||
/// Booleans
|
||||
Bool(bool),
|
||||
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
|
||||
IntRange(IntRange),
|
||||
/// Ranges of floating-point literal values (`2.0..=5.2`).
|
||||
@ -580,6 +673,12 @@ impl<'tcx> Constructor<'tcx> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn as_bool(&self) -> Option<bool> {
|
||||
match self {
|
||||
Bool(b) => Some(*b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub(super) fn as_int_range(&self) -> Option<&IntRange> {
|
||||
match self {
|
||||
IntRange(range) => Some(range),
|
||||
@ -624,10 +723,11 @@ impl<'tcx> Constructor<'tcx> {
|
||||
_ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty),
|
||||
},
|
||||
Slice(slice) => slice.arity(),
|
||||
Str(..)
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| IntRange(..)
|
||||
| Str(..)
|
||||
| Opaque
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
@ -743,6 +843,7 @@ impl<'tcx> Constructor<'tcx> {
|
||||
|
||||
(Single, Single) => true,
|
||||
(Variant(self_id), Variant(other_id)) => self_id == other_id,
|
||||
(Bool(self_b), Bool(other_b)) => self_b == other_b,
|
||||
|
||||
(IntRange(self_range), IntRange(other_range)) => self_range.is_subrange(other_range),
|
||||
(F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => {
|
||||
@ -795,12 +896,11 @@ pub(super) enum ConstructorSet {
|
||||
hidden_variants: Vec<VariantIdx>,
|
||||
non_exhaustive: bool,
|
||||
},
|
||||
/// Booleans.
|
||||
Bool,
|
||||
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
|
||||
/// The second range is only useful for `char`.
|
||||
/// This is reused for bool. FIXME: don't.
|
||||
/// `non_exhaustive` is used when the range is not allowed to be matched exhaustively (that's
|
||||
/// for usize/isize).
|
||||
Integers { range_1: IntRange, range_2: Option<IntRange>, non_exhaustive: bool },
|
||||
Integers { range_1: IntRange, range_2: Option<IntRange> },
|
||||
/// The type is matched by slices. The usize is the compile-time length of the array, if known.
|
||||
Slice(Option<usize>),
|
||||
/// The type is matched by slices whose elements are uninhabited.
|
||||
@ -836,8 +936,13 @@ pub(super) struct SplitConstructorSet<'tcx> {
|
||||
impl ConstructorSet {
|
||||
#[instrument(level = "debug", skip(cx), ret)]
|
||||
pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
|
||||
let make_range =
|
||||
|start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
|
||||
let make_range = |start, end| {
|
||||
IntRange::from_range(
|
||||
MaybeInfiniteInt::new_finite(cx.tcx, ty, start),
|
||||
MaybeInfiniteInt::new_finite(cx.tcx, ty, end),
|
||||
RangeEnd::Included,
|
||||
)
|
||||
};
|
||||
// This determines the set of all possible constructors for the type `ty`. For numbers,
|
||||
// arrays and slices we use ranges and variable-length slices when appropriate.
|
||||
//
|
||||
@ -847,35 +952,43 @@ impl ConstructorSet {
|
||||
// Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
|
||||
// `cx.is_uninhabited()`).
|
||||
match ty.kind() {
|
||||
ty::Bool => {
|
||||
Self::Integers { range_1: make_range(0, 1), range_2: None, non_exhaustive: false }
|
||||
}
|
||||
ty::Bool => Self::Bool,
|
||||
ty::Char => {
|
||||
// The valid Unicode Scalar Value ranges.
|
||||
Self::Integers {
|
||||
range_1: make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
|
||||
range_2: Some(make_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
|
||||
non_exhaustive: false,
|
||||
}
|
||||
}
|
||||
&ty::Int(ity) => {
|
||||
// `usize`/`isize` are not allowed to be matched exhaustively unless the
|
||||
// `precise_pointer_size_matching` feature is enabled.
|
||||
let non_exhaustive =
|
||||
ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching;
|
||||
let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128;
|
||||
let min = 1u128 << (bits - 1);
|
||||
let max = min - 1;
|
||||
Self::Integers { range_1: make_range(min, max), non_exhaustive, range_2: None }
|
||||
let range = if ty.is_ptr_sized_integral()
|
||||
&& !cx.tcx.features().precise_pointer_size_matching
|
||||
{
|
||||
// The min/max values of `isize` are not allowed to be observed unless the
|
||||
// `precise_pointer_size_matching` feature is enabled.
|
||||
IntRange { lo: NegInfinity, hi: PosInfinity }
|
||||
} else {
|
||||
let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128;
|
||||
let min = 1u128 << (bits - 1);
|
||||
let max = min - 1;
|
||||
make_range(min, max)
|
||||
};
|
||||
Self::Integers { range_1: range, range_2: None }
|
||||
}
|
||||
&ty::Uint(uty) => {
|
||||
// `usize`/`isize` are not allowed to be matched exhaustively unless the
|
||||
// `precise_pointer_size_matching` feature is enabled.
|
||||
let non_exhaustive =
|
||||
ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching;
|
||||
let size = Integer::from_uint_ty(&cx.tcx, uty).size();
|
||||
let max = size.truncate(u128::MAX);
|
||||
Self::Integers { range_1: make_range(0, max), non_exhaustive, range_2: None }
|
||||
let range = if ty.is_ptr_sized_integral()
|
||||
&& !cx.tcx.features().precise_pointer_size_matching
|
||||
{
|
||||
// The max value of `usize` is not allowed to be observed unless the
|
||||
// `precise_pointer_size_matching` feature is enabled.
|
||||
let lo = MaybeInfiniteInt::new_finite(cx.tcx, ty, 0);
|
||||
IntRange { lo, hi: PosInfinity }
|
||||
} else {
|
||||
let size = Integer::from_uint_ty(&cx.tcx, uty).size();
|
||||
let max = size.truncate(u128::MAX);
|
||||
make_range(0, max)
|
||||
};
|
||||
Self::Integers { range_1: range, range_2: None }
|
||||
}
|
||||
ty::Array(sub_ty, len) if len.try_eval_target_usize(cx.tcx, cx.param_env).is_some() => {
|
||||
let len = len.eval_target_usize(cx.tcx, cx.param_env) as usize;
|
||||
@ -1009,7 +1122,28 @@ impl ConstructorSet {
|
||||
missing.push(NonExhaustive);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Integers { range_1, range_2, non_exhaustive } => {
|
||||
ConstructorSet::Bool => {
|
||||
let mut seen_false = false;
|
||||
let mut seen_true = false;
|
||||
for b in seen.map(|ctor| ctor.as_bool().unwrap()) {
|
||||
if b {
|
||||
seen_true = true;
|
||||
} else {
|
||||
seen_false = true;
|
||||
}
|
||||
}
|
||||
if seen_false {
|
||||
present.push(Bool(false));
|
||||
} else {
|
||||
missing.push(Bool(false));
|
||||
}
|
||||
if seen_true {
|
||||
present.push(Bool(true));
|
||||
} else {
|
||||
missing.push(Bool(true));
|
||||
}
|
||||
}
|
||||
ConstructorSet::Integers { range_1, range_2 } => {
|
||||
let seen_ranges: Vec<_> =
|
||||
seen.map(|ctor| ctor.as_int_range().unwrap().clone()).collect();
|
||||
for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
|
||||
@ -1026,10 +1160,6 @@ impl ConstructorSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *non_exhaustive {
|
||||
missing.push(NonExhaustive);
|
||||
}
|
||||
}
|
||||
&ConstructorSet::Slice(array_len) => {
|
||||
let seen_slices = seen.map(|c| c.as_slice().unwrap());
|
||||
@ -1204,10 +1334,11 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
|
||||
},
|
||||
Str(..)
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| IntRange(..)
|
||||
| Str(..)
|
||||
| Opaque
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
@ -1336,7 +1467,14 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
}
|
||||
PatKind::Constant { value } => {
|
||||
match pat.ty.kind() {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
ty::Bool => {
|
||||
ctor = match value.try_eval_bool(cx.tcx, cx.param_env) {
|
||||
Some(b) => Bool(b),
|
||||
None => Opaque,
|
||||
};
|
||||
fields = Fields::empty();
|
||||
}
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
|
||||
Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)),
|
||||
None => Opaque,
|
||||
@ -1387,24 +1525,34 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
PatKind::Range(box PatRange { lo, hi, end }) => {
|
||||
use rustc_apfloat::Float;
|
||||
let ty = lo.ty();
|
||||
let lo = lo.try_eval_bits(cx.tcx, cx.param_env).unwrap();
|
||||
let hi = hi.try_eval_bits(cx.tcx, cx.param_env).unwrap();
|
||||
PatKind::Range(box PatRange { lo, hi, end, .. }) => {
|
||||
let ty = pat.ty;
|
||||
ctor = match ty.kind() {
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, *end))
|
||||
let lo =
|
||||
MaybeInfiniteInt::from_pat_range_bdy(*lo, ty, cx.tcx, cx.param_env);
|
||||
let hi =
|
||||
MaybeInfiniteInt::from_pat_range_bdy(*hi, ty, cx.tcx, cx.param_env);
|
||||
IntRange(IntRange::from_range(lo, hi, *end))
|
||||
}
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
let lo = rustc_apfloat::ieee::Single::from_bits(lo);
|
||||
let hi = rustc_apfloat::ieee::Single::from_bits(hi);
|
||||
F32Range(lo, hi, *end)
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
let lo = rustc_apfloat::ieee::Double::from_bits(lo);
|
||||
let hi = rustc_apfloat::ieee::Double::from_bits(hi);
|
||||
F64Range(lo, hi, *end)
|
||||
ty::Float(fty) => {
|
||||
use rustc_apfloat::Float;
|
||||
let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.param_env));
|
||||
let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.param_env));
|
||||
match fty {
|
||||
ty::FloatTy::F32 => {
|
||||
use rustc_apfloat::ieee::Single;
|
||||
let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
|
||||
let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
|
||||
F32Range(lo, hi, *end)
|
||||
}
|
||||
ty::FloatTy::F64 => {
|
||||
use rustc_apfloat::ieee::Double;
|
||||
let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
|
||||
let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
|
||||
F64Range(lo, hi, *end)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => bug!("invalid type for range pattern: {}", ty),
|
||||
};
|
||||
@ -1614,9 +1762,11 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
Bool(b) => write!(f, "{b}"),
|
||||
// Best-effort, will render signed ranges incorrectly
|
||||
IntRange(range) => write!(f, "{range:?}"),
|
||||
F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
|
||||
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
|
||||
IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0`
|
||||
Str(value) => write!(f, "{value}"),
|
||||
Opaque => write!(f, "<constant pattern>"),
|
||||
Or => {
|
||||
@ -1666,10 +1816,14 @@ impl<'tcx> WitnessPat<'tcx> {
|
||||
self.ty
|
||||
}
|
||||
|
||||
pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Pat<'tcx> {
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
|
||||
/// appear in diagnostics, like float ranges.
|
||||
pub(crate) fn to_diagnostic_pat(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Pat<'tcx> {
|
||||
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
|
||||
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx)));
|
||||
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_diagnostic_pat(cx)));
|
||||
let kind = match &self.ctor {
|
||||
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
|
||||
IntRange(range) => return range.to_diagnostic_pat(self.ty, cx.tcx),
|
||||
Single | Variant(_) => match self.ty.kind() {
|
||||
ty::Tuple(..) => PatKind::Leaf {
|
||||
subpatterns: subpatterns
|
||||
@ -1739,7 +1893,6 @@ impl<'tcx> WitnessPat<'tcx> {
|
||||
}
|
||||
}
|
||||
&Str(value) => PatKind::Constant { value },
|
||||
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
|
||||
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
|
||||
Missing { .. } => bug!(
|
||||
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
|
||||
|
@ -17,11 +17,11 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::mir::interpret::{
|
||||
ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
|
||||
};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput};
|
||||
use rustc_middle::mir::{self, BorrowKind, Const, Mutability, UserTypeProjection};
|
||||
use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
|
||||
use rustc_middle::thir::{
|
||||
Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
|
||||
};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, CanonicalUserTypeAnnotation, GenericArg, GenericArgsRef, Region, Ty, TyCtxt,
|
||||
@ -90,7 +90,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
&mut self,
|
||||
expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
) -> Result<
|
||||
(Option<mir::Const<'tcx>>, Option<Ascription<'tcx>>, Option<LocalDefId>),
|
||||
(Option<PatRangeBoundary<'tcx>>, Option<Ascription<'tcx>>, Option<LocalDefId>),
|
||||
ErrorGuaranteed,
|
||||
> {
|
||||
match expr {
|
||||
@ -113,7 +113,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
);
|
||||
return Err(self.tcx.sess.delay_span_bug(expr.span, msg));
|
||||
};
|
||||
Ok((Some(value), ascr, inline_const))
|
||||
Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,32 +187,25 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
|
||||
let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
|
||||
|
||||
let lo = lo.unwrap_or_else(|| {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let lo = ty.numeric_min_val(self.tcx).unwrap();
|
||||
mir::Const::from_ty_const(lo, self.tcx)
|
||||
});
|
||||
let hi = hi.unwrap_or_else(|| {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let hi = ty.numeric_max_val(self.tcx).unwrap();
|
||||
mir::Const::from_ty_const(hi, self.tcx)
|
||||
});
|
||||
assert_eq!(lo.ty(), ty);
|
||||
assert_eq!(hi.ty(), ty);
|
||||
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
|
||||
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
|
||||
|
||||
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env);
|
||||
let mut kind = match (end, cmp) {
|
||||
let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
|
||||
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
|
||||
match (end, cmp) {
|
||||
// `x..y` where `x < y`.
|
||||
// Non-empty because the range includes at least `x`.
|
||||
(RangeEnd::Excluded, Some(Ordering::Less)) => {
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end }))
|
||||
}
|
||||
// `x..=y` where `x == y`.
|
||||
(RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
|
||||
(RangeEnd::Excluded, Some(Ordering::Less)) => {}
|
||||
// `x..=y` where `x < y`.
|
||||
(RangeEnd::Included, Some(Ordering::Less)) => {
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end }))
|
||||
(RangeEnd::Included, Some(Ordering::Less)) => {}
|
||||
// `x..=y` where `x == y` and `x` and `y` are finite.
|
||||
(RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
|
||||
kind = PatKind::Constant { value: lo.as_finite().unwrap() };
|
||||
}
|
||||
// `..=x` where `x == ty::MIN`.
|
||||
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
|
||||
// `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
|
||||
// to include `ty::MAX`).
|
||||
(RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
|
||||
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
|
||||
_ => {
|
||||
// Emit a more appropriate message if there was overflow.
|
||||
@ -231,7 +224,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
};
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If we are handling a range with associated constants (e.g.
|
||||
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
|
||||
@ -851,59 +844,3 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub(crate) fn compare_const_vals<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
a: mir::Const<'tcx>,
|
||||
b: mir::Const<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Option<Ordering> {
|
||||
assert_eq!(a.ty(), b.ty());
|
||||
|
||||
let ty = a.ty();
|
||||
|
||||
// This code is hot when compiling matches with many ranges. So we
|
||||
// special-case extraction of evaluated scalars for speed, for types where
|
||||
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
|
||||
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
|
||||
// in this way.
|
||||
match ty.kind() {
|
||||
ty::Float(_) | ty::Int(_) => {} // require special handling, see below
|
||||
_ => match (a, b) {
|
||||
(
|
||||
mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _a_ty),
|
||||
mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _b_ty),
|
||||
) => return Some(a.cmp(&b)),
|
||||
(mir::Const::Ty(a), mir::Const::Ty(b)) => {
|
||||
return Some(a.kind().cmp(&b.kind()));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
|
||||
let a = a.eval_bits(tcx, param_env);
|
||||
let b = b.eval_bits(tcx, param_env);
|
||||
|
||||
use rustc_apfloat::Float;
|
||||
match *ty.kind() {
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
let a = rustc_apfloat::ieee::Single::from_bits(a);
|
||||
let b = rustc_apfloat::ieee::Single::from_bits(b);
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
let a = rustc_apfloat::ieee::Double::from_bits(a);
|
||||
let b = rustc_apfloat::ieee::Double::from_bits(b);
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
ty::Int(ity) => {
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
let size = rustc_target::abi::Integer::from_int_ty(&tcx, ity).size();
|
||||
let a = size.sign_extend(a);
|
||||
let b = size.sign_extend(b);
|
||||
Some((a as i128).cmp(&(b as i128)))
|
||||
}
|
||||
_ => Some(a.cmp(&b)),
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,8 @@
|
||||
use self::ArmType::*;
|
||||
use self::Usefulness::*;
|
||||
use super::deconstruct_pat::{
|
||||
Constructor, ConstructorSet, DeconstructedPat, IntRange, SplitConstructorSet, WitnessPat,
|
||||
Constructor, ConstructorSet, DeconstructedPat, IntRange, MaybeInfiniteInt, SplitConstructorSet,
|
||||
WitnessPat,
|
||||
};
|
||||
use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered};
|
||||
|
||||
@ -1013,7 +1014,7 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
|
||||
|
||||
if IntRange::is_integral(ty) {
|
||||
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
|
||||
let overlap_as_pat = overlap.to_pat(cx.tcx, ty);
|
||||
let overlap_as_pat = overlap.to_diagnostic_pat(ty, cx.tcx);
|
||||
let overlaps: Vec<_> = overlapped_spans
|
||||
.iter()
|
||||
.copied()
|
||||
@ -1031,9 +1032,10 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
|
||||
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
|
||||
for overlap_range in split_int_ranges.clone() {
|
||||
if overlap_range.is_singleton() {
|
||||
let overlap: u128 = overlap_range.boundaries().0;
|
||||
// Spans of ranges that start or end with the overlap.
|
||||
let overlap: MaybeInfiniteInt = overlap_range.lo;
|
||||
// Ranges that look like `lo..=overlap`.
|
||||
let mut prefixes: SmallVec<[_; 1]> = Default::default();
|
||||
// Ranges that look like `overlap..=hi`.
|
||||
let mut suffixes: SmallVec<[_; 1]> = Default::default();
|
||||
// Iterate on patterns that contained `overlap`.
|
||||
for pat in column.iter() {
|
||||
@ -1043,17 +1045,16 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
|
||||
// Don't lint when one of the ranges is a singleton.
|
||||
continue;
|
||||
}
|
||||
let (start, end) = this_range.boundaries();
|
||||
if start == overlap {
|
||||
// `this_range` looks like `overlap..=end`; it overlaps with any ranges that
|
||||
// look like `start..=overlap`.
|
||||
if this_range.lo == overlap {
|
||||
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
|
||||
// ranges that look like `lo..=overlap`.
|
||||
if !prefixes.is_empty() {
|
||||
emit_lint(overlap_range, this_span, &prefixes);
|
||||
}
|
||||
suffixes.push(this_span)
|
||||
} else if end == overlap {
|
||||
// `this_range` looks like `start..=overlap`; it overlaps with any ranges
|
||||
// that look like `overlap..=end`.
|
||||
} else if this_range.hi == overlap.plus_one() {
|
||||
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
|
||||
// ranges that look like `overlap..=hi`.
|
||||
if !suffixes.is_empty() {
|
||||
emit_lint(overlap_range, this_span, &suffixes);
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
fn main() {
|
||||
match 0usize {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
//~| NOTE pattern `_` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `usize::MAX..` not covered
|
||||
//~| NOTE pattern `usize::MAX..` not covered
|
||||
//~| NOTE the matched value is of type `usize`
|
||||
//~| NOTE `usize` does not have a fixed maximum value
|
||||
0..=usize::MAX => {}
|
||||
}
|
||||
|
||||
match 0isize {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
//~| NOTE pattern `_` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
//~| NOTE patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
//~| NOTE the matched value is of type `isize`
|
||||
//~| NOTE `isize` does not have a fixed maximum value
|
||||
//~| NOTE `isize` does not have fixed minimum and maximum values
|
||||
isize::MIN..=isize::MAX => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/feature-gate-precise_pointer_size_matching.rs:2:11
|
||||
|
|
||||
LL | match 0usize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ 0..=usize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + usize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/feature-gate-precise_pointer_size_matching.rs:10:11
|
||||
|
|
||||
LL | match 0isize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ isize::MIN..=isize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + ..isize::MIN | isize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0004]: non-exhaustive patterns: type `usize` is non-empty
|
||||
--> $DIR/pointer-sized-int.rs:48:11
|
||||
--> $DIR/pointer-sized-int.rs:54:11
|
||||
|
|
||||
LL | match 7usize {}
|
||||
| ^^^^^^
|
||||
|
@ -1,162 +1,162 @@
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:12:11
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:14:11
|
||||
|
|
||||
LL | match 0usize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ 0 ..= usize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + usize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:17:11
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:19:11
|
||||
|
|
||||
LL | match 0isize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ isize::MIN ..= isize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + ..isize::MIN | isize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:22:8
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:25:8
|
||||
|
|
||||
LL | m!(0usize, 0..=usize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() }
|
||||
| +++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:24:8
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:27:8
|
||||
|
|
||||
LL | m!(0usize, 0..5 | 5..=usize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() }
|
||||
| +++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:26:8
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:29:8
|
||||
|
|
||||
LL | m!(0usize, 0..usize::MAX | usize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() }
|
||||
| +++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
|
||||
--> $DIR/pointer-sized-int.rs:28:8
|
||||
|
|
||||
LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
|
||||
| ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `(usize, bool)`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, (_, _) => todo!() }
|
||||
| +++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `(usize::MAX.., _)` not covered
|
||||
--> $DIR/pointer-sized-int.rs:31:8
|
||||
|
|
||||
LL | m!(0isize, isize::MIN..=isize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
|
||||
| ^^^^^^^^^^^^^^ pattern `(usize::MAX.., _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
= note: the matched value is of type `(usize, bool)`
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, (usize::MAX.., _) => todo!() }
|
||||
| ++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:33:8
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:36:8
|
||||
|
|
||||
LL | m!(0isize, isize::MIN..=isize::MAX);
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() }
|
||||
| ++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:38:8
|
||||
|
|
||||
LL | m!(0isize, isize::MIN..5 | 5..=isize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() }
|
||||
| ++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:35:8
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:40:8
|
||||
|
|
||||
LL | m!(0isize, isize::MIN..isize::MAX | isize::MAX);
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, _ => todo!() }
|
||||
| ++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() }
|
||||
| ++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
|
||||
--> $DIR/pointer-sized-int.rs:37:8
|
||||
error[E0004]: non-exhaustive patterns: `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered
|
||||
--> $DIR/pointer-sized-int.rs:42:8
|
||||
|
|
||||
LL | m!((0isize, true), (isize::MIN..5, true)
|
||||
| ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
|
||||
| ^^^^^^^^^^^^^^ patterns `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `(isize, bool)`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL | match $s { $($t)+ => {}, (_, _) => todo!() }
|
||||
| +++++++++++++++++++
|
||||
LL | match $s { $($t)+ => {}, (..isize::MIN, _) | (isize::MAX.., _) => todo!() }
|
||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/pointer-sized-int.rs:41:11
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/pointer-sized-int.rs:47:11
|
||||
|
|
||||
LL | match 0isize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ 1 ..= isize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + ..isize::MIN | isize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `usize` is non-empty
|
||||
--> $DIR/pointer-sized-int.rs:48:11
|
||||
--> $DIR/pointer-sized-int.rs:54:11
|
||||
|
|
||||
LL | match 7usize {}
|
||||
| ^^^^^^
|
||||
|
@ -1,6 +1,7 @@
|
||||
// revisions: allow deny
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![cfg_attr(allow, feature(precise_pointer_size_matching))]
|
||||
#![allow(overlapping_range_endpoints)]
|
||||
|
||||
macro_rules! m {
|
||||
($s:expr, $($t:tt)+) => {
|
||||
@ -8,6 +9,7 @@ macro_rules! m {
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn main() {
|
||||
match 0usize {
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
@ -19,6 +21,7 @@ fn main() {
|
||||
isize::MIN ..= isize::MAX => {}
|
||||
}
|
||||
|
||||
m!(0usize, 0..);
|
||||
m!(0usize, 0..=usize::MAX);
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
m!(0usize, 0..5 | 5..=usize::MAX);
|
||||
@ -27,7 +30,9 @@ fn main() {
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
m!(0usize, 0..=usize::MAX | usize::MAX..);
|
||||
|
||||
m!(0isize, ..0 | 0..);
|
||||
m!(0isize, isize::MIN..=isize::MAX);
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
m!(0isize, isize::MIN..5 | 5..=isize::MAX);
|
||||
@ -37,6 +42,7 @@ fn main() {
|
||||
m!((0isize, true), (isize::MIN..5, true)
|
||||
| (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
|
||||
//[deny]~^^ ERROR non-exhaustive patterns
|
||||
m!(0isize, ..=isize::MIN | isize::MIN..=isize::MAX | isize::MAX..);
|
||||
|
||||
match 0isize {
|
||||
//[deny]~^ ERROR non-exhaustive patterns
|
||||
|
@ -1,18 +1,18 @@
|
||||
// This tests that the lint message explains the reason for the error.
|
||||
fn main() {
|
||||
match 0usize {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
//~| NOTE pattern `_` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `usize::MAX..` not covered
|
||||
//~| NOTE pattern `usize::MAX..` not covered
|
||||
//~| NOTE the matched value is of type `usize`
|
||||
//~| NOTE `usize` does not have a fixed maximum value
|
||||
0..=usize::MAX => {}
|
||||
}
|
||||
|
||||
match 0isize {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
//~| NOTE pattern `_` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
//~| NOTE patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
//~| NOTE the matched value is of type `isize`
|
||||
//~| NOTE `isize` does not have a fixed maximum value
|
||||
//~| NOTE `isize` does not have fixed minimum and maximum values
|
||||
isize::MIN..=isize::MAX => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/precise_pointer_matching-message.rs:3:11
|
||||
|
|
||||
LL | match 0usize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ 0..=usize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + usize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered
|
||||
--> $DIR/precise_pointer_matching-message.rs:11:11
|
||||
|
|
||||
LL | match 0isize {
|
||||
| ^^^^^^ pattern `_` not covered
|
||||
| ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ isize::MIN..=isize::MAX => {},
|
||||
LL + _ => todo!()
|
||||
LL + ..isize::MIN | isize::MAX.. => todo!()
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -6,19 +6,19 @@ struct B<T, U>(T, U);
|
||||
|
||||
fn main() {
|
||||
match 0 {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `usize::MAX..` not covered [E0004]
|
||||
0 => (),
|
||||
1..=usize::MAX => (),
|
||||
}
|
||||
|
||||
match (0usize, 0usize) {
|
||||
//~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `(usize::MAX.., _)` not covered [E0004]
|
||||
(0, 0) => (),
|
||||
(1..=usize::MAX, 1..=usize::MAX) => (),
|
||||
}
|
||||
|
||||
match (0isize, 0usize) {
|
||||
//~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered [E0004]
|
||||
(isize::MIN..=isize::MAX, 0) => (),
|
||||
(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
|
||||
}
|
||||
@ -30,14 +30,14 @@ fn main() {
|
||||
}
|
||||
|
||||
match Some(4) {
|
||||
//~^ ERROR non-exhaustive patterns: `Some(_)` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `Some(usize::MAX..)` not covered
|
||||
Some(0) => (),
|
||||
Some(1..=usize::MAX) => (),
|
||||
None => (),
|
||||
}
|
||||
|
||||
match Some(Some(Some(0))) {
|
||||
//~^ ERROR non-exhaustive patterns: `Some(Some(Some(_)))` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `Some(Some(Some(usize::MAX..)))` not covered
|
||||
Some(Some(Some(0))) => (),
|
||||
Some(Some(Some(1..=usize::MAX))) => (),
|
||||
Some(Some(None)) => (),
|
||||
@ -46,13 +46,13 @@ fn main() {
|
||||
}
|
||||
|
||||
match (A { a: 0usize }) {
|
||||
//~^ ERROR non-exhaustive patterns: `A { .. }` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `A { a: usize::MAX.. }` not covered [E0004]
|
||||
A { a: 0 } => (),
|
||||
A { a: 1..=usize::MAX } => (),
|
||||
}
|
||||
|
||||
match B(0isize, 0usize) {
|
||||
//~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `B(..isize::MIN, _)` and `B(isize::MAX.., _)` not covered [E0004]
|
||||
B(isize::MIN..=isize::MAX, 0) => (),
|
||||
B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
|
||||
}
|
||||
@ -60,7 +60,7 @@ fn main() {
|
||||
// Should report only the note about usize not having fixed max value and not report
|
||||
// report the note about isize
|
||||
match B(0isize, 0usize) {
|
||||
//~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
|
||||
//~^ ERROR non-exhaustive patterns: `B(_, usize::MAX..)` not covered [E0004]
|
||||
B(_, 0) => (),
|
||||
B(_, 1..=usize::MAX) => (),
|
||||
}
|
||||
|
@ -1,46 +1,46 @@
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
error[E0004]: non-exhaustive patterns: `usize::MAX..` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:8:11
|
||||
|
|
||||
LL | match 0 {
|
||||
| ^ pattern `_` not covered
|
||||
| ^ pattern `usize::MAX..` not covered
|
||||
|
|
||||
= note: the matched value is of type `usize`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ 1..=usize::MAX => (),
|
||||
LL ~ _ => todo!(),
|
||||
LL ~ usize::MAX.. => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `(usize::MAX.., _)` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:14:11
|
||||
|
|
||||
LL | match (0usize, 0usize) {
|
||||
| ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
|
||||
| ^^^^^^^^^^^^^^^^ pattern `(usize::MAX.., _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `(usize, usize)`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ (1..=usize::MAX, 1..=usize::MAX) => (),
|
||||
LL ~ (_, _) => todo!(),
|
||||
LL ~ (usize::MAX.., _) => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:20:11
|
||||
|
|
||||
LL | match (0isize, 0usize) {
|
||||
| ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
|
||||
| ^^^^^^^^^^^^^^^^ patterns `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `(isize, usize)`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ (isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
|
||||
LL ~ (_, _) => todo!(),
|
||||
LL ~ (..isize::MIN, _) | (isize::MAX.., _) => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
|
||||
@ -61,11 +61,11 @@ LL ~ None => {},
|
||||
LL + Some(_) => todo!()
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `Some(usize::MAX..)` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:32:11
|
||||
|
|
||||
LL | match Some(4) {
|
||||
| ^^^^^^^ pattern `Some(_)` not covered
|
||||
| ^^^^^^^ pattern `Some(usize::MAX..)` not covered
|
||||
|
|
||||
note: `Option<usize>` defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
@ -73,19 +73,19 @@ note: `Option<usize>` defined here
|
||||
|
|
||||
= note: not covered
|
||||
= note: the matched value is of type `Option<usize>`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ None => (),
|
||||
LL ~ Some(_) => todo!(),
|
||||
LL ~ Some(usize::MAX..) => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Some(Some(Some(_)))` not covered
|
||||
error[E0004]: non-exhaustive patterns: `Some(Some(Some(usize::MAX..)))` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:39:11
|
||||
|
|
||||
LL | match Some(Some(Some(0))) {
|
||||
| ^^^^^^^^^^^^^^^^^^^ pattern `Some(Some(Some(_)))` not covered
|
||||
| ^^^^^^^^^^^^^^^^^^^ pattern `Some(Some(Some(usize::MAX..)))` not covered
|
||||
|
|
||||
note: `Option<Option<Option<usize>>>` defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
@ -97,19 +97,19 @@ note: `Option<Option<Option<usize>>>` defined here
|
||||
|
|
||||
= note: not covered
|
||||
= note: the matched value is of type `Option<Option<Option<usize>>>`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ None => (),
|
||||
LL ~ Some(Some(Some(_))) => todo!(),
|
||||
LL ~ Some(Some(Some(usize::MAX..))) => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `A { .. }` not covered
|
||||
error[E0004]: non-exhaustive patterns: `A { a: usize::MAX.. }` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:48:11
|
||||
|
|
||||
LL | match (A { a: 0usize }) {
|
||||
| ^^^^^^^^^^^^^^^^^ pattern `A { .. }` not covered
|
||||
| ^^^^^^^^^^^^^^^^^ pattern `A { a: usize::MAX.. }` not covered
|
||||
|
|
||||
note: `A<usize>` defined here
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:1:8
|
||||
@ -117,19 +117,19 @@ note: `A<usize>` defined here
|
||||
LL | struct A<T> {
|
||||
| ^
|
||||
= note: the matched value is of type `A<usize>`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ A { a: 1..=usize::MAX } => (),
|
||||
LL ~ A { .. } => todo!(),
|
||||
LL ~ A { a: usize::MAX.. } => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `B(..isize::MIN, _)` and `B(isize::MAX.., _)` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:54:11
|
||||
|
|
||||
LL | match B(0isize, 0usize) {
|
||||
| ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
|
||||
| ^^^^^^^^^^^^^^^^^ patterns `B(..isize::MIN, _)` and `B(isize::MAX.., _)` not covered
|
||||
|
|
||||
note: `B<isize, usize>` defined here
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
|
||||
@ -137,19 +137,19 @@ note: `B<isize, usize>` defined here
|
||||
LL | struct B<T, U>(T, U);
|
||||
| ^
|
||||
= note: the matched value is of type `B<isize, usize>`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
|
||||
LL ~ B(_, _) => todo!(),
|
||||
LL ~ B(..isize::MIN, _) | B(isize::MAX.., _) => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `B(_, usize::MAX..)` not covered
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:62:11
|
||||
|
|
||||
LL | match B(0isize, 0usize) {
|
||||
| ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
|
||||
| ^^^^^^^^^^^^^^^^^ pattern `B(_, usize::MAX..)` not covered
|
||||
|
|
||||
note: `B<isize, usize>` defined here
|
||||
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
|
||||
@ -157,12 +157,12 @@ note: `B<isize, usize>` defined here
|
||||
LL | struct B<T, U>(T, U);
|
||||
| ^
|
||||
= note: the matched value is of type `B<isize, usize>`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ B(_, 1..=usize::MAX) => (),
|
||||
LL ~ B(_, _) => todo!(),
|
||||
LL ~ B(_, usize::MAX..) => todo!(),
|
||||
|
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
@ -1,88 +1,101 @@
|
||||
struct Foo {
|
||||
first: bool,
|
||||
second: Option<[usize; 4]>
|
||||
second: Option<[usize; 4]>,
|
||||
}
|
||||
|
||||
fn struct_with_a_nested_enum_and_vector() {
|
||||
match (Foo { first: true, second: None }) {
|
||||
//~^ ERROR non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `Foo { first: false, second: Some([0_usize, _, _, _]) }` and `Foo { first: false, second: Some([2_usize.., _, _, _]) }` not covered
|
||||
Foo { first: true, second: None } => (),
|
||||
Foo { first: true, second: Some(_) } => (),
|
||||
Foo { first: false, second: None } => (),
|
||||
Foo { first: false, second: Some([1, 2, 3, 4]) } => ()
|
||||
Foo { first: false, second: Some([1, 2, 3, 4]) } => (),
|
||||
}
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Red,
|
||||
Green,
|
||||
CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
|
||||
CustomRGBA { a: bool, r: u8, g: u8, b: u8 },
|
||||
}
|
||||
|
||||
fn enum_with_single_missing_variant() {
|
||||
match Color::Red {
|
||||
//~^ ERROR non-exhaustive patterns: `Color::Red` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `Color::Red` not covered
|
||||
Color::CustomRGBA { .. } => (),
|
||||
Color::Green => ()
|
||||
Color::Green => (),
|
||||
}
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
North, East, South, West
|
||||
North,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
}
|
||||
|
||||
fn enum_with_multiple_missing_variants() {
|
||||
match Direction::North {
|
||||
//~^ ERROR non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
|
||||
Direction::North => ()
|
||||
//~^ ERROR non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
|
||||
Direction::North => (),
|
||||
}
|
||||
}
|
||||
|
||||
enum ExcessiveEnum {
|
||||
First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, Eleventh, Twelfth
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth,
|
||||
Sixth,
|
||||
Seventh,
|
||||
Eighth,
|
||||
Ninth,
|
||||
Tenth,
|
||||
Eleventh,
|
||||
Twelfth,
|
||||
}
|
||||
|
||||
fn enum_with_excessive_missing_variants() {
|
||||
match ExcessiveEnum::First {
|
||||
//~^ ERROR `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
|
||||
|
||||
ExcessiveEnum::First => ()
|
||||
//~^ ERROR `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
|
||||
ExcessiveEnum::First => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn enum_struct_variant() {
|
||||
match Color::Red {
|
||||
//~^ ERROR non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
|
||||
Color::Red => (),
|
||||
Color::Green => (),
|
||||
Color::CustomRGBA { a: false, r: _, g: _, b: 0 } => (),
|
||||
Color::CustomRGBA { a: false, r: _, g: _, b: _ } => ()
|
||||
Color::CustomRGBA { a: false, r: _, g: _, b: _ } => (),
|
||||
}
|
||||
}
|
||||
|
||||
enum Enum {
|
||||
First,
|
||||
Second(bool)
|
||||
Second(bool),
|
||||
}
|
||||
|
||||
fn vectors_with_nested_enums() {
|
||||
let x: &'static [Enum] = &[Enum::First, Enum::Second(false)];
|
||||
match *x {
|
||||
//~^ ERROR non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
|
||||
//~^ ERROR non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
|
||||
[] => (),
|
||||
[_] => (),
|
||||
[Enum::First, _] => (),
|
||||
[Enum::Second(true), Enum::First] => (),
|
||||
[Enum::Second(true), Enum::Second(true)] => (),
|
||||
[Enum::Second(false), _] => (),
|
||||
[_, _, ref tail @ .., _] => ()
|
||||
[_, _, ref tail @ .., _] => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_nil() {
|
||||
match ((), false) {
|
||||
//~^ ERROR non-exhaustive patterns: `((), false)` not covered
|
||||
((), true) => ()
|
||||
//~^ ERROR non-exhaustive patterns: `((), false)` not covered
|
||||
((), true) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0004]: non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
|
||||
error[E0004]: non-exhaustive patterns: `Foo { first: false, second: Some([0_usize, _, _, _]) }` and `Foo { first: false, second: Some([2_usize.., _, _, _]) }` not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:7:11
|
||||
|
|
||||
LL | match (Foo { first: true, second: None }) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { first: false, second: Some([_, _, _, _]) }` not covered
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo { first: false, second: Some([0_usize, _, _, _]) }` and `Foo { first: false, second: Some([2_usize.., _, _, _]) }` not covered
|
||||
|
|
||||
note: `Foo` defined here
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:1:8
|
||||
@ -10,12 +10,10 @@ note: `Foo` defined here
|
||||
LL | struct Foo {
|
||||
| ^^^
|
||||
= note: the matched value is of type `Foo`
|
||||
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ Foo { first: false, second: Some([1, 2, 3, 4]) } => (),
|
||||
LL + Foo { first: false, second: Some([_, _, _, _]) } => todo!()
|
||||
LL ~ Foo { first: false, second: Some([0_usize, _, _, _]) } | Foo { first: false, second: Some([2_usize.., _, _, _]) } => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Color::Red` not covered
|
||||
@ -35,40 +33,42 @@ LL | Red,
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ Color::Green => (),
|
||||
LL + Color::Red => todo!()
|
||||
LL ~ Color::Red => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:35:11
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:38:11
|
||||
|
|
||||
LL | match Direction::North {
|
||||
| ^^^^^^^^^^^^^^^^ patterns `Direction::East`, `Direction::South` and `Direction::West` not covered
|
||||
|
|
||||
note: `Direction` defined here
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:31:12
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:32:5
|
||||
|
|
||||
LL | enum Direction {
|
||||
| ---------
|
||||
LL | North, East, South, West
|
||||
| ^^^^ ^^^^^ ^^^^ not covered
|
||||
| | |
|
||||
| | not covered
|
||||
| not covered
|
||||
LL | North,
|
||||
LL | East,
|
||||
| ^^^^ not covered
|
||||
LL | South,
|
||||
| ^^^^^ not covered
|
||||
LL | West,
|
||||
| ^^^^ not covered
|
||||
= note: the matched value is of type `Direction`
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ Direction::North => (),
|
||||
LL + Direction::East | Direction::South | Direction::West => todo!()
|
||||
LL ~ Direction::East | Direction::South | Direction::West => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:46:11
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:60:11
|
||||
|
|
||||
LL | match ExcessiveEnum::First {
|
||||
| ^^^^^^^^^^^^^^^^^^^^ patterns `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
|
||||
|
|
||||
note: `ExcessiveEnum` defined here
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:41:6
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:44:6
|
||||
|
|
||||
LL | enum ExcessiveEnum {
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -76,11 +76,11 @@ LL | enum ExcessiveEnum {
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
|
||||
|
|
||||
LL ~ ExcessiveEnum::First => (),
|
||||
LL + _ => todo!()
|
||||
LL ~ _ => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:54:11
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:67:11
|
||||
|
|
||||
LL | match Color::Red {
|
||||
| ^^^^^^^^^^ pattern `Color::CustomRGBA { a: true, .. }` not covered
|
||||
@ -91,17 +91,17 @@ note: `Color` defined here
|
||||
LL | enum Color {
|
||||
| -----
|
||||
...
|
||||
LL | CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
|
||||
LL | CustomRGBA { a: bool, r: u8, g: u8, b: u8 },
|
||||
| ^^^^^^^^^^ not covered
|
||||
= note: the matched value is of type `Color`
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ Color::CustomRGBA { a: false, r: _, g: _, b: _ } => (),
|
||||
LL + Color::CustomRGBA { a: true, .. } => todo!()
|
||||
LL ~ Color::CustomRGBA { a: true, .. } => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:70:11
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:83:11
|
||||
|
|
||||
LL | match *x {
|
||||
| ^^ pattern `[Enum::Second(true), Enum::Second(false)]` not covered
|
||||
@ -110,11 +110,11 @@ LL | match *x {
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ [_, _, ref tail @ .., _] => (),
|
||||
LL + [Enum::Second(true), Enum::Second(false)] => todo!()
|
||||
LL ~ [Enum::Second(true), Enum::Second(false)] => todo!(),
|
||||
|
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `((), false)` not covered
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:83:11
|
||||
--> $DIR/non-exhaustive-pattern-witness.rs:96:11
|
||||
|
|
||||
LL | match ((), false) {
|
||||
| ^^^^^^^^^^^ pattern `((), false)` not covered
|
||||
@ -123,7 +123,7 @@ LL | match ((), false) {
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL ~ ((), true) => (),
|
||||
LL + ((), false) => todo!()
|
||||
LL ~ ((), false) => todo!(),
|
||||
|
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
|
||||
fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) {}
|
||||
//~^ ERROR refutable pattern in function argument
|
||||
//~| `(_, _)` not covered
|
||||
//~| `(..=0_isize, _)` and `(2_isize.., _)` not covered
|
||||
|
||||
fn main() {
|
||||
let (1, (Some(1), 2..=3)) = (1, (None, 2));
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0005]: refutable pattern in function argument
|
||||
--> $DIR/refutable-pattern-errors.rs:1:9
|
||||
|
|
||||
LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
|
||||
LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ patterns `(..=0_isize, _)` and `(2_isize.., _)` not covered
|
||||
|
|
||||
= note: the matched value is of type `(isize, (Option<isize>, isize))`
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let f = |3: isize| println!("hello");
|
||||
//~^ ERROR refutable pattern in function argument
|
||||
//~| `_` not covered
|
||||
//~| `..=2_isize` and `4_isize..` not covered
|
||||
f(4);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ error[E0005]: refutable pattern in function argument
|
||||
--> $DIR/refutable-pattern-in-fn-arg.rs:2:14
|
||||
|
|
||||
LL | let f = |3: isize| println!("hello");
|
||||
| ^ pattern `_` not covered
|
||||
| ^ patterns `..=2_isize` and `4_isize..` not covered
|
||||
|
|
||||
= note: the matched value is of type `isize`
|
||||
help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0004]: non-exhaustive patterns: `Foo(_, _)` not covered
|
||||
error[E0004]: non-exhaustive patterns: `Foo(..=0_isize, _)` and `Foo(3_isize.., _)` not covered
|
||||
--> $DIR/tuple-struct-nonexhaustive.rs:5:11
|
||||
|
|
||||
LL | match x {
|
||||
| ^ pattern `Foo(_, _)` not covered
|
||||
| ^ patterns `Foo(..=0_isize, _)` and `Foo(3_isize.., _)` not covered
|
||||
|
|
||||
note: `Foo` defined here
|
||||
--> $DIR/tuple-struct-nonexhaustive.rs:1:8
|
||||
@ -10,12 +10,10 @@ note: `Foo` defined here
|
||||
LL | struct Foo(isize, isize);
|
||||
| ^^^
|
||||
= note: the matched value is of type `Foo`
|
||||
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
|
||||
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
||||
|
|
||||
LL ~ Foo(2, b) => println!("{}", b),
|
||||
LL + Foo(_, _) => todo!()
|
||||
LL + Foo(..=0_isize, _) | Foo(3_isize.., _) => todo!()
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue
Block a user