diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2069b746b5a7f..239e3119e29a8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -339,6 +339,7 @@ language_item_table! { OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); GlobalAlloc, sym::global_alloc_ty, global_alloc_ty, Target::Struct, GenericRequirement::None; + NoaliasAllocator, sym::noalias_allocator, noalias_allocator_trait, Target::Trait, GenericRequirement::None; PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb433aef68cf8..80193b4246530 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1397,6 +1397,7 @@ symbols! { no_sanitize, no_stack_check, no_std, + noalias_allocator, nomem, non_ascii_idents, non_exhaustive, diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8729e98278a82..f4cd96ee8e5f5 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -54,9 +54,19 @@ unsafe extern "Rust" { #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Default, Debug)] // the compiler needs to know when a Box uses the global allocator vs a custom one +// FIXME(nia-e): change everything to use the noalias_allocator lang item instead #[lang = "global_alloc_ty"] pub struct Global; +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl core::alloc::AllocatorClone for Global {} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl core::alloc::StaticAllocator for Global {} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl core::alloc::NoaliasAllocator for Global {} + /// Allocates memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::alloc`] method diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index bd3f10a16dd9b..1790b69167a5b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -206,10 +206,12 @@ use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -use crate::alloc::{AllocError, Allocator, Global, Layout}; +use crate::alloc::{ + AllocError, Allocator, AllocatorClone, AllocatorEq, Global, Layout, StaticAllocator, +}; use crate::raw_vec::RawVec; #[cfg(not(no_global_oom_handling))] -use crate::str::from_boxed_utf8_unchecked; +use crate::str::from_boxed_utf8_unchecked_in; /// Conversion related impls for `Box<_>` (`From`, `downcast`, etc) mod convert; @@ -710,7 +712,7 @@ impl Box { #[inline(always)] pub fn pin_in(x: T, alloc: A) -> Pin where - A: 'static + Allocator, + A: StaticAllocator, { Self::into_pin(Self::new_in(x, alloc)) } @@ -1935,7 +1937,7 @@ impl Box { #[stable(feature = "box_into_pin", since = "1.63.0")] pub fn into_pin(boxed: Self) -> Pin where - A: 'static, + A: StaticAllocator, { // It's not possible to move or replace the insides of a `Pin>` // when `T: !Unpin`, so it's safe to pin it directly without any @@ -2109,11 +2111,10 @@ impl Clone for Box<[T], A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "box_slice_clone", since = "1.3.0")] -impl Clone for Box { +impl Clone for Box { fn clone(&self) -> Self { - // this makes a copy of the data - let buf: Box<[u8]> = self.as_bytes().into(); - unsafe { from_boxed_utf8_unchecked(buf) } + let buf = Box::clone_from_ref_in(self.as_bytes(), self.1.clone()); + unsafe { from_boxed_utf8_unchecked_in(buf) } } } @@ -2485,3 +2486,20 @@ unsafe impl Allocator for Box { unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocatorClone for Box +where + T: AllocatorClone, + A: Allocator, + Box: Clone, +{ +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocatorEq for Box +where + T: AllocatorEq + ?Sized, + A: Allocator, +{ +} diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index d6a8e78991b84..4f5dd89e6e3b3 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -5,7 +5,7 @@ use core::fmt; use core::mem; use core::pin::Pin; -use crate::alloc::Allocator; +use crate::alloc::{Allocator, StaticAllocator}; #[cfg(not(no_global_oom_handling))] use crate::borrow::Cow; use crate::boxed::Box; @@ -38,7 +38,7 @@ impl From for Box { #[stable(feature = "pin", since = "1.33.0")] impl From> for Pin> where - A: 'static, + A: StaticAllocator, { /// Converts a `Box` into a `Pin>`. If `T` does not implement [`Unpin`], then /// `*boxed` will be pinned in memory and unable to be moved. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 523c9b8b15858..a79f1d8992caf 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -268,7 +268,7 @@ use core::{borrow, fmt, hint}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -use crate::alloc::{AllocError, Allocator, Global, Layout}; +use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] @@ -1773,7 +1773,7 @@ impl Rc { #[stable(feature = "rc_weak", since = "1.4.0")] pub fn downgrade(this: &Self) -> Weak where - A: Clone, + A: AllocatorClone, { this.inner().inc_weak(); // Make sure we do not create a dangling Weak @@ -1854,7 +1854,7 @@ impl Rc { #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn increment_strong_count_in(ptr: *const T, alloc: A) where - A: Clone, + A: AllocatorClone, { // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop let rc = unsafe { mem::ManuallyDrop::new(Rc::::from_raw_in(ptr, alloc)) }; @@ -2030,7 +2030,7 @@ impl Rc { } #[cfg(not(no_global_oom_handling))] -impl Rc { +impl Rc { /// Makes a mutable reference into the given `Rc`. /// /// If there are other `Rc` pointers to the same allocation, then `make_mut` will @@ -2305,7 +2305,7 @@ impl Rc { // Free the allocation without dropping its contents let (bptr, alloc) = Box::into_raw_with_allocator(src); - let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, alloc.by_ref()); + let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, &alloc); drop(src); Self::from_ptr_in(ptr, alloc) @@ -2494,7 +2494,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Rc { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Rc { +impl Clone for Rc { /// Makes a clone of the `Rc` pointer. /// /// This creates another pointer to the same allocation, increasing the @@ -2519,10 +2519,10 @@ impl Clone for Rc { } #[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Rc {} +impl UseCloned for Rc {} #[unstable(feature = "share_trait", issue = "156756")] -impl Share for Rc {} +impl Share for Rc {} #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] @@ -3543,7 +3543,7 @@ impl Weak { #[stable(feature = "rc_weak", since = "1.4.0")] pub fn upgrade(&self) -> Option> where - A: Clone, + A: AllocatorClone, { let inner = self.inner()?; @@ -3688,7 +3688,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { } #[stable(feature = "rc_weak", since = "1.4.0")] -impl Clone for Weak { +impl Clone for Weak { /// Makes a clone of the `Weak` pointer that points to the same allocation. /// /// # Examples @@ -3710,7 +3710,7 @@ impl Clone for Weak { } #[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Weak {} +impl UseCloned for Weak {} #[stable(feature = "rc_weak", since = "1.4.0")] impl fmt::Debug for Weak { @@ -4410,7 +4410,7 @@ impl UniqueRc { } } -impl UniqueRc { +impl UniqueRc { /// Creates a new weak reference to the `UniqueRc`. /// /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index cf04402e3d984..d2ee3517e9235 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -794,6 +794,20 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { unsafe { Box::from_raw(Box::into_raw(v) as *mut str) } } +/// Converts a boxed slice of bytes to a boxed string slice without checking +/// that the string contains valid UTF-8 generically over the box's allocator. +/// +/// # Safety +/// +/// * The provided bytes must contain a valid UTF-8 sequence. +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe fn from_boxed_utf8_unchecked_in( + v: Box<[u8], A>, +) -> Box { + let (ptr, alloc) = Box::into_raw_with_allocator(v); + unsafe { Box::from_raw_in(ptr as *mut str, alloc) } +} + /// Converts leading ascii bytes in `s` by calling the `convert` function. /// /// For better average performance, this happens in chunks of `2*size_of::()`. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 4a9878d8e249d..8c717a7b2e242 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -35,7 +35,7 @@ use core::{borrow, fmt, hint}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -use crate::alloc::{AllocError, Allocator, Global, Layout}; +use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; @@ -1931,7 +1931,7 @@ impl Arc { #[stable(feature = "arc_weak", since = "1.4.0")] pub fn downgrade(this: &Self) -> Weak where - A: Clone, + A: AllocatorClone, { // This Relaxed is OK because we're checking the value in the CAS // below. @@ -2062,7 +2062,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn increment_strong_count_in(ptr: *const T, alloc: A) where - A: Clone, + A: AllocatorClone, { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = unsafe { mem::ManuallyDrop::new(Arc::from_raw_in(ptr, alloc)) }; @@ -2250,7 +2250,7 @@ impl Arc { // Free the allocation without dropping its contents let (bptr, alloc) = Box::into_raw_with_allocator(src); - let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, alloc.by_ref()); + let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, &alloc); drop(src); Self::from_ptr_in(ptr, alloc) @@ -2376,7 +2376,7 @@ impl ArcFromSlice for Arc<[T]> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Arc { +impl Clone for Arc { /// Makes a clone of the `Arc` pointer. /// /// This creates another pointer to the same allocation, increasing the @@ -2430,10 +2430,10 @@ impl Clone for Arc { } #[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Arc {} +impl UseCloned for Arc {} #[unstable(feature = "share_trait", issue = "156756")] -impl Share for Arc {} +impl Share for Arc {} #[stable(feature = "rust1", since = "1.0.0")] impl Deref for Arc { @@ -2455,7 +2455,7 @@ unsafe impl DerefPure for Arc {} impl LegacyReceiver for Arc {} #[cfg(not(no_global_oom_handling))] -impl Arc { +impl Arc { /// Makes a mutable reference into the given `Arc`. /// /// If there are other `Arc` pointers to the same allocation, then `make_mut` will @@ -3274,7 +3274,7 @@ impl Weak { #[stable(feature = "arc_weak", since = "1.4.0")] pub fn upgrade(&self) -> Option> where - A: Clone, + A: AllocatorClone, { #[inline] fn checked_increment(n: usize) -> Option { @@ -3409,7 +3409,7 @@ impl Weak { } #[stable(feature = "arc_weak", since = "1.4.0")] -impl Clone for Weak { +impl Clone for Weak { /// Makes a clone of the `Weak` pointer that points to the same allocation. /// /// # Examples @@ -3441,7 +3441,7 @@ impl Clone for Weak { } #[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Weak {} +impl UseCloned for Weak {} #[stable(feature = "downgraded_weak", since = "1.10.0")] impl Default for Weak { @@ -4029,7 +4029,7 @@ impl From> for Arc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc<[T], A> { +impl From> for Arc<[T], A> { /// Allocates a reference-counted slice and moves `v`'s items into it. /// /// # Example @@ -4839,7 +4839,7 @@ impl UniqueArc { } } -impl UniqueArc { +impl UniqueArc { /// Creates a new weak reference to the `UniqueArc`. /// /// Attempting to upgrade this weak reference will fail before the `UniqueArc` has been converted diff --git a/library/alloctests/tests/boxed.rs b/library/alloctests/tests/boxed.rs index c73b7109fb387..74db1c7de31e7 100644 --- a/library/alloctests/tests/boxed.rs +++ b/library/alloctests/tests/boxed.rs @@ -211,13 +211,6 @@ unsafe impl Allocator for ConstAllocator { } Ok(new_ptr) } - - fn by_ref(&self) -> &Self - where - Self: Sized, - { - self - } } #[allow(unused)] diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 102fb19efc8ea..b69bf4eecf26f 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -82,7 +82,8 @@ impl fmt::Display for AllocError { /// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and /// * [`layout.size()`] must fall in the range `min ..= max`, where: /// - `min` is the size of the layout used to allocate the block, and -/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`]. +/// - `max` is the actual size returned from [`allocate`], [`grow`], [`shrink`], +/// or their respective zeroed variants. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size @@ -90,9 +91,13 @@ impl fmt::Display for AllocError { /// # Safety /// /// Memory blocks that are [*currently allocated*] by an allocator, -/// must point to valid memory, and retain their validity until either: -/// - the memory block is deallocated, or -/// - the allocator is dropped. +/// must point to memory which is valid for both reads and writes where the +/// blocks do not overlap, and they must retain their validity until either: +/// - the memory block is deallocated, +/// - the allocator is mutated through public API taking `&mut` access (notably, +/// running the allocator's destructor is such a mutation), or +/// - any lifetime parameter on the allocator's type expires, thus making the +/// type invalid. /// /// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it. /// A copied or cloned allocator must behave like the original allocator. @@ -100,6 +105,12 @@ impl fmt::Display for AllocError { /// A memory block which is [*currently allocated*] may be passed to /// any method of the allocator that accepts such an argument. /// +/// Users of this trait must not rely on side effects of allocating or deallocating method calls +/// on `Allocator` implementors being observable (i.e. it is sound for an allocation followed +/// immediately by a deallocation to be optimised away). While it is possible to make +/// such calls *unlikely* to be elided (e.g. for benchmarking), this cannot be relied upon +/// for soundness. +/// /// Additionally, any memory block returned by the allocator must /// satisfy the allocation invariants described in `core::ptr`. /// In particular, if a block has base address `p` and size `n`, @@ -107,9 +118,21 @@ impl fmt::Display for AllocError { /// /// This ensures that pointer arithmetic within the allocation /// (for example, `ptr.add(len)`) cannot overflow the address space. +/// +/// As an experimental requirement, implementors of the trait must guarantee that +/// none of the methods herein unwind. Due to the bound being experimental, it may be +/// removed in the future and so this cannot be relied upon by other unsafe code for +/// proving soundness. +/// /// [*currently allocated*]: #currently-allocated-memory +// NOTE: the above bound on allocating methods not unwinding, alongside the similar +// bound on `AllocatorClone`, are currently load-bearing in std! see the below issues +// and make sure they cannot be triggered before relaxing this: +// https://rust.tf/156490 +// https://rust.tf/155746 #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[rustc_dyn_incompatible_trait] pub const unsafe trait Allocator { /// Attempts to allocate a block of memory. /// @@ -118,9 +141,10 @@ pub const unsafe trait Allocator { /// The returned block may have a larger size than specified by `layout.size()`, and may or may /// not have its contents initialized. /// - /// The returned block of memory remains valid as long as it is [*currently allocated*] and the shorter of: - /// - the borrow-checker lifetime of the allocator type itself. - /// - as long as the allocator and all its clones have not been dropped. + /// The returned block of memory remains valid as long as it is [*currently allocated*] and + /// the borrow-checker lifetime of the allocator type has not expired. Note that implementors + /// which are also [`AllocatorClone`], [`AllocatorEq`], or [`StaticAllocator`] must obey + /// stricter semantics. /// /// [*currently allocated*]: #currently-allocated-memory /// @@ -129,7 +153,7 @@ pub const unsafe trait Allocator { /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet /// allocator's size or alignment constraints. /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// Implementations are encouraged to return `Err` on memory exhaustion rather than /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) /// @@ -146,7 +170,7 @@ pub const unsafe trait Allocator { /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet /// allocator's size or alignment constraints. /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// Implementations are encouraged to return `Err` on memory exhaustion rather than /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) /// @@ -168,6 +192,13 @@ pub const unsafe trait Allocator { /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and /// * `layout` must [*fit*] that block of memory. /// + /// Note that it is *immediate* language UB for a deallocation or reallocation to + /// invalidate any outstanding references, smart pointers, etc.; thus, notably, an + /// allocator that has been moved into its own [*currently allocated*] memory may + /// not have its backing memory be freed, even if the allocator is never used again + /// afterwards. This is due to the fact that such a deallocation would invalidate the + /// `&self` reference passed to this method. + /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting unsafe fn deallocate(&self, ptr: NonNull, layout: Layout); @@ -202,7 +233,7 @@ pub const unsafe trait Allocator { /// Returns `Err` if the new layout does not meet the allocator's size and alignment /// constraints of the allocator, or if growing otherwise fails. /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// Implementations are encouraged to return `Err` on memory exhaustion rather than /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) /// @@ -265,7 +296,7 @@ pub const unsafe trait Allocator { /// Returns `Err` if the new layout does not meet the allocator's size and alignment /// constraints of the allocator, or if growing otherwise fails. /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// Implementations are encouraged to return `Err` on memory exhaustion rather than /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) /// @@ -329,7 +360,7 @@ pub const unsafe trait Allocator { /// Returns `Err` if the new layout does not meet the allocator's size and alignment /// constraints of the allocator, or if shrinking otherwise fails. /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// Implementations are encouraged to return `Err` on memory exhaustion rather than /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) /// @@ -362,19 +393,102 @@ pub const unsafe trait Allocator { Ok(new_ptr) } +} - /// Creates a "by reference" adapter for this instance of `Allocator`. - /// - /// The returned adapter also implements `Allocator` and will simply borrow this. - #[inline(always)] - fn by_ref(&self) -> &Self - where - Self: Sized, - { - self - } +/// Marker trait for enabling dyn-compatible allocators. +/// +/// # Usage +/// +/// `dyn DynAllocator` objects implement [`Allocator`], thus enabling these to be +/// used as dynamically-dispatched allocators. This also applies to `dyn` objects +/// with autotrait bounds alongside `DynAllocator`, e.g. `dyn DynAllocator + Send`. +/// +/// # Safety +/// +/// Same as [`Allocator`]. +#[unstable(feature = "allocator_api", issue = "32838")] +#[expect(private_bounds)] +pub impl(self) unsafe trait DynAllocator: DynAllocatorInternal {} + +unsafe trait DynAllocatorInternal { + fn __dyn_allocate(&self, layout: Layout) -> Result, AllocError>; + fn __dyn_allocate_zeroed(&self, layout: Layout) -> Result, AllocError>; + unsafe fn __dyn_deallocate(&self, ptr: NonNull, layout: Layout); + unsafe fn __dyn_grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError>; + unsafe fn __dyn_grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError>; + unsafe fn __dyn_shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError>; +} + +/// Guarantees that we can emit `noalias` attributes for a certain allocator. To enable this, +/// the pointer passed back in via de/reallocating methods must only be used to access +/// memory inside of that allocation. Furthermore, this pointer should be considered +/// "mutably borrowed" from the pointer returned by (re)allocating methods and the usual +/// aliasing rules for mutable borrows apply: when their lifetime ends (e.g. because a +/// pointer they were derived from gets used again), they are invalidated must not be used +/// anymore. +/// +/// This is *highly unlikely* to be possible to implement for most allocators. LLVM +/// maintains special-case code for the global allocator in order to enable this trait +/// to always be implemented for `Global`, but an allocator simply being suitable +/// for use as the global allocator *does not* mean it can implement this trait when +/// it is *not* the global allocator. +#[lang = "noalias_allocator"] +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait NoaliasAllocator: Allocator {} + +/// Marks a type's [`Clone`] implementation as sound with regard to [`Allocator`]. +/// Implementors must ensure that, upon cloning, the two allocators are interchangeable +/// (i.e. it is possible to free memory with one that was allocated with the other). +/// Further, mutable accesses such as moving or dropping the allocator must not invalidate +/// its currently allocated blocks at least so long as clones exist. +/// +/// Additionally, the currently-experimental bound that allocators do not unwind when +/// (de)allocating also applies to guaranteeing allocators will not panic when cloned. +/// As this is an experimental requirement that may be relaxed later, unsafe code cannot +/// rely on it for correctness. This bound trivially holds for allocators that are `Copy`. +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait AllocatorClone: Allocator + Clone {} + +/// Marks a type's [`PartialEq`] implementation as sound with regard to [`Allocator`]. +/// Implementors must ensure that, upon equality, the two allocators are interchangeable +/// (i.e. it is possible to free memory with one that was allocated with the other), and +/// that the two allocators behave "as if" they are clones of each other as per +/// [`AllocatorClone`]. +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait AllocatorEq: Allocator + PartialEq +where + T: ?Sized + AllocatorEq, +{ } +/// Marks that an allocator will never invalidate currently allocated memory, even +/// if its lifetime expires or a mutable reference to it is acquired. This trivially +/// applies to allocators that always maintain global state, e.g. `System` or `Global`. +/// Notably, this means that the only way for memory obtained from such an allocator +/// to be invalidated is an explicit call to a de/reallocating method. +/// +/// This is a necessity in conjunction with [`Pin`], as only `'static` allocators may +/// be used to back a pinned pointer. +/// +/// [`Pin`]: ../../core/pin/struct.Pin.html +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait StaticAllocator: Allocator {} + #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe impl Allocator for &A @@ -485,3 +599,109 @@ where unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } + +unsafe impl DynAllocatorInternal for A { + fn __dyn_allocate(&self, layout: Layout) -> Result, AllocError> { + self.allocate(layout) + } + fn __dyn_allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { + self.allocate_zeroed(layout) + } + unsafe fn __dyn_deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: Guaranteed by caller + unsafe { self.deallocate(ptr, layout) } + } + unsafe fn __dyn_grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.grow(ptr, old_layout, new_layout) } + } + unsafe fn __dyn_grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.grow_zeroed(ptr, old_layout, new_layout) } + } + unsafe fn __dyn_shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.shrink(ptr, old_layout, new_layout) } + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl DynAllocator for A {} + +// FIXME(nia-e): See if it's possible to make this built-in to the typesystem, +// e.g. by making the impl work on arbitrary `dyn DynAlloc + Foo + Bar`. Otherwise, +// just expand this macro with other trait combinations for now. Also needs to be +// extendd for arbitrary `dyn DynAllocator + 'a`. +// https://rust.tf/157506 + +macro_rules! impl_dyn_allocator { + ($t:ty, $($n:ty),+) => { + impl_dyn_allocator!($t); + impl_dyn_allocator!($($n),+); + }; + + ($t:ty) => { + #[unstable(feature = "allocator_api", issue = "32838")] + unsafe impl Allocator for $t { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.__dyn_allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: Guaranteed by caller + unsafe { self.__dyn_deallocate(ptr, layout) } + } + fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { + self.__dyn_allocate_zeroed(layout) + } + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.__dyn_grow(ptr, old_layout, new_layout) } + } + unsafe fn grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.__dyn_grow_zeroed(ptr, old_layout, new_layout) } + } + unsafe fn shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: Guaranteed by caller + unsafe { self.__dyn_shrink(ptr, old_layout, new_layout) } + } + } + }; +} + +impl_dyn_allocator!( + dyn DynAllocator, + dyn DynAllocator + Send, + dyn DynAllocator + Sync, + dyn DynAllocator + Send + Sync +); diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 7a576e083df7c..53939b9dcf2b3 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -138,6 +138,12 @@ pub use alloc_crate::alloc::*; #[derive(Debug, Default, Copy, Clone)] pub struct System; +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl core::alloc::AllocatorClone for System {} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl core::alloc::StaticAllocator for System {} + impl System { #[inline] fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { diff --git a/tests/ui/allocator/dyn-compatible.rs b/tests/ui/allocator/dyn-compatible.rs deleted file mode 100644 index 9d8235e58d929..0000000000000 --- a/tests/ui/allocator/dyn-compatible.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -// Check that `Allocator` is dyn-compatible, this allows for polymorphic allocators - -#![feature(allocator_api)] - -use std::alloc::{Allocator, System}; - -fn ensure_dyn_compatible(_: &dyn Allocator) {} - -fn main() { - ensure_dyn_compatible(&System); -} diff --git a/tests/ui/box/leak-alloc.rs b/tests/ui/box/leak-alloc.rs index 3f0f39f448b91..e87650f42ab61 100644 --- a/tests/ui/box/leak-alloc.rs +++ b/tests/ui/box/leak-alloc.rs @@ -21,7 +21,7 @@ fn use_value(_: u32) {} fn main() { let alloc = Alloc {}; - let boxed = Box::new_in(10, alloc.by_ref()); + let boxed = Box::new_in(10, &alloc); let theref = Box::leak(boxed); drop(alloc); //~^ ERROR cannot move out of `alloc` because it is borrowed diff --git a/tests/ui/box/leak-alloc.stderr b/tests/ui/box/leak-alloc.stderr index bdaa9449f913e..ad8eb17fd28d0 100644 --- a/tests/ui/box/leak-alloc.stderr +++ b/tests/ui/box/leak-alloc.stderr @@ -3,8 +3,8 @@ error[E0505]: cannot move out of `alloc` because it is borrowed | LL | let alloc = Alloc {}; | ----- binding `alloc` declared here -LL | let boxed = Box::new_in(10, alloc.by_ref()); - | ----- borrow of `alloc` occurs here +LL | let boxed = Box::new_in(10, &alloc); + | ------ borrow of `alloc` occurs here LL | let theref = Box::leak(boxed); LL | drop(alloc); | ^^^^^ move out of `alloc` occurs here @@ -18,8 +18,8 @@ note: if `Alloc` implemented `Clone`, you could clone the value LL | struct Alloc {} | ^^^^^^^^^^^^ consider implementing `Clone` for this type ... -LL | let boxed = Box::new_in(10, alloc.by_ref()); - | ----- you could clone this value +LL | let boxed = Box::new_in(10, &alloc); + | ----- you could clone this value error: aborting due to 1 previous error