| //! Intermediate representation for the physical layout of some type. |
| |
| use super::derive::{CanTriviallyDeriveCopy, CanTriviallyDeriveDebug, |
| CanTriviallyDeriveDefault, CanTriviallyDeriveHash, |
| CanTriviallyDerivePartialEqOrPartialOrd, CanDerive}; |
| use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type, TypeKind}; |
| use clang; |
| use std::{cmp, mem}; |
| |
| /// A type that represents the struct layout of a type. |
| #[derive(Debug, Clone, Copy, PartialEq)] |
| pub struct Layout { |
| /// The size (in bytes) of this layout. |
| pub size: usize, |
| /// The alignment (in bytes) of this layout. |
| pub align: usize, |
| /// Whether this layout's members are packed or not. |
| pub packed: bool, |
| } |
| |
| #[test] |
| fn test_layout_for_size() { |
| let ptr_size = mem::size_of::<*mut ()>(); |
| assert_eq!(Layout::for_size(ptr_size), Layout::new(ptr_size, ptr_size)); |
| assert_eq!( |
| Layout::for_size(3 * ptr_size), |
| Layout::new(3 * ptr_size, ptr_size) |
| ); |
| } |
| |
| impl Layout { |
| /// Construct a new `Layout` with the given `size` and `align`. It is not |
| /// packed. |
| pub fn new(size: usize, align: usize) -> Self { |
| Layout { |
| size, |
| align, |
| packed: false, |
| } |
| } |
| |
| /// Creates a non-packed layout for a given size, trying to use the maximum |
| /// alignment possible. |
| pub fn for_size(size: usize) -> Self { |
| let mut next_align = 2; |
| while size % next_align == 0 && |
| next_align <= mem::size_of::<*mut ()>() |
| { |
| next_align *= 2; |
| } |
| Layout { |
| size: size, |
| align: next_align / 2, |
| packed: false, |
| } |
| } |
| |
| /// Is this a zero-sized layout? |
| pub fn is_zero(&self) -> bool { |
| self.size == 0 && self.align == 0 |
| } |
| |
| /// Construct a zero-sized layout. |
| pub fn zero() -> Self { |
| Self::new(0, 0) |
| } |
| |
| /// Get this layout as an opaque type. |
| pub fn opaque(&self) -> Opaque { |
| Opaque(*self) |
| } |
| } |
| |
| /// When we are treating a type as opaque, it is just a blob with a `Layout`. |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct Opaque(pub Layout); |
| |
| impl Opaque { |
| /// Construct a new opaque type from the given clang type. |
| pub fn from_clang_ty(ty: &clang::Type) -> Type { |
| let layout = Layout::new(ty.size(), ty.align()); |
| let ty_kind = TypeKind::Opaque; |
| Type::new(None, Some(layout), ty_kind, false) |
| } |
| |
| /// Return the known rust type we should use to create a correctly-aligned |
| /// field with this layout. |
| pub fn known_rust_type_for_array(&self) -> Option<&'static str> { |
| Some(match self.0.align { |
| 8 => "u64", |
| 4 => "u32", |
| 2 => "u16", |
| 1 => "u8", |
| _ => return None, |
| }) |
| } |
| |
| /// Return the array size that an opaque type for this layout should have if |
| /// we know the correct type for it, or `None` otherwise. |
| pub fn array_size(&self) -> Option<usize> { |
| if self.known_rust_type_for_array().is_some() { |
| Some(self.0.size / cmp::max(self.0.align, 1)) |
| } else { |
| None |
| } |
| } |
| |
| /// Return `true` if this opaque layout's array size will fit within the |
| /// maximum number of array elements that Rust allows deriving traits |
| /// with. Return `false` otherwise. |
| pub fn array_size_within_derive_limit(&self) -> bool { |
| self.array_size().map_or(false, |size| { |
| size <= RUST_DERIVE_IN_ARRAY_LIMIT |
| }) |
| } |
| } |
| |
| impl CanTriviallyDeriveDebug for Opaque { |
| fn can_trivially_derive_debug(&self) -> bool { |
| self.array_size_within_derive_limit() |
| } |
| } |
| |
| impl CanTriviallyDeriveDefault for Opaque { |
| fn can_trivially_derive_default(&self) -> bool { |
| self.array_size_within_derive_limit() |
| } |
| } |
| |
| impl CanTriviallyDeriveCopy for Opaque { |
| fn can_trivially_derive_copy(&self) -> bool { |
| self.array_size_within_derive_limit() |
| } |
| } |
| |
| impl CanTriviallyDeriveHash for Opaque { |
| fn can_trivially_derive_hash(&self) -> bool { |
| self.array_size_within_derive_limit() |
| } |
| } |
| |
| impl CanTriviallyDerivePartialEqOrPartialOrd for Opaque { |
| fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive { |
| self.array_size().map_or(CanDerive::No, |size| { |
| if size <= RUST_DERIVE_IN_ARRAY_LIMIT { |
| CanDerive::Yes |
| } else { |
| CanDerive::ArrayTooLarge |
| } |
| }) |
| } |
| } |