|
| 1 | +# Canonicalization |
| 2 | + |
| 3 | +Canonicalization is the process of **isolating** an inference value |
| 4 | +from its context. It is a key part of implementing |
| 5 | +[canonical queries][cq], and you may wish to read the parent chapter |
| 6 | +to get more context. |
| 7 | + |
| 8 | +Canonicalization is really based on a very simple concept: every |
| 9 | +[inference variable](../type-inference.html#vars) is always in one of |
| 10 | +two states: either it is **unbound**, in which case we don't know yet |
| 11 | +what type it is, or it is **bound**, in which case we do. So to |
| 12 | +isolate some data-structure T that contains types/regions from its |
| 13 | +environment, we just walk down and find the unbound variables that |
| 14 | +appear in T; those variables get replaced with "canonical variables", |
| 15 | +starting from zero and numbered in a fixed order (left to right, for |
| 16 | +the most part, but really it doesn't matter as long as it is |
| 17 | +consistent). |
| 18 | + |
| 19 | +[cq]: ./canonical-queries.html |
| 20 | + |
| 21 | +So, for example, if we have the type `X = (?T, ?U)`, where `?T` and |
| 22 | +`?U` are distinct, unbound inference variables, then the canonical |
| 23 | +form of `X` would be `(?0, ?1)`, where `?0` and `?1` represent these |
| 24 | +**canonical placeholders**. Note that the type `Y = (?U, ?T)` also |
| 25 | +canonicalizes to `(?0, ?1)`. But the type `Z = (?T, ?T)` would |
| 26 | +canonicalize to `(?0, ?0)` (as would `(?U, ?U)`). In other words, the |
| 27 | +exact identity of the inference variables is not important – unless |
| 28 | +they are repeated. |
| 29 | + |
| 30 | +We use this to improve caching as well as to detect cycles and other |
| 31 | +things during trait resolution. Roughly speaking, the idea is that if |
| 32 | +two trait queries have the same canonical form, then they will get |
| 33 | +the same answer. That answer will be expressed in terms of the |
| 34 | +canonical variables (`?0`, `?1`), which we can then map back to the |
| 35 | +original variables (`?T`, `?U`). |
| 36 | + |
| 37 | +## Canonicalizing the query |
| 38 | + |
| 39 | +To see how it works, imagine that we are asking to solve the following |
| 40 | +trait query: `?A: Foo<'static, ?B>`, where `?A` and `?B` are unbound. |
| 41 | +This query contains two unbound variables, but it also contains the |
| 42 | +lifetime `'static`. The trait system generally ignores all lifetimes |
| 43 | +and treats them equally, so when canonicalizing, we will *also* |
| 44 | +replace any [free lifetime](../appendix/background.html#free-vs-bound) with a |
| 45 | +canonical variable (Note that `'static` is actually a _free_ lifetime |
| 46 | +variable here. We are not considering it in the typing context of the whole |
| 47 | +program but only in the context of this trait reference. Mathematically, we |
| 48 | +are not quantifying over the whole program, but only this obligation). |
| 49 | +Therefore, we get the following result: |
| 50 | + |
| 51 | +```text |
| 52 | +?0: Foo<'?1, ?2> |
| 53 | +``` |
| 54 | + |
| 55 | +Sometimes we write this differently, like so: |
| 56 | + |
| 57 | +```text |
| 58 | +for<T,L,T> { ?0: Foo<'?1, ?2> } |
| 59 | +``` |
| 60 | + |
| 61 | +This `for<>` gives some information about each of the canonical |
| 62 | +variables within. In this case, each `T` indicates a type variable, |
| 63 | +so `?0` and `?2` are types; the `L` indicates a lifetime variable, so |
| 64 | +`?1` is a lifetime. The `canonicalize` method *also* gives back a |
| 65 | +`CanonicalVarValues` array OV with the "original values" for each |
| 66 | +canonicalized variable: |
| 67 | + |
| 68 | +```text |
| 69 | +[?A, 'static, ?B] |
| 70 | +``` |
| 71 | + |
| 72 | +We'll need this vector OV later, when we process the query response. |
| 73 | + |
| 74 | +## Executing the query |
| 75 | + |
| 76 | +Once we've constructed the canonical query, we can try to solve it. |
| 77 | +To do so, we will wind up creating a fresh inference context and |
| 78 | +**instantiating** the canonical query in that context. The idea is that |
| 79 | +we create a substitution S from the canonical form containing a fresh |
| 80 | +inference variable (of suitable kind) for each canonical variable. |
| 81 | +So, for our example query: |
| 82 | + |
| 83 | +```text |
| 84 | +for<T,L,T> { ?0: Foo<'?1, ?2> } |
| 85 | +``` |
| 86 | + |
| 87 | +the substitution S might be: |
| 88 | + |
| 89 | +```text |
| 90 | +S = [?A, '?B, ?C] |
| 91 | +``` |
| 92 | + |
| 93 | +We can then replace the bound canonical variables (`?0`, etc) with |
| 94 | +these inference variables, yielding the following fully instantiated |
| 95 | +query: |
| 96 | + |
| 97 | +```text |
| 98 | +?A: Foo<'?B, ?C> |
| 99 | +``` |
| 100 | + |
| 101 | +Remember that substitution S though! We're going to need it later. |
| 102 | + |
| 103 | +OK, now that we have a fresh inference context and an instantiated |
| 104 | +query, we can go ahead and try to solve it. The trait solver itself is |
| 105 | +explained in more detail in [another section](./slg.html), but |
| 106 | +suffice to say that it will compute a [certainty value][cqqr] (`Proven` or |
| 107 | +`Ambiguous`) and have side-effects on the inference variables we've |
| 108 | +created. For example, if there were only one impl of `Foo`, like so: |
| 109 | + |
| 110 | +[cqqr]: ./canonical-queries.html#query-response |
| 111 | + |
| 112 | +```rust,ignore |
| 113 | +impl<'a, X> Foo<'a, X> for Vec<X> |
| 114 | +where X: 'a |
| 115 | +{ ... } |
| 116 | +``` |
| 117 | + |
| 118 | +then we might wind up with a certainty value of `Proven`, as well as |
| 119 | +creating fresh inference variables `'?D` and `?E` (to represent the |
| 120 | +parameters on the impl) and unifying as follows: |
| 121 | + |
| 122 | +- `'?B = '?D` |
| 123 | +- `?A = Vec<?E>` |
| 124 | +- `?C = ?E` |
| 125 | + |
| 126 | +We would also accumulate the region constraint `?E: '?D`, due to the |
| 127 | +where clause. |
| 128 | + |
| 129 | +In order to create our final query result, we have to "lift" these |
| 130 | +values out of the query's inference context and into something that |
| 131 | +can be reapplied in our original inference context. We do that by |
| 132 | +**re-applying canonicalization**, but to the **query result**. |
| 133 | + |
| 134 | +## Canonicalizing the query result |
| 135 | + |
| 136 | +As discussed in [the parent section][cqqr], most trait queries wind up |
| 137 | +with a result that brings together a "certainty value" `certainty`, a |
| 138 | +result substitution `var_values`, and some region constraints. To |
| 139 | +create this, we wind up re-using the substitution S that we created |
| 140 | +when first instantiating our query. To refresh your memory, we had a query |
| 141 | + |
| 142 | +```text |
| 143 | +for<T,L,T> { ?0: Foo<'?1, ?2> } |
| 144 | +``` |
| 145 | + |
| 146 | +for which we made a substutition S: |
| 147 | + |
| 148 | +```text |
| 149 | +S = [?A, '?B, ?C] |
| 150 | +``` |
| 151 | + |
| 152 | +We then did some work which unified some of those variables with other things. |
| 153 | +If we "refresh" S with the latest results, we get: |
| 154 | + |
| 155 | +```text |
| 156 | +S = [Vec<?E>, '?D, ?E] |
| 157 | +``` |
| 158 | + |
| 159 | +These are precisely the new values for the three input variables from |
| 160 | +our original query. Note though that they include some new variables |
| 161 | +(like `?E`). We can make those go away by canonicalizing again! We don't |
| 162 | +just canonicalize S, though, we canonicalize the whole query response QR: |
| 163 | + |
| 164 | +```text |
| 165 | +QR = { |
| 166 | + certainty: Proven, // or whatever |
| 167 | + var_values: [Vec<?E>, '?D, ?E] // this is S |
| 168 | + region_constraints: [?E: '?D], // from the impl |
| 169 | + value: (), // for our purposes, just (), but |
| 170 | + // in some cases this might have |
| 171 | + // a type or other info |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +The result would be as follows: |
| 176 | + |
| 177 | +```text |
| 178 | +Canonical(QR) = for<T, L> { |
| 179 | + certainty: Proven, |
| 180 | + var_values: [Vec<?0>, '?1, ?0] |
| 181 | + region_constraints: [?0: '?1], |
| 182 | + value: (), |
| 183 | +} |
| 184 | +``` |
| 185 | + |
| 186 | +(One subtle point: when we canonicalize the query **result**, we do not |
| 187 | +use any special treatment for free lifetimes. Note that both |
| 188 | +references to `'?D`, for example, were converted into the same |
| 189 | +canonical variable (`?1`). This is in contrast to the original query, |
| 190 | +where we canonicalized every free lifetime into a fresh canonical |
| 191 | +variable.) |
| 192 | + |
| 193 | +Now, this result must be reapplied in each context where needed. |
| 194 | + |
| 195 | +## Processing the canonicalized query result |
| 196 | + |
| 197 | +In the previous section we produced a canonical query result. We now have |
| 198 | +to apply that result in our original context. If you recall, way back in the |
| 199 | +beginning, we were trying to prove this query: |
| 200 | + |
| 201 | +```text |
| 202 | +?A: Foo<'static, ?B> |
| 203 | +``` |
| 204 | + |
| 205 | +We canonicalized that into this: |
| 206 | + |
| 207 | +```text |
| 208 | +for<T,L,T> { ?0: Foo<'?1, ?2> } |
| 209 | +``` |
| 210 | + |
| 211 | +and now we got back a canonical response: |
| 212 | + |
| 213 | +```text |
| 214 | +for<T, L> { |
| 215 | + certainty: Proven, |
| 216 | + var_values: [Vec<?0>, '?1, ?0] |
| 217 | + region_constraints: [?0: '?1], |
| 218 | + value: (), |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +We now want to apply that response to our context. Conceptually, how |
| 223 | +we do that is to (a) instantiate each of the canonical variables in |
| 224 | +the result with a fresh inference variable, (b) unify the values in |
| 225 | +the result with the original values, and then (c) record the region |
| 226 | +constraints for later. Doing step (a) would yield a result of |
| 227 | + |
| 228 | +```text |
| 229 | +{ |
| 230 | + certainty: Proven, |
| 231 | + var_values: [Vec<?C>, '?D, ?C] |
| 232 | + ^^ ^^^ fresh inference variables |
| 233 | + region_constraints: [?C: '?D], |
| 234 | + value: (), |
| 235 | +} |
| 236 | +``` |
| 237 | + |
| 238 | +Step (b) would then unify: |
| 239 | + |
| 240 | +```text |
| 241 | +?A with Vec<?C> |
| 242 | +'static with '?D |
| 243 | +?B with ?C |
| 244 | +``` |
| 245 | + |
| 246 | +And finally the region constraint of `?C: 'static` would be recorded |
| 247 | +for later verification. |
| 248 | + |
| 249 | +(What we *actually* do is a mildly optimized variant of that: Rather |
| 250 | +than eagerly instantiating all of the canonical values in the result |
| 251 | +with variables, we instead walk the vector of values, looking for |
| 252 | +cases where the value is just a canonical variable. In our example, |
| 253 | +`values[2]` is `?C`, so that means we can deduce that `?C := ?B` and |
| 254 | +`'?D := 'static`. This gives us a partial set of values. Anything for |
| 255 | +which we do not find a value, we create an inference variable.) |
| 256 | + |
0 commit comments