Infer wildcard type from other patterns at every pattern level

This commit is contained in:
Oli Scherer 2022-06-28 11:19:38 +00:00
parent 92e470a1de
commit 57e9f7a556
4 changed files with 72 additions and 26 deletions

View File

@ -1202,35 +1202,32 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
/// Creates a new list of wildcard fields for a given constructor. The result must have a
/// length of `constructor.arity()`.
pub(super) fn wildcards(
cx: &MatchCheckCtxt<'p, 'tcx>,
ty: Ty<'tcx>,
constructor: &Constructor<'tcx>,
) -> Self {
#[instrument(level = "trace")]
pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
let ret = match constructor {
Single | Variant(_) => match ty.kind() {
ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()),
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)),
Single | Variant(_) => match pcx.ty.kind() {
ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()),
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)),
ty::Adt(adt, substs) => {
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.
Fields::wildcards_from_tys(cx, once(substs.type_at(0)))
Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)))
} else {
let variant = &adt.variant(constructor.variant_index_for_adt(*adt));
let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant)
let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant)
.map(|(_, ty)| ty);
Fields::wildcards_from_tys(cx, tys)
Fields::wildcards_from_tys(pcx.cx, tys)
}
}
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
_ => bug!("Unexpected type for `Single` constructor: {:?}", pcx),
},
Slice(slice) => match *ty.kind() {
Slice(slice) => match *pcx.ty.kind() {
ty::Slice(ty) | ty::Array(ty, _) => {
let arity = slice.arity();
Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty))
Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty))
}
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
_ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
},
Str(..)
| FloatRange(..)
@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
bug!("called `Fields::wildcards` on an `Or` ctor")
}
};
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
debug!(?ret);
ret
}
@ -1286,7 +1283,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
/// `Some(_)`.
pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor);
let fields = Fields::wildcards(pcx, &ctor);
DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
}
@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
pub(super) fn specialize<'a>(
&'a self,
cx: &MatchCheckCtxt<'p, 'tcx>,
pcx: PatCtxt<'_, 'p, 'tcx>,
other_ctor: &Constructor<'tcx>,
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
match (&self.ctor, other_ctor) {
(Wildcard, _) => {
// We return a wildcard for each field of `other_ctor`.
Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect()
Fields::wildcards(pcx, other_ctor).iter_patterns().collect()
}
(Slice(self_slice), Slice(other_slice))
if self_slice.arity() != other_slice.arity() =>
@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
let prefix = &self.fields.fields[..prefix];
let suffix = &self.fields.fields[self_slice.arity() - suffix..];
let wildcard: &_ =
cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
let extra_wildcards = other_slice.arity() - self_slice.arity();
let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
prefix.iter().chain(extra_wildcards).chain(suffix).collect()

View File

@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
/// This is roughly the inverse of `Constructor::apply`.
fn pop_head_constructor(
&self,
cx: &MatchCheckCtxt<'p, 'tcx>,
pcx: PatCtxt<'_, 'p, 'tcx>,
ctor: &Constructor<'tcx>,
) -> PatStack<'p, 'tcx> {
// We pop the head pattern and push the new fields extracted from the arguments of
// `self.head()`.
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor);
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor);
new_fields.extend_from_slice(&self.pats[1..]);
PatStack::from_vec(new_fields)
}
@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
let mut matrix = Matrix::empty();
for row in &self.patterns {
if ctor.is_covered_by(pcx, row.head().ctor()) {
let new_row = row.pop_head_constructor(pcx.cx, ctor);
let new_row = row.pop_head_constructor(pcx, ctor);
matrix.push(new_row);
}
}
@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>(
is_under_guard: bool,
is_top_level: bool,
) -> Usefulness<'p, 'tcx> {
debug!("matrix,v={:?}{:?}", matrix, v);
debug!(?matrix, ?v);
let Matrix { patterns: rows, .. } = matrix;
// The base case. We are pattern-matching on () and the return value is
@ -827,7 +827,15 @@ fn is_useful<'p, 'tcx>(
}
}
} else {
let ty = v.head().ty();
let mut ty = v.head().ty();
// Opaque types can't get destructured/split, but the patterns can
// actually hint at hidden types, so we use the patterns' types instead.
if let ty::Opaque(..) = v.head().ty().kind() {
if let Some(row) = rows.first() {
ty = row.head().ty();
}
}
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>(
debug!("specialize({:?})", ctor);
// We cache the result of `Fields::wildcards` because it is used a lot.
let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
let v = v.pop_head_constructor(cx, &ctor);
let v = v.pop_head_constructor(pcx, &ctor);
let usefulness = ensure_sufficient_stack(|| {
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
});

View File

@ -0,0 +1,29 @@
// check-pass
#[allow(unconditional_recursion)]
fn foo(b: bool) -> impl Copy {
let (mut x, mut y) = foo(false);
x = 42;
y = "foo";
if b {
panic!()
} else {
foo(true)
}
}
fn bar(b: bool) -> Option<impl Copy> {
if b {
return None;
}
match bar(!b) {
Some((mut x, mut y)) => {
x = 42;
y = "foo";
}
None => {}
}
None
}
fn main() {}

View File

@ -9,4 +9,16 @@ fn foo(foo: T) {
y = "foo";
}
type U = impl Copy;
fn bar(bar: Option<U>) {
match bar {
Some((mut x, mut y)) => {
x = 42;
y = "foo";
}
None => {}
}
}
fn main() {}