]> git.proxmox.com Git - rustc.git/blame_incremental - compiler/rustc_middle/src/ty/fold.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / fold.rs
... / ...
CommitLineData
1use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitableExt};
2use rustc_data_structures::fx::FxIndexMap;
3use rustc_hir::def_id::DefId;
4
5use std::collections::BTreeMap;
6
7pub use rustc_type_ir::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
8
9///////////////////////////////////////////////////////////////////////////
10// Some sample folders
11
12pub struct BottomUpFolder<'tcx, F, G, H>
13where
14 F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
15 G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
16 H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
17{
18 pub tcx: TyCtxt<'tcx>,
19 pub ty_op: F,
20 pub lt_op: G,
21 pub ct_op: H,
22}
23
24impl<'tcx, F, G, H> TypeFolder<TyCtxt<'tcx>> for BottomUpFolder<'tcx, F, G, H>
25where
26 F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
27 G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
28 H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
29{
30 fn interner(&self) -> TyCtxt<'tcx> {
31 self.tcx
32 }
33
34 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
35 let t = ty.super_fold_with(self);
36 (self.ty_op)(t)
37 }
38
39 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
40 let r = r.super_fold_with(self);
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
50///////////////////////////////////////////////////////////////////////////
51// Region folder
52
53impl<'tcx> TyCtxt<'tcx> {
54 /// Folds the escaping and free regions in `value` using `f`.
55 pub fn fold_regions<T>(
56 self,
57 value: T,
58 mut f: impl FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>,
59 ) -> T
60 where
61 T: TypeFoldable<TyCtxt<'tcx>>,
62 {
63 value.fold_with(&mut RegionFolder::new(self, &mut f))
64 }
65}
66
67/// Folds over the substructure of a type, visiting its component
68/// types and all regions that occur *free* within it.
69///
70/// That is, `Ty` can contain function or method types that bind
71/// regions at the call site (`ReLateBound`), and occurrences of
72/// regions (aka "lifetimes") that are bound within a type are not
73/// visited by this folder; only regions that occur free will be
74/// visited by `fld_r`.
75
76pub struct RegionFolder<'a, 'tcx> {
77 tcx: TyCtxt<'tcx>,
78
79 /// Stores the index of a binder *just outside* the stuff we have
80 /// visited. So this begins as INNERMOST; when we pass through a
81 /// binder, it is incremented (via `shift_in`).
82 current_index: ty::DebruijnIndex,
83
84 /// Callback invokes for each free region. The `DebruijnIndex`
85 /// points to the binder *just outside* the ones we have passed
86 /// through.
87 fold_region_fn:
88 &'a mut (dyn FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx> + 'a),
89}
90
91impl<'a, 'tcx> RegionFolder<'a, 'tcx> {
92 #[inline]
93 pub fn new(
94 tcx: TyCtxt<'tcx>,
95 fold_region_fn: &'a mut dyn FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>,
96 ) -> RegionFolder<'a, 'tcx> {
97 RegionFolder { tcx, current_index: ty::INNERMOST, fold_region_fn }
98 }
99}
100
101impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
102 fn interner(&self) -> TyCtxt<'tcx> {
103 self.tcx
104 }
105
106 fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
107 &mut self,
108 t: ty::Binder<'tcx, T>,
109 ) -> ty::Binder<'tcx, T> {
110 self.current_index.shift_in(1);
111 let t = t.super_fold_with(self);
112 self.current_index.shift_out(1);
113 t
114 }
115
116 #[instrument(skip(self), level = "debug", ret)]
117 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
118 match *r {
119 ty::ReLateBound(debruijn, _) if debruijn < self.current_index => {
120 debug!(?self.current_index, "skipped bound region");
121 r
122 }
123 _ => {
124 debug!(?self.current_index, "folding free region");
125 (self.fold_region_fn)(r, self.current_index)
126 }
127 }
128 }
129}
130
131///////////////////////////////////////////////////////////////////////////
132// Bound vars replacer
133
134pub trait BoundVarReplacerDelegate<'tcx> {
135 fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
136 fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
137 fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx>;
138}
139
140pub struct FnMutDelegate<'a, 'tcx> {
141 pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
142 pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
143 pub consts: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a),
144}
145
146impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> {
147 fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
148 (self.regions)(br)
149 }
150 fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
151 (self.types)(bt)
152 }
153 fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
154 (self.consts)(bv, ty)
155 }
156}
157
158/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
159struct BoundVarReplacer<'tcx, D> {
160 tcx: TyCtxt<'tcx>,
161
162 /// As with `RegionFolder`, represents the index of a binder *just outside*
163 /// the ones we have visited.
164 current_index: ty::DebruijnIndex,
165
166 delegate: D,
167}
168
169impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> {
170 fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self {
171 BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate }
172 }
173}
174
175impl<'tcx, D> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'tcx, D>
176where
177 D: BoundVarReplacerDelegate<'tcx>,
178{
179 fn interner(&self) -> TyCtxt<'tcx> {
180 self.tcx
181 }
182
183 fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
184 &mut self,
185 t: ty::Binder<'tcx, T>,
186 ) -> ty::Binder<'tcx, T> {
187 self.current_index.shift_in(1);
188 let t = t.super_fold_with(self);
189 self.current_index.shift_out(1);
190 t
191 }
192
193 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
194 match *t.kind() {
195 ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
196 let ty = self.delegate.replace_ty(bound_ty);
197 debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
198 ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32())
199 }
200 _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
201 _ => t,
202 }
203 }
204
205 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
206 match *r {
207 ty::ReLateBound(debruijn, br) if debruijn == self.current_index => {
208 let region = self.delegate.replace_region(br);
209 if let ty::ReLateBound(debruijn1, br) = *region {
210 // If the callback returns a late-bound region,
211 // that region should always use the INNERMOST
212 // debruijn index. Then we adjust it to the
213 // correct depth.
214 assert_eq!(debruijn1, ty::INNERMOST);
215 self.tcx.mk_re_late_bound(debruijn, br)
216 } else {
217 region
218 }
219 }
220 _ => r,
221 }
222 }
223
224 fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
225 match ct.kind() {
226 ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
227 let ct = self.delegate.replace_const(bound_const, ct.ty());
228 debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST));
229 ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
230 }
231 _ => ct.super_fold_with(self),
232 }
233 }
234
235 fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
236 if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
237 }
238}
239
240impl<'tcx> TyCtxt<'tcx> {
241 /// Replaces all regions bound by the given `Binder` with the
242 /// results returned by the closure; the closure is expected to
243 /// return a free region (relative to this binder), and hence the
244 /// binder is removed in the return type. The closure is invoked
245 /// once for each unique `BoundRegionKind`; multiple references to the
246 /// same `BoundRegionKind` will reuse the previous result. A map is
247 /// returned at the end with each bound region and the free region
248 /// that replaced it.
249 ///
250 /// # Panics
251 ///
252 /// This method only replaces late bound regions. Any types or
253 /// constants bound by `value` will cause an ICE.
254 pub fn replace_late_bound_regions<T, F>(
255 self,
256 value: Binder<'tcx, T>,
257 mut fld_r: F,
258 ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
259 where
260 F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
261 T: TypeFoldable<TyCtxt<'tcx>>,
262 {
263 let mut region_map = BTreeMap::new();
264 let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
265 let value = self.replace_late_bound_regions_uncached(value, real_fld_r);
266 (value, region_map)
267 }
268
269 pub fn replace_late_bound_regions_uncached<T, F>(
270 self,
271 value: Binder<'tcx, T>,
272 mut replace_regions: F,
273 ) -> T
274 where
275 F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
276 T: TypeFoldable<TyCtxt<'tcx>>,
277 {
278 let value = value.skip_binder();
279 if !value.has_escaping_bound_vars() {
280 value
281 } else {
282 let delegate = FnMutDelegate {
283 regions: &mut replace_regions,
284 types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"),
285 consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"),
286 };
287 let mut replacer = BoundVarReplacer::new(self, delegate);
288 value.fold_with(&mut replacer)
289 }
290 }
291
292 /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
293 /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c`
294 /// closure replaces escaping bound consts.
295 pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
296 self,
297 value: T,
298 delegate: impl BoundVarReplacerDelegate<'tcx>,
299 ) -> T {
300 if !value.has_escaping_bound_vars() {
301 value
302 } else {
303 let mut replacer = BoundVarReplacer::new(self, delegate);
304 value.fold_with(&mut replacer)
305 }
306 }
307
308 /// Replaces all types or regions bound by the given `Binder`. The `fld_r`
309 /// closure replaces bound regions, the `fld_t` closure replaces bound
310 /// types, and `fld_c` replaces bound constants.
311 pub fn replace_bound_vars_uncached<T: TypeFoldable<TyCtxt<'tcx>>>(
312 self,
313 value: Binder<'tcx, T>,
314 delegate: impl BoundVarReplacerDelegate<'tcx>,
315 ) -> T {
316 self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
317 }
318
319 /// Replaces any late-bound regions bound in `value` with
320 /// free variants attached to `all_outlive_scope`.
321 pub fn liberate_late_bound_regions<T>(
322 self,
323 all_outlive_scope: DefId,
324 value: ty::Binder<'tcx, T>,
325 ) -> T
326 where
327 T: TypeFoldable<TyCtxt<'tcx>>,
328 {
329 self.replace_late_bound_regions_uncached(value, |br| {
330 self.mk_re_free(all_outlive_scope, br.kind)
331 })
332 }
333
334 pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
335 where
336 T: TypeFoldable<TyCtxt<'tcx>>,
337 {
338 let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars);
339 self.replace_escaping_bound_vars_uncached(
340 value,
341 FnMutDelegate {
342 regions: &mut |r: ty::BoundRegion| {
343 self.mk_re_late_bound(
344 ty::INNERMOST,
345 ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
346 )
347 },
348 types: &mut |t: ty::BoundTy| {
349 self.mk_bound(ty::INNERMOST, ty::BoundTy { var: shift_bv(t.var), kind: t.kind })
350 },
351 consts: &mut |c, ty: Ty<'tcx>| {
352 self.mk_const(ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)), ty)
353 },
354 },
355 )
356 }
357
358 /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
359 /// method lookup and a few other places where precise region relationships are not required.
360 pub fn erase_late_bound_regions<T>(self, value: Binder<'tcx, T>) -> T
361 where
362 T: TypeFoldable<TyCtxt<'tcx>>,
363 {
364 self.replace_late_bound_regions(value, |_| self.lifetimes.re_erased).0
365 }
366
367 /// Anonymize all bound variables in `value`, this is mostly used to improve caching.
368 pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
369 where
370 T: TypeFoldable<TyCtxt<'tcx>>,
371 {
372 struct Anonymize<'a, 'tcx> {
373 tcx: TyCtxt<'tcx>,
374 map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>,
375 }
376 impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
377 fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
378 let entry = self.map.entry(br.var);
379 let index = entry.index();
380 let var = ty::BoundVar::from_usize(index);
381 let kind = entry
382 .or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon(None)))
383 .expect_region();
384 let br = ty::BoundRegion { var, kind };
385 self.tcx.mk_re_late_bound(ty::INNERMOST, br)
386 }
387 fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
388 let entry = self.map.entry(bt.var);
389 let index = entry.index();
390 let var = ty::BoundVar::from_usize(index);
391 let kind = entry
392 .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
393 .expect_ty();
394 self.tcx.mk_bound(ty::INNERMOST, BoundTy { var, kind })
395 }
396 fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
397 let entry = self.map.entry(bv);
398 let index = entry.index();
399 let var = ty::BoundVar::from_usize(index);
400 let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
401 self.tcx.mk_const(ty::ConstKind::Bound(ty::INNERMOST, var), ty)
402 }
403 }
404
405 let mut map = Default::default();
406 let delegate = Anonymize { tcx: self, map: &mut map };
407 let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
408 let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values());
409 Binder::bind_with_vars(inner, bound_vars)
410 }
411}
412
413///////////////////////////////////////////////////////////////////////////
414// Shifter
415//
416// Shifts the De Bruijn indices on all escaping bound vars by a
417// fixed amount. Useful in substitution or when otherwise introducing
418// a binding level that is not intended to capture the existing bound
419// vars. See comment on `shift_vars_through_binders` method in
420// `subst.rs` for more details.
421
422struct Shifter<'tcx> {
423 tcx: TyCtxt<'tcx>,
424 current_index: ty::DebruijnIndex,
425 amount: u32,
426}
427
428impl<'tcx> Shifter<'tcx> {
429 pub fn new(tcx: TyCtxt<'tcx>, amount: u32) -> Self {
430 Shifter { tcx, current_index: ty::INNERMOST, amount }
431 }
432}
433
434impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
435 fn interner(&self) -> TyCtxt<'tcx> {
436 self.tcx
437 }
438
439 fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
440 &mut self,
441 t: ty::Binder<'tcx, T>,
442 ) -> ty::Binder<'tcx, T> {
443 self.current_index.shift_in(1);
444 let t = t.super_fold_with(self);
445 self.current_index.shift_out(1);
446 t
447 }
448
449 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
450 match *r {
451 ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => {
452 let debruijn = debruijn.shifted_in(self.amount);
453 self.tcx.mk_re_late_bound(debruijn, br)
454 }
455 _ => r,
456 }
457 }
458
459 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
460 match *ty.kind() {
461 ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
462 let debruijn = debruijn.shifted_in(self.amount);
463 self.tcx.mk_bound(debruijn, bound_ty)
464 }
465
466 _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
467 _ => ty,
468 }
469 }
470
471 fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
472 if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.kind()
473 && debruijn >= self.current_index
474 {
475 let debruijn = debruijn.shifted_in(self.amount);
476 self.tcx.mk_const(ty::ConstKind::Bound(debruijn, bound_ct), ct.ty())
477 } else {
478 ct.super_fold_with(self)
479 }
480 }
481
482 fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
483 if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
484 }
485}
486
487pub fn shift_region<'tcx>(
488 tcx: TyCtxt<'tcx>,
489 region: ty::Region<'tcx>,
490 amount: u32,
491) -> ty::Region<'tcx> {
492 match *region {
493 ty::ReLateBound(debruijn, br) if amount > 0 => {
494 tcx.mk_re_late_bound(debruijn.shifted_in(amount), br)
495 }
496 _ => region,
497 }
498}
499
500pub fn shift_vars<'tcx, T>(tcx: TyCtxt<'tcx>, value: T, amount: u32) -> T
501where
502 T: TypeFoldable<TyCtxt<'tcx>>,
503{
504 debug!("shift_vars(value={:?}, amount={})", value, amount);
505
506 if amount == 0 || !value.has_escaping_bound_vars() {
507 return value;
508 }
509
510 value.fold_with(&mut Shifter::new(tcx, amount))
511}