Skip to content

Introduce Arena::try_alloc_from_iter. #140023

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
68 changes: 45 additions & 23 deletions compiler/rustc_arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#![feature(decl_macro)]
#![feature(dropck_eyepatch)]
#![feature(maybe_uninit_slice)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(unwrap_infallible)]
// tidy-alphabetical-end

use std::alloc::Layout;
Expand Down Expand Up @@ -200,6 +202,18 @@ impl<T> TypedArena<T> {
/// storing the elements in the arena.
#[inline]
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
self.try_alloc_from_iter(iter.into_iter().map(Ok::<T, !>)).into_ok()
}

/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
///
/// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
/// storing the elements in the arena.
#[inline]
pub fn try_alloc_from_iter<E>(
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
// reference to `self` and adding elements to the arena during iteration.
Expand All @@ -214,18 +228,19 @@ impl<T> TypedArena<T> {
// doesn't need to be hyper-optimized.
assert!(size_of::<T>() != 0);

let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
let mut vec = vec?;
if vec.is_empty() {
return &mut [];
return Ok(&mut []);
}
// Move the content to the arena by copying and then forgetting it.
let len = vec.len();
let start_ptr = self.alloc_raw_slice(len);
unsafe {
Ok(unsafe {
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
}
})
}

/// Grows the arena.
Expand Down Expand Up @@ -566,27 +581,34 @@ impl DroplessArena {
// `drop`.
unsafe { self.write_from_iter(iter, len, mem) }
}
(_, _) => {
outline(move || -> &mut [T] {
// Takes care of reentrancy.
let mut vec: SmallVec<[_; 8]> = iter.collect();
if vec.is_empty() {
return &mut [];
}
// Move the content to the arena by copying it and then forgetting
// the content of the SmallVec
unsafe {
let len = vec.len();
let start_ptr =
self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T;
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
}
})
}
(_, _) => outline(move || self.try_alloc_from_iter(iter.map(Ok::<T, !>)).into_ok()),
}
}

#[inline]
pub fn try_alloc_from_iter<T, E>(
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we
// cannot know the minimum length of the iterator in this case.
assert!(size_of::<T>() != 0);

// Takes care of reentrancy.
let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
let mut vec = vec?;
if vec.is_empty() {
return Ok(&mut []);
}
// Move the content to the arena by copying and then forgetting it.
let len = vec.len();
Ok(unsafe {
let start_ptr = self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T;
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
})
}
}

/// Declare an `Arena` containing one dropless arena and many typed arenas (the
Expand Down
12 changes: 2 additions & 10 deletions compiler/rustc_mir_transform/src/jump_threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,8 @@ impl<'a> ConditionSet<'a> {
arena: &'a DroplessArena,
f: impl Fn(Condition) -> Option<Condition>,
) -> Option<ConditionSet<'a>> {
let mut all_ok = true;
let set = arena.alloc_from_iter(self.iter().map_while(|c| {
if let Some(c) = f(c) {
Some(c)
} else {
all_ok = false;
None
}
}));
all_ok.then_some(ConditionSet(set))
let set = arena.try_alloc_from_iter(self.iter().map(|c| f(c).ok_or(()))).ok()?;
Some(ConditionSet(set))
}
}

Expand Down
Loading