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//! ```
4748use std::convert::Infallible;
49use std::mem;
50use std::sync::Arc;
5152use rustc_index::{Idx, IndexVec};
53use thin_vec::ThinVec;
54use tracing::{debug, instrument};
5556use crate::inherent::*;
57use crate::visit::{TypeVisitable, TypeVisitableExtas _};
58use crate::{selfas ty, BoundVarIndexKind, Interner};
5960/// 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`].
82fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error>;
8384/// 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.
97fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self;
98}
99100// 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)`.
108fn try_super_fold_with<F: FallibleTypeFolder<I>>(
109self,
110 folder: &mut F,
111 ) -> Result<Self, F::Error>;
112113/// 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`.
116fn super_fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self;
117}
118119/// 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 {
125fn cx(&self) -> I;
126127fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
128where
129T: TypeFoldable<I>,
130 {
131t.super_fold_with(self)
132 }
133134fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
135t.super_fold_with(self)
136 }
137138// The default region folder is a no-op because `Region` is non-recursive
139 // and has no `super_fold_with` method to call.
140fn fold_region(&mut self, r: I::Region) -> I::Region {
141r142 }
143144fn fold_const(&mut self, c: I::Const) -> I::Const {
145c.super_fold_with(self)
146 }
147148fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
149p.super_fold_with(self)
150 }
151152fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
153c.super_fold_with(self)
154 }
155}
156157/// 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 {
165type Error;
166167fn cx(&self) -> I;
168169fn try_fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> Result<ty::Binder<I, T>, Self::Error>
170where
171T: TypeFoldable<I>,
172 {
173t.try_super_fold_with(self)
174 }
175176fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> {
177t.try_super_fold_with(self)
178 }
179180// The default region folder is a no-op because `Region` is non-recursive
181 // and has no `super_fold_with` method to call.
182fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
183Ok(r)
184 }
185186fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> {
187c.try_super_fold_with(self)
188 }
189190fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
191p.try_super_fold_with(self)
192 }
193194fn try_fold_clauses(&mut self, c: I::Clauses) -> Result<I::Clauses, Self::Error> {
195c.try_super_fold_with(self)
196 }
197}
198199///////////////////////////////////////////////////////////////////////////
200// Traversal implementations.
201202impl<I: Interner, T: TypeFoldable<I>, U: TypeFoldable<I>> TypeFoldable<I> for (T, U) {
203fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<(T, U), F::Error> {
204Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?))
205 }
206207fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
208 (self.0.fold_with(folder), self.1.fold_with(folder))
209 }
210}
211212impl<I: Interner, A: TypeFoldable<I>, B: TypeFoldable<I>, C: TypeFoldable<I>> TypeFoldable<I>
213for (A, B, C)
214{
215fn try_fold_with<F: FallibleTypeFolder<I>>(
216self,
217 folder: &mut F,
218 ) -> Result<(A, B, C), F::Error> {
219Ok((
220self.0.try_fold_with(folder)?,
221self.1.try_fold_with(folder)?,
222self.2.try_fold_with(folder)?,
223 ))
224 }
225226fn 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}
230231impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Option<T> {
232fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
233Ok(match self {
234Some(v) => Some(v.try_fold_with(folder)?),
235None => None,
236 })
237 }
238239fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
240Some(self?.fold_with(folder))
241 }
242}
243244impl<I: Interner, T: TypeFoldable<I>, E: TypeFoldable<I>> TypeFoldable<I> for Result<T, E> {
245fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
246Ok(match self {
247Ok(v) => Ok(v.try_fold_with(folder)?),
248Err(e) => Err(e.try_fold_with(folder)?),
249 })
250 }
251252fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
253match self {
254Ok(v) => Ok(v.fold_with(folder)),
255Err(e) => Err(e.fold_with(folder)),
256 }
257 }
258}
259260fn fold_arc<T: Clone, E>(
261mut 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.
267unsafe {
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`.
273Arc::make_mut(&mut arc);
274275// Casting to `Arc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
276 // is `repr(transparent)`.
277let ptr = Arc::into_raw(arc).cast::<mem::ManuallyDrop<T>>();
278let mut unique = Arc::from_raw(ptr);
279280// 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.
283let slot = Arc::get_mut(&mut unique).unwrap_unchecked();
284285// 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.
289let owned = mem::ManuallyDrop::take(slot);
290let folded = fold(owned)?;
291*slot = mem::ManuallyDrop::new(folded);
292293// Cast back to `Arc<T>`.
294Ok(Arc::from_raw(Arc::into_raw(unique).cast()))
295 }
296}
297298impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Arc<T> {
299fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
300fold_arc(self, |t| t.try_fold_with(folder))
301 }
302303fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
304match fold_arc::<T, Infallible>(self, |t| Ok(t.fold_with(folder))) {
305Ok(t) => t,
306 }
307 }
308}
309310impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<T> {
311fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
312*self = (*self).try_fold_with(folder)?;
313Ok(self)
314 }
315316fn fold_with<F: TypeFolder<I>>(mut self, folder: &mut F) -> Self {
317*self = (*self).fold_with(folder);
318self319 }
320}
321322impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> {
323fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
324self.into_iter().map(|t| t.try_fold_with(folder)).collect()
325 }
326327fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
328self.into_iter().map(|t| t.fold_with(folder)).collect()
329 }
330}
331332impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for ThinVec<T> {
333fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
334self.into_iter().map(|t| t.try_fold_with(folder)).collect()
335 }
336337fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
338self.into_iter().map(|t| t.fold_with(folder)).collect()
339 }
340}
341342impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<[T]> {
343fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
344Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice)
345 }
346347fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
348Vec::into_boxed_slice(Vec::from(self).fold_with(folder))
349 }
350}
351352impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
353fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
354self.raw.try_fold_with(folder).map(IndexVec::from_raw)
355 }
356357fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
358 IndexVec::from_raw(self.raw.fold_with(folder))
359 }
360}
361362///////////////////////////////////////////////////////////////////////////
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.
370371struct Shifter<I: Interner> {
372 cx: I,
373 current_index: ty::DebruijnIndex,
374 amount: u32,
375}
376377impl<I: Interner> Shifter<I> {
378fn new(cx: I, amount: u32) -> Self {
379Shifter { cx, current_index: ty::INNERMOST, amount }
380 }
381}
382383impl<I: Interner> TypeFolder<I> for Shifter<I> {
384fn cx(&self) -> I {
385self.cx
386 }
387388fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
389self.current_index.shift_in(1);
390let t = t.super_fold_with(self);
391self.current_index.shift_out(1);
392t393 }
394395fn fold_region(&mut self, r: I::Region) -> I::Region {
396match r.kind() {
397 ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
398if debruijn >= self.current_index =>
399 {
400let debruijn = debruijn.shifted_in(self.amount);
401 Region::new_bound(self.cx, debruijn, br)
402 }
403_ => r,
404 }
405 }
406407fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
408match ty.kind() {
409 ty::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
410if debruijn >= self.current_index =>
411 {
412let debruijn = debruijn.shifted_in(self.amount);
413 Ty::new_bound(self.cx, debruijn, bound_ty)
414 }
415416_ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
417_ => ty,
418 }
419 }
420421fn fold_const(&mut self, ct: I::Const) -> I::Const {
422match ct.kind() {
423 ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ct)
424if debruijn >= self.current_index =>
425 {
426let debruijn = debruijn.shifted_in(self.amount);
427 Const::new_bound(self.cx, debruijn, bound_ct)
428 }
429_ => ct.super_fold_with(self),
430 }
431 }
432433fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
434if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
435 }
436437fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
438if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c }
439 }
440}
441442pub fn shift_region<I: Interner>(cx: I, region: I::Region, amount: u32) -> I::Region {
443match 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}
450451x;#[instrument(level = "trace", skip(cx), ret)]452pub fn shift_vars<I: Interner, T>(cx: I, value: T, amount: u32) -> T
453where
454T: TypeFoldable<I>,
455{
456if amount == 0 || !value.has_escaping_bound_vars() {
457 value
458 } else {
459 value.fold_with(&mut Shifter::new(cx, amount))
460 }
461}
462463///////////////////////////////////////////////////////////////////////////
464// Region folder
465466pub 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
472T: TypeFoldable<I>,
473{
474value.fold_with(&mut RegionFolder::new(cx, f))
475}
476477/// 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,
486487/// 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`).
490current_index: ty::DebruijnIndex,
491492/// Callback invoked for each free region. The `DebruijnIndex`
493 /// points to the binder *just outside* the ones we have passed
494 /// through.
495fold_region_fn: F,
496}
497498impl<I, F> RegionFolder<I, F> {
499#[inline]
500pub fn new(cx: I, fold_region_fn: F) -> RegionFolder<I, F> {
501RegionFolder { cx, current_index: ty::INNERMOST, fold_region_fn }
502 }
503}
504505impl<I, F> TypeFolder<I> for RegionFolder<I, F>
506where
507I: Interner,
508 F: FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
509{
510fn cx(&self) -> I {
511self.cx
512 }
513514fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
515self.current_index.shift_in(1);
516let t = t.super_fold_with(self);
517self.current_index.shift_out(1);
518t519 }
520521x;#[instrument(skip(self), level = "debug", ret)]522fn fold_region(&mut self, r: I::Region) -> I::Region {
523match r.kind() {
524 ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
525if debruijn < self.current_index =>
526 {
527debug!(?self.current_index, "skipped bound region");
528 r
529 }
530 ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
531debug!(?self.current_index, "skipped bound region");
532 r
533 }
534_ => {
535debug!(?self.current_index, "folding free region");
536 (self.fold_region_fn)(r, self.current_index)
537 }
538 }
539 }
540541fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
542if t.has_regions() { t.super_fold_with(self) } else { t }
543 }
544545fn fold_const(&mut self, ct: I::Const) -> I::Const {
546if ct.has_regions() { ct.super_fold_with(self) } else { ct }
547 }
548549fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
550if p.has_regions() { p.super_fold_with(self) } else { p }
551 }
552553fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
554if c.has_regions() { c.super_fold_with(self) } else { c }
555 }
556}