Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --Given the following code: <!-- Please provide a link to -- The current output is: <!-- The following is not always necessary. --Ideally the output should look like: I don't have any understanding of the internal machinery of the diagnostics, but it seems that the value is being cast to the unsigned variant of the integer before displayed (in this case -). Of course, the discriminant does its job just fine, and explicitly casting to an also works as expected, only the diagnostics are a bit unfortunate. Changing to in the example causes it to underflow close to which may seem a little intimidating to a newer user who accidentally stumbles on this :smile: The output is the same on both Stable and Nightly <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Gave it some more thought, and I'm guessing just evaluates to (and vice-versa for the other integer types) under the hood, but due to consistent wrapping behavior and two's complement, there is no difference to the user since they have to explicitly cast the enum to the respective integer to read it anyway. Well except for this error message I guess.
So I got curious and decided to check out what causes this. Looking around a bit trying to find which part of type-checking had to do with enums led me to this . At the end we there is a part where we go through all the discriminants and ensure none of them are duplicates, and if there are duplicates we emit the E0081 error pointing to the duplicates and all that. <details<summaryThe code</summary</detailsHowever, the type of the is being tracked correctly with respect to any . I a print statement and sure enough: The type of the discriminant is understood to be i8, but the value is expressed as a u8. If I can find a nice solution to this i'll submit a PR.
Honestly, I think this error could probably be reworded as well; since doesn't have an explicitly defined discriminant, we should also clarify that it's used by counting up from the previously defined discriminant, which here is . Of course, that doesn't have to be part of the first PR, but I figure it's worth mentioning here.
Yeah that would improve the error quality in this case. I haven't checked if we can infer from context whether the discriminant has been explicitly assigned or not, but I assume we can, and in that case whenever we detect a duplicate we could add a small note saying that that the implicit discriminant is just a += 1 from the previous discriminant as you say. Doesn't hurt to do that for both positive and negative discriminant duplicates. Anyway I've got something in the works, I'll explore adding this note aswell as soon as I can get back to my computer with some spare time. claim
You should be able to assume that it's been previously assigned, since if every discriminant were inferred, there wouldn't be conflicts, unless the number of variants overflows the type (which I think has a separate error? I'm not actually sure) And I think a good example to test here would be: Since the discriminant for would be actually 3, and fail.
Oh, I meant inferring if whether the duplicate discriminant was explicitly set or not. In the above example, that would be detecting if was assigned explicitly or not. But yeah it was possible, HIR has all the information we need. So here's what I currently have. An enum like this: Errors as such: Not sure about the note. We could split it out with its own span and point to which variant the duplicate increment ( in this case) is incremented from, but i feel like that is unnecessarily verbose. Thoughts? Also I reworded some of the original error message, hopefully it's clearer this way? If not i can revert that. Edit: Sorry i didn't read your comment properly :smiley: . Yeah i guess we could iterate backwards until we hit the first defined discriminant, and point to that in the note.
Yeah, in that case I'd say something like: discriminant is computed as (specified) + N variants later = (duplicate)
Ok, I think I'll leave the notification for another PR :disappointed: . The reason is that in the duplicate-detection loop, we are only storing the type as we go, and not the HIR representation of the variant. This means that (unless I'm missing something) we can't iterate backwards after we hit a duplicate with no assigned discriminant, to the last discriminant assigned explicitly. As an example, for this enum: In the loop, once we get to we compare it to the previously approved discriminants, and detect that is a duplicate of . We want to point to as the "increment startpoint", but we've ditched the HIR information at this point for the approved discriminants (which V2 is part of). Ofcourse we could just loop through all of the variants all over with the HIR included, but that is really inelegant (and ineffecient). So what we should do is re-write the loop but I don't feel like messing with that right now.
Yeah, I think it's totally reasonable to just fix the extremely obvious bug of reinterpreting the integer before tackling the larger issue of the message being weird to begin with. I mostly just wanted to add my thoughts here for a potential solution so anyone else looking to help with this has some ideas to work with.
(Should I create a separate issue for my other suggestions, or would it make sense to reopen this issue until those errors are also fixed?)
It would make sense to me to make a new issue specifically for the stuff we missed from this PR? There are probably good reasons to just go through the entire function. Here's one of the small (possible) imrovements: the check in could probably be done so that we don't have to do two passes?
Alright, filed! Future discussion can go there.

Discussion:
} let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len()); <ins> // This tracks the previous variant span (in the loop) incase we need it for diagnostics let mut prev_variant_span: Span = DUMMY_SP; </ins> for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) { // Check for duplicate discriminant values if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {

Some(ref expr) => tcx.hir().span(expr.hir_id), None => v.span, }; <del> let display_discr = display_discriminant_value(tcx, v, discr.val); let display_discr_i = display_discriminant_value(tcx, variant_i, disr_vals[i].val); struct_span_err!( </del> <ins> let display_discr = format_discriminant_overflow(tcx, v, discr); let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]); let no_disr = v.disr_expr.is_none(); let mut err = struct_span_err!( </ins> tcx.sess, <del> span, </del> <ins> sp, </ins> E0081, <del> "discriminant value `{}` already exists", discr.val, ) .span_label(i_span, format!("first use of {display_discr_i}")) .span_label(span, format!("enum already has {display_discr}")) .emit(); </del> <ins> "discriminant value `{}` assigned more than once", discr, ); err.span_label(i_span, format!("first assignment of {display_discr_i}")); err.span_label(span, format!("second assignment of {display_discr}")); if no_disr { err.span_label( prev_variant_span, format!( "assigned discriminant for `{}` was incremented from this discriminant", v.ident ), ); } err.emit(); </ins> } disr_vals.push(discr); <ins> prev_variant_span = v.span; </ins> } check_representable(tcx, sp, def_id); check_transparent(tcx, sp, def); } <del> /// Format an enum discriminant value for use in a diagnostic message. fn display_discriminant_value<'tcx>( </del> <ins> /// In the case that a discriminant is both a duplicate and an overflowing literal, /// we insert both the assigned discriminant and the literal it overflowed from into the formatted /// output. Otherwise we format the discriminant normally. fn format_discriminant_overflow<'tcx>( </ins> tcx: TyCtxt<'tcx>, variant: &hir::Variant<'_>, <del> evaluated: u128, </del> <ins> dis: Discr<'tcx>, </ins> ) -> String { if let Some(expr) = &variant.disr_expr { let body = &tcx.hir().body(expr.body).value; if let hir::ExprKind::Lit(lit) = &body.kind && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node <del> && evaluated != *lit_value </del> <ins> && dis.val != *lit_value </ins> { <del> return format!("`{evaluated}` (overflowed from `{lit_value}`)"); </del> <ins> return format!("`{dis}` (overflowed from `{lit_value}`)"); </ins> } } <del> format!("`{}`", evaluated) </del> <ins> format!("`{dis}`") </ins> } pub(super) fn check_type_params_are_used<'tcx>(

// so force the repr. #[cfg_attr(not(target_pointer_width = "32"), repr(i32))] enum Eu64 { <del> Au64 = 0, //~NOTE first use of `0` </del> <ins> //~^ ERROR discriminant value `0` assigned more than once Au64 = 0, //~^NOTE first assignment of `0` </ins> Bu64 = 0x8000_0000_0000_0000 <del> //~^ ERROR discriminant value `0` already exists //~| NOTE enum already has `0` (overflowed from `9223372036854775808`) </del> <ins> //~^NOTE second assignment of `0` (overflowed from `9223372036854775808`) </ins> } fn main() {}

<del> error[E0081]: discriminant value `0` already exists --> $DIR/enum-discrim-autosizing.rs:8:12 </del> <ins> error[E0081]: discriminant value `0` assigned more than once --> $DIR/enum-discrim-autosizing.rs:6:1 </ins> | <del> LL |     Au64 = 0, |            - first use of `0` LL |     Bu64 = 0x8000_0000_0000_0000 |            ^^^^^^^^^^^^^^^^^^^^^ enum already has `0` (overflowed from `9223372036854775808`) </del> <ins> LL | / enum Eu64 { LL | | LL | |     Au64 = 0, | |            - first assignment of `0` LL | | LL | |     Bu64 = 0x8000_0000_0000_0000 | |            --------------------- second assignment of `0` (overflowed from `9223372036854775808`) LL | | LL | | } | |_^ </ins> error: aborting due to previous error

enum Enum { <ins> //~^ ERROR discriminant value `3` assigned more than once </ins> P = 3, <del> //~^ NOTE first use of `3` </del> <ins> //~^ NOTE first assignment of `3` </ins> X = 3, <del> //~^ ERROR discriminant value `3` already exists //~| NOTE enum already has `3` </del> <ins> //~^ NOTE second assignment of `3` </ins> Y = 5 } #[repr(u8)] enum EnumOverflowRepr { <ins> //~^ ERROR discriminant value `1` assigned more than once </ins> P = 257, <del> //~^ NOTE first use of `1` (overflowed from `257`) </del> <ins> //~^ NOTE first assignment of `1` (overflowed from `257`) </ins> X = 513, <del> //~^ ERROR discriminant value `1` already exists //~| NOTE enum already has `1` (overflowed from `513`) </del> <ins> //~^ NOTE second assignment of `1` (overflowed from `513`) } #[repr(i8)] enum NegDisEnum { //~^ ERROR discriminant value `-1` assigned more than once First = -1, //~^ NOTE first assignment of `-1` Second = -2, //~^ NOTE assigned discriminant for `Last` was incremented from this discriminant Last, //~^ NOTE second assignment of `-1` </ins> } fn main() {

<del> error[E0081]: discriminant value `3` already exists --> $DIR/E0081.rs:4:9 </del> <ins> error[E0081]: discriminant value `3` assigned more than once --> $DIR/E0081.rs:1:1 </ins> | <del> LL |     P = 3, |         - first use of `3` LL | LL |     X = 3, |         ^ enum already has `3` </del> <ins> LL | / enum Enum { LL | | LL | |     P = 3, | |         - first assignment of `3` LL | | LL | |     X = 3, | |         - second assignment of `3` LL | | LL | |     Y = 5 LL | | } | |_^ </ins> <del> error[E0081]: discriminant value `1` already exists --> $DIR/E0081.rs:14:9 </del> <ins> error[E0081]: discriminant value `1` assigned more than once --> $DIR/E0081.rs:11:1 </ins> | <del> LL |     P = 257, |         --- first use of `1` (overflowed from `257`) LL | LL |     X = 513, |         ^^^ enum already has `1` (overflowed from `513`) </del> <ins> LL | / enum EnumOverflowRepr { LL | | LL | |     P = 257, | |         --- first assignment of `1` (overflowed from `257`) LL | | LL | |     X = 513, | |         --- second assignment of `1` (overflowed from `513`) LL | | LL | | } | |_^ </ins> <del> error: aborting due to 2 previous errors </del> <ins> error[E0081]: discriminant value `-1` assigned more than once --> $DIR/E0081.rs:20:1 | LL | / enum NegDisEnum { LL | | LL | |     First = -1, | |             -- first assignment of `-1` LL | | LL | |     Second = -2, | |     ----------- assigned discriminant for `Last` was incremented from this discriminant LL | | LL | |     Last, | |     ---- second assignment of `-1` LL | | LL | | } | |_^ error: aborting due to 3 previous errors </ins> For more information about this error, try `rustc --explain E0081`.

const N: isize = 1; enum Foo { <ins> //~^ ERROR discriminant value `1` assigned more than once //~| ERROR discriminant value `1` assigned more than once //~| ERROR discriminant value `1` assigned more than once </ins> A = 1, B = 1, <del> //~^ ERROR discriminant value `1` already exists </del> C = 0, D, <del> //~^ ERROR discriminant value `1` already exists </del> E = N, <del> //~^ ERROR discriminant value `1` already exists </del> }

<del> error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:5:9 </del> <ins> error[E0081]: discriminant value `1` assigned more than once --> $DIR/issue-15524.rs:3:1 </ins> | <del> LL |     A = 1, |         - first use of `1` LL |     B = 1, |         ^ enum already has `1` </del> <ins> LL | / enum Foo { LL | | LL | | LL | | LL | |     A = 1, | |         - first assignment of `1` LL | |     B = 1, | |         - second assignment of `1` ...  | LL | | LL | | } | |_^ </ins> <del> error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:8:5 </del> <ins> error[E0081]: discriminant value `1` assigned more than once --> $DIR/issue-15524.rs:3:1 </ins> | <del> LL |     A = 1, |         - first use of `1` ... LL |     D, |     ^ enum already has `1` </del> <ins> LL | / enum Foo { LL | | LL | | LL | | LL | |     A = 1, | |         - first assignment of `1` LL | |     B = 1, LL | |     C = 0, | |     ----- assigned discriminant for `D` was incremented from this discriminant LL | |     D, | |     - second assignment of `1` ...  | LL | | LL | | } | |_^ </ins> <del> error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:11:9 </del> <ins> error[E0081]: discriminant value `1` assigned more than once --> $DIR/issue-15524.rs:3:1 </ins> | <del> LL |     A = 1, |         - first use of `1` ... LL |     E = N, |         ^ enum already has `1` </del> <ins> LL | / enum Foo { LL | | LL | | LL | | LL | |     A = 1, | |         - first assignment of `1` ...  | LL | |     E = N, | |         - second assignment of `1` LL | | LL | | } | |_^ </ins> error: aborting due to 3 previous errors

// Black and White have the same discriminator value ... enum Color { <ins> //~^ ERROR discriminant value `0` assigned more than once </ins> Red = 0xff0000, Green = 0x00ff00, Blue = 0x0000ff, Black = 0x000000, <del> White = 0x000000, //~ ERROR discriminant value `0` already exists </del> <ins> White = 0x000000, </ins> } fn main() { }

<del> error[E0081]: discriminant value `0` already exists --> $DIR/tag-variant-disr-dup.rs:8:13 </del> <ins> error[E0081]: discriminant value `0` assigned more than once --> $DIR/tag-variant-disr-dup.rs:3:1 </ins> | <del> LL |     Black = 0x000000, |             -------- first use of `0` LL |     White = 0x000000, |             ^^^^^^^^ enum already has `0` </del> <ins> LL | / enum Color { LL | | LL | |     Red = 0xff0000, LL | |     Green = 0x00ff00, LL | |     Blue = 0x0000ff, LL | |     Black = 0x000000, | |             -------- first assignment of `0` LL | |     White = 0x000000, | |             -------- second assignment of `0` LL | | } | |_^ </ins> error: aborting due to previous error