Skip to content

Do not compute type_of for impl item if impl where clauses are unsatisfied - #140276

Merged
bors merged 1 commit into
rust-lang:masterfrom
compiler-errors:typeof-less-eagerly
Apr 29, 2025
Merged

Do not compute type_of for impl item if impl where clauses are unsatisfied#140276
bors merged 1 commit into
rust-lang:masterfrom
compiler-errors:typeof-less-eagerly

Conversation

@compiler-errors

@compiler-errorscompiler-errors commented Apr 25, 2025

Copy link
Copy Markdown
Contributor

Consider the following code:

traitFoo{fncall(self) -> implSend;}traitNested{}impl<T>FooforTwhereT:Nested,{fncall(self) -> implSized{NotSatisfied.call()}}structNotSatisfied;implFooforNotSatisfied{fncall(self) -> implSized{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:

let impl_source = match selcx.select(&trait_obligation){

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

Fixesrust-lang/trait-system-refactor-initiative#185

@rustbotrustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Apr 25, 2025
Comment threadtests/ui/impl-trait/in-trait/cycle-if-impl-doesnt-apply.rs Outdated
@rust-log-analyzer

This comment has been minimized.

@lcnrlcnr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me on the fix. I think there's no good reason not to call try_evaluate_goals here.

I also feel like it sucks that we need to think about these cycles in the first place :< #139788

Comment threadtests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs
Comment threadtests/ui/impl-trait/in-trait/cycle-if-impl-doesnt-apply.rs
@lcnr

lcnr commented Apr 28, 2025

Copy link
Copy Markdown
Contributor

@bors r+ rollup

@bors

bors commented Apr 28, 2025

Copy link
Copy Markdown
Collaborator

📌 Commit 105d1dc has been approved by lcnr

It is now in the queue for this repository.

@borsbors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 28, 2025
ChrisDenton added a commit to ChrisDenton/rust that referenced this pull request Apr 28, 2025
…y, 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
Fixesrust-lang/trait-system-refactor-initiative#185
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 28, 2025
…enton
Rollup of 9 pull requests
Successful merges:
- rust-lang#139308 (add autodiff inline)
- rust-lang#140276 (Do not compute type_of for impl item if impl where clauses are unsatisfied)
- rust-lang#140302 (Move inline asm check to typeck, properly handle aliases)
- rust-lang#140323 (Implement the internal feature `cfg_target_has_reliable_f16_f128`)
- rust-lang#140374 (Resolve instance for SymFn in global/naked asm)
- rust-lang#140391 (Rename sub_ptr to offset_from_unsigned in docs)
- rust-lang#140394 (Make bootstrap git tests more self-contained)
- rust-lang#140396 (Workaround for windows-gnu rust-lld test failure)
- rust-lang#140402 (only return nested goals for `Certainty::Yes`)
r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 28, 2025
[DO NOT MERGE] bootstrap with `-Znext-solver=globally`
A revival of rust-lang#124812.
Current status:
~~`./x.py b --stage 2` passes 🎉~~
`try` builds succeed 🎉 🎉 🎉
~~[top 100 most downloaded crates on crates.io compile](rust-lang#133502 (comment)
[top 1000 most downloaded crates on crates.io compile](rust-lang#133502 (comment))
[first perf run](rust-lang#133502 (comment)) 👻
### in-flight changes
- rust-lang#124852, unsure whether I actually want to land this PR for now
- rust-lang#139587
- https://github.com/lcnr/rust/tree/opaque-type-method-call
- rust-lang#140302
- rust-lang#140276
- rust-lang#140260
- rust-lang#140405
r? `@ghost`
ChrisDenton added a commit to ChrisDenton/rust that referenced this pull request Apr 28, 2025
…y, 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
Fixesrust-lang/trait-system-refactor-initiative#185
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 28, 2025
…enton
Rollup of 10 pull requests
Successful merges:
- rust-lang#139308 (add autodiff inline)
- rust-lang#139656 (Stabilize `slice_as_chunks` library feature)
- rust-lang#140022 (allow deref patterns to move out of boxes)
- rust-lang#140276 (Do not compute type_of for impl item if impl where clauses are unsatisfied)
- rust-lang#140302 (Move inline asm check to typeck, properly handle aliases)
- rust-lang#140323 (Implement the internal feature `cfg_target_has_reliable_f16_f128`)
- rust-lang#140391 (Rename sub_ptr to offset_from_unsigned in docs)
- rust-lang#140394 (Make bootstrap git tests more self-contained)
- rust-lang#140396 (Workaround for windows-gnu rust-lld test failure)
- rust-lang#140402 (only return nested goals for `Certainty::Yes`)
Failed merges:
- rust-lang#139765 ([beta] Delay `hash_extract_if` stabilization from 1.87 to 1.88)
r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 29, 2025
…enton
Rollup of 10 pull requests
Successful merges:
- rust-lang#139308 (add autodiff inline)
- rust-lang#139656 (Stabilize `slice_as_chunks` library feature)
- rust-lang#140022 (allow deref patterns to move out of boxes)
- rust-lang#140276 (Do not compute type_of for impl item if impl where clauses are unsatisfied)
- rust-lang#140302 (Move inline asm check to typeck, properly handle aliases)
- rust-lang#140323 (Implement the internal feature `cfg_target_has_reliable_f16_f128`)
- rust-lang#140391 (Rename sub_ptr to offset_from_unsigned in docs)
- rust-lang#140394 (Make bootstrap git tests more self-contained)
- rust-lang#140396 (Workaround for windows-gnu rust-lld test failure)
- rust-lang#140402 (only return nested goals for `Certainty::Yes`)
r? `@ghost`
`@rustbot` modify labels: rollup
@bors
bors merged commit 0bd531a into rust-lang:masterApr 29, 2025
@rustbotrustbot added this to the 1.88.0 milestone Apr 29, 2025
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 29, 2025
Rollup merge of rust-lang#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
Fixesrust-lang/trait-system-refactor-initiative#185
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 29, 2025
[DO NOT MERGE] bootstrap with `-Znext-solver=globally`
A revival of rust-lang#124812.
Current status:
~~`./x.py b --stage 2` passes 🎉~~
`try` builds succeed 🎉 🎉 🎉
~~[top 100 most downloaded crates on crates.io compile](rust-lang#133502 (comment)
[top 1000 most downloaded crates on crates.io compile](rust-lang#133502 (comment))
[first perf run](rust-lang#133502 (comment)) 👻
### in-flight changes
- rust-lang#124852, unsure whether I actually want to land this PR for now
- rust-lang#139587
- https://github.com/lcnr/rust/tree/opaque-type-method-call
- rust-lang#140302
- rust-lang#140276
- rust-lang#140260
- rust-lang#140405
r? `@ghost`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-borsStatus: Waiting on bors to run and complete tests. Bors will change the label on completion.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.WG-trait-system-refactorThe Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

salvo_core regression: RPITIT type_of cycle

5 participants

@compiler-errors@rust-log-analyzer@lcnr@bors@rustbot