2
2
3
3
Canonicalization is the process of ** isolating** an inference value
4
4
from its context. It is a key part of implementing
5
- [ canonical queries] [ cq ] .
5
+ [ canonical queries] [ cq ] , and you may wish to read the parent chapter
6
+ to get more context.
6
7
7
8
Canonicalization is really based on a very simple concept: every
8
9
[ inference variable] ( ./type-inference.html#vars ) is always in one of
@@ -33,6 +34,8 @@ the same answer. That answer will be expressed in terms of the
33
34
canonical variables (` ?0 ` , ` ?1 ` ), which we can then map back to the
34
35
original variables (` ?T ` , ` ?U ` ).
35
36
37
+ ## Canonicalizing the query
38
+
36
39
To see how it works, imagine that we are asking to solve the following
37
40
trait query: ` ?A: Foo<'static, ?B> ` , where ` ?A ` and ` ?B ` are unbound.
38
41
This query contains two unbound variables, but it also contains the
@@ -48,54 +51,174 @@ Sometimes we write this differently, like so:
48
51
for<T,L,T> { ?0: Foo<'?1, ?2> }
49
52
50
53
This ` for<> ` gives some information about each of the canonical
51
- variables within. In this case, I am saying that ` ?0 ` is a type
52
- (` T ` ), ` ?1 ` is a lifetime (` L ` ), and ` ?2 ` is also a type (` T ` ). The
53
- ` canonicalize ` method * also* gives back a ` CanonicalVarValues ` array
54
- with the "original values" for each canonicalized variable:
54
+ variables within. In this case, each ` T ` indicates a type variable,
55
+ so ` ?0 ` and ` ?2 ` are types; the ` L ` indicates a lifetime varibale, so
56
+ ` ?1 ` is a lifetime. The ` canonicalize ` method * also* gives back a
57
+ ` CanonicalVarValues ` array OV with the "original values" for each
58
+ canonicalized variable:
55
59
56
60
[?A, 'static, ?B]
61
+
62
+ We'll need this vector OV later, when we process the query response.
63
+
64
+ ## Executing the query
65
+
66
+ Once we've constructed the canonical query, we can try to solve it.
67
+ To do so, we will wind up creating a fresh inference context and
68
+ ** instantiating** the canonical query in that context. The idea is that
69
+ we create a substitution S from the canonical form containing a fresh
70
+ inference variable (of suitable kind) for each canonical variable.
71
+ So, for our example query:
57
72
58
- Now we do the query and get back some result R. As part of that
59
- result, we'll have an array of values for the canonical inputs. For
60
- example, the canonical result might be:
73
+ for<T,L,T> { ?0: Foo<'?1, ?2> }
74
+
75
+ the substitution S might be:
76
+
77
+ S = [?A, '?B, ?C]
78
+
79
+ We can then replace the bound canonical variables (` ?0 ` , etc) with
80
+ these inference variables, yielding the following fully instantiated
81
+ query:
82
+
83
+ ?A: Foo<'?B, ?C>
84
+
85
+ Remember that substitution S though! We're going to need it later.
86
+
87
+ OK, now that we have a fresh inference context and an instantiated
88
+ query, we can go ahead and try to solve it. The trait solver itself is
89
+ explained in more detail in [ another section] ( ./traits-slg.html ) , but
90
+ suffice to say that it will compute a [ certainty value] [ cqqr ] (` Proven ` or
91
+ ` Ambiguous ` ) and have side-effects on the inference variables we've
92
+ created. For example, if there were only one impl of ` Foo ` , like so:
93
+
94
+ [ cqqr ] : ./traits-canonical-queries.html#query-response
61
95
62
96
```
63
- for<?0, ?1> {
64
- values = [ Vec<?0>, '1, ?0 ]
65
- ^^ ^^ ^^ these are variables in the result!
66
- ...
67
- }
97
+ impl<'a, X> Foo<'a, X> for Vec<X>
98
+ where X: 'a
99
+ { ... }
68
100
```
69
101
70
- Note that this result is itself canonical and may include some
71
- variables (in this case, ` ?0 ` ).
102
+ then we might wind up with a certainty value of ` Proven ` , as well as
103
+ creating fresh inference variables ` '?D ` and ` ?E ` (to represent the
104
+ parameters on the impl) and unifying as follows:
105
+
106
+ - ` '?B = '?D `
107
+ - ` ?A = Vec<?E> `
108
+ - ` ?C = ?E `
109
+
110
+ We would also accumulate the region constraint ` ?E: '?D ` , due to the
111
+ where clause.
112
+
113
+ In order to create our final query result, we have to "lift" these
114
+ values out of the query's inference context and into something that
115
+ can be reapplied in our original inference context. We do that by
116
+ ** re-applying canonicalization** , but to the ** query result** .
72
117
73
- What we want to do conceptually is to (a) instantiate each of the
74
- canonical variables in the result with a fresh inference variable
75
- and then (b) unify the values in the result with the original values.
76
- Doing step (a) would yield a result of
118
+ ## Canonicalizing the query result
119
+
120
+ As discussed in [ the parent section] [ cqqr ] , most trait queries wind up
121
+ with a result that brings together a "certainty value" ` certainty ` , a
122
+ result substitution ` var_values ` , and some region constraints. To
123
+ create this, we wind up re-using the substitution S that we created
124
+ when first instantiating our query. To refresh your memory, we had a query
125
+
126
+ for<T,L,T> { ?0: Foo<'?1, ?2> }
127
+
128
+ for which we made a substutition S:
129
+
130
+ S = [?A, '?B, ?C]
131
+
132
+ We then did some work which unified some of those variables with other things. If we
133
+ "refresh" S with the latest results, we get:
134
+
135
+ S = [Vec<?E>, '?D, ?E]
136
+
137
+ These are precisely the new values for the three input variables from
138
+ our original query. Note though that they include some new variables
139
+ (like ` ?E ` ). We can make those go away by canonicalizing again! We don't
140
+ just canonicalize S, though, we canonicalize the whole query response QR:
141
+
142
+ QR = {
143
+ certainty: Proven, // or whatever
144
+ var_values: [Vec<?E>, '?D, ?E] // this is S
145
+ region_constraints: [?E: '?D], // from the impl
146
+ value: (), // for our purposes, just (), but
147
+ // in some cases this might have
148
+ // a type or other info
149
+ }
150
+
151
+ The result would be as follows:
152
+
153
+ Canonical(QR) = for<T, L> {
154
+ certainty: Proven,
155
+ var_values: [Vec<?0>, '?1, ?2]
156
+ region_constraints: [?2: '?1],
157
+ value: (),
158
+ }
159
+
160
+ (One subtle point: when we canonicalize the query ** result** , we do not
161
+ use any special treatment for free lifetimes. Note that both
162
+ references to ` '?D ` , for example, were converted into the same
163
+ canonical variable (` ?1 ` ). This is in contrast to the original query,
164
+ where we canonicalized every free lifetime into a fresh canonical
165
+ variable.)
166
+
167
+ Now, this result must be reapplied in each context where needed.
168
+
169
+ ## Processing the canonicalized query result
170
+
171
+ In the previous section we produced a canonical query result. We now have
172
+ to apply that result in our original context. If you recall, way back in the
173
+ beginning, we were trying to prove this query:
174
+
175
+ ?A: Foo<'static, ?B>
176
+
177
+ We canonicalized that into this:
178
+
179
+ for<T,L,T> { ?0: Foo<'?1, ?2> }
180
+
181
+ and now we got back a canonical response:
182
+
183
+ for<T, L> {
184
+ certainty: Proven,
185
+ var_values: [Vec<?0>, '?1, ?2]
186
+ region_constraints: [?2: '?1],
187
+ value: (),
188
+ }
189
+
190
+ We now want to apply that response to our context. Conceptually, how
191
+ we do that is to (a) instantiate each of the canonical variables in
192
+ the result with a fresh inference variable, (b) unify the values in
193
+ the result with the original values, and then (c) record the region
194
+ constraints for later. Doing step (a) would yield a result of
77
195
78
196
```
79
197
{
80
- values = [ Vec<?C>, '?X, ?C ]
81
- ^^ ^^^ fresh inference variables in `self`
82
- ..
83
- }
198
+ certainty: Proven,
199
+ var_values: [Vec<?C>, '?D, ?C]
200
+ ^^ ^^^ fresh inference variables
201
+ region_constraints: [?C: '?D],
202
+ value: (),
203
+ }
84
204
```
85
205
86
206
Step (b) would then unify:
87
207
88
208
```
89
209
?A with Vec<?C>
90
- 'static with '?X
210
+ 'static with '?D
91
211
?B with ?C
92
212
```
93
213
94
- (What we actually do is a mildly optimized variant of that: Rather
214
+ And finally the region constraint of ` ?C: 'static ` would be recorded
215
+ for later verification.
216
+
217
+ (What we * actually* do is a mildly optimized variant of that: Rather
95
218
than eagerly instantiating all of the canonical values in the result
96
219
with variables, we instead walk the vector of values, looking for
97
220
cases where the value is just a canonical variable. In our example,
98
221
` values[2] ` is ` ?C ` , so that we means we can deduce that `?C := ?B and
99
- ` '?X := 'static ` . This gives us a partial set of values. Anything for
222
+ ` '?D := 'static ` . This gives us a partial set of values. Anything for
100
223
which we do not find a value, we create an inference variable.)
101
224
0 commit comments