Skip to content

Compiler incorrectly (and inconsistently) finds Iterator methods on i32 #84495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
aDotInTheVoid opened this issue Apr 23, 2021 · 5 comments · Fixed by #94867
Closed

Compiler incorrectly (and inconsistently) finds Iterator methods on i32 #84495

aDotInTheVoid opened this issue Apr 23, 2021 · 5 comments · Fixed by #94867
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-trait-system Area: Trait system D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@aDotInTheVoid
Copy link
Member

Given the following code: playground

fn main() {
    let x: i32 = 1;
    println!("{:?}", x.count())
}

The current output is:

error[E0599]: the method `count` exists for type `i32`, but its trait bounds were not satisfied
 --> src/main.rs:3:24
  |
3 |     println!("{:?}", x.count())
  |                        ^^^^^ method cannot be called on `i32` due to unsatisfied trait bounds
  |
  = note: the following trait bounds were not satisfied:
          `i32: Iterator`
          which is required by `&mut i32: Iterator`

error: aborting due to previous error

Ideally the output should look like:

error[E0599]: the method `count` does not exist for type `i32`

For some reason, this doesn't happen for all traits in std. Eg

fn main() {
    let x: i32 = 1;
    println!("{:?}", x.write(&[1, 2, 3]))
}

Just gives

error[E0599]: no method named `write` found for type `i32` in the current scope
 --> src/main.rs:3:24
  |
3 |     println!("{:?}", x.write(&[1, 2, 3]))
  |                        ^^^^^ method not found in `i32`

error: aborting due to previous error

playground

Version: 1.53.0-nightly (2021-04-22 7f4afdf)

@aDotInTheVoid aDotInTheVoid added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 23, 2021
@tlyu
Copy link
Contributor

tlyu commented Apr 23, 2021

Some discussion on Discord suggests that

impl<I: Iterator + ?Sized> Iterator for &mut I

src

might be a culprit.

Trying with the variant

fn main() {
    let x: i32 = 1;
    println!("{:?}", x.next())
}

playground

produces

error[E0599]: no method named `next` found for type `i32` in the current scope
 --> src/main.rs:3:24
  |
3 |     println!("{:?}", x.next())
  |                        ^^^^ method not found in `i32`

error: aborting due to previous error

as expected.

So it looks like the trait bound I: Iterator + ?Sized is being honored for the search for the next method, which is defined in the impl<…> Iterator for &mut I block, but not for the search for the count method, which is defined in trait Iterator definition.

@tlyu
Copy link
Contributor

tlyu commented Apr 24, 2021

I suggest retitling this to "Compiler incorrectly (and inconsistently) finds Iterator methods on i32".

@estebank estebank added A-trait-system Area: Trait system D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. labels Apr 25, 2021
@aDotInTheVoid aDotInTheVoid changed the title Compiller sugests Iterator bound needed when .count() called on i32 Compiler incorrectly (and inconsistently) finds Iterator methods on i32 Apr 25, 2021
@tlyu
Copy link
Contributor

tlyu commented Apr 29, 2021

I'd also like to suggest adding D-newcomer-roadblock because I've seen several other newcomers on Discord run into this confusing diagnostic.

@tlyu
Copy link
Contributor

tlyu commented May 9, 2021

I tried to replicate the incorrect diagnostic with a more minimal case than Iterator. It looks like it depends on the borrow mode of the receiver. This code (playground):

trait Trait {
    // similar to Iterator::next
    fn f1(&self);
    // similar to Iterator::count
    fn f2(self) where Self: Sized {}
    // similar to Iterator::size_hint
    fn f3(&self) {}
}
// similar to impl<T: Iterator + ?Sized> Iterator for &mut T
impl<T: Trait> Trait for &T
{
    fn f1(&self) { (**self).f1() }
}
fn main() {
    1i32.f1();
    1i32.f2();
    1i32.f3();
}

produces these errors:

   Compiling playground v0.0.1 (/playground)
error[E0599]: no method named `f1` found for type `i32` in the current scope
  --> src/lib.rs:15:10
   |
15 |     1i32.f1();
   |          ^^ method not found in `i32`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f1`, perhaps you need to implement it
  --> src/lib.rs:1:1
   |
1  | trait Trait {
   | ^^^^^^^^^^^

error[E0599]: the method `f2` exists for type `i32`, but its trait bounds were not satisfied
  --> src/lib.rs:16:10
   |
16 |     1i32.f2();
   |          ^^ method cannot be called on `i32` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `i32: Trait`
           which is required by `&i32: Trait`
   = help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f2`, perhaps you need to implement it
  --> src/lib.rs:1:1
   |
1  | trait Trait {
   | ^^^^^^^^^^^

error[E0599]: no method named `f3` found for type `i32` in the current scope
  --> src/lib.rs:17:10
   |
17 |     1i32.f3();
   |          ^^ method not found in `i32`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f3`, perhaps you need to implement it
  --> src/lib.rs:1:1
   |
1  | trait Trait {
   | ^^^^^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0599`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Trait::f1() is a required trait method that takes a ref to self, similar to Iterator::next() (the mut doesn't seem necessary to exhibit these diagnostics).
Trait::f2() is a default trait method that moves self, similar to most of the Iterator methods. It seems like a default trait method that moves self is necessary to trigger this incorrect diagnostic.
Trait::f3() is a default trait method that takes a ref to self, similar to Iterator::size_hint().

I've built a debugging version of the compiler and I'm trying to understand the debug logs for the trait and typechecking systems.

@tlyu
Copy link
Contributor

tlyu commented May 9, 2021

To elaborate on my prior comment: newcomers seem likely to encounter and be confused by this diagnostic. I ran into it myself by trying to call Iterator methods directly on Vec, and I've seen several others be confused by that. I think I've also seen newcomers be confused by trying to call Iterator methods directly on arrays.

@rustbot label +D-newcomer-roadblock

@rustbot rustbot added the D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. label May 9, 2021
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 12, 2022
…ssion-test, r=Dylan-DPC

Add regression test for `<i32 as Iterator>::count`

Closes rust-lang#84495
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 12, 2022
…ssion-test, r=Dylan-DPC

Add regression test for `<i32 as Iterator>::count`

Closes rust-lang#84495
@bors bors closed this as completed in ce6f987 Mar 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-trait-system Area: Trait system D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants