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 {

View file

@ -1,8 +1,9 @@
// compile-flags: --edition=2021
// check-pass
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy; //~ ERROR unconstrained opaque type
type T = impl Copy;
let foo: T = (1u32, 2u32);
let (a, b): (u32, u32) = foo;
}

View file

@ -1,10 +0,0 @@
error: unconstrained opaque type
--> $DIR/cross_inference_pattern_bug.rs:5:14
|
LL | type T = impl Copy;
| ^^^^^^^^^
|
= note: `T` must be used in combination with a concrete type within the same module
error: aborting due to previous error

View file

@ -1,13 +1,13 @@
// known-bug: #96572
// compile-flags: --edition=2021 --crate-type=lib
// rustc-env:RUST_BACKTRACE=0
// check-pass
// tracked in https://github.com/rust-lang/rust/issues/96572
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy; // error: unconstrained opaque type
type T = impl Copy;
let foo: T = (1u32, 2u32);
let (a, b) = foo; // removing this line makes the code compile
let (a, b) = foo; // this line used to make the code fail
}

View file

@ -1,10 +0,0 @@
error: unconstrained opaque type
--> $DIR/cross_inference_pattern_bug_no_type.rs:10:14
|
LL | type T = impl Copy; // error: unconstrained opaque type
| ^^^^^^^^^
|
= note: `T` must be used in combination with a concrete type within the same module
error: aborting due to previous error

View file

@ -0,0 +1,10 @@
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
match foo {
None => (),
Some((a, b, c)) => (), //~ ERROR mismatched types
}
}

View file

@ -0,0 +1,15 @@
error[E0308]: mismatched types
--> $DIR/issue-96572-unconstrained-mismatch.rs:8:14
|
LL | match foo {
| --- this expression has type `T`
LL | None => (),
LL | Some((a, b, c)) => (),
| ^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements
|
= note: expected tuple `(u32, u32)`
found tuple `(_, _, _)`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,11 @@
#![feature(type_alias_impl_trait)]
// check-pass
#[derive(Copy, Clone)]
struct Foo((u32, u32));
fn main() {
type U = impl Copy;
let foo: U = Foo((1u32, 2u32));
let Foo((a, b)) = foo;
}

View file

@ -0,0 +1,13 @@
#![feature(type_alias_impl_trait)]
// check-pass
fn main() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
let x = move || {
match foo {
None => (),
Some((a, b)) => (),
}
};
}

View file

@ -0,0 +1,13 @@
#![feature(type_alias_impl_trait)]
// check-pass
#[derive(Copy, Clone)]
struct Foo((u32, u32));
fn main() {
type T = impl Copy;
let foo: T = Foo((1u32, 2u32));
let x = move || {
let Foo((a, b)) = foo;
};
}

View file

@ -0,0 +1,11 @@
#![feature(type_alias_impl_trait)]
// check-pass
fn main() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
match foo {
None => (),
Some((a, b)) => (),
}
}