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//! #![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_span::def_id::LocalModId;
50use rustc_type_ir::TyKind::*;
51use tracing::instrument;
52
53use crate::query::Providers;
54use crate::ty::{self, DefId, Ty, TyCtxt, TypeVisitableExt, TypingEnv, VariantDef, Visibility};
55
56pub mod inhabited_predicate;
57
58pub use inhabited_predicate::InhabitedPredicate;
59
60pub(crate) fn provide(providers: &mut Providers) {
61    *providers = Providers {
62        inhabited_predicate_adt,
63        inhabited_predicate_type,
64        is_opsem_inhabited_raw,
65        ..*providers
66    };
67}
68
69/// Returns an `InhabitedPredicate` that is generic over type parameters and
70/// requires calling [`InhabitedPredicate::instantiate`]
71fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
72    if let Some(def_id) = def_id.as_local() {
73        tcx.ensure_ok().check_representability(def_id);
74    }
75
76    let adt = tcx.adt_def(def_id);
77    InhabitedPredicate::any(
78        tcx,
79        adt.variants().iter().map(|variant| variant.inhabited_predicate(tcx, adt)),
80    )
81}
82
83impl<'tcx> VariantDef {
84    /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited.
85    pub fn inhabited_predicate(
86        &self,
87        tcx: TyCtxt<'tcx>,
88        adt: ty::AdtDef<'_>,
89    ) -> InhabitedPredicate<'tcx> {
90        if true {
    if !!adt.is_union() {
        ::core::panicking::panic("assertion failed: !adt.is_union()")
    };
};debug_assert!(!adt.is_union());
91        InhabitedPredicate::all(
92            tcx,
93            self.fields.iter().map(|field| {
94                let pred = tcx
95                    .type_of(field.did)
96                    .instantiate_identity()
97                    .skip_norm_wip()
98                    .inhabited_predicate(tcx);
99                if adt.is_enum() {
100                    return pred;
101                }
102                match field.vis {
103                    Visibility::Public => pred,
104                    Visibility::Restricted(from) => {
105                        InhabitedPredicate::NotInModule(from).or(tcx, pred)
106                    }
107                }
108            }),
109        )
110    }
111}
112
113impl<'tcx> Ty<'tcx> {
114    x;#[instrument(level = "debug", skip(tcx), ret)]
115    pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
116        debug_assert!(!self.has_infer());
117        match self.kind() {
118            // For now, unions are always considered inhabited
119            Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
120            // Non-exhaustive ADTs from other crates are always considered inhabited
121            Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => {
122                InhabitedPredicate::True
123            }
124            Never => InhabitedPredicate::False,
125            // FIXME(#155345): This should only encounter rigid aliases with the new solver.
126            Param(_)
127            | Alias(
128                _,
129                ty::AliasTy {
130                    kind: ty::Inherent { .. } | ty::Projection { .. } | ty::Free { .. },
131                    ..
132                },
133            ) => InhabitedPredicate::GenericType(self),
134            &Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {
135                match def_id.as_local() {
136                    // Foreign opaque is considered inhabited.
137                    None => InhabitedPredicate::True,
138                    // Local opaque type may possibly be revealed.
139                    Some(local_def_id) => {
140                        let key = ty::OpaqueTypeKey { def_id: local_def_id, args };
141                        InhabitedPredicate::OpaqueType(key)
142                    }
143                }
144            }
145            Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
146            // use a query for more complex cases
147            Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
148            // references and other types are inhabited
149            _ => InhabitedPredicate::True,
150        }
151    }
152
153    /// Checks whether a type is visibly uninhabited from a particular module.
154    ///
155    /// # Example
156    /// ```
157    /// #![feature(never_type)]
158    /// # fn main() {}
159    /// enum Void {}
160    /// mod a {
161    ///     pub mod b {
162    ///         pub struct SecretlyUninhabited {
163    ///             _priv: !,
164    ///         }
165    ///     }
166    /// }
167    ///
168    /// mod c {
169    ///     use super::Void;
170    ///     pub struct AlsoSecretlyUninhabited {
171    ///         _priv: Void,
172    ///     }
173    ///     mod d {
174    ///     }
175    /// }
176    ///
177    /// struct Foo {
178    ///     x: a::b::SecretlyUninhabited,
179    ///     y: c::AlsoSecretlyUninhabited,
180    /// }
181    /// ```
182    /// In this code, the type `Foo` will only be visibly uninhabited inside the
183    /// modules b, c and d. This effects pattern-matching on `Foo` or types that
184    /// contain `Foo`.
185    ///
186    /// # Example
187    /// ```ignore (illustrative)
188    /// let foo_result: Result<T, Foo> = ... ;
189    /// let Ok(t) = foo_result;
190    /// ```
191    /// This code should only compile in modules where the uninhabitedness of Foo is
192    /// visible.
193    pub fn is_inhabited_from(
194        self,
195        tcx: TyCtxt<'tcx>,
196        module: LocalModId,
197        typing_env: ty::TypingEnv<'tcx>,
198    ) -> bool {
199        self.inhabited_predicate(tcx).apply(tcx, typing_env, module)
200    }
201
202    /// Returns true if the type is uninhabited without regard to visibility.
203    ///
204    /// This is still conservative; for instance, a `#[non_exhaustive]` enum *in another crate*
205    /// is always considered inhabited.
206    pub fn is_privately_uninhabited(
207        self,
208        tcx: TyCtxt<'tcx>,
209        typing_env: ty::TypingEnv<'tcx>,
210    ) -> bool {
211        !self.inhabited_predicate(tcx).apply_ignore_module(tcx, typing_env)
212    }
213
214    /// Returns whether `self` is considered inhabited on the opsem level, i.e., its validity
215    /// invariant might be satisfiable. `self` is expected to be monomorphic and normalized.
216    ///
217    /// Key constraints are:
218    /// - if a type's validity invariant is satisfiable, it must be opsem-inhabited.
219    /// - if a type's layout is marked uninhabited, it must be opsem-uninhabited.
220    ///
221    /// Beyond that, the value returned by this function is not a stable guarantee.
222    pub fn is_opsem_inhabited(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
223        // Handle simple cases directly, use the query with its cache for the rest.
224        OpsemInhabitedCtx { tcx, typing_env, seen: None, stop_at_ref: false }.is_inhabited_ty(self)
225    }
226}
227
228/// N.B. this query should only be called through `Ty::inhabited_predicate`
229fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> {
230    match *ty.kind() {
231        Adt(adt, args) => tcx.inhabited_predicate_adt(adt.did()).instantiate(tcx, args),
232
233        Tuple(tys) => {
234            InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx)))
235        }
236
237        // If we can evaluate the array length before having a `ParamEnv`, then
238        // we can simplify the predicate. This is an optimization.
239        Array(ty, len) => match len.try_to_target_usize(tcx) {
240            Some(0) => InhabitedPredicate::True,
241            Some(1..) => ty.inhabited_predicate(tcx),
242            None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
243        },
244
245        _ => crate::util::bug::bug_fmt(format_args!("unexpected TyKind, use `Ty::inhabited_predicate`"))bug!("unexpected TyKind, use `Ty::inhabited_predicate`"),
246    }
247}
248
249/// Context for computing whether a type is inhabited on the opsem level.
250/// See `is_opsem_inhabited` above for the spec of what we compute.
251struct OpsemInhabitedCtx<'tcx> {
252    tcx: TyCtxt<'tcx>,
253    typing_env: TypingEnv<'tcx>,
254    /// IDs of ADTs that have been encountered in the current stack.
255    /// It's `None` unless we are inside the `is_opsem_inhabited_raw` query,
256    /// which is only invoked for more complex types.
257    seen: Option<FxHashSet<DefId>>,
258    /// If an ADT is encountered recursively within itself, then `stop_at_ref`
259    /// is set to `true`, and then any nested references are considered inhabited.
260    stop_at_ref: bool,
261}
262
263impl<'tcx> OpsemInhabitedCtx<'tcx> {
264    /// See `is_opsem_inhabited` above for the spec of what we compute.
265    fn is_inhabited_ty(&mut self, ty: Ty<'tcx>) -> bool {
266        let tcx = self.tcx;
267        match *ty.kind() {
268            // Trivially (un)inhabited types
269            ty::Int(_)
270            | ty::Uint(_)
271            | ty::Float(_)
272            | ty::Bool
273            | ty::Char
274            | ty::Str
275            | ty::Foreign(..)
276            | ty::RawPtr(..)
277            | ty::FnPtr(..)
278            | ty::FnDef(..) => true,
279            ty::Dynamic(..) => true, // We can't reason about traits, assume they are inhabited
280            ty::Slice(..) => true,   // Slices can always be empty
281            ty::Never => false,
282
283            // Types where we recurse
284            ty::Ref(_, pointee, _) => {
285                if self.stop_at_ref {
286                    // Bailing out here is safe as the layout code always considers references
287                    // inhabited, so the implication ("layout uninhabited => opsem uninhabited")
288                    // is upheld.
289                    return true;
290                }
291                self.is_inhabited_ty(pointee)
292            }
293            ty::Tuple(tys) => tys.iter().all(|ty| self.is_inhabited_ty(ty)),
294            ty::Array(elem, len) => {
295                len.try_to_target_usize(tcx).unwrap() == 0 || self.is_inhabited_ty(elem)
296            }
297            ty::Pat(inner, _pat) => self.is_inhabited_ty(inner),
298            ty::Closure(_def, args) => {
299                let args = args.as_closure();
300                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
301            }
302            ty::Coroutine(_def, args) => {
303                let args = args.as_coroutine();
304                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
305            }
306            ty::CoroutineClosure(_def, args) => {
307                let args = args.as_coroutine_closure();
308                args.upvar_tys().iter().all(|ty| self.is_inhabited_ty(ty))
309            }
310            ty::UnsafeBinder(base) => {
311                let base = tcx.instantiate_bound_regions_with_erased((*base).into());
312                self.is_inhabited_ty(base)
313            }
314            ty::Adt(..) => self.is_inhabited_adt_ty(ty),
315
316            ty::Error(_error_guaranteed) => {
317                // We have a token proving there was an error, so we can return a dummy value.
318                true
319            }
320
321            ty::Infer(..)
322            | ty::Placeholder(..)
323            | ty::Bound(..)
324            | ty::Param(..)
325            | ty::Alias(..)
326            | ty::CoroutineWitness(..) => {
327                crate::util::bug::bug_fmt(format_args!("non-normalized type in `is_opsem_uninhabited`: `{0}`",
        ty))bug!("non-normalized type in `is_opsem_uninhabited`: `{ty}`")
328            }
329        }
330    }
331
332    fn is_inhabited_adt_ty(&mut self, ty: Ty<'tcx>) -> bool {
333        let ty::Adt(adt_def, adt_args) = *ty.kind() else {
334            ::core::panicking::panic("internal error: entered unreachable code");unreachable! {}
335        };
336        let Self { tcx, typing_env, .. } = *self;
337
338        if adt_def.is_union() {
339            // Unions are always inhabited.
340            return true;
341        }
342
343        let Some(seen) = self.seen.as_mut() else {
344            // stop recursing, invoke the query.
345            return tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty));
346        };
347
348        let new_adt = seen.insert(adt_def.did());
349        // If we have seen this ADT before, stop at the next reference to avoid infinite
350        // recursion. We can't stop here since we have to ensure that "layout uninhabited"
351        // implies "opsem uninhabited". References are always layout-inhabited so the
352        // implication is vacuously true.
353        let stop_at_ref_prev = self.stop_at_ref;
354        self.stop_at_ref |= !new_adt;
355
356        // We are inhabited if in some variant all fields are inhabited.
357        let inhabited = adt_def.variants().iter().any(|variant| {
358            variant.fields.iter().all(|field| {
359                let ty = field.ty(tcx, adt_args);
360                let ty = tcx.normalize_erasing_regions(typing_env, ty);
361                self.is_inhabited_ty(ty)
362            })
363        });
364
365        self.stop_at_ref = stop_at_ref_prev;
366        // Remove the type again so that we allow it to appear on other branches.
367        if new_adt {
368            self.seen.as_mut().unwrap().remove(&adt_def.did());
369        }
370
371        inhabited
372    }
373}
374
375fn is_opsem_inhabited_raw<'tcx>(
376    tcx: TyCtxt<'tcx>,
377    env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
378) -> bool {
379    let (ty, typing_env) = (env.value, env.typing_env);
380    {
    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!(
381        ty.kind(),
382        ty::Adt(..),
383        "the query should only be invoked by `Ty::is_opsem_inhabited`"
384    );
385
386    OpsemInhabitedCtx { tcx, typing_env, seen: Some(FxHashSet::default()), stop_at_ref: false }
387        .is_inhabited_adt_ty(ty)
388}