diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5004d0c80220f..bd2be07d66b30 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -814,7 +814,7 @@ impl LayoutCalculator { let (max, min) = largest_niche // We might have no inhabited variants, so pretend there's at least one. .unwrap_or((0, 0)); - let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max); + let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::discr_range_of_repr(tcx, ty, &repr, min, max); let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 369874521e57e..e830ab7fd1121 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -183,6 +183,11 @@ impl ReprOptions { /// Returns the discriminant type, given these `repr` options. /// This must only be called on enums! + /// + /// This is the "typeck type" of the discriminant, which is effectively the maximum size: + /// discriminant values will be wrapped to fit (with a lint). Layout can later decide to use a + /// smaller type for the tag that stores the discriminant at runtime and that will work just + /// fine, it just induces casts when getting/setting the discriminant. pub fn discr_type(&self) -> IntegerType { self.int.unwrap_or(IntegerType::Pointer(true)) } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 886ebddc75c97..9560d7e377fda 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -784,7 +784,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); - crate::collect::lower_enum_variant_types(tcx, def_id.to_def_id()); + crate::collect::lower_enum_variant_types(tcx, def_id); check_enum(tcx, def_id); check_variances_for_type_defn(tcx, def_id); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b72e743f95b0f..accaec634699c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -19,7 +19,7 @@ use std::cell::Cell; use std::iter; use std::ops::Bound; -use rustc_abi::ExternAbi; +use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; @@ -605,32 +605,70 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.ensure_ok().predicates_of(def_id); } -pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { +pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); let mut prev_discr = None::>; + // Some of the logic below relies on `i128` being able to hold all c_int and c_uint values. + assert!(tcx.sess.target.c_int_width < 128); + let mut min_discr = i128::MAX; + let mut max_discr = i128::MIN; // fill the discriminant values and field types for variant in def.variants() { let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - prev_discr = Some( - if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr { - def.eval_explicit_discr(tcx, const_def_id).ok() - } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { - Some(discr) - } else { + let cur_discr = if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr { + def.eval_explicit_discr(tcx, const_def_id).ok() + } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { + Some(discr) + } else { + let span = tcx.def_span(variant.def_id); + tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed { + span, + discr: prev_discr.unwrap().to_string(), + item_name: tcx.item_ident(variant.def_id), + wrapped_discr: wrapped_discr.to_string(), + }); + None + } + .unwrap_or(wrapped_discr); + + if def.repr().c() { + let c_int = Size::from_bits(tcx.sess.target.c_int_width); + let c_uint_max = i128::try_from(c_int.unsigned_int_max()).unwrap(); + // c_int is a signed type, so get a proper signed version of the discriminant + let discr_size = cur_discr.ty.int_size_and_signed(tcx).0; + let discr_val = discr_size.sign_extend(cur_discr.val); + min_discr = min_discr.min(discr_val); + max_discr = max_discr.max(discr_val); + + // The discriminant range must either fit into c_int or c_uint. + if !(min_discr >= c_int.signed_int_min() && max_discr <= c_int.signed_int_max()) + && !(min_discr >= 0 && max_discr <= c_uint_max) + { let span = tcx.def_span(variant.def_id); - tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed { + let msg = if discr_val < c_int.signed_int_min() || discr_val > c_uint_max { + "`repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`" + } else if discr_val < 0 { + "`repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`" + } else { + "`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`" + }; + tcx.node_span_lint( + rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT, + tcx.local_def_id_to_hir_id(def_id), span, - discr: prev_discr.unwrap().to_string(), - item_name: tcx.item_ident(variant.def_id), - wrapped_discr: wrapped_discr.to_string(), - }); - None + |d| { + d.primary_message(msg) + .note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C") + .help("use `repr($int_ty)` instead to explicitly set the size of this enum"); + } + ); } - .unwrap_or(wrapped_discr), - ); + } + + prev_discr = Some(cur_discr); for f in &variant.fields { tcx.ensure_ok().generics_of(f.did); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index eaec0c9857d28..c29892adb1631 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -10,7 +10,7 @@ use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations +mod improper_ctypes; // these files do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations pub(crate) use improper_ctypes::ImproperCTypesLint; use crate::lints::{ @@ -25,7 +25,6 @@ use crate::lints::{ use crate::{LateContext, LateLintPass, LintContext}; mod literal; - use literal::{int_ty_range, lint_literal, uint_ty_range}; declare_lint! { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3d0974d5d2809..bdd3d8e9ceef4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -86,6 +86,7 @@ declare_lint_pass! { REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, + REPR_C_ENUMS_LARGER_THAN_INT, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, RUST_2021_INCOMPATIBLE_OR_PATTERNS, @@ -5200,3 +5201,50 @@ declare_lint! { Warn, r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#, } + +declare_lint! { + /// The `repr_c_enums_larger_than_int` lint detects `repr(C)` enums with discriminant + /// values that do not fit into a C `int`. + /// + /// ### Example + /// + /// ```rust,ignore (only errors on 64bit) + /// #[repr(C)] + /// enum E { + /// V = 9223372036854775807, // i64::MAX + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: `repr(C)` enum discriminant does not fit into C `int` + /// --> $DIR/repr-c-big-discriminant1.rs:16:5 + /// | + /// LL | A = 9223372036854775807, // i64::MAX + /// | ^ + /// | + /// = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + /// = help: use `repr($int_ty)` instead to explicitly set the size of this enum + /// ``` + /// + /// ### Explanation + /// + /// In C, enums with discriminants that do not fit into an `int` are a portability hazard: such + /// enums are only permitted since C23, and not supported e.g. by MSVC. Furthermore, Rust + /// interprets the discriminant values of `repr(C)` enums as expressions of type `isize`, which + /// cannot be changed in a backwards-compatible way. If the discriminant is given as a literal + /// that does not fit into `isize`, it is wrapped (with a warning). This makes it impossible to + /// implement the C23 behavior of enums where the enum discriminants have no predefined type and + /// instead the enum uses a type large enough to hold all discriminants. + /// + /// Therefore, `repr(C)` enums require all discriminants to fit into a C `int`. + pub REPR_C_ENUMS_LARGER_THAN_INT, + Warn, + "repr(C) enums with discriminant values that do not fit into a C int", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #124403 ", + report_in_deps: false, + }; +} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 47b45c58b9f8c..feb987d2d419e 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -72,7 +72,10 @@ impl abi::Integer { /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr<'tcx>( + /// + /// This is the basis for computing the type of the *tag* of an enum (which can be smaller than + /// the type of the *discriminant*, which is determined by [`ReprOptions::discr_type`]). + fn discr_range_of_repr<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, repr: &ReprOptions, @@ -108,7 +111,8 @@ impl abi::Integer { abi::Integer::I8 }; - // Pick the smallest fit. + // Pick the smallest fit. Prefer unsigned; that matches clang in cases where this makes a + // difference (https://godbolt.org/z/h4xEasW1d) so it is crucial for repr(C). if unsigned_fit <= signed_fit { (cmp::max(unsigned_fit, at_least), false) } else { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index c4cb43011adb0..a332c7b257285 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -595,8 +595,8 @@ fn layout_of_uncached<'tcx>( // UnsafeCell and UnsafePinned both disable niche optimizations let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned(); - let get_discriminant_type = - |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max); + let discr_range_of_repr = + |min, max| abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min, max); let discriminants_iter = || { def.is_enum() @@ -619,7 +619,7 @@ fn layout_of_uncached<'tcx>( def.is_enum(), is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), - get_discriminant_type, + discr_range_of_repr, discriminants_iter(), !maybe_unsized, ) @@ -644,7 +644,7 @@ fn layout_of_uncached<'tcx>( def.is_enum(), is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), - get_discriminant_type, + discr_range_of_repr, discriminants_iter(), !maybe_unsized, ) else { diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 1311ee31182c6..74c4f090f3a3e 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -14,6 +14,8 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou if layout.size.bytes() >= tcx.data_layout.obj_size_bound() { bug!("size is too large, in the following layout:\n{layout:#?}"); } + // FIXME(#124403): Once `repr_c_enums_larger_than_int` is a hard error, we could assert + // here that a repr(c) enum discriminant is never larger than a c_int. if !cfg!(debug_assertions) { // Stop here, the rest is kind of expensive. diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 4f4c653cb46e7..076474caf5580 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -177,6 +177,21 @@ impl Add for isize { } } +#[lang = "neg"] +pub trait Neg { + type Output; + + fn neg(self) -> Self::Output; +} + +impl Neg for isize { + type Output = isize; + + fn neg(self) -> isize { + loop {} // Dummy impl, not actually used + } +} + #[lang = "sync"] trait Sync {} impl_marker_trait!( @@ -231,6 +246,13 @@ pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] pub unsafe fn transmute(src: Src) -> Dst; + + #[rustc_nounwind] + #[rustc_intrinsic] + pub const fn size_of() -> usize; + #[rustc_nounwind] + #[rustc_intrinsic] + pub const fn align_of() -> usize; } #[lang = "c_void"] diff --git a/tests/ui/enum-discriminant/discriminant_size.rs b/tests/ui/enum-discriminant/discriminant_size.rs index b1feff3c59e1d..537940cfb7ee3 100644 --- a/tests/ui/enum-discriminant/discriminant_size.rs +++ b/tests/ui/enum-discriminant/discriminant_size.rs @@ -2,6 +2,7 @@ #![feature(core_intrinsics)] use std::intrinsics::discriminant_value; +use std::mem::size_of; enum E1 { A, @@ -20,6 +21,14 @@ enum E3 { B = 100, } +// Enums like this are found in the ecosystem, let's make sure they get the right size. +#[repr(C)] +#[allow(overflowing_literals)] +enum UnsignedIntEnum { + A = 0, + O = 0xffffffff, // doesn't fit into `int`, but fits into `unsigned int` +} + #[repr(i128)] enum E4 { A = 0x1223_3445_5667_7889, @@ -27,24 +36,38 @@ enum E4 { } fn main() { + assert_eq!(size_of::(), 1); let mut target: [isize; 3] = [0, 0, 0]; target[1] = discriminant_value(&E1::A); assert_eq!(target, [0, 0, 0]); target[1] = discriminant_value(&E1::B); assert_eq!(target, [0, 1, 0]); + assert_eq!(size_of::(), 1); let mut target: [i8; 3] = [0, 0, 0]; target[1] = discriminant_value(&E2::A); assert_eq!(target, [0, 7, 0]); target[1] = discriminant_value(&E2::B); assert_eq!(target, [0, -2, 0]); + // E3's size is target-dependent let mut target: [isize; 3] = [0, 0, 0]; target[1] = discriminant_value(&E3::A); assert_eq!(target, [0, 42, 0]); target[1] = discriminant_value(&E3::B); assert_eq!(target, [0, 100, 0]); + #[allow(overflowing_literals)] + { + assert_eq!(size_of::(), 4); + let mut target: [isize; 3] = [0, -1, 0]; + target[1] = discriminant_value(&UnsignedIntEnum::A); + assert_eq!(target, [0, 0, 0]); + target[1] = discriminant_value(&UnsignedIntEnum::O); + assert_eq!(target, [0, 0xffffffff as isize, 0]); + } + + assert_eq!(size_of::(), 16); let mut target: [i128; 3] = [0, 0, 0]; target[1] = discriminant_value(&E4::A); assert_eq!(target, [0, 0x1223_3445_5667_7889, 0]); diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr32.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr32.stderr new file mode 100644 index 0000000000000..66eae8eccee5b --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr32.stderr @@ -0,0 +1,35 @@ +error: literal out of range for `isize` + --> $DIR/repr-c-big-discriminant1.rs:16:9 + | +LL | A = 9223372036854775807, // i64::MAX + | ^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `9223372036854775807` does not fit into the type `isize` whose range is `-2147483648..=2147483647` + = note: `#[deny(overflowing_literals)]` on by default + +error: literal out of range for `isize` + --> $DIR/repr-c-big-discriminant1.rs:24:9 + | +LL | A = -2147483649, // i32::MIN-1 + | ^^^^^^^^^^^ + | + = note: the literal `-2147483649` does not fit into the type `isize` whose range is `-2147483648..=2147483647` + +error: literal out of range for `isize` + --> $DIR/repr-c-big-discriminant1.rs:32:9 + | +LL | A = 2147483648, // i32::MAX+1 + | ^^^^^^^^^^ + | + = note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647` + +error: literal out of range for `isize` + --> $DIR/repr-c-big-discriminant1.rs:41:9 + | +LL | A = 2147483648, // i32::MAX+1 + | ^^^^^^^^^^ + | + = note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr new file mode 100644 index 0000000000000..12967ed163d4e --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr @@ -0,0 +1,62 @@ +error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/repr-c-big-discriminant1.rs:16:5 + | +LL | A = 9223372036854775807, // i64::MAX + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum +note: the lint level is defined here + --> $DIR/repr-c-big-discriminant1.rs:6:9 + | +LL | #![deny(repr_c_enums_larger_than_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/repr-c-big-discriminant1.rs:24:5 + | +LL | A = -2147483649, // i32::MIN-1 + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + +error: `repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int` + --> $DIR/repr-c-big-discriminant1.rs:34:5 + | +LL | B = -1, + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + +error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int` + --> $DIR/repr-c-big-discriminant1.rs:41:5 + | +LL | A = 2147483648, // i32::MAX+1 + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + +error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/repr-c-big-discriminant1.rs:51:5 + | +LL | A = I64_MAX as isize, + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + +error: aborting due to 5 previous errors + diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant1.rs b/tests/ui/enum-discriminant/repr-c-big-discriminant1.rs new file mode 100644 index 0000000000000..4041beadc5096 --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant1.rs @@ -0,0 +1,65 @@ +//@ revisions: ptr32 ptr64 +//@[ptr32] compile-flags: --target i686-unknown-linux-gnu +//@[ptr32] needs-llvm-components: x86 +//@[ptr64] compile-flags: --target x86_64-unknown-linux-gnu +//@[ptr64] needs-llvm-components: x86 +#![deny(repr_c_enums_larger_than_int)] + +//@ add-core-stubs +#![feature(no_core)] +#![no_core] +extern crate minicore; +use minicore::*; + +#[repr(C)] +enum OverflowingEnum1 { + A = 9223372036854775807, // i64::MAX + //[ptr32]~^ ERROR: literal out of range + //[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int` + //[ptr64]~^^^ WARN: previously accepted +} + +#[repr(C)] +enum OverflowingEnum2 { + A = -2147483649, // i32::MIN-1 + //[ptr32]~^ ERROR: literal out of range + //[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int` + //[ptr64]~^^^ WARN: previously accepted +} + +#[repr(C)] +enum OverflowingEnum3a { + A = 2147483648, // i32::MAX+1 + //[ptr32]~^ ERROR: literal out of range + B = -1, + //[ptr64]~^ ERROR: discriminant does not fit into C `unsigned int`, and a previous + //[ptr64]~^^ WARN: previously accepted +} +#[repr(C)] +enum OverflowingEnum3b { + B = -1, + A = 2147483648, // i32::MAX+1 + //[ptr32]~^ ERROR: literal out of range + //[ptr64]~^^ ERROR: discriminant does not fit into C `int`, and a previous + //[ptr64]~^^^ WARN: previously accepted +} + +const I64_MAX: i64 = 9223372036854775807; + +#[repr(C)] +enum OverflowingEnum4 { + A = I64_MAX as isize, + //[ptr64]~^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int` + //[ptr64]~^^ WARN: previously accepted + // No warning/error on 32bit targets, but the `as isize` hints that wrapping is occurring. +} + +// Enums like this are found in the ecosystem, let's make sure they get accepted. +#[repr(C)] +#[allow(overflowing_literals)] +enum OkayEnum { + A = 0, + O = 0xffffffff, +} + +fn main() {} diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr32.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr32.stderr new file mode 100644 index 0000000000000..cf714c028947f --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr32.stderr @@ -0,0 +1,11 @@ +error[E0370]: enum discriminant overflowed + --> $DIR/repr-c-big-discriminant2.rs:22:5 + | +LL | B, // +1 + | ^ overflowed on value after 2147483647 + | + = note: explicitly set `B = -2147483648` if that is desired outcome + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0370`. diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr new file mode 100644 index 0000000000000..8598a5c4525be --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr @@ -0,0 +1,18 @@ +error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int` + --> $DIR/repr-c-big-discriminant2.rs:22:5 + | +LL | B, // +1 + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum +note: the lint level is defined here + --> $DIR/repr-c-big-discriminant2.rs:6:9 + | +LL | #![deny(repr_c_enums_larger_than_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant2.rs b/tests/ui/enum-discriminant/repr-c-big-discriminant2.rs new file mode 100644 index 0000000000000..3e9ea925b1023 --- /dev/null +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant2.rs @@ -0,0 +1,28 @@ +//@ revisions: ptr32 ptr64 +//@[ptr32] compile-flags: --target i686-unknown-linux-gnu +//@[ptr32] needs-llvm-components: x86 +//@[ptr64] compile-flags: --target x86_64-unknown-linux-gnu +//@[ptr64] needs-llvm-components: x86 +#![deny(repr_c_enums_larger_than_int)] + +//@ add-core-stubs +#![feature(no_core)] +#![no_core] +extern crate minicore; +use minicore::*; + +// Separate test since it suppresses other errors on ptr32: +// ensure we find the bad discriminant when it is implicitly computed by incrementing +// the previous discriminant. + +#[repr(C)] +enum OverflowingEnum { + NEG = -1, + A = 2147483647, // i32::MAX + B, // +1 + //[ptr32]~^ ERROR: enum discriminant overflowed + //[ptr64]~^^ ERROR: discriminant does not fit into C `int` + //[ptr64]~^^^ WARN: previously accepted +} + +fn main() {}