Skip to content

Pattern-matching on #[non_exhaustive] unit/tuple structs and variants causes confusing diagnostic #107165

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

Open
obi1kenobi opened this issue Jan 21, 2023 · 4 comments
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-patterns Relating to patterns and pattern matching D-papercut Diagnostics: An error or lint that needs small tweaks. E-help-wanted Call for participation: Help is requested to fix this issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@obi1kenobi
Copy link
Member

obi1kenobi commented Jan 21, 2023

Code

In crate A:

pub enum Foo {
    #[non_exhaustive]
    Unit,

    #[non_exhaustive]
    Tuple(i64),
}

In crate B:

use A::Foo;

fn use_foo(val: Foo) {
    match val {
        Foo::Unit => todo!(),
        Foo::Tuple(..) => todo!(),
    }
}

Current output

error[E0603]: unit variant `Unit` is private
   --> crate_b/src/main.rs:150:30
    |
150 |         Foo::Unit => todo!(),
    |              ^^^^ private unit variant
    |
note: the unit variant `Unit` is defined here
   --> /.../crate_a/src/lib.rs:22:5
    |
22  |     Unit,
    |     ^^^^

error[E0603]: tuple variant `Tuple` is private
   --> crate_b/src/main.rs:151:30
    |
151 |         Foo::Tuple(..) => todo!(),
    |              ^^^^^ private tuple variant
    |
note: the tuple variant `Tuple` is defined here
   --> /.../crate_a/src/lib.rs:25:5
    |
25  |     Tuple(i64),
    |     ^^^^^

For more information about this error, try `rustc --explain E0603`.
error: could not compile `crate_b` due to 2 previous errors

Desired output

The error message should reference #[non_exhaustive] rather than being implementation-centric. The "variant is private" message is unexpected, confusing the average user since variants don't take pub or other visibility specifiers.

The output should also suggest the fix: use a struct pattern instead, like Foo::Unit { .. } and Foo::Tuple{ 0: val, .. }.

Rationale and extra context

Originally a conversation on Mastodon, @estebank suggested I file an issue:
https://hachyderm.io/@predrag/109723971447778736

Other cases

No response

Anything else?

No response

@obi1kenobi obi1kenobi 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 Jan 21, 2023
@estebank estebank added D-papercut Diagnostics: An error or lint that needs small tweaks. A-patterns Relating to patterns and pattern matching labels Jan 30, 2023
@obi1kenobi obi1kenobi changed the title Pattern-matching on enum's #[non_exhaustive] variant causes confusing diagnostic Pattern-matching on #[non_exhaustive] unit/tuple structs and variants causes confusing diagnostic Feb 12, 2023
@obi1kenobi
Copy link
Member Author

Just tried the related setup with a tuple and unit struct instead of a variant, and the diagnostics there could use some sharpening as well.

They are all technically correct, but a bit implementation-oriented and never suggest the solution of using Foo { .. } syntax which would make the code work as intended.

Unit struct

first lib

fn f(value: second_lib::Foo) {
    match value {
        second_lib::Foo => {
            println!("reached!");
        }
    }
}

second lib

#[non_exhaustive]
pub struct Foo;

error:

error[E0603]: unit struct `Foo` is private
  --> first_lib/src/lib.rs:37:25
   |
37 |         second_lib::Foo => {
   |                     ^^^ private unit struct
   |
note: the unit struct `Foo` is defined here
  --> /.../second_lib/src/lib.rs:25:1
   |
25 | pub struct Foo;
   | ^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0603`.

Tuple struct

first lib

fn f(value: second_lib::Foo) {
    match value {
        second_lib::Foo(..) => {
            println!("reached!");
        }
    }
}

second lib

#[non_exhaustive]
pub struct Foo(pub i64, i64);

error:

error[E0603]: tuple struct constructor `Foo` is private
  --> first_lib/src/lib.rs:37:25
   |
37 |         second_lib::Foo(..) => {
   |                     ^^^ private tuple struct constructor
   |
  ::: /.../second_lib/src/lib.rs:25:16
   |
25 | pub struct Foo(pub i64, i64);
   |                ------------ a constructor is private if any of the fields is private
   |
note: the tuple struct constructor `Foo` is defined here
  --> /.../second_lib/src/lib.rs:25:1
   |
25 | pub struct Foo(pub i64, i64);
   | ^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0603`.

@Nadrieril
Copy link
Member

Nadrieril commented Dec 1, 2023

Hm, I'm guessing the pattern-checking code checks whether the struct constructor is visible, which it isn't if the struct is non_exhaustive. We probably want to check the visibility of individual fields here instead. That doesn't sound too hard but would require some investigation. Let's see if anyone wants to help.

@Nadrieril Nadrieril added the E-help-wanted Call for participation: Help is requested to fix this issue. label Dec 1, 2023
@ffmancera
Copy link
Contributor

ffmancera commented Feb 20, 2024

@rustbot claim

@r-raymond
Copy link
Contributor

Looks like diagnostics have improved since this issue was created.

---- [ui] tests/ui/match/non-exhaustive-variant-hint-issue-107165.rs stdout ----
normalized stderr:
error[E0603]: unit variant `Unit` is private
  --> $DIR/non-exhaustive-variant-hint-issue-107165.rs:39:19
   |
LL |         Elibrary::Unit => (),
   |                   ^^^^ private unit variant
   |
note: the unit variant `Unit` is defined here
  --> $DIR/auxiliary/non_exhaustive_structs_and_variants_lib.rs:3:5
   |
LL |     #[non_exhaustive]
   |     ----------------- cannot be constructed because it is `#[non_exhaustive]`
LL |     Unit,
   |     ^^^^

I assume there is still value in hinting that the fix might be using the struct pattern?

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-patterns Relating to patterns and pattern matching D-papercut Diagnostics: An error or lint that needs small tweaks. E-help-wanted Call for participation: Help is requested to fix this issue. 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.

5 participants