use arena::{DroplessArena, TypedArena}; use smallvec::SmallVec; use std::cell::RefCell; use std::marker::PhantomData; use std::mem; use std::ptr; use std::slice; /// This declares a list of types which can be allocated by `Arena`. /// /// The `few` modifier will cause allocation to use the shared arena and recording the destructor. /// This is faster and more memory efficient if there's only a few allocations of the type. /// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is /// faster and more memory efficient if there is lots of allocations. /// /// Specifying the `decode` modifier will add decode impls for &T and &[T] where T is the type /// listed. These impls will appear in the implement_ty_decoder! macro. #[macro_export] macro_rules! arena_types { ($macro:path, $args:tt, $tcx:lifetime) => ( $macro!($args, [ [] layouts: rustc::ty::layout::LayoutDetails, [] generics: rustc::ty::Generics, [] trait_def: rustc::ty::TraitDef, [] adt_def: rustc::ty::AdtDef, [] steal_mir: rustc::ty::steal::Steal>, [] mir: rustc::mir::BodyAndCache<$tcx>, [] steal_promoted: rustc::ty::steal::Steal< rustc_index::vec::IndexVec< rustc::mir::Promoted, rustc::mir::BodyAndCache<$tcx> > >, [] promoted: rustc_index::vec::IndexVec< rustc::mir::Promoted, rustc::mir::BodyAndCache<$tcx> >, [] tables: rustc::ty::TypeckTables<$tcx>, [] const_allocs: rustc::mir::interpret::Allocation, [] vtable_method: Option<( rustc::hir::def_id::DefId, rustc::ty::subst::SubstsRef<$tcx> )>, [few, decode] mir_keys: rustc::util::nodemap::DefIdSet, [decode] specialization_graph: rustc::traits::specialization_graph::Graph, [] region_scope_tree: rustc::middle::region::ScopeTree, [] item_local_set: rustc::util::nodemap::ItemLocalSet, [decode] mir_const_qualif: rustc_index::bit_set::BitSet, [] trait_impls_of: rustc::ty::trait_def::TraitImpls, [] dropck_outlives: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::traits::query::dropck_outlives::DropckOutlivesResult<'tcx> > >, [] normalize_projection_ty: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::traits::query::normalize::NormalizationResult<'tcx> > >, [] implied_outlives_bounds: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, Vec> > >, [] type_op_subtype: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, ()> >, [] type_op_normalize_poly_fn_sig: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::PolyFnSig<'tcx>> >, [] type_op_normalize_fn_sig: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::FnSig<'tcx>> >, [] type_op_normalize_predicate: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Predicate<'tcx>> >, [] type_op_normalize_ty: rustc::infer::canonical::Canonical<'tcx, rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Ty<'tcx>> >, [few] crate_inherent_impls: rustc::ty::CrateInherentImpls, [few] upstream_monomorphizations: rustc::util::nodemap::DefIdMap< rustc_data_structures::fx::FxHashMap< rustc::ty::subst::SubstsRef<'tcx>, rustc::hir::def_id::CrateNum > >, [few] diagnostic_items: rustc_data_structures::fx::FxHashMap< syntax::symbol::Symbol, rustc::hir::def_id::DefId, >, [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes, [few] lint_levels: rustc::lint::LintLevelMap, [few] stability_index: rustc::middle::stability::Index<'tcx>, [few] features: rustc_feature::Features, [few] all_traits: Vec, [few] privacy_access_levels: rustc::middle::privacy::AccessLevels, [few] target_features_whitelist: rustc_data_structures::fx::FxHashMap< String, Option >, [few] wasm_import_module_map: rustc_data_structures::fx::FxHashMap< rustc::hir::def_id::DefId, String >, [few] get_lib_features: rustc::middle::lib_features::LibFeatures, [few] defined_lib_features: rustc::middle::lang_items::LanguageItems, [few] visible_parent_map: rustc::util::nodemap::DefIdMap, [few] foreign_module: rustc::middle::cstore::ForeignModule, [few] foreign_modules: Vec, [few] reachable_non_generics: rustc::util::nodemap::DefIdMap< rustc::middle::exported_symbols::SymbolExportLevel >, [few] crate_variances: rustc::ty::CrateVariancesMap<'tcx>, [few] inferred_outlives_crate: rustc::ty::CratePredicatesMap<'tcx>, [] upvars: rustc_data_structures::fx::FxIndexMap, // HIR types [few] hir_forest: rustc::hir::map::Forest<$tcx>, [] attribute: syntax::ast::Attribute, [few] global_asm: rustc::hir::GlobalAsm, [] fn_decl: rustc::hir::FnDecl, [] foreign_item: rustc::hir::ForeignItem<$tcx>, [] impl_item_ref: rustc::hir::ImplItemRef, [few] macro_def: rustc::hir::MacroDef<$tcx>, [] param: rustc::hir::Param, [] path: rustc::hir::Path, [] struct_field: rustc::hir::StructField<$tcx>, [] trait_item_ref: rustc::hir::TraitItemRef, [] ty: rustc::hir::Ty, [] variant: rustc::hir::Variant<$tcx>, ], $tcx); ) } macro_rules! arena_for_type { ([][$ty:ty]) => { TypedArena<$ty> }; ([few $(, $attrs:ident)*][$ty:ty]) => { PhantomData<$ty> }; ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { arena_for_type!([$($attrs),*]$args) }; } macro_rules! declare_arena { ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { #[derive(Default)] pub struct Arena<$tcx> { dropless: DroplessArena, drop: DropArena, $($name: arena_for_type!($a[$ty]),)* } } } macro_rules! which_arena_for_type { ([][$arena:expr]) => { Some($arena) }; ([few$(, $attrs:ident)*][$arena:expr]) => { None }; ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { which_arena_for_type!([$($attrs),*]$args) }; } macro_rules! impl_arena_allocatable { ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { $( impl ArenaAllocatable for $ty {} unsafe impl<$tcx> ArenaField<$tcx> for $ty { #[inline] fn arena<'a>(_arena: &'a Arena<$tcx>) -> Option<&'a TypedArena> { which_arena_for_type!($a[&_arena.$name]) } } )* } } arena_types!(declare_arena, [], 'tcx); arena_types!(impl_arena_allocatable, [], 'tcx); pub trait ArenaAllocatable {} impl ArenaAllocatable for T {} unsafe trait ArenaField<'tcx>: Sized { /// Returns a specific arena to allocate from. /// If `None` is returned, the `DropArena` will be used. fn arena<'a>(arena: &'a Arena<'tcx>) -> Option<&'a TypedArena>; } unsafe impl<'tcx, T> ArenaField<'tcx> for T { #[inline] default fn arena<'a>(_: &'a Arena<'tcx>) -> Option<&'a TypedArena> { panic!() } } impl<'tcx> Arena<'tcx> { #[inline] pub fn alloc(&self, value: T) -> &mut T { if !mem::needs_drop::() { return self.dropless.alloc(value); } match >::arena(self) { Some(arena) => arena.alloc(value), None => unsafe { self.drop.alloc(value) }, } } #[inline] pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { if value.len() == 0 { return &mut []; } self.dropless.alloc_slice(value) } pub fn alloc_from_iter>( &'a self, iter: I, ) -> &'a mut [T] { if !mem::needs_drop::() { return self.dropless.alloc_from_iter(iter); } match >::arena(self) { Some(arena) => arena.alloc_from_iter(iter), None => unsafe { self.drop.alloc_from_iter(iter) }, } } } /// Calls the destructor for an object when dropped. struct DropType { drop_fn: unsafe fn(*mut u8), obj: *mut u8, } unsafe fn drop_for_type(to_drop: *mut u8) { std::ptr::drop_in_place(to_drop as *mut T) } impl Drop for DropType { fn drop(&mut self) { unsafe { (self.drop_fn)(self.obj) } } } /// An arena which can be used to allocate any type. /// Allocating in this arena is unsafe since the type system /// doesn't know which types it contains. In order to /// allocate safely, you must store a PhantomData /// alongside this arena for each type T you allocate. #[derive(Default)] struct DropArena { /// A list of destructors to run when the arena drops. /// Ordered so `destructors` gets dropped before the arena /// since its destructor can reference memory in the arena. destructors: RefCell>, arena: DroplessArena, } impl DropArena { #[inline] unsafe fn alloc(&self, object: T) -> &mut T { let mem = self.arena.alloc_raw(mem::size_of::(), mem::align_of::()) as *mut _ as *mut T; // Write into uninitialized memory. ptr::write(mem, object); let result = &mut *mem; // Record the destructor after doing the allocation as that may panic // and would cause `object`'s destuctor to run twice if it was recorded before self.destructors .borrow_mut() .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); result } #[inline] unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); if vec.is_empty() { return &mut []; } let len = vec.len(); let start_ptr = self .arena .alloc_raw(len.checked_mul(mem::size_of::()).unwrap(), mem::align_of::()) as *mut _ as *mut T; let mut destructors = self.destructors.borrow_mut(); // Reserve space for the destructors so we can't panic while adding them destructors.reserve(len); // Move the content to the arena by copying it and then forgetting // the content of the SmallVec vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); mem::forget(vec.drain(..)); // Record the destructors after doing the allocation as that may panic // and would cause `object`'s destuctor to run twice if it was recorded before for i in 0..len { destructors.push(DropType { drop_fn: drop_for_type::, obj: start_ptr.offset(i as isize) as *mut u8, }); } slice::from_raw_parts_mut(start_ptr, len) } }