Skip to content

Commit 2c9d81b

Browse files
committed
Added lifetime param to Arena.
It (1.) is invariant, (2.) must strictly outlive the arena itself, (3.) constrains the inputs to the arena so that their borrows must also strictly outlive the arena itself. This implies that, for now, one can no longer have cross-references between data allocated via the same `Arena` (even when the data is not subject to the Drop Check rule). Instead one must carry multiple `Arena` instances, or (more commonly), use one or more `TypedArena` instances with enums encoding the different variants of allocated data.
1 parent c1cda07 commit 2c9d81b

File tree

1 file changed

+44
-21
lines changed

1 file changed

+44
-21
lines changed

src/libarena/lib.rs

+44-21
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,29 @@ impl Chunk {
8989
/// than objects without destructors. This reduces overhead when initializing
9090
/// plain-old-data (`Copy` types) and means we don't need to waste time running
9191
/// their destructors.
92-
pub struct Arena {
92+
pub struct Arena<'longer_than_self> {
9393
// The head is separated out from the list as a unbenchmarked
9494
// microoptimization, to avoid needing to case on the list to access the
9595
// head.
9696
head: RefCell<Chunk>,
9797
copy_head: RefCell<Chunk>,
9898
chunks: RefCell<Vec<Chunk>>,
99+
_invariant: marker::InvariantLifetime<'longer_than_self>,
99100
}
100101

101-
impl Arena {
102+
impl<'a> Arena<'a> {
102103
/// Allocates a new Arena with 32 bytes preallocated.
103-
pub fn new() -> Arena {
104+
pub fn new() -> Arena<'a> {
104105
Arena::new_with_size(32)
105106
}
106107

107108
/// Allocates a new Arena with `initial_size` bytes preallocated.
108-
pub fn new_with_size(initial_size: usize) -> Arena {
109+
pub fn new_with_size(initial_size: usize) -> Arena<'a> {
109110
Arena {
110111
head: RefCell::new(chunk(initial_size, false)),
111112
copy_head: RefCell::new(chunk(initial_size, true)),
112113
chunks: RefCell::new(Vec::new()),
114+
_invariant: marker::InvariantLifetime,
113115
}
114116
}
115117
}
@@ -123,7 +125,7 @@ fn chunk(size: usize, is_copy: bool) -> Chunk {
123125
}
124126

125127
#[unsafe_destructor]
126-
impl Drop for Arena {
128+
impl<'longer_than_self> Drop for Arena<'longer_than_self> {
127129
fn drop(&mut self) {
128130
unsafe {
129131
destroy_chunk(&*self.head.borrow());
@@ -181,7 +183,7 @@ fn un_bitpack_tydesc_ptr(p: usize) -> (*const TyDesc, bool) {
181183
((p & !1) as *const TyDesc, p & 1 == 1)
182184
}
183185

184-
impl Arena {
186+
impl<'longer_than_self> Arena<'longer_than_self> {
185187
fn chunk_size(&self) -> usize {
186188
self.copy_head.borrow().capacity()
187189
}
@@ -294,7 +296,7 @@ impl Arena {
294296
/// Allocates a new item in the arena, using `op` to initialize the value,
295297
/// and returns a reference to it.
296298
#[inline]
297-
pub fn alloc<T, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
299+
pub fn alloc<T:'longer_than_self, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
298300
unsafe {
299301
if intrinsics::needs_drop::<T>() {
300302
self.alloc_noncopy(op)
@@ -318,20 +320,6 @@ fn test_arena_destructors() {
318320
}
319321
}
320322

321-
#[test]
322-
fn test_arena_alloc_nested() {
323-
struct Inner { value: usize }
324-
struct Outer<'a> { inner: &'a Inner }
325-
326-
let arena = Arena::new();
327-
328-
let result = arena.alloc(|| Outer {
329-
inner: arena.alloc(|| Inner { value: 10 })
330-
});
331-
332-
assert_eq!(result.inner.value, 10);
333-
}
334-
335323
#[test]
336324
#[should_fail]
337325
fn test_arena_destructors_fail() {
@@ -529,6 +517,41 @@ mod tests {
529517
z: i32,
530518
}
531519

520+
#[test]
521+
fn test_arena_alloc_nested() {
522+
struct Inner { value: u8 }
523+
struct Outer<'a> { inner: &'a Inner }
524+
enum EI<'e> { I(Inner), O(Outer<'e>) }
525+
526+
struct Wrap<'a>(TypedArena<EI<'a>>);
527+
528+
impl<'a> Wrap<'a> {
529+
fn alloc_inner<F:Fn() -> Inner>(&self, f: F) -> &Inner {
530+
let r: &EI = self.0.alloc(EI::I(f()));
531+
if let &EI::I(ref i) = r {
532+
i
533+
} else {
534+
panic!("mismatch");
535+
}
536+
}
537+
fn alloc_outer<F:Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
538+
let r: &EI = self.0.alloc(EI::O(f()));
539+
if let &EI::O(ref o) = r {
540+
o
541+
} else {
542+
panic!("mismatch");
543+
}
544+
}
545+
}
546+
547+
let arena = Wrap(TypedArena::new());
548+
549+
let result = arena.alloc_outer(|| Outer {
550+
inner: arena.alloc_inner(|| Inner { value: 10 }) });
551+
552+
assert_eq!(result.inner.value, 10);
553+
}
554+
532555
#[test]
533556
pub fn test_copy() {
534557
let arena = TypedArena::new();

0 commit comments

Comments
 (0)