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.kind() {
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.kind() {
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    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
182        if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c }
183    }
184}
185
186impl<'tcx> TyCtxt<'tcx> {
187    /// Replaces all regions bound by the given `Binder` with the
188    /// results returned by the closure; the closure is expected to
189    /// return a free region (relative to this binder), and hence the
190    /// binder is removed in the return type. The closure is invoked
191    /// once for each unique `BoundRegionKind`; multiple references to the
192    /// same `BoundRegionKind` will reuse the previous result. A map is
193    /// returned at the end with each bound region and the free region
194    /// that replaced it.
195    ///
196    /// # Panics
197    ///
198    /// This method only replaces late bound regions. Any types or
199    /// constants bound by `value` will cause an ICE.
200    pub fn instantiate_bound_regions<T, F>(
201        self,
202        value: Binder<'tcx, T>,
203        mut fld_r: F,
204    ) -> (T, FxIndexMap<ty::BoundRegion, ty::Region<'tcx>>)
205    where
206        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
207        T: TypeFoldable<TyCtxt<'tcx>>,
208    {
209        let mut region_map = FxIndexMap::default();
210        let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
211        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
212        (value, region_map)
213    }
214
215    pub fn instantiate_bound_regions_uncached<T, F>(
216        self,
217        value: Binder<'tcx, T>,
218        mut replace_regions: F,
219    ) -> T
220    where
221        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
222        T: TypeFoldable<TyCtxt<'tcx>>,
223    {
224        let value = value.skip_binder();
225        if !value.has_escaping_bound_vars() {
226            value
227        } else {
228            let delegate = FnMutDelegate {
229                regions: &mut replace_regions,
230                types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"),
231                consts: &mut |b| bug!("unexpected bound ct in binder: {b:?}"),
232            };
233            let mut replacer = BoundVarReplacer::new(self, delegate);
234            value.fold_with(&mut replacer)
235        }
236    }
237
238    /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
239    /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c`
240    /// closure replaces escaping bound consts.
241    pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
242        self,
243        value: T,
244        delegate: impl BoundVarReplacerDelegate<'tcx>,
245    ) -> T {
246        if !value.has_escaping_bound_vars() {
247            value
248        } else {
249            let mut replacer = BoundVarReplacer::new(self, delegate);
250            value.fold_with(&mut replacer)
251        }
252    }
253
254    /// Replaces all types or regions bound by the given `Binder`. The `fld_r`
255    /// closure replaces bound regions, the `fld_t` closure replaces bound
256    /// types, and `fld_c` replaces bound constants.
257    pub fn replace_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
258        self,
259        value: Binder<'tcx, T>,
260        delegate: impl BoundVarReplacerDelegate<'tcx>,
261    ) -> T {
262        self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
263    }
264
265    /// Replaces any late-bound regions bound in `value` with
266    /// free variants attached to `all_outlive_scope`.
267    pub fn liberate_late_bound_regions<T>(
268        self,
269        all_outlive_scope: DefId,
270        value: ty::Binder<'tcx, T>,
271    ) -> T
272    where
273        T: TypeFoldable<TyCtxt<'tcx>>,
274    {
275        self.instantiate_bound_regions_uncached(value, |br| {
276            let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
277            ty::Region::new_late_param(self, all_outlive_scope, kind)
278        })
279    }
280
281    pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
282    where
283        T: TypeFoldable<TyCtxt<'tcx>>,
284    {
285        let shift_bv = |bv: ty::BoundVar| bv + bound_vars;
286        self.replace_escaping_bound_vars_uncached(
287            value,
288            FnMutDelegate {
289                regions: &mut |r: ty::BoundRegion| {
290                    ty::Region::new_bound(
291                        self,
292                        ty::INNERMOST,
293                        ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
294                    )
295                },
296                types: &mut |t: ty::BoundTy| {
297                    Ty::new_bound(
298                        self,
299                        ty::INNERMOST,
300                        ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
301                    )
302                },
303                consts: &mut |c| ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c)),
304            },
305        )
306    }
307
308    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
309    /// method lookup and a few other places where precise region relationships are not required.
310    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'tcx, T>) -> T
311    where
312        T: TypeFoldable<TyCtxt<'tcx>>,
313    {
314        self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0
315    }
316
317    /// Anonymize all bound variables in `value`, this is mostly used to improve caching.
318    pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
319    where
320        T: TypeFoldable<TyCtxt<'tcx>>,
321    {
322        struct Anonymize<'a, 'tcx> {
323            tcx: TyCtxt<'tcx>,
324            map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>,
325        }
326        impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
327            fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
328                let entry = self.map.entry(br.var);
329                let index = entry.index();
330                let var = ty::BoundVar::from_usize(index);
331                let kind = entry
332                    .or_insert_with(|| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon))
333                    .expect_region();
334                let br = ty::BoundRegion { var, kind };
335                ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
336            }
337            fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
338                let entry = self.map.entry(bt.var);
339                let index = entry.index();
340                let var = ty::BoundVar::from_usize(index);
341                let kind = entry
342                    .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
343                    .expect_ty();
344                Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
345            }
346            fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> {
347                let entry = self.map.entry(bv);
348                let index = entry.index();
349                let var = ty::BoundVar::from_usize(index);
350                let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
351                ty::Const::new_bound(self.tcx, ty::INNERMOST, var)
352            }
353        }
354
355        let mut map = Default::default();
356        let delegate = Anonymize { tcx: self, map: &mut map };
357        let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
358        let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values());
359        Binder::bind_with_vars(inner, bound_vars)
360    }
361}