rustc_middle/ty/
visit.rs

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