Specialization: cannot specialize an impl of a local trait when the default impl is a blanket impl bounded by a non-local trait

c4878c9
Opened by bstrie at 2024-04-19 14:11:04

Found this in the Chucklefish AMA thread, in the subthread about working around the lack of Ord on floats.

This code compiles:

#![feature(specialization)]

trait Foo { fn foo(&self); }

impl<T: Ord> Foo for T { default fn foo(&self) {} }

...But adding a specialized impl fails:

#![feature(specialization)]

trait Foo { fn foo(&self); }

impl<T: Ord> Foo for T { default fn foo(&self) {} }

impl Foo for f64 { fn foo(&self) {} }

Error message:

error[E0119]: conflicting implementations of trait `Foo` for type `f64`:
 --> src/main.rs:7:1
  |
5 | impl<T: Ord> Foo for T { default fn foo(&self) {} }
  | --------------------------------------------------- first implementation here
6 | 
7 | impl Foo for f64 { fn foo(&self) {} }
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `f64`
  |
  = note: upstream crates may add new impl of trait `std::cmp::Ord` for type `f64` in future versions

I'm afraid I don't see how the error message is relevant, or how an upstream impl of Ord for f64 could conflict with this impl of the local Foo trait. Is this intended behavior?

The above is a reduction, here's the original example which raises a few other questions:

#![feature(specialization)]

pub trait OrdSubset: PartialOrd<Self> + PartialEq<Self> {
	fn is_outside_order(&self) -> bool;
}

impl<T: Ord> OrdSubset for T {
    default fn is_outside_order(&self) -> bool {
        false
    }
}

// I'd expect this to work
impl OrdSubset for f64 {
	fn is_outside_order(&self) -> bool {
			(*self).is_nan()
	}
}

// not sure about this
impl<'a, A: OrdSubset> OrdSubset for &'a A {
	fn is_outside_order(&self) -> bool {
		(**self).is_outside_order()
	}
}

/*   The next two are basically the same as above, &mut and f64

impl<'a, A: OrdSubset> OrdSubset for &'a mut A {
	fn is_outside_order(&self) -> bool {
		(**self).is_outside_order()
	}
}

impl OrdSubset for f32 {
	fn is_outside_order(&self) -> bool {
		(*self).is_nan()
	}
}
*/
fn main() {
}
  1. Also, if it's determined that the above behavior is intended, then should the error be triggered by the default impl in the first place, given that it appears impossible to specialize?

    bstrie at 2017-10-26 01:09:10

  2. Funny, impl Foo for u64 works! So potential overlap gives an error, but definite overlap is fine.

    Josh Stone at 2017-10-26 01:24:29

  3. It seems that https://github.com/rust-lang/rust/issues/31844#issuecomment-325870682 is the same issue with more genericity sprinkled in. The more specific impl is blocked when it's unclear whether it's a specialization or the only impl in the first place.

    Just like the commenter there I would expect the specific impl to be used either way.

    Edit:
    1st example here looks similar: https://github.com/rust-lang/rust/issues/31844#issuecomment-284235369 and this is @withoutboats's answer https://github.com/rust-lang/rust/issues/31844#issuecomment-284268302 regarding the previous example

    Emerentius at 2017-10-26 01:45:43

  4. This is definitely the "known" behavior right now - in the sense that this is what I would expect to happen, the comment I wrote in the other issue explains why. However, it seems to me that we should be able to make this work by reasoning through "potential specialization" along the same lines that we have an understanding of "potential overlap" today (potential overlap being what triggers an overlap error in the first place).

    EDIT: To be clearer, "potential specialization" as in - "if these were to overlap, would one specialize the other?"

    srrrse at 2017-10-26 04:39:04