Skip to content

Commit 4502f0e

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 911ed33 commit 4502f0e

File tree

1 file changed

+44
-21
lines changed

1 file changed

+44
-21
lines changed

src/libarena/lib.rs

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

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

108109
/// Allocates a new Arena with `initial_size` bytes preallocated.
109-
pub fn new_with_size(initial_size: uint) -> Arena {
110+
pub fn new_with_size(initial_size: uint) -> Arena<'a> {
110111
Arena {
111112
head: RefCell::new(chunk(initial_size, false)),
112113
copy_head: RefCell::new(chunk(initial_size, true)),
113114
chunks: RefCell::new(Vec::new()),
115+
_invariant: marker::InvariantLifetime,
114116
}
115117
}
116118
}
@@ -124,7 +126,7 @@ fn chunk(size: uint, is_copy: bool) -> Chunk {
124126
}
125127

126128
#[unsafe_destructor]
127-
impl Drop for Arena {
129+
impl<'longer_than_self> Drop for Arena<'longer_than_self> {
128130
fn drop(&mut self) {
129131
unsafe {
130132
destroy_chunk(&*self.head.borrow());
@@ -182,7 +184,7 @@ fn un_bitpack_tydesc_ptr(p: uint) -> (*const TyDesc, bool) {
182184
((p & !1) as *const TyDesc, p & 1 == 1)
183185
}
184186

185-
impl Arena {
187+
impl<'longer_than_self> Arena<'longer_than_self> {
186188
fn chunk_size(&self) -> uint {
187189
self.copy_head.borrow().capacity()
188190
}
@@ -295,7 +297,7 @@ impl Arena {
295297
/// Allocates a new item in the arena, using `op` to initialize the value,
296298
/// and returns a reference to it.
297299
#[inline]
298-
pub fn alloc<T, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
300+
pub fn alloc<T:'longer_than_self, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
299301
unsafe {
300302
if intrinsics::needs_drop::<T>() {
301303
self.alloc_noncopy(op)
@@ -319,20 +321,6 @@ fn test_arena_destructors() {
319321
}
320322
}
321323

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

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

0 commit comments

Comments
 (0)