|
| 1 | +# Fold and the Folder trait |
| 2 | + |
| 3 | +The [`Fold`] trait permits one to traverse a type or other term in the |
| 4 | +chalk-ir and make a copy of it, possibly making small substitutions or |
| 5 | +alterations along the way. Folding also allows copying a term from one |
| 6 | +type family to another. |
| 7 | + |
| 8 | +[`Fold`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html |
| 9 | + |
| 10 | +To use the [`Fold`] trait, one invokes the [`Fold::fold_with`] method, supplying some |
| 11 | +"folder" as well as the number of "in scope binders" for that term (typically `0` |
| 12 | +to start): |
| 13 | + |
| 14 | +```rust,ignore |
| 15 | +let output_ty = input_ty.fold_with(&mut folder, 0); |
| 16 | +``` |
| 17 | + |
| 18 | +[`Fold::fold_with`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#tymethod.fold_with |
| 19 | + |
| 20 | +The folder is some instance of the [`Folder`] trait. This trait |
| 21 | +defines a few key callbacks that allow you to substitute different |
| 22 | +values as the fold proceeds. For example, when a type is folded, the |
| 23 | +folder can substitute a new type in its place. |
| 24 | + |
| 25 | +[`Folder`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Folder.html |
| 26 | + |
| 27 | +## Uses for folders |
| 28 | + |
| 29 | +A common use for `Fold` is to permit a substitution -- that is, |
| 30 | +replacing generic type parameters with their values. |
| 31 | + |
| 32 | +## From Fold to Folder to SuperFold and back again |
| 33 | + |
| 34 | +The overall flow of folding is like this. |
| 35 | + |
| 36 | +1. [`Fold::fold_with`] is invoked on the outermost term. It recursively |
| 37 | + walks the term. |
| 38 | +2. For those sorts of terms (types, lifetimes, goals, program clauses) that have |
| 39 | + callbacks in the [`Folder`] trait, invoking [`Fold::fold_with`] will in turn |
| 40 | + invoke the corresponding method on the [`Folder`] trait, such as `Folder::fold_ty`. |
| 41 | +3. The default implementation of `Folder::fold_ty`, in turn, invokes |
| 42 | + `SuperFold::super_fold_with`. This will recursively fold the |
| 43 | + contents of the type. In some cases, the `super_fold_with` |
| 44 | + implementation invokes more specialized methods on [`Folder`], such |
| 45 | + as [`Folder::fold_free_var_ty`], which makes it easier to write |
| 46 | + folders that just intercept *certain* types. |
| 47 | + |
| 48 | +Thus, as a user, you can customize folding by: |
| 49 | + |
| 50 | +* Defining your own `Folder` type |
| 51 | +* Implementing the appropriate methods to "intercept" types/lifetimes/etc at the right level of |
| 52 | + detail |
| 53 | +* In those methods, if you find a case where you would prefer not to |
| 54 | + substitute a new value, then invoke `SuperFold::super_fold_with` to |
| 55 | + return to the default behavior. |
| 56 | + |
| 57 | +## The `binders` argument |
| 58 | + |
| 59 | +Each callback in the [`Folder`] trait takes a `binders` argument. This indicates |
| 60 | +the number of binders that we have traversed during folding, which is relevant for debruijn indices. |
| 61 | +So e.g. a bound variable with depth 1, if invoked with a `binders` value of 1, indicates something that was bound to something external to the fold. |
| 62 | + |
| 63 | +XXX explain with examples and in more detail |
| 64 | + |
| 65 | +## The `Fold::Result` associated type |
| 66 | + |
| 67 | +The `Fold` trait defines a [`Result`] associated type, indicating the |
| 68 | +type that will result from folding. |
| 69 | + |
| 70 | +[`Result`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#associatedtype.Result |
| 71 | + |
| 72 | +## When to implement the Fold and SuperFold traits |
| 73 | + |
| 74 | +Any piece of IR that represents a kind of "term" (e.g., a type, part |
| 75 | +of a type, or a goal, etc) in the logic should implement `Fold`. We |
| 76 | +also implement `Fold` for common collection types like `Vec` as well |
| 77 | +as tuples, references, etc. |
| 78 | + |
| 79 | +The `SuperFold` trait should only be implemented for those types that |
| 80 | +have a callback defined on the `Folder` trait (e.g., types and |
| 81 | +lifetimes). |
| 82 | + |
| 83 | +## Derives |
| 84 | + |
| 85 | +Using the `chalk-derive` crate, you can auto-derive the `Fold` trait. |
| 86 | +There isn't presently a derive for `SuperFold` since it is very rare |
| 87 | +to require it. The derive for `Fold` is a bit cludgy and requires: |
| 88 | + |
| 89 | +* You must import `Fold` into scope. |
| 90 | +* The type you are deriving `Fold` on must have either: |
| 91 | + * A type parameter that has a `TypeFamily` bound, like `TF: TypeFamily` |
| 92 | + * A type parameter that has a `HasTypeFamily` bound, like `TF: HasTypeFamily` |
| 93 | + * The `has_type_family(XXX)` attribute. |
| 94 | + |
| 95 | + |
0 commit comments