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

Don't return definite guidance if we find one solution and then flounder #331

Closed
wants to merge 1 commit into from

Conversation

flodiebold
Copy link
Member

If we find one solution and then flounder, this previously resulted in a Guidance::Definite result. I think the reason is that any_future_answer has no answers to check and hence returns true.

flodiebold added a commit to flodiebold/rust-analyzer that referenced this pull request Feb 15, 2020
This adds some tools helpful when debugging nondeterminism in analysis-stats:
 - a `--randomize` option that analyses everything in random order
 - a `-vv` option that prints even more detail

Also add a debug log if Chalk fuel is exhausted (which would be a source of
nondeterminism, but didn't happen in my tests).

I found one source of nondeterminism (rust-lang/chalk#331), but there are still
other cases remaining.
flodiebold added a commit to flodiebold/rust-analyzer that referenced this pull request Feb 15, 2020
This adds some tools helpful when debugging nondeterminism in analysis-stats:
 - a `--randomize` option that analyses everything in random order
 - a `-vv` option that prints even more detail

Also add a debug log if Chalk fuel is exhausted (which would be a source of
nondeterminism, but didn't happen in my tests).

I found one source of nondeterminism (rust-lang/chalk#331), but there are still
other cases remaining.
@flodiebold
Copy link
Member Author

flodiebold commented Feb 15, 2020

I think there may still be a problem with the any_future_answer check, because I found another case where we sometimes erroneously get definite guidance; in that case, there's more than one solution, but the first few solutions are all of the form &?0, so we get back &?0 as guidance. I'm not seeing any floundering in that case.

bors bot added a commit to rust-lang/rust-analyzer that referenced this pull request Feb 15, 2020
3157: Extend analysis-stats a bit r=matklad a=flodiebold

This adds some tools helpful when debugging nondeterminism in analysis-stats:
 - a `--randomize` option that analyses everything in random order
 - a `-vv` option that prints even more detail

Also add a debug log if Chalk fuel is exhausted (which would be a source of
nondeterminism, but didn't happen in my tests).

I found one source of nondeterminism (rust-lang/chalk#331), but there are still
other cases remaining.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
@jackh726
Copy link
Member

I have to think about this a bit more, but I don't think this fix is the most correct one to make.

I think the reason is that any_future_answer has no answers to check and hence returns true.
Well, that can't be the case because it can still Flounder.

This might be a problem with MayInvalidate, but I don't know yet.

Nevertheless, thanks for the test. If you remove non_enumerable from OtherTrait, it returns Ambiguous right? Also, can you add a short comment to the test to explain what it is for? Thanks.

@flodiebold
Copy link
Member Author

I have to think about this a bit more, but I don't think this fix is the most correct one to make.

That's very possible, especially since there still seems to be some similar problem.

Nevertheless, thanks for the test. If you remove non_enumerable from OtherTrait, it returns Ambiguous right?

Yes.

Also, can you add a short comment to the test to explain what it is for? Thanks.

Done.

@flodiebold
Copy link
Member Author

Ok, I was able to reproduce the second problem:

/// Don't return definite guidance if we are able to merge two solutions and the
/// third one matches that as well (the fourth may not).
#[test]
fn normalize_ambiguous() {
    test! {
        program {
            trait IntoIterator { type Item; }

            struct Ref<T> { }
            struct A { }
            struct B { }
            struct C { }

            struct D { }

            impl IntoIterator for Ref<A> { type Item = Ref<A>; }
            impl IntoIterator for Ref<B> { type Item = Ref<B>; }
            impl IntoIterator for Ref<C> { type Item = Ref<C>; }
            impl IntoIterator for Ref<D> { type Item = D; }
        }

        goal {
            exists<T, U> {
                Normalize(<Ref<T> as IntoIterator>::Item -> U)
            }
        } yields {
            "Ambiguous; no inference guidance"
        }
    }
}

This currently results in

Ambiguous; definite substitution for<?U0,?U0> { [?0 := ^0, ?1 := Ref<^1>] }

because any_future_answer actually just looks at the next answer, which happens to match in this case.

@jackh726
Copy link
Member

any_future_answer will test every strand with MayInvalidate. So this again points to MayInvalidate not being correct.

@jackh726
Copy link
Member

jackh726 commented Feb 17, 2020

Also, this test (in the comment) passes on current master, but not with the changes in this PR.

@flodiebold
Copy link
Member Author

Also, this test (in the comment) passes on current master, but not with the changes in this PR.

Ah, I think with my change it hits the 'answer cached' case:

if let Some(answer) = self.tables[table].answer(answer) {
info!("answer cached = {:?}", answer);
return test(C::inference_normalized_subst_from_subst(&answer.subst));
}

@@ -69,6 +69,10 @@ impl<I: Interner> context::AggregateOps<SlgContext<I>> for SlgContextOps<'_, I>
break Guidance::Unknown;
}

if let AnswerResult::Floundered = answers.peek_answer(|| should_continue()) {
break Guidance::Suggested(subst);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I think if we floundered we probably want Guidance::Unknown, as that suggests that we never really explored the options.

@nikomatsakis
Copy link
Contributor

But I see there were a lot more comments :) I have to read those and fully grok what they're saying

cjhopman pushed a commit to cjhopman/rust-analyzer that referenced this pull request Apr 10, 2020
This adds some tools helpful when debugging nondeterminism in analysis-stats:
 - a `--randomize` option that analyses everything in random order
 - a `-vv` option that prints even more detail

Also add a debug log if Chalk fuel is exhausted (which would be a source of
nondeterminism, but didn't happen in my tests).

I found one source of nondeterminism (rust-lang/chalk#331), but there are still
other cases remaining.
Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

@flodiebold would you be up to rebase this, but return Guidance::Unknown in the case of floundering?

If we find one solution and then flounder, this previously resulted in a
`Guidance::Definite` result. I think the reason is that `any_future_answer` has
no answers to check and hence returns true.
@flodiebold
Copy link
Member Author

@nikomatsakis Done! I added the second test as well, with the currently wrong result.

/// Don't return definite guidance if we are able to merge two solutions and the
/// third one matches that as well (the fourth may not).
#[test]
fn normalize_ambiguous() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this test have anything to do with floundering?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so (except that this test only reproduces the problem with the fix for the other problem, I think because it calls peek_answer)

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, curious.

Normalize(<Ref<T> as IntoIterator>::Item -> U)
}
} yields {
// FIXME: this is wrong!
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably create a FIXME policy of creating open issues to track these...

@nikomatsakis
Copy link
Contributor

I'm confused I guess if we want to land this now and investigate later or do more investigation. It seems like it's improving some problems you encountered in rust-analyzer?

@flodiebold
Copy link
Member Author

I think we could land this now. It's not that important for rust-analyzer anymore though since I switched to the recursive solver ;)

jackh726 added a commit to jackh726/chalk that referenced this pull request May 25, 2020
nathanwhit pushed a commit to nathanwhit/chalk that referenced this pull request May 28, 2020
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

Successfully merging this pull request may close these issues.

3 participants