Skip to content

Unrecognized associated type bound on another associated type #24159

Description

@kvark
trait Bar<T> { fn dummy(&self); }
trait Car<T> { fn dummy(&self); }

trait Foo {
    type A;
    type B: Bar<Self::A>;
    type C: Car<Self::A>;

    fn get_b(&self) -> &Self::B;
}

fn test_bar<A, B: Bar<A>>(_: &B) {}

fn test<A, F: Foo<A=A>>(f: &F) {
    test_bar(f.get_b());
}

Gives me:

<anon>:15:16: 15:23 error: the trait `Bar<A>` is not implemented for the type `<F as Foo>::B` [E0277]
<anon>:15     test_bar(f.get_b());
                         ^~~~~~~
<anon>:15:16: 15:23 error: the trait `Car<A>` is not implemented for the type `<F as Foo>::C` [E0277]
<anon>:15     test_bar(f.get_b());
                         ^~~~~~~
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

There is a workaround:

fn test<A, B: Bar<A>, C: Car<A>, F: Foo<A=A, B=B, C=C>>(f: &F) {
    test_bar(f.get_b());
}

But it's ugly and should not be necessary.

We hit this problem a lot with gfx::Device, and I'd like to see cleaner use of it without explicit CommandBuffer bounds that we use as a workaround.

Activity

  1. apasel422 commented on Apr 7, 2015

    @apasel422
    Contributor

    This may be related to #23481.

  2. Mark-Simulacrum commented on May 16, 2017

    @Mark-Simulacrum
    Member

    Edited original post to remove PhantomFn bounds, they're no longer needed. Issue still reproduces:

    error[E0277]: the trait bound `<F as Foo>::B: Bar<A>` is not satisfied
      --> test.rs:14:1
       |
    14 | / fn test<A, F: Foo<A=A>>(f: &F) {
    15 | |     test_bar(f.get_b());
    16 | | }
       | |_^ the trait `Bar<A>` is not implemented for `<F as Foo>::B`
       |
       = help: consider adding a `where <F as Foo>::B: Bar<A>` bound
       = note: required by `Foo`
    
    error[E0277]: the trait bound `<F as Foo>::C: Car<A>` is not satisfied
      --> test.rs:14:1
       |
    14 | / fn test<A, F: Foo<A=A>>(f: &F) {
    15 | |     test_bar(f.get_b());
    16 | | }
       | |_^ the trait `Car<A>` is not implemented for `<F as Foo>::C`
       |
       = help: consider adding a `where <F as Foo>::C: Car<A>` bound
       = note: required by `Foo`
    
    error: aborting due to 2 previous errors
    
  3. added
    C-bugCategory: This is a bug.
    and removed
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    on Jul 22, 2017
  4. rkarp commented on Dec 10, 2017

    @rkarp
    Contributor

    A lot of related issues for this have been opened over time.

    Duplicates: #37808, #39532, #40093, #48674, #54375, #54844

    Probably same underlying cause: #32722, #37883, #38917, #43475, #47897, #54149

    There's also a related discussion on the Rust forums: https://users.rust-lang.org/t/bounds-on-associated-types-enforced-too-soon/12948

  5. nikomatsakis commented on Mar 13, 2018

    @nikomatsakis
    Contributor

    Mildly simplified:

    trait Bar<T> { fn dummy(&self); }
    
    trait Foo {
        type A;
        type B: Bar<Self::A>;
    
        fn get_b(&self) -> &Self::B;
    }
    
    fn test_bar<A, B: Bar<A>>(_: &B) {}
    
    fn test<A, F: Foo<A=A>>(f: &F) {
        test_bar(f.get_b());
    }
    
    fn main() { }

    So, probably the fix is "just" that we need to do some normalization somewhere or other. My preference is to fix this by moving over to lazy normalization and chalk, which will do away with these sorts of shortcomings for good. But it's probably worth looking to see if this can be fixed a touch more rapidly by adding a normalization in the right place. I'll try to do that in a bit and mentor/open a fix if it seems reasonable.

  6. malbarbo commented on Jun 20, 2018

    @malbarbo
    Contributor

    Any progress here @nikomatsakis?

  7. ZerothLaw commented on Aug 17, 2018

    @ZerothLaw

    Looks like the issue is known and mentioned at this line: https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/check/compare_method.rs#L252

    This indicates this may be the area to check in when working on this? Still working on understanding a lot of this code, so I'm not sure.

  8. daboross commented on Aug 26, 2018

    @daboross
    Contributor

    Ran into this as well. Here's my minimal test case:

    trait Bar<T> {}
    trait Caz {
        type A;
        type B: Bar<Self::A>;
    }
    
    fn test<T, U>() where T: Caz, U: Caz<A = T::A> {}
    error[E0277]: the trait bound `<U as Caz>::B: Bar<<T as Caz>::A>` is not satisfied
     --> src/main.rs:7:1
      |
    7 | fn test<T, U>() where T: Caz, U: Caz<A = T::A> {}
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<<T as Caz>::A>` is not implemented for `<U as Caz>::B`
      |
      = help: consider adding a `where <U as Caz>::B: Bar<<T as Caz>::A>` bound
    note: required by `Caz`
     --> src/main.rs:2:1
      |
    2 | trait Caz {
      | ^^^^^^^^^
    
  9. elidupree commented on Mar 26, 2019

    @elidupree

    I've just run into this issue in one of my own projects, and reduced it to a test case that's even simpler than the last one:

    trait Bar<T> {}
    trait Caz {
        type A;
        type B: Bar<Self::A>;
    }
    fn test<T: Caz<A = ()>>() {}

    Although it compiles successfully if you add the bizarrely-tautological-looking where T::B: Bar<T::A>.

    For now, I'm stuck with adding the where clause, but that's putting a LOT of boilerplate in my code (the actual where clause is 71 characters long and appears in dozens of places, including being de-facto required in code that uses my library, not just inside the library itself). Is there any progress on this?

  10. added 2 commits that reference this issue on Oct 19, 2019
  11. estebank commented on Nov 5, 2019

    @estebank
    Contributor

    Current output:

    error[E0277]: the trait bound `<F as Foo>::B: Bar<A>` is not satisfied
      --> src/lib.rs:14:1
       |
    4  |   trait Foo {
       |   --------- required by `Foo`
    ...
    14 |   fn test<A, F: Foo<A=A>>(f: &F) {
       |   ^                             - help: consider further restricting the associated type: `where <F as Foo>::B: Bar<A>`
       |  _|
       | |
    15 | |     test_bar(f.get_b());
    16 | | }
       | |_^ the trait `Bar<A>` is not implemented for `<F as Foo>::B`
    
    error[E0277]: the trait bound `<F as Foo>::C: Car<A>` is not satisfied
      --> src/lib.rs:14:1
       |
    4  |   trait Foo {
       |   --------- required by `Foo`
    ...
    14 |   fn test<A, F: Foo<A=A>>(f: &F) {
       |   ^                             - help: consider further restricting the associated type: `where <F as Foo>::C: Car<A>`
       |  _|
       | |
    15 | |     test_bar(f.get_b());
    16 | | }
       | |_^ the trait `Car<A>` is not implemented for `<F as Foo>::C`
    

    Applying the suggestions makes the code compile.

    Edit: updated output:

    error[E0277]: the trait bound `<F as Foo>::B: Bar<A>` is not satisfied
      --> src/lib.rs:14:15
       |
    4  | trait Foo {
       |       --- required by a bound in this
    5  |     type A;
    6  |     type B: Bar<Self::A>;
       |             ------------ required by this bound in `Foo`
    ...
    14 | fn test<A, F: Foo<A=A>>(f: &F) {
       |               ^^^^^^^^ the trait `Bar<A>` is not implemented for `<F as Foo>::B`
       |
    help: consider further restricting the associated type
       |
    14 | fn test<A, F: Foo<A=A>>(f: &F) where <F as Foo>::B: Bar<A> {
       |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0277]: the trait bound `<F as Foo>::C: Car<A>` is not satisfied
      --> src/lib.rs:14:15
       |
    4  | trait Foo {
       |       --- required by a bound in this
    ...
    7  |     type C: Car<Self::A>;
       |             ------------ required by this bound in `Foo`
    ...
    14 | fn test<A, F: Foo<A=A>>(f: &F) {
       |               ^^^^^^^^ the trait `Car<A>` is not implemented for `<F as Foo>::C`
       |
    help: consider further restricting the associated type
       |
    14 | fn test<A, F: Foo<A=A>>(f: &F) where <F as Foo>::C: Car<A> {
       |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

    Niko's minimized version:

    error[E0277]: the trait bound `<F as Foo>::B: Bar<A>` is not satisfied
      --> src/main.rs:12:1
       |
    3  |   trait Foo {
       |   --------- required by `Foo`
    ...
    12 |   fn test<A, F: Foo<A=A>>(f: &F) {
       |   ^                             - help: consider further restricting the associated type: `where <F as Foo>::B: Bar<A>`
       |  _|
       | |
    13 | |     test_bar(f.get_b());
    14 | | }
       | |_^ the trait `Bar<A>` is not implemented for `<F as Foo>::B`
    

    @daboross' case:

    error[E0277]: the trait bound `<U as Caz>::B: Bar<<T as Caz>::A>` is not satisfied
     --> src/lib.rs:7:1
      |
    2 | trait Caz {
      | --------- required by `Caz`
    ...
    7 | fn test<T, U>() where T: Caz, U: Caz<A = T::A> {}
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^
      | |                                             |
      | |                                             help: consider further restricting the associated type: `, <U as Caz>::B: Bar<<T as Caz>::A>`
      | the trait `Bar<<T as Caz>::A>` is not implemented for `<U as Caz>::B`
    

    Update: this case now compiles.


    In call cases applying the suggestions fulfill the requirements.

  12. dignifiedquire commented on Jun 14, 2020

    @dignifiedquire

    @nikomatsakis any update on this? Is there anything I can do help with this? I'd be happy and try to help where I can, as this is blocking a lot of nice usages of typenum for me at the moment.

  13. daboross commented on Jun 14, 2020

    @daboross
    Contributor

    It looks like this compiles under -Z chalk, or at least the bounds compile. The body function complains about inference, but I'm inclined to think that's just a chalk bug / missing feature and unrelated to this issue.

    Given that, the solution might just be fully stable chalk integration. If it is, then this issue would fall into the same bucket as features like implied bounds, where there isn't a lot we can do outside of just pushing chalk integration.

  14. nikomatsakis commented on Jun 15, 2020

    @nikomatsakis
    Contributor

    I think my original analysis is still correct. I think the normalizing is missing here:

    let bounds = predicates_of.instantiate(self.tcx(), substs);

    After we invoke instantiate, we should also be normalizing, probably kind of like this

    let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } =
    ensure_sufficient_stack(|| {
    project::normalize_with_depth(
    self,
    obligation.param_env,
    obligation.cause.clone(),
    obligation.recursion_depth + 1,
    &impl_trait_ref,
    )

    If Chalk solves this, it is probably because of lazy normalization.

  15. estebank commented on Feb 2, 2021

    @estebank
    Contributor

    All of the cases I mentioned now compile. I believe this can be closed by adding tests.

  16. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on Feb 2, 2021
  17. added 2 commits that reference this issue on Feb 2, 2021
    5f1616d
    81c64b3
  18. added a commit that references this issue on May 26, 2021
  19. added 3 commits that reference this issue on May 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)C-bugCategory: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.P-mediumMedium priorityT-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions