Skip to content

Commit

Permalink
Reveal opaque types in depth
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Oct 30, 2023
1 parent 39105be commit 3280909
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 16 deletions.
41 changes: 33 additions & 8 deletions compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ use rustc_arena::TypedArena;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
use rustc_session::lint;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::{Span, DUMMY_SP};
Expand Down Expand Up @@ -362,17 +364,40 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {

/// Type inference occasionally gives us opaque types in places where corresponding patterns
/// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
/// types, we use the corresponding concrete type if possible.
/// types, we use the corresponding concrete type if possible. This recursively reveals all the
/// opaque types in `ty` that we can.
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
if let Some(local_def_id) = alias_ty.def_id.as_local() {
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
return real_ty.ty;
struct RevealOpaqueTys<'tcx> {
tcx: TyCtxt<'tcx>,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
}

impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RevealOpaqueTys<'tcx> {
fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
if let Some(local_def_id) = alias_ty.def_id.as_local() {
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
ty = real_ty.ty;
}
}
}

if ty.has_opaque_types() { ty.super_fold_with(self) } else { ty }
}
}
ty

if ty.has_opaque_types() {
ty.fold_with(&mut RevealOpaqueTys {
tcx: self.tcx,
typeck_results: self.typeck_results,
})
} else {
ty
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions tests/ui/pattern/usefulness/impl-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ fn option_never(x: Void) -> Option<impl Copy> {
}
match option_never(x) {
None => {}
// FIXME: Unreachable not detected because `is_uninhabited` did not look into the
// opaque type.
_ => {}
_ => {} //~ERROR unreachable
}
match option_never(x) {
None => {}
}
}
Some(x)
Expand Down
16 changes: 11 additions & 5 deletions tests/ui/pattern/usefulness/impl-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,33 @@ LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/impl-trait.rs:57:13
--> $DIR/impl-trait.rs:45:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/impl-trait.rs:58:13
|
LL | Some(_) => {}
| ^^^^^^^

error: unreachable pattern
--> $DIR/impl-trait.rs:61:13
--> $DIR/impl-trait.rs:62:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/impl-trait.rs:82:9
--> $DIR/impl-trait.rs:83:9
|
LL | _ => {}
| - matches any value
LL | Some((a, b)) => {}
| ^^^^^^^^^^^^ unreachable pattern

error: unreachable pattern
--> $DIR/impl-trait.rs:96:9
--> $DIR/impl-trait.rs:97:9
|
LL | Some((mut x, mut y)) => {
| ^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -64,6 +70,6 @@ LL + _ => todo!(),
LL + }
|

error: aborting due to 7 previous errors
error: aborting due to 8 previous errors

For more information about this error, try `rustc --explain E0004`.

0 comments on commit 3280909

Please sign in to comment.