You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/solve/candidate-preference.md
+40-18Lines changed: 40 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,16 +1,23 @@
1
1
# Candidate preference
2
2
3
-
There are multiple ways to prove `Trait` and `NormalizesTo` goals. Each such option is called a [`Candidate`]. If there are multiple applicable candidates, we prefer some candidates over others. We store the relevant information in their [`CandidateSource`].
3
+
There are multiple ways to prove `Trait` and `NormalizesTo` goals.
4
+
Each such option is called a [`Candidate`].
5
+
If there are multiple applicable candidates, we prefer some candidates over others.
6
+
We store the relevant information in their [`CandidateSource`].
4
7
5
-
This preference may result in incorrect inference or region constraints and would therefore be unsound during coherence. Because of this, we simply try to merge all candidates in coherence.
8
+
This preference may result in incorrect inference or region constraints and would therefore be unsound during coherence.
9
+
Because of this, we simply try to merge all candidates in coherence.
6
10
7
11
## `Trait` goals
8
12
9
-
Trait goals merge their applicable candidates in [`fn merge_trait_candidates`]. This document provides additional details and references to explain *why* we've got the current preference rules.
13
+
Trait goals merge their applicable candidates in [`fn merge_trait_candidates`].
14
+
This document provides additional details and references to explain *why* we've got the current preference rules.
Trivial builtin impls are builtin impls which are known to be always applicable for well-formed types. This means that if one exists, using another candidate should never have fewer constraints. We currently only consider `Sized` - and `MetaSized` - impls to be trivial.
18
+
Trivial builtin impls are builtin impls which are known to be always applicable for well-formed types.
19
+
This means that if one exists, using another candidate should never have fewer constraints.
20
+
We currently only consider `Sized` - and `MetaSized` - impls to be trivial.
14
21
15
22
This is necessary to prevent a lifetime error for the following pattern
16
23
@@ -50,7 +57,8 @@ where
50
57
### `CandidateSource::ParamEnv`
51
58
52
59
Once there's at least one *non-global*`ParamEnv` candidate, we prefer *all*`ParamEnv` candidates over other candidate kinds.
53
-
A where-bound is global if it is not higher-ranked and doesn't contain any generic parameters. It may contain `'static`.
60
+
A where-bound is global if it is not higher-ranked and doesn't contain any generic parameters.
61
+
It may contain `'static`.
54
62
55
63
We try to apply where-bounds over other candidates as users tends to have the most control over them, so they can most easily
56
64
adjust them in case our candidate preference is incorrect.
@@ -68,7 +76,8 @@ fn foo<'a, T: Trait<'a>>() {
68
76
}
69
77
```
70
78
71
-
We also need this as shadowed impls can result in currently ambiguous solver cycles: [trait-system-refactor-initiative#76]. Without preference we'd be forced to fail with ambiguity
79
+
We also need this as shadowed impls can result in currently ambiguous solver cycles: [trait-system-refactor-initiative#76].
80
+
Without preference we'd be forced to fail with ambiguity
72
81
errors if the where-bound results in region constraints to avoid incompleteness.
73
82
```rust
74
83
traitSuper {
@@ -94,7 +103,9 @@ fn overflow<T: Trait>() {
94
103
}
95
104
```
96
105
97
-
This preference causes a lot of issues. See [#24066]. Most of the
106
+
This preference causes a lot of issues.
107
+
See [#24066].
108
+
Most of the
98
109
issues are caused by preferring where-bounds over impls even if the where-bound guides type inference:
99
110
```rust
100
111
traitTrait<T> {
@@ -167,7 +178,10 @@ where
167
178
168
179
#### Why no preference for global where-bounds
169
180
170
-
Global where-bounds are either fully implied by an impl or unsatisfiable. If they are unsatisfiable, we don't really care what happens. If a where-bound is fully implied then using the impl to prove the trait goal cannot result in additional constraints. For trait goals this is only useful for where-bounds which use `'static`:
181
+
Global where-bounds are either fully implied by an impl or unsatisfiable.
182
+
If they are unsatisfiable, we don't really care what happens.
183
+
If a where-bound is fully implied then using the impl to prove the trait goal cannot result in additional constraints.
184
+
For trait goals this is only useful for where-bounds which use `'static`:
171
185
172
186
```rust
173
187
traitA {
@@ -181,13 +195,15 @@ where
181
195
x.test();
182
196
}
183
197
```
184
-
More importantly, by using impls here we prevent global where-bounds from shadowing impls when normalizing associated types. There are no known issues from preferring impls over global where-bounds.
198
+
More importantly, by using impls here we prevent global where-bounds from shadowing impls when normalizing associated types.
199
+
There are no known issues from preferring impls over global where-bounds.
185
200
186
201
#### Why still consider global where-bounds
187
202
188
203
Given that we just use impls even if there exists a global where-bounds, you may ask why we don't just ignore these global where-bounds entirely: we use them to weaken the inference guidance from non-global where-bounds.
189
204
190
-
Without a global where-bound, we currently prefer non-global where bounds even though there would be an applicable impl as well. By adding a non-global where-bound, this unnecessary inference guidance is disabled, allowing the following to compile:
205
+
Without a global where-bound, we currently prefer non-global where bounds even though there would be an applicable impl as well.
206
+
By adding a non-global where-bound, this unnecessary inference guidance is disabled, allowing the following to compile:
191
207
```rust
192
208
fncheck<Color>(color:Color)
193
209
where
@@ -209,7 +225,9 @@ impl From<Vec> for f32 {
209
225
210
226
### `CandidateSource::AliasBound`
211
227
212
-
We prefer alias-bound candidates over impls. We currently use this preference to guide type inference, causing the following to compile. I personally don't think this preference is desirable 🤷
228
+
We prefer alias-bound candidates over impls.
229
+
We currently use this preference to guide type inference, causing the following to compile.
230
+
I personally don't think this preference is desirable 🤷
We prefer builtin trait object impls over user-written impls. This is **unsound** and should be remoed in the future. See [#57893](https://github.com/rust-lang/rust/issues/57893) and [#141347](https://github.com/rust-lang/rust/pull/141347) for more details.
275
+
We prefer builtin trait object impls over user-written impls.
276
+
This is **unsound** and should be remoed in the future.
277
+
See [#57893](https://github.com/rust-lang/rust/issues/57893) and [#141347](https://github.com/rust-lang/rust/pull/141347) for more details.
258
278
259
279
## `NormalizesTo` goals
260
280
@@ -336,7 +356,7 @@ Even if the trait goal was proven via an impl, we still prefer `ParamEnv` candid
336
356
#### We prefer "orphaned" where-bounds
337
357
338
358
We add "orphaned" `Projection` clauses into the `ParamEnv` when normalizing item bounds of GATs and RPITIT in `fn check_type_bounds`.
339
-
We need to prefer these `ParamEnv` candidates over impls and other where-bounds.
359
+
We need to prefer these `ParamEnv` candidates over impls and other where-bounds.
340
360
```rust
341
361
#![feature(associated_type_defaults)]
342
362
traitFoo {
@@ -355,7 +375,8 @@ I don't fully understand the cases where this preference is actually necessary a
355
375
356
376
#### We prefer global where-bounds over impls
357
377
358
-
This is necessary for the following to compile. I don't know whether anything relies on it in practice 🤷
378
+
This is necessary for the following to compile.
379
+
I don't know whether anything relies on it in practice 🤷
359
380
```rust
360
381
traitId {
361
382
typeThis;
@@ -423,7 +444,8 @@ where
423
444
424
445
#### RPITIT `type_of` cycles
425
446
426
-
We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762]. It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788].
447
+
We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762].
448
+
It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788].
427
449
428
450
```rust
429
451
usestd::future::Future;
@@ -457,8 +479,8 @@ where
457
479
#### Trait definition cannot use associated types from always applicable impls
458
480
459
481
The `T: Trait` assumption in the trait definition prevents it from normalizing
460
-
`<Self as Trait>::Assoc` to `T` by using the blanket impl. This feels like a somewhat
461
-
desirable constraint, if not incredibly so.
482
+
`<Self as Trait>::Assoc` to `T` by using the blanket impl.
483
+
This feels like a somewhat desirable constraint, if not incredibly so.
0 commit comments