diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index ddc6481eec78e..0c1a2dda4ae75 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -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, AllocErr> { NonNull::new(alloc(layout)).ok_or(AllocErr) diff --git a/src/liballoc/collections/mod.rs b/src/liballoc/collections/mod.rs index 5a33ddc14f004..48a07e694db4d 100644 --- a/src/liballoc/collections/mod.rs +++ b/src/liballoc/collections/mod.rs @@ -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 { /// 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 for CollectionAllocErr { #[inline] fn from(AllocErr: AllocErr) -> Self { - CollectionAllocErr::AllocErr + CollectionAllocErr::AllocErr(AllocErr) } } #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -impl From for CollectionAllocErr { +impl From for CollectionAllocErr { #[inline] fn from(_: LayoutErr) -> Self { CollectionAllocErr::CapacityOverflow diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d1fc5ac3b30d4..54da395dd275e 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -81,7 +81,7 @@ impl RawVec { let elem_size = mem::size_of::(); let alloc_size = cap.checked_mul(elem_size).unwrap_or_else(|| capacity_overflow()); - alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow()); + alloc_guard::(alloc_size).unwrap_or_else(|_| capacity_overflow()); // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { @@ -305,7 +305,7 @@ impl RawVec { // `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::(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 RawVec { // 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::(new_cap) { - Ok(ptr) => (new_cap, ptr.into()), - Err(_) => handle_alloc_error(Layout::array::(new_cap).unwrap()), + let layout = Layout::array::(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 RawVec { // 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::(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 RawVec { /// 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> { self.reserve_internal(used_cap, needed_extra_cap, Fallible, Exact) } @@ -410,7 +413,7 @@ impl RawVec { 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 RawVec { /// 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 { + -> Result> { // 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 RawVec { /// 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> { self.reserve_internal(used_cap, needed_extra_cap, Fallible, Amortized) } @@ -490,7 +493,7 @@ impl RawVec { 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 RawVec { let new_layout = Layout::new::().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::(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 RawVec { needed_extra_cap: usize, fallibility: Fallibility, strategy: ReserveStrategy, - ) -> Result<(), CollectionAllocErr> { + ) -> Result<(), CollectionAllocErr> { 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 RawVec { }; 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 { // all 4GB in user-space. e.g., PAE or x32 #[inline] -fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { +fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { if mem::size_of::() < 8 && alloc_size > core::isize::MAX as usize { Err(CapacityOverflow) } else { diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index c124457118cb9..81ceddafb5022 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -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, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, Self::Err>; /// Deallocate the memory referenced by `ptr`. /// @@ -820,7 +821,7 @@ pub unsafe trait Alloc { unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, 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, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, 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 { + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { 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, layout: Layout, - new_size: usize) -> Result { + new_size: usize) -> Result { 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(&mut self) -> Result, AllocErr> - where Self: Sized - { - let k = Layout::new::(); - 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(&mut self, ptr: NonNull) - where Self: Sized - { - let k = Layout::new::(); - 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(&mut self, n: usize) -> Result, AllocErr> - where Self: Sized - { - match Layout::array::(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(&mut self, - ptr: NonNull, - n_old: usize, - n_new: usize) -> Result, AllocErr> - where Self: Sized - { - match (Layout::array::(n_old), Layout::array::(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(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> - where Self: Sized - { - match Layout::array::(n) { - Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(ptr.cast(), k.clone())) - } - _ => { - Err(AllocErr) - } - } - } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 4241f47b661d7..56554eab4ce61 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -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, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index f9fb392f9f52b..f82fc56de5254 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -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), } }