Skip to main content

rustc_type_ir/
fold.rs

1//! A folding traversal mechanism for complex data structures that contain type
2//! information.
3//!
4//! This is a modifying traversal. It consumes the data structure, producing a
5//! (possibly) modified version of it. Both fallible and infallible versions are
6//! available. The name is potentially confusing, because this traversal is more
7//! like `Iterator::map` than `Iterator::fold`.
8//!
9//! This traversal has limited flexibility. Only a small number of "types of
10//! interest" within the complex data structures can receive custom
11//! modification. These are the ones containing the most important type-related
12//! information, such as `Ty`, `Predicate`, `Region`, and `Const`.
13//!
14//! There are three traits involved in each traversal.
15//! - `TypeFoldable`. This is implemented once for many types, including:
16//!   - Types of interest, for which the methods delegate to the folder.
17//!   - All other types, including generic containers like `Vec` and `Option`.
18//!     It defines a "skeleton" of how they should be folded.
19//! - `TypeSuperFoldable`. This is implemented only for recursive types of
20//!   interest, and defines the folding "skeleton" for these types. (This
21//!   excludes `Region` because it is non-recursive, i.e. it never contains
22//!   other types of interest.)
23//! - `TypeFolder`/`FallibleTypeFolder`. One of these is implemented for each
24//!   folder. This defines how types of interest are folded.
25//!
26//! This means each fold is a mixture of (a) generic folding operations, and (b)
27//! custom fold operations that are specific to the folder.
28//! - The `TypeFoldable` impls handle most of the traversal, and call into
29//!   `TypeFolder`/`FallibleTypeFolder` when they encounter a type of interest.
30//! - A `TypeFolder`/`FallibleTypeFolder` may call into another `TypeFoldable`
31//!   impl, because some of the types of interest are recursive and can contain
32//!   other types of interest.
33//! - A `TypeFolder`/`FallibleTypeFolder` may also call into a `TypeSuperFoldable`
34//!   impl, because each folder might provide custom handling only for some types
35//!   of interest, or only for some variants of each type of interest, and then
36//!   use default traversal for the remaining cases.
37//!
38//! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U:
39//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be folded like so:
40//! ```text
41//! s.fold_with(folder) calls
42//! - ty.fold_with(folder) calls
43//!   - folder.fold_ty(ty) may call
44//!     - ty.super_fold_with(folder)
45//! - u.fold_with(folder)
46//! ```
47
48use std::convert::Infallible;
49use std::mem;
50use std::sync::Arc;
51
52use rustc_index::{Idx, IndexVec};
53use thin_vec::ThinVec;
54use tracing::{debug, instrument};
55
56use crate::inherent::*;
57use crate::visit::{TypeVisitable, TypeVisitableExt as _};
58use crate::{self as ty, BoundVarIndexKind, Interner};
59
60/// This trait is implemented for every type that can be folded,
61/// providing the skeleton of the traversal.
62///
63/// To implement this conveniently, use the derive macro located in
64/// `rustc_macros`.
65///
66/// This trait is a sub-trait of `TypeVisitable`. This is because many
67/// `TypeFolder` instances use the methods in `TypeVisitableExt` while folding,
68/// which means in practice almost every foldable type needs to also be
69/// visitable. (However, there are some types that are visitable without being
70/// foldable.)
71pub trait TypeFoldable<I: Interner>: TypeVisitable<I> + Clone {
72    /// The entry point for folding. To fold a value `t` with a folder `f`
73    /// call: `t.try_fold_with(f)`.
74    ///
75    /// For most types, this just traverses the value, calling `try_fold_with`
76    /// on each field/element.
77    ///
78    /// For types of interest (such as `Ty`), the implementation of this method
79    /// calls a folder method specifically for that type (such as
80    /// `F::try_fold_ty`). This is where control transfers from [`TypeFoldable`]
81    /// to [`FallibleTypeFolder`].
82    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error>;
83
84    /// The entry point for folding. To fold a value `t` with a folder `f`
85    /// call: `t.fold_with(f)`.
86    ///
87    /// For most types, this just traverses the value, calling `fold_with`
88    /// on each field/element.
89    ///
90    /// For types of interest (such as `Ty`), the implementation of this method
91    /// calls a folder method specifically for that type (such as
92    /// `F::fold_ty`). This is where control transfers from `TypeFoldable`
93    /// to `TypeFolder`.
94    ///
95    /// Same as [`TypeFoldable::try_fold_with`], but not fallible. Make sure to keep
96    /// the behavior in sync across functions.
97    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self;
98}
99
100// This trait is implemented for types of interest.
101pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> {
102    /// Provides a default fold for a recursive type of interest. This should
103    /// only be called within `TypeFolder` methods, when a non-custom traversal
104    /// is desired for the value of the type of interest passed to that method.
105    /// For example, in `MyFolder::try_fold_ty(ty)`, it is valid to call
106    /// `ty.try_super_fold_with(self)`, but any other folding should be done
107    /// with `xyz.try_fold_with(self)`.
108    fn try_super_fold_with<F: FallibleTypeFolder<I>>(
109        self,
110        folder: &mut F,
111    ) -> Result<Self, F::Error>;
112
113    /// A convenient alternative to `try_super_fold_with` for use with
114    /// infallible folders. Do not override this method, to ensure coherence
115    /// with `try_super_fold_with`.
116    fn super_fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self;
117}
118
119/// This trait is implemented for every infallible folding traversal. There is
120/// a fold method defined for every type of interest. Each such method has a
121/// default that does an "identity" fold. Implementations of these methods
122/// often fall back to a `super_fold_with` method if the primary argument
123/// doesn't satisfy a particular condition.
124pub trait TypeFolder<I: Interner>: Sized {
125    fn cx(&self) -> I;
126
127    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
128    where
129        T: TypeFoldable<I>,
130    {
131        t.super_fold_with(self)
132    }
133
134    fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
135        t.super_fold_with(self)
136    }
137
138    // The default region folder is a no-op because `Region` is non-recursive
139    // and has no `super_fold_with` method to call.
140    fn fold_region(&mut self, r: I::Region) -> I::Region {
141        r
142    }
143
144    fn fold_const(&mut self, c: I::Const) -> I::Const {
145        c.super_fold_with(self)
146    }
147
148    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
149        p.super_fold_with(self)
150    }
151
152    fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
153        c.super_fold_with(self)
154    }
155}
156
157/// This trait is implemented for every folding traversal. There is a fold
158/// method defined for every type of interest. Each such method has a default
159/// that does an "identity" fold.
160///
161/// A blanket implementation of this trait (that defers to the relevant
162/// method of [`TypeFolder`]) is provided for all infallible folders in
163/// order to ensure the two APIs are coherent.
164pub trait FallibleTypeFolder<I: Interner>: Sized {
165    type Error;
166
167    fn cx(&self) -> I;
168
169    fn try_fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> Result<ty::Binder<I, T>, Self::Error>
170    where
171        T: TypeFoldable<I>,
172    {
173        t.try_super_fold_with(self)
174    }
175
176    fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> {
177        t.try_super_fold_with(self)
178    }
179
180    // The default region folder is a no-op because `Region` is non-recursive
181    // and has no `super_fold_with` method to call.
182    fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
183        Ok(r)
184    }
185
186    fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> {
187        c.try_super_fold_with(self)
188    }
189
190    fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
191        p.try_super_fold_with(self)
192    }
193
194    fn try_fold_clauses(&mut self, c: I::Clauses) -> Result<I::Clauses, Self::Error> {
195        c.try_super_fold_with(self)
196    }
197}
198
199///////////////////////////////////////////////////////////////////////////
200// Traversal implementations.
201
202impl<I: Interner, T: TypeFoldable<I>, U: TypeFoldable<I>> TypeFoldable<I> for (T, U) {
203    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<(T, U), F::Error> {
204        Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?))
205    }
206
207    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
208        (self.0.fold_with(folder), self.1.fold_with(folder))
209    }
210}
211
212impl<I: Interner, A: TypeFoldable<I>, B: TypeFoldable<I>, C: TypeFoldable<I>> TypeFoldable<I>
213    for (A, B, C)
214{
215    fn try_fold_with<F: FallibleTypeFolder<I>>(
216        self,
217        folder: &mut F,
218    ) -> Result<(A, B, C), F::Error> {
219        Ok((
220            self.0.try_fold_with(folder)?,
221            self.1.try_fold_with(folder)?,
222            self.2.try_fold_with(folder)?,
223        ))
224    }
225
226    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
227        (self.0.fold_with(folder), self.1.fold_with(folder), self.2.fold_with(folder))
228    }
229}
230
231impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Option<T> {
232    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
233        Ok(match self {
234            Some(v) => Some(v.try_fold_with(folder)?),
235            None => None,
236        })
237    }
238
239    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
240        Some(self?.fold_with(folder))
241    }
242}
243
244impl<I: Interner, T: TypeFoldable<I>, E: TypeFoldable<I>> TypeFoldable<I> for Result<T, E> {
245    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
246        Ok(match self {
247            Ok(v) => Ok(v.try_fold_with(folder)?),
248            Err(e) => Err(e.try_fold_with(folder)?),
249        })
250    }
251
252    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
253        match self {
254            Ok(v) => Ok(v.fold_with(folder)),
255            Err(e) => Err(e.fold_with(folder)),
256        }
257    }
258}
259
260fn fold_arc<T: Clone, E>(
261    mut arc: Arc<T>,
262    fold: impl FnOnce(T) -> Result<T, E>,
263) -> Result<Arc<T>, E> {
264    // We merely want to replace the contained `T`, if at all possible,
265    // so that we don't needlessly allocate a new `Arc` or indeed clone
266    // the contained type.
267    unsafe {
268        // First step is to ensure that we have a unique reference to
269        // the contained type, which `Arc::make_mut` will accomplish (by
270        // allocating a new `Arc` and cloning the `T` only if required).
271        // This is done *before* casting to `Arc<ManuallyDrop<T>>` so that
272        // panicking during `make_mut` does not leak the `T`.
273        Arc::make_mut(&mut arc);
274
275        // Casting to `Arc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
276        // is `repr(transparent)`.
277        let ptr = Arc::into_raw(arc).cast::<mem::ManuallyDrop<T>>();
278        let mut unique = Arc::from_raw(ptr);
279
280        // Call to `Arc::make_mut` above guarantees that `unique` is the
281        // sole reference to the contained value, so we can avoid doing
282        // a checked `get_mut` here.
283        let slot = Arc::get_mut(&mut unique).unwrap_unchecked();
284
285        // Semantically move the contained type out from `unique`, fold
286        // it, then move the folded value back into `unique`. Should
287        // folding fail, `ManuallyDrop` ensures that the "moved-out"
288        // value is not re-dropped.
289        let owned = mem::ManuallyDrop::take(slot);
290        let folded = fold(owned)?;
291        *slot = mem::ManuallyDrop::new(folded);
292
293        // Cast back to `Arc<T>`.
294        Ok(Arc::from_raw(Arc::into_raw(unique).cast()))
295    }
296}
297
298impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Arc<T> {
299    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
300        fold_arc(self, |t| t.try_fold_with(folder))
301    }
302
303    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
304        match fold_arc::<T, Infallible>(self, |t| Ok(t.fold_with(folder))) {
305            Ok(t) => t,
306        }
307    }
308}
309
310impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<T> {
311    fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
312        *self = (*self).try_fold_with(folder)?;
313        Ok(self)
314    }
315
316    fn fold_with<F: TypeFolder<I>>(mut self, folder: &mut F) -> Self {
317        *self = (*self).fold_with(folder);
318        self
319    }
320}
321
322impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> {
323    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
324        self.into_iter().map(|t| t.try_fold_with(folder)).collect()
325    }
326
327    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
328        self.into_iter().map(|t| t.fold_with(folder)).collect()
329    }
330}
331
332impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for ThinVec<T> {
333    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
334        self.into_iter().map(|t| t.try_fold_with(folder)).collect()
335    }
336
337    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
338        self.into_iter().map(|t| t.fold_with(folder)).collect()
339    }
340}
341
342impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<[T]> {
343    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
344        Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice)
345    }
346
347    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
348        Vec::into_boxed_slice(Vec::from(self).fold_with(folder))
349    }
350}
351
352impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
353    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
354        self.raw.try_fold_with(folder).map(IndexVec::from_raw)
355    }
356
357    fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
358        IndexVec::from_raw(self.raw.fold_with(folder))
359    }
360}
361
362///////////////////////////////////////////////////////////////////////////
363// Shifter
364//
365// Shifts the De Bruijn indices on all escaping bound vars by a
366// fixed amount. Useful in instantiation or when otherwise introducing
367// a binding level that is not intended to capture the existing bound
368// vars. See comment on `shift_vars_through_binders` method in
369// `rustc_middle/src/ty/generic_args.rs` for more details.
370
371struct Shifter<I: Interner> {
372    cx: I,
373    current_index: ty::DebruijnIndex,
374    amount: u32,
375}
376
377impl<I: Interner> Shifter<I> {
378    fn new(cx: I, amount: u32) -> Self {
379        Shifter { cx, current_index: ty::INNERMOST, amount }
380    }
381}
382
383impl<I: Interner> TypeFolder<I> for Shifter<I> {
384    fn cx(&self) -> I {
385        self.cx
386    }
387
388    fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
389        self.current_index.shift_in(1);
390        let t = t.super_fold_with(self);
391        self.current_index.shift_out(1);
392        t
393    }
394
395    fn fold_region(&mut self, r: I::Region) -> I::Region {
396        match r.kind() {
397            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
398                if debruijn >= self.current_index =>
399            {
400                let debruijn = debruijn.shifted_in(self.amount);
401                Region::new_bound(self.cx, debruijn, br)
402            }
403            _ => r,
404        }
405    }
406
407    fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
408        match ty.kind() {
409            ty::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
410                if debruijn >= self.current_index =>
411            {
412                let debruijn = debruijn.shifted_in(self.amount);
413                Ty::new_bound(self.cx, debruijn, bound_ty)
414            }
415
416            _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
417            _ => ty,
418        }
419    }
420
421    fn fold_const(&mut self, ct: I::Const) -> I::Const {
422        match ct.kind() {
423            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ct)
424                if debruijn >= self.current_index =>
425            {
426                let debruijn = debruijn.shifted_in(self.amount);
427                Const::new_bound(self.cx, debruijn, bound_ct)
428            }
429            _ => ct.super_fold_with(self),
430        }
431    }
432
433    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
434        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
435    }
436
437    fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
438        if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c }
439    }
440}
441
442pub fn shift_region<I: Interner>(cx: I, region: I::Region, amount: u32) -> I::Region {
443    match region.kind() {
444        ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) if amount > 0 => {
445            Region::new_bound(cx, debruijn.shifted_in(amount), br)
446        }
447        _ => region,
448    }
449}
450
451x;#[instrument(level = "trace", skip(cx), ret)]
452pub fn shift_vars<I: Interner, T>(cx: I, value: T, amount: u32) -> T
453where
454    T: TypeFoldable<I>,
455{
456    if amount == 0 || !value.has_escaping_bound_vars() {
457        value
458    } else {
459        value.fold_with(&mut Shifter::new(cx, amount))
460    }
461}
462
463///////////////////////////////////////////////////////////////////////////
464// Region folder
465
466pub fn fold_regions<I: Interner, T>(
467    cx: I,
468    value: T,
469    f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
470) -> T
471where
472    T: TypeFoldable<I>,
473{
474    value.fold_with(&mut RegionFolder::new(cx, f))
475}
476
477/// Folds over the substructure of a type, visiting its component
478/// types and all regions that occur *free* within it.
479///
480/// That is, function pointer types and trait objects can introduce
481/// new bound regions which are not visited by this visitor as
482/// they are not free; only regions that occur free will be
483/// visited by `fold_region_fn`.
484pub struct RegionFolder<I, F> {
485    cx: I,
486
487    /// Stores the index of a binder *just outside* the stuff we have
488    /// visited. So this begins as INNERMOST; when we pass through a
489    /// binder, it is incremented (via `shift_in`).
490    current_index: ty::DebruijnIndex,
491
492    /// Callback invoked for each free region. The `DebruijnIndex`
493    /// points to the binder *just outside* the ones we have passed
494    /// through.
495    fold_region_fn: F,
496}
497
498impl<I, F> RegionFolder<I, F> {
499    #[inline]
500    pub fn new(cx: I, fold_region_fn: F) -> RegionFolder<I, F> {
501        RegionFolder { cx, current_index: ty::INNERMOST, fold_region_fn }
502    }
503}
504
505impl<I, F> TypeFolder<I> for RegionFolder<I, F>
506where
507    I: Interner,
508    F: FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
509{
510    fn cx(&self) -> I {
511        self.cx
512    }
513
514    fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
515        self.current_index.shift_in(1);
516        let t = t.super_fold_with(self);
517        self.current_index.shift_out(1);
518        t
519    }
520
521    x;#[instrument(skip(self), level = "debug", ret)]
522    fn fold_region(&mut self, r: I::Region) -> I::Region {
523        match r.kind() {
524            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
525                if debruijn < self.current_index =>
526            {
527                debug!(?self.current_index, "skipped bound region");
528                r
529            }
530            ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
531                debug!(?self.current_index, "skipped bound region");
532                r
533            }
534            _ => {
535                debug!(?self.current_index, "folding free region");
536                (self.fold_region_fn)(r, self.current_index)
537            }
538        }
539    }
540
541    fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
542        if t.has_regions() { t.super_fold_with(self) } else { t }
543    }
544
545    fn fold_const(&mut self, ct: I::Const) -> I::Const {
546        if ct.has_regions() { ct.super_fold_with(self) } else { ct }
547    }
548
549    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
550        if p.has_regions() { p.super_fold_with(self) } else { p }
551    }
552
553    fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
554        if c.has_regions() { c.super_fold_with(self) } else { c }
555    }
556}