This commit is contained in:
Nadrieril 2024-02-06 03:12:21 +01:00
parent ea38166390
commit 39441e4cdd

View File

@ -10,7 +10,7 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::{self, Const}; use rustc_middle::mir::{self, Const};
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
use rustc_session::lint; use rustc_session::lint;
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
@ -158,32 +158,19 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
} }
} }
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide pub(crate) fn variant_sub_tys(
// 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, &self,
ty: RevealedTy<'tcx>, ty: RevealedTy<'tcx>,
variant: &'tcx VariantDef, variant: &'tcx VariantDef,
) -> impl Iterator<Item = (FieldIdx, RevealedTy<'tcx>, bool)> + Captures<'p> + Captures<'_> ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_>
{ {
let cx = self; let ty::Adt(_, args) = ty.kind() else { bug!() };
let ty::Adt(adt, args) = ty.kind() else { bug!() }; variant.fields.iter().map(move |field| {
// Whether we must avoid matching the fields of this variant exhaustively. let ty = field.ty(self.tcx, args);
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
variant.fields.iter().enumerate().map(move |(i, field)| {
let ty = field.ty(cx.tcx, args);
// `field.ty()` doesn't normalize after instantiating. // `field.ty()` doesn't normalize after instantiating.
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); let ty = self.tcx.normalize_erasing_regions(self.param_env, ty);
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); let ty = self.reveal_opaque_ty(ty);
let is_uninhabited = (cx.tcx.features().exhaustive_patterns (field, ty)
|| cx.tcx.features().min_exhaustive_patterns)
&& cx.is_uninhabited(ty);
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
let ty = cx.reveal_opaque_ty(ty);
(FieldIdx::new(i), ty, skip)
}) })
} }
@ -230,9 +217,21 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
} else { } else {
let variant = let variant =
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
let tys = cx
.list_variant_nonhidden_fields(ty, variant) // In the cases of either a `#[non_exhaustive]` field list or a non-public
.map(|(_, ty, skip)| (ty, SkipField(skip))); // field, we skip uninhabited fields in order not to reveal the
// uninhabitedness of the whole variant.
let is_non_exhaustive =
variant.is_field_list_non_exhaustive() && !adt.did().is_local();
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.tcx.features().exhaustive_patterns
|| cx.tcx.features().min_exhaustive_patterns)
&& cx.is_uninhabited(*ty);
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
(ty, SkipField(skip))
});
cx.dropless_arena.alloc_from_iter(tys) cx.dropless_arena.alloc_from_iter(tys)
} }
} }
@ -269,9 +268,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
// 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.
1 1
} else { } else {
let variant = let variant_idx = RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt);
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); adt.variant(variant_idx).fields.len()
self.list_variant_nonhidden_fields(ty, variant).count()
} }
} }
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
@ -507,20 +505,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
}; };
let variant = let variant =
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
// For each field in the variant, we store the relevant index into `self.fields` if any. fields = cx
let mut field_id_to_id: Vec<Option<usize>> = .variant_sub_tys(ty, variant)
(0..variant.fields.len()).map(|_| None).collect(); .map(|(_, ty)| DeconstructedPat::wildcard(ty))
let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map( .collect();
|(i, (field, ty, _))| {
field_id_to_id[field.index()] = Some(i);
ty
},
);
fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
for pat in subpatterns { for pat in subpatterns {
if let Some(i) = field_id_to_id[pat.field.index()] { fields[pat.field.index()] = self.lower_pat(&pat.pattern);
fields[i] = self.lower_pat(&pat.pattern);
}
} }
} }
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
@ -762,11 +752,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
ty::Adt(adt_def, args) => { ty::Adt(adt_def, args) => {
let variant_index = let variant_index =
RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
let variant = &adt_def.variant(variant_index); let subpatterns = subpatterns
let subpatterns = cx .enumerate()
.list_variant_nonhidden_fields(*pat.ty(), variant) .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
.zip(subpatterns)
.map(|((field, _ty, _), pattern)| FieldPat { field, pattern })
.collect(); .collect();
if adt_def.is_enum() { if adt_def.is_enum() {