Skip to content

Commit 929a236

Browse files
committed
do not complain about enums where all discriminants fit into a c_uint
1 parent 8437509 commit 929a236

8 files changed

+122
-16
lines changed

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,10 @@ pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
610610
let repr_type = def.repr().discr_type();
611611
let initial = repr_type.initial_discriminant(tcx);
612612
let mut prev_discr = None::<Discr<'_>>;
613+
// Some of the logic below relies on `i128` being able to hold all c_int and c_uint values.
614+
assert!(tcx.sess.target.c_int_width < 128);
615+
let mut min_discr = i128::MAX;
616+
let mut max_discr = i128::MIN;
613617

614618
// fill the discriminant values and field types
615619
for variant in def.variants() {
@@ -631,19 +635,32 @@ pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
631635
.unwrap_or(wrapped_discr);
632636

633637
if def.repr().c() {
638+
let c_int = Size::from_bits(tcx.sess.target.c_int_width);
639+
let c_uint_max = i128::try_from(c_int.unsigned_int_max()).unwrap();
634640
// c_int is a signed type, so get a proper signed version of the discriminant
635641
let discr_size = cur_discr.ty.int_size_and_signed(tcx).0;
636642
let discr_val = discr_size.sign_extend(cur_discr.val);
643+
min_discr = min_discr.min(discr_val);
644+
max_discr = max_discr.max(discr_val);
637645

638-
let c_int = Size::from_bits(tcx.sess.target.c_int_width);
639-
if discr_val < c_int.signed_int_min() || discr_val > c_int.signed_int_max() {
646+
// The discriminant range must either fit into c_int or c_uint.
647+
if !(min_discr >= c_int.signed_int_min() && max_discr <= c_int.signed_int_max())
648+
&& !(min_discr >= 0 && max_discr <= c_uint_max)
649+
{
640650
let span = tcx.def_span(variant.def_id);
651+
let msg = if discr_val < c_int.signed_int_min() || discr_val > c_uint_max {
652+
"`repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`"
653+
} else if discr_val < 0 {
654+
"`repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`"
655+
} else {
656+
"`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`"
657+
};
641658
tcx.node_span_lint(
642659
rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT,
643660
tcx.local_def_id_to_hir_id(def_id),
644661
span,
645662
|d| {
646-
d.primary_message("`repr(C)` enum discriminant does not fit into C `int`")
663+
d.primary_message(msg)
647664
.note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C")
648665
.help("use `repr($int_ty)` instead to explicitly set the size of this enum");
649666
}

tests/ui/enum-discriminant/discriminant_size.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![feature(core_intrinsics)]
33

44
use std::intrinsics::discriminant_value;
5+
use std::mem::size_of;
56

67
enum E1 {
78
A,
@@ -20,31 +21,53 @@ enum E3 {
2021
B = 100,
2122
}
2223

24+
// Enums like this are found in the ecosystem, let's make sure they get the right size.
25+
#[repr(C)]
26+
#[allow(overflowing_literals)]
27+
enum UnsignedIntEnum {
28+
A = 0,
29+
O = 0xffffffff, // doesn't fit into `int`, but fits into `unsigned int`
30+
}
31+
2332
#[repr(i128)]
2433
enum E4 {
2534
A = 0x1223_3445_5667_7889,
2635
B = -0x1223_3445_5667_7889,
2736
}
2837

2938
fn main() {
39+
assert_eq!(size_of::<E1>(), 1);
3040
let mut target: [isize; 3] = [0, 0, 0];
3141
target[1] = discriminant_value(&E1::A);
3242
assert_eq!(target, [0, 0, 0]);
3343
target[1] = discriminant_value(&E1::B);
3444
assert_eq!(target, [0, 1, 0]);
3545

46+
assert_eq!(size_of::<E2>(), 1);
3647
let mut target: [i8; 3] = [0, 0, 0];
3748
target[1] = discriminant_value(&E2::A);
3849
assert_eq!(target, [0, 7, 0]);
3950
target[1] = discriminant_value(&E2::B);
4051
assert_eq!(target, [0, -2, 0]);
4152

53+
// E3's size is target-dependent
4254
let mut target: [isize; 3] = [0, 0, 0];
4355
target[1] = discriminant_value(&E3::A);
4456
assert_eq!(target, [0, 42, 0]);
4557
target[1] = discriminant_value(&E3::B);
4658
assert_eq!(target, [0, 100, 0]);
4759

60+
#[allow(overflowing_literals)]
61+
{
62+
assert_eq!(size_of::<UnsignedIntEnum>(), 4);
63+
let mut target: [isize; 3] = [0, -1, 0];
64+
target[1] = discriminant_value(&UnsignedIntEnum::A);
65+
assert_eq!(target, [0, 0, 0]);
66+
target[1] = discriminant_value(&UnsignedIntEnum::O);
67+
assert_eq!(target, [0, 0xffffffff as isize, 0]);
68+
}
69+
70+
assert_eq!(size_of::<E4>(), 16);
4871
let mut target: [i128; 3] = [0, 0, 0];
4972
target[1] = discriminant_value(&E4::A);
5073
assert_eq!(target, [0, 0x1223_3445_5667_7889, 0]);

tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr32.stderr

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,21 @@ LL | A = -2147483649, // i32::MIN-1
1515
|
1616
= note: the literal `-2147483649` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
1717

18-
error: aborting due to 2 previous errors
18+
error: literal out of range for `isize`
19+
--> $DIR/repr-c-big-discriminant1.rs:32:9
20+
|
21+
LL | A = 2147483648, // i32::MAX+1
22+
| ^^^^^^^^^^
23+
|
24+
= note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
25+
26+
error: literal out of range for `isize`
27+
--> $DIR/repr-c-big-discriminant1.rs:41:9
28+
|
29+
LL | A = 2147483648, // i32::MAX+1
30+
| ^^^^^^^^^^
31+
|
32+
= note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
33+
34+
error: aborting due to 4 previous errors
1935

tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: `repr(C)` enum discriminant does not fit into C `int`
1+
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
22
--> $DIR/repr-c-big-discriminant1.rs:16:5
33
|
44
LL | A = 9223372036854775807, // i64::MAX
@@ -14,7 +14,7 @@ note: the lint level is defined here
1414
LL | #![deny(repr_c_enums_larger_than_int)]
1515
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1616

17-
error: `repr(C)` enum discriminant does not fit into C `int`
17+
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
1818
--> $DIR/repr-c-big-discriminant1.rs:24:5
1919
|
2020
LL | A = -2147483649, // i32::MIN-1
@@ -25,9 +25,31 @@ LL | A = -2147483649, // i32::MIN-1
2525
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
2626
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
2727

28-
error: `repr(C)` enum discriminant does not fit into C `int`
28+
error: `repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`
2929
--> $DIR/repr-c-big-discriminant1.rs:34:5
3030
|
31+
LL | B = -1,
32+
| ^
33+
|
34+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
35+
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
36+
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
37+
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
38+
39+
error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`
40+
--> $DIR/repr-c-big-discriminant1.rs:41:5
41+
|
42+
LL | A = 2147483648, // i32::MAX+1
43+
| ^
44+
|
45+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
46+
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
47+
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
48+
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
49+
50+
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
51+
--> $DIR/repr-c-big-discriminant1.rs:51:5
52+
|
3153
LL | A = I64_MAX as isize,
3254
| ^
3355
|
@@ -36,5 +58,5 @@ LL | A = I64_MAX as isize,
3658
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
3759
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
3860

39-
error: aborting due to 3 previous errors
61+
error: aborting due to 5 previous errors
4062

tests/ui/enum-discriminant/repr-c-big-discriminant1.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,51 @@ use minicore::*;
1515
enum OverflowingEnum1 {
1616
A = 9223372036854775807, // i64::MAX
1717
//[ptr32]~^ ERROR: literal out of range
18-
//[ptr64]~^^ ERROR: discriminant does not fit into C `int`
18+
//[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
1919
//[ptr64]~^^^ WARN: previously accepted
2020
}
2121

2222
#[repr(C)]
2323
enum OverflowingEnum2 {
2424
A = -2147483649, // i32::MIN-1
2525
//[ptr32]~^ ERROR: literal out of range
26-
//[ptr64]~^^ ERROR: discriminant does not fit into C `int`
26+
//[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
27+
//[ptr64]~^^^ WARN: previously accepted
28+
}
29+
30+
#[repr(C)]
31+
enum OverflowingEnum3a {
32+
A = 2147483648, // i32::MAX+1
33+
//[ptr32]~^ ERROR: literal out of range
34+
B = -1,
35+
//[ptr64]~^ ERROR: discriminant does not fit into C `unsigned int`, and a previous
36+
//[ptr64]~^^ WARN: previously accepted
37+
}
38+
#[repr(C)]
39+
enum OverflowingEnum3b {
40+
B = -1,
41+
A = 2147483648, // i32::MAX+1
42+
//[ptr32]~^ ERROR: literal out of range
43+
//[ptr64]~^^ ERROR: discriminant does not fit into C `int`, and a previous
2744
//[ptr64]~^^^ WARN: previously accepted
2845
}
2946

3047
const I64_MAX: i64 = 9223372036854775807;
3148

3249
#[repr(C)]
33-
enum OverflowingEnum3 {
50+
enum OverflowingEnum4 {
3451
A = I64_MAX as isize,
35-
//[ptr64]~^ ERROR: discriminant does not fit into C `int`
52+
//[ptr64]~^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
3653
//[ptr64]~^^ WARN: previously accepted
3754
// No warning/error on 32bit targets, but the `as isize` hints that wrapping is occurring.
3855
}
3956

57+
// Enums like this are found in the ecosystem, let's make sure they get accepted.
58+
#[repr(C)]
59+
#[allow(overflowing_literals)]
60+
enum OkayEnum {
61+
A = 0,
62+
O = 0xffffffff,
63+
}
64+
4065
fn main() {}

tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr32.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0370]: enum discriminant overflowed
2-
--> $DIR/repr-c-big-discriminant2.rs:19:5
2+
--> $DIR/repr-c-big-discriminant2.rs:22:5
33
|
44
LL | B, // +1
55
| ^ overflowed on value after 2147483647

tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: `repr(C)` enum discriminant does not fit into C `int`
2-
--> $DIR/repr-c-big-discriminant2.rs:19:5
1+
error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`
2+
--> $DIR/repr-c-big-discriminant2.rs:22:5
33
|
44
LL | B, // +1
55
| ^

tests/ui/enum-discriminant/repr-c-big-discriminant2.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
extern crate minicore;
1212
use minicore::*;
1313

14-
// Separate test since it suppresses other errors on ptr32
14+
// Separate test since it suppresses other errors on ptr32:
15+
// ensure we find the bad discriminant when it is implicitly computed by incrementing
16+
// the previous discriminant.
1517

1618
#[repr(C)]
1719
enum OverflowingEnum {
20+
NEG = -1,
1821
A = 2147483647, // i32::MAX
1922
B, // +1
2023
//[ptr32]~^ ERROR: enum discriminant overflowed

0 commit comments

Comments
 (0)