| //! This module contains logic for determining whether a type is inhabited or |
| //! uninhabited. The [`InhabitedPredicate`] type captures the minimum |
| //! information needed to determine whether a type is inhabited given a |
| //! `ParamEnv` and module ID. |
| //! |
| //! # Example |
| //! ```rust |
| //! #![feature(never_type)] |
| //! mod a { |
| //! pub mod b { |
| //! pub struct SecretlyUninhabited { |
| //! _priv: !, |
| //! } |
| //! } |
| //! } |
| //! |
| //! mod c { |
| //! enum Void {} |
| //! pub struct AlsoSecretlyUninhabited { |
| //! _priv: Void, |
| //! } |
| //! mod d { |
| //! } |
| //! } |
| //! |
| //! struct Foo { |
| //! x: a::b::SecretlyUninhabited, |
| //! y: c::AlsoSecretlyUninhabited, |
| //! } |
| //! ``` |
| //! In this code, the type `Foo` will only be visibly uninhabited inside the |
| //! modules `b`, `c` and `d`. Calling `inhabited_predicate` on `Foo` will |
| //! return `NotInModule(b) AND NotInModule(c)`. |
| //! |
| //! We need this information for pattern-matching on `Foo` or types that contain |
| //! `Foo`. |
| //! |
| //! # Example |
| //! ```ignore(illustrative) |
| //! let foo_result: Result<T, Foo> = ... ; |
| //! let Ok(t) = foo_result; |
| //! ``` |
| //! This code should only compile in modules where the uninhabitedness of `Foo` |
| //! is visible. |
| |
| use crate::query::Providers; |
| use crate::ty::context::TyCtxt; |
| use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; |
| |
| use rustc_type_ir::sty::TyKind::*; |
| |
| pub mod inhabited_predicate; |
| |
| pub use inhabited_predicate::InhabitedPredicate; |
| |
| pub(crate) fn provide(providers: &mut Providers) { |
| *providers = Providers { inhabited_predicate_adt, inhabited_predicate_type, ..*providers }; |
| } |
| |
| /// Returns an `InhabitedPredicate` that is generic over type parameters and |
| /// requires calling [`InhabitedPredicate::subst`] |
| fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { |
| if let Some(def_id) = def_id.as_local() { |
| if matches!(tcx.representability(def_id), ty::Representability::Infinite) { |
| return InhabitedPredicate::True; |
| } |
| } |
| let adt = tcx.adt_def(def_id); |
| InhabitedPredicate::any( |
| tcx, |
| adt.variants().iter().map(|variant| variant.inhabited_predicate(tcx, adt)), |
| ) |
| } |
| |
| impl<'tcx> VariantDef { |
| /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. |
| pub fn inhabited_predicate( |
| &self, |
| tcx: TyCtxt<'tcx>, |
| adt: ty::AdtDef<'_>, |
| ) -> InhabitedPredicate<'tcx> { |
| debug_assert!(!adt.is_union()); |
| if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { |
| // Non-exhaustive variants from other crates are always considered inhabited. |
| return InhabitedPredicate::True; |
| } |
| InhabitedPredicate::all( |
| tcx, |
| self.fields.iter().map(|field| { |
| let pred = tcx.type_of(field.did).subst_identity().inhabited_predicate(tcx); |
| if adt.is_enum() { |
| return pred; |
| } |
| match field.vis { |
| Visibility::Public => pred, |
| Visibility::Restricted(from) => { |
| pred.or(tcx, InhabitedPredicate::NotInModule(from)) |
| } |
| } |
| }), |
| ) |
| } |
| } |
| |
| impl<'tcx> Ty<'tcx> { |
| pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> { |
| match self.kind() { |
| // For now, unions are always considered inhabited |
| Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, |
| // Non-exhaustive ADTs from other crates are always considered inhabited |
| Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => { |
| InhabitedPredicate::True |
| } |
| Never => InhabitedPredicate::False, |
| Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), |
| // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. |
| // However it's unclear if the substs passed to `InhabitedPredicate::subst` are of the correct |
| // format, i.e. don't contain parent substs. If you hit this case, please verify this beforehand. |
| Alias(ty::Inherent, _) => { |
| bug!("unimplemented: inhabitedness checking for inherent projections") |
| } |
| Tuple(tys) if tys.is_empty() => InhabitedPredicate::True, |
| // use a query for more complex cases |
| Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self), |
| // references and other types are inhabited |
| _ => InhabitedPredicate::True, |
| } |
| } |
| |
| /// Checks whether a type is visibly uninhabited from a particular module. |
| /// |
| /// # Example |
| /// ``` |
| /// #![feature(never_type)] |
| /// # fn main() {} |
| /// enum Void {} |
| /// mod a { |
| /// pub mod b { |
| /// pub struct SecretlyUninhabited { |
| /// _priv: !, |
| /// } |
| /// } |
| /// } |
| /// |
| /// mod c { |
| /// use super::Void; |
| /// pub struct AlsoSecretlyUninhabited { |
| /// _priv: Void, |
| /// } |
| /// mod d { |
| /// } |
| /// } |
| /// |
| /// struct Foo { |
| /// x: a::b::SecretlyUninhabited, |
| /// y: c::AlsoSecretlyUninhabited, |
| /// } |
| /// ``` |
| /// In this code, the type `Foo` will only be visibly uninhabited inside the |
| /// modules b, c and d. This effects pattern-matching on `Foo` or types that |
| /// contain `Foo`. |
| /// |
| /// # Example |
| /// ```ignore (illustrative) |
| /// let foo_result: Result<T, Foo> = ... ; |
| /// let Ok(t) = foo_result; |
| /// ``` |
| /// This code should only compile in modules where the uninhabitedness of Foo is |
| /// visible. |
| pub fn is_inhabited_from( |
| self, |
| tcx: TyCtxt<'tcx>, |
| module: DefId, |
| param_env: ty::ParamEnv<'tcx>, |
| ) -> bool { |
| self.inhabited_predicate(tcx).apply(tcx, param_env, module) |
| } |
| |
| /// Returns true if the type is uninhabited without regard to visibility |
| pub fn is_privately_uninhabited( |
| self, |
| tcx: TyCtxt<'tcx>, |
| param_env: ty::ParamEnv<'tcx>, |
| ) -> bool { |
| !self.inhabited_predicate(tcx).apply_ignore_module(tcx, param_env) |
| } |
| } |
| |
| /// N.B. this query should only be called through `Ty::inhabited_predicate` |
| fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> { |
| match *ty.kind() { |
| Adt(adt, substs) => tcx.inhabited_predicate_adt(adt.did()).subst(tcx, substs), |
| |
| Tuple(tys) => { |
| InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx))) |
| } |
| |
| // If we can evaluate the array length before having a `ParamEnv`, then |
| // we can simplify the predicate. This is an optimization. |
| Array(ty, len) => match len.kind().try_to_target_usize(tcx) { |
| Some(0) => InhabitedPredicate::True, |
| Some(1..) => ty.inhabited_predicate(tcx), |
| None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)), |
| }, |
| |
| _ => bug!("unexpected TyKind, use `Ty::inhabited_predicate`"), |
| } |
| } |