Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ptr::Alignment type #102072

Merged
merged 3 commits into from
Oct 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions library/core/src/alloc/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl Layout {
#[stable(feature = "alloc_layout", since = "1.28.0")]
#[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")]
#[inline]
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError> {
if !align.is_power_of_two() {
return Err(LayoutError);
Expand Down Expand Up @@ -114,6 +115,7 @@ impl Layout {
#[rustc_const_stable(feature = "const_alloc_layout_unchecked", since = "1.36.0")]
#[must_use]
#[inline]
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
// SAFETY: the caller is required to uphold the preconditions.
unsafe { Layout { size, align: ValidAlign::new_unchecked(align) } }
Expand All @@ -134,6 +136,7 @@ impl Layout {
#[must_use = "this returns the minimum alignment, \
without modifying the layout"]
#[inline]
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
pub const fn align(&self) -> usize {
self.align.as_usize()
}
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
#![feature(core_panic)]
#![feature(duration_consts_float)]
#![feature(maybe_uninit_uninit_array)]
#![feature(ptr_alignment_type)]
#![feature(ptr_metadata)]
#![feature(slice_ptr_get)]
#![feature(slice_split_at_unchecked)]
Expand Down
9 changes: 4 additions & 5 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ mod maybe_uninit;
#[stable(feature = "maybe_uninit", since = "1.36.0")]
pub use maybe_uninit::MaybeUninit;

mod valid_align;
// For now this type is left crate-local. It could potentially make sense to expose
// it publicly, as it would be a nice parameter type for methods which need to take
// alignment as a parameter, such as `Layout::padding_needed_for`.
pub(crate) use valid_align::ValidAlign;
// FIXME: This is left here for now to avoid complications around pending reverts.
// Once <https://github.com/rust-lang/rust/issues/101899> is fully resolved,
// this should be removed and the references in `alloc::Layout` updated.
pub(crate) use ptr::Alignment as ValidAlign;

mod transmutability;
#[unstable(feature = "transmutability", issue = "99571")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::convert::TryFrom;
use crate::convert::{TryFrom, TryInto};
use crate::intrinsics::assert_unsafe_precondition;
use crate::num::NonZeroUsize;
use crate::{cmp, fmt, hash, mem, num};
Expand All @@ -8,134 +8,178 @@ use crate::{cmp, fmt, hash, mem, num};
///
/// Note that particularly large alignments, while representable in this type,
/// are likely not to be supported by actual allocators and linkers.
#[derive(Copy, Clone)]
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub(crate) struct ValidAlign(ValidAlignEnum);
pub struct Alignment(AlignmentEnum);

// ValidAlign is `repr(usize)`, but via extra steps.
const _: () = assert!(mem::size_of::<ValidAlign>() == mem::size_of::<usize>());
const _: () = assert!(mem::align_of::<ValidAlign>() == mem::align_of::<usize>());
// Alignment is `repr(usize)`, but via extra steps.
const _: () = assert!(mem::size_of::<Alignment>() == mem::size_of::<usize>());
const _: () = assert!(mem::align_of::<Alignment>() == mem::align_of::<usize>());

impl ValidAlign {
/// Creates a `ValidAlign` from a power-of-two `usize`.
fn _alignment_can_be_structurally_matched(a: Alignment) -> bool {
matches!(a, Alignment::MIN)
}

impl Alignment {
/// The smallest possible alignment, 1.
///
/// All addresses are always aligned at least this much.
///
/// # Examples
///
/// ```
/// #![feature(ptr_alignment_type)]
/// use std::ptr::Alignment;
///
/// assert_eq!(Alignment::MIN.as_usize(), 1);
/// ```
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0);

/// Returns the alignment for a type.
///
/// This provides the same numerical value as [`mem::align_of`],
/// but in an `Alignment` instead of a `usize.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub const fn of<T>() -> Self {
// SAFETY: rustc ensures that type alignment is always a power of two.
unsafe { Alignment::new_unchecked(mem::align_of::<T>()) }
}

/// Creates an `Alignment` from a `usize`, or returns `None` if it's
/// not a power of two.
///
/// Note that `0` is not a power of two, nor a valid alignment.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub const fn new(align: usize) -> Option<Self> {
if align.is_power_of_two() {
// SAFETY: Just checked it only has one bit set
Some(unsafe { Self::new_unchecked(align) })
} else {
None
}
}

/// Creates an `Alignment` from a power-of-two `usize`.
///
/// # Safety
///
/// `align` must be a power of two.
///
/// Equivalently, it must be `1 << exp` for some `exp` in `0..usize::BITS`.
/// It must *not* be zero.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub(crate) const unsafe fn new_unchecked(align: usize) -> Self {
pub const unsafe fn new_unchecked(align: usize) -> Self {
// SAFETY: Precondition passed to the caller.
unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) };

// SAFETY: By precondition, this must be a power of two, and
// our variants encompass all possible powers of two.
unsafe { mem::transmute::<usize, ValidAlign>(align) }
unsafe { mem::transmute::<usize, Alignment>(align) }
}

/// Returns the alignment as a [`NonZeroUsize`]
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub(crate) const fn as_usize(self) -> usize {
pub const fn as_usize(self) -> usize {
self.0 as usize
}

/// Returns the alignment as a [`usize`]
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub(crate) const fn as_nonzero(self) -> NonZeroUsize {
pub const fn as_nonzero(self) -> NonZeroUsize {
// SAFETY: All the discriminants are non-zero.
unsafe { NonZeroUsize::new_unchecked(self.as_usize()) }
}

/// Returns the base 2 logarithm of the alignment.
/// Returns the base-2 logarithm of the alignment.
///
/// This is always exact, as `self` represents a power of two.
///
/// # Examples
///
/// ```
/// #![feature(ptr_alignment_type)]
/// use std::ptr::Alignment;
///
/// assert_eq!(Alignment::of::<u8>().log2(), 0);
/// assert_eq!(Alignment::new(1024).unwrap().log2(), 10);
/// ```
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
pub(crate) fn log2(self) -> u32 {
pub fn log2(self) -> u32 {
self.as_nonzero().trailing_zeros()
}

/// Returns the alignment for a type.
#[inline]
pub(crate) fn of<T>() -> Self {
// SAFETY: rustc ensures that type alignment is always a power of two.
unsafe { ValidAlign::new_unchecked(mem::align_of::<T>()) }
}
}

impl fmt::Debug for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl fmt::Debug for Alignment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2())
}
}

impl TryFrom<NonZeroUsize> for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl TryFrom<NonZeroUsize> for Alignment {
type Error = num::TryFromIntError;

#[inline]
fn try_from(align: NonZeroUsize) -> Result<ValidAlign, Self::Error> {
if align.is_power_of_two() {
// SAFETY: Just checked for power-of-two
unsafe { Ok(ValidAlign::new_unchecked(align.get())) }
} else {
Err(num::TryFromIntError(()))
}
fn try_from(align: NonZeroUsize) -> Result<Alignment, Self::Error> {
align.get().try_into()
}
}

impl TryFrom<usize> for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl TryFrom<usize> for Alignment {
type Error = num::TryFromIntError;

#[inline]
fn try_from(align: usize) -> Result<ValidAlign, Self::Error> {
if align.is_power_of_two() {
// SAFETY: Just checked for power-of-two
unsafe { Ok(ValidAlign::new_unchecked(align)) }
} else {
Err(num::TryFromIntError(()))
}
}
}

impl cmp::Eq for ValidAlign {}

impl cmp::PartialEq for ValidAlign {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_nonzero() == other.as_nonzero()
fn try_from(align: usize) -> Result<Alignment, Self::Error> {
Self::new(align).ok_or(num::TryFromIntError(()))
}
}

impl cmp::Ord for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl cmp::Ord for Alignment {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_nonzero().cmp(&other.as_nonzero())
}
}

impl cmp::PartialOrd for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl cmp::PartialOrd for Alignment {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}

impl hash::Hash for ValidAlign {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
impl hash::Hash for Alignment {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_nonzero().hash(state)
}
}

#[cfg(target_pointer_width = "16")]
type ValidAlignEnum = ValidAlignEnum16;
type AlignmentEnum = AlignmentEnum16;
#[cfg(target_pointer_width = "32")]
type ValidAlignEnum = ValidAlignEnum32;
type AlignmentEnum = AlignmentEnum32;
#[cfg(target_pointer_width = "64")]
type ValidAlignEnum = ValidAlignEnum64;
type AlignmentEnum = AlignmentEnum64;

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(u16)]
enum ValidAlignEnum16 {
enum AlignmentEnum16 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
Expand All @@ -154,9 +198,9 @@ enum ValidAlignEnum16 {
_Align1Shl15 = 1 << 15,
}

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
enum ValidAlignEnum32 {
enum AlignmentEnum32 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
Expand Down Expand Up @@ -191,9 +235,9 @@ enum ValidAlignEnum32 {
_Align1Shl31 = 1 << 31,
}

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(u64)]
enum ValidAlignEnum64 {
enum AlignmentEnum64 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
Expand Down
4 changes: 4 additions & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ use crate::intrinsics::{

use crate::mem::{self, MaybeUninit};

mod alignment;
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
pub use alignment::Alignment;

#[stable(feature = "rust1", since = "1.0.0")]
#[doc(inline)]
pub use crate::intrinsics::copy_nonoverlapping;
Expand Down