Skip to main content

rustc_middle/ty/
fold.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_hir::def_id::DefId;
3use rustc_span::bug;
4use rustc_type_ir::{BoundVarReplacerDelegate, FnMutDelegate};
5
6use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
7
8///////////////////////////////////////////////////////////////////////////
9// Some sample folders
10
11pub struct BottomUpFolder<'tcx, F, G, H>
12where
13    F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
14    G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
15    H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
16{
17    pub tcx: TyCtxt<'tcx>,
18    pub ty_op: F,
19    pub lt_op: G,
20    pub ct_op: H,
21}
22
23impl<'tcx, F, G, H> TypeFolder<TyCtxt<'tcx>> for BottomUpFolder<'tcx, F, G, H>
24where
25    F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
26    G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
27    H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
28{
29    fn cx(&self) -> TyCtxt<'tcx> {
30        self.tcx
31    }
32
33    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
34        let t = ty.super_fold_with(self);
35        (self.ty_op)(t)
36    }
37
38    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
39        // This one is a little different, because `super_fold_with` is not
40        // implemented on non-recursive `Region`.
41        (self.lt_op)(r)
42    }
43
44    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
45        let ct = ct.super_fold_with(self);
46        (self.ct_op)(ct)
47    }
48}
49
50impl<'tcx> TyCtxt<'tcx> {
51    /// Replaces all regions bound by the given `Binder` with the
52    /// results returned by the closure; the closure is expected to
53    /// return a free region (relative to this binder), and hence the
54    /// binder is removed in the return type. The closure is invoked
55    /// once for each unique `BoundRegionKind`; multiple references to the
56    /// same `BoundRegionKind` will reuse the previous result. A map is
57    /// returned at the end with each bound region and the free region
58    /// that replaced it.
59    ///
60    /// # Panics
61    ///
62    /// This method only replaces late bound regions. Any types or
63    /// constants bound by `value` will cause an ICE.
64    pub fn instantiate_bound_regions<T, F>(
65        self,
66        value: Binder<'tcx, T>,
67        mut fld_r: F,
68    ) -> (T, FxIndexMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>)
69    where
70        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
71        T: TypeFoldable<TyCtxt<'tcx>>,
72    {
73        let mut region_map = FxIndexMap::default();
74        let real_fld_r =
75            |br: ty::BoundRegion<'tcx>| *region_map.entry(br).or_insert_with(|| fld_r(br));
76        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
77        (value, region_map)
78    }
79
80    pub fn instantiate_bound_regions_uncached<T, F>(
81        self,
82        value: Binder<'tcx, T>,
83        mut replace_regions: F,
84    ) -> T
85    where
86        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
87        T: TypeFoldable<TyCtxt<'tcx>>,
88    {
89        let delegate = FnMutDelegate {
90            regions: &mut replace_regions,
91            types: &mut |b| ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected bound ty in binder: {0:?}", b),
    Location::caller())bug!("unexpected bound ty in binder: {b:?}"),
92            consts: &mut |b| ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected bound ct in binder: {0:?}", b),
    Location::caller())bug!("unexpected bound ct in binder: {b:?}"),
93        };
94        rustc_type_ir::replace_escaping_bound_vars_uncached(self, value.skip_binder(), delegate)
95    }
96
97    /// Replaces escaping bound vars using `delegate`.
98    pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
99        self,
100        value: T,
101        delegate: impl BoundVarReplacerDelegate<TyCtxt<'tcx>>,
102    ) -> T {
103        rustc_type_ir::replace_escaping_bound_vars_uncached(self, value, delegate)
104    }
105
106    /// Replaces the variables bound by `value` using `delegate`.
107    pub fn replace_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
108        self,
109        value: Binder<'tcx, T>,
110        delegate: impl BoundVarReplacerDelegate<TyCtxt<'tcx>>,
111    ) -> T {
112        self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
113    }
114
115    /// Replaces any late-bound regions bound in `value` with
116    /// free variants attached to `all_outlive_scope`.
117    pub fn liberate_late_bound_regions<T>(
118        self,
119        all_outlive_scope: DefId,
120        value: ty::Binder<'tcx, T>,
121    ) -> T
122    where
123        T: TypeFoldable<TyCtxt<'tcx>>,
124    {
125        self.instantiate_bound_regions_uncached(value, |br| {
126            let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind);
127            ty::Region::new_late_param(self, all_outlive_scope, kind)
128        })
129    }
130
131    pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
132    where
133        T: TypeFoldable<TyCtxt<'tcx>>,
134    {
135        rustc_type_ir::shift_bound_var_indices(self, bound_vars, value)
136    }
137
138    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
139    /// method lookup and a few other places where precise region relationships are not required.
140    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'tcx, T>) -> T
141    where
142        T: TypeFoldable<TyCtxt<'tcx>>,
143    {
144        self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0
145    }
146
147    /// Anonymize all bound variables in `value`, this is mostly used to improve caching.
148    pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
149    where
150        T: TypeFoldable<TyCtxt<'tcx>>,
151    {
152        struct Anonymize<'a, 'tcx> {
153            tcx: TyCtxt<'tcx>,
154            map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind<'tcx>>,
155        }
156        impl<'tcx> BoundVarReplacerDelegate<TyCtxt<'tcx>> for Anonymize<'_, 'tcx> {
157            fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
158                let entry = self.map.entry(br.var);
159                let index = entry.index();
160                let var = ty::BoundVar::from_usize(index);
161                let kind = entry
162                    .or_insert_with(|| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon))
163                    .expect_region();
164                let br = ty::BoundRegion { var, kind };
165                ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
166            }
167            fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
168                let entry = self.map.entry(bt.var);
169                let index = entry.index();
170                let var = ty::BoundVar::from_usize(index);
171                let kind = entry
172                    .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
173                    .expect_ty();
174                Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
175            }
176            fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
177                let entry = self.map.entry(bc.var);
178                let index = entry.index();
179                let var = ty::BoundVar::from_usize(index);
180                let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
181                ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var))
182            }
183        }
184
185        let mut map = Default::default();
186        let delegate = Anonymize { tcx: self, map: &mut map };
187        let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
188        let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values());
189        Binder::bind_with_vars(inner, bound_vars)
190    }
191}