4343//! This code should only compile in modules where the uninhabitedness of `Foo`
4444//! is visible.
4545
46+ use rustc_data_structures:: fx:: FxHashSet ;
4647use rustc_type_ir:: TyKind :: * ;
4748use tracing:: instrument;
4849
@@ -54,7 +55,12 @@ pub mod inhabited_predicate;
5455pub use inhabited_predicate:: InhabitedPredicate ;
5556
5657pub ( crate ) fn provide ( providers : & mut Providers ) {
57- * providers = Providers { inhabited_predicate_adt, inhabited_predicate_type, ..* providers } ;
58+ * providers = Providers {
59+ inhabited_predicate_adt,
60+ inhabited_predicate_type,
61+ is_opsem_inhabited_raw,
62+ ..* providers
63+ } ;
5864}
5965
6066/// Returns an `InhabitedPredicate` that is generic over type parameters and
@@ -186,14 +192,26 @@ impl<'tcx> Ty<'tcx> {
186192 self . inhabited_predicate ( tcx) . apply ( tcx, typing_env, module)
187193 }
188194
189- /// Returns true if the type is uninhabited without regard to visibility
195+ /// Returns true if the type is uninhabited without regard to visibility.
196+ ///
197+ /// This is still conservative; for instance, a `#[non_exhaustive]` enum *in another crate*
198+ /// is always considered inhabited.
190199 pub fn is_privately_uninhabited (
191200 self ,
192201 tcx : TyCtxt < ' tcx > ,
193202 typing_env : ty:: TypingEnv < ' tcx > ,
194203 ) -> bool {
195204 !self . inhabited_predicate ( tcx) . apply_ignore_module ( tcx, typing_env)
196205 }
206+
207+ /// Returns whether `self` is considered inhabited on the opsem level, i.e., its validity
208+ /// invariant might be satisfiable. `self` is expected to be monomorphic and normalized.
209+ pub fn is_opsem_inhabited ( self , tcx : TyCtxt < ' tcx > , typing_env : ty:: TypingEnv < ' tcx > ) -> bool {
210+ // Handle simple cases directly, use the query with its cache for the rest.
211+ is_opsem_inhabited_recursor ( self , tcx, & mut FxHashSet :: default ( ) , & |ty, _, _| {
212+ tcx. is_opsem_inhabited_raw ( typing_env. as_query_input ( ty) )
213+ } )
214+ }
197215}
198216
199217/// N.B. this query should only be called through `Ty::inhabited_predicate`
@@ -216,3 +234,125 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP
216234 _ => bug ! ( "unexpected TyKind, use `Ty::inhabited_predicate`" ) ,
217235 }
218236}
237+
238+ /// `seen` is used to bound the recursion: the set contains all ADTs that we encountered
239+ /// on our path to the current type.
240+ ///
241+ /// When we encounter an ADT, we call `adt_handler`, giving it as its last argument a closure that
242+ /// it can invoke to continue the recursion.
243+ fn is_opsem_inhabited_recursor < ' tcx > (
244+ ty : Ty < ' tcx > ,
245+ tcx : TyCtxt < ' tcx > ,
246+ seen : & mut FxHashSet < Ty < ' tcx > > ,
247+ adt_handler : & impl Fn (
248+ Ty < ' tcx > ,
249+ & mut FxHashSet < Ty < ' tcx > > ,
250+ & dyn Fn ( Ty < ' tcx > , & mut FxHashSet < Ty < ' tcx > > ) -> bool ,
251+ ) -> bool ,
252+ ) -> bool {
253+ match * ty. kind ( ) {
254+ // Trivially (un)inhabited types
255+ ty:: Int ( _)
256+ | ty:: Uint ( _)
257+ | ty:: Float ( _)
258+ | ty:: Bool
259+ | ty:: Char
260+ | ty:: Str
261+ | ty:: Foreign ( ..)
262+ | ty:: RawPtr ( ..)
263+ | ty:: FnPtr ( ..)
264+ | ty:: FnDef ( ..) => true ,
265+ ty:: Dynamic ( ..) => true , // We can't reason about traits, assume they are inhabited
266+ ty:: Slice ( ..) => true , // Slices can always be empty
267+ ty:: Never => false ,
268+
269+ // Types where we recurse
270+ ty:: Ref ( _, pointee, _) => is_opsem_inhabited_recursor ( pointee, tcx, seen, adt_handler) ,
271+ ty:: Tuple ( tys) => {
272+ tys. iter ( ) . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, seen, adt_handler) )
273+ }
274+ ty:: Array ( elem, len) => {
275+ len. try_to_target_usize ( tcx) . unwrap ( ) == 0
276+ || is_opsem_inhabited_recursor ( elem, tcx, seen, adt_handler)
277+ }
278+ ty:: Pat ( inner, _pat) => is_opsem_inhabited_recursor ( inner, tcx, seen, adt_handler) ,
279+ ty:: Closure ( _def, args) => {
280+ let args = args. as_closure ( ) ;
281+ args. upvar_tys ( )
282+ . iter ( )
283+ . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, seen, adt_handler) )
284+ }
285+ ty:: Coroutine ( _def, args) => {
286+ let args = args. as_coroutine ( ) ;
287+ args. upvar_tys ( )
288+ . iter ( )
289+ . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, seen, adt_handler) )
290+ }
291+ ty:: CoroutineClosure ( _def, args) => {
292+ let args = args. as_coroutine_closure ( ) ;
293+ args. upvar_tys ( )
294+ . iter ( )
295+ . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, seen, adt_handler) )
296+ }
297+ ty:: UnsafeBinder ( ..) => {
298+ true // FIXME(unsafe_binder) should these really be trivially inhabited?
299+ }
300+ ty:: Adt ( ..) => {
301+ // ADTs need a special handler to avoid infinite recursion.
302+ adt_handler ( ty, seen, & |ty, seen| {
303+ is_opsem_inhabited_recursor ( ty, tcx, seen, adt_handler)
304+ } )
305+ }
306+
307+ ty:: Error ( _)
308+ | ty:: Infer ( ..)
309+ | ty:: Placeholder ( ..)
310+ | ty:: Bound ( ..)
311+ | ty:: Param ( ..)
312+ | ty:: Alias ( ..)
313+ | ty:: CoroutineWitness ( ..) => {
314+ bug ! ( "non-normalized type in `is_opsem_uninhabited_raw::rec`: `{ty}`" )
315+ }
316+ }
317+ }
318+
319+ fn is_opsem_inhabited_raw < ' tcx > (
320+ tcx : TyCtxt < ' tcx > ,
321+ env : ty:: PseudoCanonicalInput < ' tcx , Ty < ' tcx > > ,
322+ ) -> bool {
323+ let ( ty, typing_env) = ( env. value , env. typing_env ) ;
324+ is_opsem_inhabited_recursor ( ty, tcx, & mut FxHashSet :: default ( ) , & |ty, seen, rec| {
325+ let ty:: Adt ( adt_def, adt_args) = * ty. kind ( ) else {
326+ unreachable ! { }
327+ } ;
328+ if adt_def. is_union ( ) {
329+ // Unions are always inhabited.
330+ return true ;
331+ }
332+
333+ if !seen. insert ( ty) {
334+ // We recursed to the same type. Coinductively assume that the type is inhabited.
335+ return true ;
336+ }
337+ if !tcx. recursion_limit ( ) . value_within_limit ( seen. len ( ) ) {
338+ // We seem to be stuck in an infinite recursion.
339+ // Bailing out here is unfortunate as it means that the recursion limit affects the
340+ // operational semantics... but what else could we do?
341+ return true ;
342+ }
343+
344+ // We are inhabited if in some variant all fields are inhabited.
345+ let inhabited = adt_def. variants ( ) . iter ( ) . any ( |variant| {
346+ variant. fields . iter ( ) . all ( |field| {
347+ let ty = field. ty ( tcx, adt_args) ;
348+ let ty = tcx. normalize_erasing_regions ( typing_env, ty) ;
349+ rec ( ty, seen)
350+ } )
351+ } ) ;
352+
353+ // Remove the type again so that we allow it to appear on other branches.
354+ seen. remove ( & ty) ;
355+
356+ inhabited
357+ } )
358+ }
0 commit comments