Skip to main content

rustc_middle/ty/inhabitedness/
mod.rs

1//! This module contains logic for determining whether a type is inhabited or
2//! uninhabited. The [`InhabitedPredicate`] type captures the minimum
3//! information needed to determine whether a type is inhabited given a
4//! `ParamEnv` and module ID.
5//!
6//! # Example
7//! ```rust
8#![cfg_attr(bootstrap, doc = "#![feature(never_type)]")]
9//! mod a {
10//!     pub mod b {
11//!         pub struct SecretlyUninhabited {
12//!             _priv: !,
13//!         }
14//!     }
15//! }
16//!
17//! mod c {
18//!     enum Void {}
19//!     pub struct AlsoSecretlyUninhabited {
20//!         _priv: Void,
21//!     }
22//!     mod d {
23//!     }
24//! }
25//!
26//! struct Foo {
27//!     x: a::b::SecretlyUninhabited,
28//!     y: c::AlsoSecretlyUninhabited,
29//! }
30//! ```
31//! In this code, the type `Foo` will only be visibly uninhabited inside the
32//! modules `b`, `c` and `d`. Calling `inhabited_predicate` on `Foo` will
33//! return `NotInModule(b) AND NotInModule(c)`.
34//!
35//! We need this information for pattern-matching on `Foo` or types that contain
36//! `Foo`.
37//!
38//! # Example
39//! ```ignore(illustrative)
40//! let foo_result: Result<T, Foo> = ... ;
41//! let Ok(t) = foo_result;
42//! ```
43//! This code should only compile in modules where the uninhabitedness of `Foo`
44//! is visible.
45
46use std::assert_matches;
47
48use rustc_data_structures::fx::FxHashSet;
49use rustc_hir::def::DefKind;
50use rustc_span::bug;
51use rustc_span::def_id::LocalModId;
52use rustc_type_ir::TyKind::*;
53use tracing::instrument;
54
55use crate::query::Providers;
56use crate::ty::consts::ConstExt;
57use crate::ty::{
58    self, AdtDef, DefId, Ty, TyCtxt, TypeVisitableExt, TypingEnv, VariantDef, Visibility,
59};
60
61pub mod inhabited_predicate;
62
63pub use inhabited_predicate::InhabitedPredicate;
64
65pub(crate) fn provide(providers: &mut Providers) {
66    *providers = Providers {
67        inhabited_predicate_for_def,
68        inhabited_predicate_type,
69        is_opsem_inhabited_raw,
70        ..*providers
71    };
72}
73
74/// Returns an `InhabitedPredicate` that is generic over type parameters and
75/// requires calling [`InhabitedPredicate::instantiate`]
76fn inhabited_predicate_for_def(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
77    match tcx.def_kind(def_id) {
78        DefKind::Enum => {
79            if let Some(def_id) = def_id.as_local() {
80                tcx.ensure_ok().check_representability(def_id);
81            }
82            let adt = tcx.adt_def(def_id);
83            InhabitedPredicate::any(tcx, adt.variants().iter().map(|v| v.inhabited_predicate(tcx)))
84        }
85        DefKind::Struct => {
86            if let Some(def_id) = def_id.as_local() {
87                tcx.ensure_ok().check_representability(def_id);
88            }
89            let adt = tcx.adt_def(def_id);
90            variant_inhabited_predicate(tcx, adt, adt.non_enum_variant())
91        }
92        DefKind::Variant => {
93            let adt = tcx.adt_def(tcx.parent(def_id));
94            let variant = adt.variant_with_id(def_id);
95            variant_inhabited_predicate(tcx, adt, variant)
96        }
97        def_kind => ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected DefKind: {0:?}", def_kind), Location::caller())bug!("unexpected DefKind: {def_kind:?}"),
98    }
99}
100
101impl VariantDef {
102    pub fn inhabited_predicate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
103        if self.fields.is_empty() {
104            return InhabitedPredicate::True;
105        }
106        tcx.inhabited_predicate_for_def(self.def_id)
107    }
108}
109
110fn variant_inhabited_predicate<'tcx>(
111    tcx: TyCtxt<'tcx>,
112    adt: AdtDef<'tcx>,
113    variant: &VariantDef,
114) -> InhabitedPredicate<'tcx> {
115    InhabitedPredicate::all(
116        tcx,
117        variant.fields.iter().map(|field| {
118            let pred = tcx
119                .type_of(field.did)
120                .instantiate_identity()
121                .skip_norm_wip()
122                .inhabited_predicate(tcx);
123            if adt.is_enum() {
124                return pred;
125            }
126            match field.vis {
127                Visibility::Public => pred,
128                Visibility::Restricted(from) => InhabitedPredicate::NotInModule(from).or(tcx, pred),
129            }
130        }),
131    )
132}
133
134impl<'tcx> Ty<'tcx> {
135    {}
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
            ::tracing::Level::DEBUG <=
                ::tracing::level_filters::LevelFilter::current() || { false }
    {
    __tracing_attr_span =
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("inhabited_predicate",
                                "rustc_middle::ty::inhabitedness", ::tracing::Level::DEBUG,
                                ::tracing_core::__macro_support::Option::Some("/rustc-dev/5ceaf6608eb354c2f5bbb3b8d974caa367dac81c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs"),
                                ::tracing_core::__macro_support::Option::Some(135u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_middle::ty::inhabitedness"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("self")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("self");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::SPAN)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let mut interest = ::tracing::subscriber::Interest::never();
            if ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        { interest = __CALLSITE.interest(); !interest.is_never() }
                    &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest) {
                let meta = __CALLSITE.metadata();
                ::tracing::Span::new(meta,
                    &{
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&self)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        };
    __tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
    (move ||
                {

                    #[allow(unknown_lints, unreachable_code, clippy ::
                    diverging_sub_expression, clippy :: empty_loop, clippy ::
                    let_unit_value, clippy :: let_with_type_underscore, clippy
                    :: needless_return, clippy :: unreachable)]
                    if false {
                        let __tracing_attr_fake_return: InhabitedPredicate<'tcx> =
                            loop {};
                        return __tracing_attr_fake_return;
                    }
                    {
                        if true {
                            if !!self.has_infer() {
                                ::core::panicking::panic("assertion failed: !self.has_infer()")
                            };
                        };
                        match self.kind() {
                            Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
                            Adt(adt, _) if
                                adt.variant_list_has_applicable_non_exhaustive() => {
                                InhabitedPredicate::True
                            }
                            Never => InhabitedPredicate::False,
                            Param(_) |
                                Alias(_, ty::AliasTy {
                                kind: ty::Inherent { .. } | ty::Projection { .. } |
                                    ty::Free { .. }, .. }) =>
                                InhabitedPredicate::GenericType(self),
                            &Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args,
                                .. }) => {
                                match def_id.as_local() {
                                    None => InhabitedPredicate::True,
                                    Some(local_def_id) => {
                                        let key = ty::OpaqueTypeKey { def_id: local_def_id, args };
                                        InhabitedPredicate::OpaqueType(key)
                                    }
                                }
                            }
                            Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
                            Adt(..) | Array(..) | Tuple(_) =>
                                tcx.inhabited_predicate_type(self),
                            _ => InhabitedPredicate::True,
                        }
                    }
                })();
{
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/5ceaf6608eb354c2f5bbb3b8d974caa367dac81c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs:135",
                        "rustc_middle::ty::inhabitedness", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/5ceaf6608eb354c2f5bbb3b8d974caa367dac81c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(135u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::ty::inhabitedness"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("return")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("return");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};
x;#[instrument(level = "debug", skip(tcx), ret)]
136    pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
137        debug_assert!(!self.has_infer());
138        match self.kind() {
139            // For now, unions are always considered inhabited
140            Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
141            // Non-exhaustive ADTs from other crates are always considered inhabited
142            Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => {
143                InhabitedPredicate::True
144            }
145            Never => InhabitedPredicate::False,
146            // FIXME(#155345): This should only encounter rigid aliases with the new solver.
147            Param(_)
148            | Alias(
149                _,
150                ty::AliasTy {
151                    kind: ty::Inherent { .. } | ty::Projection { .. } | ty::Free { .. },
152                    ..
153                },
154            ) => InhabitedPredicate::GenericType(self),
155            &Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {
156                match def_id.as_local() {
157                    // Foreign opaque is considered inhabited.
158                    None => InhabitedPredicate::True,
159                    // Local opaque type may possibly be revealed.
160                    Some(local_def_id) => {
161                        let key = ty::OpaqueTypeKey { def_id: local_def_id, args };
162                        InhabitedPredicate::OpaqueType(key)
163                    }
164                }
165            }
166            Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
167            // use a query for more complex cases
168            Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
169            // references and other types are inhabited
170            _ => InhabitedPredicate::True,
171        }
172    }
173
174    /// Checks whether a type is visibly uninhabited from a particular module.
175    ///
176    /// # Example
177    /// ```
178    #[cfg_attr(bootstrap, doc = "#![feature(never_type)]")]
179    /// # fn main() {}
180    /// enum Void {}
181    /// mod a {
182    ///     pub mod b {
183    ///         pub struct SecretlyUninhabited {
184    ///             _priv: !,
185    ///         }
186    ///     }
187    /// }
188    ///
189    /// mod c {
190    ///     use super::Void;
191    ///     pub struct AlsoSecretlyUninhabited {
192    ///         _priv: Void,
193    ///     }
194    ///     mod d {
195    ///     }
196    /// }
197    ///
198    /// struct Foo {
199    ///     x: a::b::SecretlyUninhabited,
200    ///     y: c::AlsoSecretlyUninhabited,
201    /// }
202    /// ```
203    /// In this code, the type `Foo` will only be visibly uninhabited inside the
204    /// modules b, c and d. This effects pattern-matching on `Foo` or types that
205    /// contain `Foo`.
206    ///
207    /// # Example
208    /// ```ignore (illustrative)
209    /// let foo_result: Result<T, Foo> = ... ;
210    /// let Ok(t) = foo_result;
211    /// ```
212    /// This code should only compile in modules where the uninhabitedness of Foo is
213    /// visible.
214    pub fn is_inhabited_from(
215        self,
216        tcx: TyCtxt<'tcx>,
217        module: LocalModId,
218        typing_env: ty::TypingEnv<'tcx>,
219    ) -> bool {
220        self.inhabited_predicate(tcx).apply(tcx, typing_env, module)
221    }
222
223    /// Returns true if the type is uninhabited without regard to visibility.
224    ///
225    /// This is still conservative; for instance, a `#[non_exhaustive]` enum *in another crate*
226    /// is always considered inhabited.
227    pub fn is_privately_uninhabited(
228        self,
229        tcx: TyCtxt<'tcx>,
230        typing_env: ty::TypingEnv<'tcx>,
231    ) -> bool {
232        !self.inhabited_predicate(tcx).apply_ignore_module(tcx, typing_env)
233    }
234
235    /// Returns whether `self` is considered inhabited on the opsem level, i.e., its validity
236    /// invariant might be satisfiable. `self` is expected to be monomorphic and normalized.
237    ///
238    /// Key constraints are:
239    /// - if a type's validity invariant is satisfiable, it must be opsem-inhabited.
240    /// - if a type's layout is marked uninhabited, it must be opsem-uninhabited.
241    ///
242    /// Beyond that, the value returned by this function is not a stable guarantee.
243    pub fn is_opsem_inhabited(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
244        // Handle simple cases directly, use the query with its cache for the rest.
245        OpsemInhabitedCtx { tcx, typing_env, seen: None, stop_at_ref: false }.is_inhabited_ty(self)
246    }
247}
248
249/// N.B. this query should only be called through `Ty::inhabited_predicate`
250fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> {
251    match *ty.kind() {
252        Adt(adt, args) => tcx.inhabited_predicate_for_def(adt.did()).instantiate(tcx, args),
253
254        Tuple(tys) => {
255            InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx)))
256        }
257
258        // If we can evaluate the array length before having a `ParamEnv`, then
259        // we can simplify the predicate. This is an optimization.
260        Array(ty, len) => match len.try_to_target_usize(tcx) {
261            Some(0) => InhabitedPredicate::True,
262            Some(1..) => ty.inhabited_predicate(tcx),
263            None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
264        },
265
266        _ => ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected TyKind, use `Ty::inhabited_predicate`"),
    Location::caller())bug!("unexpected TyKind, use `Ty::inhabited_predicate`"),
267    }
268}
269
270/// Context for computing whether a type is inhabited on the opsem level.
271/// See `is_opsem_inhabited` above for the spec of what we compute.
272struct OpsemInhabitedCtx<'tcx> {
273    tcx: TyCtxt<'tcx>,
274    typing_env: TypingEnv<'tcx>,
275    /// IDs of ADTs that have been encountered in the current stack.
276    /// It's `None` unless we are inside the `is_opsem_inhabited_raw` query,
277    /// which is only invoked for more complex types.
278    seen: Option<FxHashSet<DefId>>,
279    /// If an ADT is encountered recursively within itself, then `stop_at_ref`
280    /// is set to `true`, and then any nested references are considered inhabited.
281    stop_at_ref: bool,
282}
283
284impl<'tcx> OpsemInhabitedCtx<'tcx> {
285    /// See `is_opsem_inhabited` above for the spec of what we compute.
286    fn is_inhabited_ty(&mut self, ty: Ty<'tcx>) -> bool {
287        let tcx = self.tcx;
288        match *ty.kind() {
289            // Trivially (un)inhabited types
290            ty::Int(_)
291            | ty::Uint(_)
292            | ty::Float(_)
293            | ty::Bool
294            | ty::Char
295            | ty::Str
296            | ty::Foreign(..)
297            | ty::RawPtr(..)
298            | ty::FnPtr(..)
299            | ty::FnDef(..) => true,
300            ty::Dynamic(..) => true, // We can't reason about traits, assume they are inhabited
301            ty::Slice(..) => true,   // Slices can always be empty
302            ty::Never => false,
303
304            // Types where we recurse
305            ty::Ref(_, pointee, _) => {
306                if self.stop_at_ref {
307                    // Bailing out here is safe as the layout code always considers references
308                    // inhabited, so the implication ("layout uninhabited => opsem uninhabited")
309                    // is upheld.
310                    return true;
311                }
312                self.is_inhabited_ty(pointee)
313            }
314            ty::Tuple(tys) => tys.iter().all(|ty| self.is_inhabited_ty(ty)),
315            ty::Array(elem, len) => {
316                len.try_to_target_usize(tcx).unwrap() == 0 || self.is_inhabited_ty(elem)
317            }
318            ty::Pat(inner, _pat) => self.is_inhabited_ty(inner),
319            ty::Closure(_def, args) => {
320                let args = args.as_closure();
321                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
322            }
323            ty::Coroutine(_def, args) => {
324                let args = args.as_coroutine();
325                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
326            }
327            ty::CoroutineClosure(_def, args) => {
328                let args = args.as_coroutine_closure();
329                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
330            }
331            ty::UnsafeBinder(base) => {
332                let base = tcx.instantiate_bound_regions_with_erased((*base).into());
333                self.is_inhabited_ty(base)
334            }
335            ty::Adt(..) => self.is_inhabited_adt_ty(ty),
336
337            ty::Error(_error_guaranteed) => {
338                // We have a token proving there was an error, so we can return a dummy value.
339                true
340            }
341
342            ty::Infer(..)
343            | ty::Placeholder(..)
344            | ty::Bound(..)
345            | ty::Param(..)
346            | ty::Alias(..)
347            | ty::CoroutineWitness(..) => {
348                ::rustc_span::macros::bug_impl(None,
    format_args!("non-normalized type in `is_opsem_uninhabited`: `{0}`", ty),
    Location::caller())bug!("non-normalized type in `is_opsem_uninhabited`: `{ty}`")
349            }
350        }
351    }
352
353    fn is_inhabited_adt_ty(&mut self, ty: Ty<'tcx>) -> bool {
354        let ty::Adt(adt_def, adt_args) = *ty.kind() else {
355            ::core::panicking::panic("internal error: entered unreachable code");unreachable! {}
356        };
357        let Self { tcx, typing_env, .. } = *self;
358
359        if adt_def.is_union() {
360            // Unions are always inhabited.
361            return true;
362        }
363
364        let Some(seen) = self.seen.as_mut() else {
365            // stop recursing, invoke the query.
366            return tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty));
367        };
368
369        let new_adt = seen.insert(adt_def.did());
370        // If we have seen this ADT before, stop at the next reference to avoid infinite
371        // recursion. We can't stop here since we have to ensure that "layout uninhabited"
372        // implies "opsem uninhabited". References are always layout-inhabited so the
373        // implication is vacuously true.
374        let stop_at_ref_prev = self.stop_at_ref;
375        self.stop_at_ref |= !new_adt;
376
377        // We are inhabited if in some variant all fields are inhabited.
378        let inhabited = adt_def.variants().iter().any(|variant| {
379            variant.fields.iter().all(|field| {
380                let ty = field.ty(tcx, adt_args);
381                let ty = tcx.normalize_erasing_regions(typing_env, ty);
382                self.is_inhabited_ty(ty)
383            })
384        });
385
386        self.stop_at_ref = stop_at_ref_prev;
387        // Remove the type again so that we allow it to appear on other branches.
388        if new_adt {
389            self.seen.as_mut().unwrap().remove(&adt_def.did());
390        }
391
392        inhabited
393    }
394}
395
396fn is_opsem_inhabited_raw<'tcx>(
397    tcx: TyCtxt<'tcx>,
398    env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
399) -> bool {
400    let (ty, typing_env) = (env.value, env.typing_env);
401    {
    match ty.kind() {
        ty::Adt(..) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val, "ty::Adt(..)",
                ::core::option::Option::Some(format_args!("the query should only be invoked by `Ty::is_opsem_inhabited`")));
        }
    }
};assert_matches!(
402        ty.kind(),
403        ty::Adt(..),
404        "the query should only be invoked by `Ty::is_opsem_inhabited`"
405    );
406
407    OpsemInhabitedCtx { tcx, typing_env, seen: Some(FxHashSet::default()), stop_at_ref: false }
408        .is_inhabited_adt_ty(ty)
409}