diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index a75f17766a80..824cf0ccb6eb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -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}; @@ -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> 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 + } } } diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs index d971e3655bc2..92011dcff5a9 100644 --- a/tests/ui/pattern/usefulness/impl-trait.rs +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -42,9 +42,10 @@ fn option_never(x: Void) -> Option { } 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) diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr index c29ceb48b5af..5b7688180df7 100644 --- a/tests/ui/pattern/usefulness/impl-trait.stderr +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -11,19 +11,25 @@ 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 @@ -31,7 +37,7 @@ 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)) => { | ^^^^^^^^^^^^^^^^^^^^ @@ -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`.