Skip to main content

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<'tcx>) -> ty::Region<'tcx>;
62    fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx>;
63    fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> 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<'tcx>) -> ty::Region<'tcx> + 'a),
71    pub types: &'a mut (dyn FnMut(ty::BoundTy<'tcx>) -> Ty<'tcx> + 'a),
72    pub consts: &'a mut (dyn FnMut(ty::BoundConst<'tcx>) -> ty::Const<'tcx> + 'a),
73}
74
75impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> {
76    fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
77        (self.regions)(br)
78    }
79    fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
80        (self.types)(bt)
81    }
82    fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
83        (self.consts)(bc)
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(ty::BoundVarIndexKind::Bound(debruijn), bound_ty)
129                if debruijn == self.current_index =>
130            {
131                let ty = self.delegate.replace_ty(bound_ty);
132                if true {
    if !!ty.has_vars_bound_above(ty::INNERMOST) {
        ::core::panicking::panic("assertion failed: !ty.has_vars_bound_above(ty::INNERMOST)")
    };
};debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
133                ty::shift_vars(self.tcx, ty, self.current_index.as_u32())
134            }
135            _ => {
136                if !t.has_vars_bound_at_or_above(self.current_index) {
137                    t
138                } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
139                    t
140                } else {
141                    let res = t.super_fold_with(self);
142                    if !self.cache.insert((self.current_index, t), res) {
    ::core::panicking::panic("assertion failed: self.cache.insert((self.current_index, t), res)")
};assert!(self.cache.insert((self.current_index, t), res));
143                    res
144                }
145            }
146        }
147    }
148
149    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
150        match r.kind() {
151            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
152                if debruijn == self.current_index =>
153            {
154                let region = self.delegate.replace_region(br);
155                if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn1), br) = region.kind() {
156                    // If the callback returns a bound region,
157                    // that region should always use the INNERMOST
158                    // debruijn index. Then we adjust it to the
159                    // correct depth.
160                    match (&debruijn1, &ty::INNERMOST) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(debruijn1, ty::INNERMOST);
161                    ty::Region::new_bound(self.tcx, debruijn, br)
162                } else {
163                    region
164                }
165            }
166            _ => r,
167        }
168    }
169
170    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
171        match ct.kind() {
172            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)
173                if debruijn == self.current_index =>
174            {
175                let ct = self.delegate.replace_const(bound_const);
176                if true {
    if !!ct.has_vars_bound_above(ty::INNERMOST) {
        ::core::panicking::panic("assertion failed: !ct.has_vars_bound_above(ty::INNERMOST)")
    };
};debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST));
177                ty::shift_vars(self.tcx, ct, self.current_index.as_u32())
178            }
179            _ => ct.super_fold_with(self),
180        }
181    }
182
183    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
184        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
185    }
186
187    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
188        if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c }
189    }
190}
191
192impl<'tcx> TyCtxt<'tcx> {
193    /// Replaces all regions bound by the given `Binder` with the
194    /// results returned by the closure; the closure is expected to
195    /// return a free region (relative to this binder), and hence the
196    /// binder is removed in the return type. The closure is invoked
197    /// once for each unique `BoundRegionKind`; multiple references to the
198    /// same `BoundRegionKind` will reuse the previous result. A map is
199    /// returned at the end with each bound region and the free region
200    /// that replaced it.
201    ///
202    /// # Panics
203    ///
204    /// This method only replaces late bound regions. Any types or
205    /// constants bound by `value` will cause an ICE.
206    pub fn instantiate_bound_regions<T, F>(
207        self,
208        value: Binder<'tcx, T>,
209        mut fld_r: F,
210    ) -> (T, FxIndexMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>)
211    where
212        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
213        T: TypeFoldable<TyCtxt<'tcx>>,
214    {
215        let mut region_map = FxIndexMap::default();
216        let real_fld_r =
217            |br: ty::BoundRegion<'tcx>| *region_map.entry(br).or_insert_with(|| fld_r(br));
218        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
219        (value, region_map)
220    }
221
222    pub fn instantiate_bound_regions_uncached<T, F>(
223        self,
224        value: Binder<'tcx, T>,
225        mut replace_regions: F,
226    ) -> T
227    where
228        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
229        T: TypeFoldable<TyCtxt<'tcx>>,
230    {
231        let value = value.skip_binder();
232        if !value.has_escaping_bound_vars() {
233            value
234        } else {
235            let delegate = FnMutDelegate {
236                regions: &mut replace_regions,
237                types: &mut |b| crate::util::bug::bug_fmt(format_args!("unexpected bound ty in binder: {0:?}",
        b))bug!("unexpected bound ty in binder: {b:?}"),
238                consts: &mut |b| crate::util::bug::bug_fmt(format_args!("unexpected bound ct in binder: {0:?}",
        b))bug!("unexpected bound ct in binder: {b:?}"),
239            };
240            let mut replacer = BoundVarReplacer::new(self, delegate);
241            value.fold_with(&mut replacer)
242        }
243    }
244
245    /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
246    /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c`
247    /// closure replaces escaping bound consts.
248    pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
249        self,
250        value: T,
251        delegate: impl BoundVarReplacerDelegate<'tcx>,
252    ) -> T {
253        if !value.has_escaping_bound_vars() {
254            value
255        } else {
256            let mut replacer = BoundVarReplacer::new(self, delegate);
257            value.fold_with(&mut replacer)
258        }
259    }
260
261    /// Replaces all types or regions bound by the given `Binder`. The `fld_r`
262    /// closure replaces bound regions, the `fld_t` closure replaces bound
263    /// types, and `fld_c` replaces bound constants.
264    pub fn replace_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
265        self,
266        value: Binder<'tcx, T>,
267        delegate: impl BoundVarReplacerDelegate<'tcx>,
268    ) -> T {
269        self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
270    }
271
272    /// Replaces any late-bound regions bound in `value` with
273    /// free variants attached to `all_outlive_scope`.
274    pub fn liberate_late_bound_regions<T>(
275        self,
276        all_outlive_scope: DefId,
277        value: ty::Binder<'tcx, T>,
278    ) -> T
279    where
280        T: TypeFoldable<TyCtxt<'tcx>>,
281    {
282        self.instantiate_bound_regions_uncached(value, |br| {
283            let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
284            ty::Region::new_late_param(self, all_outlive_scope, kind)
285        })
286    }
287
288    pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
289    where
290        T: TypeFoldable<TyCtxt<'tcx>>,
291    {
292        let shift_bv = |bv: ty::BoundVar| bv + bound_vars;
293        self.replace_escaping_bound_vars_uncached(
294            value,
295            FnMutDelegate {
296                regions: &mut |r: ty::BoundRegion<'tcx>| {
297                    ty::Region::new_bound(
298                        self,
299                        ty::INNERMOST,
300                        ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
301                    )
302                },
303                types: &mut |t: ty::BoundTy<'tcx>| {
304                    Ty::new_bound(
305                        self,
306                        ty::INNERMOST,
307                        ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
308                    )
309                },
310                consts: &mut |c| {
311                    ty::Const::new_bound(self, ty::INNERMOST, ty::BoundConst::new(shift_bv(c.var)))
312                },
313            },
314        )
315    }
316
317    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
318    /// method lookup and a few other places where precise region relationships are not required.
319    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'tcx, T>) -> T
320    where
321        T: TypeFoldable<TyCtxt<'tcx>>,
322    {
323        self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0
324    }
325
326    /// Anonymize all bound variables in `value`, this is mostly used to improve caching.
327    pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
328    where
329        T: TypeFoldable<TyCtxt<'tcx>>,
330    {
331        struct Anonymize<'a, 'tcx> {
332            tcx: TyCtxt<'tcx>,
333            map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind<'tcx>>,
334        }
335        impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
336            fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
337                let entry = self.map.entry(br.var);
338                let index = entry.index();
339                let var = ty::BoundVar::from_usize(index);
340                let kind = entry
341                    .or_insert_with(|| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon))
342                    .expect_region();
343                let br = ty::BoundRegion { var, kind };
344                ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
345            }
346            fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
347                let entry = self.map.entry(bt.var);
348                let index = entry.index();
349                let var = ty::BoundVar::from_usize(index);
350                let kind = entry
351                    .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
352                    .expect_ty();
353                Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
354            }
355            fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
356                let entry = self.map.entry(bc.var);
357                let index = entry.index();
358                let var = ty::BoundVar::from_usize(index);
359                let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
360                ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var))
361            }
362        }
363
364        let mut map = Default::default();
365        let delegate = Anonymize { tcx: self, map: &mut map };
366        let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
367        let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values());
368        Binder::bind_with_vars(inner, bound_vars)
369    }
370}