Poor interaction between int fallback and other flow of type information

40d5e87
Opened by Aaron Turon at 2023-04-05 17:36:40

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>.)

  1. cc @nikomatsakis

    Aaron Turon at 2015-03-20 04:56:18

  2. 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

  3. cc me

    Felix S Klock II at 2015-04-01 07:56:30

  4. 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

  5. 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

  6. @nikomatsakis The interesting thing about 32.count_zeros() is that it would totally work if count_zeroes came 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 and DefIds).

    Eduard-Mihai Burtescu at 2016-01-05 22:26:52

  7. 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 if count_zeroes came 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 and DefIds).

    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

  8. Triage: no change

    Steve Klabnik at 2018-09-24 18:11:25