diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index ae17942785ff8..c571ac507248d 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -1,7 +1,7 @@ use smallvec::SmallVec; use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, ParamEnv, Ty}; +use crate::ty::{self, DefId, OpaqueTypeKey, ParamEnv, Ty}; /// Represents whether some type is inhabited in a given context. /// Examples of uninhabited types are `!`, `enum Void {}`, or a struct @@ -23,6 +23,8 @@ pub enum InhabitedPredicate<'tcx> { /// Inhabited if some generic type is inhabited. /// These are replaced by calling [`Self::instantiate`]. GenericType(Ty<'tcx>), + /// Inhabited if either we don't know the hidden type or we know it and it is inhabited. + OpaqueType(OpaqueTypeKey<'tcx>), /// A AND B And(&'tcx [InhabitedPredicate<'tcx>; 2]), /// A OR B @@ -30,35 +32,53 @@ pub enum InhabitedPredicate<'tcx> { } impl<'tcx> InhabitedPredicate<'tcx> { - /// Returns true if the corresponding type is inhabited in the given - /// `ParamEnv` and module + /// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module. pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool { - let Ok(result) = self.apply_inner::(tcx, param_env, &mut Default::default(), &|id| { - Ok(tcx.is_descendant_of(module_def_id, id)) - }); + self.apply_revealing_opaque(tcx, param_env, module_def_id, &|_| None) + } + + /// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module, + /// revealing opaques when possible. + pub fn apply_revealing_opaque( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + module_def_id: DefId, + reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option>, + ) -> bool { + let Ok(result) = self.apply_inner::( + tcx, + param_env, + &mut Default::default(), + &|id| Ok(tcx.is_descendant_of(module_def_id, id)), + reveal_opaque, + ); result } /// Same as `apply`, but returns `None` if self contains a module predicate pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok() + self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(()), &|_| None).ok() } /// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is, /// privately uninhabited types are considered always uninhabited. pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool { let Ok(result) = - self.apply_inner::(tcx, param_env, &mut Default::default(), &|_| Ok(true)); + self.apply_inner::(tcx, param_env, &mut Default::default(), &|_| Ok(true), &|_| { + None + }); result } - #[instrument(level = "debug", skip(tcx, param_env, in_module), ret)] + #[instrument(level = "debug", skip(tcx, param_env, in_module, reveal_opaque), ret)] fn apply_inner( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection in_module: &impl Fn(DefId) -> Result, + reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option>, ) -> Result { match self { Self::False => Ok(false), @@ -84,18 +104,41 @@ impl<'tcx> InhabitedPredicate<'tcx> { return Ok(true); // Recover; this will error later. } eval_stack.push(t); - let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module); + let ret = + pred.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque); eval_stack.pop(); ret } } } - Self::And([a, b]) => { - try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module)) - } - Self::Or([a, b]) => { - try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module)) - } + Self::OpaqueType(key) => match reveal_opaque(key) { + // Unknown opaque is assumed inhabited. + None => Ok(true), + // Known opaque type is inspected recursively. + Some(t) => { + // A cyclic opaque type can happen in corner cases that would only error later. + // See e.g. `tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs`. + if eval_stack.contains(&t) { + return Ok(true); // Recover; this will error later. + } + eval_stack.push(t); + let ret = t.inhabited_predicate(tcx).apply_inner( + tcx, + param_env, + eval_stack, + in_module, + reveal_opaque, + ); + eval_stack.pop(); + ret + } + }, + Self::And([a, b]) => try_and(a, b, |x| { + x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque) + }), + Self::Or([a, b]) => try_or(a, b, |x| { + x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque) + }), } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index d67fb9fd0b734..bbcc244cb26de 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -45,7 +45,7 @@ use crate::query::Providers; use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; +use crate::ty::{self, DefId, Ty, TypeVisitableExt, VariantDef, Visibility}; use rustc_type_ir::TyKind::*; @@ -105,6 +105,7 @@ impl<'tcx> VariantDef { impl<'tcx> Ty<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> { + debug_assert!(!self.has_infer()); match self.kind() { // For now, unions are always considered inhabited Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, @@ -113,7 +114,18 @@ impl<'tcx> Ty<'tcx> { InhabitedPredicate::True } Never => InhabitedPredicate::False, - Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), + Param(_) | Alias(ty::Projection | ty::Weak, _) => InhabitedPredicate::GenericType(self), + Alias(ty::Opaque, alias_ty) => { + match alias_ty.def_id.as_local() { + // Foreign opaque is considered inhabited. + None => InhabitedPredicate::True, + // Local opaque type may possibly be revealed. + Some(local_def_id) => { + let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args }; + InhabitedPredicate::OpaqueType(key) + } + } + } // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. // However it's unclear if the args passed to `InhabitedPredicate::instantiate` are of the correct // format, i.e. don't contain parent args. If you hit this case, please verify this beforehand. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index adaa2ecbe4fd0..a5a47724f3f02 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; @@ -74,8 +74,16 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> { } impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { - pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - !ty.is_inhabited_from(self.tcx, self.module, self.param_env) + fn reveal_opaque(&self, key: OpaqueTypeKey<'tcx>) -> Option> { + self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty) + } + pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + !ty.inhabited_predicate(self.tcx).apply_revealing_opaque( + self.tcx, + self.param_env, + self.module, + &|key| self.reveal_opaque(key), + ) } /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. @@ -319,7 +327,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let is_inhabited = v .inhabited_predicate(cx.tcx, *def) .instantiate(cx.tcx, args) - .apply(cx.tcx, cx.param_env, cx.module); + .apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| { + cx.reveal_opaque(key) + }); // Variants that depend on a disabled unstable feature. let is_unstable = matches!( cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None), diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs index 6e8c0feb71030..ceb97315e9dde 100644 --- a/tests/ui/pattern/usefulness/impl-trait.rs +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -46,9 +46,7 @@ fn option_never(x: Void) -> Option { } match option_never(x) { None => {} - // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque - // types. - _ => {} + _ => {} //~ ERROR unreachable } } Some(x) @@ -137,10 +135,21 @@ fn nested_empty_opaque(x: Void) -> X { let opaque_void = nested_empty_opaque(x); let secretely_void = SecretelyVoid(opaque_void); match secretely_void { - // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque - // types. - _ => {} + _ => {} //~ ERROR unreachable } } x } + +type Y = (impl Copy, impl Copy); +struct SecretelyDoubleVoid(Y); +fn super_nested_empty_opaque(x: Void) -> Y { + if false { + let opaque_void = super_nested_empty_opaque(x); + let secretely_void = SecretelyDoubleVoid(opaque_void); + match secretely_void { + _ => {} //~ ERROR unreachable + } + } + (x, x) +} diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr index df6d43dbb3f28..ba8b12f9f66c6 100644 --- a/tests/ui/pattern/usefulness/impl-trait.stderr +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -23,25 +23,31 @@ LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:61:13 + --> $DIR/impl-trait.rs:49:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:59:13 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:65:13 + --> $DIR/impl-trait.rs:63:13 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:78:9 + --> $DIR/impl-trait.rs:76:9 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:88:9 + --> $DIR/impl-trait.rs:86:9 | LL | _ => {} | - matches any value @@ -49,25 +55,37 @@ LL | Some((a, b)) => {} | ^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:96:13 + --> $DIR/impl-trait.rs:94:13 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:107:9 + --> $DIR/impl-trait.rs:105:9 | LL | Some((mut x, mut y)) => { | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:126:13 + --> $DIR/impl-trait.rs:124:13 | LL | _ => {} | - matches any value LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern +error: unreachable pattern + --> $DIR/impl-trait.rs:138:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:151:13 + | +LL | _ => {} + | ^ + error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty --> $DIR/impl-trait.rs:23:11 | @@ -96,6 +114,6 @@ LL + _ => todo!(), LL + } | -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`.