Skip to content

Commit 59fa40a

Browse files
committed
Filter out fields that should not be seen
This was previously done by giving those fields the type tyerr. This was a hack.
1 parent 70b3872 commit 59fa40a

File tree

1 file changed

+113
-48
lines changed

1 file changed

+113
-48
lines changed

src/librustc_mir_build/hair/pattern/_match.rs

Lines changed: 113 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ use rustc_hir::{HirId, RangeEnd};
242242
use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
243243
use rustc_middle::mir::Field;
244244
use rustc_middle::ty::layout::IntegerExt;
245-
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef};
245+
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeFoldable};
246246
use rustc_session::lint;
247247
use rustc_span::{Span, DUMMY_SP};
248248
use rustc_target::abi::{Integer, Size, VariantIdx};
@@ -591,7 +591,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
591591
}
592592
}
593593

594-
// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
594+
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
595595
crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
596596
match ty.kind {
597597
ty::Adt(def, ..) => {
@@ -600,15 +600,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
600600
_ => false,
601601
}
602602
}
603-
604-
// Returns whether the given variant is from another crate and has its fields declared
605-
// `#[non_exhaustive]`.
606-
fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool {
607-
match ty.kind {
608-
ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(),
609-
_ => false,
610-
}
611-
}
612603
}
613604

614605
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -876,7 +867,7 @@ impl<'tcx> Constructor<'tcx> {
876867
ty: Ty<'tcx>,
877868
fields: Fields<'p, 'tcx>,
878869
) -> Pat<'tcx> {
879-
let mut subpatterns = fields.into_iter().cloned();
870+
let mut subpatterns = fields.all_patterns();
880871

881872
let pat = match self {
882873
Single | Variant(_) => match ty.kind {
@@ -945,12 +936,45 @@ impl<'tcx> Constructor<'tcx> {
945936
}
946937
}
947938

939+
#[derive(Debug, Copy, Clone)]
940+
enum FilteredField<'p, 'tcx> {
941+
Kept(&'p Pat<'tcx>),
942+
Hidden(Ty<'tcx>),
943+
}
944+
945+
impl<'p, 'tcx> FilteredField<'p, 'tcx> {
946+
fn kept(self) -> Option<&'p Pat<'tcx>> {
947+
match self {
948+
FilteredField::Kept(p) => Some(p),
949+
FilteredField::Hidden(_) => None,
950+
}
951+
}
952+
953+
fn to_pattern(self) -> Pat<'tcx> {
954+
match self {
955+
FilteredField::Kept(p) => p.clone(),
956+
FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty),
957+
}
958+
}
959+
}
960+
948961
/// A value can be decomposed into a constructor applied to some fields. This struct represents
949962
/// those fields, generalized to allow patterns in each field. See also `Constructor`.
963+
///
964+
/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is
965+
/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we
966+
/// still need to have those fields back when going to/from a `Pat`. Mot of this is handled
967+
/// automatically in `Fields`, but when constructing or deconstructing fields you need to use the
968+
/// correct method. As a rule, when going to/from the matrix, use the filtered field list; when
969+
/// going to/from `Pat`, use the full field list.
970+
/// This filtering is uncommon in practice, because uninhabited fields are rarely used.
950971
#[derive(Debug, Clone)]
951972
enum Fields<'p, 'tcx> {
973+
/// Lists of patterns that don't contain any filtered fields.
952974
Slice(&'p [Pat<'tcx>]),
953975
Vec(SmallVec<[&'p Pat<'tcx>; 2]>),
976+
/// Patterns where some of the fields need to be hidden.
977+
Filtered(SmallVec<[FilteredField<'p, 'tcx>; 2]>),
954978
}
955979

956980
impl<'p, 'tcx> Fields<'p, 'tcx> {
@@ -964,7 +988,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
964988
Fields::Slice(std::slice::from_ref(pat))
965989
}
966990

967-
fn from_vec(pats: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
991+
/// Construct a new `Fields` from the given patterns. You must be sure those patterns can't
992+
/// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`.
993+
fn from_vec_unfiltered(pats: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
968994
Fields::Vec(pats)
969995
}
970996

@@ -999,26 +1025,40 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
9991025
Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
10001026
} else {
10011027
let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)];
1002-
let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant);
1003-
Fields::wildcards_from_tys(
1004-
cx,
1005-
variant.fields.iter().map(|field| {
1006-
let ty = field.ty(cx.tcx, substs);
1007-
let is_visible = adt.is_enum()
1008-
|| field.vis.is_accessible_from(cx.module, cx.tcx);
1009-
let is_inhabited = !cx.is_uninhabited(ty);
1010-
// Treat all uninhabited non-visible fields as `TyErr`. They can't
1011-
// appear in any other pattern from this match (because they are
1012-
// private), so their type does not matter - but we don't want
1013-
// to know they are uninhabited.
1014-
// Also treat all uninhabited types in non-exhaustive variants as
1015-
// `TyErr`.
1016-
let allowed_to_inspect =
1017-
is_inhabited || (is_visible && !is_non_exhaustive);
1018-
1019-
if allowed_to_inspect { ty } else { cx.tcx.types.err }
1020-
}),
1021-
)
1028+
// Whether we must not match the fields of this variant exhaustively.
1029+
let is_non_exhaustive =
1030+
variant.is_field_list_non_exhaustive() && !adt.did.is_local();
1031+
let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs));
1032+
// In the following cases, we don't need to filter out any fields. This is
1033+
// the vast majority of real cases, since uninhabited fields are uncommon.
1034+
let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive)
1035+
|| !field_tys.clone().any(|ty| cx.is_uninhabited(ty));
1036+
1037+
if has_no_hidden_fields {
1038+
Fields::wildcards_from_tys(cx, field_tys)
1039+
} else {
1040+
let fields = variant
1041+
.fields
1042+
.iter()
1043+
.map(|field| {
1044+
let ty = field.ty(cx.tcx, substs);
1045+
let is_visible = adt.is_enum()
1046+
|| field.vis.is_accessible_from(cx.module, cx.tcx);
1047+
let is_uninhabited = cx.is_uninhabited(ty);
1048+
1049+
// In the cases of either a `#[non_exhaustive]` field list
1050+
// or a non-public field, we hide uninhabited fields in
1051+
// order not to reveal the uninhabitedness of the whole
1052+
// variant.
1053+
if is_uninhabited && (!is_visible || is_non_exhaustive) {
1054+
FilteredField::Hidden(ty)
1055+
} else {
1056+
FilteredField::Kept(wildcard_from_ty(ty))
1057+
}
1058+
})
1059+
.collect();
1060+
Fields::Filtered(fields)
1061+
}
10221062
}
10231063
}
10241064
_ => Fields::empty(),
@@ -1038,29 +1078,30 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10381078
match self {
10391079
Fields::Slice(pats) => pats.len(),
10401080
Fields::Vec(pats) => pats.len(),
1081+
Fields::Filtered(fields) => fields.iter().filter(|p| p.kept().is_some()).count(),
10411082
}
10421083
}
10431084

1044-
fn into_iter(self) -> impl Iterator<Item = &'p Pat<'tcx>> {
1045-
let pats: SmallVec<_> = match self {
1046-
Fields::Slice(pats) => pats.iter().collect(),
1047-
Fields::Vec(pats) => pats,
1085+
/// Returns the complete list of patterns, including hidden fields.
1086+
fn all_patterns(self) -> impl Iterator<Item = Pat<'tcx>> {
1087+
let pats: SmallVec<[_; 2]> = match self {
1088+
Fields::Slice(pats) => pats.iter().cloned().collect(),
1089+
Fields::Vec(pats) => pats.into_iter().cloned().collect(),
1090+
Fields::Filtered(fields) => {
1091+
// We don't skip any fields here.
1092+
fields.into_iter().map(|p| p.to_pattern()).collect()
1093+
}
10481094
};
10491095
pats.into_iter()
10501096
}
10511097

10521098
/// Overrides some of the fields with the provided patterns.
10531099
fn replace_with_fieldpats(
10541100
&self,
1055-
cx: &MatchCheckCtxt<'p, 'tcx>,
10561101
new_pats: impl IntoIterator<Item = &'p FieldPat<'tcx>>,
1057-
is_non_exhaustive: bool,
10581102
) -> Self {
10591103
self.replace_fields_indexed(
1060-
new_pats
1061-
.into_iter()
1062-
.map(|pat| (pat.field.index(), &pat.pattern))
1063-
.filter(|(_, pat)| !(is_non_exhaustive && cx.is_uninhabited(pat.ty))),
1104+
new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)),
10641105
)
10651106
}
10661107

@@ -1080,6 +1121,13 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10801121
pats[i] = pat
10811122
}
10821123
}
1124+
Fields::Filtered(fields) => {
1125+
for (i, pat) in new_pats {
1126+
if let FilteredField::Kept(p) = &mut fields[i] {
1127+
*p = pat
1128+
}
1129+
}
1130+
}
10831131
Fields::Slice(_) => unreachable!(),
10841132
}
10851133
fields
@@ -1093,7 +1141,21 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10931141
pats: impl IntoIterator<Item = Pat<'tcx>>,
10941142
) -> Self {
10951143
let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats);
1096-
Fields::Slice(pats)
1144+
1145+
match self {
1146+
Fields::Filtered(fields) => {
1147+
let mut pats = pats.iter();
1148+
let mut fields = fields.clone();
1149+
for f in &mut fields {
1150+
if let FilteredField::Kept(p) = f {
1151+
// We take one input pattern for each `Kept` field, in order.
1152+
*p = pats.next().unwrap();
1153+
}
1154+
}
1155+
Fields::Filtered(fields)
1156+
}
1157+
_ => Fields::Slice(pats),
1158+
}
10971159
}
10981160

10991161
fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> {
@@ -1103,6 +1165,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11031165
pats.extend_from_slice(stack);
11041166
pats
11051167
}
1168+
Fields::Filtered(fields) => {
1169+
// We skip hidden fields here
1170+
fields.into_iter().filter_map(|p| p.kept()).chain(stack.iter().copied()).collect()
1171+
}
11061172
};
11071173
PatStack::from_vec(pats)
11081174
}
@@ -2411,12 +2477,11 @@ fn specialize_one_pattern<'p, 'tcx>(
24112477
if constructor != &Variant(variant.def_id) {
24122478
return None;
24132479
}
2414-
let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant);
2415-
Some(ctor_wild_subpatterns.replace_with_fieldpats(cx, subpatterns, is_non_exhaustive))
2480+
Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns))
24162481
}
24172482

24182483
PatKind::Leaf { ref subpatterns } => {
2419-
Some(ctor_wild_subpatterns.replace_with_fieldpats(cx, subpatterns, false))
2484+
Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns))
24202485
}
24212486

24222487
PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)),
@@ -2485,7 +2550,7 @@ fn specialize_one_pattern<'p, 'tcx>(
24852550
Some(&*cx.pattern_arena.alloc(pattern))
24862551
})
24872552
.collect::<Option<_>>()?;
2488-
Some(Fields::from_vec(pats))
2553+
Some(Fields::from_vec_unfiltered(pats))
24892554
}
24902555

24912556
PatKind::Constant { .. } | PatKind::Range { .. } => {

0 commit comments

Comments
 (0)