use std::cell::Cell;
use std::fmt;
use std::iter::once;
use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
use rustc_arena::DroplessArena;
use rustc_hir::HirId;
use rustc_hir::def_id::DefId;
use rustc_index::{Idx, IndexVec};
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use crate::constructor::Constructor::*;
use crate::constructor::{
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
};
use crate::lints::lint_nonexhaustive_missing_variants;
use crate::pat_column::PatternColumn;
use crate::rustc::print::EnumInfo;
use crate::usefulness::{PlaceValidity, compute_match_usefulness};
use crate::{PatCx, PrivateUninhabitedField, errors};
mod print;
pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p, 'tcx>>;
pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>;
pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>;
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>;
pub type RedundancyExplanation<'p, 'tcx> =
crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>;
pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>;
pub type UsefulnessReport<'p, 'tcx> =
crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>;
pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RevealedTy<'tcx>(Ty<'tcx>);
impl<'tcx> fmt::Display for RevealedTy<'tcx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl<'tcx> std::ops::Deref for RevealedTy<'tcx> {
type Target = Ty<'tcx>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'tcx> RevealedTy<'tcx> {
pub fn inner(self) -> Ty<'tcx> {
self.0
}
}
#[derive(Clone)]
pub struct RustcPatCtxt<'p, 'tcx: 'p> {
pub tcx: TyCtxt<'tcx>,
pub typeck_results: &'tcx ty::TypeckResults<'tcx>,
pub module: DefId,
pub typing_env: ty::TypingEnv<'tcx>,
pub dropless_arena: &'p DroplessArena,
pub match_lint_level: HirId,
pub whole_match_span: Option<Span>,
pub scrut_span: Span,
pub refutable: bool,
pub known_valid_scrutinee: bool,
pub internal_state: RustcPatCtxtState,
}
#[derive(Clone, Default)]
pub struct RustcPatCtxtState {
has_lowered_deref_pat: Cell<bool>,
}
impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RustcPatCtxt").finish()
}
}
impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
#[inline]
pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind()
else {
bug!()
};
if let Some(local_def_id) = def_id.as_local() {
let key = ty::OpaqueTypeKey { def_id: local_def_id, args };
if let Some(ty) = cx.reveal_opaque_key(key) {
return RevealedTy(ty);
}
}
RevealedTy(ty)
}
if let ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() {
reveal_inner(self, ty)
} else {
RevealedTy(ty)
}
}
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
self.typeck_results
.hidden_types
.get(&key.def_id)
.map(|x| x.ty.instantiate(self.tcx, key.args))
}
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
self.tcx,
self.typing_env,
self.module,
&|key| self.reveal_opaque_key(key),
)
}
pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
match ty.kind() {
ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
_ => false,
}
}
pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
ty.is_ptr_sized_integral() && {
let lo = self.hoist_pat_range_bdy(range.lo, ty);
matches!(lo, PatRangeBoundary::PosInfinity)
|| matches!(range.hi, MaybeInfiniteInt::Finite(0))
}
}
pub(crate) fn variant_sub_tys(
&self,
ty: RevealedTy<'tcx>,
variant: &'tcx VariantDef,
) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> {
let ty::Adt(_, args) = ty.kind() else { bug!() };
variant.fields.iter().map(move |field| {
let ty = field.ty(self.tcx, args);
let ty =
self.tcx.try_normalize_erasing_regions(self.typing_env, ty).unwrap_or_else(|e| {
self.tcx.dcx().span_delayed_bug(
self.scrut_span,
format!(
"Failed to normalize {:?} in typing_env={:?} while getting variant sub tys for {ty:?}",
e.get_type_for_failure(),
self.typing_env,
),
);
ty
});
let ty = self.reveal_opaque_ty(ty);
(field, ty)
})
}
pub(crate) fn variant_index_for_adt(
ctor: &Constructor<'p, 'tcx>,
adt: ty::AdtDef<'tcx>,
) -> VariantIdx {
match *ctor {
Variant(idx) => idx,
Struct | UnionField => {
assert!(!adt.is_enum());
FIRST_VARIANT
}
_ => bug!("bad constructor {:?} for adt {:?}", ctor, adt),
}
}
pub(crate) fn ctor_sub_tys(
&self,
ctor: &Constructor<'p, 'tcx>,
ty: RevealedTy<'tcx>,
) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)> + ExactSizeIterator {
fn reveal_and_alloc<'a, 'tcx>(
cx: &'a RustcPatCtxt<'_, 'tcx>,
iter: impl Iterator<Item = Ty<'tcx>>,
) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
cx.dropless_arena.alloc_from_iter(
iter.map(|ty| cx.reveal_opaque_ty(ty))
.map(|ty| (ty, PrivateUninhabitedField(false))),
)
}
let cx = self;
let slice = match ctor {
Struct | Variant(_) | UnionField => match ty.kind() {
ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
ty::Adt(adt, _) => {
let variant = &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
let is_visible =
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
let is_uninhabited = cx.is_uninhabited(*ty);
let skip = is_uninhabited && !is_visible;
(ty, PrivateUninhabitedField(skip))
});
cx.dropless_arena.alloc_from_iter(tys)
}
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
},
Ref => match ty.kind() {
ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
_ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
},
Slice(slice) => match ty.builtin_index() {
Some(ty) => {
let arity = slice.arity();
reveal_and_alloc(cx, (0..arity).map(|_| ty))
}
None => bug!("bad slice pattern {:?} {:?}", ctor, ty),
},
DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
| PrivateUninhabited | Wildcard => &[],
Or => {
bug!("called `Fields::wildcards` on an `Or` ctor")
}
};
slice.iter().copied()
}
pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
match ctor {
Struct | Variant(_) | UnionField => match ty.kind() {
ty::Tuple(fs) => fs.len(),
ty::Adt(adt, ..) => {
let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
adt.variant(variant_idx).fields.len()
}
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
},
Ref | DerefPattern(_) => 1,
Slice(slice) => slice.arity(),
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
| PrivateUninhabited | Wildcard => 0,
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
}
}
pub fn ctors_for_ty(
&self,
ty: RevealedTy<'tcx>,
) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
let cx = self;
let make_uint_range = |start, end| {
IntRange::from_range(
MaybeInfiniteInt::new_finite_uint(start),
MaybeInfiniteInt::new_finite_uint(end),
RangeEnd::Included,
)
};
ty.error_reported()?;
Ok(match ty.kind() {
ty::Bool => ConstructorSet::Bool,
ty::Char => {
ConstructorSet::Integers {
range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128),
range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
}
}
&ty::Int(ity) => {
let range = if ty.is_ptr_sized_integral() {
IntRange {
lo: MaybeInfiniteInt::NegInfinity,
hi: MaybeInfiniteInt::PosInfinity,
}
} else {
let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
let min = 1u128 << (size - 1);
let max = min - 1;
let min = MaybeInfiniteInt::new_finite_int(min, size);
let max = MaybeInfiniteInt::new_finite_int(max, size);
IntRange::from_range(min, max, RangeEnd::Included)
};
ConstructorSet::Integers { range_1: range, range_2: None }
}
&ty::Uint(uty) => {
let range = if ty.is_ptr_sized_integral() {
let lo = MaybeInfiniteInt::new_finite_uint(0);
IntRange { lo, hi: MaybeInfiniteInt::PosInfinity }
} else {
let size = Integer::from_uint_ty(&cx.tcx, uty).size();
let max = size.truncate(u128::MAX);
make_uint_range(0, max)
};
ConstructorSet::Integers { range_1: range, range_2: None }
}
ty::Slice(sub_ty) => ConstructorSet::Slice {
array_len: None,
subtype_is_empty: cx.is_uninhabited(*sub_ty),
},
ty::Array(sub_ty, len) => {
ConstructorSet::Slice {
array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize),
subtype_is_empty: cx.is_uninhabited(*sub_ty),
}
}
ty::Adt(def, args) if def.is_enum() => {
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
if def.variants().is_empty() && !is_declared_nonexhaustive {
ConstructorSet::NoConstructors
} else {
let mut variants =
IndexVec::from_elem(VariantVisibility::Visible, def.variants());
for (idx, v) in def.variants().iter_enumerated() {
let variant_def_id = def.variant(idx).def_id;
let is_inhabited = v
.inhabited_predicate(cx.tcx, *def)
.instantiate(cx.tcx, args)
.apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
cx.reveal_opaque_key(key)
});
let is_unstable = matches!(
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
EvalResult::Deny { .. }
);
let is_doc_hidden =
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
let visibility = if !is_inhabited {
VariantVisibility::Empty
} else if is_unstable || is_doc_hidden {
VariantVisibility::Hidden
} else {
VariantVisibility::Visible
};
variants[idx] = visibility;
}
ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
}
}
ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
ty::Adt(..) | ty::Tuple(..) => {
ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
}
ty::Ref(..) => ConstructorSet::Ref,
ty::Never => ConstructorSet::NoConstructors,
ty::Float(_)
| ty::Str
| ty::Foreign(_)
| ty::RawPtr(_, _)
| ty::FnDef(_, _)
| ty::FnPtr(..)
| ty::Pat(_, _)
| ty::Dynamic(_, _)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::UnsafeBinder(_)
| ty::Alias(_)
| ty::Param(_)
| ty::Error(_) => ConstructorSet::Unlistable,
ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
}
})
}
pub(crate) fn lower_pat_range_bdy(
&self,
bdy: PatRangeBoundary<'tcx>,
ty: RevealedTy<'tcx>,
) -> MaybeInfiniteInt {
match bdy {
PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
PatRangeBoundary::Finite(value) => {
let bits = value.to_leaf().to_bits_unchecked();
match *ty.kind() {
ty::Int(ity) => {
let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
MaybeInfiniteInt::new_finite_int(bits, size)
}
_ => MaybeInfiniteInt::new_finite_uint(bits),
}
}
PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity,
}
}
pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
let cx = self;
let ty = cx.reveal_opaque_ty(pat.ty);
let ctor;
let arity;
let fields: Vec<_>;
match &pat.kind {
PatKind::Binding { subpattern: Some(subpat), .. }
| PatKind::Guard { subpattern: subpat, .. } => return self.lower_pat(subpat),
PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
ctor = Wildcard;
fields = vec![];
arity = 0;
}
PatKind::Deref { pin, subpattern } => {
fields = vec![self.lower_pat(subpattern).at_index(0)];
arity = 1;
ctor = match (pin, ty.maybe_pinned_ref()) {
(ty::Pinnedness::Not, Some((_, ty::Pinnedness::Not, _, _))) => Ref,
(ty::Pinnedness::Pinned, Some((inner_ty, ty::Pinnedness::Pinned, _, _))) => {
self.internal_state.has_lowered_deref_pat.set(true);
DerefPattern(RevealedTy(inner_ty))
}
_ => span_bug!(
pat.span,
"pattern has unexpected type: pat: {:?}, ty: {:?}",
pat.kind,
ty.inner()
),
};
}
PatKind::DerefPattern { subpattern, .. } => {
fields = vec![self.lower_pat(subpattern).at_index(0)];
arity = 1;
ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
self.internal_state.has_lowered_deref_pat.set(true);
}
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
match ty.kind() {
ty::Tuple(fs) => {
ctor = Struct;
arity = fs.len();
fields = subpatterns
.iter()
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
.collect();
}
ty::Adt(adt, _) => {
ctor = match pat.kind {
PatKind::Leaf { .. } if adt.is_union() => UnionField,
PatKind::Leaf { .. } => Struct,
PatKind::Variant { variant_index, .. } => Variant(variant_index),
_ => bug!(),
};
let variant =
&adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
arity = variant.fields.len();
fields = subpatterns
.iter()
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
.collect();
}
_ => span_bug!(
pat.span,
"pattern has unexpected type: pat: {:?}, ty: {}",
pat.kind,
ty.inner()
),
}
}
PatKind::Constant { value } => {
match ty.kind() {
ty::Bool => {
ctor = Bool(value.try_to_bool().unwrap());
fields = vec![];
arity = 0;
}
ty::Char | ty::Int(_) | ty::Uint(_) => {
ctor = {
let bits = value.to_leaf().to_bits_unchecked();
let x = match *ty.kind() {
ty::Int(ity) => {
let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
MaybeInfiniteInt::new_finite_int(bits, size)
}
_ => MaybeInfiniteInt::new_finite_uint(bits),
};
IntRange(IntRange::from_singleton(x))
};
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F16) => {
use rustc_apfloat::Float;
let bits = value.to_leaf().to_u16();
let value = rustc_apfloat::ieee::Half::from_bits(bits.into());
ctor = F16Range(value, value, RangeEnd::Included);
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F32) => {
use rustc_apfloat::Float;
let bits = value.to_leaf().to_u32();
let value = rustc_apfloat::ieee::Single::from_bits(bits.into());
ctor = F32Range(value, value, RangeEnd::Included);
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F64) => {
use rustc_apfloat::Float;
let bits = value.to_leaf().to_u64();
let value = rustc_apfloat::ieee::Double::from_bits(bits.into());
ctor = F64Range(value, value, RangeEnd::Included);
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F128) => {
use rustc_apfloat::Float;
let bits = value.to_leaf().to_u128();
let value = rustc_apfloat::ieee::Quad::from_bits(bits);
ctor = F128Range(value, value, RangeEnd::Included);
fields = vec![];
arity = 0;
}
ty::Str => {
ctor = Str(*value);
fields = vec![];
arity = 0;
}
_ => {
ctor = Opaque(OpaqueId::new());
fields = vec![];
arity = 0;
}
}
}
PatKind::Range(patrange) => {
let PatRange { lo, hi, end, .. } = patrange.as_ref();
let end = match end {
rustc_hir::RangeEnd::Included => RangeEnd::Included,
rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
};
ctor = match ty.kind() {
ty::Char | ty::Int(_) | ty::Uint(_) => {
let lo = cx.lower_pat_range_bdy(*lo, ty);
let hi = cx.lower_pat_range_bdy(*hi, ty);
IntRange(IntRange::from_range(lo, hi, end))
}
ty::Float(fty) => {
use rustc_apfloat::Float;
let lo = lo.as_finite().map(|c| c.to_leaf().to_bits_unchecked());
let hi = hi.as_finite().map(|c| c.to_leaf().to_bits_unchecked());
match fty {
ty::FloatTy::F16 => {
use rustc_apfloat::ieee::Half;
let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
F16Range(lo, hi, end)
}
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)
}
ty::FloatTy::F128 => {
use rustc_apfloat::ieee::Quad;
let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
F128Range(lo, hi, end)
}
}
}
_ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()),
};
fields = vec![];
arity = 0;
}
PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
let array_len = match ty.kind() {
ty::Array(_, length) => Some(
length
.try_to_target_usize(cx.tcx)
.expect("expected len of array pat to be definite")
as usize,
),
ty::Slice(_) => None,
_ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()),
};
let kind = if slice.is_some() {
SliceKind::VarLen(prefix.len(), suffix.len())
} else {
SliceKind::FixedLen(prefix.len() + suffix.len())
};
ctor = Slice(Slice::new(array_len, kind));
fields = prefix
.iter()
.chain(suffix.iter())
.map(|p| self.lower_pat(&*p))
.enumerate()
.map(|(i, p)| p.at_index(i))
.collect();
arity = kind.arity();
}
PatKind::Or { .. } => {
ctor = Or;
let pats = expand_or_pat(pat);
fields = pats
.into_iter()
.map(|p| self.lower_pat(p))
.enumerate()
.map(|(i, p)| p.at_index(i))
.collect();
arity = fields.len();
}
PatKind::Never => {
ctor = Wildcard;
fields = vec![];
arity = 0;
}
PatKind::Error(_) => {
ctor = Opaque(OpaqueId::new());
fields = vec![];
arity = 0;
}
}
DeconstructedPat::new(ctor, fields, arity, ty, pat)
}
fn hoist_pat_range_bdy(
&self,
miint: MaybeInfiniteInt,
ty: RevealedTy<'tcx>,
) -> PatRangeBoundary<'tcx> {
use MaybeInfiniteInt::*;
let tcx = self.tcx;
match miint {
NegInfinity => PatRangeBoundary::NegInfinity,
Finite(_) => {
let size = ty.primitive_size(tcx);
let bits = match *ty.kind() {
ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(),
_ => miint.as_finite_uint().unwrap(),
};
match ScalarInt::try_from_uint(bits, size) {
Some(scalar) => {
let valtree = ty::ValTree::from_scalar_int(tcx, scalar);
PatRangeBoundary::Finite(valtree)
}
None => PatRangeBoundary::PosInfinity,
}
}
PosInfinity => PatRangeBoundary::PosInfinity,
}
}
fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
use MaybeInfiniteInt::*;
let cx = self;
if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
"_".to_string()
} else if range.is_singleton() {
let lo = cx.hoist_pat_range_bdy(range.lo, ty);
let value = ty::Value { ty: ty.inner(), valtree: lo.as_finite().unwrap() };
value.to_string()
} else {
let mut end = rustc_hir::RangeEnd::Included;
let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
if matches!(lo, PatRangeBoundary::PosInfinity) {
let max = ty.numeric_max_val(cx.tcx).unwrap();
let max = ty::ValTree::from_scalar_int(cx.tcx, max.try_to_scalar_int().unwrap());
lo = PatRangeBoundary::Finite(max);
}
let hi = if let Some(hi) = range.hi.minus_one() {
hi
} else {
end = rustc_hir::RangeEnd::Excluded;
range.hi
};
let hi = cx.hoist_pat_range_bdy(hi, ty);
PatRange { lo, hi, end, ty: ty.inner() }.to_string()
}
}
pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
let cx = self;
let print = |p| cx.print_witness_pat(p);
match pat.ctor() {
Bool(b) => b.to_string(),
Str(s) => s.to_string(),
IntRange(range) => return self.print_pat_range(range, *pat.ty()),
Struct | Variant(_) | UnionField => {
let enum_info = match *pat.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
adt_def,
variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
},
ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
};
let subpatterns = pat
.iter_fields()
.enumerate()
.map(|(i, pat)| print::FieldPat {
field: FieldIdx::new(i),
pattern: print(pat),
is_wildcard: would_print_as_wildcard(cx.tcx, pat),
})
.collect::<Vec<_>>();
let mut s = String::new();
print::write_struct_like(
&mut s,
self.tcx,
pat.ty().inner(),
&enum_info,
&subpatterns,
)
.unwrap();
s
}
Ref => {
let mut s = String::new();
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
s
}
DerefPattern(_) if pat.ty().is_box() && !self.tcx.features().deref_patterns() => {
format!("box {}", print(&pat.fields[0]))
}
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
Slice(slice) => {
let (prefix_len, has_dot_dot) = match slice.kind {
SliceKind::FixedLen(len) => (len, false),
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
};
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
if has_dot_dot && slice.array_len.is_some() {
while let [rest @ .., last] = prefix
&& would_print_as_wildcard(cx.tcx, last)
{
prefix = rest;
}
while let [first, rest @ ..] = suffix
&& would_print_as_wildcard(cx.tcx, first)
{
suffix = rest;
}
}
let prefix = prefix.iter().map(print).collect::<Vec<_>>();
let suffix = suffix.iter().map(print).collect::<Vec<_>>();
let mut s = String::new();
print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
s
}
Never if self.tcx.features().never_patterns() => "!".to_string(),
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
),
F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
bug!("can't convert to pattern: {:?}", pat)
}
}
}
}
fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
match p.ctor() {
Constructor::IntRange(IntRange {
lo: MaybeInfiniteInt::NegInfinity,
hi: MaybeInfiniteInt::PosInfinity,
})
| Constructor::Wildcard
| Constructor::NonExhaustive
| Constructor::Hidden
| Constructor::PrivateUninhabited => true,
Constructor::Never if !tcx.features().never_patterns() => true,
_ => false,
}
}
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
type Ty = RevealedTy<'tcx>;
type Error = ErrorGuaranteed;
type VariantIdx = VariantIdx;
type StrLit = ty::Value<'tcx>;
type ArmData = HirId;
type PatData = &'p Pat<'tcx>;
fn is_exhaustive_patterns_feature_on(&self) -> bool {
self.tcx.features().exhaustive_patterns()
}
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
self.ctor_arity(ctor, *ty)
}
fn ctor_sub_tys(
&self,
ctor: &crate::constructor::Constructor<Self>,
ty: &Self::Ty,
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator {
self.ctor_sub_tys(ctor, *ty)
}
fn ctors_for_ty(
&self,
ty: &Self::Ty,
) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
self.ctors_for_ty(*ty)
}
fn write_variant_name(
f: &mut fmt::Formatter<'_>,
ctor: &crate::constructor::Constructor<Self>,
ty: &Self::Ty,
) -> fmt::Result {
if let ty::Adt(adt, _) = ty.kind() {
let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
write!(f, "{}", variant.name)?;
}
Ok(())
}
fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error {
span_bug!(self.scrut_span, "{}", fmt)
}
fn lint_overlapping_range_endpoints(
&self,
pat: &crate::pat::DeconstructedPat<Self>,
overlaps_on: IntRange,
overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
) {
let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
let overlaps: Vec<_> = overlaps_with
.iter()
.map(|pat| pat.data().span)
.map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
.collect();
let pat_span = pat.data().span;
self.tcx.emit_node_span_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
self.match_lint_level,
pat_span,
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
);
}
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
let span = self.whole_match_span.unwrap_or(self.scrut_span);
Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
}
fn lint_non_contiguous_range_endpoints(
&self,
pat: &crate::pat::DeconstructedPat<Self>,
gap: IntRange,
gapped_with: &[&crate::pat::DeconstructedPat<Self>],
) {
let &thir_pat = pat.data();
let thir::PatKind::Range(range) = &thir_pat.kind else { return };
if range.end != rustc_hir::RangeEnd::Excluded {
return;
}
let suggested_range: String = {
let mut suggested_range = PatRange::clone(range);
suggested_range.end = rustc_hir::RangeEnd::Included;
suggested_range.to_string()
};
let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
if gapped_with.is_empty() {
self.tcx.emit_node_span_lint(
lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
self.match_lint_level,
thir_pat.span,
errors::ExclusiveRangeMissingMax {
first_range: thir_pat.span,
max: gap_as_pat,
suggestion: suggested_range,
},
);
} else {
self.tcx.emit_node_span_lint(
lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
self.match_lint_level,
thir_pat.span,
errors::ExclusiveRangeMissingGap {
first_range: thir_pat.span,
gap: gap_as_pat.to_string(),
suggestion: suggested_range,
gap_with: gapped_with
.iter()
.map(|pat| errors::GappedRange {
span: pat.data().span,
gap: gap_as_pat.to_string(),
first_range: range.to_string(),
})
.collect(),
},
);
}
}
fn match_may_contain_deref_pats(&self) -> bool {
self.internal_state.has_lowered_deref_pat.get()
}
fn report_mixed_deref_pat_ctors(
&self,
deref_pat: &crate::pat::DeconstructedPat<Self>,
normal_pat: &crate::pat::DeconstructedPat<Self>,
) -> Self::Error {
let deref_pattern_label = deref_pat.data().span;
let normal_constructor_label = normal_pat.data().span;
self.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors {
spans: vec![deref_pattern_label, normal_constructor_label],
smart_pointer_ty: deref_pat.ty().inner(),
deref_pattern_label,
normal_constructor_label,
})
}
}
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
if let PatKind::Or { pats } = &pat.kind {
for pat in pats.iter() {
expand(pat, vec);
}
} else {
vec.push(pat)
}
}
let mut pats = Vec::new();
expand(pat, &mut pats);
pats
}
pub fn analyze_match<'p, 'tcx>(
tycx: &RustcPatCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
let report = compute_match_usefulness(
tycx,
arms,
scrut_ty,
scrut_validity,
tycx.tcx.pattern_complexity_limit().0,
)?;
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
let pat_column = PatternColumn::new(arms);
lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
}
Ok(report)
}