Auto merge of #138951 - jwnrt:alloc-raw-vec-strict-prov, r=Noratrieb

Replace last `usize` -> `ptr` transmute in `alloc` with strict provenance API

This replaces the `usize -> ptr` transmute in `RawVecInner::new_in` with a strict provenance API (`NonNull::without_provenance`).

The API is changed to take an `Alignment` which encodes the non-null constraint needed for `Unique` and allows us to do the construction safely.

Two internal-only APIs were added to let us avoid UB-checking in this hot code: `Layout::alignment` to get the `Alignment` type directly rather than as a `usize`, and `Unique::from_non_null` to create `Unique` in const context without a transmute.
This commit is contained in:
bors 2025-04-06 23:07:48 +00:00
commit 25a615bf82
5 changed files with 23 additions and 6 deletions

View file

@ -135,6 +135,7 @@
#![feature(pattern)]
#![feature(pin_coerce_unsized_trait)]
#![feature(pointer_like_trait)]
#![feature(ptr_alignment_type)]
#![feature(ptr_internals)]
#![feature(ptr_metadata)]
#![feature(set_ptr_value)]

View file

@ -6,7 +6,7 @@
use core::marker::PhantomData;
use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::ptr::{self, NonNull, Unique};
use core::ptr::{self, Alignment, NonNull, Unique};
use core::{cmp, hint};
#[cfg(not(no_global_oom_handling))]
@ -177,7 +177,7 @@ impl<T, A: Allocator> RawVec<T, A> {
/// the returned `RawVec`.
#[inline]
pub(crate) const fn new_in(alloc: A) -> Self {
Self { inner: RawVecInner::new_in(alloc, align_of::<T>()), _marker: PhantomData }
Self { inner: RawVecInner::new_in(alloc, Alignment::of::<T>()), _marker: PhantomData }
}
/// Like `with_capacity`, but parameterized over the choice of
@ -409,8 +409,8 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
impl<A: Allocator> RawVecInner<A> {
#[inline]
const fn new_in(alloc: A, align: usize) -> Self {
let ptr = unsafe { core::mem::transmute(align) };
const fn new_in(alloc: A, align: Alignment) -> Self {
let ptr = Unique::from_non_null(NonNull::without_provenance(align.as_nonzero()));
// `cap: 0` means "unallocated". zero-sized types are ignored.
Self { ptr, cap: ZERO_CAP, alloc }
}
@ -465,7 +465,7 @@ impl<A: Allocator> RawVecInner<A> {
// Don't allocate here because `Drop` will not deallocate when `capacity` is 0.
if layout.size() == 0 {
return Ok(Self::new_in(alloc, elem_layout.align()));
return Ok(Self::new_in(alloc, elem_layout.alignment()));
}
if let Err(err) = alloc_guard(layout.size()) {

View file

@ -28,6 +28,8 @@
#![feature(iter_next_chunk)]
#![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_uninit_array_transpose)]
#![feature(nonnull_provenance)]
#![feature(ptr_alignment_type)]
#![feature(ptr_internals)]
#![feature(sized_type_properties)]
#![feature(slice_iter_mut_as_mut_slice)]

View file

@ -520,6 +520,14 @@ impl Layout {
unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) }
}
}
/// Perma-unstable access to `align` as `Alignment` type.
#[unstable(issue = "none", feature = "std_internals")]
#[doc(hidden)]
#[inline]
pub const fn alignment(&self) -> Alignment {
self.align
}
}
#[stable(feature = "alloc_layout", since = "1.28.0")]

View file

@ -100,6 +100,12 @@ impl<T: ?Sized> Unique<T> {
}
}
/// Create a new `Unique` from a `NonNull` in const context.
#[inline]
pub const fn from_non_null(pointer: NonNull<T>) -> Self {
Unique { pointer, _marker: PhantomData }
}
/// Acquires the underlying `*mut` pointer.
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
@ -202,6 +208,6 @@ impl<T: ?Sized> From<NonNull<T>> for Unique<T> {
/// This conversion is infallible since `NonNull` cannot be null.
#[inline]
fn from(pointer: NonNull<T>) -> Self {
Unique { pointer, _marker: PhantomData }
Unique::from_non_null(pointer)
}
}