diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index a6310ae5e66..f8893ae29f5 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,76 +1,60 @@ -//! A generalized traversal mechanism for complex data structures that contain -//! type information. +//! A folding traversal mechanism for complex data structures that contain type +//! information. //! -//! There are two types of traversal. -//! - Folding. This is a modifying traversal. It consumes the data structure, -//! producing a (possibly) modified version of it. Both fallible and -//! infallible versions are available. The name is potentially -//! confusing, because this traversal is more like `Iterator::map` than -//! `Iterator::fold`. -//! - Visiting. This is a read-only traversal of the data structure. +//! This is a modifying traversal. It consumes the data structure, producing a +//! (possibly) modified version of it. Both fallible and infallible versions are +//! available. The name is potentially confusing, because this traversal is more +//! like `Iterator::map` than `Iterator::fold`. //! -//! These traversals have limited flexibility. Only a small number of "types of +//! This traversal has limited flexibility. Only a small number of "types of //! interest" within the complex data structures can receive custom -//! modification (when folding) or custom visitation (when visiting). These are -//! the ones containing the most important type-related information, such as -//! `Ty`, `Predicate`, `Region`, and `Const`. +//! modification. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. //! -//! There are three traits involved in each traversal type. -//! - `TypeFoldable`. This is implemented once for many types. This includes -//! both: +//! There are three groups of traits involved in each traversal. +//! - `TypeFoldable`. This is implemented once for many types, including: //! - Types of interest, for which the the methods delegate to the -//! folder/visitor. +//! folder. //! - All other types, including generic containers like `Vec` and `Option`. -//! It defines a "skeleton" of how they should be traversed, for both -//! folding and visiting. +//! It defines a "skeleton" of how they should be folded. //! - `TypeSuperFoldable`. This is implemented only for each type of interest, -//! and defines the traversal "skeleton" for these types. -//! - `TypeFolder`/`FallibleTypeFolder` (for infallible/fallible folding -//! traversals) or `TypeVisitor` (for visiting traversals). One of these is -//! implemented for each folder/visitor. This defines how types of interest -//! are folded/visited. +//! and defines the folding "skeleton" for these types. +//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each +//! folder. This defines how types of interest are folded. //! -//! This means each traversal is a mixture of (a) generic traversal operations, -//! and (b) custom fold/visit operations that are specific to the -//! folder/visitor. +//! This means each fold is a mixture of (a) generic folding operations, and (b) +//! custom fold operations that are specific to the folder. //! - The `TypeFoldable` impls handle most of the traversal, and call into -//! `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` when they encounter a -//! type of interest. -//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may call into another -//! `TypeFoldable` impl, because some of the types of interest are recursive -//! and can contain other types of interest. -//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may also call into -//! a `TypeSuperFoldable` impl, because each folder/visitor might provide -//! custom handling only for some types of interest, or only for some -//! variants of each type of interest, and then use default traversal for the -//! remaining cases. +//! `TypeFolder`/`FallibleTypeFolder` when they encounter a type of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may call into another `TypeFoldable` +//! impl, because some of the types of interest are recursive and can contain +//! other types of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may also call into a `TypeSuperFoldable` +//! impl, because each folder might provide custom handling only for some types +//! of interest, or only for some variants of each type of interest, and then +//! use default traversal for the remaining cases. //! //! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: -//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be folded like so: //! ```text -//! s.visit_with(visitor) calls -//! - ty.visit_with(visitor) calls -//! - visitor.visit_ty(ty) may call -//! - ty.super_visit_with(visitor) -//! - u.visit_with(visitor) +//! s.fold_with(folder) calls +//! - ty.fold_with(folder) calls +//! - folder.fold_ty(ty) may call +//! - ty.super_fold_with(folder) +//! - u.fold_with(folder) //! ``` use crate::mir; -use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; -use rustc_errors::ErrorGuaranteed; +use crate::ty::{self, Binder, Ty, TyCtxt, TypeVisitable}; use rustc_hir::def_id::DefId; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sso::SsoHashSet; use std::collections::BTreeMap; -use std::fmt; -use std::ops::ControlFlow; -/// This trait is implemented for every type that can be folded/visited, +/// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. /// /// To implement this conveniently, use the derive macro located in /// `rustc_macros`. -pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { +pub trait TypeFoldable<'tcx>: TypeVisitable<'tcx> { /// The entry point for folding. To fold a value `t` with a folder `f` /// call: `t.try_fold_with(f)`. /// @@ -89,115 +73,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn fold_with>(self, folder: &mut F) -> Self { self.try_fold_with(folder).into_ok() } - - /// The entry point for visiting. To visit a value `t` with a visitor `v` - /// call: `t.visit_with(v)`. - /// - /// For most types, this just traverses the value, calling `visit_with` on - /// each field/element. - /// - /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as - /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to - /// `TypeVisitor`. - fn visit_with>(&self, visitor: &mut V) -> ControlFlow; - - /// Returns `true` if `self` has any late-bound regions that are either - /// bound by `binder` or bound by some binder outside of `binder`. - /// If `binder` is `ty::INNERMOST`, this indicates whether - /// there are any late-bound regions that appear free. - fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() - } - - /// Returns `true` if this `self` has any regions that escape `binder` (and - /// hence are not bound by it). - fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { - self.has_vars_bound_at_or_above(binder.shifted_in(1)) - } - - fn has_escaping_bound_vars(&self) -> bool { - self.has_vars_bound_at_or_above(ty::INNERMOST) - } - - #[instrument(level = "trace")] - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) - } - fn has_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) - } - fn has_opaque_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) - } - fn references_error(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_ERROR) - } - fn error_reported(&self) -> Option { - if self.references_error() { - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) - } else { - None - } - } - fn has_param_types_or_consts(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) - } - fn has_infer_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_INFER) - } - fn has_infer_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER) - } - fn has_infer_types_or_consts(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER) - } - fn needs_infer(&self) -> bool { - self.has_type_flags(TypeFlags::NEEDS_INFER) - } - fn has_placeholders(&self) -> bool { - self.has_type_flags( - TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER, - ) - } - fn needs_subst(&self) -> bool { - self.has_type_flags(TypeFlags::NEEDS_SUBST) - } - /// "Free" regions in this context means that it has any region - /// that is not (a) erased or (b) late-bound. - fn has_free_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - fn has_erased_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_ERASED) - } - - /// True if there are any un-erased free regions. - fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - /// Indicates whether this value references only 'global' - /// generic parameters that are the same regardless of what fn we are - /// in. This is used for caching. - fn is_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) - } - - /// True if there are any late-bound regions - fn has_late_bound_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) - } - - /// Indicates whether this value still has parameters/placeholders/inference variables - /// which could be replaced later, in a way that would change the results of `impl` - /// specialization. - fn still_further_specializable(&self) -> bool { - self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) - } } // This trait is implemented for types of interest. @@ -219,14 +94,6 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> { fn super_fold_with>(self, folder: &mut F) -> Self { self.try_super_fold_with(folder).into_ok() } - - /// Provides a default visit for a type of interest. This should only be - /// called within `TypeVisitor` methods, when a non-custom traversal is - /// desired for the value of the type of interest passed to that method. - /// For example, in `MyVisitor::visit_ty(ty)`, it is valid to call - /// `ty.super_visit_with(self)`, but any other visiting should be done - /// with `xyz.visit_with(self)`. - fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow; } /// This trait is implemented for every infallible folding traversal. There is @@ -376,44 +243,6 @@ where } } -/// This trait is implemented for every visiting traversal. There is a visit -/// method defined for every type of interest. Each such method has a default -/// that recurses into the type's fields in a non-custom fashion. -pub trait TypeVisitor<'tcx>: Sized { - type BreakTy = !; - - fn visit_binder>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow { - t.super_visit_with(self) - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { - c.super_visit_with(self) - } - - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { - uv.super_visit_with(self) - } - - fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { - p.super_visit_with(self) - } - - fn visit_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> ControlFlow { - c.super_visit_with(self) - } -} - /////////////////////////////////////////////////////////////////////////// // Some sample folders @@ -472,99 +301,6 @@ impl<'tcx> TyCtxt<'tcx> { { value.fold_with(&mut RegionFolder::new(self, &mut f)) } - - /// Invoke `callback` on every region appearing free in `value`. - pub fn for_each_free_region( - self, - value: &impl TypeFoldable<'tcx>, - mut callback: impl FnMut(ty::Region<'tcx>), - ) { - self.any_free_region_meets(value, |r| { - callback(r); - false - }); - } - - /// Returns `true` if `callback` returns true for every region appearing free in `value`. - pub fn all_free_regions_meet( - self, - value: &impl TypeFoldable<'tcx>, - mut callback: impl FnMut(ty::Region<'tcx>) -> bool, - ) -> bool { - !self.any_free_region_meets(value, |r| !callback(r)) - } - - /// Returns `true` if `callback` returns true for some region appearing free in `value`. - pub fn any_free_region_meets( - self, - value: &impl TypeFoldable<'tcx>, - callback: impl FnMut(ty::Region<'tcx>) -> bool, - ) -> bool { - struct RegionVisitor { - /// The index of a binder *just outside* the things we have - /// traversed. If we encounter a bound region bound by this - /// binder or one outer to it, it appears free. Example: - /// - /// ```ignore (illustrative) - /// for<'a> fn(for<'b> fn(), T) - /// // ^ ^ ^ ^ - /// // | | | | here, would be shifted in 1 - /// // | | | here, would be shifted in 2 - /// // | | here, would be `INNERMOST` shifted in by 1 - /// // | here, initially, binder would be `INNERMOST` - /// ``` - /// - /// You see that, initially, *any* bound value is free, - /// because we've not traversed any binders. As we pass - /// through a binder, we shift the `outer_index` by 1 to - /// account for the new binder that encloses us. - outer_index: ty::DebruijnIndex, - callback: F, - } - - impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor - where - F: FnMut(ty::Region<'tcx>) -> bool, - { - type BreakTy = (); - - fn visit_binder>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow { - self.outer_index.shift_in(1); - let result = t.super_visit_with(self); - self.outer_index.shift_out(1); - result - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - match *r { - ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => { - ControlFlow::CONTINUE - } - _ => { - if (self.callback)(r) { - ControlFlow::BREAK - } else { - ControlFlow::CONTINUE - } - } - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - // We're only interested in types involving regions - if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { - ty.super_visit_with(self) - } else { - ControlFlow::CONTINUE - } - } - } - - value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() - } } /// Folds over the substructure of a type, visiting its component @@ -861,45 +597,6 @@ impl<'tcx> TyCtxt<'tcx> { ) } - /// Returns a set of all late-bound regions that are constrained - /// by `value`, meaning that if we instantiate those LBR with - /// variables and equate `value` with something else, those - /// variables will also be equated. - pub fn collect_constrained_late_bound_regions( - self, - value: &Binder<'tcx, T>, - ) -> FxHashSet - where - T: TypeFoldable<'tcx>, - { - self.collect_late_bound_regions(value, true) - } - - /// Returns a set of all late-bound regions that appear in `value` anywhere. - pub fn collect_referenced_late_bound_regions( - self, - value: &Binder<'tcx, T>, - ) -> FxHashSet - where - T: TypeFoldable<'tcx>, - { - self.collect_late_bound_regions(value, false) - } - - fn collect_late_bound_regions( - self, - value: &Binder<'tcx, T>, - just_constraint: bool, - ) -> FxHashSet - where - T: TypeFoldable<'tcx>, - { - let mut collector = LateBoundRegionsCollector::new(just_constraint); - let result = value.as_ref().skip_binder().visit_with(&mut collector); - assert!(result.is_continue()); // should never have stopped early - collector.regions - } - /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions(self, value: Binder<'tcx, T>) -> T @@ -940,103 +637,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -pub struct ValidateBoundVars<'tcx> { - bound_vars: &'tcx ty::List, - binder_index: ty::DebruijnIndex, - // We may encounter the same variable at different levels of binding, so - // this can't just be `Ty` - visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, -} - -impl<'tcx> ValidateBoundVars<'tcx> { - pub fn new(bound_vars: &'tcx ty::List) -> Self { - ValidateBoundVars { - bound_vars, - binder_index: ty::INNERMOST, - visited: SsoHashSet::default(), - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { - type BreakTy = (); - - fn visit_binder>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.outer_exclusive_binder() < self.binder_index - || !self.visited.insert((self.binder_index, t)) - { - return ControlFlow::BREAK; - } - match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - if self.bound_vars.len() <= bound_ty.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); - } - let list_var = self.bound_vars[bound_ty.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Ty(kind) => { - if kind != bound_ty.kind { - bug!( - "Mismatched type kinds: {:?} doesn't var in list {:?}", - bound_ty.kind, - list_var - ); - } - } - _ => { - bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) - } - } - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - match *r { - ty::ReLateBound(index, br) if index == self.binder_index => { - if self.bound_vars.len() <= br.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); - } - let list_var = self.bound_vars[br.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Region(kind) => { - if kind != br.kind { - bug!( - "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", - br.kind, - list_var, - self.bound_vars - ); - } - } - _ => bug!( - "Mismatched bound variable kinds! Expected region, found {:?}", - list_var - ), - } - } - - _ => (), - }; - - r.super_visit_with(self) - } -} - /////////////////////////////////////////////////////////////////////////// // Shifter // @@ -1141,301 +741,3 @@ where value.fold_with(&mut Shifter::new(tcx, amount)) } - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundEscapingVars; - -/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a -/// bound region or a bound type. -/// -/// So, for example, consider a type like the following, which has two binders: -/// -/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope -/// -/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the -/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner -/// fn type*, that type has an escaping region: `'a`. -/// -/// Note that what I'm calling an "escaping var" is often just called a "free var". However, -/// we already use the term "free var". It refers to the regions or types that we use to represent -/// bound regions or type params on a fn definition while we are type checking its body. -/// -/// To clarify, conceptually there is no particular difference between -/// an "escaping" var and a "free" var. However, there is a big -/// difference in practice. Basically, when "entering" a binding -/// level, one is generally required to do some sort of processing to -/// a bound var, such as replacing it with a fresh/placeholder -/// var, or making an entry in the environment to represent the -/// scope to which it is attached, etc. An escaping var represents -/// a bound var for which this processing has not yet been done. -struct HasEscapingVarsVisitor { - /// Anything bound by `outer_index` or "above" is escaping. - outer_index: ty::DebruijnIndex, -} - -impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { - type BreakTy = FoundEscapingVars; - - fn visit_binder>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow { - self.outer_index.shift_in(1); - let result = t.super_visit_with(self); - self.outer_index.shift_out(1); - result - } - - #[inline] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - // If the outer-exclusive-binder is *strictly greater* than - // `outer_index`, that means that `t` contains some content - // bound at `outer_index` or above (because - // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. - if t.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - // If the region is bound by `outer_index` or anything outside - // of outer index, then it escapes the binders we have - // visited. - if r.bound_at_or_above_binder(self.outer_index) { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } - - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { - // we don't have a `visit_infer_const` callback, so we have to - // hook in here to catch this case (annoying...), but - // otherwise we do want to remember to visit the rest of the - // const, as it has types/regions embedded in a lot of other - // places. - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { - ControlFlow::Break(FoundEscapingVars) - } - _ => ct.super_visit_with(self), - } - } - - #[inline] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - if predicate.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::CONTINUE - } - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundFlags; - -// FIXME: Optimize for checking for infer flags -struct HasTypeFlagsVisitor { - flags: ty::TypeFlags, -} - -impl std::fmt::Debug for HasTypeFlagsVisitor { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.flags.fmt(fmt) - } -} - -impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { - type BreakTy = FoundFlags; - - #[inline] - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - let flags = t.flags(); - trace!(t.flags=?t.flags()); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(skip(self), level = "trace")] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - let flags = r.type_flags(); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { - let flags = FlagComputation::for_const(c); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { - let flags = FlagComputation::for_unevaluated_const(uv); - trace!(r.flags=?flags); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - #[instrument(level = "trace")] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - debug!( - "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", - predicate, - predicate.flags(), - self.flags - ); - if predicate.flags().intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::CONTINUE - } - } -} - -/// Collects all the late-bound regions at the innermost binding level -/// into a hash set. -struct LateBoundRegionsCollector { - current_index: ty::DebruijnIndex, - regions: FxHashSet, - - /// `true` if we only want regions that are known to be - /// "constrained" when you equate this type with another type. In - /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating - /// them constraints `'a == 'b`. But if you have `<&'a u32 as - /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those - /// types may mean that `'a` and `'b` don't appear in the results, - /// so they are not considered *constrained*. - just_constrained: bool, -} - -impl LateBoundRegionsCollector { - fn new(just_constrained: bool) -> Self { - LateBoundRegionsCollector { - current_index: ty::INNERMOST, - regions: Default::default(), - just_constrained, - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { - fn visit_binder>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow { - self.current_index.shift_in(1); - let result = t.super_visit_with(self); - self.current_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - // if we are only looking for "constrained" region, we have to - // ignore the inputs to a projection, as they may not appear - // in the normalized form - if self.just_constrained { - if let ty::Projection(..) = t.kind() { - return ControlFlow::CONTINUE; - } - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { - // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear - // in the normalized form - if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { - return ControlFlow::CONTINUE; - } - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - if let ty::ReLateBound(debruijn, br) = *r { - if debruijn == self.current_index { - self.regions.insert(br.kind); - } - } - ControlFlow::CONTINUE - } -} - -/// Finds the max universe present -pub struct MaxUniverse { - max_universe: ty::UniverseIndex, -} - -impl MaxUniverse { - pub fn new() -> Self { - MaxUniverse { max_universe: ty::UniverseIndex::ROOT } - } - - pub fn max_universe(self) -> ty::UniverseIndex { - self.max_universe - } -} - -impl<'tcx> TypeVisitor<'tcx> for MaxUniverse { - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if let ty::Placeholder(placeholder) = t.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow { - if let ty::ConstKind::Placeholder(placeholder) = c.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - if let ty::RePlaceholder(placeholder) = *r { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); - } - - ControlFlow::CONTINUE - } -} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c71905b4aca..e546920a084 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -9,9 +9,8 @@ //! //! ["The `ty` module: representing types"]: https://rustc-dev-guide.rust-lang.org/ty.html -pub use self::fold::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitor, -}; +pub use self::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; @@ -110,6 +109,7 @@ pub mod relate; pub mod subst; pub mod trait_def; pub mod util; +pub mod visit; pub mod vtable; pub mod walk; diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs new file mode 100644 index 00000000000..5365067209a --- /dev/null +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -0,0 +1,745 @@ +//! A visiting traversal mechanism for complex data structures that contain type +//! information. +//! +//! This is a read-only traversal of the data structure. +//! +//! This traversal has limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! visitation. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. +//! +//! There are three groups of traits involved in each traversal. +//! - `TypeVisitable`. This is implemented once for many types, including: +//! - Types of interest, for which the the methods delegate to the +//! visitor. +//! - All other types, including generic containers like `Vec` and `Option`. +//! It defines a "skeleton" of how they should be visited. +//! - `TypeSuperVisitable`. This is implemented only for each type of interest, +//! and defines the visiting "skeleton" for these types. +//! - `TypeVisitor`. This is implemented for each visitor. This defines how +//! types of interest are visited. +//! +//! This means each visit is a mixture of (a) generic visiting operations, and (b) +//! custom visit operations that are specific to the visitor. +//! - The `TypeVisitable` impls handle most of the traversal, and call into +//! `TypeVisitor` when they encounter a type of interest. +//! - A `TypeVisitor` may call into another `TypeVisitable` impl, because some of +//! the types of interest are recursive and can contain other types of interest. +//! - A `TypeVisitor` may also call into a `TypeSuperVisitable` impl, because each +//! visitor might provide custom handling only for some types of interest, or +//! only for some variants of each type of interest, and then use default +//! traversal for the remaining cases. +//! +//! For example, if you have `struct S(Ty, U)` where `S: TypeVisitable` and `U: +//! TypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! ```text +//! s.visit_with(visitor) calls +//! - ty.visit_with(visitor) calls +//! - visitor.visit_ty(ty) may call +//! - ty.super_visit_with(visitor) +//! - u.visit_with(visitor) +//! ``` +use crate::mir; +use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; +use rustc_errors::ErrorGuaranteed; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; +use std::fmt; +use std::ops::ControlFlow; + +/// This trait is implemented for every type that can be visited, +/// providing the skeleton of the traversal. +/// +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. +pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { + /// The entry point for visiting. To visit a value `t` with a visitor `v` + /// call: `t.visit_with(v)`. + /// + /// For most types, this just traverses the value, calling `visit_with` on + /// each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of this method + /// that calls a visitor method specifically for that type (such as + /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to + /// `TypeVisitor`. + fn visit_with>(&self, visitor: &mut V) -> ControlFlow; + + /// Returns `true` if `self` has any late-bound regions that are either + /// bound by `binder` or bound by some binder outside of `binder`. + /// If `binder` is `ty::INNERMOST`, this indicates whether + /// there are any late-bound regions that appear free. + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + } + + /// Returns `true` if this `self` has any regions that escape `binder` (and + /// hence are not bound by it). + fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { + self.has_vars_bound_at_or_above(binder.shifted_in(1)) + } + + fn has_escaping_bound_vars(&self) -> bool { + self.has_vars_bound_at_or_above(ty::INNERMOST) + } + + #[instrument(level = "trace")] + fn has_type_flags(&self, flags: TypeFlags) -> bool { + self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) + } + fn has_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PROJECTION) + } + fn has_opaque_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) + } + fn references_error(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_ERROR) + } + fn error_reported(&self) -> Option { + if self.references_error() { + Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + } else { + None + } + } + fn has_param_types_or_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) + } + fn has_infer_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_INFER) + } + fn has_infer_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER) + } + fn has_infer_types_or_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER) + } + fn needs_infer(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_INFER) + } + fn has_placeholders(&self) -> bool { + self.has_type_flags( + TypeFlags::HAS_RE_PLACEHOLDER + | TypeFlags::HAS_TY_PLACEHOLDER + | TypeFlags::HAS_CT_PLACEHOLDER, + ) + } + fn needs_subst(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_SUBST) + } + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + fn has_erased_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_ERASED) + } + + /// True if there are any un-erased free regions. + fn has_erasable_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// Indicates whether this value references only 'global' + /// generic parameters that are the same regardless of what fn we are + /// in. This is used for caching. + fn is_global(&self) -> bool { + !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) + } + + /// True if there are any late-bound regions + fn has_late_bound_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) + } + + /// Indicates whether this value still has parameters/placeholders/inference variables + /// which could be replaced later, in a way that would change the results of `impl` + /// specialization. + fn still_further_specializable(&self) -> bool { + self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) + } +} + +pub trait TypeSuperVisitable<'tcx>: TypeVisitable<'tcx> { + /// Provides a default visit for a type of interest. This should only be + /// called within `TypeVisitor` methods, when a non-custom traversal is + /// desired for the value of the type of interest passed to that method. + /// For example, in `MyVisitor::visit_ty(ty)`, it is valid to call + /// `ty.super_visit_with(self)`, but any other visiting should be done + /// with `xyz.visit_with(self)`. + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow; +} + +/// This trait is implemented for every visiting traversal. There is a visit +/// method defined for every type of interest. Each such method has a default +/// that recurses into the type's fields in a non-custom fashion. +pub trait TypeVisitor<'tcx>: Sized { + type BreakTy = !; + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + t.super_visit_with(self) + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + r.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + c.super_visit_with(self) + } + + fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { + uv.super_visit_with(self) + } + + fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { + p.super_visit_with(self) + } + + fn visit_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> ControlFlow { + c.super_visit_with(self) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Region folder + +impl<'tcx> TyCtxt<'tcx> { + /// Invoke `callback` on every region appearing free in `value`. + pub fn for_each_free_region( + self, + value: &impl TypeVisitable<'tcx>, + mut callback: impl FnMut(ty::Region<'tcx>), + ) { + self.any_free_region_meets(value, |r| { + callback(r); + false + }); + } + + /// Returns `true` if `callback` returns true for every region appearing free in `value`. + pub fn all_free_regions_meet( + self, + value: &impl TypeVisitable<'tcx>, + mut callback: impl FnMut(ty::Region<'tcx>) -> bool, + ) -> bool { + !self.any_free_region_meets(value, |r| !callback(r)) + } + + /// Returns `true` if `callback` returns true for some region appearing free in `value`. + pub fn any_free_region_meets( + self, + value: &impl TypeVisitable<'tcx>, + callback: impl FnMut(ty::Region<'tcx>) -> bool, + ) -> bool { + struct RegionVisitor { + /// The index of a binder *just outside* the things we have + /// traversed. If we encounter a bound region bound by this + /// binder or one outer to it, it appears free. Example: + /// + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(), T) + /// // ^ ^ ^ ^ + /// // | | | | here, would be shifted in 1 + /// // | | | here, would be shifted in 2 + /// // | | here, would be `INNERMOST` shifted in by 1 + /// // | here, initially, binder would be `INNERMOST` + /// ``` + /// + /// You see that, initially, *any* bound value is free, + /// because we've not traversed any binders. As we pass + /// through a binder, we shift the `outer_index` by 1 to + /// account for the new binder that encloses us. + outer_index: ty::DebruijnIndex, + callback: F, + } + + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor + where + F: FnMut(ty::Region<'tcx>) -> bool, + { + type BreakTy = (); + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + match *r { + ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => { + ControlFlow::CONTINUE + } + _ => { + if (self.callback)(r) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + // We're only interested in types involving regions + if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { + ty.super_visit_with(self) + } else { + ControlFlow::CONTINUE + } + } + } + + value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() + } + + /// Returns a set of all late-bound regions that are constrained + /// by `value`, meaning that if we instantiate those LBR with + /// variables and equate `value` with something else, those + /// variables will also be equated. + pub fn collect_constrained_late_bound_regions( + self, + value: &Binder<'tcx, T>, + ) -> FxHashSet + where + T: TypeVisitable<'tcx>, + { + self.collect_late_bound_regions(value, true) + } + + /// Returns a set of all late-bound regions that appear in `value` anywhere. + pub fn collect_referenced_late_bound_regions( + self, + value: &Binder<'tcx, T>, + ) -> FxHashSet + where + T: TypeVisitable<'tcx>, + { + self.collect_late_bound_regions(value, false) + } + + fn collect_late_bound_regions( + self, + value: &Binder<'tcx, T>, + just_constraint: bool, + ) -> FxHashSet + where + T: TypeVisitable<'tcx>, + { + let mut collector = LateBoundRegionsCollector::new(just_constraint); + let result = value.as_ref().skip_binder().visit_with(&mut collector); + assert!(result.is_continue()); // should never have stopped early + collector.regions + } +} + +pub struct ValidateBoundVars<'tcx> { + bound_vars: &'tcx ty::List, + binder_index: ty::DebruijnIndex, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, +} + +impl<'tcx> ValidateBoundVars<'tcx> { + pub fn new(bound_vars: &'tcx ty::List) -> Self { + ValidateBoundVars { + bound_vars, + binder_index: ty::INNERMOST, + visited: SsoHashSet::default(), + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { + type BreakTy = (); + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if t.outer_exclusive_binder() < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::BREAK; + } + match *t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + if self.bound_vars.len() <= bound_ty.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); + } + let list_var = self.bound_vars[bound_ty.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Ty(kind) => { + if kind != bound_ty.kind { + bug!( + "Mismatched type kinds: {:?} doesn't var in list {:?}", + bound_ty.kind, + list_var + ); + } + } + _ => { + bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) + } + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => { + if self.bound_vars.len() <= br.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); + } + let list_var = self.bound_vars[br.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Region(kind) => { + if kind != br.kind { + bug!( + "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", + br.kind, + list_var, + self.bound_vars + ); + } + } + _ => bug!( + "Mismatched bound variable kinds! Expected region, found {:?}", + list_var + ), + } + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundEscapingVars; + +/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a +/// bound region or a bound type. +/// +/// So, for example, consider a type like the following, which has two binders: +/// +/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope +/// +/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the +/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner +/// fn type*, that type has an escaping region: `'a`. +/// +/// Note that what I'm calling an "escaping var" is often just called a "free var". However, +/// we already use the term "free var". It refers to the regions or types that we use to represent +/// bound regions or type params on a fn definition while we are type checking its body. +/// +/// To clarify, conceptually there is no particular difference between +/// an "escaping" var and a "free" var. However, there is a big +/// difference in practice. Basically, when "entering" a binding +/// level, one is generally required to do some sort of processing to +/// a bound var, such as replacing it with a fresh/placeholder +/// var, or making an entry in the environment to represent the +/// scope to which it is attached, etc. An escaping var represents +/// a bound var for which this processing has not yet been done. +struct HasEscapingVarsVisitor { + /// Anything bound by `outer_index` or "above" is escaping. + outer_index: ty::DebruijnIndex, +} + +impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { + type BreakTy = FoundEscapingVars; + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + #[inline] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + // If the outer-exclusive-binder is *strictly greater* than + // `outer_index`, that means that `t` contains some content + // bound at `outer_index` or above (because + // `outer_exclusive_binder` is always 1 higher than the + // content in `t`). Therefore, `t` has some escaping vars. + if t.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + // If the region is bound by `outer_index` or anything outside + // of outer index, then it escapes the binders we have + // visited. + if r.bound_at_or_above_binder(self.outer_index) { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { + // we don't have a `visit_infer_const` callback, so we have to + // hook in here to catch this case (annoying...), but + // otherwise we do want to remember to visit the rest of the + // const, as it has types/regions embedded in a lot of other + // places. + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { + ControlFlow::Break(FoundEscapingVars) + } + _ => ct.super_visit_with(self), + } + } + + #[inline] + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { + if predicate.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::CONTINUE + } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundFlags; + +// FIXME: Optimize for checking for infer flags +struct HasTypeFlagsVisitor { + flags: ty::TypeFlags, +} + +impl std::fmt::Debug for HasTypeFlagsVisitor { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.flags.fmt(fmt) + } +} + +impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { + type BreakTy = FoundFlags; + + #[inline] + #[instrument(skip(self), level = "trace")] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + let flags = t.flags(); + trace!(t.flags=?t.flags()); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(skip(self), level = "trace")] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + let flags = r.type_flags(); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + let flags = FlagComputation::for_const(c); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { + let flags = FlagComputation::for_unevaluated_const(uv); + trace!(r.flags=?flags); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } + + #[inline] + #[instrument(level = "trace")] + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { + debug!( + "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", + predicate, + predicate.flags(), + self.flags + ); + if predicate.flags().intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::CONTINUE + } + } +} + +/// Collects all the late-bound regions at the innermost binding level +/// into a hash set. +struct LateBoundRegionsCollector { + current_index: ty::DebruijnIndex, + regions: FxHashSet, + + /// `true` if we only want regions that are known to be + /// "constrained" when you equate this type with another type. In + /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating + /// them constraints `'a == 'b`. But if you have `<&'a u32 as + /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those + /// types may mean that `'a` and `'b` don't appear in the results, + /// so they are not considered *constrained*. + just_constrained: bool, +} + +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { + LateBoundRegionsCollector { + current_index: ty::INNERMOST, + regions: Default::default(), + just_constrained, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.current_index.shift_in(1); + let result = t.super_visit_with(self); + self.current_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + // if we are only looking for "constrained" region, we have to + // ignore the inputs to a projection, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::Projection(..) = t.kind() { + return ControlFlow::CONTINUE; + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + // if we are only looking for "constrained" region, we have to + // ignore the inputs of an unevaluated const, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::ConstKind::Unevaluated(..) = c.kind() { + return ControlFlow::CONTINUE; + } + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + if let ty::ReLateBound(debruijn, br) = *r { + if debruijn == self.current_index { + self.regions.insert(br.kind); + } + } + ControlFlow::CONTINUE + } +} + +/// Finds the max universe present +pub struct MaxUniverse { + max_universe: ty::UniverseIndex, +} + +impl MaxUniverse { + pub fn new() -> Self { + MaxUniverse { max_universe: ty::UniverseIndex::ROOT } + } + + pub fn max_universe(self) -> ty::UniverseIndex { + self.max_universe + } +} + +impl<'tcx> TypeVisitor<'tcx> for MaxUniverse { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if let ty::Placeholder(placeholder) = t.kind() { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow { + if let ty::ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + if let ty::RePlaceholder(placeholder) = *r { + self.max_universe = ty::UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + ControlFlow::CONTINUE + } +}