stepping into impls for norm is unproductive

This commit is contained in:
lcnr 2025-04-16 10:19:14 +02:00
parent efb1e3d676
commit 48e119ef5a
6 changed files with 201 additions and 8 deletions

View file

@ -286,18 +286,23 @@ where
// fixing it may cause inference breakage or introduce ambiguity.
GoalSource::Misc => PathKind::Unknown,
GoalSource::NormalizeGoal(path_kind) => path_kind,
GoalSource::ImplWhereBound => {
GoalSource::ImplWhereBound => match self.current_goal_kind {
// We currently only consider a cycle coinductive if it steps
// into a where-clause of a coinductive trait.
CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive,
// While normalizing via an impl does step into a where-clause of
// an impl, accessing the associated item immediately steps out of
// it again. This means cycles/recursive calls are not guarded
// by impls used for normalization.
//
// See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs
// for how this can go wrong.
CurrentGoalKind::NormalizesTo => PathKind::Inductive,
// We probably want to make all traits coinductive in the future,
// so we treat cycles involving their where-clauses as ambiguous.
if let CurrentGoalKind::CoinductiveTrait = self.current_goal_kind {
PathKind::Coinductive
} else {
PathKind::Unknown
}
}
// so we treat cycles involving where-clauses of not-yet coinductive
// traits as ambiguous for now.
CurrentGoalKind::Misc => PathKind::Unknown,
},
// Relating types is always unproductive. If we were to map proof trees to
// corecursive functions as explained in #136824, relating types never
// introduces a constructor which could cause the recursion to be guarded.

View file

@ -0,0 +1,24 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass
// Regression test for trait-system-refactor-initiative#176.
//
// Normalizing `<Vec<T> as IntoIterator>::IntoIter` has two candidates
// inside of the function:
// - `impl<T> IntoIterator for Vec<T>` which trivially applies
// - `impl<I: Iterator> IntoIterator for I`
// - requires `Vec<T>: Iterator`
// - where-clause requires `<Vec<T> as IntoIterator>::IntoIter eq Vec<T>`
// - normalize `<Vec<T> as IntoIterator>::IntoIter` again, cycle
//
// We need to treat this cycle as an error to be able to use the actual impl.
fn test<T>()
where
<Vec<T> as IntoIterator>::IntoIter: Iterator,
{
}
fn main() {}

View file

@ -0,0 +1,48 @@
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:41:1
|
LL | / fn generic<T>()
LL | | where
LL | | <Foo as Trait<T>>::Assoc: Bound,
| |____________________________________^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required for `Foo` to implement `Trait<T>`
--> $DIR/normalizes-to-is-not-productive.rs:24:19
|
LL | impl<T: Bound, U> Trait<U> for T {
| ----- ^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:41:4
|
LL | fn generic<T>()
| ^^^^^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required for `Foo` to implement `Trait<T>`
--> $DIR/normalizes-to-is-not-productive.rs:24:19
|
LL | impl<T: Bound, U> Trait<U> for T {
| ----- ^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:48:19
|
LL | impls_bound::<Foo>();
| ^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required by a bound in `impls_bound`
--> $DIR/normalizes-to-is-not-productive.rs:28:19
|
LL | fn impls_bound<T: Bound>() {
| ^^^^^ required by this bound in `impls_bound`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,31 @@
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:43:31
|
LL | <Foo as Trait<T>>::Assoc: Bound,
| ^^^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required for `Foo` to implement `Trait<T>`
--> $DIR/normalizes-to-is-not-productive.rs:24:19
|
LL | impl<T: Bound, U> Trait<U> for T {
| ----- ^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:48:19
|
LL | impls_bound::<Foo>();
| ^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required by a bound in `impls_bound`
--> $DIR/normalizes-to-is-not-productive.rs:28:19
|
LL | fn impls_bound<T: Bound>() {
| ^^^^^ required by this bound in `impls_bound`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,54 @@
//@ ignore-compare-mode-next-solver (explicit)
//@ compile-flags: -Znext-solver
// Make sure that stepping into impl where-clauses of `NormalizesTo`
// goals is unproductive. This must not compile, see the inline
// comments.
trait Bound {
fn method();
}
impl Bound for u32 {
fn method() {}
}
trait Trait<T> {
type Assoc: Bound;
}
struct Foo;
impl Trait<u32> for Foo {
type Assoc = u32;
}
impl<T: Bound, U> Trait<U> for T {
type Assoc = T;
}
fn impls_bound<T: Bound>() {
T::method();
}
// The where-clause requires `Foo: Trait<T>` to hold to be wf.
// If stepping into where-clauses during normalization is considered
// to be productive, this would be the case:
//
// - `Foo: Trait<T>`
// - via blanket impls, requires `Foo: Bound`
// - via where-bound, requires `Foo eq <Foo as Trait<T>>::Assoc`
// - normalize `<Foo as Trait<T>>::Assoc`
// - via blanket impl, requires where-clause `Foo: Bound` -> cycle
fn generic<T>()
where
<Foo as Trait<T>>::Assoc: Bound,
//~^ ERROR the trait bound `Foo: Bound` is not satisfied
{
// Requires proving `Foo: Bound` by normalizing
// `<Foo as Trait<T>>::Assoc` to `Foo`.
impls_bound::<Foo>();
//~^ ERROR the trait bound `Foo: Bound` is not satisfied
}
fn main() {
// Requires proving `<Foo as Trait<u32>>::Assoc: Bound`.
// This is trivially true.
generic::<u32>();
}

View file

@ -0,0 +1,31 @@
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:42:31
|
LL | <Foo as Trait<T>>::Assoc: Bound,
| ^^^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required for `Foo` to implement `Trait<T>`
--> $DIR/normalizes-to-is-not-productive.rs:23:19
|
LL | impl<T: Bound, U> Trait<U> for T {
| ----- ^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
error[E0277]: the trait bound `Foo: Bound` is not satisfied
--> $DIR/normalizes-to-is-not-productive.rs:47:19
|
LL | impls_bound::<Foo>();
| ^^^ the trait `Bound` is not implemented for `Foo`
|
= help: the trait `Bound` is implemented for `u32`
note: required by a bound in `impls_bound`
--> $DIR/normalizes-to-is-not-productive.rs:27:19
|
LL | fn impls_bound<T: Bound>() {
| ^^^^^ required by this bound in `impls_bound`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.