Instantiate predicate binder without recanonicalizing goal in new solver by compiler-errors ยท Pull Request #136997 ยท rust-lang/rust

lcnr

@compiler-errors @lcnr

@lcnr lcnr mentioned this pull request

May 5, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 5, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 6, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 6, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 6, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 7, 2025

bors added a commit to rust-lang-ci/rust that referenced this pull request

May 9, 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 ๐ŸŽ‰ ๐ŸŽ‰ ๐ŸŽ‰

[first perf run](rust-lang#133502 (comment)) ๐Ÿ‘ป

### crater

This does not detect hangs or memory issues.

| date | #crates | #regressions |
| ---- | ------- | ------------ |
| 2025.04.11 | 100 | 2 |
| 2025.04.11 | 1000 | 27 |
| 2025.04.17 | 10000 | 456 |
| 2025.04.18 | 10000 | 437 |
| 2025.04.24 | 10000 | 164 |
| 2025.04.26 | 10000 | 108 |
| 2025.04.28 | 10000 | 91 |
| 2025.05.01 | 10000 | 145 woops |
| 2025.05.03 | 624228[^1] |  1585 |
| 2025.05.05 | 8964[^2] | 931 |
| 2025.05.06 | 4401[^2] | 726 |
| 2025.05.07 | 2704[^2] | 668 |

[^1]: a complete crater run
[^2]: only testing crates which may have regressed from the above run

### in-flight changes

- rust-lang#140712
- rust-lang#136997
- rust-lang#139587
- rust-lang#140497
- rust-lang#124852, unsure whether I actually want to land this PR for now
- https://github.com/lcnr/rust/tree/opaque-type-method-call
- rust-lang#140375
- rust-lang#140405
- look into blanket impls for opaque type infer vars as well
- rust-lang#140496
- double recursion limit in the new solver

r? `@ghost`

bors added a commit that referenced this pull request

Jun 2, 2025
Instantiate predicate binder without recanonicalizing goal in new solver

This has the side-effect of making the leak check stronger.

r? lcnr

@lcnr lcnr added the needs-fcp

This change is insta-stable, or significant enough to need a team FCP to proceed.

label

Jun 3, 2025

@lcnr lcnr mentioned this pull request

Sep 18, 2025

bors added a commit that referenced this pull request

Oct 21, 2025
`-Znext-solver` instantiate predicate binder without recanonicalizing goal

This strengthens the leak check to match the old trait solver. The new trait solver now also instantiates higher ranked goals in the same scope as candidate selection, so the leak check in each candidate detects placeholder errors involving this higher ranked goal.

E.g. let's look at tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs
```rust
trait Trait<T, U> {}
impl<'a> Trait<&'a str, &'a str> for () {}
impl<'a> Trait<&'a str, String> for () {}
fn impls_trait<T: for<'a> Trait<&'a str, U>, U>() {}

fn main() {
    impls_trait::<(), _>();
}
```
Here proving `(): for<'a> Trait<&'a str, ?u>` via `impl<'a> Trait<&'a str, &'a str> for ()` equates `?u` with `&'!a str` which results in a leak check error as `?u` cannot name `'a`. If this leak check error happens while considering candidates we drop the first impl and infer `?u` to `String`. If not, this remains ambiguous.

This behavior is a bit iffy, see the FCP proposal in #119820 for more details on why this current behavior is somewhat undesirable. However, considering placeholders from higher-ranked goals for candidate selection does allow more code to compile and a lot of the code *feels like it should compile*. **This caused us to revert the change of #119820 in #127568.**

I originally expected that we can avoid breakage with the new solver differently here, e.g. by considering OR-region constraints. However, doing so is a significant change and I don't have a great idea for how that should work. Matching the old solver behavior for now should not make this cleaner approach any more difficult in the future, so let's just go with what actually allows us to stabilize the new solver for now.

This PR changing the new solver to match the behavior of the old one wrt the leak check. As the new solver is already used by default in coherence, this allows more code to compile, see `tests/ui/higher-ranked/leak-check/leak-check-in-selection-7-coherence.rs`:
```rust
struct W<T, U>(T, U);

trait Trait<T> {}
// using this impl results in a higher-ranked region error.
impl<'a> Trait<W<&'a str, &'a str>> for () {}
impl<'a> Trait<W<&'a str, String>> for () {}

trait NotString {}
impl NotString for &str {}
impl NotString for u32 {}

trait Overlap<U> {}
impl<T: for<'a> Trait<W<&'a str, U>>, U> Overlap<U> for T {}
impl<U: NotString> Overlap<U> for () {}

fn main() {}
```

This behavior is quite arbitrary and not something I expect users to rely on in practice, however, it should still go through an FCP imo.

r? `@BoxyUwU` originally implemented by `@compiler-errors` in #136997. Closes rust-lang/trait-system-refactor-initiative#120.

github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request

Oct 27, 2025
`-Znext-solver` instantiate predicate binder without recanonicalizing goal

This strengthens the leak check to match the old trait solver. The new trait solver now also instantiates higher ranked goals in the same scope as candidate selection, so the leak check in each candidate detects placeholder errors involving this higher ranked goal.

E.g. let's look at tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs
```rust
trait Trait<T, U> {}
impl<'a> Trait<&'a str, &'a str> for () {}
impl<'a> Trait<&'a str, String> for () {}
fn impls_trait<T: for<'a> Trait<&'a str, U>, U>() {}

fn main() {
    impls_trait::<(), _>();
}
```
Here proving `(): for<'a> Trait<&'a str, ?u>` via `impl<'a> Trait<&'a str, &'a str> for ()` equates `?u` with `&'!a str` which results in a leak check error as `?u` cannot name `'a`. If this leak check error happens while considering candidates we drop the first impl and infer `?u` to `String`. If not, this remains ambiguous.

This behavior is a bit iffy, see the FCP proposal in rust-lang/rust#119820 for more details on why this current behavior is somewhat undesirable. However, considering placeholders from higher-ranked goals for candidate selection does allow more code to compile and a lot of the code *feels like it should compile*. **This caused us to revert the change of rust-lang/rust#119820 in rust-lang/rust#127568.**

I originally expected that we can avoid breakage with the new solver differently here, e.g. by considering OR-region constraints. However, doing so is a significant change and I don't have a great idea for how that should work. Matching the old solver behavior for now should not make this cleaner approach any more difficult in the future, so let's just go with what actually allows us to stabilize the new solver for now.

This PR changing the new solver to match the behavior of the old one wrt the leak check. As the new solver is already used by default in coherence, this allows more code to compile, see `tests/ui/higher-ranked/leak-check/leak-check-in-selection-7-coherence.rs`:
```rust
struct W<T, U>(T, U);

trait Trait<T> {}
// using this impl results in a higher-ranked region error.
impl<'a> Trait<W<&'a str, &'a str>> for () {}
impl<'a> Trait<W<&'a str, String>> for () {}

trait NotString {}
impl NotString for &str {}
impl NotString for u32 {}

trait Overlap<U> {}
impl<T: for<'a> Trait<W<&'a str, U>>, U> Overlap<U> for T {}
impl<U: NotString> Overlap<U> for () {}

fn main() {}
```

This behavior is quite arbitrary and not something I expect users to rely on in practice, however, it should still go through an FCP imo.

r? `@BoxyUwU` originally implemented by `@compiler-errors` in rust-lang/rust#136997. Closes rust-lang/trait-system-refactor-initiative#120.

Kobzol pushed a commit to Kobzol/rustc_codegen_gcc that referenced this pull request

Dec 21, 2025
`-Znext-solver` instantiate predicate binder without recanonicalizing goal

This strengthens the leak check to match the old trait solver. The new trait solver now also instantiates higher ranked goals in the same scope as candidate selection, so the leak check in each candidate detects placeholder errors involving this higher ranked goal.

E.g. let's look at tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs
```rust
trait Trait<T, U> {}
impl<'a> Trait<&'a str, &'a str> for () {}
impl<'a> Trait<&'a str, String> for () {}
fn impls_trait<T: for<'a> Trait<&'a str, U>, U>() {}

fn main() {
    impls_trait::<(), _>();
}
```
Here proving `(): for<'a> Trait<&'a str, ?u>` via `impl<'a> Trait<&'a str, &'a str> for ()` equates `?u` with `&'!a str` which results in a leak check error as `?u` cannot name `'a`. If this leak check error happens while considering candidates we drop the first impl and infer `?u` to `String`. If not, this remains ambiguous.

This behavior is a bit iffy, see the FCP proposal in rust-lang/rust#119820 for more details on why this current behavior is somewhat undesirable. However, considering placeholders from higher-ranked goals for candidate selection does allow more code to compile and a lot of the code *feels like it should compile*. **This caused us to revert the change of rust-lang/rust#119820 in rust-lang/rust#127568.**

I originally expected that we can avoid breakage with the new solver differently here, e.g. by considering OR-region constraints. However, doing so is a significant change and I don't have a great idea for how that should work. Matching the old solver behavior for now should not make this cleaner approach any more difficult in the future, so let's just go with what actually allows us to stabilize the new solver for now.

This PR changing the new solver to match the behavior of the old one wrt the leak check. As the new solver is already used by default in coherence, this allows more code to compile, see `tests/ui/higher-ranked/leak-check/leak-check-in-selection-7-coherence.rs`:
```rust
struct W<T, U>(T, U);

trait Trait<T> {}
// using this impl results in a higher-ranked region error.
impl<'a> Trait<W<&'a str, &'a str>> for () {}
impl<'a> Trait<W<&'a str, String>> for () {}

trait NotString {}
impl NotString for &str {}
impl NotString for u32 {}

trait Overlap<U> {}
impl<T: for<'a> Trait<W<&'a str, U>>, U> Overlap<U> for T {}
impl<U: NotString> Overlap<U> for () {}

fn main() {}
```

This behavior is quite arbitrary and not something I expect users to rely on in practice, however, it should still go through an FCP imo.

r? `@BoxyUwU` originally implemented by `@compiler-errors` in rust-lang/rust#136997. Closes rust-lang/trait-system-refactor-initiative#120.