1//! The arena, a fast but limited type of allocator.
2//!
3//! Arenas are a type of allocator that destroy the objects within, all at
4//! once, once the arena itself is destroyed. They do not support deallocation
5//! of individual objects while the arena itself is still alive. The benefit
6//! of an arena is very fast allocation; just a pointer bump.
7//!
8//! This crate implements several kinds of arena.
910// tidy-alphabetical-start
11#![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine.
12#![allow(internal_features)]
13#![cfg_attr(bootstrap, feature(never_type))]
14#![cfg_attr(test, feature(test))]
15#![deny(unsafe_op_in_unsafe_fn)]
16#![doc(test(no_crate_inject, attr(deny(warnings))))]
17#![feature(decl_macro)]
18#![feature(dropck_eyepatch)]
19#![feature(rustc_attrs)]
20#![feature(unwrap_infallible)]
21// tidy-alphabetical-end
2223use std::alloc::Layout;
24use std::cell::{Cell, RefCell};
25use std::marker::PhantomData;
26use std::mem::{self, MaybeUninit};
27use std::ptr::{self, NonNull};
28use std::{cmp, hint, slice};
2930use smallvec::SmallVec;
3132/// This calls the passed function while ensuring it won't be inlined into the caller.
33#[inline(never)]
34#[cold]
35fn outline<F: FnOnce() -> R, R>(f: F) -> R {
36f()
37}
3839struct ArenaChunk<T = u8> {
40/// The raw storage for the arena chunk.
41storage: NonNull<[MaybeUninit<T>]>,
42/// The number of valid entries in the chunk.
43entries: usize,
44}
4546unsafe impl<#[may_dangle] T> Dropfor ArenaChunk<T> {
47fn drop(&mut self) {
48unsafe { drop(Box::from_raw(self.storage.as_mut())) }
49 }
50}
5152impl<T> ArenaChunk<T> {
53#[inline]
54unsafe fn new(capacity: usize) -> ArenaChunk<T> {
55ArenaChunk { storage: Box::into_non_null(Box::new_uninit_slice(capacity)), entries: 0 }
56 }
5758/// Destroys this arena chunk.
59 ///
60 /// # Safety
61 ///
62 /// The caller must ensure that `len` elements of this chunk have been initialized.
63#[inline]
64unsafe fn destroy(&mut self, len: usize) {
65// The branch on needs_drop() is an -O1 performance optimization.
66 // Without the branch, dropping TypedArena<T> takes linear time.
67if mem::needs_drop::<T>() {
68// SAFETY: The caller must ensure that `len` elements of this chunk have
69 // been initialized.
70unsafe {
71let slice = self.storage.as_mut();
72slice[..len].assume_init_drop();
73 }
74 }
75 }
7677// Returns a pointer to the first allocated object.
78#[inline]
79fn start(&mut self) -> *mut T {
80self.storage.as_ptr() as *mut T
81 }
8283// Returns a pointer to the end of the allocated space.
84#[inline]
85fn end(&mut self) -> *mut T {
86unsafe {
87if size_of::<T>() == 0 {
88// A pointer as large as possible for zero-sized elements.
89ptr::without_provenance_mut(!0)
90 } else {
91self.start().add(self.storage.len())
92 }
93 }
94 }
95}
9697// The arenas start with PAGE-sized chunks, and then each new chunk is twice as
98// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon
99// we stop growing. This scales well, from arenas that are barely used up to
100// arenas that are used for 100s of MiBs. Note also that the chosen sizes match
101// the usual sizes of pages and huge pages on Linux.
102const PAGE: usize = 4096;
103const HUGE_PAGE: usize = 2 * 1024 * 1024;
104105/// An arena that can hold objects of only one type.
106pub struct TypedArena<T> {
107/// A pointer to the next object to be allocated.
108ptr: Cell<*mut T>,
109110/// A pointer to the end of the allocated area. When this pointer is
111 /// reached, a new chunk is allocated.
112end: Cell<*mut T>,
113114/// A vector of arena chunks.
115chunks: RefCell<Vec<ArenaChunk<T>>>,
116117/// Marker indicating that dropping the arena causes its owned
118 /// instances of `T` to be dropped.
119_own: PhantomData<T>,
120}
121122impl<T> Defaultfor TypedArena<T> {
123/// Creates a new `TypedArena`.
124fn default() -> TypedArena<T> {
125TypedArena {
126// We set both `ptr` and `end` to 0 so that the first call to
127 // alloc() will trigger a grow().
128ptr: Cell::new(ptr::null_mut()),
129 end: Cell::new(ptr::null_mut()),
130 chunks: Default::default(),
131 _own: PhantomData,
132 }
133 }
134}
135136impl<T> TypedArena<T> {
137/// Allocates an object in the `TypedArena`, returning a reference to it.
138#[inline]
139pub fn alloc(&self, object: T) -> &mut T {
140if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
141142if self.ptr == self.end {
143self.grow(1)
144 }
145146unsafe {
147let ptr = self.ptr.get();
148// Advance the pointer.
149self.ptr.set(self.ptr.get().add(1));
150// Write into uninitialized memory.
151ptr::write(ptr, object);
152&mut *ptr153 }
154 }
155156#[inline]
157fn can_allocate(&self, additional: usize) -> bool {
158// FIXME: this should *likely* use `offset_from`, but more
159 // investigation is needed (including running tests in miri).
160let available_bytes = self.end.get().addr() - self.ptr.get().addr();
161let additional_bytes = additional.checked_mul(size_of::<T>()).unwrap();
162available_bytes >= additional_bytes163 }
164165/// Allocates storage for `len >= 1` values in this arena, and returns a
166 /// raw pointer to the first value's storage.
167 ///
168 /// # Safety
169 ///
170 /// Caller must initialize each of the `len` slots to a droppable value
171 /// before the arena is dropped.
172 ///
173 /// In practice, this typically means that the caller must be able to
174 /// raw-copy `len` already-initialized values into the slice without any
175 /// possibility of panicking.
176 ///
177 /// FIXME(Zalathar): This is *very* fragile; perhaps we need a different
178 /// approach to arena-allocating slices of droppable values.
179#[inline]
180unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
181if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
182if !(len != 0) { ::core::panicking::panic("assertion failed: len != 0") };assert!(len != 0);
183184// Ensure the current chunk can fit `len` objects.
185if !self.can_allocate(len) {
186self.grow(len);
187if true {
if !self.can_allocate(len) {
::core::panicking::panic("assertion failed: self.can_allocate(len)")
};
};debug_assert!(self.can_allocate(len));
188 }
189190let start_ptr = self.ptr.get();
191// SAFETY: `can_allocate`/`grow` ensures that there is enough space for
192 // `len` elements.
193unsafe { self.ptr.set(start_ptr.add(len)) };
194start_ptr195 }
196197/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
198 ///
199 /// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
200 /// storing the elements in the arena.
201#[inline]
202pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
203self.try_alloc_from_iter(iter.into_iter().map(Ok::<T, !>)).into_ok()
204 }
205206/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
207 ///
208 /// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
209 /// storing the elements in the arena.
210#[inline]
211pub fn try_alloc_from_iter<E>(
212&self,
213 iter: impl IntoIterator<Item = Result<T, E>>,
214 ) -> Result<&mut [T], E> {
215// Despite the similarity with `DroplessArena`, we cannot reuse their fast case. The reason
216 // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
217 // reference to `self` and adding elements to the arena during iteration.
218 //
219 // For this reason, if we pre-allocated any space for the elements of this iterator, we'd
220 // have to track that some uninitialized elements are followed by some initialized elements,
221 // else we might accidentally drop uninitialized memory if something panics or if the
222 // iterator doesn't fill all the length we expected.
223 //
224 // So we collect all the elements beforehand, which takes care of reentrancy and panic
225 // safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
226 // doesn't need to be hyper-optimized.
227if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
228229let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
230let mut vec = vec?;
231if vec.is_empty() {
232return Ok(&mut []);
233 }
234// Move the content to the arena by copying and then forgetting it.
235let len = vec.len();
236237// SAFETY: After allocating raw storage for exactly `len` values, we
238 // must fully initialize the storage without panicking, and we must
239 // also prevent the stale values in the vec from being dropped.
240Ok(unsafe {
241let start_ptr = self.alloc_raw_slice(len);
242// Initialize the newly-allocated storage without panicking.
243vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
244// Prevent the stale values in the vec from being dropped.
245vec.set_len(0);
246 slice::from_raw_parts_mut(start_ptr, len)
247 })
248 }
249250/// Grows the arena.
251#[inline(never)]
252 #[cold]
253fn grow(&self, additional: usize) {
254unsafe {
255// We need the element size to convert chunk sizes (ranging from
256 // PAGE to HUGE_PAGE bytes) to element counts.
257let elem_size = cmp::max(1, size_of::<T>());
258let mut chunks = self.chunks.borrow_mut();
259let mut new_cap;
260if let Some(last_chunk) = chunks.last_mut() {
261// If a type is `!needs_drop`, we don't need to keep track of how many elements
262 // the chunk stores - the field will be ignored anyway.
263if mem::needs_drop::<T>() {
264// FIXME: this should *likely* use `offset_from`, but more
265 // investigation is needed (including running tests in miri).
266let used_bytes = self.ptr.get().addr() - last_chunk.start().addr();
267last_chunk.entries = used_bytes / size_of::<T>();
268 }
269270// If the previous chunk's len is less than HUGE_PAGE
271 // bytes, then this chunk will be least double the previous
272 // chunk's size.
273new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
274new_cap*= 2;
275 } else {
276new_cap = PAGE / elem_size;
277 }
278// Also ensure that this chunk can fit `additional`.
279new_cap = cmp::max(additional, new_cap);
280281let chunk = chunks.push_mut(ArenaChunk::<T>::new(new_cap));
282self.ptr.set(chunk.start());
283self.end.set(chunk.end());
284 }
285 }
286287// Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
288 // chunks.
289fn clear_last_chunk(&self, last_chunk: &mut ArenaChunk<T>) {
290// Determine how much was filled.
291let start = last_chunk.start().addr();
292// We obtain the value of the pointer to the first uninitialized element.
293let end = self.ptr.get().addr();
294// We then calculate the number of elements to be dropped in the last chunk,
295 // which is the filled area's length.
296{
match (&size_of::<T>(), &0) {
(left_val, right_val) => {
if *left_val == *right_val {
let kind = ::core::panicking::AssertKind::Ne;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_ne!(size_of::<T>(), 0);
297// FIXME: this should *likely* use `offset_from`, but more
298 // investigation is needed (including running tests in miri).
299let diff = (end - start) / size_of::<T>();
300// Pass that to the `destroy` method.
301unsafe {
302last_chunk.destroy(diff);
303 }
304// Reset the chunk.
305self.ptr.set(last_chunk.start());
306 }
307}
308309unsafe impl<#[may_dangle] T> Dropfor TypedArena<T> {
310fn drop(&mut self) {
311unsafe {
312// Determine how much was filled.
313let mut chunks_borrow = self.chunks.borrow_mut();
314if let Some(mut last_chunk) = chunks_borrow.pop() {
315// Drop the contents of the last chunk.
316self.clear_last_chunk(&mut last_chunk);
317// The last chunk will be dropped. Destroy all other chunks.
318for chunk in chunks_borrow.iter_mut() {
319 chunk.destroy(chunk.entries);
320 }
321 }
322// Box handles deallocation of `last_chunk` and `self.chunks`.
323}
324 }
325}
326327unsafe impl<T: Send> Sendfor TypedArena<T> {}
328329#[inline(always)]
330fn align_down(val: usize, align: usize) -> usize {
331if true {
if !align.is_power_of_two() {
::core::panicking::panic("assertion failed: align.is_power_of_two()")
};
};debug_assert!(align.is_power_of_two());
332val & !(align - 1)
333}
334335#[inline(always)]
336fn align_up(val: usize, align: usize) -> usize {
337if true {
if !align.is_power_of_two() {
::core::panicking::panic("assertion failed: align.is_power_of_two()")
};
};debug_assert!(align.is_power_of_two());
338 (val + align - 1) & !(align - 1)
339}
340341// Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
342// to optimize away alignment code.
343const DROPLESS_ALIGNMENT: usize = align_of::<usize>();
344345/// An arena that can hold objects of multiple different types that impl `Copy`
346/// and/or satisfy `!mem::needs_drop`.
347pub struct DroplessArena {
348/// A pointer to the start of the free space.
349start: Cell<*mut u8>,
350351/// A pointer to the end of free space.
352 ///
353 /// The allocation proceeds downwards from the end of the chunk towards the
354 /// start. (This is slightly simpler and faster than allocating upwards,
355 /// see <https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html>.)
356 /// When this pointer crosses the start pointer, a new chunk is allocated.
357 ///
358 /// This is kept aligned to DROPLESS_ALIGNMENT.
359end: Cell<*mut u8>,
360361/// A vector of arena chunks.
362chunks: RefCell<Vec<ArenaChunk>>,
363}
364365unsafe impl Sendfor DroplessArena {}
366367impl Defaultfor DroplessArena {
368#[inline]
369fn default() -> DroplessArena {
370DroplessArena {
371// We set both `start` and `end` to 0 so that the first call to
372 // alloc() will trigger a grow().
373start: Cell::new(ptr::null_mut()),
374 end: Cell::new(ptr::null_mut()),
375 chunks: Default::default(),
376 }
377 }
378}
379380impl DroplessArena {
381#[inline(never)]
382 #[cold]
383fn grow(&self, layout: Layout) {
384// Add some padding so we can align `self.end` while
385 // still fitting in a `layout` allocation.
386let additional = layout.size() + cmp::max(DROPLESS_ALIGNMENT, layout.align()) - 1;
387388unsafe {
389let mut chunks = self.chunks.borrow_mut();
390let mut new_cap;
391if let Some(last_chunk) = chunks.last_mut() {
392// There is no need to update `last_chunk.entries` because that
393 // field isn't used by `DroplessArena`.
394395 // If the previous chunk's len is less than HUGE_PAGE
396 // bytes, then this chunk will be least double the previous
397 // chunk's size.
398new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
399new_cap*= 2;
400 } else {
401new_cap = PAGE;
402 }
403// Also ensure that this chunk can fit `additional`.
404new_cap = cmp::max(additional, new_cap);
405406let chunk = chunks.push_mut(ArenaChunk::new(align_up(new_cap, PAGE)));
407self.start.set(chunk.start());
408409// Align the end to DROPLESS_ALIGNMENT.
410let end = align_down(chunk.end().addr(), DROPLESS_ALIGNMENT);
411412// Make sure we don't go past `start`. This should not happen since the allocation
413 // should be at least DROPLESS_ALIGNMENT - 1 bytes.
414if true {
if !(chunk.start().addr() <= end) {
::core::panicking::panic("assertion failed: chunk.start().addr() <= end")
};
};debug_assert!(chunk.start().addr() <= end);
415416self.end.set(chunk.end().with_addr(end));
417 }
418 }
419420#[inline]
421pub fn alloc_raw(&self, layout: Layout) -> *mut u8 {
422if !(layout.size() != 0) {
::core::panicking::panic("assertion failed: layout.size() != 0")
};assert!(layout.size() != 0);
423424// This loop executes once or twice: if allocation fails the first
425 // time, the `grow` ensures it will succeed the second time.
426loop {
427let start = self.start.get().addr();
428let old_end = self.end.get();
429let end = old_end.addr();
430431// Align allocated bytes so that `self.end` stays aligned to
432 // DROPLESS_ALIGNMENT.
433let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
434435// Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT.
436unsafe { hint::assert_unchecked(end == align_down(end, DROPLESS_ALIGNMENT)) };
437438if let Some(sub) = end.checked_sub(bytes) {
439let new_end = align_down(sub, layout.align());
440if start <= new_end {
441let new_end = old_end.with_addr(new_end);
442// `new_end` is aligned to DROPLESS_ALIGNMENT as `align_down`
443 // preserves alignment as both `end` and `bytes` are already
444 // aligned to DROPLESS_ALIGNMENT.
445self.end.set(new_end);
446return new_end;
447 }
448 }
449450// No free space left. Allocate a new chunk to satisfy the request.
451 // On failure the grow will panic or abort.
452self.grow(layout);
453 }
454 }
455456#[inline]
457pub fn alloc<T>(&self, object: T) -> &mut T {
458if !!mem::needs_drop::<T>() {
::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
459if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
460461let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;
462463unsafe {
464// Write into uninitialized memory.
465ptr::write(mem, object);
466&mut *mem467 }
468 }
469470/// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable
471 /// reference to it. Will panic if passed a zero-sized type.
472 ///
473 /// Panics:
474 ///
475 /// - Zero-sized types
476 /// - Zero-length slices
477#[inline]
478pub fn alloc_slice<T>(&self, slice: &[T]) -> &mut [T]
479where
480T: Copy,
481 {
482if !!mem::needs_drop::<T>() {
::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
483if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
484if !!slice.is_empty() {
::core::panicking::panic("assertion failed: !slice.is_empty()")
};assert!(!slice.is_empty());
485486let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
487488unsafe {
489mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len());
490 slice::from_raw_parts_mut(mem, slice.len())
491 }
492 }
493494/// Allocates a string slice that is copied into the `DroplessArena`, returning a
495 /// reference to it. Will panic if passed an empty string.
496 ///
497 /// Panics:
498 ///
499 /// - Zero-length string
500#[inline]
501pub fn alloc_str(&self, string: &str) -> &str {
502let slice = self.alloc_slice(string.as_bytes());
503504// SAFETY: the result has a copy of the same valid UTF-8 bytes.
505unsafe { std::str::from_utf8_unchecked(slice) }
506 }
507508/// # Safety
509 ///
510 /// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that
511 /// that memory stays allocated and not shared for the lifetime of `self`. This must hold even
512 /// if `iter.next()` allocates onto `self`.
513#[inline]
514unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
515&self,
516mut iter: I,
517 len: usize,
518 mem: *mut T,
519 ) -> &mut [T] {
520let mut i = 0;
521// Use a manual loop since LLVM manages to optimize it better for
522 // slice iterators
523loop {
524// SAFETY: The caller must ensure that `mem` is valid for writes up to
525 // `size_of::<T>() * len`.
526unsafe {
527match iter.next() {
528Some(value) if i < len => mem.add(i).write(value),
529Some(_) | None => {
530// We only return as many items as the iterator gave us, even
531 // though it was supposed to give us `len`
532return slice::from_raw_parts_mut(mem, i);
533 }
534 }
535 }
536i += 1;
537 }
538 }
539540#[inline]
541pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
542if !!mem::needs_drop::<T>() {
::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
543if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
544545// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
546 // allocate additional elements while we're iterating.
547let iter = iter.into_iter();
548549let size_hint = iter.size_hint();
550551match size_hint {
552 (min, Some(max)) if min == max => {
553// We know the exact number of elements the iterator expects to produce here.
554let len = min;
555556if len == 0 {
557return &mut [];
558 }
559560let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T;
561// SAFETY: `write_from_iter` doesn't touch `self`. It only touches the slice we just
562 // reserved. If the iterator panics or doesn't output `len` elements, this will
563 // leave some unallocated slots in the arena, which is fine because we do not call
564 // `drop`.
565unsafe { self.write_from_iter(iter, len, mem) }
566 }
567 (_, _) => outline(move || self.try_alloc_from_iter(iter.map(Ok::<T, !>)).into_ok()),
568 }
569 }
570571#[inline]
572pub fn try_alloc_from_iter<T, E>(
573&self,
574 iter: impl IntoIterator<Item = Result<T, E>>,
575 ) -> Result<&mut [T], E> {
576// Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we
577 // cannot know the minimum length of the iterator in this case.
578if !!mem::needs_drop::<T>() {
::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
579if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
580581// Takes care of reentrancy.
582let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
583let mut vec = vec?;
584if vec.is_empty() {
585return Ok(&mut []);
586 }
587// Move the content to the arena by copying and then forgetting it.
588let len = vec.len();
589Ok(unsafe {
590let start_ptr = self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T;
591vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
592vec.set_len(0);
593 slice::from_raw_parts_mut(start_ptr, len)
594 })
595 }
596}
597598/// Declares an `Arena` that can allocate values of a variety of `Copy`, `needs_drop` and
599/// `!needs_drop` types.
600///
601/// The declared arena actually contains a single [`DroplessArena`], plus a separate
602/// [`TypedArena`] for each of the types listed in the body of the macro invocation.
603///
604/// Any type that is `Copy` can be allocated in the arena without needing to be listed
605/// explicitly. Those values will be stored in the [`DroplessArena`].
606///
607/// Types that are `!Copy` can only be allocated if they are listed in the macro invocation.
608/// For types that are `!Copy + needs_drop`, values will be stored in the corresponding
609/// [`TypedArena`] and will be dropped when the arena is dropped.
610///
611/// As an optimization, types that are `!Copy + !needs_drop` will actually be stored in the
612/// [`DroplessArena`], and the corresponding [`TypedArena`] will remain empty. This makes
613/// better use of the dropless arena's storage blocks, while the overhead of having a few
614/// unused typed-arenas is negligible.
615#[rustc_macro_transparency = "semiopaque"]
616pub macro declare_arena(
617// Each of these entries becomes a `$name: TypedArena<$ty>` field in the arena.
618 // This allows values of non-copy type $ty to be allocated in the arena.
619 // The field names must be distinct, but have no further significance.
620$(
621$name:ident: $ty:ty,
622 )*
623) {
624#[derive(Default)]
625pub struct Arena<'tcx> {
626pub dropless: $crate::DroplessArena,
627 $($name: $crate::TypedArena<$ty>,)*
628 }
629630pub trait ArenaAllocatable<'tcx, C = rustc_arena::IsNotCopy>: Sized {
631#[allow(clippy::mut_from_ref)]
632fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self;
633#[allow(clippy::mut_from_ref)]
634fn allocate_from_iter(
635 arena: &'tcx Arena<'tcx>,
636 iter: impl ::std::iter::IntoIterator<Item = Self>,
637 ) -> &'tcx mut [Self];
638 }
639640// Any type that impls `Copy` can be arena-allocated in the `DroplessArena`.
641impl<'tcx, T: Copy> ArenaAllocatable<'tcx, rustc_arena::IsCopy> for T {
642#[inline]
643 #[allow(clippy::mut_from_ref)]
644fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self {
645 arena.dropless.alloc(self)
646 }
647#[inline]
648 #[allow(clippy::mut_from_ref)]
649fn allocate_from_iter(
650 arena: &'tcx Arena<'tcx>,
651 iter: impl ::std::iter::IntoIterator<Item = Self>,
652 ) -> &'tcx mut [Self] {
653 arena.dropless.alloc_from_iter(iter)
654 }
655 }
656 $(
657impl<'tcx> ArenaAllocatable<'tcx, rustc_arena::IsNotCopy> for $ty {
658#[inline]
659fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self {
660if !::std::mem::needs_drop::<Self>() {
661 arena.dropless.alloc(self)
662 } else {
663 arena.$name.alloc(self)
664 }
665 }
666667#[inline]
668 #[allow(clippy::mut_from_ref)]
669fn allocate_from_iter(
670 arena: &'tcx Arena<'tcx>,
671 iter: impl ::std::iter::IntoIterator<Item = Self>,
672 ) -> &'tcx mut [Self] {
673if !::std::mem::needs_drop::<Self>() {
674 arena.dropless.alloc_from_iter(iter)
675 } else {
676 arena.$name.alloc_from_iter(iter)
677 }
678 }
679 }
680 )*
681682impl<'tcx> Arena<'tcx> {
683#[inline]
684 #[allow(clippy::mut_from_ref)]
685pub fn alloc<T: ArenaAllocatable<'tcx, C>, C>(&'tcx self, value: T) -> &mut T {
686 value.allocate_on(self)
687 }
688689// Any type that impls `Copy` can have slices be arena-allocated in the `DroplessArena`.
690#[inline]
691 #[allow(clippy::mut_from_ref)]
692pub fn alloc_slice<T: ::std::marker::Copy>(&self, value: &[T]) -> &mut [T] {
693if value.is_empty() {
694return &mut [];
695 }
696self.dropless.alloc_slice(value)
697 }
698699#[inline]
700pub fn alloc_str(&self, string: &str) -> &str {
701if string.is_empty() {
702return "";
703 }
704self.dropless.alloc_str(string)
705 }
706707#[inline]
708pub fn alloc_os_str(&self, os_str: &::std::ffi::OsStr) -> &::std::ffi::OsStr {
709use ::std::ffi::OsStr;
710if os_str.is_empty() {
711return OsStr::new("");
712 }
713let bytes = self.dropless.alloc_slice(os_str.as_encoded_bytes());
714// SAFETY: These bytes are an exact copy of `os_str.as_encoded_bytes()`.
715unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
716 }
717718#[inline]
719pub fn alloc_path(&self, path: &::std::path::Path) -> &::std::path::Path {
720use ::std::path::Path;
721 Path::new(self.alloc_os_str(path.as_os_str()))
722 }
723724#[allow(clippy::mut_from_ref)]
725pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, C>, C>(
726&'tcx self,
727 iter: impl ::std::iter::IntoIterator<Item = T>,
728 ) -> &mut [T] {
729 T::allocate_from_iter(self, iter)
730 }
731732#[allow(clippy::mut_from_ref)]
733pub fn alloc_index_slice_from_iter<I, T, C>(
734&'tcx self,
735 iter: impl ::std::iter::IntoIterator<Item = T>,
736 ) -> &mut ::rustc_index::IndexSlice<I, T>
737where
738I: ::rustc_index::Idx,
739 T: ArenaAllocatable<'tcx, C>,
740 {
741 ::rustc_index::IndexSlice::from_raw_mut(self.alloc_from_iter(iter))
742 }
743 }
744}
745746// Marker types that let us give different behaviour for arenas allocating
747// `Copy` types vs `!Copy` types.
748pub struct IsCopy;
749pub struct IsNotCopy;
750751#[cfg(test)]
752mod tests;