Type inference incorrectly selects specialized instance of type parameter

68c500a
Opened by Steven Fackler at 2024-04-19 14:20:07
#![feature(specialization)]
use std::vec;

struct Foo<T>(T);

impl<T> Foo<T> {
    fn foo<I: IntoIterator<Item = T>>(it: I) -> Foo<T> {
        <Self as SpecExtend<_, _>>::from_iter(it.into_iter())
    }
}

trait SpecExtend<T, I> {
    fn from_iter(iter: I) -> Self;
}

impl<T, I> SpecExtend<T, I> for Foo<T>
    where I: Iterator<Item = T>
{
    default fn from_iter(iter: I) -> Self {
        panic!()
    }

}

impl<T> SpecExtend<T, vec::IntoIter<T>> for Foo<T> {
    fn from_iter(iter: vec::IntoIter<T>) -> Self {
        panic!()
    }
}

fn main() {}
rustc 1.17.0-nightly (134c4a0f0 2017-03-20)
error[E0308]: mismatched types
 --> <anon>:8:47
  |
8 |         <Self as SpecExtend<_, _>>::from_iter(it.into_iter())
  |                                               ^^^^^^^^^^^^^^ expected struct `std::vec::IntoIter`, found associated type
  |
  = note: expected type `std::vec::IntoIter<T>`
             found type `<I as std::iter::IntoIterator>::IntoIter`

error: aborting due to previous error

@aturon

  1. Minimized from https://github.com/sfackler/rust/commit/c674b23b29e0281a95fe9998124fcb477e3d6bc5

    Steven Fackler at 2017-03-21 22:53:41

  2. @withoutboats pointed out that if you adjust line 8 to <Self as SpecExtend<T, I::IntoIter>>::from_iter(it.into_iter()), it compiles properly, so it looks like a type inference issue.

    Steven Fackler at 2017-03-21 23:00:09

  3. Here's another repro (I think) that I made before I came across this issue:

    #![feature(specialization)]
    
    use std::marker::PhantomData;
    use std::ops::{Deref, DerefMut};
    
    trait Tag {}
    struct TagA;
    struct TagB;
    struct TagC;
    impl Tag for TagA {}
    impl Tag for TagB {}
    impl Tag for TagC {}
    
    struct Foo<T: Tag> { data: [i32; 8], _pd: PhantomData<T> }
    
    impl<T: Tag> Deref for Foo<T> {
        type Target = [i32];
        default fn deref(&self) -> &Self::Target { &self.data[..7] }
    }
    impl<T: Tag> DerefMut for Foo<T> {
        default fn deref_mut(&mut self) -> &mut Self::Target { &mut self.data[..7] }
    }
    
    impl Deref for Foo<TagA> {
        fn deref(&self) -> &Self::Target { &self.data[1..] }
    }
    impl DerefMut for Foo<TagA> {
        fn deref_mut(&mut self) -> &mut Self::Target { &mut self.data[1..] }
    }
    
    trait FooBuilder: Sized
      + DerefMut<Target = [i32]> // ***
    {
        fn new_foo(data: [i32; 8]) -> Self;
    }
    impl<T: Tag> FooBuilder for Foo<T> {
        fn new_foo(data: [i32; 8]) -> Self { Self { data, _pd: PhantomData } }
    }
    
    struct Bar<T: Tag>(Foo<T>);
    impl<T: Tag> Bar<T> {
        fn new(data: [i32; 8]) -> Self { Bar(Foo::new_foo(data)) } // !!!
    }
    
    fn main() {
        let data = [0i32; 8];
        let _: Bar<TagA> = Bar::new(data);
        let _: Bar<TagB> = Bar::new(data);
        let _: Bar<TagC> = Bar::new(data);
    }
    

    Commenting the line marked *** compiles, but leaving it uncommented yields (rustc 1.19.0-nightly (03abb1bd7 2017-06-13)):

    error[E0308]: mismatched types
      --> src/main.rs:42:42
       |
    42 |     fn new(data: [i32; 8]) -> Self { Bar(Foo::new_foo(data)) } // !!!
       |                                          ^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `TagA`
       |
       = note: expected type `Foo<T>`
                  found type `Foo<TagA>`
    

    Though it's redundant given the method's return type, the workaround is to change the expression involving the call to new_foo to Bar(Foo::<T>::new_foo(data)) (credit to @abonander).

    Dodheim at 2017-06-14 12:57:47