Deriving Debug on an enum with uninhabited type triggers warning
// no warning
#[derive(Debug)]
enum Void {}
// warns about unreachable pattern
#[derive(Debug)]
enum Foo {
Bar(u8),
Void(Void),
}
Caused by https://github.com/rust-lang/rust/pull/38069
// no warning #[derive(Debug)] enum Void {} // warns about unreachable pattern #[derive(Debug)] enum Foo { Bar(u8), Void(Void), }Caused by https://github.com/rust-lang/rust/pull/38069
<!-- TRIAGEBOT_START --> <!-- TRIAGEBOT_ASSIGN_START --> <!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":null}$$TRIAGEBOT_ASSIGN_DATA_END --> <!-- TRIAGEBOT_ASSIGN_END --> <!-- TRIAGEBOT_END -->rustbot at 2020-04-18 22:52:45
Not seeing this in playground. Maybe it got fixed somewhere along the way?
scottmcm at 2017-07-17 05:29:23
The code gives the following compiler output (with a
#[crate_type="lib"]\n\nprefix)warning: enum is never used: `Void` --> src/lib.rs:5:1 | 5 | enum Void {} | ^^^^^^^^^ | = note: #[warn(dead_code)] on by default warning: enum is never used: `Foo` --> src/lib.rs:9:1 | 9 | enum Foo { | ^^^^^^^^There's no warning about an unreachable pattern anymore. As such, proposing this be closed.
Ryan Scheel at 2018-09-29 04:54:04
cc @estebank
Ryan Scheel at 2018-09-30 10:00:45
@Havvy thanks for the heads up! Lets close with a test to avoid regressions from the current behavior.
Esteban Kuber at 2018-10-01 04:20:28
This problem resurfaced, but only when actually using
!. This code (Playground):#![feature(never_type)] #[derive(Debug)] pub struct Foo(!); #[derive(Debug)] pub struct Bar(Empty); #[derive(Debug)] pub enum Empty {}Leads to this output (with
nightly-2019-02-06):warning: unreachable expression --> src/lib.rs:5:16 | 5 | pub struct Foo(!); | ^ | = note: #[warn(unreachable_code)] on by defaultSo should we reopen this issue?
Lukas Kalbertodt at 2019-02-07 15:02:34
Yes, reopen it or refile.
On Thu, Feb 7, 2019, 4:03 PM Lukas Kalbertodt notifications@github.com wrote:
This problem resurfaced, but only when actually using !. This code ( Playground https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=76b116e886c0a1d1af7e87b52d1b33e6 ):
#![feature(never_type)]
#[derive(Debug)]pub struct Foo(!);
#[derive(Debug)]pub struct Bar(Empty);
#[derive(Debug)]pub enum Empty {}
Leads to this output (with nightly-2019-02-06):
warning: unreachable expression --> src/lib.rs:5:16 | 5 | pub struct Foo(!); | ^ | = note: #[warn(unreachable_code)] on by default
So should we reopen this issue?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/38885#issuecomment-461457064, or mute the thread https://github.com/notifications/unsubscribe-auth/ABiDIhaypq1z1kmjXnqwRGQcMxTJYowEks5vLEAtgaJpZM4LdAcT .
Esteban Kuber at 2019-02-07 15:51:09
What's the reasoning behind a type like
struct Foo(!)?The warning comes from the expanded macro:
pub struct Foo(!); #[automatically_derived] #[allow(unused_qualifications)] impl ::std::fmt::Debug for Foo { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Foo(ref __self_0_0) => { let mut debug_trait_builder = f.debug_tuple("Foo"); let _ = debug_trait_builder.field(&&(*__self_0_0)); ^^^^^^^^^^^^^^ warning: unreachable expression debug_trait_builder.finish() } } } }Ilija Tovilo at 2019-07-25 21:56:11
What's the reasoning behind a type like
struct Foo(!)?Mainly some strange type level things. Adding a
!field makes sure that the struct is never created and thus only used at type level. There are some uses.
So to solve this one could simply add an
#[allow(unreachable_code)]attribute to the method, right? Do you think this solution is OK? Or shouldn't that be done for some reason?If that solution is acceptable, I could prepare a PR I guess. The debug-derive definition is in this file. One would probably just need to add an attribute here. Creating the attribute is probably the most complicated part about this. Looks like
mk_attr_id,mk_spanned_attr_outerand some of these methods would be required for that.Lukas Kalbertodt at 2019-07-26 20:44:45
I honestly don't understand enough about the
nevertype. Why is it useful forFooto implementDebugwhen it can never be instantiated? Is it something like this?https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c06cdaff756d196380f250ec163105c6
But then why not just use
never?Do you think this solution is OK?
I'm new to
rustcso probably not the person to answer this. It could theoretically hide other bugs in the future but I'm not sure if that's an issue.Ilija Tovilo at 2019-07-26 21:07:15
Why is it useful for
Footo implementDebugwhen it can never be instantiated?Yeah, of course that's kinda strange. But you already provided a nice example! Generics is where you just want to derive
Debugand it's fine if theDebugimpl for your type instantiated with!is never called.I discovered this issue because I have a
#![deny(missing_debug_implementations)]in my crate and tried to slap a#[derive(Debug)]on myFooinstead of#[allow(missing_debug_implementations)]. So yeah, I found another solution in my specific situation. But the generated code shouldn't create a warning either way. This is probably a low-priority issue, but still.I'm new to rustc so probably not the person to answer this. It could theoretically hide other bugs in the future but I'm not sure if that's an issue.
Let's just hope someone else is also reading this :D But yeah, that's the reason I asked: maybe someone knows about potential real warnings that would wrongfully be suppressed.
Lukas Kalbertodt at 2019-07-26 21:14:29
Let's just hope someone else is also reading this :D
Well, stupid English language, wasn't sure if "you" means "you" singular or "you" plural 😄
There's some discussion on whether
nevertypes should be convertible to all types (https://github.com/rust-lang/rfcs/issues/2619). Apparently there are some concerns but this would make the#[derive(Debug)]unnecessary. The error is technically correct as the generated code can never be executed.Either way, those were just my two cents 🙂
Ilija Tovilo at 2019-07-26 21:25:27
https://github.com/rust-lang/rust/pull/63317 changes the output to be the following, which in my mind makes more sense than complaining about the non-use of
Void, as it'd be used ifFoo::Voidwere used:warning: variant is never constructed: `Void` --> $DIR/derive-uninhabited-enum-38885.rs:13:5 | LL | Void(Void), | ^^^^^^^^^^ | = note: `-W dead-code` implied by `-W unused`Esteban Kuber at 2019-08-06 02:31:27
@rustbot claim
Augusto Noronha at 2020-04-18 22:52:43
@rustbot release-assignment
Augusto Noronha at 2020-05-10 09:52:36
I might try and take this up and investigate it but if my understanding of #2619 is correct this is currently the intended behavior.
!does not blanket implement all traits. That said I did want to provide an additional use case.Currently I'm writing a compiler for school and I wanted to use phantom types to differentiate between 32-bit and 64-bit registers like so
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumIter, Serialize)] pub enum Register<T: Default> { RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14, R15, _Unreachable(PhantomData<T>), }however I'd also like to make
_Unreachablean unconstructable variant so I could exclude it from matches ie something like_Unreachable(!, PhantomData<T>)or_Unreachable(Infallible, PhantomData<T>). However, since!doesn't auto implement every trait I get complaints about it not being serializable despite the variant not being able to be constructed.For now one work around is to implement your own never equivalent type
enum MyNever {}and then implementing your desired traits.
Miles Conn at 2021-10-25 15:55:49
@MilesConn Sounds like you might be more interested in https://github.com/serde-rs/serde/issues/2073 than this issue?
scottmcm at 2021-10-25 22:10:34
@MilesConn Sounds like you might be more interested in serde-rs/serde#2073 than this issue?
Yeah that's more on topic! Sorry I had a lot of tabs open when I was going down this rabbit hole. Ended up with a slightly different implementation but thank you.
Miles Conn at 2021-10-28 02:02:07
Possibly of interest: The pedantic Clippy lint
empty_enumcurrently proposes replacingenum Foo {}withstruct Foo(!)(when possible), but doing so triggers this bug. I've started a discussion of whether that's actually good, but supposing it is, this bug will become much more significant once!is stable.Kevin Reid at 2024-05-20 18:56:32