diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index f475ea741c8..77009aca6d3 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -81,7 +81,7 @@ fn uncached_llvm_type<'a, 'tcx>( }; match layout.fields { - FieldsShape::Union(_) => { + FieldsShape::Primitive | FieldsShape::Union(_) => { let fill = cx.type_padding_filler(layout.size, layout.align.abi); let packed = false; match name { @@ -368,7 +368,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { _ => {} } match self.fields { - FieldsShape::Union(_) => { + FieldsShape::Primitive | FieldsShape::Union(_) => { bug!("TyAndLayout::llvm_field_index({:?}): not applicable", self) } diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index a98e77cebd8..97b02eaef35 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -210,6 +210,12 @@ impl HashStable for ::std::num::NonZeroU32 { } } +impl HashStable for ::std::num::NonZeroUsize { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.get().hash_stable(ctx, hasher) + } +} + impl HashStable for f32 { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let val: u32 = unsafe { ::std::mem::transmute(*self) }; diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index 1bb338d43ad..5a210547f13 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -22,6 +22,7 @@ use std::cmp; use std::fmt; use std::iter; use std::mem; +use std::num::NonZeroUsize; use std::ops::Bound; pub trait IntegerExt { @@ -518,7 +519,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // The never type. ty::Never => tcx.intern_layout(Layout { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Union(0), + fields: FieldsShape::Primitive, abi: Abi::Uninhabited, largest_niche: None, align: dl.i8_align, @@ -744,7 +745,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { return Ok(tcx.intern_layout(Layout { variants: Variants::Single { index }, - fields: FieldsShape::Union(variants[index].len()), + fields: FieldsShape::Union( + NonZeroUsize::new(variants[index].len()) + .ok_or(LayoutError::Unknown(ty))?, + ), abi, largest_niche: None, align, @@ -1988,7 +1992,7 @@ where if index == variant_index && // Don't confuse variants of uninhabited enums with the enum itself. // For more details see https://github.com/rust-lang/rust/issues/69763. - this.fields != FieldsShape::Union(0) => + this.fields != FieldsShape::Primitive => { this.layout } @@ -2006,7 +2010,10 @@ where let tcx = cx.tcx(); tcx.intern_layout(Layout { variants: Variants::Single { index: variant_index }, - fields: FieldsShape::Union(fields), + fields: match NonZeroUsize::new(fields) { + Some(fields) => FieldsShape::Union(fields), + None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, + }, abi: Abi::Uninhabited, largest_niche: None, align: tcx.data_layout.i8_align, diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 83b8d58e0be..2dffd78d776 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use std::fmt::Write; +use std::num::NonZeroUsize; use std::ops::RangeInclusive; use rustc_data_structures::fx::FxHashSet; @@ -647,10 +648,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline(always)] - fn visit_union(&mut self, op: OpTy<'tcx, M::PointerTag>, fields: usize) -> InterpResult<'tcx> { - // Empty unions are not accepted by rustc. But uninhabited enums - // claim to be unions, so allow them, too. - assert!(op.layout.abi.is_uninhabited() || fields > 0); + fn visit_union( + &mut self, + _op: OpTy<'tcx, M::PointerTag>, + _fields: NonZeroUsize, + ) -> InterpResult<'tcx> { Ok(()) } diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index e03984f4d0b..00d39452c49 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -6,6 +6,8 @@ use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; +use std::num::NonZeroUsize; + use super::{InterpCx, MPlaceTy, Machine, OpTy}; // A thing that we can project into, and that has a layout. @@ -130,7 +132,7 @@ macro_rules! make_value_visitor { } /// Visits the given value as a union. No automatic recursion can happen here. #[inline(always)] - fn visit_union(&mut self, _v: Self::V, _fields: usize) -> InterpResult<'tcx> + fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> { Ok(()) } @@ -208,6 +210,7 @@ macro_rules! make_value_visitor { // Visit the fields of this value. match v.layout().fields { + FieldsShape::Primitive => {}, FieldsShape::Union(fields) => { self.visit_union(v, fields)?; }, diff --git a/src/librustc_target/abi/call/mips64.rs b/src/librustc_target/abi/call/mips64.rs index 81de6306788..917dd104d14 100644 --- a/src/librustc_target/abi/call/mips64.rs +++ b/src/librustc_target/abi/call/mips64.rs @@ -88,6 +88,7 @@ where let mut prefix_index = 0; match arg.layout.fields { + abi::FieldsShape::Primitive => unreachable!(), abi::FieldsShape::Array { .. } => { // Arrays are passed indirectly arg.make_indirect(); diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index b6bfa70005b..0303312d057 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -308,13 +308,16 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } Abi::ScalarPair(..) | Abi::Aggregate { .. } => { - // Helper for computing `homogenous_aggregate`, allowing a custom + // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). let from_fields_at = |layout: Self, start: Size| -> Result<(HomogeneousAggregate, Size), Heterogeneous> { let is_union = match layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } FieldsShape::Array { count, .. } => { assert_eq!(start, Size::ZERO); diff --git a/src/librustc_target/abi/call/riscv.rs b/src/librustc_target/abi/call/riscv.rs index 0eb8816e434..2e10bed3bd4 100644 --- a/src/librustc_target/abi/call/riscv.rs +++ b/src/librustc_target/abi/call/riscv.rs @@ -87,6 +87,9 @@ where }, Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } FieldsShape::Union(_) => { if !arg_layout.is_zst() { return Err(CannotUseFpConv); diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 4c25363a657..dcf181cb59f 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -4,6 +4,7 @@ pub use Primitive::*; use crate::spec::Target; use std::convert::{TryFrom, TryInto}; +use std::num::NonZeroUsize; use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; use rustc_index::vec::{Idx, IndexVec}; @@ -619,10 +620,11 @@ impl Scalar { /// Describes how the fields of a type are located in memory. #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum FieldsShape { + /// Scalar primitives and `!`, which never have fields. + Primitive, + /// All fields start at no offset. The `usize` is the field count. - /// - /// In the case of primitives the number of fields is `0`. - Union(usize), + Union(NonZeroUsize), /// Array/vector-like placement, with all fields of identical types. Array { stride: Size, count: u64 }, @@ -660,7 +662,8 @@ pub enum FieldsShape { impl FieldsShape { pub fn count(&self) -> usize { match *self { - FieldsShape::Union(count) => count, + FieldsShape::Primitive => 0, + FieldsShape::Union(count) => count.get(), FieldsShape::Array { count, .. } => { let usize_count = count as usize; assert_eq!(usize_count as u64, count); @@ -672,8 +675,16 @@ impl FieldsShape { pub fn offset(&self, i: usize) -> Size { match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::offset: `Primitive`s have no fields") + } FieldsShape::Union(count) => { - assert!(i < count, "tried to access field {} of union with {} fields", i, count); + assert!( + i < count.get(), + "tried to access field {} of union with {} fields", + i, + count + ); Size::ZERO } FieldsShape::Array { stride, count } => { @@ -687,6 +698,9 @@ impl FieldsShape { pub fn memory_index(&self, i: usize) -> usize { match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") + } FieldsShape::Union(_) | FieldsShape::Array { .. } => i, FieldsShape::Arbitrary { ref memory_index, .. } => { let r = memory_index[i]; @@ -718,7 +732,7 @@ impl FieldsShape { } (0..self.count()).map(move |i| match *self { - FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, FieldsShape::Arbitrary { .. } => { if use_small { inverse_small[i] as usize @@ -887,7 +901,6 @@ impl Niche { #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct Layout { /// Says where the fields are located within the layout. - /// Primitives and uninhabited enums appear as unions without fields. pub fields: FieldsShape, /// Encodes information about multi-variant layouts. @@ -923,7 +936,7 @@ impl Layout { let align = scalar.value.align(cx); Layout { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Union(0), + fields: FieldsShape::Primitive, abi: Abi::Scalar(scalar), largest_niche, size, diff --git a/src/test/ui/layout/debug.stderr b/src/test/ui/layout/debug.stderr index 153dec594d3..cd8ebdffb73 100644 --- a/src/test/ui/layout/debug.stderr +++ b/src/test/ui/layout/debug.stderr @@ -316,9 +316,7 @@ LL | type Test = Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: layout_of(i32) = Layout { - fields: Union( - 0, - ), + fields: Primitive, variants: Single { index: 0, },