Skip to content
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

fallible allocator experiment #111970

Closed
wants to merge 15 commits into from
12 changes: 9 additions & 3 deletions compiler/rustc_serialize/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Support code for encoding and decoding types.

use std::alloc::Allocator;
use std::alloc::{Allocator, Fatal};
use std::borrow::Cow;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
Expand Down Expand Up @@ -273,7 +273,10 @@ impl<D: Decoder, T> Decodable<D> for PhantomData<T> {
}
}

impl<D: Decoder, A: Allocator + Default, T: Decodable<D>> Decodable<D> for Box<[T], A> {
impl<D: Decoder, A: Default, T: Decodable<D>> Decodable<D> for Box<[T], A>
where
A: Allocator<ErrorHandling = Fatal>,
{
fn decode(d: &mut D) -> Box<[T], A> {
let v: Vec<T, A> = Decodable::decode(d);
v.into_boxed_slice()
Expand Down Expand Up @@ -308,7 +311,10 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for Vec<T> {
}
}

impl<D: Decoder, T: Decodable<D>, A: Allocator + Default> Decodable<D> for Vec<T, A> {
impl<D: Decoder, T: Decodable<D>, A: Default> Decodable<D> for Vec<T, A>
where
A: Allocator<ErrorHandling = Fatal>,
{
default fn decode(d: &mut D) -> Vec<T, A> {
let len = d.read_usize();
let allocator = A::default();
Expand Down
220 changes: 220 additions & 0 deletions library/alloc/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ unsafe impl Allocator for Global {
},
}
}

#[cfg(not(no_global_oom_handling))]
type ErrorHandling = Fatal;
#[cfg(no_global_oom_handling)]
type ErrorHandling = Fallible;
}

/// The allocator for unique pointers.
Expand Down Expand Up @@ -443,3 +448,218 @@ impl<T: Copy> WriteCloneIntoRaw for T {
unsafe { target.copy_from_nonoverlapping(self, 1) };
}
}

#[cfg(all(not(no_global_oom_handling), not(test)))]
use core::error::Error;

/// Trait for handling alloc errors for allocators which
/// panic or abort instead of returning errors.
#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
#[rustc_specialization_trait]
pub trait HandleAllocError: Error {
/// Globally handle this allocation error
fn handle_alloc_error(self) -> !;
}

/// Error handling mode to use when the user of the type wants to ignore
/// allocation failures, treating them as a fatal error. Functions
/// performing allocation will return values directly.
#[derive(Debug)]
#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
pub struct Fatal;

#[unstable(feature = "alloc_internals", issue = "none")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
impl error_handling_sealed::Sealed for Fatal {}

#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
impl ErrorHandling for Fatal {
type Result<T, E: Error> = T;

fn map_result<T, E: Error>(result: Result<T, E>) -> Self::Result<T, E> {
/// Hack around lack of `cfg(no_global_oom_handling)` in core.
///
/// Using post-monomorphization errors and specialization,
/// we can enforce that any error used with `Fatal` implements
/// `HandleAllocError`, without requiring that all errors used
/// with fallible allocation implement it. This also allows
/// for `HandleAllocError` to live with the rest of the
/// global allocation handling in the `alloc` crate.
trait HandleAllocErrorInternal {
fn handle_alloc_error_internal(self) -> !;
}
impl<E: Error> HandleAllocErrorInternal for E {
default fn handle_alloc_error_internal(self) -> ! {
const {
panic!(
"user must implement `HandleAllocError` for any error type used with the `Fatal` kind of `ErrorHandling`"
)
}
}
}
impl<E: HandleAllocError> HandleAllocErrorInternal for E {
fn handle_alloc_error_internal(self) -> ! {
self.handle_alloc_error()
}
}

result.unwrap_or_else(|e| e.handle_alloc_error_internal())
}
}

/// Wrapper around an existing allocator allowing one to
/// use a fallible allocator as an infallible one.
#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
#[derive(Debug)]
pub struct FatalAdapter<A: Allocator<ErrorHandling = Fallible>>(pub A);

#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
unsafe impl<A: Allocator<ErrorHandling = Fallible>> Allocator for FatalAdapter<A> {
fn allocate(&self, layout: Layout) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
self.0.allocate(layout)
}

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

unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: Layout) {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.deallocate(ptr, layout) }
}

unsafe fn grow(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.grow(ptr, old_layout, new_layout) }
}

unsafe fn grow_zeroed(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.grow_zeroed(ptr, old_layout, new_layout) }
}

unsafe fn shrink(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.shrink(ptr, old_layout, new_layout) }
}

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

type ErrorHandling = Fatal;
}

/// Wrapper around an existing allocator allowing one to
/// use an infallible allocator as a fallible one.
#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
#[derive(Debug)]
pub struct FallibleAdapter<A: Allocator<ErrorHandling = Fatal>>(pub A);

#[unstable(feature = "allocator_api", issue = "32838")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
unsafe impl<A: Allocator<ErrorHandling = Fatal>> Allocator for FallibleAdapter<A> {
fn allocate(&self, layout: Layout) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
self.0.allocate(layout)
}

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

unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: Layout) {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.deallocate(ptr, layout) }
}

unsafe fn grow(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.grow(ptr, old_layout, new_layout) }
}

unsafe fn grow_zeroed(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.grow_zeroed(ptr, old_layout, new_layout) }
}

unsafe fn shrink(
&self,
ptr: core::ptr::NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<core::ptr::NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { self.0.shrink(ptr, old_layout, new_layout) }
}

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

type ErrorHandling = Fallible;
}

#[cfg(test)]
pub use std::alloc::{FallibleAdapter, Fatal, FatalAdapter, HandleAllocError};

#[cfg(not(no_global_oom_handling))]
use crate::collections::{TryReserveError, TryReserveErrorKind};

// One central function responsible for reporting capacity overflows. This'll
// ensure that the code generation related to these panics is minimal as there's
// only one location which panics rather than a bunch throughout the module.
#[cfg(not(no_global_oom_handling))]
pub(crate) fn capacity_overflow() -> ! {
panic!("capacity overflow");
}

#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
impl HandleAllocError for TryReserveError {
fn handle_alloc_error(self) -> ! {
match self.kind() {
TryReserveErrorKind::CapacityOverflow => capacity_overflow(),
TryReserveErrorKind::AllocError { layout, .. } => handle_alloc_error(layout),
}
}
}

pub(crate) type AllocResult<A, T, E> =
<<A as Allocator>::ErrorHandling as ErrorHandling>::Result<T, E>;
Loading