Skip to content

Commit df96b2d

Browse files
committed
sembr src/solve/candidate-preference.md
1 parent 4969642 commit df96b2d

1 file changed

Lines changed: 40 additions & 18 deletions

File tree

src/solve/candidate-preference.md

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
# Candidate preference
22

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`].
47

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.
610

711
## `Trait` goals
812

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.
1015

1116
### `CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))`
1217

13-
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.
1421

1522
This is necessary to prevent a lifetime error for the following pattern
1623

@@ -50,7 +57,8 @@ where
5057
### `CandidateSource::ParamEnv`
5158

5259
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`.
5462

5563
We try to apply where-bounds over other candidates as users tends to have the most control over them, so they can most easily
5664
adjust them in case our candidate preference is incorrect.
@@ -68,7 +76,8 @@ fn foo<'a, T: Trait<'a>>() {
6876
}
6977
```
7078

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
7281
errors if the where-bound results in region constraints to avoid incompleteness.
7382
```rust
7483
trait Super {
@@ -94,7 +103,9 @@ fn overflow<T: Trait>() {
94103
}
95104
```
96105

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
98109
issues are caused by preferring where-bounds over impls even if the where-bound guides type inference:
99110
```rust
100111
trait Trait<T> {
@@ -167,7 +178,10 @@ where
167178

168179
#### Why no preference for global where-bounds
169180

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`:
171185

172186
```rust
173187
trait A {
@@ -181,13 +195,15 @@ where
181195
x.test();
182196
}
183197
```
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.
185200

186201
#### Why still consider global where-bounds
187202

188203
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.
189204

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:
191207
```rust
192208
fn check<Color>(color: Color)
193209
where
@@ -209,7 +225,9 @@ impl From<Vec> for f32 {
209225

210226
### `CandidateSource::AliasBound`
211227

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 🤷
213231
```rust
214232
pub trait Dyn {
215233
type Word: Into<u64>;
@@ -254,7 +272,9 @@ fn foo<'a, T: Trait<'a>>() {
254272

255273
### `CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_))`
256274

257-
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.
258278

259279
## `NormalizesTo` goals
260280

@@ -336,7 +356,7 @@ Even if the trait goal was proven via an impl, we still prefer `ParamEnv` candid
336356
#### We prefer "orphaned" where-bounds
337357

338358
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.
340360
```rust
341361
#![feature(associated_type_defaults)]
342362
trait Foo {
@@ -355,7 +375,8 @@ I don't fully understand the cases where this preference is actually necessary a
355375

356376
#### We prefer global where-bounds over impls
357377

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 🤷
359380
```rust
360381
trait Id {
361382
type This;
@@ -423,7 +444,8 @@ where
423444

424445
#### RPITIT `type_of` cycles
425446

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].
427449

428450
```rust
429451
use std::future::Future;
@@ -457,8 +479,8 @@ where
457479
#### Trait definition cannot use associated types from always applicable impls
458480

459481
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.
462484

463485
```rust
464486
trait Eq<T> {}
@@ -486,4 +508,4 @@ impl<T> Trait for T {
486508
[#24066]: https://github.com/rust-lang/rust/issues/24066
487509
[#133044]: https://github.com/rust-lang/rust/issues/133044
488510
[#139762]: https://github.com/rust-lang/rust/pull/139762
489-
[#139788]: https://github.com/rust-lang/rust/issues/139788
511+
[#139788]: https://github.com/rust-lang/rust/issues/139788

0 commit comments

Comments
 (0)