@@ -54,7 +54,12 @@ pub mod inhabited_predicate;
5454pub use inhabited_predicate:: InhabitedPredicate ;
5555
5656pub ( crate ) fn provide ( providers : & mut Providers ) {
57- * providers = Providers { inhabited_predicate_adt, inhabited_predicate_type, ..* providers } ;
57+ * providers = Providers {
58+ inhabited_predicate_adt,
59+ inhabited_predicate_type,
60+ is_opsem_inhabited_raw,
61+ ..* providers
62+ } ;
5863}
5964
6065/// Returns an `InhabitedPredicate` that is generic over type parameters and
@@ -186,14 +191,26 @@ impl<'tcx> Ty<'tcx> {
186191 self . inhabited_predicate ( tcx) . apply ( tcx, typing_env, module)
187192 }
188193
189- /// Returns true if the type is uninhabited without regard to visibility
194+ /// Returns true if the type is uninhabited without regard to visibility.
195+ ///
196+ /// This is still conservative; for instance, a `#[non_exhaustive]` enum *in another crate*
197+ /// is always considered inhabited.
190198 pub fn is_privately_uninhabited (
191199 self ,
192200 tcx : TyCtxt < ' tcx > ,
193201 typing_env : ty:: TypingEnv < ' tcx > ,
194202 ) -> bool {
195203 !self . inhabited_predicate ( tcx) . apply_ignore_module ( tcx, typing_env)
196204 }
205+
206+ /// Returns whether `self` is considered inhabited on the opsem level, i.e., its validity
207+ /// invariant might be satisfiable. `self` is expected to be monomorphic and normalized.
208+ pub fn is_opsem_inhabited ( self , tcx : TyCtxt < ' tcx > , typing_env : ty:: TypingEnv < ' tcx > ) -> bool {
209+ // Handle simple cases directly, use the query with its cache for the rest.
210+ is_opsem_inhabited_recursor ( self , tcx, None , & |ty, _, _| {
211+ tcx. is_opsem_inhabited_raw ( typing_env. as_query_input ( ty) )
212+ } )
213+ }
197214}
198215
199216/// N.B. this query should only be called through `Ty::inhabited_predicate`
@@ -216,3 +233,107 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP
216233 _ => bug ! ( "unexpected TyKind, use `Ty::inhabited_predicate`" ) ,
217234 }
218235}
236+
237+ /// `root` is used to bound the recursion: the first time we encounter an ADT, we
238+ /// store its `DefId` as the "root"; if we ever hit the same ADT again we stop.
239+ /// All recursive types must go through an ADT so this catches all possible recursion.
240+ ///
241+ /// When we git an ADT, we call `adt_handler`, giving it as its last argument a closure that it
242+ /// can invoke to continue the recursion.
243+ fn is_opsem_inhabited_recursor < ' tcx > (
244+ ty : Ty < ' tcx > ,
245+ tcx : TyCtxt < ' tcx > ,
246+ root : Option < DefId > ,
247+ adt_handler : & impl Fn ( Ty < ' tcx > , Option < DefId > , & dyn Fn ( Ty < ' tcx > , Option < DefId > ) -> bool ) -> bool ,
248+ ) -> bool {
249+ match * ty. kind ( ) {
250+ // Trivially (un)inhabited types
251+ ty:: Int ( _)
252+ | ty:: Uint ( _)
253+ | ty:: Float ( _)
254+ | ty:: Bool
255+ | ty:: Char
256+ | ty:: Str
257+ | ty:: Foreign ( ..)
258+ | ty:: RawPtr ( ..)
259+ | ty:: FnPtr ( ..)
260+ | ty:: FnDef ( ..) => true ,
261+ ty:: Dynamic ( ..) => true , // We can't reason about traits, assume they are inhabited
262+ ty:: Slice ( ..) => true , // Slices can always be empty
263+ ty:: Never => false ,
264+
265+ // Types where we recurse
266+ ty:: Ref ( _, pointee, _) => is_opsem_inhabited_recursor ( pointee, tcx, root, adt_handler) ,
267+ ty:: Tuple ( tys) => {
268+ tys. iter ( ) . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, root, adt_handler) )
269+ }
270+ ty:: Array ( elem, len) => {
271+ len. try_to_target_usize ( tcx) . unwrap ( ) == 0
272+ || is_opsem_inhabited_recursor ( elem, tcx, root, adt_handler)
273+ }
274+ ty:: Pat ( inner, _pat) => is_opsem_inhabited_recursor ( inner, tcx, root, adt_handler) ,
275+ ty:: Closure ( _closure_def, closure_args) => {
276+ let closure_args = closure_args. as_closure ( ) ;
277+ closure_args
278+ . upvar_tys ( )
279+ . iter ( )
280+ . all ( |ty| is_opsem_inhabited_recursor ( ty, tcx, root, adt_handler) )
281+ }
282+ ty:: Coroutine ( ..) => {
283+ true // FIXME should these really be trivially inhabited?
284+ }
285+ ty:: CoroutineClosure ( ..) => {
286+ true // FIXME should these really be trivially inhabited?
287+ }
288+ ty:: UnsafeBinder ( ..) => {
289+ true // FIXME should these really be trivially inhabited?
290+ }
291+ ty:: Adt ( ..) => {
292+ // ADTs need a special handler to avoid infinite recursion.
293+ adt_handler ( ty, root, & |ty, root| {
294+ is_opsem_inhabited_recursor ( ty, tcx, root, adt_handler)
295+ } )
296+ }
297+
298+ ty:: Error ( _)
299+ | ty:: Infer ( ..)
300+ | ty:: Placeholder ( ..)
301+ | ty:: Bound ( ..)
302+ | ty:: Param ( ..)
303+ | ty:: Alias ( ..)
304+ | ty:: CoroutineWitness ( ..) => {
305+ bug ! ( "non-normalized type in `is_opsem_uninhabited_raw::rec`: `{ty}`" )
306+ }
307+ }
308+ }
309+
310+ fn is_opsem_inhabited_raw < ' tcx > (
311+ tcx : TyCtxt < ' tcx > ,
312+ env : ty:: PseudoCanonicalInput < ' tcx , Ty < ' tcx > > ,
313+ ) -> bool {
314+ let ( ty, typing_env) = ( env. value , env. typing_env ) ;
315+ is_opsem_inhabited_recursor ( ty, tcx, None , & |ty, root, rec| {
316+ let ty:: Adt ( adt_def, adt_args) = * ty. kind ( ) else {
317+ unreachable ! { }
318+ } ;
319+ if adt_def. is_union ( ) {
320+ // Unions are always inhabited.
321+ return true ;
322+ }
323+ if Some ( adt_def. did ( ) ) == root {
324+ // We recursed to (possibly another instance of) the same type.
325+ // Coinductively assume that the type is inhabited.
326+ return true ;
327+ }
328+ let root = root. unwrap_or ( adt_def. did ( ) ) ;
329+
330+ // We are inhabited if in some variant all fields are inhabited.
331+ adt_def. variants ( ) . iter ( ) . any ( |variant| {
332+ variant. fields . iter ( ) . all ( |field| {
333+ let ty = field. ty ( tcx, adt_args) ;
334+ let ty = tcx. normalize_erasing_regions ( typing_env, ty) ;
335+ rec ( ty, Some ( root) )
336+ } )
337+ } )
338+ } )
339+ }
0 commit comments