Type inference incorrectly selects specialized instance of type parameter
#![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
Minimized from https://github.com/sfackler/rust/commit/c674b23b29e0281a95fe9998124fcb477e3d6bc5
Steven Fackler at 2017-03-21 22:53:41
@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
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_footoBar(Foo::<T>::new_foo(data))(credit to @abonander).Dodheim at 2017-06-14 12:57:47