Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {

let niche_filling_layout = calculate_niche_filling_layout();

let discr_type = repr.discr_type();
let discr_type = repr.discr_type(dl);
let discr_int = Integer::from_attr(dl, discr_type);
// Because we can only represent one range of valid values, we'll look for the
// largest range of invalid values and pick everything else as the range of valid
Expand Down Expand Up @@ -875,7 +875,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
return Err(LayoutCalculatorError::SizeOverflow);
}

let typeck_ity = Integer::from_attr(dl, repr.discr_type());
let typeck_ity = Integer::from_attr(dl, repr.discr_type(dl));
if typeck_ity < min_ity {
// It is a bug if Layout decided on a greater discriminant size than typeck for
// some reason at this point (based on values discriminant can take on). Mostly
Expand Down
22 changes: 19 additions & 3 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,18 @@ impl ReprOptions {

/// Returns the discriminant type, given these `repr` options.
/// This must only be called on enums!
pub fn discr_type(&self) -> IntegerType {
self.int.unwrap_or(IntegerType::Pointer(true))
///
/// 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 (which it will do depending on the actual discriminant values, also enforcing
/// `c_enum_min_size` along the way) and that will work just fine, it just induces casts when
/// getting/setting the discriminant.
pub fn discr_type(&self, cx: &impl HasDataLayout) -> IntegerType {
self.int.unwrap_or(if self.c() {
IntegerType::Fixed(cx.data_layout().c_enum_max_size, true)
} else {
IntegerType::Pointer(true)
})
}

/// Returns `true` if this `#[repr()]` should inhabit "smart enum
Expand Down Expand Up @@ -274,6 +284,8 @@ pub struct TargetDataLayout {
/// Note: This isn't in LLVM's data layout string, it is `short_enum`
/// so the only valid spec for LLVM is c_int::BITS or 8
pub c_enum_min_size: Integer,
/// Maximum size of #[repr(C)] enums (defaults to c_longlong::BITS, which is always 64).
pub c_enum_max_size: Integer,
}

impl Default for TargetDataLayout {
Expand Down Expand Up @@ -307,6 +319,10 @@ impl Default for TargetDataLayout {
address_space_info: vec![],
instruction_address_space: AddressSpace::ZERO,
c_enum_min_size: Integer::I32,
// C23 allows enums to have any integer type. The largest integer type in the standard
// is `long long`, which is always 64bits (judging from our own definition in
// `library/core/src/ffi/primitives.rs`).
c_enum_max_size: Integer::I64,
}
}
}
Expand All @@ -327,7 +343,7 @@ impl TargetDataLayout {
/// [llvm data layout string](https://llvm.org/docs/LangRef.html#data-layout)
///
/// This function doesn't fill `c_enum_min_size` and it will always be `I32` since it can not be
/// determined from llvm string.
/// determined from llvm string. Likewise, it does not fill in `c_enum_max_size`.
pub fn parse_from_llvm_datalayout_string<'a>(
input: &'a str,
default_address_space: AddressSpace,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {

pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
let def = tcx.adt_def(def_id);
let repr_type = def.repr().discr_type();
let repr_type = def.repr().discr_type(&tcx);
let initial = repr_type.initial_discriminant(tcx);
let mut prev_discr = None::<Discr<'_>>;

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
}) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),

Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type(&tcx).to_ty(tcx)
}
// Sort of affects the type system, but only for the purpose of diagnostics
// so no need for ConstArg.
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ impl<'tcx> AdtDef<'tcx> {
) -> Result<Discr<'tcx>, ErrorGuaranteed> {
assert!(self.is_enum());

let repr_type = self.repr().discr_type();
let repr_type = self.repr().discr_type(&tcx);
match tcx.const_eval_poly(expr_did) {
Ok(val) => {
let typing_env = ty::TypingEnv::post_analysis(tcx, expr_did);
Expand Down Expand Up @@ -561,7 +561,7 @@ impl<'tcx> AdtDef<'tcx> {
tcx: TyCtxt<'tcx>,
) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> {
assert!(self.is_enum());
let repr_type = self.repr().discr_type();
let repr_type = self.repr().discr_type(&tcx);
let initial = repr_type.initial_discriminant(tcx);
let mut prev_discr = None::<Discr<'tcx>>;
self.variants().iter_enumerated().map(move |(i, v)| {
Expand Down Expand Up @@ -600,7 +600,7 @@ impl<'tcx> AdtDef<'tcx> {
{
val
} else {
self.repr().discr_type().initial_discriminant(tcx)
self.repr().discr_type(&tcx).initial_discriminant(tcx)
};
explicit_value.checked_add(tcx, offset as u128).0
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,7 @@ impl<'tcx> Ty<'tcx> {
/// Returns the type of the discriminant of this type.
pub fn discriminant_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self.kind() {
ty::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(tcx),
ty::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type(&tcx).to_ty(tcx),
ty::Coroutine(_, args) => args.as_coroutine().discr_ty(tcx),

ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let (source, ty) = if let ty::Adt(adt_def, ..) = source_expr.ty.kind()
&& adt_def.is_enum()
{
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
let discr_ty = adt_def.repr().discr_type(&this.tcx).to_ty(this.tcx);
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
let discr = this.temp(discr_ty, source_expr.span);
this.cfg.push_assign(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/builder/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
otherwise_block,
);
debug!("num_enum_variants: {}", adt_def.variants().len());
let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
let discr_ty = adt_def.repr().discr_type(&self.tcx).to_ty(self.tcx);
let discr = self.temp(discr_ty, test.span);
self.cfg.push_assign(
block,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);

use rustc_middle::ty::util::IntTypeExt;
let ty = adt_def.repr().discr_type();
let ty = adt_def.repr().discr_type(&tcx);
let discr_ty = ty.to_ty(tcx);

let size = tcx
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/elaborate_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ where
// Additionally, we do not want to switch on the
// discriminant after it is free-ed, because that
// way lies only trouble.
let discr_ty = adt.repr().discr_type().to_ty(self.tcx());
let discr_ty = adt.repr().discr_type(&self.tcx()).to_ty(self.tcx());
let discr = Place::from(self.new_temp(discr_ty));
let discr_rv = Rvalue::Discriminant(self.place);
let switch_block = BasicBlockData::new_stmts(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/large_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
let const_assign = StatementKind::Assign(Box::new((place, rval)));

let discr_place =
Place::from(patch.new_temp(adt_def.repr().discr_type().to_ty(tcx), span));
Place::from(patch.new_temp(adt_def.repr().discr_type(&tcx).to_ty(tcx), span));
let store_discr =
StatementKind::Assign(Box::new((discr_place, Rvalue::Discriminant(*rhs))));

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_target/src/spec/base/msvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ pub(crate) fn opts() -> TargetOptions {
emit_debug_gdb_scripts: false,
archive_format: "coff".into(),

// MSVC does not seem to ever automatically increase enums beyond their default size (see
// <https://github.com/rust-lang/rust/issues/124403>, <https://godbolt.org/z/1Pdb3hP9E>).
c_enum_min_bits: Some(32),
c_enum_max_bits: Some(32),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also affects the UEFI targets. Is that intended?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, they use the MSVC ABI for things, yes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the psABI document the enum size rules? I would be surprised if it did.^^


// Currently this is the only supported method of debuginfo on MSVC
// where `*.pdb` files show up next to the final artifact.
split_debuginfo: SplitDebuginfo::Packed,
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2386,6 +2386,13 @@ impl Target {
self.c_enum_min_bits.unwrap_or(self.c_int_width as _),
))
.map_err(|err| TargetDataLayoutErrors::InvalidBitsSize { err })?;
dl.c_enum_max_size = Integer::from_size(Size::from_bits(
// C23 allows enums to have any integer type. The largest integer type in the standard
// is `long long`, which is always 64bits (judging from our own definition in
// `library/core/src/ffi/primitives.rs`). Hence we default to 64.
self.c_enum_max_bits.unwrap_or(64),
))
.map_err(|err| TargetDataLayoutErrors::InvalidBitsSize { err })?;

Ok(dl)
}
Expand Down Expand Up @@ -2797,6 +2804,8 @@ pub struct TargetOptions {

/// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
pub c_enum_min_bits: Option<u64>,
/// Maximum number of bits in #[repr(C)] enum. Defaults to the pointer size.
pub c_enum_max_bits: Option<u64>,

/// Whether or not the DWARF `.debug_aranges` section should be generated.
pub generate_arange_section: bool,
Expand Down Expand Up @@ -3044,6 +3053,7 @@ impl Default for TargetOptions {
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
supported_sanitizers: SanitizerSet::empty(),
c_enum_min_bits: None,
c_enum_max_bits: None,
generate_arange_section: true,
supports_stack_protector: true,
entry_name: "main".into(),
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/enum_clike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
if let ty::Adt(adt, _) = ty.kind()
&& adt.is_enum()
{
ty = adt.repr().discr_type().to_ty(cx.tcx);
ty = adt.repr().discr_type(&cx.tcx).to_ty(cx.tcx);
}
match ty.kind() {
ty::Int(IntTy::Isize) => {
Expand Down
3 changes: 2 additions & 1 deletion src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
&& let hir_def::AdtId::EnumId(e) = id.0
{
let enum_data = self.db.enum_signature(e);
let ty = enum_data.repr.unwrap_or_default().discr_type();
let dl = self.db.target_data_layout(self.krate).expect("FIXME");
let ty = enum_data.repr.unwrap_or_default().discr_type(&*dl);
return chalk_ir::TyKind::Scalar(match ty {
hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed {
true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize),
Expand Down
66 changes: 64 additions & 2 deletions tests/auxiliary/minicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,67 @@ macro_rules! stringify {
};
}

#[lang = "neg"]
pub trait Neg {
type Output;

fn neg(self) -> Self::Output;
}

impl Neg for i32 {
type Output = i32;

fn neg(self) -> i32 {
loop {} // Dummy impl, not actually used
}
}

impl Neg for i64 {
type Output = i64;

fn neg(self) -> i64 {
loop {} // Dummy impl, not actually used
}
}

impl Neg for isize {
type Output = isize;

fn neg(self) -> isize {
loop {} // Dummy impl, not actually used
}
}

#[lang = "add"]
pub trait Add<Rhs = Self> {
type Output;

fn add(self, _: Rhs) -> Self::Output;
}

impl Add<isize> for isize {
impl Add for isize {
type Output = isize;

fn add(self, other: isize) -> isize {
7 // avoid needing to add all of the overflow handling and panic language items
loop {} // Dummy impl, not actually used
}
}

#[lang = "eq"]
pub trait PartialEq<Rhs = Self> {
fn eq(&self, other: &Rhs) -> bool;

fn ne(&self, other: &Rhs) -> bool {
match self.eq(other) {
true => false,
false => true,
}
}
}

impl PartialEq for usize {
fn eq(&self, other: &Self) -> bool {
loop {} // Dummy impl, not actually used
}
}

Expand Down Expand Up @@ -231,6 +280,19 @@ pub mod mem {
#[rustc_nounwind]
#[rustc_intrinsic]
pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst;

#[rustc_nounwind]
#[rustc_intrinsic]
pub const fn size_of<T>() -> usize;
#[rustc_nounwind]
#[rustc_intrinsic]
pub const fn align_of<T>() -> usize;
}

pub mod hint {
#[rustc_nounwind]
#[rustc_intrinsic]
pub const unsafe fn unreachable() -> !;
}

#[lang = "c_void"]
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/enum-discriminant/repr-c-size.msvc32.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
warning: literal out of range for `i32`
--> $DIR/repr-c-size.rs:23:9
|
LL | A = 9223372036854775807, // i64::MAX
| ^^^^^^^^^^^^^^^^^^^
|
= note: the literal `9223372036854775807` does not fit into the type `i32` whose range is `-2147483648..=2147483647`
= help: consider using the type `i64` instead
note: the lint level is defined here
--> $DIR/repr-c-size.rs:21:8
|
LL | #[warn(overflowing_literals)]
| ^^^^^^^^^^^^^^^^^^^^

warning: literal out of range for `i32`
--> $DIR/repr-c-size.rs:41:9
|
LL | A = 4294967294, // u32::MAX - 1
| ^^^^^^^^^^
|
= note: the literal `4294967294` does not fit into the type `i32` whose range is `-2147483648..=2147483647`
= help: consider using the type `u32` instead
note: the lint level is defined here
--> $DIR/repr-c-size.rs:39:8
|
LL | #[warn(overflowing_literals)]
| ^^^^^^^^^^^^^^^^^^^^

warning: 2 warnings emitted

30 changes: 30 additions & 0 deletions tests/ui/enum-discriminant/repr-c-size.msvc64.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
warning: literal out of range for `i32`
--> $DIR/repr-c-size.rs:23:9
|
LL | A = 9223372036854775807, // i64::MAX
| ^^^^^^^^^^^^^^^^^^^
|
= note: the literal `9223372036854775807` does not fit into the type `i32` whose range is `-2147483648..=2147483647`
= help: consider using the type `i64` instead
note: the lint level is defined here
--> $DIR/repr-c-size.rs:21:8
|
LL | #[warn(overflowing_literals)]
| ^^^^^^^^^^^^^^^^^^^^

warning: literal out of range for `i32`
--> $DIR/repr-c-size.rs:41:9
|
LL | A = 4294967294, // u32::MAX - 1
| ^^^^^^^^^^
|
= note: the literal `4294967294` does not fit into the type `i32` whose range is `-2147483648..=2147483647`
= help: consider using the type `u32` instead
note: the lint level is defined here
--> $DIR/repr-c-size.rs:39:8
|
LL | #[warn(overflowing_literals)]
| ^^^^^^^^^^^^^^^^^^^^

warning: 2 warnings emitted

Loading
Loading