@@ -72,10 +72,10 @@ Trait resolution consists of three major parts:
72
72
are completely fulfilled. Basically it is a worklist of obligations
73
73
to be selected: once selection is successful, the obligation is
74
74
removed from the worklist and any nested obligations are enqueued.
75
+ Fulfillment constrains inference variables.
75
76
76
- - ** Coherence** : The coherence checks are intended to ensure that there
77
- are never overlapping impls, where two impls could be used with
78
- equal precedence.
77
+ - ** Evaluation** : Checks whether obligations holds without constraining
78
+ any inference variables. Used by selection.
79
79
80
80
## Selection
81
81
@@ -111,6 +111,8 @@ may lead to other errors downstream).
111
111
112
112
### Candidate assembly
113
113
114
+ ** TODO** : Talk about _ why_ we have different candidates, and why it needs to happen in a probe.
115
+
114
116
Searches for impls/where-clauses/etc that might
115
117
possibly be used to satisfy the obligation. Each of those is called
116
118
a candidate. To avoid ambiguity, we want to find exactly one
@@ -120,68 +122,23 @@ the obligation contains unbound inference variables.
120
122
121
123
The subroutines that decide whether a particular impl/where-clause/etc applies
122
124
to a particular obligation are collectively referred to as the process of
123
- _ matching_ . As of <!-- date-check --> May 2022, this amounts to unifying
124
- the ` Self ` types, but in the future we may also recursively consider some of the
125
- nested obligations, in the case of an impl.
126
-
127
- ** TODO** : what does "unifying the ` Self ` types" mean? The ` Self ` of the
128
- obligation with that of an impl?
129
-
130
- The basic idea for candidate assembly is to do a first pass in which
131
- we identify all possible candidates. During this pass, all that we do
132
- is try and unify the type parameters. (In particular, we ignore any
133
- nested where clauses.) Presuming that this unification succeeds, the
134
- impl is added as a candidate.
125
+ _ matching_ . For ` impl ` candidates <!-- date-check: Oct 2022 --> ,
126
+ this amounts to unifying the impl header (the ` Self ` type and the trait arguments)
127
+ while ignoring nested obligations. If matching succeeds then we add it
128
+ to a set of candidates. There are other rules when assembling candidates for
129
+ built-in traits such as ` Copy ` , ` Sized ` , and ` CoerceUnsized ` .
135
130
136
131
Once this first pass is done, we can examine the set of candidates. If
137
132
it is a singleton set, then we are done: this is the only impl in
138
- scope that could possibly apply. Otherwise, we can winnow down the set
139
- of candidates by using where clauses and other conditions. If this
140
- reduced set yields a single, unambiguous entry, we're good to go,
141
- otherwise the result is considered ambiguous.
142
-
143
- #### The basic process: Inferring based on the impls we see
144
-
145
- This process is easier if we work through some examples. Consider
146
- the following trait:
147
-
148
- ``` rust,ignore
149
- trait Convert<Target> {
150
- fn convert(&self) -> Target;
151
- }
152
- ```
133
+ scope that could possibly apply. Otherwise, we can ** winnow** down the set
134
+ of candidates by using where clauses and other conditions. Winnowing uses
135
+ ` evaluate_candidate ` to check whether the nested obligations may apply.
136
+ If this still leaves more than 1 candidate, we use ` fn candidate_should_be_dropped_in_favor_of `
137
+ to prefer some candidates over others.
153
138
154
- This trait just has one method. It's about as simple as it gets. It
155
- converts from the (implicit) ` Self ` type to the ` Target ` type. If we
156
- wanted to permit conversion between ` isize ` and ` usize ` , we might
157
- implement ` Convert ` like so:
158
139
159
- ``` rust,ignore
160
- impl Convert<usize> for isize { ... } // isize -> usize
161
- impl Convert<isize> for usize { ... } // usize -> isize
162
- ```
163
-
164
- Now imagine there is some code like the following:
165
-
166
- ``` rust,ignore
167
- let x: isize = ...;
168
- let y = x.convert();
169
- ```
170
-
171
- The call to convert will generate a trait reference `Convert<$Y> for
172
- isize` , where ` $Y` is the type variable representing the type of
173
- ` y ` . Of the two impls we can see, the only one that matches is
174
- ` Convert<usize> for isize ` . Therefore, we can
175
- select this impl, which will cause the type of ` $Y ` to be unified to
176
- ` usize ` . (Note that while assembling candidates, we do the initial
177
- unifications in a transaction, so that they don't affect one another.)
178
-
179
- ** TODO** : The example says we can "select" the impl, but this section is
180
- talking specifically about candidate assembly. Does this mean we can sometimes
181
- skip confirmation? Or is this poor wording?
182
- ** TODO** : Is the unification of ` $Y ` part of trait resolution or type
183
- inference? Or is this not the same type of "inference variable" as in type
184
- inference?
140
+ If this reduced set yields a single, unambiguous entry, we're good to go,
141
+ otherwise the result is considered ambiguous.
185
142
186
143
#### Winnowing: Resolving ambiguities
187
144
@@ -281,38 +238,18 @@ result of selection would be an error.
281
238
Note that the candidate impl is chosen based on the ` Self ` type, but
282
239
confirmation is done based on (in this case) the ` Target ` type parameter.
283
240
284
- ### Selection during translation
241
+ ### Selection during codegen
285
242
286
243
As mentioned above, during type checking, we do not store the results of trait
287
- selection. At trans time, we repeat the trait selection to choose a particular
288
- impl for each method call. In this second selection, we do not consider any
289
- where-clauses to be in scope because we know that each resolution will resolve
290
- to a particular impl.
291
-
292
- One interesting twist has to do with nested obligations. In general, in trans,
293
- we only need to do a "shallow" selection for an obligation. That is, we wish to
294
- identify which impl applies, but we do not (yet) need to decide how to select
295
- any nested obligations. Nonetheless, we * do* currently do a complete resolution,
296
- and that is because it can sometimes inform the results of type inference.
244
+ selection. At codegen time, we repeat the trait selection to choose a particular
245
+ impl for each method call. This is done using ` fn codegen_select_candidate ` .
246
+ In this second selection, we do not consider any where-clauses to be in scope
247
+ because we know that each resolution will resolve to a particular impl.
248
+
249
+ One interesting twist has to do with nested obligations. In general, in codegen,
250
+ we only to figure out which candidate applies, we do not care about nested obligations,
251
+ as these are already assumed to be true. Nonetheless, we * do* currently do fulfill all of them.
252
+ That is because it can sometimes inform the results of type inference.
297
253
That is, we do not have the full substitutions in terms of the type variables
298
254
of the impl available to us, so we must run trait selection to figure
299
255
everything out.
300
-
301
- ** TODO** : is this still talking about trans?
302
-
303
- Here is an example:
304
-
305
- ``` rust,ignore
306
- trait Foo { ... }
307
- impl<U, T:Bar<U>> Foo for Vec<T> { ... }
308
-
309
- impl Bar<usize> for isize { ... }
310
- ```
311
-
312
- After one shallow round of selection for an obligation like `Vec<isize >
313
- : Foo`, we would know which impl we want, and we would know that
314
- ` T=isize ` , but we do not know the type of ` U ` . We must select the
315
- nested obligation ` isize : Bar<U> ` to find out that ` U=usize ` .
316
-
317
- It would be good to only do * just as much* nested resolution as
318
- necessary. Currently, though, we just do a full resolution.
0 commit comments