Statically enforce revealing of opaques
This commit is contained in:
parent
e51e98dde6
commit
c35272058d
@ -554,7 +554,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||
let cx = self.new_cx(refutability, None, scrut, pat.span);
|
||||
let pat = self.lower_pattern(&cx, pat)?;
|
||||
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
|
||||
let report = analyze_match(&cx, &arms, pat.ty());
|
||||
let report = analyze_match(&cx, &arms, pat.ty().inner());
|
||||
Ok((cx, report))
|
||||
}
|
||||
|
||||
@ -972,7 +972,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||
}
|
||||
} else if ty == cx.tcx.types.str_ {
|
||||
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
||||
} else if cx.is_foreign_non_exhaustive_enum(ty) {
|
||||
} else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
|
||||
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
|
||||
}
|
||||
}
|
||||
@ -1112,12 +1112,12 @@ fn collect_non_exhaustive_tys<'tcx>(
|
||||
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
|
||||
) {
|
||||
if matches!(pat.ctor(), Constructor::NonExhaustive) {
|
||||
non_exhaustive_tys.insert(pat.ty());
|
||||
non_exhaustive_tys.insert(pat.ty().inner());
|
||||
}
|
||||
if let Constructor::IntRange(range) = pat.ctor() {
|
||||
if cx.is_range_beyond_boundaries(range, pat.ty()) {
|
||||
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
|
||||
non_exhaustive_tys.insert(pat.ty());
|
||||
non_exhaustive_tys.insert(pat.ty().inner());
|
||||
}
|
||||
}
|
||||
pat.iter_fields()
|
||||
|
@ -61,8 +61,6 @@ pub trait TypeCx: Sized + fmt::Debug {
|
||||
/// Extra data to store in a pattern.
|
||||
type PatData: Clone;
|
||||
|
||||
/// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty;
|
||||
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
||||
|
||||
/// The number of fields for this constructor.
|
||||
@ -114,6 +112,7 @@ pub fn analyze_match<'p, 'tcx>(
|
||||
) -> rustc::UsefulnessReport<'p, 'tcx> {
|
||||
// Arena to store the extra wildcards we construct during analysis.
|
||||
let wildcard_arena = tycx.pattern_arena;
|
||||
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
|
||||
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
|
||||
let cx = MatchCtxt { tycx, wildcard_arena };
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_span::Span;
|
||||
@ -12,10 +12,9 @@ use crate::errors::{
|
||||
OverlappingRangeEndpoints, Uncovered,
|
||||
};
|
||||
use crate::rustc::{
|
||||
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
|
||||
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
|
||||
SplitConstructorSet, WitnessPat,
|
||||
};
|
||||
use crate::TypeCx;
|
||||
|
||||
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
||||
/// inspect the same subvalue/place".
|
||||
@ -48,14 +47,8 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.patterns.is_empty()
|
||||
}
|
||||
fn head_ty(&self, cx: MatchCtxt<'_, 'p, 'tcx>) -> Option<Ty<'tcx>> {
|
||||
if self.patterns.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty = self.patterns[0].ty();
|
||||
// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
Some(cx.tycx.reveal_opaque_ty(ty))
|
||||
fn head_ty(&self) -> Option<RevealedTy<'tcx>> {
|
||||
self.patterns.first().map(|pat| pat.ty())
|
||||
}
|
||||
|
||||
/// Do constructor splitting on the constructors of the column.
|
||||
@ -117,7 +110,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'p, 'tcx>,
|
||||
) -> Vec<WitnessPat<'p, 'tcx>> {
|
||||
let Some(ty) = column.head_ty(cx) else {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
@ -164,7 +157,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
pat_column: &PatternColumn<'p, 'tcx>,
|
||||
scrut_ty: Ty<'tcx>,
|
||||
scrut_ty: RevealedTy<'tcx>,
|
||||
) {
|
||||
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
||||
if !matches!(
|
||||
@ -182,7 +175,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||
rcx.match_lint_level,
|
||||
rcx.scrut_span,
|
||||
NonExhaustiveOmittedPattern {
|
||||
scrut_ty,
|
||||
scrut_ty: scrut_ty.inner(),
|
||||
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
|
||||
},
|
||||
);
|
||||
@ -218,7 +211,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'p, 'tcx>,
|
||||
) {
|
||||
let Some(ty) = column.head_ty(cx) else {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
return;
|
||||
};
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
|
@ -41,6 +41,30 @@ pub type UsefulnessReport<'p, 'tcx> =
|
||||
crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
|
||||
/// A type which has gone through `cx.reveal_opaque_ty`, i.e. if it was opaque it was replaced by
|
||||
/// the hidden type if allowed in the current body. This ensures we consistently inspect the hidden
|
||||
/// types when we should.
|
||||
///
|
||||
/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
|
||||
#[repr(transparent)]
|
||||
#[derive(derivative::Derivative)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derivative(Debug = "transparent")]
|
||||
pub struct RevealedTy<'tcx>(Ty<'tcx>);
|
||||
|
||||
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 RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
@ -74,20 +98,48 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
fn reveal_opaque(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
|
||||
/// Type inference occasionally gives us opaque types in places where corresponding patterns
|
||||
/// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
|
||||
/// types, we use the corresponding concrete type if possible.
|
||||
#[inline]
|
||||
pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
|
||||
fn reveal_inner<'tcx>(
|
||||
cx: &RustcMatchCheckCtxt<'_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> RevealedTy<'tcx> {
|
||||
let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!() };
|
||||
if let Some(local_def_id) = alias_ty.def_id.as_local() {
|
||||
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
|
||||
if let Some(ty) = cx.reveal_opaque_key(key) {
|
||||
return RevealedTy(ty);
|
||||
}
|
||||
}
|
||||
RevealedTy(ty)
|
||||
}
|
||||
if let ty::Alias(ty::Opaque, _) = ty.kind() {
|
||||
reveal_inner(self, ty)
|
||||
} else {
|
||||
RevealedTy(ty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hidden type corresponding to this key if the body under analysis is allowed to
|
||||
/// know it.
|
||||
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
|
||||
self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
|
||||
}
|
||||
// This can take a non-revealed `Ty` because it reveals opaques itself.
|
||||
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
|
||||
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
|
||||
self.tcx,
|
||||
self.param_env,
|
||||
self.module,
|
||||
&|key| self.reveal_opaque(key),
|
||||
&|key| self.reveal_opaque_key(key),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
|
||||
pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
|
||||
pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(def, ..) => {
|
||||
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
|
||||
@ -98,7 +150,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
|
||||
/// 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 fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool {
|
||||
pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
|
||||
ty.is_ptr_sized_integral() && {
|
||||
// The two invalid ranges are `NegInfinity..isize::MIN` (represented as
|
||||
// `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy`
|
||||
@ -110,29 +162,14 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type inference occasionally gives us opaque types in places where corresponding patterns
|
||||
/// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
|
||||
/// types, we use the corresponding concrete type if possible.
|
||||
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
|
||||
if let Some(local_def_id) = alias_ty.def_id.as_local() {
|
||||
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
|
||||
if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
|
||||
return real_ty.ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
|
||||
// uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
|
||||
// This lists the fields we keep along with their types.
|
||||
pub(crate) fn list_variant_nonhidden_fields(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
variant: &'tcx VariantDef,
|
||||
) -> impl Iterator<Item = (FieldIdx, Ty<'tcx>)> + Captures<'p> + Captures<'_> {
|
||||
) -> impl Iterator<Item = (FieldIdx, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_> {
|
||||
let cx = self;
|
||||
let ty::Adt(adt, args) = ty.kind() else { bug!() };
|
||||
// Whether we must not match the fields of this variant exhaustively.
|
||||
@ -148,6 +185,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
if is_uninhabited && (!is_visible || is_non_exhaustive) {
|
||||
None
|
||||
} else {
|
||||
let ty = cx.reveal_opaque_ty(ty);
|
||||
Some((FieldIdx::new(i), ty))
|
||||
}
|
||||
})
|
||||
@ -170,16 +208,26 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
/// Returns the types of the fields for a given constructor. The result must have a length of
|
||||
/// `ctor.arity()`.
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] {
|
||||
pub(crate) fn ctor_sub_tys(
|
||||
&self,
|
||||
ctor: &Constructor<'p, 'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
) -> &[RevealedTy<'tcx>] {
|
||||
fn reveal_and_alloc<'a, 'tcx>(
|
||||
cx: &'a RustcMatchCheckCtxt<'_, 'tcx>,
|
||||
iter: impl Iterator<Item = Ty<'tcx>>,
|
||||
) -> &'a [RevealedTy<'tcx>] {
|
||||
cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty)))
|
||||
}
|
||||
let cx = self;
|
||||
match ctor {
|
||||
Struct | Variant(_) | UnionField => match ty.kind() {
|
||||
ty::Tuple(fs) => cx.dropless_arena.alloc_from_iter(fs.iter()),
|
||||
ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
|
||||
ty::Adt(adt, args) => {
|
||||
if adt.is_box() {
|
||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
cx.dropless_arena.alloc_from_iter(once(args.type_at(0)))
|
||||
reveal_and_alloc(cx, once(args.type_at(0)))
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
@ -190,13 +238,13 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => match ty.kind() {
|
||||
ty::Ref(_, rty, _) => cx.dropless_arena.alloc_from_iter(once(*rty)),
|
||||
ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
|
||||
_ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
|
||||
},
|
||||
Slice(slice) => match *ty.kind() {
|
||||
ty::Slice(ty) | ty::Array(ty, _) => {
|
||||
let arity = slice.arity();
|
||||
cx.dropless_arena.alloc_from_iter((0..arity).map(|_| ty))
|
||||
reveal_and_alloc(cx, (0..arity).map(|_| ty))
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
|
||||
},
|
||||
@ -217,7 +265,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
|
||||
/// The number of fields for this constructor.
|
||||
pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> usize {
|
||||
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(),
|
||||
@ -254,7 +302,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
///
|
||||
/// See [`crate::constructor`] for considerations of emptiness.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet<'p, 'tcx> {
|
||||
pub fn ctors_for_ty(&self, ty: RevealedTy<'tcx>) -> ConstructorSet<'p, 'tcx> {
|
||||
let cx = self;
|
||||
let make_uint_range = |start, end| {
|
||||
IntRange::from_range(
|
||||
@ -328,7 +376,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
.inhabited_predicate(cx.tcx, *def)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| {
|
||||
cx.reveal_opaque(key)
|
||||
cx.reveal_opaque_key(key)
|
||||
});
|
||||
// Variants that depend on a disabled unstable feature.
|
||||
let is_unstable = matches!(
|
||||
@ -353,7 +401,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
|
||||
ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) },
|
||||
ty::Adt(..) | ty::Tuple(..) => {
|
||||
ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
|
||||
}
|
||||
ty::Ref(..) => ConstructorSet::Ref,
|
||||
ty::Never => ConstructorSet::NoConstructors,
|
||||
// This type is one for which we cannot list constructors, like `str` or `f64`.
|
||||
@ -379,7 +429,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub(crate) fn lower_pat_range_bdy(
|
||||
&self,
|
||||
bdy: PatRangeBoundary<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
) -> MaybeInfiniteInt {
|
||||
match bdy {
|
||||
PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
|
||||
@ -402,6 +452,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
|
||||
let singleton = |pat| std::slice::from_ref(self.pattern_arena.alloc(pat));
|
||||
let cx = self;
|
||||
let ty = cx.reveal_opaque_ty(pat.ty);
|
||||
let ctor;
|
||||
let fields: &[_];
|
||||
match &pat.kind {
|
||||
@ -414,19 +465,22 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
fields = singleton(self.lower_pat(subpattern));
|
||||
ctor = match pat.ty.kind() {
|
||||
ctor = match ty.kind() {
|
||||
// This is a box pattern.
|
||||
ty::Adt(adt, ..) if adt.is_box() => Struct,
|
||||
ty::Ref(..) => Ref,
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
|
||||
};
|
||||
}
|
||||
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
|
||||
match pat.ty.kind() {
|
||||
match ty.kind() {
|
||||
ty::Tuple(fs) => {
|
||||
ctor = Struct;
|
||||
let mut wilds: SmallVec<[_; 2]> =
|
||||
fs.iter().map(|ty| DeconstructedPat::wildcard(ty)).collect();
|
||||
let mut wilds: SmallVec<[_; 2]> = fs
|
||||
.iter()
|
||||
.map(|ty| cx.reveal_opaque_ty(ty))
|
||||
.map(|ty| DeconstructedPat::wildcard(ty))
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
wilds[pat.field.index()] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
@ -449,7 +503,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
let pat = if let Some(pat) = pattern {
|
||||
self.lower_pat(&pat.pattern)
|
||||
} else {
|
||||
DeconstructedPat::wildcard(args.type_at(0))
|
||||
DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0)))
|
||||
};
|
||||
ctor = Struct;
|
||||
fields = singleton(pat);
|
||||
@ -466,13 +520,12 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
||||
let mut field_id_to_id: Vec<Option<usize>> =
|
||||
(0..variant.fields.len()).map(|_| None).collect();
|
||||
let tys = cx
|
||||
.list_variant_nonhidden_fields(pat.ty, variant)
|
||||
.enumerate()
|
||||
.map(|(i, (field, ty))| {
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map(
|
||||
|(i, (field, ty))| {
|
||||
field_id_to_id[field.index()] = Some(i);
|
||||
ty
|
||||
});
|
||||
},
|
||||
);
|
||||
let mut wilds: SmallVec<[_; 2]> =
|
||||
tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
|
||||
for pat in subpatterns {
|
||||
@ -482,11 +535,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
fields = cx.pattern_arena.alloc_from_iter(wilds);
|
||||
}
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
|
||||
}
|
||||
}
|
||||
PatKind::Constant { value } => {
|
||||
match pat.ty.kind() {
|
||||
match ty.kind() {
|
||||
ty::Bool => {
|
||||
ctor = match value.try_eval_bool(cx.tcx, cx.param_env) {
|
||||
Some(b) => Bool(b),
|
||||
@ -497,7 +550,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
|
||||
Some(bits) => {
|
||||
let x = match *pat.ty.kind() {
|
||||
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)
|
||||
@ -540,7 +593,8 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// `Ref`), and has one field. That field has constructor `Str(value)` and no
|
||||
// subfields.
|
||||
// Note: `t` is `str`, not `&str`.
|
||||
let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat);
|
||||
let ty = self.reveal_opaque_ty(*t);
|
||||
let subpattern = DeconstructedPat::new(Str(*value), &[], ty, pat);
|
||||
ctor = Ref;
|
||||
fields = singleton(subpattern)
|
||||
}
|
||||
@ -559,7 +613,6 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
rustc_hir::RangeEnd::Included => RangeEnd::Included,
|
||||
rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
|
||||
};
|
||||
let ty = pat.ty;
|
||||
ctor = match ty.kind() {
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
let lo = cx.lower_pat_range_bdy(*lo, ty);
|
||||
@ -585,17 +638,17 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => bug!("invalid type for range pattern: {}", ty),
|
||||
_ => bug!("invalid type for range pattern: {}", ty.inner()),
|
||||
};
|
||||
fields = &[];
|
||||
}
|
||||
PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
|
||||
let array_len = match pat.ty.kind() {
|
||||
let array_len = match ty.kind() {
|
||||
ty::Array(_, length) => {
|
||||
Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize)
|
||||
}
|
||||
ty::Slice(_) => None,
|
||||
_ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
|
||||
_ => span_bug!(pat.span, "bad ty {:?} for slice pattern", ty),
|
||||
};
|
||||
let kind = if slice.is_some() {
|
||||
SliceKind::VarLen(prefix.len(), suffix.len())
|
||||
@ -624,7 +677,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
fields = &[];
|
||||
}
|
||||
}
|
||||
DeconstructedPat::new(ctor, fields, pat.ty, pat)
|
||||
DeconstructedPat::new(ctor, fields, ty, pat)
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes.
|
||||
@ -634,7 +687,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub(crate) fn hoist_pat_range_bdy(
|
||||
&self,
|
||||
miint: MaybeInfiniteInt,
|
||||
ty: Ty<'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
) -> PatRangeBoundary<'tcx> {
|
||||
use MaybeInfiniteInt::*;
|
||||
let tcx = self.tcx;
|
||||
@ -648,7 +701,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
};
|
||||
match Scalar::try_from_uint(bits, size) {
|
||||
Some(scalar) => {
|
||||
let value = mir::Const::from_scalar(tcx, scalar, ty);
|
||||
let value = mir::Const::from_scalar(tcx, scalar, ty.inner());
|
||||
PatRangeBoundary::Finite(value)
|
||||
}
|
||||
// The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value
|
||||
@ -662,7 +715,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes.
|
||||
pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx> {
|
||||
pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> {
|
||||
use MaybeInfiniteInt::*;
|
||||
let cx = self;
|
||||
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
|
||||
@ -693,10 +746,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
range.hi.minus_one()
|
||||
};
|
||||
let hi = cx.hoist_pat_range_bdy(hi, ty);
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
|
||||
};
|
||||
|
||||
Pat { ty, span: DUMMY_SP, kind }
|
||||
Pat { ty: ty.inner(), span: DUMMY_SP, kind }
|
||||
}
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
|
||||
/// appear in diagnostics, like float ranges.
|
||||
@ -768,7 +821,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
let suffix: Box<[_]> = subpatterns.collect();
|
||||
let wild = Pat::wildcard_from_ty(pat.ty());
|
||||
let wild = Pat::wildcard_from_ty(pat.ty().inner());
|
||||
PatKind::Slice {
|
||||
prefix: prefix.into_boxed_slice(),
|
||||
slice: Some(Box::new(wild)),
|
||||
@ -788,7 +841,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
Pat { ty: pat.ty(), span: DUMMY_SP, kind }
|
||||
Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind }
|
||||
}
|
||||
|
||||
/// Best-effort `Debug` implementation.
|
||||
@ -890,7 +943,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
type Ty = Ty<'tcx>;
|
||||
type Ty = RevealedTy<'tcx>;
|
||||
type VariantIdx = VariantIdx;
|
||||
type StrLit = Const<'tcx>;
|
||||
type ArmData = HirId;
|
||||
@ -900,10 +953,6 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
self.tcx.features().exhaustive_patterns
|
||||
}
|
||||
|
||||
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.reveal_opaque_ty(ty)
|
||||
}
|
||||
|
||||
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize {
|
||||
self.ctor_arity(ctor, ty)
|
||||
}
|
||||
|
@ -847,8 +847,11 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
|
||||
self.pats.len()
|
||||
}
|
||||
|
||||
fn head_opt(&self) -> Option<&'p DeconstructedPat<'p, Cx>> {
|
||||
self.pats.first().copied()
|
||||
}
|
||||
fn head(&self) -> &'p DeconstructedPat<'p, Cx> {
|
||||
self.pats[0]
|
||||
self.head_opt().unwrap()
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
|
||||
@ -1028,14 +1031,8 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
||||
matrix
|
||||
}
|
||||
|
||||
fn head_ty(&self, mcx: MatchCtxt<'_, 'p, Cx>) -> Option<Cx::Ty> {
|
||||
if self.column_count() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty = self.wildcard_row.head().ty();
|
||||
// FIXME(Nadrieril): `Cx` should only give us revealed types.
|
||||
Some(mcx.tycx.reveal_opaque_ty(ty))
|
||||
fn head_ty(&self) -> Option<Cx::Ty> {
|
||||
self.wildcard_row.head_opt().map(|pat| pat.ty())
|
||||
}
|
||||
fn column_count(&self) -> usize {
|
||||
self.wildcard_row.len()
|
||||
@ -1345,7 +1342,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
||||
return WitnessMatrix::empty();
|
||||
}
|
||||
|
||||
let Some(ty) = matrix.head_ty(mcx) else {
|
||||
let Some(ty) = matrix.head_ty() else {
|
||||
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
|
||||
// A row is useful iff it has no (unguarded) rows above it.
|
||||
for row in matrix.rows_mut() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user