Skip to main content

rustc_middle/ty/
visit.rs

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