Skip to content

Remove helper-methods from Alloc trait and add associated Err type. #60771

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

Closed
wants to merge 4 commits into from
Closed
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
2 changes: 2 additions & 0 deletions src/liballoc/alloc.rs
Original file line number Diff line number Diff line change
@@ -146,6 +146,8 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 {

#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl Alloc for Global {
type Err = AllocErr;

#[inline]
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
NonNull::new(alloc(layout)).ok_or(AllocErr)
8 changes: 4 additions & 4 deletions src/liballoc/collections/mod.rs
Original file line number Diff line number Diff line change
@@ -46,24 +46,24 @@ use crate::alloc::{AllocErr, LayoutErr};
/// Augments `AllocErr` with a CapacityOverflow variant.
#[derive(Clone, PartialEq, Eq, Debug)]
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
pub enum CollectionAllocErr {
pub enum CollectionAllocErr<E = AllocErr> {
/// Error due to the computed capacity exceeding the collection's maximum
/// (usually `isize::MAX` bytes).
CapacityOverflow,
/// Error due to the allocator (see the `AllocErr` type's docs).
AllocErr,
AllocErr(E),
}

#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
impl From<AllocErr> for CollectionAllocErr {
#[inline]
fn from(AllocErr: AllocErr) -> Self {
CollectionAllocErr::AllocErr
CollectionAllocErr::AllocErr(AllocErr)
}
}

#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
impl From<LayoutErr> for CollectionAllocErr {
impl<E> From<LayoutErr> for CollectionAllocErr<E> {
#[inline]
fn from(_: LayoutErr) -> Self {
CollectionAllocErr::CapacityOverflow
37 changes: 19 additions & 18 deletions src/liballoc/raw_vec.rs
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ impl<T, A: Alloc> RawVec<T, A> {
let elem_size = mem::size_of::<T>();

let alloc_size = cap.checked_mul(elem_size).unwrap_or_else(|| capacity_overflow());
alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow());
alloc_guard::<A::Err>(alloc_size).unwrap_or_else(|_| capacity_overflow());

// handles ZSTs and `cap = 0` alike
let ptr = if alloc_size == 0 {
@@ -305,7 +305,7 @@ impl<T, A: Alloc> RawVec<T, A> {
// `from_size_align_unchecked`.
let new_cap = 2 * self.cap;
let new_size = new_cap * elem_size;
alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow());
alloc_guard::<A::Err>(new_size).unwrap_or_else(|_| capacity_overflow());
let ptr_res = self.a.realloc(NonNull::from(self.ptr).cast(),
cur,
new_size);
@@ -320,9 +320,12 @@ impl<T, A: Alloc> RawVec<T, A> {
// skip to 4 because tiny Vec's are dumb; but not if that
// would cause overflow
let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 };
match self.a.alloc_array::<T>(new_cap) {
Ok(ptr) => (new_cap, ptr.into()),
Err(_) => handle_alloc_error(Layout::array::<T>(new_cap).unwrap()),
let layout = Layout::array::<T>(new_cap)
.expect("unable to create array layout");

match self.a.alloc(layout) {
Ok(ptr) => (new_cap, ptr.cast().into()),
Err(_) => handle_alloc_error(layout),
}
}
};
@@ -366,7 +369,7 @@ impl<T, A: Alloc> RawVec<T, A> {
// overflow and the alignment is sufficiently small.
let new_cap = 2 * self.cap;
let new_size = new_cap * elem_size;
alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow());
alloc_guard::<A::Err>(new_size).unwrap_or_else(|_| capacity_overflow());
match self.a.grow_in_place(NonNull::from(self.ptr).cast(), old_layout, new_size) {
Ok(_) => {
// We can't directly divide `size`.
@@ -382,7 +385,7 @@ impl<T, A: Alloc> RawVec<T, A> {

/// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize)
-> Result<(), CollectionAllocErr> {
-> Result<(), CollectionAllocErr<A::Err>> {

self.reserve_internal(used_cap, needed_extra_cap, Fallible, Exact)
}
@@ -410,7 +413,7 @@ impl<T, A: Alloc> RawVec<T, A> {
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Exact) {
Err(CapacityOverflow) => capacity_overflow(),
Err(AllocErr) => unreachable!(),
Err(AllocErr(_)) => unreachable!(),
Ok(()) => { /* yay */ }
}
}
@@ -419,7 +422,7 @@ impl<T, A: Alloc> RawVec<T, A> {
/// needed_extra_cap` elements. This logic is used in amortized reserve methods.
/// Returns `(new_capacity, new_alloc_size)`.
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize)
-> Result<usize, CollectionAllocErr> {
-> Result<usize, CollectionAllocErr<A::Err>> {

// Nothing we can really do about these checks :(
let required_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
@@ -431,7 +434,7 @@ impl<T, A: Alloc> RawVec<T, A> {

/// The same as `reserve`, but returns on errors instead of panicking or aborting.
pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize)
-> Result<(), CollectionAllocErr> {
-> Result<(), CollectionAllocErr<A::Err>> {
self.reserve_internal(used_cap, needed_extra_cap, Fallible, Amortized)
}

@@ -490,7 +493,7 @@ impl<T, A: Alloc> RawVec<T, A> {
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Amortized) {
Err(CapacityOverflow) => capacity_overflow(),
Err(AllocErr) => unreachable!(),
Err(AllocErr(_)) => unreachable!(),
Ok(()) => { /* yay */ }
}
}
@@ -538,7 +541,7 @@ impl<T, A: Alloc> RawVec<T, A> {

let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
// FIXME: may crash and burn on over-reserve
alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow());
alloc_guard::<A::Err>(new_layout.size()).unwrap_or_else(|_| capacity_overflow());
match self.a.grow_in_place(
NonNull::from(self.ptr).cast(), old_layout, new_layout.size(),
) {
@@ -636,10 +639,8 @@ impl<T, A: Alloc> RawVec<T, A> {
needed_extra_cap: usize,
fallibility: Fallibility,
strategy: ReserveStrategy,
) -> Result<(), CollectionAllocErr> {
) -> Result<(), CollectionAllocErr<A::Err>> {
unsafe {
use crate::alloc::AllocErr;

// NOTE: we don't early branch on ZSTs here because we want this
// to actually catch "asking for more than usize::MAX" in that case.
// If we make it past the first branch then we are guaranteed to
@@ -669,11 +670,11 @@ impl<T, A: Alloc> RawVec<T, A> {
};

match (&res, fallibility) {
(Err(AllocErr), Infallible) => handle_alloc_error(new_layout),
(Err(_), Infallible) => handle_alloc_error(new_layout),
_ => {}
}

self.ptr = res?.cast().into();
self.ptr = res.map_err(|e| CollectionAllocErr::AllocErr(e))?.cast().into();
self.cap = new_cap;

Ok(())
@@ -731,7 +732,7 @@ unsafe impl<#[may_dangle] T, A: Alloc> Drop for RawVec<T, A> {
// all 4GB in user-space. e.g., PAE or x32

#[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> {
fn alloc_guard<E>(alloc_size: usize) -> Result<(), CollectionAllocErr<E>> {
if mem::size_of::<usize>() < 8 && alloc_size > core::isize::MAX as usize {
Err(CapacityOverflow)
} else {
210 changes: 6 additions & 204 deletions src/libcore/alloc.rs
Original file line number Diff line number Diff line change
@@ -660,6 +660,7 @@ pub unsafe trait GlobalAlloc {
/// the future.
#[unstable(feature = "allocator_api", issue = "32838")]
pub unsafe trait Alloc {
type Err;

// (Note: some existing allocators have unspecified but well-defined
// behavior in response to a zero size allocation request ;
@@ -707,7 +708,7 @@ pub unsafe trait Alloc {
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr>;
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, Self::Err>;

/// Deallocate the memory referenced by `ptr`.
///
@@ -820,7 +821,7 @@ pub unsafe trait Alloc {
unsafe fn realloc(&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize) -> Result<NonNull<u8>, AllocErr> {
new_size: usize) -> Result<NonNull<u8>, Self::Err> {
let old_size = layout.size();

if new_size >= old_size {
@@ -863,7 +864,7 @@ pub unsafe trait Alloc {
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, Self::Err> {
let size = layout.size();
let p = self.alloc(layout);
if let Ok(p) = p {
@@ -891,7 +892,7 @@ pub unsafe trait Alloc {
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, Self::Err> {
let usable_size = self.usable_size(&layout);
self.alloc(layout).map(|p| Excess(p, usable_size.1))
}
@@ -918,7 +919,7 @@ pub unsafe trait Alloc {
unsafe fn realloc_excess(&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize) -> Result<Excess, AllocErr> {
new_size: usize) -> Result<Excess, Self::Err> {
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
let usable_size = self.usable_size(&new_layout);
self.realloc(ptr, layout, new_size)
@@ -1030,203 +1031,4 @@ pub unsafe trait Alloc {
Err(CannotReallocInPlace)
}
}


// == COMMON USAGE PATTERNS ==
// alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array

/// Allocates a block suitable for holding an instance of `T`.
///
/// Captures a common usage pattern for allocators.
///
/// The returned block is suitable for passing to the
/// `alloc`/`realloc` methods of this allocator.
///
/// Note to implementors: If this returns `Ok(ptr)`, then `ptr`
/// must be considered "currently allocated" and must be
/// acceptable input to methods such as `realloc` or `dealloc`,
/// *even if* `T` is a zero-sized type. In other words, if your
/// `Alloc` implementation overrides this method in a manner
/// that can return a zero-sized `ptr`, then all reallocation and
/// deallocation methods need to be similarly overridden to accept
/// such values as input.
///
/// # Errors
///
/// Returning `Err` indicates that either memory is exhausted or
/// `T` does not meet allocator's size or alignment constraints.
///
/// For zero-sized `T`, may return either of `Ok` or `Err`, but
/// will *not* yield undefined behavior.
///
/// Clients wishing to abort computation in response to an
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
fn alloc_one<T>(&mut self) -> Result<NonNull<T>, AllocErr>
where Self: Sized
{
let k = Layout::new::<T>();
if k.size() > 0 {
unsafe { self.alloc(k).map(|p| p.cast()) }
} else {
Err(AllocErr)
}
}

/// Deallocates a block suitable for holding an instance of `T`.
///
/// The given block must have been produced by this allocator,
/// and must be suitable for storing a `T` (in terms of alignment
/// as well as minimum and maximum size); otherwise yields
/// undefined behavior.
///
/// Captures a common usage pattern for allocators.
///
/// # Safety
///
/// This function is unsafe because undefined behavior can result
/// if the caller does not ensure both:
///
/// * `ptr` must denote a block of memory currently allocated via this allocator
///
/// * the layout of `T` must *fit* that block of memory.
unsafe fn dealloc_one<T>(&mut self, ptr: NonNull<T>)
where Self: Sized
{
let k = Layout::new::<T>();
if k.size() > 0 {
self.dealloc(ptr.cast(), k);
}
}

/// Allocates a block suitable for holding `n` instances of `T`.
///
/// Captures a common usage pattern for allocators.
///
/// The returned block is suitable for passing to the
/// `alloc`/`realloc` methods of this allocator.
///
/// Note to implementors: If this returns `Ok(ptr)`, then `ptr`
/// must be considered "currently allocated" and must be
/// acceptable input to methods such as `realloc` or `dealloc`,
/// *even if* `T` is a zero-sized type. In other words, if your
/// `Alloc` implementation overrides this method in a manner
/// that can return a zero-sized `ptr`, then all reallocation and
/// deallocation methods need to be similarly overridden to accept
/// such values as input.
///
/// # Errors
///
/// Returning `Err` indicates that either memory is exhausted or
/// `[T; n]` does not meet allocator's size or alignment
/// constraints.
///
/// For zero-sized `T` or `n == 0`, may return either of `Ok` or
/// `Err`, but will *not* yield undefined behavior.
///
/// Always returns `Err` on arithmetic overflow.
///
/// Clients wishing to abort computation in response to an
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
fn alloc_array<T>(&mut self, n: usize) -> Result<NonNull<T>, AllocErr>
where Self: Sized
{
match Layout::array::<T>(n) {
Ok(ref layout) if layout.size() > 0 => {
unsafe {
self.alloc(layout.clone()).map(|p| p.cast())
}
}
_ => Err(AllocErr),
}
}

/// Reallocates a block previously suitable for holding `n_old`
/// instances of `T`, returning a block suitable for holding
/// `n_new` instances of `T`.
///
/// Captures a common usage pattern for allocators.
///
/// The returned block is suitable for passing to the
/// `alloc`/`realloc` methods of this allocator.
///
/// # Safety
///
/// This function is unsafe because undefined behavior can result
/// if the caller does not ensure all of the following:
///
/// * `ptr` must be currently allocated via this allocator,
///
/// * the layout of `[T; n_old]` must *fit* that block of memory.
///
/// # Errors
///
/// Returning `Err` indicates that either memory is exhausted or
/// `[T; n_new]` does not meet allocator's size or alignment
/// constraints.
///
/// For zero-sized `T` or `n_new == 0`, may return either of `Ok` or
/// `Err`, but will *not* yield undefined behavior.
///
/// Always returns `Err` on arithmetic overflow.
///
/// Clients wishing to abort computation in response to a
/// reallocation error are encouraged to call the [`handle_alloc_error`] function,
/// rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
unsafe fn realloc_array<T>(&mut self,
ptr: NonNull<T>,
n_old: usize,
n_new: usize) -> Result<NonNull<T>, AllocErr>
where Self: Sized
{
match (Layout::array::<T>(n_old), Layout::array::<T>(n_new)) {
(Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => {
debug_assert!(k_old.align() == k_new.align());
self.realloc(ptr.cast(), k_old.clone(), k_new.size()).map(NonNull::cast)
}
_ => {
Err(AllocErr)
}
}
}

/// Deallocates a block suitable for holding `n` instances of `T`.
///
/// Captures a common usage pattern for allocators.
///
/// # Safety
///
/// This function is unsafe because undefined behavior can result
/// if the caller does not ensure both:
///
/// * `ptr` must denote a block of memory currently allocated via this allocator
///
/// * the layout of `[T; n]` must *fit* that block of memory.
///
/// # Errors
///
/// Returning `Err` indicates that either `[T; n]` or the given
/// memory block does not meet allocator's size or alignment
/// constraints.
///
/// Always returns `Err` on arithmetic overflow.
unsafe fn dealloc_array<T>(&mut self, ptr: NonNull<T>, n: usize) -> Result<(), AllocErr>
where Self: Sized
{
match Layout::array::<T>(n) {
Ok(ref k) if k.size() > 0 => {
Ok(self.dealloc(ptr.cast(), k.clone()))
}
_ => {
Err(AllocErr)
}
}
}
}
2 changes: 2 additions & 0 deletions src/libstd/alloc.rs
Original file line number Diff line number Diff line change
@@ -136,6 +136,8 @@ pub struct System;
// The Alloc impl just forwards to the GlobalAlloc impl, which is in `std::sys::*::alloc`.
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl Alloc for System {
type Err = AllocErr;

#[inline]
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr)
3 changes: 2 additions & 1 deletion src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
use crate::iter::{FromIterator, FusedIterator};
use crate::ops::Index;
use crate::sys;
use crate::alloc::AllocErr;

/// A hash map implemented with quadratic probing and SIMD lookup.
///
@@ -2540,7 +2541,7 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K,
fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> CollectionAllocErr {
match err {
hashbrown::CollectionAllocErr::CapacityOverflow => CollectionAllocErr::CapacityOverflow,
hashbrown::CollectionAllocErr::AllocErr => CollectionAllocErr::AllocErr,
hashbrown::CollectionAllocErr::AllocErr => CollectionAllocErr::AllocErr(AllocErr),
}
}