1
- //! Generalized type folding mechanism. The setup is a bit convoluted
2
- //! but allows for convenient usage. Let T be an instance of some
3
- //! "foldable type" (one which implements `TypeFoldable`) and F be an
4
- //! instance of a "folder" (a type which implements `TypeFolder`). Then
5
- //! the setup is intended to be:
1
+ //! A generalized traversal mechanism for complex data structures that contain
2
+ //! type information.
6
3
//!
7
- //! T.fold_with(F) --calls--> F.fold_T(T) --calls--> T.super_fold_with(F)
4
+ //! There are two types of traversal.
5
+ //! - Folding. This is a modifying traversal. It consumes the data structure,
6
+ //! producing a (possibly) modified version of it. Both fallible and
7
+ //! infallible versions are available. The name is potentially
8
+ //! confusing, because this traversal is more like `Iterator::map` than
9
+ //! `Iterator::fold`.
10
+ //! - Visiting. This is a read-only traversal of the data structure.
8
11
//!
9
- //! This way, when you define a new folder F, you can override
10
- //! `fold_T()` to customize the behavior, and invoke `T.super_fold_with()`
11
- //! to get the original behavior. Meanwhile, to actually fold
12
- //! something, you can just write `T.fold_with(F)`, which is
13
- //! convenient. (Note that `fold_with` will also transparently handle
14
- //! things like a `Vec<T>` where T is foldable and so on.)
12
+ //! These traversals have limited flexibility. Only a small number of "types of
13
+ //! interest" within the complex data structures can receive custom
14
+ //! modification (when folding) or custom visitation (when visiting). These are
15
+ //! the ones containing the most important type-related information, such as
16
+ //! `Ty`, `Predicate`, `Region`, and `Const`.
15
17
//!
16
- //! In this ideal setup, the only function that actually *does*
17
- //! anything is `T.super_fold_with()`, which traverses the type `T`.
18
- //! Moreover, `T.super_fold_with()` should only ever call `T.fold_with()`.
18
+ //! There are two traits involved in each traversal type.
19
+ //! - The first trait is `TypeFoldable`, which is implemented once for many
20
+ //! types. This includes both (a) types of interest, and (b) all other
21
+ //! relevant types, including generic containers like `Vec` and `Option`. It
22
+ //! defines a "skeleton" of how they should be traversed, for both folding
23
+ //! and visiting.
24
+ //! - The second trait is `TypeFolder`/`FallibleTypeFolder` (for
25
+ //! infallible/fallible folding traversals) or `TypeVisitor` (for visiting
26
+ //! traversals). One of these is implemented for each folder/visitor. This
27
+ //! defines how types of interest are handled.
19
28
//!
20
- //! In some cases, we follow a degenerate pattern where we do not have
21
- //! a `fold_T` method. Instead, `T.fold_with` traverses the structure directly.
22
- //! This is suboptimal because the behavior cannot be overridden, but it's
23
- //! much less work to implement. If you ever *do* need an override that
24
- //! doesn't exist, it's not hard to convert the degenerate pattern into the
25
- //! proper thing.
29
+ //! This means each traversal is a mixture of (a) generic traversal operations,
30
+ //! and (b) custom fold/visit operations that are specific to the
31
+ //! folder/visitor.
32
+ //! - The `TypeFoldable` impls handle most of the traversal, and call into
33
+ //! `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` when they encounter a
34
+ //! type of interest.
35
+ //! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may also call back into
36
+ //! a `TypeFoldable` impl, because (a) the types of interest are recursive
37
+ //! and can contain other types of interest, and (b) each folder/visitor
38
+ //! might provide custom handling only for some types of interest, or only
39
+ //! for some variants of each type of interest, and then use default
40
+ //! traversal for the remaining cases.
26
41
//!
27
- //! A `TypeFoldable` T can also be visited by a `TypeVisitor` V using similar setup:
28
- //!
29
- //! T.visit_with(V) --calls--> V.visit_T(T) --calls--> T.super_visit_with(V).
30
- //!
31
- //! These methods return true to indicate that the visitor has found what it is
32
- //! looking for, and does not need to visit anything else.
42
+ //! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U:
43
+ //! TypeFoldable`, and an instance `S(ty, u)`, it would be visited like so:
44
+ //! ```
45
+ //! s.visit_with(visitor) calls
46
+ //! - s.super_visit_with(visitor) calls
47
+ //! - ty.visit_with(visitor) calls
48
+ //! - visitor.visit_ty(ty) may call
49
+ //! - ty.super_visit_with(visitor)
50
+ //! - u.visit_with(visitor)
51
+ //! ```
33
52
use crate :: mir;
34
53
use crate :: ty:: { self , flags:: FlagComputation , Binder , Ty , TyCtxt , TypeFlags } ;
35
- use rustc_hir as hir;
36
54
use rustc_hir:: def_id:: DefId ;
37
55
38
56
use rustc_data_structures:: fx:: FxHashSet ;
@@ -41,42 +59,67 @@ use std::collections::BTreeMap;
41
59
use std:: fmt;
42
60
use std:: ops:: ControlFlow ;
43
61
44
- /// This trait is implemented for every type that can be folded.
45
- /// Basically, every type that has a corresponding method in `TypeFolder` .
62
+ /// This trait is implemented for every type that can be folded/visited,
63
+ /// providing the skeleton of the traversal .
46
64
///
47
- /// To implement this conveniently, use the derive macro located in `rustc_macros`.
65
+ /// To implement this conveniently, use the derive macro located in
66
+ /// `rustc_macros`.
48
67
pub trait TypeFoldable < ' tcx > : fmt:: Debug + Clone {
49
- /// Consumers may find this more convenient to use with infallible folders than
50
- /// [`try_super_fold_with`][`TypeFoldable::try_super_fold_with`], to which the
51
- /// provided default definition delegates. Implementors **should not** override
52
- /// this provided default definition, to ensure that the two methods are coherent
53
- /// (provide a definition of `try_super_fold_with` instead).
54
- fn super_fold_with < F : TypeFolder < ' tcx , Error = !> > ( self , folder : & mut F ) -> Self {
55
- self . try_super_fold_with ( folder) . into_ok ( )
68
+ /// The main entry point for folding. To fold a value `t` with a folder `f`
69
+ /// call: `t.try_fold_with(f)`.
70
+ ///
71
+ /// For types of interest (such as `Ty`), this default is overridden with a
72
+ /// method that calls a folder method specifically for that type (such as
73
+ /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable`
74
+ /// to `TypeFolder`.
75
+ ///
76
+ /// For other types, this default is used.
77
+ fn try_fold_with < F : FallibleTypeFolder < ' tcx > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
78
+ self . try_super_fold_with ( folder)
56
79
}
57
- /// Consumers may find this more convenient to use with infallible folders than
58
- /// [`try_fold_with`][`TypeFoldable::try_fold_with`], to which the provided
59
- /// default definition delegates. Implementors **should not** override this
60
- /// provided default definition, to ensure that the two methods are coherent
61
- /// (provide a definition of `try_fold_with` instead).
80
+
81
+ /// A convenient alternative to [`try_fold_with`] for use with infallible
82
+ /// folders. Do not override this method, to ensure coherence with
83
+ /// `try_fold_with`.
62
84
fn fold_with < F : TypeFolder < ' tcx , Error = !> > ( self , folder : & mut F ) -> Self {
63
85
self . try_fold_with ( folder) . into_ok ( )
64
86
}
65
87
88
+ /// Traverses the type in question, typically by calling `try_fold_with` on
89
+ /// each field/element. This is true even for types of interest such as
90
+ /// `Ty`. This should only be called within `TypeFolder` methods, when
91
+ /// non-custom traversals are desired for types of interest.
66
92
fn try_super_fold_with < F : FallibleTypeFolder < ' tcx > > (
67
93
self ,
68
94
folder : & mut F ,
69
95
) -> Result < Self , F :: Error > ;
70
96
71
- fn try_fold_with < F : FallibleTypeFolder < ' tcx > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
72
- self . try_super_fold_with ( folder)
97
+ /// A convenient alternative to [`try_super_fold_with`] for use with
98
+ /// infallible folders. Do not override this method, to ensure coherence
99
+ /// with `try_super_fold_with`.
100
+ fn super_fold_with < F : TypeFolder < ' tcx , Error = !> > ( self , folder : & mut F ) -> Self {
101
+ self . try_super_fold_with ( folder) . into_ok ( )
73
102
}
74
103
75
- fn super_visit_with < V : TypeVisitor < ' tcx > > ( & self , visitor : & mut V ) -> ControlFlow < V :: BreakTy > ;
104
+ /// The entry point for visiting. To visit a value `t` with a visitor `v`
105
+ /// call: `t.visit_with(v)`.
106
+ ///
107
+ /// For types of interest (such as `Ty`), this default is overridden with a
108
+ /// method that calls a visitor method specifically for that type (such as
109
+ /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to
110
+ /// `TypeVisitor`.
111
+ ///
112
+ /// For other types, this default is used.
76
113
fn visit_with < V : TypeVisitor < ' tcx > > ( & self , visitor : & mut V ) -> ControlFlow < V :: BreakTy > {
77
114
self . super_visit_with ( visitor)
78
115
}
79
116
117
+ /// Traverses the type in question, typically by calling `visit_with` on
118
+ /// each field/element. This is true even for types of interest such as
119
+ /// `Ty`. This should only be called within `TypeVisitor` methods, when
120
+ /// non-custom traversals are desired for types of interest.
121
+ fn super_visit_with < V : TypeVisitor < ' tcx > > ( & self , visitor : & mut V ) -> ControlFlow < V :: BreakTy > ;
122
+
80
123
/// Returns `true` if `self` has any late-bound regions that are either
81
124
/// bound by `binder` or bound by some binder outside of `binder`.
82
125
/// If `binder` is `ty::INNERMOST`, this indicates whether
@@ -168,24 +211,13 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
168
211
}
169
212
}
170
213
171
- impl < ' tcx > TypeFoldable < ' tcx > for hir:: Constness {
172
- fn try_super_fold_with < F : TypeFolder < ' tcx > > ( self , _: & mut F ) -> Result < Self , F :: Error > {
173
- Ok ( self )
174
- }
175
- fn super_visit_with < V : TypeVisitor < ' tcx > > ( & self , _: & mut V ) -> ControlFlow < V :: BreakTy > {
176
- ControlFlow :: CONTINUE
177
- }
178
- }
179
-
180
- /// The `TypeFolder` trait defines the actual *folding*. There is a
181
- /// method defined for every foldable type. Each of these has a
182
- /// default implementation that does an "identity" fold. Within each
183
- /// identity fold, it should invoke `foo.fold_with(self)` to fold each
184
- /// sub-item.
214
+ /// This trait is implemented for every folding traversal. There is a fold
215
+ /// method defined for every type of interest. Each such method has a default
216
+ /// that does an "identity" fold.
185
217
///
186
218
/// If this folder is fallible (and therefore its [`Error`][`TypeFolder::Error`]
187
- /// associated type is something other than the default, never),
188
- /// [`FallibleTypeFolder`] should be implemented manually; otherwise ,
219
+ /// associated type is something other than the default `!`) then
220
+ /// [`FallibleTypeFolder`] should be implemented manually. Otherwise ,
189
221
/// a blanket implementation of [`FallibleTypeFolder`] will defer to
190
222
/// the infallible methods of this trait to ensure that the two APIs
191
223
/// are coherent.
@@ -238,11 +270,9 @@ pub trait TypeFolder<'tcx>: Sized {
238
270
}
239
271
}
240
272
241
- /// The `FallibleTypeFolder` trait defines the actual *folding*. There is a
242
- /// method defined for every foldable type. Each of these has a
243
- /// default implementation that does an "identity" fold. Within each
244
- /// identity fold, it should invoke `foo.try_fold_with(self)` to fold each
245
- /// sub-item.
273
+ /// This trait is implemented for every folding traversal. There is a fold
274
+ /// method defined for every type of interest. Each such method has a default
275
+ /// that does an "identity" fold.
246
276
///
247
277
/// A blanket implementation of this trait (that defers to the relevant
248
278
/// method of [`TypeFolder`]) is provided for all infallible folders in
@@ -282,8 +312,8 @@ pub trait FallibleTypeFolder<'tcx>: TypeFolder<'tcx> {
282
312
}
283
313
}
284
314
285
- // Blanket implementation of fallible trait for infallible folders
286
- // delegates to infallible methods to prevent incoherence
315
+ // This blanket implementation of the fallible trait for infallible folders
316
+ // delegates to infallible methods to ensure coherence.
287
317
impl < ' tcx , F > FallibleTypeFolder < ' tcx > for F
288
318
where
289
319
F : TypeFolder < ' tcx , Error = !> ,
@@ -322,6 +352,9 @@ where
322
352
}
323
353
}
324
354
355
+ /// This trait is implemented for every visiting traversal. There is a visit
356
+ /// method defined for every type of interest. Each such method has a default
357
+ /// that recurses into the type's fields in a non-custom fashion.
325
358
pub trait TypeVisitor < ' tcx > : Sized {
326
359
type BreakTy = !;
327
360
0 commit comments