rustc_middle/ty/inhabitedness/
mod.rs1use 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
69fn 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 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 Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
120 Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => {
122 InhabitedPredicate::True
123 }
124 Never => InhabitedPredicate::False,
125 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 None => InhabitedPredicate::True,
138 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 Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
148 _ => InhabitedPredicate::True,
150 }
151 }
152
153 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 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 pub fn is_opsem_inhabited(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
223 OpsemInhabitedCtx { tcx, typing_env, seen: None, stop_at_ref: false }.is_inhabited_ty(self)
225 }
226}
227
228fn 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 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
249struct OpsemInhabitedCtx<'tcx> {
252 tcx: TyCtxt<'tcx>,
253 typing_env: TypingEnv<'tcx>,
254 seen: Option<FxHashSet<DefId>>,
258 stop_at_ref: bool,
261}
262
263impl<'tcx> OpsemInhabitedCtx<'tcx> {
264 fn is_inhabited_ty(&mut self, ty: Ty<'tcx>) -> bool {
266 let tcx = self.tcx;
267 match *ty.kind() {
268 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, ty::Slice(..) => true, ty::Never => false,
282
283 ty::Ref(_, pointee, _) => {
285 if self.stop_at_ref {
286 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 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 return true;
341 }
342
343 let Some(seen) = self.seen.as_mut() else {
344 return tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty));
346 };
347
348 let new_adt = seen.insert(adt_def.did());
349 let stop_at_ref_prev = self.stop_at_ref;
354 self.stop_at_ref |= !new_adt;
355
356 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 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}