1
Fork 0

Allow destructuring opaque types, since the patterns constrain the opaque types

This commit is contained in:
Oli Scherer 2022-06-27 16:26:41 +00:00
parent 12457f814c
commit 728c7e8bda
14 changed files with 116 additions and 31 deletions

View file

@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::hir::place::Projection as HirProjection;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::middle::region;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::AssertKind::BoundsCheck;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
@ -104,8 +105,9 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
variant = Some(*idx);
continue;
}
// These do not affect anything, they just make sure we know the right type.
ProjectionElem::OpaqueCast(_) => continue,
ProjectionElem::Index(..)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
// We don't capture array-access projections.
@ -297,16 +299,21 @@ fn strip_prefix<'tcx>(
prefix_projections: &[HirProjection<'tcx>],
) -> impl Iterator<Item = PlaceElem<'tcx>> {
let mut iter = projections.into_iter();
let mut next = || match iter.next()? {
// Filter out opaque casts, they are unnecessary in the prefix.
ProjectionElem::OpaqueCast(..) => iter.next(),
other => Some(other),
};
for projection in prefix_projections {
match projection.kind {
HirProjectionKind::Deref => {
assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
assert!(matches!(next(), Some(ProjectionElem::Deref)));
}
HirProjectionKind::Field(..) => {
if base_ty.is_enum() {
assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..))));
assert!(matches!(next(), Some(ProjectionElem::Downcast(..))));
}
assert!(matches!(iter.next(), Some(ProjectionElem::Field(..))));
assert!(matches!(next(), Some(ProjectionElem::Field(..))));
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
bug!("unexpected projection kind: {:?}", projection);
@ -320,7 +327,23 @@ fn strip_prefix<'tcx>(
impl<'tcx> PlaceBuilder<'tcx> {
pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
if let PlaceBase::Local(local) = self.base {
Place { local, projection: cx.tcx.intern_place_elems(&self.projection) }
let mut projections = vec![];
let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty);
for projection in self.projection {
// Only preserve those opaque casts that actually go from an opaque type
// to another type.
if let ProjectionElem::OpaqueCast(t) = projection {
if let ty::Opaque(..) = ty.ty.kind() {
if t != ty.ty {
projections.push(ProjectionElem::OpaqueCast(t));
}
}
} else {
projections.push(projection);
}
ty = ty.projection_ty(cx.tcx, projection);
}
Place { local, projection: cx.tcx.intern_place_elems(&projections) }
} else {
self.expect_upvars_resolved(cx).into_place(cx)
}

View file

@ -867,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
Candidate {
span: pattern.span,
has_guard,
match_pairs: smallvec![MatchPair { place, pattern }],
match_pairs: smallvec![MatchPair::new(place, pattern)],
bindings: Vec::new(),
ascriptions: Vec::new(),
subcandidates: Vec::new(),

View file

@ -98,6 +98,10 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>,
) -> MatchPair<'pat, 'tcx> {
// Force the place type to the pattern's type.
// FIXME(oli-obk): only do this when we don't already know the place type.
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
let place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
MatchPair { place, pattern }
}
}

View file

@ -967,7 +967,11 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
})
.collect();
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
// In case we're matching on an opaque type in its defining scope, the patterns define the hidden type.
// The wildcard pattern needs to have the same type, otherwise it will always be deemed useful, even if the
// match is exhaustive for the pattern type.
let wild_ty = arms.first().map_or(scrut_ty, |arm| arm.pat.ty());
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(wild_ty));
let v = PatStack::from_pattern(wild_pattern);
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true);
let non_exhaustiveness_witnesses = match usefulness {