I was thinking a while about issue #24066 the initial post of the issue mentions the following example:
trait Trait<T> {
fn call_me(&self, x: T) {}
}
impl<T> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn bug<T: Trait<U>, U>(x: T) {
x.call_me(1u32);
//~^ ERROR mismatched types
}
My personal gut feeling tells me that it's perfectly fine how it works today, because otherwise the body could further restrict the constraints from the function signature which would probably lead to even more confusion on the call site. I think a caller should be confident that his input will be accepted if it satisfies the constraints of the function signature.
But it seems to me that most people see this behaviour as an inadequacy oder even a bug therefore I would like to ask if I'm maybe completely wrong with my assessment?
This wouldn't be allowed even when the bug is fixed. The function in the example does not have any unstated requirements on T that could fail at the call site; rather, it would be using the fact that T: Trait<u32> for allT. The bug is that the bounds on the function currently hide that true fact from the body.
Nothing is newly “mandatory”. Consider this modified example:
trait Trait<T> {
fn call_me(&self, x: T) {}
}
impl<T> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn fine<T>(x: T) {
x.call_me(1u32);
}
fn bug<T: Trait<U>, U>(x: T) {
x.call_me(1u32);
}
The function fine()does compile, because the implementation it uses always exists regardless of T. The function bug() does not, even though it does the same thing, because the implementation is hidden by the bound on T even though it still exists. That doesn’t make sense: bug() shouldn't be able to do less just because it has more bounds than fine(). (Consider that bug() can work around the bug by calling fine().)
While it can be considered a bug, the precedence of ParamEnv is also something that code can rely on.
// If the ParamEnv is not prefered, the call to `.into` is ambiguous
fn method_selection<T: Into<u64>>(x: T) -> Option<u32> {
x.into().try_into().ok()
}
While I do think the behavior could be better, getting there will probably require some breakage along the way.
Incidentally, candidate selection is more complicated than "ParamEnv wins". Here are some breadcrumbs for anyone interested.