Skip to content
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

Error messages can be ambiguous between types and type parameters #81971

Open
leo60228 opened this issue Feb 10, 2021 · 9 comments
Open

Error messages can be ambiguous between types and type parameters #81971

leo60228 opened this issue Feb 10, 2021 · 9 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix`. C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@leo60228
Copy link
Contributor

Someone I know was learning Rust as a beginner. They defined a function like this:

fn test<T>(t: T) -> i32 {
    5_i32 + <i32 as From<T>>::from(t)
}

This resulted in an error:

error[E0277]: the trait bound `i32: From<T>` is not satisfied
 --> src/lib.rs:2:13
  |
2 |     5_i32 + <i32 as From<T>>::from(t)
  |             ^^^^^^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `i32`
  |
  = note: required by `from`

Based on this error, they attempted to add an i32: From<T> bound:

fn test<T, i32: From<T>>(t: T) -> i32 {
    5_i32 + <i32 as From<T>>::from(t)
}

This leads to a different, very misleading error:

error[E0277]: cannot add `i32` to `i32`
 --> src/lib.rs:2:11
  |
2 |     5_i32 + <i32 as From<T>>::from(t)
  |           ^ no implementation for `i32 + i32`
  |
  = help: the trait `Add<i32>` is not implemented for `i32`

#[warn(non_camel_case_types)] can give a hint, but I think that the error message should stand on its own. In addition, they appear to have added #![allow(non_camel_case_types)] for unrelated reasons.

Meta

rustc --version --verbose: (N/A, on playground)

@leo60228 leo60228 added the C-bug Category: This is a bug. label Feb 10, 2021
@jonas-schievink jonas-schievink added the A-diagnostics Area: Messages for errors, warnings, and lints label Feb 10, 2021
@estebank estebank added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Feb 10, 2021
@estebank
Copy link
Contributor

For the benefit of anyone learning Rust that might encounter this ticket while trying to understand how to fix their code, what is needed is to constrain the type parameter with Into<i32> instead:

fn test<T: Into<i32>>(t: T) -> i32 {
    5_i32 + t.into()
}

@estebank estebank added A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix`. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Feb 10, 2021
@estebank
Copy link
Contributor

There are two different problems that need to be handled here. The first error is slightly misleading and can only be solved by knowing about the From/Into dichotomy, but the later is problematic because a new type parameter is masking the i32 type. In type errors we clarify what is happening:

error[E0308]: mismatched types
 --> src/lib.rs:2:18
  |
1 | fn test<T, i32: From<T>>(t: T) -> i32 {
  |            --- this type parameter
2 |     let x: i32 = 0i32;
  |            ---   ^^^^ expected type parameter `i32`, found `i32`
  |            |
  |            expected due to this
  |
  = note: expected type parameter `i32` (type parameter `i32`)
                       found type `i32` (`i32`)

We should do the same for E0277.

@leo60228
Copy link
Contributor Author

For what it's worth, I believe this error actually came from https://stackoverflow.com/questions/66082378/how-to-add-trait-bound-to-a-non-generic-type (which was linked on Reddit). I just assumed that they ran into the issue (it wasn't clear from context/phrasing). This isn't relevant to the issue itself, though.

@leo60228
Copy link
Contributor Author

The first error is slightly misleading and can only be solved by knowing about the From/Into dichotomy

You could also write fn test<T>(t: T) -> i32 where i32: From<T>.

@leo60228
Copy link
Contributor Author

You could also write fn test<T>(t: T) -> i32 where i32: From<T>.

After thinking about it, I feel like it's probably best for rustc to always suggest this form. It'd probably make sense to have a clippy lint suggesting rewriting this to the more idiomatic T: Into<i32>, however.

@leo60228
Copy link
Contributor Author

There's a draft PR for that: rust-lang/rust-clippy#6620

@estebank
Copy link
Contributor

@leo60228 one consideration is that clippy lints don't always necessarily trigger and having multiple errors can be confusing to people (not everyone reads everything before figuring out what the problem is). Because of that I would also like to extend the existing error to account for this case. :)

estebank added a commit to estebank/rust that referenced this issue Feb 17, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 18, 2021
…n, r=petrochenkov

In some limited cases, suggest `where` bounds for non-type params

Partially address rust-lang#81971.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 18, 2021
…n, r=petrochenkov

In some limited cases, suggest `where` bounds for non-type params

Partially address rust-lang#81971.
@estebank estebank added D-confusing Diagnostics: Confusing error or lint that should be reworked. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Feb 20, 2021
@estebank
Copy link
Contributor

The first case is now handled, the second case of type params masking types is yet to be resolved. I think at least pointing out that that has happened on all E0277 by visiting every type involved in the obligation and seeing if there's any type parameter with the same name as another type in scope and emitting a note explaining that that's what's happening would improve the situation.

@estebank
Copy link
Contributor

estebank commented Sep 28, 2023

Current output for the first case:

error[E0277]: the trait bound `i32: From<T>` is not satisfied
 --> src/lib.rs:4:36
  |
4 |     5_i32 + <i32 as From<T>>::from(t)
  |             ---------------------- ^ the trait `From<T>` is not implemented for `i32`
  |             |
  |             required by a bound introduced by this call
  |
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
  |
3 | fn test<T>(t: T) -> i32 where i32: From<T> {
  |                         ++++++++++++++++++

and for the second case

error[E0277]: cannot add `i32` to `i32`
 --> src/lib.rs:4:11
  |
4 |     5_i32 + <i32 as From<T>>::from(t)
  |           ^ no implementation for `i32 + i32`
  |
  = help: the trait `Add<i32>` is not implemented for `i32`
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
  |
3 | fn test<T, i32: From<T>>(t: T) -> i32 where i32: Add<i32> {
  |                                       +++++++++++++++++++

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-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix`. C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants