@@ -51,7 +51,7 @@ use rustc_type_ir::TyKind::*;
5151use tracing:: instrument;
5252
5353use crate :: query:: Providers ;
54- use crate :: ty:: { self , DefId , Ty , TyCtxt , TypeVisitableExt , VariantDef , Visibility } ;
54+ use crate :: ty:: { self , DefId , Ty , TyCtxt , TypeVisitableExt , TypingEnv , VariantDef , Visibility } ;
5555
5656pub mod inhabited_predicate;
5757
@@ -221,10 +221,7 @@ impl<'tcx> Ty<'tcx> {
221221 /// Beyond that, the value returned by this function is not a stable guarantee.
222222 pub fn is_opsem_inhabited ( self , tcx : TyCtxt < ' tcx > , typing_env : ty:: TypingEnv < ' tcx > ) -> bool {
223223 // 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- } )
224+ OpsemInhabitedCtx { tcx, typing_env, seen : None , stop_at_ref : false } . is_inhabited_ty ( self )
228225 }
229226}
230227
@@ -249,109 +246,129 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP
249246 }
250247}
251248
252- /// Recurse over a type to determine whether it is inhabited on the opsem level.
249+ /// Context for computing whether a type is inhabited on the opsem level.
253250/// 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.
262- fn is_opsem_inhabited_recursor < ' tcx , SEEN > (
263- ty : Ty < ' tcx > ,
251+ struct OpsemInhabitedCtx < ' tcx > {
264252 tcx : TyCtxt < ' tcx > ,
265- seen : & mut SEEN ,
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.
266260 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 ,
261+ }
288262
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 ;
263+ impl < ' 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+ bug ! ( "non-normalized type in `is_opsem_uninhabited`: `{ty}`" )
296328 }
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) )
326329 }
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+
332+ fn is_inhabited_adt_ty ( & mut self , ty : Ty < ' tcx > ) -> bool {
333+ let ty:: Adt ( adt_def, adt_args) = * ty. kind ( ) else {
334+ unreachable ! { }
335+ } ;
336+ let Self { tcx, typing_env, .. } = * self ;
337+
338+ if adt_def. is_union ( ) {
339+ // Unions are always inhabited.
340+ return true ;
330341 }
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)
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)
339362 } )
340- }
363+ } ) ;
341364
342- ty:: Error ( _error_guaranteed) => {
343- // We have a token proving there was an error, so we can return a dummy value.
344- true
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 ( ) ) ;
345369 }
346370
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- }
371+ inhabited
355372 }
356373}
357374
@@ -366,42 +383,6 @@ fn is_opsem_inhabited_raw<'tcx>(
366383 "the query should only be invoked by `Ty::is_opsem_inhabited`"
367384 ) ;
368385
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- )
386+ OpsemInhabitedCtx { tcx, typing_env, seen : Some ( FxHashSet :: default ( ) ) , stop_at_ref : false }
387+ . is_inhabited_adt_ty ( ty)
407388}
0 commit comments