|  | use std::num::NonZero; | 
|  |  | 
|  | use rustc_hashes::Hash64; | 
|  | use rustc_index::{Idx, IndexVec}; | 
|  |  | 
|  | use crate::{ | 
|  | BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants, | 
|  | }; | 
|  |  | 
|  | /// "Simple" layout constructors that cannot fail. | 
|  | impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { | 
|  | pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self { | 
|  | let dl = cx.data_layout(); | 
|  | LayoutData { | 
|  | variants: Variants::Single { index: VariantIdx::new(0) }, | 
|  | fields: FieldsShape::Arbitrary { | 
|  | offsets: IndexVec::new(), | 
|  | memory_index: IndexVec::new(), | 
|  | }, | 
|  | backend_repr: BackendRepr::Memory { sized }, | 
|  | largest_niche: None, | 
|  | uninhabited: false, | 
|  | align: dl.i8_align, | 
|  | size: Size::ZERO, | 
|  | max_repr_align: None, | 
|  | unadjusted_abi_align: dl.i8_align.abi, | 
|  | randomization_seed: Hash64::new(0), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn never_type<C: HasDataLayout>(cx: &C) -> Self { | 
|  | let dl = cx.data_layout(); | 
|  | // This is also used for uninhabited enums, so we use `Variants::Empty`. | 
|  | LayoutData { | 
|  | variants: Variants::Empty, | 
|  | fields: FieldsShape::Primitive, | 
|  | backend_repr: BackendRepr::Memory { sized: true }, | 
|  | largest_niche: None, | 
|  | uninhabited: true, | 
|  | align: dl.i8_align, | 
|  | size: Size::ZERO, | 
|  | max_repr_align: None, | 
|  | unadjusted_abi_align: dl.i8_align.abi, | 
|  | randomization_seed: Hash64::ZERO, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { | 
|  | let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); | 
|  | let size = scalar.size(cx); | 
|  | let align = scalar.align(cx); | 
|  |  | 
|  | let range = scalar.valid_range(cx); | 
|  |  | 
|  | // All primitive types for which we don't have subtype coercions should get a distinct seed, | 
|  | // so that types wrapping them can use randomization to arrive at distinct layouts. | 
|  | // | 
|  | // Some type information is already lost at this point, so as an approximation we derive | 
|  | // the seed from what remains. For example on 64-bit targets usize and u64 can no longer | 
|  | // be distinguished. | 
|  | let randomization_seed = size | 
|  | .bytes() | 
|  | .wrapping_add( | 
|  | match scalar.primitive() { | 
|  | Primitive::Int(_, true) => 1, | 
|  | Primitive::Int(_, false) => 2, | 
|  | Primitive::Float(_) => 3, | 
|  | Primitive::Pointer(_) => 4, | 
|  | } << 32, | 
|  | ) | 
|  | // distinguishes references from pointers | 
|  | .wrapping_add((range.start as u64).rotate_right(16)) | 
|  | // distinguishes char from u32 and bool from u8 | 
|  | .wrapping_add((range.end as u64).rotate_right(16)); | 
|  |  | 
|  | LayoutData { | 
|  | variants: Variants::Single { index: VariantIdx::new(0) }, | 
|  | fields: FieldsShape::Primitive, | 
|  | backend_repr: BackendRepr::Scalar(scalar), | 
|  | largest_niche, | 
|  | uninhabited: false, | 
|  | size, | 
|  | align, | 
|  | max_repr_align: None, | 
|  | unadjusted_abi_align: align.abi, | 
|  | randomization_seed: Hash64::new(randomization_seed), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self { | 
|  | let dl = cx.data_layout(); | 
|  | let b_align = b.align(dl); | 
|  | let align = a.align(dl).max(b_align).max(dl.aggregate_align); | 
|  | let b_offset = a.size(dl).align_to(b_align.abi); | 
|  | let size = (b_offset + b.size(dl)).align_to(align.abi); | 
|  |  | 
|  | // HACK(nox): We iter on `b` and then `a` because `max_by_key` | 
|  | // returns the last maximum. | 
|  | let largest_niche = Niche::from_scalar(dl, b_offset, b) | 
|  | .into_iter() | 
|  | .chain(Niche::from_scalar(dl, Size::ZERO, a)) | 
|  | .max_by_key(|niche| niche.available(dl)); | 
|  |  | 
|  | let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes()); | 
|  |  | 
|  | LayoutData { | 
|  | variants: Variants::Single { index: VariantIdx::new(0) }, | 
|  | fields: FieldsShape::Arbitrary { | 
|  | offsets: [Size::ZERO, b_offset].into(), | 
|  | memory_index: [0, 1].into(), | 
|  | }, | 
|  | backend_repr: BackendRepr::ScalarPair(a, b), | 
|  | largest_niche, | 
|  | uninhabited: false, | 
|  | align, | 
|  | size, | 
|  | max_repr_align: None, | 
|  | unadjusted_abi_align: align.abi, | 
|  | randomization_seed: Hash64::new(combined_seed), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Returns a dummy layout for an uninhabited variant. | 
|  | /// | 
|  | /// Uninhabited variants get pruned as part of the layout calculation, | 
|  | /// so this can be used after the fact to reconstitute a layout. | 
|  | pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self { | 
|  | let dl = cx.data_layout(); | 
|  | LayoutData { | 
|  | variants: Variants::Single { index }, | 
|  | fields: match NonZero::new(fields) { | 
|  | Some(fields) => FieldsShape::Union(fields), | 
|  | None => FieldsShape::Arbitrary { | 
|  | offsets: IndexVec::new(), | 
|  | memory_index: IndexVec::new(), | 
|  | }, | 
|  | }, | 
|  | backend_repr: BackendRepr::Memory { sized: true }, | 
|  | largest_niche: None, | 
|  | uninhabited: true, | 
|  | align: dl.i8_align, | 
|  | size: Size::ZERO, | 
|  | max_repr_align: None, | 
|  | unadjusted_abi_align: dl.i8_align.abi, | 
|  | randomization_seed: Hash64::ZERO, | 
|  | } | 
|  | } | 
|  | } |