Skip to content

Commit 2226c09

Browse files
committed
Auto merge of #59536 - Zoxc:the-arena, r=eddyb
Introduce an arena type which may be used to allocate a list of types with destructors You can also specify that you want deserializers for `&'tcx [T]` and `&'tcx T` for a type in the list, which will allocate those using the arena. Based on #59517 and #59533. Look at the last commit for the interesting changes. An alternative to #56448. cc @michaelwoerister @eddyb r? @oli-obk
2 parents 0369e6c + 223f1c7 commit 2226c09

File tree

12 files changed

+288
-25
lines changed

12 files changed

+288
-25
lines changed

src/librustc/arena.rs

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
use arena::{TypedArena, DroplessArena};
2+
use std::mem;
3+
use std::ptr;
4+
use std::slice;
5+
use std::cell::RefCell;
6+
use std::marker::PhantomData;
7+
use smallvec::SmallVec;
8+
9+
#[macro_export]
10+
macro_rules! arena_types {
11+
($macro:path, $args:tt, $tcx:lifetime) => (
12+
$macro!($args, [
13+
[] vtable_method: Option<(
14+
rustc::hir::def_id::DefId,
15+
rustc::ty::subst::SubstsRef<$tcx>
16+
)>,
17+
[few] mir_keys: rustc::util::nodemap::DefIdSet,
18+
[decode] specialization_graph: rustc::traits::specialization_graph::Graph,
19+
], $tcx);
20+
)
21+
}
22+
23+
macro_rules! arena_for_type {
24+
([][$ty:ty]) => {
25+
TypedArena<$ty>
26+
};
27+
([few $(, $attrs:ident)*][$ty:ty]) => {
28+
PhantomData<$ty>
29+
};
30+
([$ignore:ident $(, $attrs:ident)*]$args:tt) => {
31+
arena_for_type!([$($attrs),*]$args)
32+
};
33+
}
34+
35+
macro_rules! declare_arena {
36+
([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => {
37+
#[derive(Default)]
38+
pub struct Arena<$tcx> {
39+
dropless: DroplessArena,
40+
drop: DropArena,
41+
$($name: arena_for_type!($a[$ty]),)*
42+
}
43+
}
44+
}
45+
46+
macro_rules! which_arena_for_type {
47+
([][$arena:expr]) => {
48+
Some($arena)
49+
};
50+
([few$(, $attrs:ident)*][$arena:expr]) => {
51+
None
52+
};
53+
([$ignore:ident$(, $attrs:ident)*]$args:tt) => {
54+
which_arena_for_type!([$($attrs),*]$args)
55+
};
56+
}
57+
58+
macro_rules! impl_arena_allocatable {
59+
([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => {
60+
$(
61+
impl ArenaAllocatable for $ty {}
62+
unsafe impl<$tcx> ArenaField<$tcx> for $ty {
63+
#[inline]
64+
fn arena<'a>(_arena: &'a Arena<$tcx>) -> Option<&'a TypedArena<Self>> {
65+
which_arena_for_type!($a[&_arena.$name])
66+
}
67+
}
68+
)*
69+
}
70+
}
71+
72+
arena_types!(declare_arena, [], 'tcx);
73+
74+
arena_types!(impl_arena_allocatable, [], 'tcx);
75+
76+
pub trait ArenaAllocatable {}
77+
78+
impl<T: Copy> ArenaAllocatable for T {}
79+
80+
pub unsafe trait ArenaField<'tcx>: Sized {
81+
/// Returns a specific arena to allocate from.
82+
/// If None is returned, the DropArena will be used.
83+
fn arena<'a>(arena: &'a Arena<'tcx>) -> Option<&'a TypedArena<Self>>;
84+
}
85+
86+
unsafe impl<'tcx, T> ArenaField<'tcx> for T {
87+
#[inline]
88+
default fn arena<'a>(_: &'a Arena<'tcx>) -> Option<&'a TypedArena<Self>> {
89+
panic!()
90+
}
91+
}
92+
93+
impl<'tcx> Arena<'tcx> {
94+
#[inline]
95+
pub fn alloc<T: ArenaAllocatable>(&self, value: T) -> &mut T {
96+
if !mem::needs_drop::<T>() {
97+
return self.dropless.alloc(value);
98+
}
99+
match <T as ArenaField<'tcx>>::arena(self) {
100+
Some(arena) => arena.alloc(value),
101+
None => unsafe { self.drop.alloc(value) },
102+
}
103+
}
104+
105+
pub fn alloc_from_iter<
106+
T: ArenaAllocatable,
107+
I: IntoIterator<Item = T>
108+
>(
109+
&'a self,
110+
iter: I
111+
) -> &'a mut [T] {
112+
if !mem::needs_drop::<T>() {
113+
return self.dropless.alloc_from_iter(iter);
114+
}
115+
match <T as ArenaField<'tcx>>::arena(self) {
116+
Some(arena) => arena.alloc_from_iter(iter),
117+
None => unsafe { self.drop.alloc_from_iter(iter) },
118+
}
119+
}
120+
}
121+
122+
/// Calls the destructor for an object when dropped.
123+
struct DropType {
124+
drop_fn: unsafe fn(*mut u8),
125+
obj: *mut u8,
126+
}
127+
128+
unsafe fn drop_for_type<T>(to_drop: *mut u8) {
129+
std::ptr::drop_in_place(to_drop as *mut T)
130+
}
131+
132+
impl Drop for DropType {
133+
fn drop(&mut self) {
134+
unsafe {
135+
(self.drop_fn)(self.obj)
136+
}
137+
}
138+
}
139+
140+
/// An arena which can be used to allocate any type.
141+
/// Allocating in this arena is unsafe since the type system
142+
/// doesn't know which types it contains. In order to
143+
/// allocate safely, you must store a PhantomData<T>
144+
/// alongside this arena for each type T you allocate.
145+
#[derive(Default)]
146+
struct DropArena {
147+
/// A list of destructors to run when the arena drops.
148+
/// Ordered so `destructors` gets dropped before the arena
149+
/// since its destructor can reference memory in the arena.
150+
destructors: RefCell<Vec<DropType>>,
151+
arena: DroplessArena,
152+
}
153+
154+
impl DropArena {
155+
#[inline]
156+
unsafe fn alloc<T>(&self, object: T) -> &mut T {
157+
let mem = self.arena.alloc_raw(
158+
mem::size_of::<T>(),
159+
mem::align_of::<T>()
160+
) as *mut _ as *mut T;
161+
// Write into uninitialized memory.
162+
ptr::write(mem, object);
163+
let result = &mut *mem;
164+
// Record the destructor after doing the allocation as that may panic
165+
// and would cause `object`'s destuctor to run twice if it was recorded before
166+
self.destructors.borrow_mut().push(DropType {
167+
drop_fn: drop_for_type::<T>,
168+
obj: result as *mut T as *mut u8,
169+
});
170+
result
171+
}
172+
173+
#[inline]
174+
unsafe fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
175+
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
176+
if vec.is_empty() {
177+
return &mut [];
178+
}
179+
let len = vec.len();
180+
181+
let start_ptr = self.arena.alloc_raw(
182+
len.checked_mul(mem::size_of::<T>()).unwrap(),
183+
mem::align_of::<T>()
184+
) as *mut _ as *mut T;
185+
186+
let mut destructors = self.destructors.borrow_mut();
187+
// Reserve space for the destructors so we can't panic while adding them
188+
destructors.reserve(len);
189+
190+
// Move the content to the arena by copying it and then forgetting
191+
// the content of the SmallVec
192+
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
193+
mem::forget(vec.drain());
194+
195+
// Record the destructors after doing the allocation as that may panic
196+
// and would cause `object`'s destuctor to run twice if it was recorded before
197+
for i in 0..len {
198+
destructors.push(DropType {
199+
drop_fn: drop_for_type::<T>,
200+
obj: start_ptr.offset(i as isize) as *mut u8,
201+
});
202+
}
203+
204+
slice::from_raw_parts_mut(start_ptr, len)
205+
}
206+
}

src/librustc/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#![cfg_attr(windows, feature(libc))]
4141
#![feature(never_type)]
4242
#![feature(exhaustive_patterns)]
43+
#![feature(overlapping_marker_traits)]
4344
#![feature(extern_types)]
4445
#![feature(nll)]
4546
#![feature(non_exhaustive)]
@@ -103,6 +104,8 @@ pub mod diagnostics;
103104
#[macro_use]
104105
pub mod query;
105106

107+
#[macro_use]
108+
pub mod arena;
106109
pub mod cfg;
107110
pub mod dep_graph;
108111
pub mod hir;

src/librustc/query/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ rustc_queries! {
8484
/// Set of all the `DefId`s in this crate that have MIR associated with
8585
/// them. This includes all the body owners, but also things like struct
8686
/// constructors.
87-
query mir_keys(_: CrateNum) -> Lrc<DefIdSet> {
87+
query mir_keys(_: CrateNum) -> &'tcx DefIdSet {
8888
desc { "getting a list of all mir_keys" }
8989
}
9090

@@ -515,7 +515,7 @@ rustc_queries! {
515515

516516
Other {
517517
query vtable_methods(key: ty::PolyTraitRef<'tcx>)
518-
-> Lrc<Vec<Option<(DefId, SubstsRef<'tcx>)>>> {
518+
-> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] {
519519
no_force
520520
desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) }
521521
}
@@ -538,8 +538,7 @@ rustc_queries! {
538538
query trait_impls_of(key: DefId) -> Lrc<ty::trait_def::TraitImpls> {
539539
desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) }
540540
}
541-
query specialization_graph_of(_: DefId)
542-
-> Lrc<specialization_graph::Graph> {}
541+
query specialization_graph_of(_: DefId) -> &'tcx specialization_graph::Graph {}
543542
query is_object_safe(key: DefId) -> bool {
544543
desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) }
545544
}

src/librustc/traits/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use crate::infer::{InferCtxt, SuppressRegionErrors};
2626
use crate::infer::outlives::env::OutlivesEnvironment;
2727
use crate::middle::region;
2828
use crate::mir::interpret::ErrorHandled;
29-
use rustc_data_structures::sync::Lrc;
3029
use rustc_macros::HashStable;
3130
use syntax::ast;
3231
use syntax_pos::{Span, DUMMY_SP};
@@ -984,11 +983,11 @@ fn substitute_normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
984983
fn vtable_methods<'a, 'tcx>(
985984
tcx: TyCtxt<'a, 'tcx, 'tcx>,
986985
trait_ref: ty::PolyTraitRef<'tcx>)
987-
-> Lrc<Vec<Option<(DefId, SubstsRef<'tcx>)>>>
986+
-> &'tcx [Option<(DefId, SubstsRef<'tcx>)>]
988987
{
989988
debug!("vtable_methods({:?})", trait_ref);
990989

991-
Lrc::new(
990+
tcx.arena.alloc_from_iter(
992991
supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
993992
let trait_methods = tcx.associated_items(trait_ref.def_id())
994993
.filter(|item| item.kind == ty::AssociatedKind::Method);
@@ -1039,7 +1038,7 @@ fn vtable_methods<'a, 'tcx>(
10391038

10401039
Some((def_id, substs))
10411040
})
1042-
}).collect()
1041+
})
10431042
)
10441043
}
10451044

src/librustc/traits/specialize/mod.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use crate::infer::{InferCtxt, InferOk};
1616
use crate::lint;
1717
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
1818
use rustc_data_structures::fx::FxHashSet;
19-
use rustc_data_structures::sync::Lrc;
2019
use syntax_pos::DUMMY_SP;
2120
use crate::traits::select::IntercrateAmbiguityCause;
2221
use crate::ty::{self, TyCtxt, TypeFoldable};
@@ -289,7 +288,7 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
289288
pub(super) fn specialization_graph_provider<'a, 'tcx>(
290289
tcx: TyCtxt<'a, 'tcx, 'tcx>,
291290
trait_id: DefId,
292-
) -> Lrc<specialization_graph::Graph> {
291+
) -> &'tcx specialization_graph::Graph {
293292
let mut sg = specialization_graph::Graph::new();
294293

295294
let mut trait_impls = tcx.all_impls(trait_id);
@@ -383,7 +382,7 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(
383382
}
384383
}
385384

386-
Lrc::new(sg)
385+
tcx.arena.alloc(sg)
387386
}
388387

389388
/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a

src/librustc/traits/specialize/specialization_graph.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
77
use crate::traits;
88
use crate::ty::{self, TyCtxt, TypeFoldable};
99
use crate::ty::fast_reject::{self, SimplifiedType};
10-
use rustc_data_structures::sync::Lrc;
1110
use syntax::ast::Ident;
1211
use crate::util::captures::Captures;
1312
use crate::util::nodemap::{DefIdMap, FxHashMap};
@@ -439,13 +438,13 @@ impl<'a, 'gcx, 'tcx> Node {
439438
}
440439
}
441440

442-
pub struct Ancestors {
441+
pub struct Ancestors<'tcx> {
443442
trait_def_id: DefId,
444-
specialization_graph: Lrc<Graph>,
443+
specialization_graph: &'tcx Graph,
445444
current_source: Option<Node>,
446445
}
447446

448-
impl Iterator for Ancestors {
447+
impl Iterator for Ancestors<'_> {
449448
type Item = Node;
450449
fn next(&mut self) -> Option<Node> {
451450
let cur = self.current_source.take();
@@ -476,7 +475,7 @@ impl<T> NodeItem<T> {
476475
}
477476
}
478477

479-
impl<'a, 'gcx, 'tcx> Ancestors {
478+
impl<'a, 'gcx, 'tcx> Ancestors<'gcx> {
480479
/// Search the items from the given ancestors, returning each definition
481480
/// with the given name and the given kind.
482481
// FIXME(#35870): avoid closures being unexported due to `impl Trait`.
@@ -509,10 +508,10 @@ impl<'a, 'gcx, 'tcx> Ancestors {
509508

510509
/// Walk up the specialization ancestors of a given impl, starting with that
511510
/// impl itself.
512-
pub fn ancestors(tcx: TyCtxt<'_, '_, '_>,
511+
pub fn ancestors(tcx: TyCtxt<'_, 'tcx, '_>,
513512
trait_def_id: DefId,
514513
start_from_impl: DefId)
515-
-> Ancestors {
514+
-> Ancestors<'tcx> {
516515
let specialization_graph = tcx.specialization_graph_of(trait_def_id);
517516
Ancestors {
518517
trait_def_id,

0 commit comments

Comments
 (0)