1# Candidate preference23There are multiple ways to prove `Trait` and `NormalizesTo` goals.4Each such option is called a [`Candidate`].5If there are multiple applicable candidates, we prefer some candidates over others.6We store the relevant information in their [`CandidateSource`].78This preference may result in incorrect inference or region constraints and would therefore be unsound during coherence.9Because of this, we simply try to merge all candidates in coherence.1011## `Trait` goals1213Trait goals merge their applicable candidates in [`fn merge_trait_candidates`].14This document provides additional details and references to explain *why* we've got the current preference rules.1516### `CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))`1718Trivial builtin impls are builtin impls which are known to be always applicable for well-formed types.19This means that if one exists, using another candidate should never have fewer constraints.20We currently only consider `Sized` - and `MetaSized` - impls to be trivial.2122This is necessary to prevent a lifetime error for the following pattern2324```rust25trait Trait<T>: Sized {}26impl<'a> Trait<u32> for &'a str {}27impl<'a> Trait<i32> for &'a str {}28fn is_sized<T: Sized>(_: T) {}29fn foo<'a, 'b, T>(x: &'b str)30where31 &'a str: Trait<T>,32{33 // Elaborating the `&'a str: Trait<T>` where-bound results in a34 // `&'a str: Sized` where-bound. We do not want to prefer this35 // over the builtin impl.36 is_sized(x);37}38```3940This preference is incorrect in case the builtin impl has a nested goal which relies on a non-param where-clause41```rust42struct MyType<'a, T: ?Sized>(&'a (), T);43fn is_sized<T>() {}44fn foo<'a, T: ?Sized>()45where46 (MyType<'a, T>,): Sized,47 MyType<'static, T>: Sized,48{49 // The where-bound is trivial while the builtin `Sized` impl for tuples50 // requires proving `MyType<'a, T>: Sized` which can only be proven by51 // using the where-clause, adding an unnecessary `'static` constraint.52 is_sized::<(MyType<'a, T>,)>();53 //~^ ERROR lifetime may not live long enough54}55```5657### `CandidateSource::ParamEnv`5859Once there's at least one *non-global* `ParamEnv` candidate, we prefer *all* `ParamEnv` candidates over other candidate kinds.60A where-bound is global if it is not higher-ranked and doesn't contain any generic parameters.61It may contain `'static`.6263We try to apply where-bounds over other candidates as users tends to have the most control over them, so they can most easily64adjust them in case our candidate preference is incorrect.6566#### Preference over `Impl` candidates6768This is necessary to avoid region errors in the following example6970```rust71trait Trait<'a> {}72impl<T> Trait<'static> for T {}73fn impls_trait<'a, T: Trait<'a>>() {}74fn foo<'a, T: Trait<'a>>() {75 impls_trait::<'a, T>();76}77```7879We also need this as shadowed impls can result in currently ambiguous solver cycles: [trait-system-refactor-initiative#76].80Without preference, we'd be forced to fail with ambiguity81errors if the where-bound results in region constraints to avoid incompleteness.82```rust83trait Super {84 type SuperAssoc;85}8687trait Trait: Super<SuperAssoc = Self::TraitAssoc> {88 type TraitAssoc;89}9091impl<T, U> Trait for T92where93 T: Super<SuperAssoc = U>,94{95 type TraitAssoc = U;96}9798fn overflow<T: Trait>() {99 // We can use the elaborated `Super<SuperAssoc = Self::TraitAssoc>` where-bound100 // to prove the where-bound of the `T: Trait` implementation. This currently results in101 // overflow.102 let x: <T as Trait>::TraitAssoc;103}104```105106This preference causes a lot of issues.107See [#24066].108Most of the109issues are caused by preferring where-bounds over impls even, if the where-bound guides type inference:110```rust111trait Trait<T> {112 fn call_me(&self, x: T) {}113}114impl<T> Trait<u32> for T {}115impl<T> Trait<i32> for T {}116fn bug<T: Trait<U>, U>(x: T) {117 x.call_me(1u32);118 //~^ ERROR mismatched types119}120```121However, even if we only apply this preference if the where-bound doesn't guide inference, it may still result122in incorrect lifetime constraints:123```rust124trait Trait<'a> {}125impl<'a> Trait<'a> for &'a str {}126fn impls_trait<'a, T: Trait<'a>>(_: T) {}127fn foo<'a, 'b>(x: &'b str)128where129 &'a str: Trait<'b>130{131 // Need to prove `&'x str: Trait<'b>` with `'b: 'x`.132 impls_trait::<'b, _>(x);133 //~^ ERROR lifetime may not live long enough134}135```136137#### Preference over `AliasBound` candidates138139This is necessary to avoid region errors in the following example140```rust141trait Bound<'a> {}142trait Trait<'a> {143 type Assoc: Bound<'a>;144}145146fn impls_bound<'b, T: Bound<'b>>() {}147fn foo<'a, 'b, 'c, T>()148where149 T: Trait<'a>,150 for<'hr> T::Assoc: Bound<'hr>,151{152 impls_bound::<'b, T::Assoc>();153 impls_bound::<'c, T::Assoc>();154}155```156It can also result in unnecessary constraints157```rust158trait Bound<'a> {}159trait Trait<'a> {160 type Assoc: Bound<'a>;161}162163fn impls_bound<'b, T: Bound<'b>>() {}164fn foo<'a, 'b, T>()165where166 T: for<'hr> Trait<'hr>,167 <T as Trait<'b>>::Assoc: Bound<'a>,168{169 // Using the where-bound for `<T as Trait<'a>>::Assoc: Bound<'a>`170 // unnecessarily equates `<T as Trait<'a>>::Assoc` with the171 // `<T as Trait<'b>>::Assoc` from the env.172 impls_bound::<'a, <T as Trait<'a>>::Assoc>();173 // For a `<T as Trait<'b>>::Assoc: Bound<'b>` the self type of the174 // where-bound matches, but the arguments of the trait bound don't.175 impls_bound::<'b, <T as Trait<'b>>::Assoc>();176}177```178179#### Why no preference for global where-bounds180181Global where-bounds are either fully implied by an impl or unsatisfiable.182If they are unsatisfiable, we don't really care what happens.183If a where-bound is fully implied, then using the impl to prove the trait goal cannot result in additional constraints.184For trait goals, this is only useful for where-bounds which use `'static`:185186```rust187trait A {188 fn test(&self);189}190191fn foo(x: &dyn A)192where193 dyn A + 'static: A, // Using this bound would lead to a lifetime error.194{195 x.test();196}197```198More importantly, by using impls here, we prevent global where-bounds from shadowing impls when normalizing associated types.199There are no known issues from preferring impls over global where-bounds.200201#### Why still consider global where-bounds202203Given 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.204205Without a global where-bound, we currently prefer non-global where bounds even though there would be an applicable impl as well.206By adding a non-global where-bound, this unnecessary inference guidance is disabled, allowing the following to compile:207```rust208fn check<Color>(color: Color)209where210 Vec: Into<Color> + Into<f32>,211{212 let _: f32 = Vec.into();213 // Without the global `Vec: Into<f32>` bound we'd214 // eagerly use the non-global `Vec: Into<Color>` bound215 // here, causing this to fail.216}217218struct Vec;219impl From<Vec> for f32 {220 fn from(_: Vec) -> Self {221 loop {}222 }223}224```225226### `CandidateSource::AliasBound`227228We prefer alias-bound candidates over impls.229We currently use this preference to guide type inference, causing the following to compile.230I personally don't think this preference is desirable 🤷231```rust232pub trait Dyn {233 type Word: Into<u64>;234 fn d_tag(&self) -> Self::Word;235 fn tag32(&self) -> Option<u32> {236 self.d_tag().into().try_into().ok()237 // prove `Self::Word: Into<?0>` and then select a method238 // on `?0`, needs eager inference.239 }240}241```242```rust243fn impl_trait() -> impl Into<u32> {244 0u16245}246247fn main() {248 // There are two possible types for `x`:249 // - `u32` by using the "alias bound" of `impl Into<u32>`250 // - `impl Into<u32>`, i.e. `u16`, by using `impl<T> From<T> for T`251 //252 // We infer the type of `x` to be `u32` even though this is not253 // strictly necessary and can even lead to surprising errors.254 let x = impl_trait().into();255 println!("{}", std::mem::size_of_val(&x));256}257```258This preference also avoids ambiguity due to region constraints, I don't know whether people rely on this in practice.259```rust260trait Bound<'a> {}261impl<T> Bound<'static> for T {}262trait Trait<'a> {263 type Assoc: Bound<'a>;264}265266fn impls_bound<'b, T: Bound<'b>>() {}267fn foo<'a, T: Trait<'a>>() {268 // Should we infer this to `'a` or `'static`.269 impls_bound::<'_, T::Assoc>();270}271```272273### `CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_))`274275We prefer builtin trait object impls over user-written impls.276This is **unsound** and should be remoed in the future.277See [#57893] and [#141347] for more details.278279## `NormalizesTo` goals280281The candidate preference behavior during normalization is implemented in [`fn assemble_and_merge_candidates`].282283284### We always consider `AliasBound` candidates285286In case the where-bound does not specify the associated item, we consider `AliasBound` candidates instead of treating the alias as rigid, even though the trait goal was proven via a `ParamEnv` candidate.287288```rust289trait Super {290 type Assoc;291}292trait Bound {293 type Assoc: Super<Assoc = u32>;294}295trait Trait: Super {}296297// Elaborating the environment results in a `T::Assoc: Super` where-bound.298// This where-bound must not prevent normalization via the `Super<Assoc = u32>`299// item bound.300fn heck<T: Bound<Assoc: Trait>>(x: <T::Assoc as Super>::Assoc) -> u32 {301 x302}303```304Using such an alias can result in additional region constraints, cc [#133044].305```rust306trait Bound<'a> {307 type Assoc;308}309trait Trait {310 type Assoc: Bound<'static, Assoc = u32>;311}312313fn heck<'a, T: Trait<Assoc: Bound<'a>>>(x: <T::Assoc as Bound<'a>>::Assoc) {314 // Normalizing the associated type requires `T::Assoc: Bound<'static>` as it315 // uses the `Bound<'static>` alias-bound instead of keeping the alias rigid.316 drop(x);317}318```319320### We prefer `ParamEnv` candidates over `AliasBound`321322While we use `AliasBound` candidates if the where-bound does not specify the associated type, in case it does, we prefer the where-bound.323This is necessary for the following example:324```rust325// Make sure we prefer the `I::IntoIterator: Iterator<Item = ()>`326// where-bound over the `I::Intoiterator: Iterator<Item = I::Item>`327// alias-bound.328329trait Iterator {330 type Item;331}332333trait IntoIterator {334 type Item;335 type IntoIter: Iterator<Item = Self::Item>;336}337338fn normalize<I: Iterator<Item = ()>>() {}339340fn foo<I>()341where342 I: IntoIterator,343 I::IntoIter: Iterator<Item = ()>,344{345 // We need to prefer the `I::IntoIterator: Iterator<Item = ()>`346 // where-bound over the `I::Intoiterator: Iterator<Item = I::Item>`347 // alias-bound.348 normalize::<I::IntoIter>();349}350```351352### We always consider where-bounds353354Even if the trait goal was proven via an impl, we still prefer `ParamEnv` candidates, if any exist.355356#### We prefer "orphaned" where-bounds357358We add "orphaned" `Projection` clauses into the `ParamEnv` when normalizing item bounds of GATs and RPITIT in `fn check_type_bounds`.359We need to prefer these `ParamEnv` candidates over impls and other where-bounds.360```rust361#![feature(associated_type_defaults)]362trait Foo {363 // We should be able to prove that `i32: Baz<Self>` because of364 // the impl below, which requires that `Self::Bar<()>: Eq<i32>`365 // which is true, because we assume `for<T> Self::Bar<T> = i32`.366 type Bar<T>: Baz<Self> = i32;367}368trait Baz<T: ?Sized> {}369impl<T: Foo + ?Sized> Baz<T> for i32 where T::Bar<()>: Eq<i32> {}370trait Eq<T> {}371impl<T> Eq<T> for T {}372```373374I don't fully understand the cases where this preference is actually necessary and haven't been able to exploit this in fun ways yet, but 🤷375376#### We prefer global where-bounds over impls377378This is necessary for the following to compile.379I don't know whether anything relies on it in practice 🤷380```rust381trait Id {382 type This;383}384impl<T> Id for T {385 type This = T;386}387388fn foo<T>(x: T) -> <u32 as Id>::This389where390 u32: Id<This = T>,391{392 x393}394```395This means normalization can result in additional region constraints, cc [#133044].396```rust397trait Trait {398 type Assoc;399}400401impl Trait for &u32 {402 type Assoc = u32;403}404405fn trait_bound<T: Trait>() {}406fn normalize<T: Trait<Assoc = u32>>() {}407408fn foo<'a>()409where410 &'static u32: Trait<Assoc = u32>,411{412 trait_bound::<&'a u32>(); // ok, proven via impl413 normalize::<&'a u32>(); // error, proven via where-bound414}415```416417### Trait where-bounds shadow impls418419Normalization of associated items does not consider impls if the corresponding trait goal has been proven via a `ParamEnv` or `AliasBound` candidate.420This means that for where-bounds which do not constrain associated types, the associated types remain *rigid*.421422#### Using impls results in different region constraints423424This is necessary to avoid unnecessary region constraints from applying impls.425```rust426trait Trait<'a> {427 type Assoc;428}429impl Trait<'static> for u32 {430 type Assoc = u32;431}432433fn bar<'b, T: Trait<'b>>() -> T::Assoc { todo!() }434fn foo<'a>()435where436 u32: Trait<'a>,437{438 // Normalizing the return type would use the impl, proving439 // the `T: Trait` where-bound would use the where-bound, resulting440 // in different region constraints.441 bar::<'_, u32>();442}443```444445#### RPITIT `type_of` cycles446447We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762].448It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788].449450```rust451use std::future::Future;452pub trait ReactiveFunction: Send {453 type Output;454455 fn invoke(self) -> Self::Output;456}457458trait AttributeValue {459 fn resolve(self) -> impl Future<Output = ()> + Send;460}461462impl<F, V> AttributeValue for F463where464 F: ReactiveFunction<Output = V>,465 V: AttributeValue,466{467 async fn resolve(self) {468 // We're awaiting `<V as AttributeValue>::{synthetic#0}` here.469 // Normalizing that one via the the impl we're currently in470 // relies on `collect_return_position_impl_trait_in_trait_tys` which471 // ends up relying on auto-trait leakage when checking that the472 // opaque return type of this function implements the `Send` item473 // bound of the trait definition.474 self.invoke().resolve().await475 }476}477```478479<!-- date-check: Mar 2026 -->480#### Trait definition cannot use associated types from always applicable impls481482The `T: Trait` assumption in the trait definition prevents it from normalizing483`<Self as Trait>::Assoc` to `T` by using the blanket impl.484This feels like a somewhat desirable constraint, if not incredibly so.485486```rust487trait Eq<T> {}488impl<T> Eq<T> for T {}489struct IsEqual<T: Eq<U>, U>(T, U);490491trait Trait: Sized {492 type Assoc;493 fn foo() -> IsEqual<Self, Self::Assoc> {494 //~^ ERROR the trait bound `Self: Eq<<Self as Trait>::Assoc>` is not satisfied495 todo!()496 }497}498499impl<T> Trait for T {500 type Assoc = T;501}502```503504[`Candidate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/assembly/struct.Candidate.html505[`CandidateSource`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/enum.CandidateSource.html506[`fn merge_trait_candidates`]: https://github.com/rust-lang/rust/blob/e3ee7f7aea5b45af3b42b5e4713da43876a65ac9/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs#L1342-L1424507[`fn assemble_and_merge_candidates`]: https://github.com/rust-lang/rust/blob/e3ee7f7aea5b45af3b42b5e4713da43876a65ac9/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs#L920-L1003508[trait-system-refactor-initiative#76]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/76509[#24066]: https://github.com/rust-lang/rust/issues/24066510[#133044]: https://github.com/rust-lang/rust/issues/133044511[#139762]: https://github.com/rust-lang/rust/pull/139762512[#139788]: https://github.com/rust-lang/rust/issues/139788513[#57893]: https://github.com/rust-lang/rust/issues/57893514[#141347]: https://github.com/rust-lang/rust/pull/141347
Findings
✓ No findings reported for this file.