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, 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 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 #[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 is_opsem_inhabited_recursor(self, tcx, &mut (), /* stop_at_ref */ false, &|ty, _, _| {
225 // ADT handler: stop recursing, invoke the query.
226 tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty))
227 })
228 }
229}
230
231/// N.B. this query should only be called through `Ty::inhabited_predicate`
232fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> {
233 match *ty.kind() {
234 Adt(adt, args) => tcx.inhabited_predicate_adt(adt.did()).instantiate(tcx, args),
235
236 Tuple(tys) => {
237 InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx)))
238 }
239
240 // If we can evaluate the array length before having a `ParamEnv`, then
241 // we can simplify the predicate. This is an optimization.
242 Array(ty, len) => match len.try_to_target_usize(tcx) {
243 Some(0) => InhabitedPredicate::True,
244 Some(1..) => ty.inhabited_predicate(tcx),
245 None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
246 },
247
248 _ => bug!("unexpected TyKind, use `Ty::inhabited_predicate`"),
249 }
250}
251
252/// Recurse over a type to determine whether it is inhabited on the opsem level.
253/// See `is_opsem_inhabited` above for the spec of what we compute.
254///
255/// When we encounter an ADT, we call `adt_handler`, giving it as its last argument a closure that
256/// it can invoke to continue the recursion. This lets us share the logic for "simple" cases
257/// (i.e., everything except for ADTs) between `Ty::is_opsem_inhabited` and the query.
258///
259/// `seen` is used to detect infinite recursion: the set contains all ADTs that we encountered
260/// on our path to the current type.
261/// If `stop_at_ref` is true, we stop recursing at the next reference we encounter.
262fn is_opsem_inhabited_recursor<'tcx, SEEN>(
263 ty: Ty<'tcx>,
264 tcx: TyCtxt<'tcx>,
265 seen: &mut SEEN,
266 stop_at_ref: bool,
267 adt_handler: &impl Fn(
268 Ty<'tcx>,
269 &mut SEEN,
270 &dyn Fn(Ty<'tcx>, &mut SEEN, /* stop_at_ref */ bool) -> bool,
271 ) -> bool,
272) -> bool {
273 match *ty.kind() {
274 // Trivially (un)inhabited types
275 ty::Int(_)
276 | ty::Uint(_)
277 | ty::Float(_)
278 | ty::Bool
279 | ty::Char
280 | ty::Str
281 | ty::Foreign(..)
282 | ty::RawPtr(..)
283 | ty::FnPtr(..)
284 | ty::FnDef(..) => true,
285 ty::Dynamic(..) => true, // We can't reason about traits, assume they are inhabited
286 ty::Slice(..) => true, // Slices can always be empty
287 ty::Never => false,
288
289 // Types where we recurse
290 ty::Ref(_, pointee, _) => {
291 if stop_at_ref {
292 // Bailing out here is safe as the layout code always considers references
293 // inhabited, so the implication ("layout uninhabited => opsem uninhabited")
294 // is upheld.
295 return true;
296 }
297 is_opsem_inhabited_recursor(pointee, tcx, seen, stop_at_ref, adt_handler)
298 }
299 ty::Tuple(tys) => tys
300 .iter()
301 .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)),
302 ty::Array(elem, len) => {
303 len.try_to_target_usize(tcx).unwrap() == 0
304 || is_opsem_inhabited_recursor(elem, tcx, seen, stop_at_ref, adt_handler)
305 }
306 ty::Pat(inner, _pat) => {
307 is_opsem_inhabited_recursor(inner, tcx, seen, stop_at_ref, adt_handler)
308 }
309 ty::Closure(_def, args) => {
310 let args = args.as_closure();
311 args.upvar_tys()
312 .iter()
313 .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler))
314 }
315 ty::Coroutine(_def, args) => {
316 let args = args.as_coroutine();
317 args.upvar_tys()
318 .iter()
319 .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler))
320 }
321 ty::CoroutineClosure(_def, args) => {
322 let args = args.as_coroutine_closure();
323 args.upvar_tys()
324 .iter()
325 .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler))
326 }
327 ty::UnsafeBinder(base) => {
328 let base = tcx.instantiate_bound_regions_with_erased((*base).into());
329 is_opsem_inhabited_recursor(base, tcx, seen, stop_at_ref, adt_handler)
330 }
331 ty::Adt(..) => {
332 // ADTs need a special handler to avoid infinite recursion. That handler is meant to
333 // call back into the recursor. Ideally it'd just call `is_opsem_inhabited_recursor` but
334 // then it would have to pass itself as the adt_handler argument which is not possible
335 // in Rust... so we provide the handler with a callback that it can use to continue the
336 // recursion with the same `adt_handler`.
337 adt_handler(ty, seen, &|ty, seen, stop_at_ref| {
338 is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)
339 })
340 }
341
342 ty::Error(_error_guaranteed) => {
343 // We have a token proving there was an error, so we can return a dummy value.
344 true
345 }
346
347 ty::Infer(..)
348 | ty::Placeholder(..)
349 | ty::Bound(..)
350 | ty::Param(..)
351 | ty::Alias(..)
352 | ty::CoroutineWitness(..) => {
353 bug!("non-normalized type in `is_opsem_uninhabited`: `{ty}`")
354 }
355 }
356}
357
358fn is_opsem_inhabited_raw<'tcx>(
359 tcx: TyCtxt<'tcx>,
360 env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
361) -> bool {
362 let (ty, typing_env) = (env.value, env.typing_env);
363 assert_matches!(
364 ty.kind(),
365 ty::Adt(..),
366 "the query should only be invoked by `Ty::is_opsem_inhabited`"
367 );
368
369 is_opsem_inhabited_recursor(
370 ty,
371 tcx,
372 &mut FxHashSet::<DefId>::default(),
373 /* stop_at_ref */ false,
374 &|ty, seen, rec| {
375 let ty::Adt(adt_def, adt_args) = *ty.kind() else {
376 unreachable! {}
377 };
378 if adt_def.is_union() {
379 // Unions are always inhabited.
380 return true;
381 }
382
383 let new_adt = seen.insert(adt_def.did());
384 // If we have seen this ADT before, stop at the next reference to avoid infinite
385 // recursion. We can't stop here since we have to ensure that "layout uninhabited"
386 // implies "opsem uninhabited". References are always layout-inhabited so the
387 // implication is vacuously true.
388 let stop_at_ref = !new_adt;
389
390 // We are inhabited if in some variant all fields are inhabited.
391 let inhabited = adt_def.variants().iter().any(|variant| {
392 variant.fields.iter().all(|field| {
393 let ty = field.ty(tcx, adt_args);
394 let ty = tcx.normalize_erasing_regions(typing_env, ty);
395 rec(ty, seen, stop_at_ref)
396 })
397 });
398
399 // Remove the type again so that we allow it to appear on other branches.
400 if new_adt {
401 seen.remove(&adt_def.did());
402 }
403
404 inhabited
405 },
406 )
407}