Some thoughts on type inference with subtyping coercion.
In current Rust:
trait Trait {}
impl Trait for fn(&'static u8) {}
fn takes_concrete_supertype(_: fn(&'static u8)) {}
fn takes_impl_trait(_: impl Trait) {}
fn to_ptr<'a>(_: &'a u8) {}
fn main() {
let fn_ptr: for<'a> fn(&'a u8) = to_ptr;
// This works fine. Rust realizes it needs to convert `fn_ptr`
// from a `for<'a> fn(&'a u8)` to the supertype `fn(&'static u8)`.
takes_concrete_supertype(fn_ptr);
// This doesn't compile today :(
// Rust doesn't manage to coerce `fn_ptr` to supertype.
takes_impl_trait(fn_ptr);
}
If a u32 in 1.. is to be as useful as a u32, code like the last line in the above example needs to compile. For Rust to be able to infer what to do, there needs to be a unique "best" supertype to coerce to to satisfy trait bounds. I would propose something like the following rules:
For any type
Tand traitTrait, one of the following two cases must hold:
TimplementsTrait. In this case, if any ofT's supertypes or subtypes also implementTrait, those impls must come from the sameimplblock as the impl ofTraitforT.Additionally, ifIf any of these rules is violated, it's a coherence error.Uis a supertype ofT, andUdoes not implementTrait, then none ofU's supertypes may implementTrait.
Tdoes not implementTrait. In this case, consider the set{U}of all supertypes ofTthat do implementTrait. One of the following must hold (otherwise, it's a coherence error):a) The described set
{U}is emptyb) All of the elements of
{U}share a common subtypeS, that itself implementsTrait, and is a member of{U}.In case 2b, when a value of type
Tis provided but a value of a type implementingTraitis expected, the value of typeTis coerced toS.
I don't believe it's possible to write impls that would conflict with these rules in Rust today, without triggering the coherence_leak_check future-compat lint. But maybe I am wrong about that, please tell me if so!