Poor interaction between int fallback and other flow of type information
A reduced example:
use std::ops::Shl;
struct Foo<T>(T);
impl<T> Shl<usize> for Foo<T> {
type Output = Foo<T>;
fn shl(self, _: usize) -> Foo<T> { self }
}
impl<T> Shl<i32> for Foo<T> {
type Output = Foo<T>;
fn shl(self, _: i32) -> Foo<T> { self }
}
fn main() {
let _ = Foo(0u32) << 2; // works fine
let _ = (Foo(0u32) << 2).0; // does not work
let x = Foo(0u32) << 2; // does not work
let _ = x.0;
let x: Foo<u32> = Foo(0u32) << 2; // works
let _ = x.0;
}
generates the following error:
<anon>:18:13: 18:31 error: the type of this value must be known in this context
<anon>:18 let _ = (Foo(0u32) << 2).0; // does not work
^~~~~~~~~~~~~~~~~~
<anon>:21:13: 21:16 error: the type of this value must be known in this context
<anon>:21 let _ = x.0;
^~~
I suspect what is happening here is that the fallback isn't being triggered early enough -- in particular, before the projection is generating the error. Note that the same problem occurs with a normal struct.
(This may be one reason that Shl/Shr are only implemented on usize for Wrapping<T>.)
cc @nikomatsakis
Aaron Turon at 2015-03-20 04:56:18
Another example, with the new inherent methods:
fn main() { let _ = 32_u8.count_zeros(); // works let _ = 32.count_zeros(); // does not work }Aaron Turon at 2015-03-20 07:41:30
cc me
Felix S Klock II at 2015-04-01 07:56:30
These examples so far seem to be "working as designed". That is, we do not do fallback early in the cycle, but only after we've gotten to the end, and we currently do require some type-directed actions (like field access) to have resolved types, for better or worse. We could do work on the type system to make it less... eager. In other words, it should generate deferred constraints and try to solve them. That is sort of the general fix to this problem I guess.
Niko Matsakis at 2015-04-04 17:14:43
This code says
let x = 1.; let y = x.min(2.); // error: type `_` does not implement any method in scope named `min`The last line puzzled me the most here. I went about to tell Rust a type to use, still no dice.
[1., 2., 3.].iter().fold(1./0., |acc, &x| f32::min(acc, x)); // OK [1., 2., 3.].iter().fold(1./0., |acc, &x| acc.min(x)); // error: type `_` does not implement any method in scope named `min` [1.0_f32, 2., 3.].iter().fold(1./0., |acc, &x| acc.min(x)); // error: type `_` does not implement any method in scope named `min`bluss at 2015-05-03 12:46:02
@nikomatsakis The interesting thing about
32.count_zeros()is that it would totally work ifcount_zeroescame from a trait because we coalesce those into one signature (the trait's view of the method).Couldn't we do something similar for an inherent method present on multiple integer types?
I guess we would need a way to represent
<_>::count_zeroes, not fully selected, which we currently don't have (trait methods are easy because they have HIR nodes andDefIds).Eduard-Mihai Burtescu at 2016-01-05 22:26:52
On Tue, Jan 05, 2016 at 02:27:31PM -0800, Eduard-Mihai Burtescu wrote:
@nikomatsakis The interesting thing about
32.count_zeros()is that it would totally work ifcount_zeroescame from a trait because we coalesce those into one signature (the trait's view of the method).Couldn't we do something similar for an inherent method present on multiple integer types?
Possibly, but it'd be very hard-coded.
I guess we would need a way to represent
<_>::count_zeroes, not fully selected, which we currently don't have (trait methods are easy because they have HIR nodes andDefIds).Yes. I suspect this is eminently doable. I've been thinking about it in the context of wanting to do a ground-up rewrite of typeck, but of course that's an ambitious (and perhaps foolhardy) way of going about things, perhaps I should look to see if we could do something more targeted. The current setup is certainly a common source of annoyance.
Niko Matsakis at 2016-01-06 15:17:46
Triage: no change
Steve Klabnik at 2018-09-24 18:11:25