Skip to content

Commit 5f6145e

Browse files
committed
repr(C) enums: fix enum size mismatch on MSVC targets
1 parent 408eacf commit 5f6145e

File tree

19 files changed

+166
-19
lines changed

19 files changed

+166
-19
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
757757

758758
let niche_filling_layout = calculate_niche_filling_layout();
759759

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

878-
let typeck_ity = Integer::from_attr(dl, repr.discr_type());
878+
let typeck_ity = Integer::from_attr(dl, repr.discr_type(dl));
879879
if typeck_ity < min_ity {
880880
// It is a bug if Layout decided on a greater discriminant size than typeck for
881881
// some reason at this point (based on values discriminant can take on). Mostly

compiler/rustc_abi/src/lib.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,22 @@ impl ReprOptions {
183183

184184
/// Returns the discriminant type, given these `repr` options.
185185
/// This must only be called on enums!
186-
pub fn discr_type(&self) -> IntegerType {
187-
self.int.unwrap_or(IntegerType::Pointer(true))
186+
///
187+
/// This is the "typeck type" of the discriminant, which is effectively the maximum size:
188+
/// discriminant values will be wrapped to fit (with a lint). Layout can later decide to use a
189+
/// smaller type (which it will do depending on the actual discriminant values, also enforcing
190+
/// `c_enum_min_size` along the way) and that will work just fine, it just induces casts when
191+
/// getting/setting the discriminant.
192+
pub fn discr_type(&self, cx: &impl HasDataLayout) -> IntegerType {
193+
self.int.unwrap_or(
194+
if self.c()
195+
&& let Some(max_size) = cx.data_layout().c_enum_max_size
196+
{
197+
IntegerType::Fixed(max_size, true)
198+
} else {
199+
IntegerType::Pointer(true)
200+
},
201+
)
188202
}
189203

190204
/// Returns `true` if this `#[repr()]` should inhabit "smart enum
@@ -274,6 +288,8 @@ pub struct TargetDataLayout {
274288
/// Note: This isn't in LLVM's data layout string, it is `short_enum`
275289
/// so the only valid spec for LLVM is c_int::BITS or 8
276290
pub c_enum_min_size: Integer,
291+
/// Maximum size of #[repr(C)] enums (defaults to pointer size).
292+
pub c_enum_max_size: Option<Integer>,
277293
}
278294

279295
impl Default for TargetDataLayout {
@@ -307,6 +323,7 @@ impl Default for TargetDataLayout {
307323
address_space_info: vec![],
308324
instruction_address_space: AddressSpace::ZERO,
309325
c_enum_min_size: Integer::I32,
326+
c_enum_max_size: None,
310327
}
311328
}
312329
}
@@ -327,7 +344,7 @@ impl TargetDataLayout {
327344
/// [llvm data layout string](https://llvm.org/docs/LangRef.html#data-layout)
328345
///
329346
/// This function doesn't fill `c_enum_min_size` and it will always be `I32` since it can not be
330-
/// determined from llvm string.
347+
/// determined from llvm string. Likewise, it does not fill in `c_enum_max_size`.
331348
pub fn parse_from_llvm_datalayout_string<'a>(
332349
input: &'a str,
333350
default_address_space: AddressSpace,

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
607607

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

compiler/rustc_hir_analysis/src/collect/type_of.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
4646
}) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
4747

4848
Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
49-
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
49+
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type(&tcx).to_ty(tcx)
5050
}
5151
// Sort of affects the type system, but only for the purpose of diagnostics
5252
// so no need for ConstArg.

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ impl<'tcx> AdtDef<'tcx> {
526526
) -> Result<Discr<'tcx>, ErrorGuaranteed> {
527527
assert!(self.is_enum());
528528

529-
let repr_type = self.repr().discr_type();
529+
let repr_type = self.repr().discr_type(&tcx);
530530
match tcx.const_eval_poly(expr_did) {
531531
Ok(val) => {
532532
let typing_env = ty::TypingEnv::post_analysis(tcx, expr_did);
@@ -561,7 +561,7 @@ impl<'tcx> AdtDef<'tcx> {
561561
tcx: TyCtxt<'tcx>,
562562
) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> {
563563
assert!(self.is_enum());
564-
let repr_type = self.repr().discr_type();
564+
let repr_type = self.repr().discr_type(&tcx);
565565
let initial = repr_type.initial_discriminant(tcx);
566566
let mut prev_discr = None::<Discr<'tcx>>;
567567
self.variants().iter_enumerated().map(move |(i, v)| {
@@ -600,7 +600,7 @@ impl<'tcx> AdtDef<'tcx> {
600600
{
601601
val
602602
} else {
603-
self.repr().discr_type().initial_discriminant(tcx)
603+
self.repr().discr_type(&tcx).initial_discriminant(tcx)
604604
};
605605
explicit_value.checked_add(tcx, offset as u128).0
606606
}

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ impl<'tcx> Ty<'tcx> {
15891589
/// Returns the type of the discriminant of this type.
15901590
pub fn discriminant_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
15911591
match self.kind() {
1592-
ty::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(tcx),
1592+
ty::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type(&tcx).to_ty(tcx),
15931593
ty::Coroutine(_, args) => args.as_coroutine().discr_ty(tcx),
15941594

15951595
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {

compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
197197
let (source, ty) = if let ty::Adt(adt_def, ..) = source_expr.ty.kind()
198198
&& adt_def.is_enum()
199199
{
200-
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
200+
let discr_ty = adt_def.repr().discr_type(&this.tcx).to_ty(this.tcx);
201201
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
202202
let discr = this.temp(discr_ty, source_expr.span);
203203
this.cfg.push_assign(

compiler/rustc_mir_build/src/builder/matches/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
8989
otherwise_block,
9090
);
9191
debug!("num_enum_variants: {}", adt_def.variants().len());
92-
let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
92+
let discr_ty = adt_def.repr().discr_type(&self.tcx).to_ty(self.tcx);
9393
let discr = self.temp(discr_ty, test.span);
9494
self.cfg.push_assign(
9595
block,

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
292292
let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);
293293

294294
use rustc_middle::ty::util::IntTypeExt;
295-
let ty = adt_def.repr().discr_type();
295+
let ty = adt_def.repr().discr_type(&tcx);
296296
let discr_ty = ty.to_ty(tcx);
297297

298298
let size = tcx

compiler/rustc_mir_transform/src/elaborate_drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ where
937937
// Additionally, we do not want to switch on the
938938
// discriminant after it is free-ed, because that
939939
// way lies only trouble.
940-
let discr_ty = adt.repr().discr_type().to_ty(self.tcx());
940+
let discr_ty = adt.repr().discr_type(&self.tcx()).to_ty(self.tcx());
941941
let discr = Place::from(self.new_temp(discr_ty));
942942
let discr_rv = Rvalue::Discriminant(self.place);
943943
let switch_block = BasicBlockData::new_stmts(

0 commit comments

Comments
 (0)