1
1
# Trait resolution
2
2
3
- This document describes the general process and points out some non-obvious
4
- things.
5
-
6
- ** WARNING:** This material was moved verbatim from a rustc README, so
7
- it may not "fit" the style of the guide until it is adapted.
3
+ This chapter describes the general process of _ trait resolution_ and points out
4
+ some non-obvious things.
8
5
9
6
## Major concepts
10
7
@@ -21,12 +18,12 @@ and then a call to that function:
21
18
let v : Vec <isize > = clone_slice (& [1 , 2 , 3 ])
22
19
```
23
20
24
- it is the job of trait resolution to figure out (in which case)
25
- whether there exists an impl of ` isize : Clone `
21
+ it is the job of trait resolution to figure out whether there exists an impl of
22
+ (in this case) ` isize : Clone ` .
26
23
27
24
Note that in some cases, like generic functions, we may not be able to
28
25
find a specific impl, but we can figure out that the caller must
29
- provide an impl. To see what I mean , consider the body of ` clone_slice ` :
26
+ provide an impl. For example , consider the body of ` clone_slice ` :
30
27
31
28
``` rust
32
29
fn clone_slice <T : Clone >(x : & [T ]) -> Vec <T > {
@@ -43,26 +40,33 @@ is, we can't find the specific impl; but based on the bound `T:Clone`,
43
40
we can say that there exists an impl which the caller must provide.
44
41
45
42
We use the term * obligation* to refer to a trait reference in need of
46
- an impl.
43
+ an impl. Basically, the trait resolution system resolves an obligation
44
+ by proving that an appropriate impl does exist.
45
+
46
+ During type checking, we do not store the results of trait selection.
47
+ We simply wish to verify that trait selection will succeed. Then
48
+ later, at trans time, when we have all concrete types available, we
49
+ can repeat the trait selection to choose an actual implementation, which
50
+ will then be generated in the output binary.
47
51
48
52
## Overview
49
53
50
54
Trait resolution consists of three major parts:
51
55
52
- - SELECTION: Deciding how to resolve a specific obligation. For
56
+ - ** Selection ** is deciding how to resolve a specific obligation. For
53
57
example, selection might decide that a specific obligation can be
54
- resolved by employing an impl which matches the self type, or by
55
- using a parameter bound. In the case of an impl, Selecting one
58
+ resolved by employing an impl which matches the ` Self ` type, or by
59
+ using a parameter bound (e.g. ` T: Trait ` ). In the case of an impl, selecting one
56
60
obligation can create * nested obligations* because of where clauses
57
61
on the impl itself. It may also require evaluating those nested
58
62
obligations to resolve ambiguities.
59
63
60
- - FULFILLMENT: The fulfillment code is what tracks that obligations
61
- are completely fulfilled. Basically it is a worklist of obligations
64
+ - ** Fulfillment ** is keeping track of which obligations
65
+ are completely fulfilled. Basically, it is a worklist of obligations
62
66
to be selected: once selection is successful, the obligation is
63
67
removed from the worklist and any nested obligations are enqueued.
64
68
65
- - COHERENCE: The coherence checks are intended to ensure that there
69
+ - ** Coherence ** checks are intended to ensure that there
66
70
are never overlapping impls, where two impls could be used with
67
71
equal precedence.
68
72
@@ -83,12 +87,21 @@ and returns a `SelectionResult`. There are three possible outcomes:
83
87
contains unbound type variables.
84
88
85
89
- ` Err(err) ` – the obligation definitely cannot be resolved due to a
86
- type error, or because there are no impls that could possibly apply,
87
- etc.
90
+ type error or because there are no impls that could possibly apply.
88
91
89
92
The basic algorithm for selection is broken into two big phases:
90
93
candidate assembly and confirmation.
91
94
95
+ Note that because of how lifetime inference works, it is not possible to
96
+ give back immediate feedback as to whether a unification or subtype
97
+ relationship between lifetimes holds or not. Therefore, lifetime
98
+ matching is * not* considered during selection. This is reflected in
99
+ the fact that subregion assignment is infallible. This may yield
100
+ lifetime constraints that will later be found to be in error (in
101
+ contrast, the non-lifetime-constraints have already been checked
102
+ during selection and can never cause an error, though naturally they
103
+ may lead to other errors downstream).
104
+
92
105
### Candidate assembly
93
106
94
107
Searches for impls/where-clauses/etc that might
@@ -98,6 +111,15 @@ candidate that is definitively applicable. In some cases, we may not
98
111
know whether an impl/where-clause applies or not – this occurs when
99
112
the obligation contains unbound inference variables.
100
113
114
+ The subroutines that decide whether a particular impl/where-clause/etc
115
+ applies to a particular obligation are collectively refered to as the
116
+ process of _ matching_ . At the moment, this amounts to
117
+ unifying the ` Self ` types, but in the future we may also recursively
118
+ consider some of the nested obligations, in the case of an impl.
119
+
120
+ ** TODO** : what does "unifying the ` Self ` types" mean? The ` Self ` of the
121
+ obligation with that of an impl?
122
+
101
123
The basic idea for candidate assembly is to do a first pass in which
102
124
we identify all possible candidates. During this pass, all that we do
103
125
is try and unify the type parameters. (In particular, we ignore any
@@ -141,16 +163,14 @@ let y = x.convert();
141
163
142
164
The call to convert will generate a trait reference `Convert<$Y> for
143
165
isize` , where ` $Y` is the type variable representing the type of
144
- ` y ` . When we match this against the two impls we can see, we will find
145
- that only one remains: ` Convert<usize> for isize ` . Therefore, we can
166
+ ` y ` . Of the two impls we can see, the only one that matches is
167
+ ` Convert<usize> for isize ` . Therefore, we can
146
168
select this impl, which will cause the type of ` $Y ` to be unified to
147
169
` usize ` . (Note that while assembling candidates, we do the initial
148
170
unifications in a transaction, so that they don't affect one another.)
149
171
150
- There are tests to this effect in src/test/run-pass:
151
-
152
- traits-multidispatch-infer-convert-source-and-target.rs
153
- traits-multidispatch-infer-convert-target.rs
172
+ ** TODO** : The example says we can "select" the impl, but this section is talking specifically about candidate assembly. Does this mean we can sometimes skip confirmation? Or is this poor wording?
173
+ ** TODO** : Is the unification of ` $Y ` part of trait resolution or type inference? Or is this not the same type of "inference variable" as in type inference?
154
174
155
175
#### Winnowing: Resolving ambiguities
156
176
@@ -167,94 +187,103 @@ impl<T:Copy> Get for T {
167
187
}
168
188
169
189
impl <T : Get > Get for Box <T > {
170
- fn get (& self ) -> Box <T > { box get_it (& * * self ) }
190
+ fn get (& self ) -> Box <T > { Box :: new ( get_it (& * * self ) ) }
171
191
}
172
192
```
173
193
174
- What happens when we invoke ` get_it(&box 1_u16) ` , for example? In this
194
+ What happens when we invoke ` get_it(&Box::new( 1_u16) ) ` , for example? In this
175
195
case, the ` Self ` type is ` Box<u16> ` – that unifies with both impls,
176
196
because the first applies to all types, and the second to all
177
- boxes. In the olden days we'd have called this ambiguous. But what we
178
- do now is do a second * winnowing * pass that considers where clauses
179
- and attempts to remove candidates – in this case, the first impl only
197
+ boxes. In order for this to be unambiguous, the compiler does a * winnowing *
198
+ pass that considers ` where ` clauses
199
+ and attempts to remove candidates. In this case, the first impl only
180
200
applies if ` Box<u16> : Copy ` , which doesn't hold. After winnowing,
181
- then, we are left with just one candidate, so we can proceed. There is
182
- a test of this in ` src/test/run-pass/traits-conditional-dispatch.rs ` .
183
-
184
- #### Matching
185
-
186
- The subroutines that decide whether a particular impl/where-clause/etc
187
- applies to a particular obligation. At the moment, this amounts to
188
- unifying the self types, but in the future we may also recursively
189
- consider some of the nested obligations, in the case of an impl.
201
+ then, we are left with just one candidate, so we can proceed.
190
202
191
- #### Lifetimes and selection
192
-
193
- Because of how that lifetime inference works, it is not possible to
194
- give back immediate feedback as to whether a unification or subtype
195
- relationship between lifetimes holds or not. Therefore, lifetime
196
- matching is * not* considered during selection. This is reflected in
197
- the fact that subregion assignment is infallible. This may yield
198
- lifetime constraints that will later be found to be in error (in
199
- contrast, the non-lifetime-constraints have already been checked
200
- during selection and can never cause an error, though naturally they
201
- may lead to other errors downstream).
202
-
203
- #### Where clauses
203
+ #### ` where ` clauses
204
204
205
205
Besides an impl, the other major way to resolve an obligation is via a
206
- where clause. The selection process is always given a * parameter
207
- environment* which contains a list of where clauses, which are
208
- basically obligations that can assume are satisfiable. We will iterate
206
+ where clause. The selection process is always given a [ parameter
207
+ environment] which contains a list of where clauses, which are
208
+ basically obligations that we can assume are satisfiable. We will iterate
209
209
over that list and check whether our current obligation can be found
210
- in that list, and if so it is considered satisfied. More precisely, we
210
+ in that list. If so, it is considered satisfied. More precisely, we
211
211
want to check whether there is a where-clause obligation that is for
212
- the same trait (or some subtrait) and for which the self types match,
213
- using the definition of * matching* given above.
212
+ the same trait (or some subtrait) and which can match against the obligation.
213
+
214
+ [ parameter environment ] : ./param_env.html
214
215
215
216
Consider this simple example:
216
217
217
218
``` rust
218
- trait A1 { /*...*/ }
219
+ trait A1 {
220
+ fn do_a1 (& self );
221
+ }
219
222
trait A2 : A1 { /*...*/ }
220
223
221
- trait B { /*...*/ }
224
+ trait B {
225
+ fn do_b (& self );
226
+ }
222
227
223
- fn foo <X : A2 + B > { /*...*/ }
228
+ fn foo <X : A2 + B >(x : X ) {
229
+ x . do_a1 (); // (*)
230
+ x . do_b (); // (#)
231
+ }
224
232
```
225
233
226
- Clearly we can use methods offered by ` A1 ` , ` A2 ` , or ` B ` within the
227
- body of ` foo ` . In each case, that will incur an obligation like `X :
228
- A1` or ` X : A2`. The parameter environment will contain two
229
- where-clauses, ` X : A2 ` and ` X : B ` . For each obligation, then, we
230
- search this list of where-clauses. To resolve an obligation ` X:A1 ` ,
231
- we would note that ` X:A2 ` implies that ` X:A1 ` .
234
+ In the body of ` foo ` , clearly we can use methods of ` A1 ` , ` A2 ` , or ` B `
235
+ on variable ` x ` . The line marked ` (*) ` will incur an obligation ` X: A1 ` ,
236
+ which the line marked ` (#) ` will incur an obligation ` X: B ` . Meanwhile,
237
+ the parameter environment will contain two where-clauses: ` X : A2 ` and ` X : B ` .
238
+ For each obligation, then, we search this list of where-clauses. The
239
+ obligation ` X: B ` trivially matches against the where-clause ` X: B ` .
240
+ To resolve an obligation ` X:A1 ` , we would note that ` X:A2 ` implies that ` X:A1 ` .
232
241
233
242
### Confirmation
234
243
235
- Confirmation unifies the output type parameters of the trait with the
236
- values found in the obligation, possibly yielding a type error. If we
237
- return to our example of the ` Convert ` trait from the previous
238
- section, confirmation is where an error would be reported, because the
239
- impl specified that ` T ` would be ` usize ` , but the obligation reported
240
- ` char ` . Hence the result of selection would be an error.
244
+ _ Confirmation_ unifies the output type parameters of the trait with the
245
+ values found in the obligation, possibly yielding a type error.
246
+
247
+ Suppose we have the following variation of the ` Convert ` example in the
248
+ previous section:
249
+
250
+ ``` rust
251
+ trait Convert <Target > {
252
+ fn convert (& self ) -> Target ;
253
+ }
254
+
255
+ impl Convert <usize > for isize { /*...*/ } // isize -> usize
256
+ impl Convert <isize > for usize { /*...*/ } // usize -> isize
257
+
258
+ let x : isize = ... ;
259
+ let y : char = x . convert (); // NOTE: `y: char` now!
260
+ ```
261
+
262
+ Confirmation is where an error would be reported because the impl specified
263
+ that ` Target ` would be ` usize ` , but the obligation reported ` char ` . Hence the
264
+ result of selection would be an error.
265
+
266
+ Note that the candidate impl is chosen base on the ` Self ` type, but
267
+ confirmation is done based on (in this case) the ` Target ` type parameter.
241
268
242
269
### Selection during translation
243
270
244
- During type checking, we do not store the results of trait selection.
245
- We simply wish to verify that trait selection will succeed. Then
246
- later, at trans time, when we have all concrete types available, we
247
- can repeat the trait selection. In this case, we do not consider any
248
- where-clauses to be in scope. We know that therefore each resolution
249
- will resolve to a particular impl.
271
+ As mentioned above, during type checking, we do not store the results of trait
272
+ selection. At trans time, repeat the trait selection to choose a particular
273
+ impl for each method call. In this second selection, we do not consider any
274
+ where-clauses to be in scope because we know that each resolution will resolve
275
+ to a particular impl.
250
276
251
277
One interesting twist has to do with nested obligations. In general, in trans,
252
278
we only need to do a "shallow" selection for an obligation. That is, we wish to
253
279
identify which impl applies, but we do not (yet) need to decide how to select
254
- any nested obligations. Nonetheless, we * do* currently do a complete resolution,
255
- and that is because it can sometimes inform the results of type inference. That is,
256
- we do not have the full substitutions in terms of the type variables of the impl available
257
- to us, so we must run trait selection to figure everything out.
280
+ any nested obligations. Nonetheless, we * do* currently do a complete
281
+ resolution, and that is because it can sometimes inform the results of type
282
+ inference. That is, we do not have the full substitutions for the type
283
+ variables of the impl available to us, so we must run trait selection to figure
284
+ everything out.
285
+
286
+ ** TODO** : is this still talking about trans?
258
287
259
288
Here is an example:
260
289
@@ -272,214 +301,3 @@ nested obligation `isize : Bar<U>` to find out that `U=usize`.
272
301
273
302
It would be good to only do * just as much* nested resolution as
274
303
necessary. Currently, though, we just do a full resolution.
275
-
276
- # Higher-ranked trait bounds
277
-
278
- One of the more subtle concepts at work are * higher-ranked trait
279
- bounds* . An example of such a bound is ` for<'a> MyTrait<&'a isize> ` .
280
- Let's walk through how selection on higher-ranked trait references
281
- works.
282
-
283
- ## Basic matching and skolemization leaks
284
-
285
- Let's walk through the test ` compile-fail/hrtb-just-for-static.rs ` to see
286
- how it works. The test starts with the trait ` Foo ` :
287
-
288
- ``` rust
289
- trait Foo <X > {
290
- fn foo (& self , x : X ) { }
291
- }
292
- ```
293
-
294
- Let's say we have a function ` want_hrtb ` that wants a type which
295
- implements ` Foo<&'a isize> ` for any ` 'a ` :
296
-
297
- ``` rust
298
- fn want_hrtb <T >() where T : for <'a > Foo <& 'a isize > { ... }
299
- ```
300
-
301
- Now we have a struct ` AnyInt ` that implements ` Foo<&'a isize> ` for any
302
- ` 'a ` :
303
-
304
- ``` rust
305
- struct AnyInt ;
306
- impl <'a > Foo <& 'a isize > for AnyInt { }
307
- ```
308
-
309
- And the question is, does ` AnyInt : for<'a> Foo<&'a isize> ` ? We want the
310
- answer to be yes. The algorithm for figuring it out is closely related
311
- to the subtyping for higher-ranked types (which is described in
312
- ` middle::infer::higher_ranked::doc ` , but also in a [ paper by SPJ] that
313
- I recommend you read).
314
-
315
- 1 . Skolemize the obligation.
316
- 2 . Match the impl against the skolemized obligation.
317
- 3 . Check for skolemization leaks.
318
-
319
- [ paper by SPJ ] : http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
320
-
321
- So let's work through our example. The first thing we would do is to
322
- skolemize the obligation, yielding ` AnyInt : Foo<&'0 isize> ` (here ` '0 `
323
- represents skolemized region #0 ). Note that now have no quantifiers;
324
- in terms of the compiler type, this changes from a ` ty::PolyTraitRef `
325
- to a ` TraitRef ` . We would then create the ` TraitRef ` from the impl,
326
- using fresh variables for it's bound regions (and thus getting
327
- ` Foo<&'$a isize> ` , where ` '$a ` is the inference variable for ` 'a ` ). Next
328
- we relate the two trait refs, yielding a graph with the constraint
329
- that ` '0 == '$a ` . Finally, we check for skolemization "leaks" – a
330
- leak is basically any attempt to relate a skolemized region to another
331
- skolemized region, or to any region that pre-existed the impl match.
332
- The leak check is done by searching from the skolemized region to find
333
- the set of regions that it is related to in any way. This is called
334
- the "taint" set. To pass the check, that set must consist * solely* of
335
- itself and region variables from the impl. If the taint set includes
336
- any other region, then the match is a failure. In this case, the taint
337
- set for ` '0 ` is ` {'0, '$a} ` , and hence the check will succeed.
338
-
339
- Let's consider a failure case. Imagine we also have a struct
340
-
341
- ``` rust
342
- struct StaticInt ;
343
- impl Foo <& 'static isize > for StaticInt ;
344
- ```
345
-
346
- We want the obligation ` StaticInt : for<'a> Foo<&'a isize> ` to be
347
- considered unsatisfied. The check begins just as before. ` 'a ` is
348
- skolemized to ` '0 ` and the impl trait reference is instantiated to
349
- ` Foo<&'static isize> ` . When we relate those two, we get a constraint
350
- like ` 'static == '0 ` . This means that the taint set for ` '0 ` is `{'0,
351
- 'static}`, which fails the leak check.
352
-
353
- ## Higher-ranked trait obligations
354
-
355
- Once the basic matching is done, we get to another interesting topic:
356
- how to deal with impl obligations. I'll work through a simple example
357
- here. Imagine we have the traits ` Foo ` and ` Bar ` and an associated impl:
358
-
359
- ``` rust
360
- trait Foo <X > {
361
- fn foo (& self , x : X ) { }
362
- }
363
-
364
- trait Bar <X > {
365
- fn bar (& self , x : X ) { }
366
- }
367
-
368
- impl <X ,F > Foo <X > for F
369
- where F : Bar <X >
370
- {
371
- }
372
- ```
373
-
374
- Now let's say we have a obligation ` for<'a> Foo<&'a isize> ` and we match
375
- this impl. What obligation is generated as a result? We want to get
376
- ` for<'a> Bar<&'a isize> ` , but how does that happen?
377
-
378
- After the matching, we are in a position where we have a skolemized
379
- substitution like ` X => &'0 isize ` . If we apply this substitution to the
380
- impl obligations, we get ` F : Bar<&'0 isize> ` . Obviously this is not
381
- directly usable because the skolemized region ` '0 ` cannot leak out of
382
- our computation.
383
-
384
- What we do is to create an inverse mapping from the taint set of ` '0 `
385
- back to the original bound region (` 'a ` , here) that ` '0 ` resulted
386
- from. (This is done in ` higher_ranked::plug_leaks ` ). We know that the
387
- leak check passed, so this taint set consists solely of the skolemized
388
- region itself plus various intermediate region variables. We then walk
389
- the trait-reference and convert every region in that taint set back to
390
- a late-bound region, so in this case we'd wind up with `for<'a> F :
391
- Bar<&'a isize>`.
392
-
393
- # Caching and subtle considerations therewith
394
-
395
- In general we attempt to cache the results of trait selection. This
396
- is a somewhat complex process. Part of the reason for this is that we
397
- want to be able to cache results even when all the types in the trait
398
- reference are not fully known. In that case, it may happen that the
399
- trait selection process is also influencing type variables, so we have
400
- to be able to not only cache the * result* of the selection process,
401
- but * replay* its effects on the type variables.
402
-
403
- ## An example
404
-
405
- The high-level idea of how the cache works is that we first replace
406
- all unbound inference variables with skolemized versions. Therefore,
407
- if we had a trait reference ` usize : Foo<$1> ` , where ` $n ` is an unbound
408
- inference variable, we might replace it with ` usize : Foo<%0> ` , where
409
- ` %n ` is a skolemized type. We would then look this up in the cache.
410
- If we found a hit, the hit would tell us the immediate next step to
411
- take in the selection process: i.e. apply impl #22 , or apply where
412
- clause ` X : Foo<Y> ` . Let's say in this case there is no hit.
413
- Therefore, we search through impls and where clauses and so forth, and
414
- we come to the conclusion that the only possible impl is this one,
415
- with def-id 22:
416
-
417
- ``` rust
418
- impl Foo <isize > for usize { ... } // Impl #22
419
- ```
420
-
421
- We would then record in the cache `usize : Foo<%0> ==>
422
- ImplCandidate(22)` . Next we would confirm ` ImplCandidate(22)`, which
423
- would (as a side-effect) unify ` $1 ` with ` isize ` .
424
-
425
- Now, at some later time, we might come along and see a `usize :
426
- Foo<$3>` . When skolemized, this would yield ` usize : Foo<%0>`, just as
427
- before, and hence the cache lookup would succeed, yielding
428
- ` ImplCandidate(22) ` . We would confirm ` ImplCandidate(22) ` which would
429
- (as a side-effect) unify ` $3 ` with ` isize ` .
430
-
431
- ## Where clauses and the local vs global cache
432
-
433
- One subtle interaction is that the results of trait lookup will vary
434
- depending on what where clauses are in scope. Therefore, we actually
435
- have * two* caches, a local and a global cache. The local cache is
436
- attached to the ` ParamEnv ` and the global cache attached to the
437
- ` tcx ` . We use the local cache whenever the result might depend on the
438
- where clauses that are in scope. The determination of which cache to
439
- use is done by the method ` pick_candidate_cache ` in ` select.rs ` . At
440
- the moment, we use a very simple, conservative rule: if there are any
441
- where-clauses in scope, then we use the local cache. We used to try
442
- and draw finer-grained distinctions, but that led to a serious of
443
- annoying and weird bugs like #22019 and #18290 . This simple rule seems
444
- to be pretty clearly safe and also still retains a very high hit rate
445
- (~ 95% when compiling rustc).
446
-
447
- # Specialization
448
-
449
- Defined in the ` specialize ` module.
450
-
451
- The basic strategy is to build up a * specialization graph* during
452
- coherence checking. Insertion into the graph locates the right place
453
- to put an impl in the specialization hierarchy; if there is no right
454
- place (due to partial overlap but no containment), you get an overlap
455
- error. Specialization is consulted when selecting an impl (of course),
456
- and the graph is consulted when propagating defaults down the
457
- specialization hierarchy.
458
-
459
- You might expect that the specialization graph would be used during
460
- selection – i.e. when actually performing specialization. This is
461
- not done for two reasons:
462
-
463
- - It's merely an optimization: given a set of candidates that apply,
464
- we can determine the most specialized one by comparing them directly
465
- for specialization, rather than consulting the graph. Given that we
466
- also cache the results of selection, the benefit of this
467
- optimization is questionable.
468
-
469
- - To build the specialization graph in the first place, we need to use
470
- selection (because we need to determine whether one impl specializes
471
- another). Dealing with this reentrancy would require some additional
472
- mode switch for selection. Given that there seems to be no strong
473
- reason to use the graph anyway, we stick with a simpler approach in
474
- selection, and use the graph only for propagating default
475
- implementations.
476
-
477
- Trait impl selection can succeed even when multiple impls can apply,
478
- as long as they are part of the same specialization family. In that
479
- case, it returns a * single* impl on success – this is the most
480
- specialized impl * known* to apply. However, if there are any inference
481
- variables in play, the returned impl may not be the actual impl we
482
- will use at trans time. Thus, we take special care to avoid projecting
483
- associated types unless either (1) the associated type does not use
484
- ` default ` and thus cannot be overridden or (2) all input types are
485
- known concretely.
0 commit comments