Rollup merge of #132255 - workingjubilee:layout-is-🏚️, r=compiler-errors

Add `LayoutS::is_uninhabited` and use it

Use accessors for the things that accessors are good at: reducing everyone's need to be nosy and peek at the internals of every data structure.
This commit is contained in:
Jubilee 2024-10-28 10:18:50 -07:00 committed by GitHub
commit 259ddf9b50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 44 additions and 41 deletions

View File

@ -4100,6 +4100,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"either", "either",
"itertools", "itertools",
"rustc_abi",
"rustc_arena", "rustc_arena",
"rustc_ast", "rustc_ast",
"rustc_attr", "rustc_attr",

View File

@ -28,7 +28,7 @@ fn absent<'a, FieldIdx, VariantIdx, F>(fields: &IndexSlice<FieldIdx, F>) -> bool
VariantIdx: Idx, VariantIdx: Idx,
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
{ {
let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited()); let uninhabited = fields.iter().any(|f| f.is_uninhabited());
// We cannot ignore alignment; that might lead us to entirely discard a variant and // We cannot ignore alignment; that might lead us to entirely discard a variant and
// produce an enum that is less aligned than it should be! // produce an enum that is less aligned than it should be!
let is_1zst = fields.iter().all(|f| f.is_1zst()); let is_1zst = fields.iter().all(|f| f.is_1zst());
@ -681,7 +681,7 @@ struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
let discr_type = repr.discr_type(); let discr_type = repr.discr_type();
let bits = Integer::from_attr(dl, discr_type).size().bits(); let bits = Integer::from_attr(dl, discr_type).size().bits();
for (i, mut val) in discriminants { for (i, mut val) in discriminants {
if !repr.c() && variants[i].iter().any(|f| f.abi.is_uninhabited()) { if !repr.c() && variants[i].iter().any(|f| f.is_uninhabited()) {
continue; continue;
} }
if discr_type.is_signed() { if discr_type.is_signed() {

View File

@ -1652,6 +1652,11 @@ pub fn is_aggregate(&self) -> bool {
} }
} }
/// Returns `true` if this is an uninhabited type
pub fn is_uninhabited(&self) -> bool {
self.abi.is_uninhabited()
}
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
let size = scalar.size(cx); let size = scalar.size(cx);

View File

@ -415,7 +415,7 @@ fn apply_attrs_llfn(
instance: Option<ty::Instance<'tcx>>, instance: Option<ty::Instance<'tcx>>,
) { ) {
let mut func_attrs = SmallVec::<[_; 3]>::new(); let mut func_attrs = SmallVec::<[_; 3]>::new();
if self.ret.layout.abi.is_uninhabited() { if self.ret.layout.is_uninhabited() {
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx)); func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx));
} }
if !self.can_unwind { if !self.can_unwind {
@ -532,7 +532,7 @@ fn apply_attrs_llfn(
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) { fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
let mut func_attrs = SmallVec::<[_; 2]>::new(); let mut func_attrs = SmallVec::<[_; 2]>::new();
if self.ret.layout.abi.is_uninhabited() { if self.ret.layout.is_uninhabited() {
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(bx.cx.llcx)); func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(bx.cx.llcx));
} }
if !self.can_unwind { if !self.can_unwind {

View File

@ -364,7 +364,7 @@ fn dbg_scope_fn(
let mut flags = DIFlags::FlagPrototyped; let mut flags = DIFlags::FlagPrototyped;
if fn_abi.ret.layout.abi.is_uninhabited() { if fn_abi.ret.layout.is_uninhabited() {
flags |= DIFlags::FlagNoReturn; flags |= DIFlags::FlagNoReturn;
} }

View File

@ -438,7 +438,7 @@ fn codegen_return_terminator(&mut self, bx: &mut Bx) {
_ => bug!("C-variadic function must have a `VaList` place"), _ => bug!("C-variadic function must have a `VaList` place"),
} }
} }
if self.fn_abi.ret.layout.abi.is_uninhabited() { if self.fn_abi.ret.layout.is_uninhabited() {
// Functions with uninhabited return values are marked `noreturn`, // Functions with uninhabited return values are marked `noreturn`,
// so we should make sure that we never actually do. // so we should make sure that we never actually do.
// We play it safe by using a well-defined `abort`, but we could go for immediate UB // We play it safe by using a well-defined `abort`, but we could go for immediate UB
@ -774,7 +774,7 @@ fn codegen_panic_intrinsic(
Some(if do_panic { Some(if do_panic {
let msg_str = with_no_visible_paths!({ let msg_str = with_no_visible_paths!({
with_no_trimmed_paths!({ with_no_trimmed_paths!({
if layout.abi.is_uninhabited() { if layout.is_uninhabited() {
// Use this error even for the other intrinsics as it is more precise. // Use this error even for the other intrinsics as it is more precise.
format!("attempted to instantiate uninhabited type `{ty}`") format!("attempted to instantiate uninhabited type `{ty}`")
} else if requirement == ValidityRequirement::Zero { } else if requirement == ValidityRequirement::Zero {

View File

@ -55,7 +55,7 @@ pub fn alloca<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx, Value = V>>(
/// Creates a `PlaceRef` to this location with the given type. /// Creates a `PlaceRef` to this location with the given type.
pub fn with_type<'tcx>(self, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> { pub fn with_type<'tcx>(self, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> {
assert!( assert!(
layout.is_unsized() || layout.abi.is_uninhabited() || self.llextra.is_none(), layout.is_unsized() || layout.is_uninhabited() || self.llextra.is_none(),
"Had pointer metadata {:?} for sized type {layout:?}", "Had pointer metadata {:?} for sized type {layout:?}",
self.llextra, self.llextra,
); );
@ -239,7 +239,7 @@ pub fn codegen_get_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
let dl = &bx.tcx().data_layout; let dl = &bx.tcx().data_layout;
let cast_to_layout = bx.cx().layout_of(cast_to); let cast_to_layout = bx.cx().layout_of(cast_to);
let cast_to = bx.cx().immediate_backend_type(cast_to_layout); let cast_to = bx.cx().immediate_backend_type(cast_to_layout);
if self.layout.abi.is_uninhabited() { if self.layout.is_uninhabited() {
return bx.cx().const_poison(cast_to); return bx.cx().const_poison(cast_to);
} }
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
@ -358,7 +358,7 @@ pub fn codegen_set_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx, bx: &mut Bx,
variant_index: VariantIdx, variant_index: VariantIdx,
) { ) {
if self.layout.for_variant(bx.cx(), variant_index).abi.is_uninhabited() { if self.layout.for_variant(bx.cx(), variant_index).is_uninhabited() {
// We play it safe by using a well-defined `abort`, but we could go for immediate UB // We play it safe by using a well-defined `abort`, but we could go for immediate UB
// if that turns out to be helpful. // if that turns out to be helpful.
bx.abort(); bx.abort();

View File

@ -203,10 +203,10 @@ fn codegen_transmute_operand(
) -> Option<OperandValue<Bx::Value>> { ) -> Option<OperandValue<Bx::Value>> {
// Check for transmutes that are always UB. // Check for transmutes that are always UB.
if operand.layout.size != cast.size if operand.layout.size != cast.size
|| operand.layout.abi.is_uninhabited() || operand.layout.is_uninhabited()
|| cast.abi.is_uninhabited() || cast.is_uninhabited()
{ {
if !operand.layout.abi.is_uninhabited() { if !operand.layout.is_uninhabited() {
// Since this is known statically and the input could have existed // Since this is known statically and the input could have existed
// without already having hit UB, might as well trap for it. // without already having hit UB, might as well trap for it.
bx.abort(); bx.abort();
@ -555,7 +555,7 @@ pub(crate) fn codegen_rvalue_operand(
assert!(bx.cx().is_backend_immediate(cast)); assert!(bx.cx().is_backend_immediate(cast));
let to_backend_ty = bx.cx().immediate_backend_type(cast); let to_backend_ty = bx.cx().immediate_backend_type(cast);
if operand.layout.abi.is_uninhabited() { if operand.layout.is_uninhabited() {
let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty)); let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty));
return OperandRef { val, layout: cast }; return OperandRef { val, layout: cast };
} }

View File

@ -395,7 +395,7 @@ fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool {
#[inline(always)] #[inline(always)]
fn enforce_validity(ecx: &InterpCx<'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool { fn enforce_validity(ecx: &InterpCx<'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool {
ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited() ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.is_uninhabited()
} }
fn load_mir( fn load_mir(

View File

@ -27,7 +27,7 @@ pub fn write_discriminant(
// discriminant, so we cannot do anything here. // discriminant, so we cannot do anything here.
// When evaluating we will always error before even getting here, but ConstProp 'executes' // When evaluating we will always error before even getting here, but ConstProp 'executes'
// dead code, so we cannot ICE here. // dead code, so we cannot ICE here.
if dest.layout().for_variant(self, variant_index).abi.is_uninhabited() { if dest.layout().for_variant(self, variant_index).is_uninhabited() {
throw_ub!(UninhabitedEnumVariantWritten(variant_index)) throw_ub!(UninhabitedEnumVariantWritten(variant_index))
} }
@ -86,7 +86,7 @@ pub fn read_discriminant(
// For consistency with `write_discriminant`, and to make sure that // For consistency with `write_discriminant`, and to make sure that
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB // `project_downcast` cannot fail due to strange layouts, we declare immediate UB
// for uninhabited variants. // for uninhabited variants.
if op.layout().for_variant(self, index).abi.is_uninhabited() { if op.layout().for_variant(self, index).is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index)) throw_ub!(UninhabitedEnumVariantRead(index))
} }
} }
@ -203,7 +203,7 @@ pub fn read_discriminant(
// Reading the discriminant of an uninhabited variant is UB. This is the basis for the // Reading the discriminant of an uninhabited variant is UB. This is the basis for the
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with // `uninhabited_enum_branching` MIR pass. It also ensures consistency with
// `write_discriminant`. // `write_discriminant`.
if op.layout().for_variant(self, index).abi.is_uninhabited() { if op.layout().for_variant(self, index).is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index)) throw_ub!(UninhabitedEnumVariantRead(index))
} }
interp_ok(index) interp_ok(index)

View File

@ -364,7 +364,7 @@ pub fn eval_intrinsic(
let msg = match requirement { let msg = match requirement {
// For *all* intrinsics we first check `is_uninhabited` to give a more specific // For *all* intrinsics we first check `is_uninhabited` to give a more specific
// error message. // error message.
_ if layout.abi.is_uninhabited() => format!( _ if layout.is_uninhabited() => format!(
"aborted execution: attempted to instantiate uninhabited type `{ty}`" "aborted execution: attempted to instantiate uninhabited type `{ty}`"
), ),
ValidityRequirement::Inhabited => bug!("handled earlier"), ValidityRequirement::Inhabited => bug!("handled earlier"),

View File

@ -315,7 +315,7 @@ fn binary_ptr_op(
let ptr = left.to_scalar().to_pointer(self)?; let ptr = left.to_scalar().to_pointer(self)?;
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap(); let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
let pointee_layout = self.layout_of(pointee_ty)?; let pointee_layout = self.layout_of(pointee_ty)?;
assert!(pointee_layout.abi.is_sized()); assert!(pointee_layout.is_sized());
// The size always fits in `i64` as it can be at most `isize::MAX`. // The size always fits in `i64` as it can be at most `isize::MAX`.
let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap(); let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap();
@ -518,14 +518,14 @@ pub fn nullary_op(
interp_ok(match null_op { interp_ok(match null_op {
SizeOf => { SizeOf => {
if !layout.abi.is_sized() { if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`"); span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`");
} }
let val = layout.size.bytes(); let val = layout.size.bytes();
ImmTy::from_uint(val, usize_layout()) ImmTy::from_uint(val, usize_layout())
} }
AlignOf => { AlignOf => {
if !layout.abi.is_sized() { if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`"); span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`");
} }
let val = layout.align.abi.bytes(); let val = layout.align.abi.bytes();

View File

@ -542,7 +542,7 @@ fn check_safe_pointer(
throw_validation_failure!(self.path, NullPtr { ptr_kind }) throw_validation_failure!(self.path, NullPtr { ptr_kind })
} }
// Do not allow references to uninhabited types. // Do not allow references to uninhabited types.
if place.layout.abi.is_uninhabited() { if place.layout.is_uninhabited() {
let ty = place.layout.ty; let ty = place.layout.ty;
throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty }) throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty })
} }
@ -867,7 +867,7 @@ fn add_data_range(&mut self, ptr: Pointer<Option<M::Provenance>>, size: Size) {
/// Add the entire given place to the "data" range of this visit. /// Add the entire given place to the "data" range of this visit.
fn add_data_range_place(&mut self, place: &PlaceTy<'tcx, M::Provenance>) { fn add_data_range_place(&mut self, place: &PlaceTy<'tcx, M::Provenance>) {
// Only sized places can be added this way. // Only sized places can be added this way.
debug_assert!(place.layout.abi.is_sized()); debug_assert!(place.layout.is_sized());
if let Some(data_bytes) = self.data_bytes.as_mut() { if let Some(data_bytes) = self.data_bytes.as_mut() {
let offset = Self::data_range_offset(self.ecx, place); let offset = Self::data_range_offset(self.ecx, place);
data_bytes.add_range(offset, place.layout.size); data_bytes.add_range(offset, place.layout.size);
@ -945,7 +945,7 @@ fn union_data_range<'e>(
layout: TyAndLayout<'tcx>, layout: TyAndLayout<'tcx>,
) -> Cow<'e, RangeSet> { ) -> Cow<'e, RangeSet> {
assert!(layout.ty.is_union()); assert!(layout.ty.is_union());
assert!(layout.abi.is_sized(), "there are no unsized unions"); assert!(layout.is_sized(), "there are no unsized unions");
let layout_cx = LayoutCx::new(*ecx.tcx, ecx.param_env); let layout_cx = LayoutCx::new(*ecx.tcx, ecx.param_env);
return M::cached_union_data_range(ecx, layout.ty, || { return M::cached_union_data_range(ecx, layout.ty, || {
let mut out = RangeSet(Vec::new()); let mut out = RangeSet(Vec::new());

View File

@ -29,7 +29,7 @@ pub fn check_validity_requirement<'tcx>(
// There is nothing strict or lax about inhabitedness. // There is nothing strict or lax about inhabitedness.
if kind == ValidityRequirement::Inhabited { if kind == ValidityRequirement::Inhabited {
return Ok(!layout.abi.is_uninhabited()); return Ok(!layout.is_uninhabited());
} }
let layout_cx = LayoutCx::new(tcx, param_env_and_ty.param_env); let layout_cx = LayoutCx::new(tcx, param_env_and_ty.param_env);

View File

@ -172,7 +172,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
return; return;
} }
}; };
if layout.abi.is_uninhabited() { if layout.is_uninhabited() {
tcx.node_span_lint( tcx.node_span_lint(
UNINHABITED_STATIC, UNINHABITED_STATIC,
tcx.local_def_id_to_hir_id(def_id), tcx.local_def_id_to_hir_id(def_id),

View File

@ -346,7 +346,7 @@ pub fn compute(
// First try computing a static layout. // First try computing a static layout.
let err = match tcx.layout_of(param_env.and(ty)) { let err = match tcx.layout_of(param_env.and(ty)) {
Ok(layout) => { Ok(layout) => {
if layout.abi.is_sized() { if layout.is_sized() {
return Ok(SizeSkeleton::Known(layout.size, Some(layout.align.abi))); return Ok(SizeSkeleton::Known(layout.size, Some(layout.align.abi)));
} else { } else {
// Just to be safe, don't claim a known layout for unsized types. // Just to be safe, don't claim a known layout for unsized types.

View File

@ -7,6 +7,7 @@ edition = "2021"
# tidy-alphabetical-start # tidy-alphabetical-start
either = "1" either = "1"
itertools = "0.12" itertools = "0.12"
rustc_abi = { path = "../rustc_abi" }
rustc_arena = { path = "../rustc_arena" } rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" } rustc_attr = { path = "../rustc_attr" }

View File

@ -1,5 +1,6 @@
//! A pass that eliminates branches on uninhabited or unreachable enum variants. //! A pass that eliminates branches on uninhabited or unreachable enum variants.
use rustc_abi::Variants;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::patch::MirPatch;
@ -9,7 +10,6 @@
}; };
use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::ty::{Ty, TyCtxt};
use rustc_target::abi::{Abi, Variants};
use tracing::trace; use tracing::trace;
pub(super) struct UnreachableEnumBranching; pub(super) struct UnreachableEnumBranching;
@ -65,7 +65,7 @@ fn variant_discriminants<'tcx>(
Variants::Multiple { variants, .. } => variants Variants::Multiple { variants, .. } => variants
.iter_enumerated() .iter_enumerated()
.filter_map(|(idx, layout)| { .filter_map(|(idx, layout)| {
(layout.abi != Abi::Uninhabited) (!layout.is_uninhabited())
.then(|| ty.discriminant_for_variant(tcx, idx).unwrap().val) .then(|| ty.discriminant_for_variant(tcx, idx).unwrap().val)
}) })
.collect(), .collect(),

View File

@ -339,9 +339,7 @@ fn from_enum(
// 2. enums that delegate their layout to a variant // 2. enums that delegate their layout to a variant
// 3. enums with multiple variants // 3. enums with multiple variants
match layout.variants() { match layout.variants() {
Variants::Single { .. } Variants::Single { .. } if layout.is_uninhabited() && layout.size == Size::ZERO => {
if layout.abi.is_uninhabited() && layout.size == Size::ZERO =>
{
// The layout representation of uninhabited, ZST enums is // The layout representation of uninhabited, ZST enums is
// defined to be like that of the `!` type, as opposed of a // defined to be like that of the `!` type, as opposed of a
// typical enum. Consequently, they cannot be descended into // typical enum. Consequently, they cannot be descended into

View File

@ -10,7 +10,7 @@ pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLa
// Type-level uninhabitedness should always imply ABI uninhabitedness. // Type-level uninhabitedness should always imply ABI uninhabitedness.
if layout.ty.is_privately_uninhabited(tcx, cx.param_env) { if layout.ty.is_privately_uninhabited(tcx, cx.param_env) {
assert!(layout.abi.is_uninhabited()); assert!(layout.is_uninhabited());
} }
if layout.size.bytes() % layout.align.abi.bytes() != 0 { if layout.size.bytes() % layout.align.abi.bytes() != 0 {
@ -262,9 +262,7 @@ fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
) )
} }
// Skip empty variants. // Skip empty variants.
if variant.size == Size::ZERO if variant.size == Size::ZERO || variant.fields.count() == 0 || variant.is_uninhabited()
|| variant.fields.count() == 0
|| variant.abi.is_uninhabited()
{ {
// These are never actually accessed anyway, so we can skip the coherence check // These are never actually accessed anyway, so we can skip the coherence check
// for them. They also fail that check, since they have // for them. They also fail that check, since they have

View File

@ -60,8 +60,8 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>(
span_bug!(tcx.def_span(ty_def_id), "not an adt") span_bug!(tcx.def_span(ty_def_id), "not an adt")
}; };
let name = adt.variant(variant_idx).name; let name = adt.variant(variant_idx).name;
let is_unsized = variant_layout.abi.is_unsized(); let is_unsized = variant_layout.is_unsized();
let is_uninhabited = variant_layout.abi.is_uninhabited(); let is_uninhabited = variant_layout.is_uninhabited();
let size = variant_layout.size.bytes() - tag_size; let size = variant_layout.size.bytes() - tag_size;
let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size }; let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
(name, type_layout_size) (name, type_layout_size)
@ -72,8 +72,8 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>(
}; };
let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| { let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| {
let is_unsized = layout.abi.is_unsized(); let is_unsized = layout.is_unsized();
let is_uninhabited = layout.abi.is_uninhabited(); let is_uninhabited = layout.is_uninhabited();
let size = layout.size.bytes(); let size = layout.size.bytes();
TypeLayoutSize { is_unsized, is_uninhabited, size } TypeLayoutSize { is_unsized, is_uninhabited, size }
}); });