Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive solver hangs on cyclic traits #773

Open
detrumi opened this issue Aug 10, 2022 · 3 comments
Open

Recursive solver hangs on cyclic traits #773

detrumi opened this issue Aug 10, 2022 · 3 comments

Comments

@detrumi
Copy link
Member

detrumi commented Aug 10, 2022

Found in rust-analyzer#12897, the following test causes the recursive solver to loop:

#[test]
fn cyclic_traits() {
    test! {
        program {
            trait Clone { }
            trait A {
                type X: B;
            }
            trait B {
                type Y: A<X = Self>;
            }
        }

        goal {
            exists<T> {
                if (<T as B>::Y: A) {
                    if (<<T as B>::Y as A>::X: Clone) {
                        T: A
                    }
                }
            }
        } yields {
            expect![["Ambiguous; no inference guidance"]]
        }
    }
}

Output:

comparing solvers:
        expected: SLG { max_size: 10, expected_answers: None }
        actual: Recursive { overflow_depth: 100, caching_enabled: true, max_size: 30 }

expected:
Ambiguous; no inference guidance
actual:
Ambiguous; definite substitution for<?U0> { [?0 := (B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<(B::Y)<(A::X)<^0.0>>>>>>>>>>>>>>>>>>>>>>>>>>>>] }
thread 'test::cycle::cyclic_traits' panicked at 'assertion failed: `(left == right)`
@detrumi
Copy link
Member Author

detrumi commented Aug 11, 2022

Slightly simpler repro, this one from rust-lang/rust-analyzer#12667. Hangs even with default depth.

#[test]
fn ra_12667() {
    test! {
        program {
            trait Clone { }
            trait A {
                type X: B;
            }
            trait B {
                type Y: A<X = Self>;
            }
        }

        goal {
            exists<T> {
                if (T: A) {
                    <T as A>::X: Clone
                }
            }
        } yields[SolverChoice::recursive_default()] {
            expect![["No possible solution"]]
        }
    }
}

@detrumi
Copy link
Member Author

detrumi commented Aug 12, 2022

So it's expanding the goals like this:

AliasEq(<(B::Y)<^0.0> as A>::X = ^0.1)
AliasEq(<(A::X)<(B::Y)<^0.0>> as B>::Y = ^0.1),
AliasEq(<(B::Y)<(A::X)<(B::Y)<^0.0>>> as A>::X = ^0.1)

which replaces <(B::Y)<^0.0> with <(B::Y)<(A::X)<(B::Y)<^0.0>>>, and then the solver just repeatedly expands the left side.
And because the goal keeps expanding, it's not triggering the cycle detection.

Do we need to start inspecting substitutions/goals for such repetitions, or is there a better solution?

@mgjm
Copy link

mgjm commented Oct 21, 2022

I ran into another variant of this problem. The following code also hangs with rust-analyzer analysis-stats . on the function outer:

pub trait Foo {
    type Bar: Bar<Foo = Self>;
}

pub trait Bar {
    type Foo: Foo<Bar = Self>;
}

type Inner<A> = <<A as Foo>::Bar as Bar>::Foo;

fn inner<A: Foo>(val: Inner<A>) -> A {
    val
}

fn outer<A: Foo>(val: Inner<A>) -> A {
    let ret = inner(val);
    ret
}

I think this is the same underlying problem, but I also wanted to share this variation to trigger the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants