Skip to content

base trait method not resolved when using impl in return position #7273

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

Closed
vemoo opened this issue Jan 14, 2021 · 6 comments · Fixed by #7739
Closed

base trait method not resolved when using impl in return position #7273

vemoo opened this issue Jan 14, 2021 · 6 comments · Fixed by #7739
Labels
A-ty type system / type inference / traits / method resolution S-actionable Someone could pick this issue up and work on it right now

Comments

@vemoo
Copy link
Contributor

vemoo commented Jan 14, 2021

Code that reproduces the issue:

fn elements_iter() -> impl Iterator<Item = usize> {
    [].iter().copied()
}

fn elements_exacte_size_iter() -> impl ExactSizeIterator<Item = usize> {
    [].iter().copied()
}

fn test() {
    for x in elements_iter() {}
    //  ^ is correctly inferred to be `usize`
    for x in elements_exacte_size_iter() {}
    //  ^ is not inferred
}

After investigating in the rust-analyzer source and playing around with a test in crates/hir_ty/src/tests/traits.rs I found out that inference works when it's an argument:

fn foo(xs: impl ExactSizeIterator<Item = usize>) {
    for x in xs {}
    //  ^ inferred `usize`
}
fn bar<I: ExactSizeIterator<Item = usize>>(xs: I) {
    for x in xs {}
    //  ^ inferred `usize`
}

This is the test:

#[test]
fn super_trait_assoc_type_impl_return() {
    check_infer(
        r#"
        trait Base {
            type Item;
            fn foo(self) -> Self::Item;
        }

        trait Super : Base {}
        
        fn base1() -> impl Base<Item = usize> { loop {} }
        fn super1() -> impl Super<Item = usize> { loop {} }

        fn test(base2: impl Base<Item = usize>, super2: impl Super<Item = usize>) {            
            base1().foo();
            super1().foo();
            base2.foo();
            super2.foo();
        }
        "#,
        expect![[r#"..."#]],
    );
}

which at the moment gives:

39..43 'self': Self
124..135 '{ loop {} }': !
126..133 'loop {}': !
131..133 '{}': ()
176..187 '{ loop {} }': !
178..185 'loop {}': !
183..185 '{}': ()
197..202 'base2': impl Base<Item = usize>
229..235 'super2': impl Super<Item = usize>
263..352 '{     ...o(); }': ()
281..286 'base1': fn base1() -> impl Base<Item = usize>
281..288 'base1()': impl Base<Item = usize>
281..294 'base1().foo()': usize
300..306 'super1': fn super1() -> impl Super<Item = usize>
300..308 'super1()': impl Super<Item = usize>
300..314 'super1().foo()': {unknown}
320..325 'base2': impl Base<Item = usize>
320..331 'base2.foo()': usize
337..343 'super2': impl Super<Item = usize>
337..349 'super2.foo()': usize
@Veykril Veykril added A-ty type system / type inference / traits / method resolution S-actionable Someone could pick this issue up and work on it right now labels Jan 14, 2021
@vemoo
Copy link
Contributor Author

vemoo commented Jan 15, 2021

After some more investigation it doesn't have to do with associated types, just with trait method resolution when using impl returns:

#[test]
fn super_trait_impl_return_trait_method_resolution() {
    check_infer(
        r#"
        trait Base {            
            fn foo(self) -> usize;
        }

        trait Super : Base {}
        
        fn base1() -> impl Base { loop {} }
        fn super1() -> impl Super { loop {} }

        fn test(base2: impl Base, super2: impl Super) {            
            base1().foo();
            super1().foo();
            base2.foo();
            super2.foo();
        }
        "#,
        expect![[r#"..."#]],
    );
}

gives:

36..40 'self': Self
102..113 '{ loop {} }': !
104..111 'loop {}': !
109..111 '{}': ()
140..151 '{ loop {} }': !
142..149 'loop {}': !
147..149 '{}': ()
161..166 'base2': impl Base
179..185 'super2': impl Super
199..288 '{     ...o(); }': ()
217..222 'base1': fn base1() -> impl Base
217..224 'base1()': impl Base
217..230 'base1().foo()': usize
236..242 'super1': fn super1() -> impl Super
236..244 'super1()': impl Super
236..250 'super1().foo()': {unknown}
256..261 'base2': impl Base
256..267 'base2.foo()': usize
273..279 'super2': impl Super
273..285 'super2.foo()': usize

I will investigate some more to see if I find a fix.

@vemoo vemoo changed the title Associated item of base trait not inferred when using impl in return position base trait method not resolved when using impl in return position Jan 15, 2021
@vemoo
Copy link
Contributor Author

vemoo commented Jan 18, 2021

After some println debugging and enabling the logs I think I found something.

If I run hir_ty::tests::infer with

trait Base {            
    fn foo(self) -> usize;
}

fn base1() -> impl Base { loop {} }

fn test() {
    base1().foo();
}

this are the logs for RUST_LOG="hir_ty=debug"

[INFO  hir_ty::traits] trait_solve_query(Implements(impl Base: Base))
[DEBUG hir_ty::traits] solve goal: UCanonical { canonical: Canonical { value: InEnvironment { environment: Env([]), goal: Implemented(SeparatorTraitRef(?)) }, binders: [] }, universes: 1 }
[DEBUG hir_ty::traits::chalk] trait_datum Base
[DEBUG hir_ty::traits::chalk] trait Base = Name(Text("Base"))
[DEBUG hir_ty::traits::chalk] impls_for_trait Base
[DEBUG hir_ty::traits::chalk] impls_for_trait returned 0 impls
[DEBUG hir_ty::traits] solve(UCanonical { canonical: Canonical { value: InEnvironment { environment: Env([]), goal: Implemented(OpaqueTyId { index: 0 }: Base) }, binders: [] }, universes: 1 }) => Some(Unique(Canonical { value: ConstrainedSubst { subst: [], constraints: [] }, binders: [] }))

and for

trait Base {            
    fn foo(self) -> usize;
}

trait Super : Base {}

fn super1() -> impl Super { loop {} }

fn test() {
    super1().foo();
}

this are the logs

[INFO  hir_ty::traits] trait_solve_query(Implements(impl Super: Base))
[DEBUG hir_ty::traits] solve goal: UCanonical { canonical: Canonical { value: InEnvironment { environment: Env([]), goal: Implemented(SeparatorTraitRef(?)) }, binders: [] }, universes: 1 }
[DEBUG hir_ty::traits::chalk] trait_datum Base
[DEBUG hir_ty::traits::chalk] trait Base = Name(Text("Base"))
[DEBUG hir_ty::traits::chalk] impls_for_trait Base
[DEBUG hir_ty::traits::chalk] impls_for_trait returned 0 impls
[DEBUG hir_ty::traits] solve(UCanonical { canonical: Canonical { value: InEnvironment { environment: Env([]), goal: Implemented(OpaqueTyId { index: 0 }: Base) }, binders: [] }, universes: 1 }) => None
...

in both cases the call to solve comes from hir_ty::method_resolution::iterate_trait_method_candidates when iterating the foo item in Base.

And if I'm understanding correctly means that chalk is finding a solution for Implements(impl Base: Base) but not for Implements(impl Super: Base). So maybe the issue is in chalk, or in the mapping to chalk?

@flodiebold
Copy link
Member

Yes, that's likely. It'd be helpful to create a Chalk test and report a bug there. I wonder though, I feel like that's been implemented before...

@vemoo
Copy link
Contributor Author

vemoo commented Jan 20, 2021

I found rust-lang/chalk#335, which makes me think that impl Trait may not be fully supported yet.

There must be some special handling for Implements(impl Base: Base) but I've not be able to find out where.

@detrumi
Copy link
Member

detrumi commented Jan 30, 2021

Thanks for reproducing this, it makes finding the underlying issue quite easy ❤️

Chalk indeed doesn't handle super traits for opaque types yet, filed rust-lang/chalk#677

@detrumi
Copy link
Member

detrumi commented Feb 17, 2021

I've fixed chalk#677, so this should be fixed once the next chalk release lands in rust-analyzer.

lnicola added a commit to lnicola/rust-analyzer that referenced this issue Feb 21, 2021
@lnicola lnicola mentioned this issue Feb 21, 2021
bors bot added a commit that referenced this issue Feb 21, 2021
7739: Bump deps r=lnicola a=lnicola

Closes #7273

bors r+

Co-authored-by: Laurențiu Nicola <[email protected]>
lnicola added a commit to lnicola/rust-analyzer that referenced this issue Feb 21, 2021
bors bot added a commit that referenced this issue Feb 21, 2021
7739: Bump deps r=lnicola a=lnicola

Closes #7273

bors r+

Co-authored-by: Laurențiu Nicola <[email protected]>
@bors bors bot closed this as completed in 14de9e5 Feb 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ty type system / type inference / traits / method resolution S-actionable Someone could pick this issue up and work on it right now
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants