rustc_middle/ty/
fold.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_hir::def_id::DefId;
3use rustc_type_ir::data_structures::DelayedMap;
4
5use crate::ty::{
6    self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
7    TypeVisitableExt,
8};
9
10///////////////////////////////////////////////////////////////////////////
11// Some sample folders
12
13pub struct BottomUpFolder<'tcx, F, G, H>
14where
15    F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
16    G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
17    H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
18{
19    pub tcx: TyCtxt<'tcx>,
20    pub ty_op: F,
21    pub lt_op: G,
22    pub ct_op: H,
23}
24
25impl<'tcx, F, G, H> TypeFolder<TyCtxt<'tcx>> for BottomUpFolder<'tcx, F, G, H>
26where
27    F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
28    G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
29    H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
30{
31    fn cx(&self) -> TyCtxt<'tcx> {
32        self.tcx
33    }
34
35    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
36        let t = ty.super_fold_with(self);
37        (self.ty_op)(t)
38    }
39
40    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
41        // This one is a little different, because `super_fold_with` is not
42        // implemented on non-recursive `Region`.
43        (self.lt_op)(r)
44    }
45
46    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
47        let ct = ct.super_fold_with(self);
48        (self.ct_op)(ct)
49    }
50}
51
52///////////////////////////////////////////////////////////////////////////
53// Bound vars replacer
54
55/// A delegate used when instantiating bound vars.
56///
57/// Any implementation must make sure that each bound variable always
58/// gets mapped to the same result. `BoundVarReplacer` caches by using
59/// a `DelayedMap` which does not cache the first few types it encounters.
60pub trait BoundVarReplacerDelegate<'tcx> {
61    fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
62    fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
63    fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx>;
64}
65
66/// A simple delegate taking 3 mutable functions. The used functions must
67/// always return the same result for each bound variable, no matter how
68/// frequently they are called.
69pub struct FnMutDelegate<'a, 'tcx> {
70    pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
71    pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
72    pub consts: &'a mut (dyn FnMut(ty::BoundVar) -> ty::Const<'tcx> + 'a),
73}
74
75impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> {
76    fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
77        (self.regions)(br)
78    }
79    fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
80        (self.types)(bt)
81    }
82    fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> {
83        (self.consts)(bv)
84    }
85}
86
87/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
88struct BoundVarReplacer<'tcx, D> {
89    tcx: TyCtxt<'tcx>,
90
91    /// As with `RegionFolder`, represents the index of a binder *just outside*
92    /// the ones we have visited.
93    current_index: ty::DebruijnIndex,
94
95    delegate: D,
96
97    /// This cache only tracks the `DebruijnIndex` and assumes that it does not matter
98    /// for the delegate how often its methods get used.
99    cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
100}
101
102impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> {
103    fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self {
104        BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate, cache: Default::default() }
105    }
106}
107
108impl<'tcx, D> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'tcx, D>
109where
110    D: BoundVarReplacerDelegate<'tcx>,
111{
112    fn cx(&self) -> TyCtxt<'tcx> {
113        self.tcx
114    }
115
116    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
117        &mut self,
118        t: ty::Binder<'tcx, T>,
119    ) -> ty::Binder<'tcx, T> {
120        self.current_index.shift_in(1);
121        let t = t.super_fold_with(self);
122        self.current_index.shift_out(1);
123        t
124    }
125
126    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
127        match *t.kind() {
128            ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
129                let ty = self.delegate.replace_ty(bound_ty);
130                debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
131                ty::shift_vars(self.tcx, ty, self.current_index.as_u32())
132            }
133            _ => {
134                if !t.has_vars_bound_at_or_above(self.current_index) {
135                    t
136                } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
137                    t
138                } else {
139                    let res = t.super_fold_with(self);
140                    assert!(self.cache.insert((self.current_index, t), res));
141                    res
142                }
143            }
144        }
145    }
146
147    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
148        match *r {
149            ty::ReBound(debruijn, br) if debruijn == self.current_index => {
150                let region = self.delegate.replace_region(br);
151                if let ty::ReBound(debruijn1, br) = *region {
152                    // If the callback returns a bound region,
153                    // that region should always use the INNERMOST
154                    // debruijn index. Then we adjust it to the
155                    // correct depth.
156                    assert_eq!(debruijn1, ty::INNERMOST);
157                    ty::Region::new_bound(self.tcx, debruijn, br)
158                } else {
159                    region
160                }
161            }
162            _ => r,
163        }
164    }
165
166    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
167        match ct.kind() {
168            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
169                let ct = self.delegate.replace_const(bound_const);
170                debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST));
171                ty::shift_vars(self.tcx, ct, self.current_index.as_u32())
172            }
173            _ => ct.super_fold_with(self),
174        }
175    }
176
177    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
178        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
179    }
180}
181
182impl<'tcx> TyCtxt<'tcx> {
183    /// Replaces all regions bound by the given `Binder` with the
184    /// results returned by the closure; the closure is expected to
185    /// return a free region (relative to this binder), and hence the
186    /// binder is removed in the return type. The closure is invoked
187    /// once for each unique `BoundRegionKind`; multiple references to the
188    /// same `BoundRegionKind` will reuse the previous result. A map is
189    /// returned at the end with each bound region and the free region
190    /// that replaced it.
191    ///
192    /// # Panics
193    ///
194    /// This method only replaces late bound regions. Any types or
195    /// constants bound by `value` will cause an ICE.
196    pub fn instantiate_bound_regions<T, F>(
197        self,
198        value: Binder<'tcx, T>,
199        mut fld_r: F,
200    ) -> (T, FxIndexMap<ty::BoundRegion, ty::Region<'tcx>>)
201    where
202        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
203        T: TypeFoldable<TyCtxt<'tcx>>,
204    {
205        let mut region_map = FxIndexMap::default();
206        let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
207        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
208        (value, region_map)
209    }
210
211    pub fn instantiate_bound_regions_uncached<T, F>(
212        self,
213        value: Binder<'tcx, T>,
214        mut replace_regions: F,
215    ) -> T
216    where
217        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
218        T: TypeFoldable<TyCtxt<'tcx>>,
219    {
220        let value = value.skip_binder();
221        if !value.has_escaping_bound_vars() {
222            value
223        } else {
224            let delegate = FnMutDelegate {
225                regions: &mut replace_regions,
226                types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"),
227                consts: &mut |b| bug!("unexpected bound ct in binder: {b:?}"),
228            };
229            let mut replacer = BoundVarReplacer::new(self, delegate);
230            value.fold_with(&mut replacer)
231        }
232    }
233
234    /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
235    /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c`
236    /// closure replaces escaping bound consts.
237    pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
238        self,
239        value: T,
240        delegate: impl BoundVarReplacerDelegate<'tcx>,
241    ) -> T {
242        if !value.has_escaping_bound_vars() {
243            value
244        } else {
245            let mut replacer = BoundVarReplacer::new(self, delegate);
246            value.fold_with(&mut replacer)
247        }
248    }
249
250    /// Replaces all types or regions bound by the given `Binder`. The `fld_r`
251    /// closure replaces bound regions, the `fld_t` closure replaces bound
252    /// types, and `fld_c` replaces bound constants.
253    pub fn replace_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
254        self,
255        value: Binder<'tcx, T>,
256        delegate: impl BoundVarReplacerDelegate<'tcx>,
257    ) -> T {
258        self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
259    }
260
261    /// Replaces any late-bound regions bound in `value` with
262    /// free variants attached to `all_outlive_scope`.
263    pub fn liberate_late_bound_regions<T>(
264        self,
265        all_outlive_scope: DefId,
266        value: ty::Binder<'tcx, T>,
267    ) -> T
268    where
269        T: TypeFoldable<TyCtxt<'tcx>>,
270    {
271        self.instantiate_bound_regions_uncached(value, |br| {
272            let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
273            ty::Region::new_late_param(self, all_outlive_scope, kind)
274        })
275    }
276
277    pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
278    where
279        T: TypeFoldable<TyCtxt<'tcx>>,
280    {
281        let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars);
282        self.replace_escaping_bound_vars_uncached(
283            value,
284            FnMutDelegate {
285                regions: &mut |r: ty::BoundRegion| {
286                    ty::Region::new_bound(
287                        self,
288                        ty::INNERMOST,
289                        ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
290                    )
291                },
292                types: &mut |t: ty::BoundTy| {
293                    Ty::new_bound(
294                        self,
295                        ty::INNERMOST,
296                        ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
297                    )
298                },
299                consts: &mut |c| ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c)),
300            },
301        )
302    }
303
304    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
305    /// method lookup and a few other places where precise region relationships are not required.
306    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'tcx, T>) -> T
307    where
308        T: TypeFoldable<TyCtxt<'tcx>>,
309    {
310        self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0
311    }
312
313    /// Anonymize all bound variables in `value`, this is mostly used to improve caching.
314    pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
315    where
316        T: TypeFoldable<TyCtxt<'tcx>>,
317    {
318        struct Anonymize<'a, 'tcx> {
319            tcx: TyCtxt<'tcx>,
320            map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>,
321        }
322        impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
323            fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
324                let entry = self.map.entry(br.var);
325                let index = entry.index();
326                let var = ty::BoundVar::from_usize(index);
327                let kind = entry
328                    .or_insert_with(|| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon))
329                    .expect_region();
330                let br = ty::BoundRegion { var, kind };
331                ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
332            }
333            fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
334                let entry = self.map.entry(bt.var);
335                let index = entry.index();
336                let var = ty::BoundVar::from_usize(index);
337                let kind = entry
338                    .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
339                    .expect_ty();
340                Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
341            }
342            fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> {
343                let entry = self.map.entry(bv);
344                let index = entry.index();
345                let var = ty::BoundVar::from_usize(index);
346                let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
347                ty::Const::new_bound(self.tcx, ty::INNERMOST, var)
348            }
349        }
350
351        let mut map = Default::default();
352        let delegate = Anonymize { tcx: self, map: &mut map };
353        let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
354        let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values());
355        Binder::bind_with_vars(inner, bound_vars)
356    }
357}