diff options
| author | Chris Denton <chris@chrisdenton.dev> | 2025-04-28 23:29:16 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-28 23:29:16 +0000 |
| commit | 0bd531aaf2622553a1488ede23db32aa51040f6c (patch) | |
| tree | 0006430ddddf18bbe6c680e79a8e22a925a0630e /compiler/rustc_next_trait_solver/src | |
| parent | 8dd26cb195df77d991ee7826b1aa587d9f638e78 (diff) | |
| parent | 105d1dcefd9baa520e63830f557d9c472f50d4fd (diff) | |
| download | rust-0bd531aaf2622553a1488ede23db32aa51040f6c.tar.gz rust-0bd531aaf2622553a1488ede23db32aa51040f6c.zip | |
Rollup merge of #140276 - compiler-errors:typeof-less-eagerly, r=lcnr
Do not compute type_of for impl item if impl where clauses are unsatisfied
Consider the following code:
```rust
trait Foo {
fn call(self) -> impl Send;
}
trait Nested {}
impl<T> Foo for T
where
T: Nested,
{
fn call(self) -> impl Sized {
NotSatisfied.call()
}
}
struct NotSatisfied;
impl Foo for NotSatisfied {
fn call(self) -> impl Sized {
todo!()
}
}
```
In `impl Foo for NotSatisfied`, we need to prove that the RPITIT is well formed. This requires proving the item bound `<NotSatisfied as Foo>::RPITIT: Send`. Normalizing `<NotSatisfied as Foo>::RPITIT: Send` assembles two impl candidates, via the `NotSatisfied` impl and the blanket `T` impl. We end up computing the `type_of` for the blanket impl even if `NotSatisfied: Nested` where clause does not hold.
This type_of query ends up needing to prove that its own `impl Sized` RPIT satisfies `Send`, which ends up needing to compute the hidden type of the RPIT, which is equal to the return type of `NotSatisfied.call()`. That ends up in a query cycle, since we subsequently try normalizing that return type via the blanket impl again!
In the old solver, we don't end up computing the `type_of` an impl candidate if its where clauses don't hold, since this select call would fail before confirming the projection candidate:
https://github.com/rust-lang/rust/blob/d7ea436a02d5de4033fcf7fd4eb8ed965d0f574c/compiler/rustc_trait_selection/src/traits/project.rs#L882
This PR makes the new solver more consistent with the old solver by adding a call to `try_evaluate_added_goals` after regstering the impl predicates, which causes us to bail before computing the `type_of` for impls if the impl definitely doesn't apply.
r? lcnr
Fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/185
Diffstat (limited to 'compiler/rustc_next_trait_solver/src')
| -rw-r--r-- | compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index f5cad623903..87faf99ced6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -194,6 +194,12 @@ where .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + // Bail if the nested goals don't hold here. This is to avoid unnecessarily + // computing the `type_of` query for associated types that never apply, as + // this may result in query cycles in the case of RPITITs. + // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/185>. + ecx.try_evaluate_added_goals()?; + // Add GAT where clauses from the trait's definition. // FIXME: We don't need these, since these are the type's own WF obligations. ecx.add_goals( |
