Specialization influnces inference

9aed485
Opened by Ariel Ben-Yehuda at 2024-12-21 05:02:39

STR

#![feature(specialization)]

struct My<T>(T);

trait Conv<T> {
    fn conv(self) -> T;
}

impl<T> Conv<T> for My<T> {
    default fn conv(self) -> T { self.0 }
}

#[cfg(broken)]
impl Conv<u32> for My<u32> {
    default fn conv(self) -> u32 { self.0 }
}

fn main() {
    let x = My(0);
    x.conv() + 0i32;
}

Expected Result

Adding a specialized impl will not affect type inference.

Actual Result

After adding the specialized impl, inference is guided to take it:

error[E0308]: mismatched types
  --> <anon>:20:16
   |
20 |     x.conv() + 0i32;
   |                ^^^^ expected u32, found i32

error[E0277]: the trait bound `u32: std::ops::Add<i32>` is not satisfied
  --> <anon>:20:5
   |
20 |     x.conv() + 0i32;
   |     ^^^^^^^^^^^^^^^ trait `u32: std::ops::Add<i32>` not satisfied
   |
   = help: the following implementations were found:
   = help:   <u32 as std::ops::Add>
   = help:   <&'a u32 as std::ops::Add<u32>>
   = help:   <u32 as std::ops::Add<&'a u32>>
   = help:   <&'b u32 as std::ops::Add<&'a u32>>

error: aborting due to 2 previous errors

cc @aturon @nikomatsakis

  1. Another example:

    #![feature(specialization)]
    use std::marker::PhantomData;
    
    trait Trait {
        type A;
        type B;
    
        fn foo(&self, a: Self::A, b: Self::B);
    }
    
    struct Foo<A, B> {
        a: PhantomData<A>,
        b: PhantomData<B>,
    }
    
    impl<A, B> Foo<A, B> {
        fn new() -> Self {
            Foo {
                a: PhantomData,
                b: PhantomData,
            }
        }
    }
    
    impl<A, B> Trait for Foo<A, B> {
        type A = A;
        type B = B;
        default fn foo(&self, _: A, _: B) {
            println!("default impl");
        }
    }
    
    // Specialized
    impl<A, B: Eq> Trait for Foo<A, B> {
        fn foo(&self, _: A, _: B) {
            println!("specialized");
        }
    }
    
    fn main() {
        let a = "a";
        let b = "b";
    
        let f = Foo::new(); // Need to specify concrete type here to compile
        f.foo(a, b);
    }
    

    Mark Rousskov at 2017-05-13 04:19:38

  2. I don't have an sscce, but I produced this small patch for the current master branch of rust, which breaks type inference regarding specialization of iterators. The interesting thing is, if I don't directly specialize the iterator, but instead create a new trait which I specialize, it compiles just fine. The patch for this can be found here.

    Jaro Fietz at 2017-12-20 22:12:51

  3. I have a patch specializing SliceConcatExt<T> for T: Copy which causes type inference to fail on a call to concat inside rustc. It happens even if I don't actually specialize anything. If I delete the empty impl block and leave the general impl block as-is, including the defaults, it works. Just the existence of the specializing impl block is enough to break it.

    Edit: I've misinterpreted the issue. Explanation at the end

    // general block
    impl<T: Clone, V: Borrow<[T]>> SliceConcatExt<T> for [V] {
        type Output = Vec<T>;
        default fn concat(&self) -> Vec<T> { ... }
        /* other methods */
    }
    
    // specializing block
    impl<T: Copy, V: Borrow<[T]>> SliceConcatExt<T> for [V] {
        /* empty */
    }
    

    The call where inference breaks is src/librustc_typeck/collect.rs Line 1299

    The error is:

    type annotations required: cannot resolve `<[&[rustc::ty::Predicate<'_>]] as std::slice::SliceConcatExt<_>>::Output == std::vec::Vec<rustc::ty::Predicate<'_>>`
    

    ~Previously only one impl of SliceConcatExt<T> could produce a Vec<_>. With the specialization it's two, but one is a subset of the other.~

    Edit: Actually, the issue has not to do with the associated type but with the generic T. concat has no information about T. join has a T separator parameter and does not have the inference problem but concat could work for any T for which V: Borrow<[T]>. That genericity might require one to add a turbofish but in that case above in the stdlib, it was not necessary. The specialization should not change that.

    Emerentius at 2018-05-12 23:29:34