Skip to content

Cannot unify associated type parameter in impl Fn with concrete instantiation #73226

Closed
@rphmeier

Description

@rphmeier

Hi, I'm running into a strange behavior where the compiler has difficulty unifying an associated type in an impl Fn with a concrete type in a certain instantiation of it.

Link to playpen: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1d0d2fd109dd2c691f2b2051b8a2d33a

I have also reproduced the code below.

trait Trait {
    type Num: Copy;
}

struct NumIsU32;
impl Trait for NumIsU32 {
    type Num = u32;
}

struct Foo<T>(std::marker::PhantomData<T>);

impl<T: Trait> Foo<T> {
    fn make_predicate() -> impl Fn(T::Num) -> bool {
        |num| true
    }
    
    fn make_predicate_boxed() -> Box<dyn Fn(T::Num) -> bool> {
        Box::new(|num| true)
    }
}

type FooForYou = Foo<NumIsU32>;

fn main() {
    // doesn't compile: can't reconcile u32 with <NumIsU32 as Trait>::Num
    // let pred = FooForYou::make_predicate();
    // assert!(pred(5u32));
    
    // does compile.
    let pred = FooForYou::make_predicate_boxed();
    assert!(pred(5u32));
}

Here is the error output for the portion that doesn't compile:

error[E0308]: mismatched types
  --> src/main.rs:27:18
   |
27 |     assert!(pred(5u32));
   |                  ^^^^ expected associated type, found `u32`
   |
   = note: expected associated type `<NumIsU32 as Trait>::Num`
                         found type `u32`
   = note: consider constraining the associated type `<NumIsU32 as Trait>::Num` to `u32` or calling a method that returns `<NumIsU32 as Trait>::Num`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

error[E0277]: expected a `std::ops::Fn<(u32,)>` closure, found `impl std::ops::Fn<(<NumIsU32 as Trait>::Num,)>`
  --> src/main.rs:27:13
   |
27 |     assert!(pred(5u32));
   |             ^^^^^^^^^^ expected an `Fn<(u32,)>` closure, found `impl std::ops::Fn<(<NumIsU32 as Trait>::Num,)>`
   |
   = help: the trait `std::ops::Fn<(u32,)>` is not implemented for `impl std::ops::Fn<(<NumIsU32 as Trait>::Num,)>`

error: aborting due to 2 previous errors

What is unclear to me is whether this is an intended restriction of the impl Trait feature or an unintended compiler issue. I believe specialization imposes some artificial restrictions on unifying specialized associated types with their concrete counterparts as a soundness protection. Is the same happening here?

I have also posted this on the users.rust-lang-org forum where it was suggested this is a bug.

Thanks for your help in advance :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-lazy-normalizationArea: Lazy normalization (tracking issue: #60471)C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions