Code change:
As reported on Zulip: https://rust- This works for simple enums with one or more variants, but for zero it fails, possibly due to interaction with . This kind of code can come up in macro code. cc
I think for zero-variant enums it fails mostly as an oversight. This doesn't even have to generate any MIR as it is unreachable code anyway. Nominating for lang team discussion.
From the head comment at , I infer that this is indeed only designed to work with C-style enums. My proposal would be to add another cast category saying that empty enums can cast to anything, desugaring to .
No, enum casts have nothing to do with C-style enums. This works:
Oh, the comment says "C-like". That's not the same as . "C-like" just means that all enum variants have no data. This is satisfied by -- there are no enum variants, and thus all of them have no data.
Here's the offending check: It seems that the "payload-free" check is not just "all variants have no data" but also "it is not empty". I would hesitate to change this definition because it's likely to affect C-like enum handling elsewhere, but maybe can get another class specifically for uninhabited enums.
I'd argue a no-variant enum is payload-free. But all uses of that function should be carefully checked. Adding a new case to seems like a hack to me.
These were historically known as "C-like" enums, and is not C-like. From GCC: Can you maybe elaborate more on this?
Sure, C might not permit them, but I don't see how that's a reason to disallow casts in Rust.
I mean, what value does have that will make sense?
It's not actually reachable, so it could just typecheck without actually ever needing to produce a value.
It can compile the same way as , which is to say, produce no code and mark the remainder of the function as unreachable. Compiling to would also work for all numeric types that this could target, although then appears unused which might cause other unintended behaviors in the compiler.
Currently, the compiler marks code . What do you mean ? Unreachable is unsafe, does it mean that is unsafe ?
This uses no unsafe code: Here the code after the match statement (if there was any) would be unreachable, and this requires no unsafe code. Basically is an impossible value, therefore the function cannot be called in the first place and no code need be generated. EDIT: Your linked example is pattern matching on an with no branches. Here is an empty enum, , so an empty match of an empty enum is exhaustive without any unsafe.
Thanks, I got that part now. But the question raised by : What is the concrete example of macros that have use cases for ? Also, is a never type, if allowed, would we want to make any operations on unreachable?
The actual macro where this came up was a safe wrapper that declares an int-like enum and an array indexed by the enum. Roughly, it creates a type and , and then the implementation for uses where . When has zero elements, this causes the macro to produce malformed code. It is possible to work around this issue by explicitly checking for this case, but I don't really see a reason for it not to work in the first place. Any operation on Void is unreachable. That means that the compiler can do whatever it wants to for codegen here because it doesn't matter, the function is not callable. But from a typing point of view, I think that should have type , not .
It's no surprise that macros run into this -- this happens any time that we have special exceptions. "s without fields can be cast to integers, except if the enum has 0 variants" is such a special exception, and from a language design perspective it has no reason to exist. Not sure what the next step here is -- probably an RFC? This is a language extension after all.
Discussed in the meeting today: We agreed that this seems like a harmless extension (specifically: permitting zero variant enums to be to a usize). We don't feel an RFC is required and we would recommend somebody creating a PR that fixes this and nominates the PR for lang-team consideration, we can do an FCP directly on the PR.
I have opened to fix this issue.

Related discussion:
text: // # First handle non-scalar source values. <del> // Handle cast from a univariant (ZST) enum. </del> <ins> // Handle cast from a ZST enum (0 or 1 variants). </ins> match src.layout.variants { Variants::Single { index } => { <ins> if src.layout.abi.is_uninhabited() { // This is dead code, because an uninhabited enum is UB to // instantiate. throw_ub!(Unreachable); } </ins> if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { assert!(src.layout.is_zst()); let discr_layout = self.layout_of(discr.ty)?;