Rollup merge of #144390 - oli-obk:arbitrary-enum-discrs, r=SparrowLii

Remove dead code and extend test coverage and diagnostics around it

I was staring a bit at the `dont_niche_optimize_enum` variable and figured out that part of it is dead code (at least today it is). I changed the diagnostic and test around the code that makes that part dead code, so everything that makes removing that code sound is visible in this PR
This commit is contained in:
Matthias Krüger
2025-07-25 11:16:39 +02:00
committed by GitHub
6 changed files with 65 additions and 48 deletions

View File

@@ -313,7 +313,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
scalar_valid_range: (Bound<u128>, Bound<u128>), scalar_valid_range: (Bound<u128>, Bound<u128>),
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>, discriminants: impl Iterator<Item = (VariantIdx, i128)>,
dont_niche_optimize_enum: bool,
always_sized: bool, always_sized: bool,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let (present_first, present_second) = { let (present_first, present_second) = {
@@ -352,13 +351,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// structs. (We have also handled univariant enums // structs. (We have also handled univariant enums
// that allow representation optimization.) // that allow representation optimization.)
assert!(is_enum); assert!(is_enum);
self.layout_of_enum( self.layout_of_enum(repr, variants, discr_range_of_repr, discriminants)
repr,
variants,
discr_range_of_repr,
discriminants,
dont_niche_optimize_enum,
)
} }
} }
@@ -599,7 +592,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>, discriminants: impl Iterator<Item = (VariantIdx, i128)>,
dont_niche_optimize_enum: bool,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
// Until we've decided whether to use the tagged or // Until we've decided whether to use the tagged or
// niche filling LayoutData, we don't want to intern the // niche filling LayoutData, we don't want to intern the
@@ -618,7 +610,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} }
let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> { let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
if dont_niche_optimize_enum { if repr.inhibit_enum_layout_opt() {
return None; return None;
} }

View File

@@ -1642,20 +1642,40 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
if def.repr().int.is_none() { if def.repr().int.is_none() {
let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const)); let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const));
let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_)); let get_disr = |var: &ty::VariantDef| match var.discr {
ty::VariantDiscr::Explicit(disr) => Some(disr),
ty::VariantDiscr::Relative(_) => None,
};
let has_non_units = def.variants().iter().any(|var| !is_unit(var)); let non_unit = def.variants().iter().find(|var| !is_unit(var));
let disr_units = def.variants().iter().any(|var| is_unit(var) && has_disr(var)); let disr_unit =
let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var)); def.variants().iter().filter(|var| is_unit(var)).find_map(|var| get_disr(var));
let disr_non_unit =
def.variants().iter().filter(|var| !is_unit(var)).find_map(|var| get_disr(var));
if disr_non_unit || (disr_units && has_non_units) { if disr_non_unit.is_some() || (disr_unit.is_some() && non_unit.is_some()) {
struct_span_code_err!( let mut err = struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
tcx.def_span(def_id), tcx.def_span(def_id),
E0732, E0732,
"`#[repr(inttype)]` must be specified" "`#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants"
) );
.emit(); if let Some(disr_non_unit) = disr_non_unit {
err.span_label(
tcx.def_span(disr_non_unit),
"explicit discriminant on non-unit variant specified here",
);
} else {
err.span_label(
tcx.def_span(disr_unit.unwrap()),
"explicit discriminant specified here",
);
err.span_label(
tcx.def_span(non_unit.unwrap().def_id),
"non-unit discriminant declared here",
);
}
err.emit();
} }
} }

View File

@@ -603,12 +603,6 @@ fn layout_of_uncached<'tcx>(
.flatten() .flatten()
}; };
let dont_niche_optimize_enum = def.repr().inhibit_enum_layout_opt()
|| def
.variants()
.iter_enumerated()
.any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()));
let maybe_unsized = def.is_struct() let maybe_unsized = def.is_struct()
&& def.non_enum_variant().tail_opt().is_some_and(|last_field| { && def.non_enum_variant().tail_opt().is_some_and(|last_field| {
let typing_env = ty::TypingEnv::post_analysis(tcx, def.did()); let typing_env = ty::TypingEnv::post_analysis(tcx, def.did());
@@ -625,7 +619,6 @@ fn layout_of_uncached<'tcx>(
tcx.layout_scalar_valid_range(def.did()), tcx.layout_scalar_valid_range(def.did()),
get_discriminant_type, get_discriminant_type,
discriminants_iter(), discriminants_iter(),
dont_niche_optimize_enum,
!maybe_unsized, !maybe_unsized,
) )
.map_err(|err| map_error(cx, ty, err))?; .map_err(|err| map_error(cx, ty, err))?;
@@ -651,7 +644,6 @@ fn layout_of_uncached<'tcx>(
tcx.layout_scalar_valid_range(def.did()), tcx.layout_scalar_valid_range(def.did()),
get_discriminant_type, get_discriminant_type,
discriminants_iter(), discriminants_iter(),
dont_niche_optimize_enum,
!maybe_unsized, !maybe_unsized,
) else { ) else {
bug!("failed to compute unsized layout of {ty:?}"); bug!("failed to compute unsized layout of {ty:?}");

View File

@@ -3,9 +3,9 @@
use std::{cmp, ops::Bound}; use std::{cmp, ops::Bound};
use hir_def::{ use hir_def::{
AdtId, VariantId,
layout::{Integer, ReprOptions, TargetDataLayout}, layout::{Integer, ReprOptions, TargetDataLayout},
signatures::{StructFlags, VariantFields}, signatures::{StructFlags, VariantFields},
AdtId, VariantId,
}; };
use intern::sym; use intern::sym;
use rustc_index::IndexVec; use rustc_index::IndexVec;
@@ -13,9 +13,9 @@ use smallvec::SmallVec;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
Substitution, TraitEnvironment,
db::HirDatabase, db::HirDatabase,
layout::{Layout, LayoutError, field_ty}, layout::{field_ty, Layout, LayoutError},
Substitution, TraitEnvironment,
}; };
use super::LayoutCx; use super::LayoutCx;
@@ -85,16 +85,6 @@ pub fn layout_of_adt_query(
let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?; let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?;
Some((id, d)) Some((id, d))
}), }),
// FIXME: The current code for niche-filling relies on variant indices
// instead of actual discriminants, so enums with
// explicit discriminants (RFC #2363) would misbehave and we should disable
// niche optimization for them.
// The code that do it in rustc:
// repr.inhibit_enum_layout_opt() || def
// .variants()
// .iter_enumerated()
// .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
repr.inhibit_enum_layout_opt(),
!matches!(def, AdtId::EnumId(..)) !matches!(def, AdtId::EnumId(..))
&& variants && variants
.iter() .iter()

View File

@@ -1,8 +1,17 @@
#![crate_type="lib"] #![crate_type = "lib"]
// Test that if any variant is non-unit,
// we need a repr.
enum Enum { enum Enum {
//~^ ERROR `#[repr(inttype)]` must be specified //~^ ERROR `#[repr(inttype)]` must be specified
Unit = 1, Unit = 1,
Tuple() = 2, Tuple(),
Struct{} = 3, Struct {},
}
// Test that if any non-unit variant has an explicit
// discriminant we need a repr.
enum Enum2 {
//~^ ERROR `#[repr(inttype)]` must be specified
Tuple() = 2,
} }

View File

@@ -1,9 +1,23 @@
error[E0732]: `#[repr(inttype)]` must be specified error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants
--> $DIR/arbitrary_enum_discriminant-no-repr.rs:3:1 --> $DIR/arbitrary_enum_discriminant-no-repr.rs:5:1
| |
LL | enum Enum { LL | enum Enum {
| ^^^^^^^^^ | ^^^^^^^^^
LL |
LL | Unit = 1,
| - explicit discriminant specified here
LL | Tuple(),
| ----- non-unit discriminant declared here
error: aborting due to 1 previous error error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants
--> $DIR/arbitrary_enum_discriminant-no-repr.rs:14:1
|
LL | enum Enum2 {
| ^^^^^^^^^^
LL |
LL | Tuple() = 2,
| - explicit discriminant on non-unit variant specified here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0732`. For more information about this error, try `rustc --explain E0732`.