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

Diagnostics for mismatched generic types for constructors accessed via Self could show where the mismatch occurs #68476

Open
shepmaster opened this issue Jan 23, 2020 · 4 comments
Labels
A-associated-items Area: Associated items such as associated types and consts. A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@shepmaster
Copy link
Member

In this code, I used Self::VariantName to construct a new enum. However, since Self is treated as Wrapper<T>, I get an error when I try to populate Self::There / Wrapper<T>::There with a value of type U:

enum Wrapper<T> {
    There(T),
    NotThere,
}

impl<T> Wrapper<T> {
    fn map<U>(self, f: impl FnOnce(T) -> U) -> Wrapper<U> {
        match self {
            Self::There(v) => Self::There(f(v)),
            Self::NotThere => Self::NotThere,
        }
    }
}

(Playground)

Errors:

error[E0308]: mismatched types
 --> src/lib.rs:9:43
  |
9 |             Self::There(v) => Self::There(f(v)),
  |                                           ^^^^ expected type parameter, found a different type parameter
  |
  = note: expected type `T`
             found type `U`
  = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0308]: mismatched types
 --> src/lib.rs:9:31
  |
7 |     fn map<U>(self, f: impl FnOnce(T) -> U) -> Wrapper<U> {
  |                                                ---------- expected `Wrapper<U>` because of return type
8 |         match self {
9 |             Self::There(v) => Self::There(f(v)),
  |                               ^^^^^^^^^^^^^^^^^ expected type parameter, found a different type parameter
  |
  = note: expected type `Wrapper<U>`
             found type `Wrapper<T>`
  = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

This threw me for a fair number of minutes, mostly spent saying "no, that function f takes a T and returns a U, not the other way around".

Technically, the compiler is pointing to the entire call of f which should have tipped me off to realizing that it's the constructor call that was an issue.

Interestingly, the same problem doesn't occur for structs:

struct Wrapper<T>(T);

impl<T> Wrapper<T> {
    fn map<U>(self, f: impl FnOnce(T) -> U) -> Wrapper<U> {
        Self(f(self.0))
    }
}
@shepmaster shepmaster added the A-diagnostics Area: Messages for errors, warnings, and lints label Jan 23, 2020
@shepmaster
Copy link
Member Author

A semi-related case is:

fn generic<T>(_: T) {}

fn example() {
    let specific = generic::<i32>;
    specific(true);
}
error[E0308]: mismatched types
 --> src/lib.rs:5:14
  |
5 |     specific(true);
  |              ^^^^ expected i32, found bool

One possible solution would be to say something like

help: `specific` expects the type `i32` because it was specified here:
    let specific = generic::<i32>;
                          ^^^^^^^

Add in the original case something like:

help: `Self::There` expects the type `T` because it was specified here:
impl<T> Wrapper<T> {
        ^^^^^^^^^^

@estebank estebank added A-associated-items Area: Associated items such as associated types and consts. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 23, 2020
@shepmaster
Copy link
Member Author

Technically, the compiler is pointing to the entire call of f which should have tipped me off to realizing that it's the constructor call that was an issue.

Another improvement could be to explicitly state this. Something like “this argument to $function-name expects the type Foo”. This seems like it could be expanded to other things like struct fields as well (“this struct field expects ...”)

@JohnTitor JohnTitor added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jan 26, 2020
@estebank
Copy link
Contributor

estebank commented Apr 8, 2020

Current output:

error[E0308]: mismatched types
 --> src/lib.rs:9:43
  |
6 | impl<T> Wrapper<T> {
  |      - expected type parameter
7 |     fn map<U>(self, f: impl FnOnce(T) -> U) -> Wrapper<U> {
  |            - found type parameter
8 |         match self {
9 |             Self::There(v) => Self::There(f(v)),
  |                                           ^^^^ expected type parameter `T`, found type parameter `U`
  |
  = note: expected type parameter `T`
             found type parameter `U`
  = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0308]: mismatched types
 --> src/lib.rs:9:31
  |
6 | impl<T> Wrapper<T> {
  |      - found type parameter
7 |     fn map<U>(self, f: impl FnOnce(T) -> U) -> Wrapper<U> {
  |            - expected type parameter           ---------- expected `Wrapper<U>` because of return type
8 |         match self {
9 |             Self::There(v) => Self::There(f(v)),
  |                               ^^^^^^^^^^^^^^^^^ expected type parameter `U`, found type parameter `T`
  |
  = note: expected enum `Wrapper<U>`
             found enum `Wrapper<T>`
  = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error: aborting due to 2 previous errors

@shepmaster shepmaster changed the title Diagnostics for mismatched generic types for enum constructors accessed via Self could show where the mismatch occurs Diagnostics for mismatched generic types for constructors accessed via Self could show where the mismatch occurs Apr 10, 2024
@shepmaster
Copy link
Member Author

A similar case occurs with lifetime generics, as well as using a struct instead of my original enum:

pub struct Wrapper<'a>(&'a str);

impl<'a> Wrapper<'a> {
    fn make_one() -> Wrapper<'static> {
        Self("")
    }
}
error: lifetime may not live long enough
 --> src/lib.rs:5:9
  |
3 | impl<'a> Wrapper<'a> {
  |      -- lifetime `'a` defined here
4 |     fn make_one() -> Wrapper<'static> {
5 |         Self("")
  |         ^^^^^^^^ returning this value requires that `'a` must outlive `'static`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items such as associated types and consts. A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-papercut Diagnostics: An error or lint that needs small tweaks. 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