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? # for a free GitHub account to open an issue and contact its maintainers and the community.

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

Already on GitHub? # 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?

# for free to join this conversation on GitHub. Already have an account? # 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