Today, I have run into an error most magical.
It turns out that if you write f64: Mul<C>
as a trait bound on a function, then rustc forgets that f64
s can be multiplied with f64
s.
use ::std::ops::Mul;
fn foo<C>(max: C) -> <f64 as Mul<C>>::Output
where f64: Mul<C>,
{
1.0f64 * 3.0f64 * max
}
fn main() { }
Compiling wtfbounds v0.1.0 (file:///home/lampam/cpp/throwaway/wtfbounds)
error[E0308]: mismatched types
--> src/main.rs:6:14
|
6 | 1.0f64 * 3.0f64 * max
| ^^^^^^ expected type parameter, found f64
|
= note: expected type `C`
found type `f64`
error[E0369]: binary operation `*` cannot be applied to type `<f64 as std::ops::Mul<C>>::Output`
--> src/main.rs:6:5
|
6 | 1.0f64 * 3.0f64 * max
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::ops::Mul` might be missing for `<f64 as std::ops::Mul<C>>::Output`
error: aborting due to 2 previous errors
error: Could not compile `wtfbounds`.
To work around this, it is insufficient even to add a f64: Mul<f64, Output=f64>
bound; you must write out the full UFCS <f64 as Mul<f64>>::mul(1.0, 3.0)
. It has behaved like this since rust 1.0.
Anyways, since there's no way on Earth that an issue like this has never been discovered and reported before, I've been idly searching the issue tracker with various vague keywords like "bound," but haven't found anything that sounds like it could be related, yet.
...it's gotta be there, right?
Edit: I "minimized" my example by adding f64
type suffixes, to show that the issue does not have to do with untyped-float-literal magicks
Edit: It is not specific to binops (the traits or the sugar)...
Edit: ...nor do the type arguments to the trait even need to be capable of unifying. All you need is an impl with a generic type argument to the trait, after which all monomorphic impls are forgotten (and outright ignored if you try to explicitly specify them)
use ::std::ops::Mul;
trait Trait<A>: Sized {
fn lel(self, a: A) -> Self { self}
}
struct Foo;
struct Bar<C>(C);
impl Trait<Foo> for f64 {}
impl<C> Trait<Bar<C>> for f64 {}
fn foo<C>() -> f64
where
f64: Trait<Foo>, // <- rustc will completely forget about this...
f64: Trait<Bar<C>>, // <- ...so long as this is generic
{
(1.0f64).lel(Foo).lel({ let b: Bar<C> = panic!(); b })
}
fn main() { }