1
Fork 0

Statically enforce revealing of opaques

This commit is contained in:
Nadrieril 2023-12-26 13:21:35 +01:00
parent e51e98dde6
commit c35272058d
5 changed files with 132 additions and 94 deletions

View file

@ -554,7 +554,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
let cx = self.new_cx(refutability, None, scrut, pat.span); let cx = self.new_cx(refutability, None, scrut, pat.span);
let pat = self.lower_pattern(&cx, pat)?; let pat = self.lower_pattern(&cx, pat)?;
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }]; 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)) Ok((cx, report))
} }
@ -972,7 +972,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
} }
} else if ty == cx.tcx.types.str_ { } else if ty == cx.tcx.types.str_ {
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary"); 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")); 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>>, non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
) { ) {
if matches!(pat.ctor(), Constructor::NonExhaustive) { 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 let Constructor::IntRange(range) = pat.ctor() {
if cx.is_range_beyond_boundaries(range, pat.ty()) { 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`. // 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() pat.iter_fields()

View file

@ -61,8 +61,6 @@ pub trait TypeCx: Sized + fmt::Debug {
/// Extra data to store in a pattern. /// Extra data to store in a pattern.
type PatData: Clone; 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; fn is_exhaustive_patterns_feature_on(&self) -> bool;
/// The number of fields for this constructor. /// The number of fields for this constructor.
@ -114,6 +112,7 @@ pub fn analyze_match<'p, 'tcx>(
) -> rustc::UsefulnessReport<'p, 'tcx> { ) -> rustc::UsefulnessReport<'p, 'tcx> {
// Arena to store the extra wildcards we construct during analysis. // Arena to store the extra wildcards we construct during analysis.
let wildcard_arena = tycx.pattern_arena; 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 scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
let cx = MatchCtxt { tycx, wildcard_arena }; let cx = MatchCtxt { tycx, wildcard_arena };

View file

@ -1,7 +1,7 @@
use smallvec::SmallVec; use smallvec::SmallVec;
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty;
use rustc_session::lint; use rustc_session::lint;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::Span; use rustc_span::Span;
@ -12,10 +12,9 @@ use crate::errors::{
OverlappingRangeEndpoints, Uncovered, OverlappingRangeEndpoints, Uncovered,
}; };
use crate::rustc::{ use crate::rustc::{
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
SplitConstructorSet, WitnessPat, SplitConstructorSet, WitnessPat,
}; };
use crate::TypeCx;
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
/// inspect the same subvalue/place". /// inspect the same subvalue/place".
@ -48,14 +47,8 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.patterns.is_empty() self.patterns.is_empty()
} }
fn head_ty(&self, cx: MatchCtxt<'_, 'p, 'tcx>) -> Option<Ty<'tcx>> { fn head_ty(&self) -> Option<RevealedTy<'tcx>> {
if self.patterns.len() == 0 { self.patterns.first().map(|pat| pat.ty())
return None;
}
let ty = self.patterns[0].ty();
// FIXME(Nadrieril): `Cx` should only give us revealed types.
Some(cx.tycx.reveal_opaque_ty(ty))
} }
/// Do constructor splitting on the constructors of the column. /// 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>, cx: MatchCtxt<'a, 'p, 'tcx>,
column: &PatternColumn<'p, 'tcx>, column: &PatternColumn<'p, 'tcx>,
) -> Vec<WitnessPat<'p, 'tcx>> { ) -> Vec<WitnessPat<'p, 'tcx>> {
let Some(ty) = column.head_ty(cx) else { let Some(ty) = column.head_ty() else {
return Vec::new(); return Vec::new();
}; };
let pcx = &PlaceCtxt::new_dummy(cx, ty); 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>, cx: MatchCtxt<'a, 'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>], arms: &[MatchArm<'p, 'tcx>],
pat_column: &PatternColumn<'p, 'tcx>, pat_column: &PatternColumn<'p, 'tcx>,
scrut_ty: Ty<'tcx>, scrut_ty: RevealedTy<'tcx>,
) { ) {
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
if !matches!( if !matches!(
@ -182,7 +175,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
rcx.match_lint_level, rcx.match_lint_level,
rcx.scrut_span, rcx.scrut_span,
NonExhaustiveOmittedPattern { NonExhaustiveOmittedPattern {
scrut_ty, scrut_ty: scrut_ty.inner(),
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses), 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>, cx: MatchCtxt<'a, 'p, 'tcx>,
column: &PatternColumn<'p, 'tcx>, column: &PatternColumn<'p, 'tcx>,
) { ) {
let Some(ty) = column.head_ty(cx) else { let Some(ty) = column.head_ty() else {
return; return;
}; };
let pcx = &PlaceCtxt::new_dummy(cx, ty); let pcx = &PlaceCtxt::new_dummy(cx, ty);

View file

@ -41,6 +41,30 @@ pub type UsefulnessReport<'p, 'tcx> =
crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>; crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<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)] #[derive(Clone)]
pub struct RustcMatchCheckCtxt<'p, 'tcx> { pub struct RustcMatchCheckCtxt<'p, 'tcx> {
pub tcx: TyCtxt<'tcx>, pub tcx: TyCtxt<'tcx>,
@ -74,20 +98,48 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> {
} }
impl<'p, 'tcx> 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) 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 { pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque( !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
self.tcx, self.tcx,
self.param_env, self.param_env,
self.module, 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]`. /// 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() { match ty.kind() {
ty::Adt(def, ..) => { ty::Adt(def, ..) => {
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local() 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 /// 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). /// `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() && { ty.is_ptr_sized_integral() && {
// The two invalid ranges are `NegInfinity..isize::MIN` (represented as // The two invalid ranges are `NegInfinity..isize::MIN` (represented as
// `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy` // `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 // 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. // uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
// This lists the fields we keep along with their types. // This lists the fields we keep along with their types.
pub(crate) fn list_variant_nonhidden_fields( pub(crate) fn list_variant_nonhidden_fields(
&self, &self,
ty: Ty<'tcx>, ty: RevealedTy<'tcx>,
variant: &'tcx VariantDef, 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 cx = self;
let ty::Adt(adt, args) = ty.kind() else { bug!() }; let ty::Adt(adt, args) = ty.kind() else { bug!() };
// Whether we must not match the fields of this variant exhaustively. // 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) { if is_uninhabited && (!is_visible || is_non_exhaustive) {
None None
} else { } else {
let ty = cx.reveal_opaque_ty(ty);
Some((FieldIdx::new(i), 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 /// Returns the types of the fields for a given constructor. The result must have a length of
/// `ctor.arity()`. /// `ctor.arity()`.
#[instrument(level = "trace", skip(self))] #[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; let cx = self;
match ctor { match ctor {
Struct | Variant(_) | UnionField => match ty.kind() { 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) => { ty::Adt(adt, args) => {
if adt.is_box() { if adt.is_box() {
// The only legal patterns of type `Box` (outside `std`) are `_` and 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. // 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 { } else {
let variant = let variant =
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); &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:?}"), _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
}, },
Ref => match ty.kind() { 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:?}"), _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
}, },
Slice(slice) => match *ty.kind() { Slice(slice) => match *ty.kind() {
ty::Slice(ty) | ty::Array(ty, _) => { ty::Slice(ty) | ty::Array(ty, _) => {
let arity = slice.arity(); 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), _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
}, },
@ -217,7 +265,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
} }
/// The number of fields for this constructor. /// 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 { match ctor {
Struct | Variant(_) | UnionField => match ty.kind() { Struct | Variant(_) | UnionField => match ty.kind() {
ty::Tuple(fs) => fs.len(), ty::Tuple(fs) => fs.len(),
@ -254,7 +302,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
/// ///
/// See [`crate::constructor`] for considerations of emptiness. /// See [`crate::constructor`] for considerations of emptiness.
#[instrument(level = "debug", skip(self), ret)] #[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 cx = self;
let make_uint_range = |start, end| { let make_uint_range = |start, end| {
IntRange::from_range( IntRange::from_range(
@ -328,7 +376,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
.inhabited_predicate(cx.tcx, *def) .inhabited_predicate(cx.tcx, *def)
.instantiate(cx.tcx, args) .instantiate(cx.tcx, args)
.apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| { .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. // Variants that depend on a disabled unstable feature.
let is_unstable = matches!( 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(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::Ref(..) => ConstructorSet::Ref,
ty::Never => ConstructorSet::NoConstructors, ty::Never => ConstructorSet::NoConstructors,
// This type is one for which we cannot list constructors, like `str` or `f64`. // 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( pub(crate) fn lower_pat_range_bdy(
&self, &self,
bdy: PatRangeBoundary<'tcx>, bdy: PatRangeBoundary<'tcx>,
ty: Ty<'tcx>, ty: RevealedTy<'tcx>,
) -> MaybeInfiniteInt { ) -> MaybeInfiniteInt {
match bdy { match bdy {
PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity, 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> { 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 singleton = |pat| std::slice::from_ref(self.pattern_arena.alloc(pat));
let cx = self; let cx = self;
let ty = cx.reveal_opaque_ty(pat.ty);
let ctor; let ctor;
let fields: &[_]; let fields: &[_];
match &pat.kind { match &pat.kind {
@ -414,19 +465,22 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
} }
PatKind::Deref { subpattern } => { PatKind::Deref { subpattern } => {
fields = singleton(self.lower_pat(subpattern)); fields = singleton(self.lower_pat(subpattern));
ctor = match pat.ty.kind() { ctor = match ty.kind() {
// This is a box pattern. // This is a box pattern.
ty::Adt(adt, ..) if adt.is_box() => Struct, ty::Adt(adt, ..) if adt.is_box() => Struct,
ty::Ref(..) => Ref, 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, .. } => { PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
match pat.ty.kind() { match ty.kind() {
ty::Tuple(fs) => { ty::Tuple(fs) => {
ctor = Struct; ctor = Struct;
let mut wilds: SmallVec<[_; 2]> = let mut wilds: SmallVec<[_; 2]> = fs
fs.iter().map(|ty| DeconstructedPat::wildcard(ty)).collect(); .iter()
.map(|ty| cx.reveal_opaque_ty(ty))
.map(|ty| DeconstructedPat::wildcard(ty))
.collect();
for pat in subpatterns { for pat in subpatterns {
wilds[pat.field.index()] = self.lower_pat(&pat.pattern); 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 { let pat = if let Some(pat) = pattern {
self.lower_pat(&pat.pattern) self.lower_pat(&pat.pattern)
} else { } else {
DeconstructedPat::wildcard(args.type_at(0)) DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0)))
}; };
ctor = Struct; ctor = Struct;
fields = singleton(pat); 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. // 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>> = let mut field_id_to_id: Vec<Option<usize>> =
(0..variant.fields.len()).map(|_| None).collect(); (0..variant.fields.len()).map(|_| None).collect();
let tys = cx let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map(
.list_variant_nonhidden_fields(pat.ty, variant) |(i, (field, ty))| {
.enumerate()
.map(|(i, (field, ty))| {
field_id_to_id[field.index()] = Some(i); field_id_to_id[field.index()] = Some(i);
ty ty
}); },
);
let mut wilds: SmallVec<[_; 2]> = let mut wilds: SmallVec<[_; 2]> =
tys.map(|ty| DeconstructedPat::wildcard(ty)).collect(); tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
for pat in subpatterns { for pat in subpatterns {
@ -482,11 +535,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
} }
fields = cx.pattern_arena.alloc_from_iter(wilds); 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 } => { PatKind::Constant { value } => {
match pat.ty.kind() { match ty.kind() {
ty::Bool => { ty::Bool => {
ctor = match value.try_eval_bool(cx.tcx, cx.param_env) { ctor = match value.try_eval_bool(cx.tcx, cx.param_env) {
Some(b) => Bool(b), Some(b) => Bool(b),
@ -497,7 +550,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
ty::Char | ty::Int(_) | ty::Uint(_) => { ty::Char | ty::Int(_) | ty::Uint(_) => {
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) { ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
Some(bits) => { Some(bits) => {
let x = match *pat.ty.kind() { let x = match *ty.kind() {
ty::Int(ity) => { ty::Int(ity) => {
let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
MaybeInfiniteInt::new_finite_int(bits, size) 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 // `Ref`), and has one field. That field has constructor `Str(value)` and no
// subfields. // subfields.
// Note: `t` is `str`, not `&str`. // 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; ctor = Ref;
fields = singleton(subpattern) fields = singleton(subpattern)
} }
@ -559,7 +613,6 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
rustc_hir::RangeEnd::Included => RangeEnd::Included, rustc_hir::RangeEnd::Included => RangeEnd::Included,
rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded, rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
}; };
let ty = pat.ty;
ctor = match ty.kind() { ctor = match ty.kind() {
ty::Char | ty::Int(_) | ty::Uint(_) => { ty::Char | ty::Int(_) | ty::Uint(_) => {
let lo = cx.lower_pat_range_bdy(*lo, ty); 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 = &[]; fields = &[];
} }
PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { 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) => { ty::Array(_, length) => {
Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize) Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize)
} }
ty::Slice(_) => None, 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() { let kind = if slice.is_some() {
SliceKind::VarLen(prefix.len(), suffix.len()) SliceKind::VarLen(prefix.len(), suffix.len())
@ -624,7 +677,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
fields = &[]; fields = &[];
} }
} }
DeconstructedPat::new(ctor, fields, pat.ty, pat) DeconstructedPat::new(ctor, fields, ty, pat)
} }
/// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes. /// 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( pub(crate) fn hoist_pat_range_bdy(
&self, &self,
miint: MaybeInfiniteInt, miint: MaybeInfiniteInt,
ty: Ty<'tcx>, ty: RevealedTy<'tcx>,
) -> PatRangeBoundary<'tcx> { ) -> PatRangeBoundary<'tcx> {
use MaybeInfiniteInt::*; use MaybeInfiniteInt::*;
let tcx = self.tcx; let tcx = self.tcx;
@ -648,7 +701,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
}; };
match Scalar::try_from_uint(bits, size) { match Scalar::try_from_uint(bits, size) {
Some(scalar) => { Some(scalar) => {
let value = mir::Const::from_scalar(tcx, scalar, ty); let value = mir::Const::from_scalar(tcx, scalar, ty.inner());
PatRangeBoundary::Finite(value) PatRangeBoundary::Finite(value)
} }
// The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum 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. /// 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::*; use MaybeInfiniteInt::*;
let cx = self; let cx = self;
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) { let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
@ -693,10 +746,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
range.hi.minus_one() range.hi.minus_one()
}; };
let hi = cx.hoist_pat_range_bdy(hi, ty); 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 /// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
/// appear in diagnostics, like float ranges. /// appear in diagnostics, like float ranges.
@ -768,7 +821,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
} }
} }
let suffix: Box<[_]> = subpatterns.collect(); let suffix: Box<[_]> = subpatterns.collect();
let wild = Pat::wildcard_from_ty(pat.ty()); let wild = Pat::wildcard_from_ty(pat.ty().inner());
PatKind::Slice { PatKind::Slice {
prefix: prefix.into_boxed_slice(), prefix: prefix.into_boxed_slice(),
slice: Some(Box::new(wild)), 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. /// Best-effort `Debug` implementation.
@ -890,7 +943,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
} }
impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
type Ty = Ty<'tcx>; type Ty = RevealedTy<'tcx>;
type VariantIdx = VariantIdx; type VariantIdx = VariantIdx;
type StrLit = Const<'tcx>; type StrLit = Const<'tcx>;
type ArmData = HirId; type ArmData = HirId;
@ -900,10 +953,6 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
self.tcx.features().exhaustive_patterns 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 { fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize {
self.ctor_arity(ctor, ty) self.ctor_arity(ctor, ty)
} }

View file

@ -847,8 +847,11 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
self.pats.len() self.pats.len()
} }
fn head_opt(&self) -> Option<&'p DeconstructedPat<'p, Cx>> {
self.pats.first().copied()
}
fn head(&self) -> &'p DeconstructedPat<'p, Cx> { 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<'_> { fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
@ -1028,14 +1031,8 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
matrix matrix
} }
fn head_ty(&self, mcx: MatchCtxt<'_, 'p, Cx>) -> Option<Cx::Ty> { fn head_ty(&self) -> Option<Cx::Ty> {
if self.column_count() == 0 { self.wildcard_row.head_opt().map(|pat| pat.ty())
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 column_count(&self) -> usize { fn column_count(&self) -> usize {
self.wildcard_row.len() self.wildcard_row.len()
@ -1345,7 +1342,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
return WitnessMatrix::empty(); 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 (). // 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. // A row is useful iff it has no (unguarded) rows above it.
for row in matrix.rows_mut() { for row in matrix.rows_mut() {