Skip to main content

hir_ty/next_solver/
fold.rs

1//! Fold impls for the next-trait-solver.
2
3use rustc_type_ir::{
4    BoundVarIndexKind, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable,
5    TypeVisitableExt, inherent::IntoKind,
6};
7
8use crate::next_solver::{BoundConst, FxIndexMap};
9
10use super::{
11    Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, SolverDefId, Ty,
12    TyKind,
13};
14
15/// A delegate used when instantiating bound vars.
16///
17/// Any implementation must make sure that each bound variable always
18/// gets mapped to the same result. `BoundVarReplacer` caches by using
19/// a `DelayedMap` which does not cache the first few types it encounters.
20pub trait BoundVarReplacerDelegate<'db> {
21    fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db>;
22    fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db>;
23    fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db>;
24}
25
26/// A simple delegate taking 3 mutable functions. The used functions must
27/// always return the same result for each bound variable, no matter how
28/// frequently they are called.
29pub struct FnMutDelegate<'db, 'a> {
30    pub regions: &'a mut (dyn FnMut(BoundRegion<'db>) -> Region<'db> + 'a),
31    pub types: &'a mut (dyn FnMut(BoundTy<'db>) -> Ty<'db> + 'a),
32    pub consts: &'a mut (dyn FnMut(BoundConst<'db>) -> Const<'db> + 'a),
33}
34
35impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> {
36    fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> {
37        (self.regions)(br)
38    }
39    fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> {
40        (self.types)(bt)
41    }
42    fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> {
43        (self.consts)(bv)
44    }
45}
46
47/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
48pub(crate) struct BoundVarReplacer<'db, D> {
49    interner: DbInterner<'db>,
50    /// As with `RegionFolder`, represents the index of a binder *just outside*
51    /// the ones we have visited.
52    current_index: DebruijnIndex,
53
54    delegate: D,
55}
56
57impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> {
58    pub(crate) fn new(tcx: DbInterner<'db>, delegate: D) -> Self {
59        BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate }
60    }
61}
62
63impl<'db, D> TypeFolder<DbInterner<'db>> for BoundVarReplacer<'db, D>
64where
65    D: BoundVarReplacerDelegate<'db>,
66{
67    fn cx(&self) -> DbInterner<'db> {
68        self.interner
69    }
70
71    fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
72        &mut self,
73        t: Binder<'db, T>,
74    ) -> Binder<'db, T> {
75        self.current_index.shift_in(1);
76        let t = t.super_fold_with(self);
77        self.current_index.shift_out(1);
78        t
79    }
80
81    fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
82        match t.kind() {
83            TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
84                if debruijn == self.current_index =>
85            {
86                let ty = self.delegate.replace_ty(bound_ty);
87                debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO));
88                rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32())
89            }
90            _ => {
91                if !t.has_vars_bound_at_or_above(self.current_index) {
92                    t
93                } else {
94                    t.super_fold_with(self)
95                }
96            }
97        }
98    }
99
100    fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
101        match r.kind() {
102            RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), br)
103                if debruijn == self.current_index =>
104            {
105                let region = self.delegate.replace_region(br);
106                if let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn1), br) = region.kind()
107                {
108                    // If the callback returns a bound region,
109                    // that region should always use the INNERMOST
110                    // debruijn index. Then we adjust it to the
111                    // correct depth.
112                    assert_eq!(debruijn1, DebruijnIndex::ZERO);
113                    Region::new_bound(self.interner, debruijn, br)
114                } else {
115                    region
116                }
117            }
118            _ => r,
119        }
120    }
121
122    fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
123        match ct.kind() {
124            ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_const)
125                if debruijn == self.current_index =>
126            {
127                let ct = self.delegate.replace_const(bound_const);
128                debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO));
129                rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32())
130            }
131            _ => ct.super_fold_with(self),
132        }
133    }
134
135    fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> {
136        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
137    }
138}
139
140pub fn fold_tys<'db, T: TypeFoldable<DbInterner<'db>>>(
141    interner: DbInterner<'db>,
142    t: T,
143    callback: impl FnMut(Ty<'db>) -> Ty<'db>,
144) -> T {
145    struct Folder<'db, F> {
146        interner: DbInterner<'db>,
147        callback: F,
148    }
149    impl<'db, F: FnMut(Ty<'db>) -> Ty<'db>> TypeFolder<DbInterner<'db>> for Folder<'db, F> {
150        fn cx(&self) -> DbInterner<'db> {
151            self.interner
152        }
153
154        fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
155            let t = t.super_fold_with(self);
156            (self.callback)(t)
157        }
158    }
159
160    t.fold_with(&mut Folder { interner, callback })
161}
162
163impl<'db> DbInterner<'db> {
164    /// Replaces all regions bound by the given `Binder` with the
165    /// results returned by the closure; the closure is expected to
166    /// return a free region (relative to this binder), and hence the
167    /// binder is removed in the return type. The closure is invoked
168    /// once for each unique `BoundRegionKind`; multiple references to the
169    /// same `BoundRegionKind` will reuse the previous result. A map is
170    /// returned at the end with each bound region and the free region
171    /// that replaced it.
172    ///
173    /// # Panics
174    ///
175    /// This method only replaces late bound regions. Any types or
176    /// constants bound by `value` will cause an ICE.
177    pub fn instantiate_bound_regions<T, F>(
178        self,
179        value: Binder<'db, T>,
180        mut fld_r: F,
181    ) -> (T, FxIndexMap<BoundRegion<'db>, Region<'db>>)
182    where
183        F: FnMut(BoundRegion<'db>) -> Region<'db>,
184        T: TypeFoldable<DbInterner<'db>>,
185    {
186        let mut region_map = FxIndexMap::default();
187        let real_fld_r = |br: BoundRegion<'db>| *region_map.entry(br).or_insert_with(|| fld_r(br));
188        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
189        (value, region_map)
190    }
191
192    pub fn instantiate_bound_regions_uncached<T, F>(
193        self,
194        value: Binder<'db, T>,
195        mut replace_regions: F,
196    ) -> T
197    where
198        F: FnMut(BoundRegion<'db>) -> Region<'db>,
199        T: TypeFoldable<DbInterner<'db>>,
200    {
201        let value = value.skip_binder();
202        if !value.has_escaping_bound_vars() {
203            value
204        } else {
205            let delegate = FnMutDelegate {
206                regions: &mut replace_regions,
207                types: &mut |b| panic!("unexpected bound ty in binder: {b:?}"),
208                consts: &mut |b| panic!("unexpected bound ct in binder: {b:?}"),
209            };
210            let mut replacer = BoundVarReplacer::new(self, delegate);
211            value.fold_with(&mut replacer)
212        }
213    }
214
215    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
216    /// method lookup and a few other places where precise region relationships are not required.
217    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'db, T>) -> T
218    where
219        T: TypeFoldable<DbInterner<'db>>,
220    {
221        self.instantiate_bound_regions(value, |_| Region::new_erased(self)).0
222    }
223
224    /// Replaces any late-bound regions bound in `value` with
225    /// free variants attached to `all_outlive_scope`.
226    pub fn liberate_late_bound_regions<T>(
227        self,
228        all_outlive_scope: SolverDefId<'db>,
229        value: Binder<'db, T>,
230    ) -> T
231    where
232        T: TypeFoldable<DbInterner<'db>>,
233    {
234        self.instantiate_bound_regions_uncached(value, |br| {
235            Region::new_late_param(self, all_outlive_scope, br)
236        })
237    }
238}