1
Fork 0

Simplify field filtering

This commit is contained in:
Nadrieril 2020-12-19 07:11:00 +00:00
parent 53e03fb7c1
commit 1c176d1150
2 changed files with 57 additions and 65 deletions

View file

@ -1058,41 +1058,30 @@ impl<'tcx> SplitWildcard<'tcx> {
} }
/// Some fields need to be explicitly hidden away in certain cases; see the comment above the /// Some fields need to be explicitly hidden away in certain cases; see the comment above the
/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden /// `Fields` struct. This struct represents such a potentially-hidden field.
/// we still keep its type around.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(super) enum FilteredField<'p, 'tcx> { pub(super) enum FilteredField<'p, 'tcx> {
Kept(&'p Pat<'tcx>), Kept(&'p Pat<'tcx>),
Hidden(Ty<'tcx>), Hidden,
} }
impl<'p, 'tcx> FilteredField<'p, 'tcx> { impl<'p, 'tcx> FilteredField<'p, 'tcx> {
fn kept(self) -> Option<&'p Pat<'tcx>> { fn kept(self) -> Option<&'p Pat<'tcx>> {
match self { match self {
FilteredField::Kept(p) => Some(p), FilteredField::Kept(p) => Some(p),
FilteredField::Hidden(_) => None, FilteredField::Hidden => None,
}
}
fn to_pattern(self) -> Pat<'tcx> {
match self {
FilteredField::Kept(p) => p.clone(),
FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty),
} }
} }
} }
/// A value can be decomposed into a constructor applied to some fields. This struct represents /// A value can be decomposed into a constructor applied to some fields. This struct represents
/// those fields, generalized to allow patterns in each field. See also `Constructor`. /// those fields, generalized to allow patterns in each field. See also `Constructor`.
/// This is constructed from a constructor using [`Fields::wildcards()`].
/// ///
/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is /// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is
/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we /// uninhabited. For that, we filter these fields out of the matrix. This is handled automatically
/// still need to have those fields back when going to/from a `Pat`. Most of this is handled /// in `Fields`. This filtering is uncommon in practice, because uninhabited fields are rare used,
/// automatically in `Fields`, but when constructing or deconstructing `Fields` you need to be /// so we avoid it when possible to preserve performance.
/// careful. As a rule, when going to/from the matrix, use the filtered field list; when going
/// to/from `Pat`, use the full field list.
/// This filtering is uncommon in practice, because uninhabited fields are rarely used, so we avoid
/// it when possible to preserve performance.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(super) enum Fields<'p, 'tcx> { pub(super) enum Fields<'p, 'tcx> {
/// Lists of patterns that don't contain any filtered fields. /// Lists of patterns that don't contain any filtered fields.
@ -1101,21 +1090,19 @@ pub(super) enum Fields<'p, 'tcx> {
/// have not measured if it really made a difference. /// have not measured if it really made a difference.
Slice(&'p [Pat<'tcx>]), Slice(&'p [Pat<'tcx>]),
Vec(SmallVec<[&'p Pat<'tcx>; 2]>), Vec(SmallVec<[&'p Pat<'tcx>; 2]>),
/// Patterns where some of the fields need to be hidden. `kept_count` caches the number of /// Patterns where some of the fields need to be hidden. For all intents and purposes we only
/// non-hidden fields. /// care about the non-hidden fields. We need to keep the real field index for those fields;
/// we're morally storing a `Vec<(usize, &Pat)>` but what we do is more convenient.
/// `len` counts the number of non-hidden fields
Filtered { Filtered {
fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>,
kept_count: usize, len: usize,
}, },
} }
impl<'p, 'tcx> Fields<'p, 'tcx> { impl<'p, 'tcx> Fields<'p, 'tcx> {
fn empty() -> Self { /// Internal use. Use `Fields::wildcards()` instead.
Fields::Slice(&[]) /// Must not be used if the pattern is a field of a struct/tuple/variant.
}
/// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field
/// of a struct/tuple/variant.
fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self {
Fields::Slice(std::slice::from_ref(pat)) Fields::Slice(std::slice::from_ref(pat))
} }
@ -1160,7 +1147,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
if has_no_hidden_fields { if has_no_hidden_fields {
Fields::wildcards_from_tys(cx, field_tys) Fields::wildcards_from_tys(cx, field_tys)
} else { } else {
let mut kept_count = 0; let mut len = 0;
let fields = variant let fields = variant
.fields .fields
.iter() .iter()
@ -1175,14 +1162,14 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
// order not to reveal the uninhabitedness of the whole // order not to reveal the uninhabitedness of the whole
// variant. // variant.
if is_uninhabited && (!is_visible || is_non_exhaustive) { if is_uninhabited && (!is_visible || is_non_exhaustive) {
FilteredField::Hidden(ty) FilteredField::Hidden
} else { } else {
kept_count += 1; len += 1;
FilteredField::Kept(wildcard_from_ty(ty)) FilteredField::Kept(wildcard_from_ty(ty))
} }
}) })
.collect(); .collect();
Fields::Filtered { fields, kept_count } Fields::Filtered { fields, len }
} }
} }
} }
@ -1196,7 +1183,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty), _ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
}, },
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing
| Wildcard => Fields::empty(), | Wildcard => Fields::Slice(&[]),
}; };
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret ret
@ -1218,14 +1205,16 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
/// `self`: `[false]` /// `self`: `[false]`
/// returns `Some(false)` /// returns `Some(false)`
pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> { pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> {
let mut subpatterns = self.all_patterns(); let subpatterns_and_indices = self.patterns_and_indices();
let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned();
let pat = match ctor { let pat = match ctor {
Single | Variant(_) => match pcx.ty.kind() { Single | Variant(_) => match pcx.ty.kind() {
ty::Adt(..) | ty::Tuple(..) => { ty::Adt(..) | ty::Tuple(..) => {
let subpatterns = subpatterns // We want the real indices here.
.enumerate() let subpatterns = subpatterns_and_indices
.map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) .iter()
.map(|&(field, p)| FieldPat { field, pattern: p.clone() })
.collect(); .collect();
if let ty::Adt(adt, substs) = pcx.ty.kind() { if let ty::Adt(adt, substs) = pcx.ty.kind() {
@ -1290,39 +1279,42 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) } Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) }
} }
/// Returns the number of patterns from the viewpoint of match-checking, i.e. excluding hidden /// Returns the number of patterns. This is the same as the arity of the constructor used to
/// fields. This is what we want in most cases in this file, the only exception being /// construct `self`.
/// conversion to/from `Pat`.
pub(super) fn len(&self) -> usize { pub(super) fn len(&self) -> usize {
match self { match self {
Fields::Slice(pats) => pats.len(), Fields::Slice(pats) => pats.len(),
Fields::Vec(pats) => pats.len(), Fields::Vec(pats) => pats.len(),
Fields::Filtered { kept_count, .. } => *kept_count, Fields::Filtered { len, .. } => *len,
} }
} }
/// Returns the complete list of patterns, including hidden fields. /// Returns the list of patterns along with the corresponding field indices.
fn all_patterns(self) -> impl Iterator<Item = Pat<'tcx>> { fn patterns_and_indices(&self) -> SmallVec<[(Field, &'p Pat<'tcx>); 2]> {
let pats: SmallVec<[_; 2]> = match self { match self {
Fields::Slice(pats) => pats.iter().cloned().collect(), Fields::Slice(pats) => {
Fields::Vec(pats) => pats.into_iter().cloned().collect(), pats.iter().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
}
Fields::Vec(pats) => {
pats.iter().copied().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
}
Fields::Filtered { fields, .. } => { Fields::Filtered { fields, .. } => {
// We don't skip any fields here. // Indices must be relative to the full list of patterns
fields.into_iter().map(|p| p.to_pattern()).collect() fields
.iter()
.enumerate()
.filter_map(|(i, p)| Some((Field::new(i), p.kept()?)))
.collect()
}
} }
};
pats.into_iter()
} }
/// Returns the filtered list of patterns, not including hidden fields. /// Returns the list of patterns.
pub(super) fn filtered_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> { pub(super) fn into_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> {
match self { match self {
Fields::Slice(pats) => pats.iter().collect(), Fields::Slice(pats) => pats.iter().collect(),
Fields::Vec(pats) => pats, Fields::Vec(pats) => pats,
Fields::Filtered { fields, .. } => { Fields::Filtered { fields, .. } => fields.iter().filter_map(|p| p.kept()).collect(),
// We skip hidden fields here
fields.into_iter().filter_map(|p| p.kept()).collect()
}
} }
} }
@ -1338,10 +1330,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
} }
/// Overrides some of the fields with the provided patterns. This is used when a pattern /// Overrides some of the fields with the provided patterns. This is used when a pattern
/// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start
/// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry /// with a `Fields` that is just one wildcard per field of the `Foo` struct, and override the
/// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns /// entry corresponding to `field1` with the pattern `Some(_)`. This is also used for slice
/// for the same reason. /// patterns for the same reason.
fn replace_fields_indexed( fn replace_fields_indexed(
&self, &self,
new_pats: impl IntoIterator<Item = (usize, &'p Pat<'tcx>)>, new_pats: impl IntoIterator<Item = (usize, &'p Pat<'tcx>)>,
@ -1369,8 +1361,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
fields fields
} }
/// Replaces contained fields with the given filtered list of patterns, e.g. taken from the /// Replaces contained fields with the given list of patterns. There must be `len()` patterns
/// matrix. There must be `len()` patterns in `pats`. /// in `pats`.
pub(super) fn replace_fields( pub(super) fn replace_fields(
&self, &self,
cx: &MatchCheckCtxt<'p, 'tcx>, cx: &MatchCheckCtxt<'p, 'tcx>,
@ -1379,7 +1371,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats);
match self { match self {
Fields::Filtered { fields, kept_count } => { Fields::Filtered { fields, len } => {
let mut pats = pats.iter(); let mut pats = pats.iter();
let mut fields = fields.clone(); let mut fields = fields.clone();
for f in &mut fields { for f in &mut fields {
@ -1388,7 +1380,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
*p = pats.next().unwrap(); *p = pats.next().unwrap();
} }
} }
Fields::Filtered { fields, kept_count: *kept_count } Fields::Filtered { fields, len: *len }
} }
_ => Fields::Slice(pats), _ => Fields::Slice(pats),
} }

View file

@ -447,7 +447,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
// We pop the head pattern and push the new fields extracted from the arguments of // We pop the head pattern and push the new fields extracted from the arguments of
// `self.head()`. // `self.head()`.
let mut new_fields = let mut new_fields =
ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).filtered_patterns(); ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).into_patterns();
new_fields.extend_from_slice(&self.pats[1..]); new_fields.extend_from_slice(&self.pats[1..]);
PatStack::from_vec(new_fields) PatStack::from_vec(new_fields)
} }