Skip to content

Commit f671bc9

Browse files
committed
Improve error message for E0081
Previously whenever a duplicate discriminant was detected for an Enum, we would print the discriminant bits in the diagnostic without any casting. This caused us to display incorrect values for negative discriminants. After this PR we format the discriminant signedness correctly. Also reworded some of the original error messages.
1 parent 56fd680 commit f671bc9

File tree

9 files changed

+174
-76
lines changed

9 files changed

+174
-76
lines changed

compiler/rustc_typeck/src/check/check.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,8 @@ fn check_enum<'tcx>(
13761376
}
13771377

13781378
let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
1379+
// This tracks the previous variant span (in the loop) incase we need it for diagnostics
1380+
let mut prev_variant_span: Span = DUMMY_SP;
13791381
for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) {
13801382
// Check for duplicate discriminant values
13811383
if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
@@ -1390,42 +1392,59 @@ fn check_enum<'tcx>(
13901392
Some(ref expr) => tcx.hir().span(expr.hir_id),
13911393
None => v.span,
13921394
};
1393-
let display_discr = display_discriminant_value(tcx, v, discr.val);
1394-
let display_discr_i = display_discriminant_value(tcx, variant_i, disr_vals[i].val);
1395-
struct_span_err!(
1395+
let display_discr = format_discriminant_overflow(tcx, v, discr);
1396+
let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]);
1397+
let no_disr = v.disr_expr.is_none();
1398+
let mut err = struct_span_err!(
13961399
tcx.sess,
1397-
span,
1400+
sp,
13981401
E0081,
1399-
"discriminant value `{}` already exists",
1400-
discr.val,
1401-
)
1402-
.span_label(i_span, format!("first use of {display_discr_i}"))
1403-
.span_label(span, format!("enum already has {display_discr}"))
1404-
.emit();
1402+
"discriminant value `{}` assigned more than once",
1403+
discr,
1404+
);
1405+
1406+
err.span_label(i_span, format!("first assignment of {display_discr_i}"));
1407+
err.span_label(span, format!("second assignment of {display_discr}"));
1408+
1409+
if no_disr {
1410+
err.span_label(
1411+
prev_variant_span,
1412+
format!(
1413+
"assigned discriminant for `{}` was incremented from this discriminant",
1414+
v.ident
1415+
),
1416+
);
1417+
}
1418+
err.emit();
14051419
}
1420+
14061421
disr_vals.push(discr);
1422+
prev_variant_span = v.span;
14071423
}
14081424

14091425
check_representable(tcx, sp, def_id);
14101426
check_transparent(tcx, sp, def);
14111427
}
14121428

1413-
/// Format an enum discriminant value for use in a diagnostic message.
1414-
fn display_discriminant_value<'tcx>(
1429+
/// In the case that a discriminant is both a duplicate and an overflowing literal,
1430+
/// we insert both the assigned discriminant and the literal it overflowed from into the formatted
1431+
/// output. Otherwise we format the discriminant normally.
1432+
fn format_discriminant_overflow<'tcx>(
14151433
tcx: TyCtxt<'tcx>,
14161434
variant: &hir::Variant<'_>,
1417-
evaluated: u128,
1435+
dis: Discr<'tcx>,
14181436
) -> String {
14191437
if let Some(expr) = &variant.disr_expr {
14201438
let body = &tcx.hir().body(expr.body).value;
14211439
if let hir::ExprKind::Lit(lit) = &body.kind
14221440
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
1423-
&& evaluated != *lit_value
1441+
&& dis.val != *lit_value
14241442
{
1425-
return format!("`{evaluated}` (overflowed from `{lit_value}`)");
1443+
return format!("`{dis}` (overflowed from `{lit_value}`)");
14261444
}
14271445
}
1428-
format!("`{}`", evaluated)
1446+
1447+
format!("`{dis}`")
14291448
}
14301449

14311450
pub(super) fn check_type_params_are_used<'tcx>(

src/test/ui/enum/enum-discrim-autosizing.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
// so force the repr.
55
#[cfg_attr(not(target_pointer_width = "32"), repr(i32))]
66
enum Eu64 {
7-
Au64 = 0, //~NOTE first use of `0`
7+
//~^ ERROR discriminant value `0` assigned more than once
8+
Au64 = 0,
9+
//~^NOTE first assignment of `0`
810
Bu64 = 0x8000_0000_0000_0000
9-
//~^ ERROR discriminant value `0` already exists
10-
//~| NOTE enum already has `0` (overflowed from `9223372036854775808`)
11+
//~^NOTE second assignment of `0` (overflowed from `9223372036854775808`)
1112
}
1213

1314
fn main() {}

src/test/ui/enum/enum-discrim-autosizing.stderr

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
error[E0081]: discriminant value `0` already exists
2-
--> $DIR/enum-discrim-autosizing.rs:8:12
1+
error[E0081]: discriminant value `0` assigned more than once
2+
--> $DIR/enum-discrim-autosizing.rs:6:1
33
|
4-
LL | Au64 = 0,
5-
| - first use of `0`
6-
LL | Bu64 = 0x8000_0000_0000_0000
7-
| ^^^^^^^^^^^^^^^^^^^^^ enum already has `0` (overflowed from `9223372036854775808`)
4+
LL | / enum Eu64 {
5+
LL | |
6+
LL | | Au64 = 0,
7+
| | - first assignment of `0`
8+
LL | |
9+
LL | | Bu64 = 0x8000_0000_0000_0000
10+
| | --------------------- second assignment of `0` (overflowed from `9223372036854775808`)
11+
LL | |
12+
LL | | }
13+
| |_^
814

915
error: aborting due to previous error
1016

src/test/ui/error-codes/E0081.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
enum Enum {
2+
//~^ ERROR discriminant value `3` assigned more than once
23
P = 3,
3-
//~^ NOTE first use of `3`
4+
//~^ NOTE first assignment of `3`
45
X = 3,
5-
//~^ ERROR discriminant value `3` already exists
6-
//~| NOTE enum already has `3`
6+
//~^ NOTE second assignment of `3`
77
Y = 5
88
}
99

1010
#[repr(u8)]
1111
enum EnumOverflowRepr {
12+
//~^ ERROR discriminant value `1` assigned more than once
1213
P = 257,
13-
//~^ NOTE first use of `1` (overflowed from `257`)
14+
//~^ NOTE first assignment of `1` (overflowed from `257`)
1415
X = 513,
15-
//~^ ERROR discriminant value `1` already exists
16-
//~| NOTE enum already has `1` (overflowed from `513`)
16+
//~^ NOTE second assignment of `1` (overflowed from `513`)
17+
}
18+
19+
#[repr(i8)]
20+
enum NegDisEnum {
21+
//~^ ERROR discriminant value `-1` assigned more than once
22+
First = -1,
23+
//~^ NOTE first assignment of `-1`
24+
Second = -2,
25+
//~^ NOTE assigned discriminant for `Last` was incremented from this discriminant
26+
Last,
27+
//~^ NOTE second assignment of `-1`
1728
}
1829

1930
fn main() {

src/test/ui/error-codes/E0081.stderr

+43-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,49 @@
1-
error[E0081]: discriminant value `3` already exists
2-
--> $DIR/E0081.rs:4:9
1+
error[E0081]: discriminant value `3` assigned more than once
2+
--> $DIR/E0081.rs:1:1
33
|
4-
LL | P = 3,
5-
| - first use of `3`
6-
LL |
7-
LL | X = 3,
8-
| ^ enum already has `3`
4+
LL | / enum Enum {
5+
LL | |
6+
LL | | P = 3,
7+
| | - first assignment of `3`
8+
LL | |
9+
LL | | X = 3,
10+
| | - second assignment of `3`
11+
LL | |
12+
LL | | Y = 5
13+
LL | | }
14+
| |_^
915

10-
error[E0081]: discriminant value `1` already exists
11-
--> $DIR/E0081.rs:14:9
16+
error[E0081]: discriminant value `1` assigned more than once
17+
--> $DIR/E0081.rs:11:1
1218
|
13-
LL | P = 257,
14-
| --- first use of `1` (overflowed from `257`)
15-
LL |
16-
LL | X = 513,
17-
| ^^^ enum already has `1` (overflowed from `513`)
19+
LL | / enum EnumOverflowRepr {
20+
LL | |
21+
LL | | P = 257,
22+
| | --- first assignment of `1` (overflowed from `257`)
23+
LL | |
24+
LL | | X = 513,
25+
| | --- second assignment of `1` (overflowed from `513`)
26+
LL | |
27+
LL | | }
28+
| |_^
1829

19-
error: aborting due to 2 previous errors
30+
error[E0081]: discriminant value `-1` assigned more than once
31+
--> $DIR/E0081.rs:20:1
32+
|
33+
LL | / enum NegDisEnum {
34+
LL | |
35+
LL | | First = -1,
36+
| | -- first assignment of `-1`
37+
LL | |
38+
LL | | Second = -2,
39+
| | ----------- assigned discriminant for `Last` was incremented from this discriminant
40+
LL | |
41+
LL | | Last,
42+
| | ---- second assignment of `-1`
43+
LL | |
44+
LL | | }
45+
| |_^
46+
47+
error: aborting due to 3 previous errors
2048

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

src/test/ui/issues/issue-15524.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
const N: isize = 1;
22

33
enum Foo {
4+
//~^ ERROR discriminant value `1` assigned more than once
5+
//~| ERROR discriminant value `1` assigned more than once
6+
//~| ERROR discriminant value `1` assigned more than once
47
A = 1,
58
B = 1,
6-
//~^ ERROR discriminant value `1` already exists
79
C = 0,
810
D,
9-
//~^ ERROR discriminant value `1` already exists
1011

1112
E = N,
12-
//~^ ERROR discriminant value `1` already exists
1313

1414
}
1515

src/test/ui/issues/issue-15524.stderr

+45-20
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,53 @@
1-
error[E0081]: discriminant value `1` already exists
2-
--> $DIR/issue-15524.rs:5:9
1+
error[E0081]: discriminant value `1` assigned more than once
2+
--> $DIR/issue-15524.rs:3:1
33
|
4-
LL | A = 1,
5-
| - first use of `1`
6-
LL | B = 1,
7-
| ^ enum already has `1`
4+
LL | / enum Foo {
5+
LL | |
6+
LL | |
7+
LL | |
8+
LL | | A = 1,
9+
| | - first assignment of `1`
10+
LL | | B = 1,
11+
| | - second assignment of `1`
12+
... |
13+
LL | |
14+
LL | | }
15+
| |_^
816

9-
error[E0081]: discriminant value `1` already exists
10-
--> $DIR/issue-15524.rs:8:5
17+
error[E0081]: discriminant value `1` assigned more than once
18+
--> $DIR/issue-15524.rs:3:1
1119
|
12-
LL | A = 1,
13-
| - first use of `1`
14-
...
15-
LL | D,
16-
| ^ enum already has `1`
20+
LL | / enum Foo {
21+
LL | |
22+
LL | |
23+
LL | |
24+
LL | | A = 1,
25+
| | - first assignment of `1`
26+
LL | | B = 1,
27+
LL | | C = 0,
28+
| | ----- assigned discriminant for `D` was incremented from this discriminant
29+
LL | | D,
30+
| | - second assignment of `1`
31+
... |
32+
LL | |
33+
LL | | }
34+
| |_^
1735

18-
error[E0081]: discriminant value `1` already exists
19-
--> $DIR/issue-15524.rs:11:9
36+
error[E0081]: discriminant value `1` assigned more than once
37+
--> $DIR/issue-15524.rs:3:1
2038
|
21-
LL | A = 1,
22-
| - first use of `1`
23-
...
24-
LL | E = N,
25-
| ^ enum already has `1`
39+
LL | / enum Foo {
40+
LL | |
41+
LL | |
42+
LL | |
43+
LL | | A = 1,
44+
| | - first assignment of `1`
45+
... |
46+
LL | | E = N,
47+
| | - second assignment of `1`
48+
LL | |
49+
LL | | }
50+
| |_^
2651

2752
error: aborting due to 3 previous errors
2853

src/test/ui/tag-variant-disr-dup.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Black and White have the same discriminator value ...
22

33
enum Color {
4+
//~^ ERROR discriminant value `0` assigned more than once
45
Red = 0xff0000,
56
Green = 0x00ff00,
67
Blue = 0x0000ff,
78
Black = 0x000000,
8-
White = 0x000000, //~ ERROR discriminant value `0` already exists
9+
White = 0x000000,
910
}
1011

1112
fn main() { }

src/test/ui/tag-variant-disr-dup.stderr

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
error[E0081]: discriminant value `0` already exists
2-
--> $DIR/tag-variant-disr-dup.rs:8:13
1+
error[E0081]: discriminant value `0` assigned more than once
2+
--> $DIR/tag-variant-disr-dup.rs:3:1
33
|
4-
LL | Black = 0x000000,
5-
| -------- first use of `0`
6-
LL | White = 0x000000,
7-
| ^^^^^^^^ enum already has `0`
4+
LL | / enum Color {
5+
LL | |
6+
LL | | Red = 0xff0000,
7+
LL | | Green = 0x00ff00,
8+
LL | | Blue = 0x0000ff,
9+
LL | | Black = 0x000000,
10+
| | -------- first assignment of `0`
11+
LL | | White = 0x000000,
12+
| | -------- second assignment of `0`
13+
LL | | }
14+
| |_^
815

916
error: aborting due to previous error
1017

0 commit comments

Comments
 (0)