rustc_middle/ty/
visit.rs

1use std::ops::ControlFlow;
2
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_type_ir::fold::TypeFoldable;
5pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
6
7use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
8
9///////////////////////////////////////////////////////////////////////////
10// Region folder
11
12impl<'tcx> TyCtxt<'tcx> {
13    /// Invoke `callback` on every region appearing free in `value`.
14    pub fn for_each_free_region(
15        self,
16        value: &impl TypeVisitable<TyCtxt<'tcx>>,
17        mut callback: impl FnMut(ty::Region<'tcx>),
18    ) {
19        self.any_free_region_meets(value, |r| {
20            callback(r);
21            false
22        });
23    }
24
25    /// Returns `true` if `callback` returns true for every region appearing free in `value`.
26    pub fn all_free_regions_meet(
27        self,
28        value: &impl TypeVisitable<TyCtxt<'tcx>>,
29        mut callback: impl FnMut(ty::Region<'tcx>) -> bool,
30    ) -> bool {
31        !self.any_free_region_meets(value, |r| !callback(r))
32    }
33
34    /// Returns `true` if `callback` returns true for some region appearing free in `value`.
35    pub fn any_free_region_meets(
36        self,
37        value: &impl TypeVisitable<TyCtxt<'tcx>>,
38        callback: impl FnMut(ty::Region<'tcx>) -> bool,
39    ) -> bool {
40        struct RegionVisitor<F> {
41            /// The index of a binder *just outside* the things we have
42            /// traversed. If we encounter a bound region bound by this
43            /// binder or one outer to it, it appears free. Example:
44            ///
45            /// ```ignore (illustrative)
46            ///       for<'a> fn(for<'b> fn(), T)
47            /// // ^          ^          ^     ^
48            /// // |          |          |     | here, would be shifted in 1
49            /// // |          |          | here, would be shifted in 2
50            /// // |          | here, would be `INNERMOST` shifted in by 1
51            /// // | here, initially, binder would be `INNERMOST`
52            /// ```
53            ///
54            /// You see that, initially, *any* bound value is free,
55            /// because we've not traversed any binders. As we pass
56            /// through a binder, we shift the `outer_index` by 1 to
57            /// account for the new binder that encloses us.
58            outer_index: ty::DebruijnIndex,
59            callback: F,
60        }
61
62        impl<'tcx, F> TypeVisitor<TyCtxt<'tcx>> for RegionVisitor<F>
63        where
64            F: FnMut(ty::Region<'tcx>) -> bool,
65        {
66            type Result = ControlFlow<()>;
67
68            fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
69                &mut self,
70                t: &Binder<'tcx, T>,
71            ) -> Self::Result {
72                self.outer_index.shift_in(1);
73                let result = t.super_visit_with(self);
74                self.outer_index.shift_out(1);
75                result
76            }
77
78            fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
79                match *r {
80                    ty::ReBound(debruijn, _) if debruijn < self.outer_index => {
81                        ControlFlow::Continue(())
82                    }
83                    _ => {
84                        if (self.callback)(r) {
85                            ControlFlow::Break(())
86                        } else {
87                            ControlFlow::Continue(())
88                        }
89                    }
90                }
91            }
92
93            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
94                // We're only interested in types involving regions
95                if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) {
96                    ty.super_visit_with(self)
97                } else {
98                    ControlFlow::Continue(())
99                }
100            }
101        }
102
103        value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break()
104    }
105
106    /// Returns a set of all late-bound regions that are constrained
107    /// by `value`, meaning that if we instantiate those LBR with
108    /// variables and equate `value` with something else, those
109    /// variables will also be equated.
110    pub fn collect_constrained_late_bound_regions<T>(
111        self,
112        value: Binder<'tcx, T>,
113    ) -> FxIndexSet<ty::BoundRegionKind>
114    where
115        T: TypeFoldable<TyCtxt<'tcx>>,
116    {
117        self.collect_late_bound_regions(value, true)
118    }
119
120    /// Returns a set of all late-bound regions that appear in `value` anywhere.
121    pub fn collect_referenced_late_bound_regions<T>(
122        self,
123        value: Binder<'tcx, T>,
124    ) -> FxIndexSet<ty::BoundRegionKind>
125    where
126        T: TypeFoldable<TyCtxt<'tcx>>,
127    {
128        self.collect_late_bound_regions(value, false)
129    }
130
131    fn collect_late_bound_regions<T>(
132        self,
133        value: Binder<'tcx, T>,
134        just_constrained: bool,
135    ) -> FxIndexSet<ty::BoundRegionKind>
136    where
137        T: TypeFoldable<TyCtxt<'tcx>>,
138    {
139        let mut collector = LateBoundRegionsCollector::new(just_constrained);
140        let value = value.skip_binder();
141        let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
142        value.visit_with(&mut collector);
143        collector.regions
144    }
145}
146
147/// Collects all the late-bound regions at the innermost binding level
148/// into a hash set.
149struct LateBoundRegionsCollector {
150    current_index: ty::DebruijnIndex,
151    regions: FxIndexSet<ty::BoundRegionKind>,
152
153    /// `true` if we only want regions that are known to be
154    /// "constrained" when you equate this type with another type. In
155    /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating
156    /// them constraints `'a == 'b`. But if you have `<&'a u32 as
157    /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those
158    /// types may mean that `'a` and `'b` don't appear in the results,
159    /// so they are not considered *constrained*.
160    just_constrained: bool,
161}
162
163impl LateBoundRegionsCollector {
164    fn new(just_constrained: bool) -> Self {
165        Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
166    }
167}
168
169impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
170    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
171        self.current_index.shift_in(1);
172        t.super_visit_with(self);
173        self.current_index.shift_out(1);
174    }
175
176    fn visit_ty(&mut self, t: Ty<'tcx>) {
177        if self.just_constrained {
178            match t.kind() {
179                // If we are only looking for "constrained" regions, we have to ignore the
180                // inputs to a projection as they may not appear in the normalized form.
181                ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
182                    return;
183                }
184                // All weak alias types should've been expanded beforehand.
185                ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
186                _ => {}
187            }
188        }
189
190        t.super_visit_with(self)
191    }
192
193    fn visit_const(&mut self, c: ty::Const<'tcx>) {
194        // if we are only looking for "constrained" region, we have to
195        // ignore the inputs of an unevaluated const, as they may not appear
196        // in the normalized form
197        if self.just_constrained {
198            if let ty::ConstKind::Unevaluated(..) = c.kind() {
199                return;
200            }
201        }
202
203        c.super_visit_with(self)
204    }
205
206    fn visit_region(&mut self, r: ty::Region<'tcx>) {
207        if let ty::ReBound(debruijn, br) = *r {
208            if debruijn == self.current_index {
209                self.regions.insert(br.kind);
210            }
211        }
212    }
213}
214
215/// Finds the max universe present
216pub struct MaxUniverse {
217    max_universe: ty::UniverseIndex,
218}
219
220impl MaxUniverse {
221    pub fn new() -> Self {
222        MaxUniverse { max_universe: ty::UniverseIndex::ROOT }
223    }
224
225    pub fn max_universe(self) -> ty::UniverseIndex {
226        self.max_universe
227    }
228}
229
230impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxUniverse {
231    fn visit_ty(&mut self, t: Ty<'tcx>) {
232        if let ty::Placeholder(placeholder) = t.kind() {
233            self.max_universe = ty::UniverseIndex::from_u32(
234                self.max_universe.as_u32().max(placeholder.universe.as_u32()),
235            );
236        }
237
238        t.super_visit_with(self)
239    }
240
241    fn visit_const(&mut self, c: ty::consts::Const<'tcx>) {
242        if let ty::ConstKind::Placeholder(placeholder) = c.kind() {
243            self.max_universe = ty::UniverseIndex::from_u32(
244                self.max_universe.as_u32().max(placeholder.universe.as_u32()),
245            );
246        }
247
248        c.super_visit_with(self)
249    }
250
251    fn visit_region(&mut self, r: ty::Region<'tcx>) {
252        if let ty::RePlaceholder(placeholder) = *r {
253            self.max_universe = ty::UniverseIndex::from_u32(
254                self.max_universe.as_u32().max(placeholder.universe.as_u32()),
255            );
256        }
257    }
258}