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

Add NormalizeFn domain goal that associates projections of functions on traits with the matching implementation #726

Closed
wants to merge 9 commits into from

Conversation

lf-
Copy link
Contributor

@lf- lf- commented Sep 4, 2021

Fixes #716
Related to rust-lang/rust-analyzer#4558

This adds a NormalizeFn domain goal that's analogous to Normalize for types, which can be used to infer the related impl functions.

Along with this, I modify the chalk integration testing system's parser to add syntax naming impls: impl SomeTrait@MyImpl<T> for SomeType<T>. Impl functions can be referenced with @MyImpl::some_func<P1, P2, .....>. The parser also now supports functions in traits and impls.

The current state of this patch is ready for others to break it ;-) I don't have enough edge cases of traits in my head to think of all the ways to find more bugs in it.

Here is one of the tests from the PR, for context on what it does:

#[test]
fn impl_function_basic_generics() {
    test! {
        program {
            trait Trait<T> {
                fn a();
            }

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

            impl@Impl<T> Trait<T> for A<T> {
                fn a();
            }
            impl@NotImpl<T> Trait<T> for B<T> {
                fn a();
            }
        }

        goal {
            exists <F> {
                NormalizeFn(<A<C> as Trait<C>>::a -> F)
            }
        } yields {
            "Unique; substitution [?0 := {impl @Impl}::a<C>], lifetime constraints []"
        }
    }
}

This may be generalizable to constants also? I didn't explore this yet. If it's desired to explore it for this PR, I will have to take a break for a while before getting to it.

@lf-
Copy link
Contributor Author

lf- commented Sep 4, 2021

@lf-
Copy link
Contributor Author

lf- commented Sep 4, 2021

tried rebasing, see if that fixes the spurious CI failure due to lalrpop failing to build

@lf-
Copy link
Contributor Author

lf- commented Sep 4, 2021

sigh added a cargo update commit

@nikomatsakis nikomatsakis self-assigned this Sep 8, 2021
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.

This looks very good! I left some requests for more tests.

@@ -194,6 +194,146 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
let (other_params, trait_params) = parameters.split_at(split_point);
(trait_params, other_params)
}

/// Given the full set of parameters (or binders) for an
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, this duplication is a bit unfortunate. I wonder if there's a good way to refactor things to avoid it. I guess it's a consequence of trying to preserve the split between associated types and functions.

}

#[test]
fn impl_function_basic_generics() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have a variant of this test that includes method generics?

e.g.,

trait Trait<T> {
    fn a<U>(self);
}

fn a(v: T);
}
impl@NotImpl<T> Trait<T> for B<T> {
fn a(v: T);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have a test where the choice between impls is driven by the where clauses?

e.g.

impl<T> Trait<T> for T: Copy { fn a(); }
impl<T> Trait<T> for Vec<T> { fn a(); }

normalizing Vec<u32> should give the second impl.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This necessitates overlapping impls, does it not?

Copy link

Choose a reason for hiding this comment

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

T: Copy and Vec<T> do not overlap, or do you mean something else?

tests/test/impls.rs Show resolved Hide resolved
@lf-
Copy link
Contributor Author

lf- commented Sep 9, 2021

This looks very good! I left some requests for more tests.

So I've been working a little on implementing this in rust analyzer and I feel like I might really want to just refactor the whole thing to work on any kind of associated item, which is a refactoring I'm slightly dreading doing because it feels overwhelming (it shouldn't actually be that bad but still). I'm increasingly convinced it needs to happen both for this use case and for the compiler :/

It should not actually be that terrible because hardly any of this is actually function-specific logic, really, I just have to try it.

I'll see about writing those tests because they'll be useful regardless.

@nikomatsakis
Copy link
Contributor

@lf- I don't understand what refactoring you have in mind exactly. Do you mean having just one kind of domain goal, used for both associated types and for associated functions?

@lf-
Copy link
Contributor Author

lf- commented Sep 10, 2021

@lf- I don't understand what refactoring you have in mind exactly. Do you mean having just one kind of domain goal, used for both associated types and for associated functions?

Not quite: I want one domain goal for identifying the appropriate definition in impls, for all item types including constants and associated types, as proposed on the original r-a thread. Normalize would be kept as is and NormalizeFn would be changed.

@lf-
Copy link
Contributor Author

lf- commented Sep 16, 2021

I found a bug I probably wrote in chalk-integration that I can't solve right now, with "duplicate or shadowed parameters" in the case of function generics, where the generic is introduced in the binders in the outer scope and also in LowerFnDefn. Need to do more work to figure this out.

@bors
Copy link
Contributor

bors commented Oct 3, 2021

☔ The latest upstream changes (presumably #730) made this pull request unmergeable. Please resolve the merge conflicts.

@nikomatsakis
Copy link
Contributor

@lf- I've been talking with @tmandry about the idea of exposing an associated type for every associated function. I think that is very likely to happen. That suggests that I was wrong to encourage you to split associated functions and associated types, and it would be better to combine the two. We should definitely find some time to sync up on the best path forward for this PR!

@nikomatsakis
Copy link
Contributor

@lf- I'm going to close this PR, because I think you've kind of run out of time to pursue it, but I would like to pick it up again.

@HKalbasi HKalbasi mentioned this pull request Apr 17, 2022
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.

identifying which method will run for a trait
4 participants