Skip to content

Commit

Permalink
Rollup merge of rust-lang#91884 - woppopo:const_box, r=oli-obk
Browse files Browse the repository at this point in the history
Constify `Box<T, A>` methods

Tracking issue: none yet

Most of the methods bounded on `~const`. `intrinsics::const_eval_select` is used for handling an allocation error.

<details><summary>Constified API</summary>

```rust
impl<T, A: Allocator> Box<T, A> {
    pub const fn new_in(x: T, alloc: A) -> Self
    where
        A: ~const Allocator + ~const Drop;
    pub const fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError>
    where
        T: ~const Drop,
        A: ~const Allocator + ~const Drop;
    pub const fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
    where
        A: ~const Allocator + ~const Drop;
    pub const fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
    where
        A: ~const Allocator + ~const Drop;
    pub const fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
    where
        A: ~const Allocator + ~const Drop;
    pub const fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
    where
        A: ~const Allocator + ~const Drop;
    pub const fn pin_in(x: T, alloc: A) -> Pin<Self>
    where
        A: 'static,
        A: 'static + ~const Allocator + ~const Drop,
    pub const fn into_boxed_slice(boxed: Self) -> Box<[T], A>;
    pub const fn into_inner(boxed: Self) -> T
    where
        Self: ~const Drop,
}

impl<T, A: Allocator> Box<MaybeUninit<T>, A> {
    pub const unsafe fn assume_init(self) -> Box<T, A>;
    pub const fn write(mut boxed: Self, value: T) -> Box<T, A>;
    pub const unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self;
    pub const fn into_raw_with_allocator(b: Self) -> (*mut T, A);
    pub const fn into_unique(b: Self) -> (Unique<T>, A);
    pub const fn allocator(b: &Self) -> &A;
    pub const fn leak<'a>(b: Self) -> &'a mut T
    where
        A: 'a;
    pub const fn into_pin(boxed: Self) -> Pin<Self>
    where
        A: 'static;
}

unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> const Drop for Box<T, A>;
impl<T: ?Sized, A: Allocator> const From<Box<T, A>> for Pin<Box<T, A>>
where
    A: 'static;
impl<T: ?Sized, A: Allocator> const Deref for Box<T, A>;
impl<T: ?Sized, A: Allocator> const DerefMut for Box<T, A>;
impl<T: ?Sized, A: Allocator> const Unpin for Box<T, A> where A: 'static;
```

</details>

<details><summary>Example</summary>

```rust
pub struct ConstAllocator;

unsafe impl const Allocator for ConstAllocator {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        unsafe {
            let ptr = core::intrinsics::const_allocate(layout.size(), layout.align());
            Ok(NonNull::new_unchecked(ptr as *mut [u8; 0] as *mut [u8]))
        }
    }

    unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
        /* do nothing */
    }

    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.allocate(layout)
    }

    unsafe fn grow(
        &self,
        _ptr: NonNull<u8>,
        _old_layout: Layout,
        _new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unimplemented!()
    }

    unsafe fn grow_zeroed(
        &self,
        _ptr: NonNull<u8>,
        _old_layout: Layout,
        _new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unimplemented!()
    }

    unsafe fn shrink(
        &self,
        _ptr: NonNull<u8>,
        _old_layout: Layout,
        _new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unimplemented!()
    }

    fn by_ref(&self) -> &Self
    where
        Self: Sized,
    {
        self
    }
}

#[test]
fn const_box() {
    const VALUE: u32 = {
        let mut boxed = Box::new_in(1u32, ConstAllocator);
        assert!(*boxed == 1);

        *boxed = 42;
        assert!(*boxed == 42);

        *boxed
    };

    assert!(VALUE == 42);
}
```

</details>
  • Loading branch information
matthiaskrgr committed Jan 3, 2022
2 parents efaf429 + 51e4291 commit b6f45cc
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 32 deletions.
23 changes: 18 additions & 5 deletions library/alloc/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,17 +323,21 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {

#[cfg_attr(not(test), lang = "box_free")]
#[inline]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
// This signature has to be the same as `Box`, otherwise an ICE will happen.
// When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as
// well.
// For example if `Box` is changed to `struct Box<T: ?Sized, A: Allocator>(Unique<T>, A)`,
// this function has to be changed to `fn box_free<T: ?Sized, A: Allocator>(Unique<T>, A)` as well.
pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
pub(crate) const unsafe fn box_free<T: ?Sized, A: ~const Allocator + ~const Drop>(
ptr: Unique<T>,
alloc: A,
) {
unsafe {
let size = size_of_val(ptr.as_ref());
let align = min_align_of_val(ptr.as_ref());
let layout = Layout::from_size_align_unchecked(size, align);
alloc.deallocate(ptr.cast().into(), layout)
alloc.deallocate(From::from(ptr.cast()), layout)
}
}

Expand Down Expand Up @@ -361,13 +365,22 @@ extern "Rust" {
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
#[stable(feature = "global_alloc", since = "1.28.0")]
#[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
#[rustc_allocator_nounwind]
#[cold]
pub fn handle_alloc_error(layout: Layout) -> ! {
unsafe {
__rust_alloc_error_handler(layout.size(), layout.align());
pub const fn handle_alloc_error(layout: Layout) -> ! {
const fn ct_error(_: Layout) -> ! {
panic!("allocation failed");
}

fn rt_error(layout: Layout) -> ! {
unsafe {
__rust_alloc_error_handler(layout.size(), layout.align());
}
}

unsafe { core::intrinsics::const_eval_select((layout,), ct_error, rt_error) }
}

// For alloc test `std::alloc::handle_alloc_error` can be used directly.
Expand Down
92 changes: 68 additions & 24 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,13 @@ impl<T, A: Allocator> Box<T, A> {
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[must_use]
#[inline]
pub fn new_in(x: T, alloc: A) -> Self {
pub const fn new_in(x: T, alloc: A) -> Self
where
A: ~const Allocator + ~const Drop,
{
let mut boxed = Self::new_uninit_in(alloc);
unsafe {
boxed.as_mut_ptr().write(x);
Expand All @@ -372,8 +376,13 @@ impl<T, A: Allocator> Box<T, A> {
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError> {
pub const fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError>
where
T: ~const Drop,
A: ~const Allocator + ~const Drop,
{
let mut boxed = Self::try_new_uninit_in(alloc)?;
unsafe {
boxed.as_mut_ptr().write(x);
Expand Down Expand Up @@ -402,10 +411,14 @@ impl<T, A: Allocator> Box<T, A> {
/// assert_eq!(*five, 5)
/// ```
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[cfg(not(no_global_oom_handling))]
#[must_use]
// #[unstable(feature = "new_uninit", issue = "63291")]
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
pub const fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
where
A: ~const Allocator + ~const Drop,
{
let layout = Layout::new::<mem::MaybeUninit<T>>();
// NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable.
// That would make code size bigger.
Expand Down Expand Up @@ -439,7 +452,11 @@ impl<T, A: Allocator> Box<T, A> {
/// ```
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "new_uninit", issue = "63291")]
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
pub const fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
where
A: ~const Allocator + ~const Drop,
{
let layout = Layout::new::<mem::MaybeUninit<T>>();
let ptr = alloc.allocate(layout)?.cast();
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
Expand All @@ -466,10 +483,14 @@ impl<T, A: Allocator> Box<T, A> {
///
/// [zeroed]: mem::MaybeUninit::zeroed
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[cfg(not(no_global_oom_handling))]
// #[unstable(feature = "new_uninit", issue = "63291")]
#[must_use]
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
pub const fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
where
A: ~const Allocator + ~const Drop,
{
let layout = Layout::new::<mem::MaybeUninit<T>>();
// NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable.
// That would make code size bigger.
Expand Down Expand Up @@ -503,7 +524,11 @@ impl<T, A: Allocator> Box<T, A> {
/// [zeroed]: mem::MaybeUninit::zeroed
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "new_uninit", issue = "63291")]
pub fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
pub const fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
where
A: ~const Allocator + ~const Drop,
{
let layout = Layout::new::<mem::MaybeUninit<T>>();
let ptr = alloc.allocate_zeroed(layout)?.cast();
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
Expand All @@ -513,20 +538,22 @@ impl<T, A: Allocator> Box<T, A> {
/// `x` will be pinned in memory and unable to be moved.
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[must_use]
#[inline(always)]
pub fn pin_in(x: T, alloc: A) -> Pin<Self>
pub const fn pin_in(x: T, alloc: A) -> Pin<Self>
where
A: 'static,
A: 'static + ~const Allocator + ~const Drop,
{
Self::new_in(x, alloc).into()
Self::into_pin(Self::new_in(x, alloc))
}

/// Converts a `Box<T>` into a `Box<[T]>`
///
/// This conversion does not allocate on the heap and happens in place.
#[unstable(feature = "box_into_boxed_slice", issue = "71582")]
pub fn into_boxed_slice(boxed: Self) -> Box<[T], A> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
pub const fn into_boxed_slice(boxed: Self) -> Box<[T], A> {
let (raw, alloc) = Box::into_raw_with_allocator(boxed);
unsafe { Box::from_raw_in(raw as *mut [T; 1], alloc) }
}
Expand All @@ -543,8 +570,12 @@ impl<T, A: Allocator> Box<T, A> {
/// assert_eq!(Box::into_inner(c), 5);
/// ```
#[unstable(feature = "box_into_inner", issue = "80437")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn into_inner(boxed: Self) -> T {
pub const fn into_inner(boxed: Self) -> T
where
Self: ~const Drop,
{
*boxed
}
}
Expand Down Expand Up @@ -758,8 +789,9 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
/// assert_eq!(*five, 5)
/// ```
#[unstable(feature = "new_uninit", issue = "63291")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub unsafe fn assume_init(self) -> Box<T, A> {
pub const unsafe fn assume_init(self) -> Box<T, A> {
let (raw, alloc) = Box::into_raw_with_allocator(self);
unsafe { Box::from_raw_in(raw as *mut T, alloc) }
}
Expand Down Expand Up @@ -792,8 +824,9 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
/// }
/// ```
#[unstable(feature = "new_uninit", issue = "63291")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn write(mut boxed: Self, value: T) -> Box<T, A> {
pub const fn write(mut boxed: Self, value: T) -> Box<T, A> {
unsafe {
(*boxed).write(value);
boxed.assume_init()
Expand Down Expand Up @@ -938,8 +971,9 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
/// [memory layout]: self#memory-layout
/// [`Layout`]: crate::Layout
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self {
pub const unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self {
Box(unsafe { Unique::new_unchecked(raw) }, alloc)
}

Expand Down Expand Up @@ -1035,8 +1069,9 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
///
/// [memory layout]: self#memory-layout
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
pub const fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
let (leaked, alloc) = Box::into_unique(b);
(leaked.as_ptr(), alloc)
}
Expand All @@ -1046,9 +1081,10 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
issue = "none",
reason = "use `Box::leak(b).into()` or `Unique::from(Box::leak(b))` instead"
)]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
#[doc(hidden)]
pub fn into_unique(b: Self) -> (Unique<T>, A) {
pub const fn into_unique(b: Self) -> (Unique<T>, A) {
// Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a
// raw pointer for the type system. Turning it directly into a raw pointer would not be
// recognized as "releasing" the unique pointer to permit aliased raw accesses,
Expand All @@ -1064,8 +1100,9 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
/// to call it as `Box::allocator(&b)` instead of `b.allocator()`. This
/// is so that there is no conflict with a method on the inner type.
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn allocator(b: &Self) -> &A {
pub const fn allocator(b: &Self) -> &A {
&b.1
}

Expand Down Expand Up @@ -1105,8 +1142,9 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
/// assert_eq!(*static_ref, [4, 2, 3]);
/// ```
#[stable(feature = "box_leak", since = "1.26.0")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub fn leak<'a>(b: Self) -> &'a mut T
pub const fn leak<'a>(b: Self) -> &'a mut T
where
A: 'a,
{
Expand All @@ -1119,7 +1157,8 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
///
/// This is also available via [`From`].
#[unstable(feature = "box_into_pin", issue = "62370")]
pub fn into_pin(boxed: Self) -> Pin<Self>
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
pub const fn into_pin(boxed: Self) -> Pin<Self>
where
A: 'static,
{
Expand All @@ -1131,7 +1170,8 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
}

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> const Drop for Box<T, A> {
fn drop(&mut self) {
// FIXME: Do nothing, drop is currently performed by compiler.
}
Expand Down Expand Up @@ -1341,7 +1381,8 @@ impl<T> From<T> for Box<T> {
}

#[stable(feature = "pin", since = "1.33.0")]
impl<T: ?Sized, A: Allocator> From<Box<T, A>> for Pin<Box<T, A>>
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const From<Box<T, A>> for Pin<Box<T, A>>
where
A: 'static,
{
Expand Down Expand Up @@ -1720,7 +1761,8 @@ impl<T: ?Sized, A: Allocator> fmt::Pointer for Box<T, A> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const Deref for Box<T, A> {
type Target = T;

fn deref(&self) -> &T {
Expand All @@ -1729,7 +1771,8 @@ impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const DerefMut for Box<T, A> {
fn deref_mut(&mut self) -> &mut T {
&mut **self
}
Expand Down Expand Up @@ -1908,7 +1951,8 @@ impl<T: ?Sized, A: Allocator> AsMut<T> for Box<T, A> {
* could have a method to project a Pin<T> from it.
*/
#[stable(feature = "pin", since = "1.33.0")]
impl<T: ?Sized, A: Allocator> Unpin for Box<T, A> where A: 'static {}
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const Unpin for Box<T, A> where A: 'static {}

#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R> + Unpin, R, A: Allocator> Generator<R> for Box<G, A>
Expand Down
16 changes: 16 additions & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,20 @@
#![feature(array_windows)]
#![feature(async_stream)]
#![feature(coerce_unsized)]
#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))]
#![feature(const_box)]
#![cfg_attr(not(no_global_oom_handling), feature(const_btree_new))]
#![feature(const_cow_is_borrowed)]
#![feature(const_convert)]
#![feature(const_size_of_val)]
#![feature(const_align_of_val)]
#![feature(const_ptr_read)]
#![feature(const_maybe_uninit_write)]
#![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_refs_to_cell)]
#![feature(core_intrinsics)]
#![feature(const_eval_select)]
#![feature(const_pin)]
#![feature(dispatch_from_dyn)]
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
Expand Down Expand Up @@ -134,8 +145,13 @@
#![feature(box_syntax)]
#![feature(cfg_sanitize)]
#![feature(cfg_target_has_atomic)]
#![feature(const_deref)]
#![feature(const_fn_trait_bound)]
#![feature(const_mut_refs)]
#![feature(const_ptr_write)]
#![feature(const_precise_live_drops)]
#![feature(const_trait_impl)]
#![feature(const_try)]
#![cfg_attr(bootstrap, feature(destructuring_assignment))]
#![feature(dropck_eyepatch)]
#![feature(exclusive_range_pattern)]
Expand Down
Loading

0 comments on commit b6f45cc

Please sign in to comment.