about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2023-11-16 08:53:21 +0100
committerNadrieril <nadrieril+git@gmail.com>2023-12-23 14:59:12 +0100
commit34307ab7c55de1c81d2476c86b44d03728b3f0f0 (patch)
tree8f20dde563cb0e6b5ef207da1db15954a0a85063
parentc03d978a4bcb7c01d8cdf80bd7600b27e2d21588 (diff)
downloadrust-34307ab7c55de1c81d2476c86b44d03728b3f0f0.tar.gz
rust-34307ab7c55de1c81d2476c86b44d03728b3f0f0.zip
Reveal empty opaques in depth
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs75
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs16
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs18
-rw-r--r--tests/ui/pattern/usefulness/impl-trait.rs21
-rw-r--r--tests/ui/pattern/usefulness/impl-trait.stderr34
5 files changed, 128 insertions, 36 deletions
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
index ae17942785f..c571ac50724 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<Ty<'tcx>>,
+    ) -> 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<bool> {
-        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<E: std::fmt::Debug>(
         self,
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
         eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection
         in_module: &impl Fn(DefId) -> Result<bool, E>,
+        reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>>,
     ) -> Result<bool, E> {
         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 d67fb9fd0b7..bbcc244cb26 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 adaa2ecbe4f..a5a47724f3f 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<Ty<'tcx>> {
+        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 6e8c0feb710..ceb97315e9d 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<impl Copy> {
         }
         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 df6d43dbb3f..ba8b12f9f66 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`.