Slightly reduce ItemTree memory footprint

This commit is contained in:
Jonas Schievink 2020-06-23 19:42:19 +02:00
parent f9a1a9cd3c
commit c019002d17
3 changed files with 68 additions and 53 deletions

View file

@ -38,6 +38,8 @@ impl ops::Deref for Attrs {
} }
impl Attrs { impl Attrs {
pub const EMPTY: Attrs = Attrs { entries: None };
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
match def { match def {
AttrDefId::ModuleId(module) => { AttrDefId::ModuleId(module) => {

View file

@ -35,16 +35,8 @@ use crate::{
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
/// The item tree of a source file. #[derive(Default, Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq)] struct ItemTreeData {
pub struct ItemTree {
file_id: HirFileId,
top_level: Vec<ModItem>,
top_attrs: Attrs,
attrs: FxHashMap<ModItem, Attrs>,
empty_attrs: Attrs,
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
imports: Arena<Import>, imports: Arena<Import>,
extern_crates: Arena<ExternCrate>, extern_crates: Arena<ExternCrate>,
functions: Arena<Function>, functions: Arena<Function>,
@ -63,6 +55,26 @@ pub struct ItemTree {
exprs: Arena<Expr>, exprs: Arena<Expr>,
} }
#[derive(Debug, Eq, PartialEq, Hash)]
enum AttrOwner {
/// Attributes on an item.
ModItem(ModItem),
/// Inner attributes of the source file.
TopLevel,
// FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
}
/// The item tree of a source file.
#[derive(Debug, Eq, PartialEq)]
pub struct ItemTree {
file_id: HirFileId,
top_level: SmallVec<[ModItem; 1]>,
attrs: FxHashMap<AttrOwner, Attrs>,
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
data: Option<Box<ItemTreeData>>,
}
impl ItemTree { impl ItemTree {
pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id)); let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id));
@ -95,7 +107,9 @@ impl ItemTree {
} }
}; };
item_tree.top_attrs = top_attrs.unwrap_or_default(); if let Some(attrs) = top_attrs {
item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
}
Arc::new(item_tree) Arc::new(item_tree)
} }
@ -103,26 +117,9 @@ impl ItemTree {
Self { Self {
file_id, file_id,
top_level: Default::default(), top_level: Default::default(),
top_attrs: Default::default(),
attrs: Default::default(), attrs: Default::default(),
empty_attrs: Default::default(),
inner_items: Default::default(), inner_items: Default::default(),
imports: Default::default(), data: Default::default(),
extern_crates: Default::default(),
functions: Default::default(),
structs: Default::default(),
fields: Default::default(),
unions: Default::default(),
enums: Default::default(),
variants: Default::default(),
consts: Default::default(),
statics: Default::default(),
traits: Default::default(),
impls: Default::default(),
type_aliases: Default::default(),
mods: Default::default(),
macro_calls: Default::default(),
exprs: Default::default(),
} }
} }
@ -134,11 +131,11 @@ impl ItemTree {
/// Returns the inner attributes of the source file. /// Returns the inner attributes of the source file.
pub fn top_level_attrs(&self) -> &Attrs { pub fn top_level_attrs(&self) -> &Attrs {
&self.top_attrs self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
} }
pub fn attrs(&self, of: ModItem) -> &Attrs { pub fn attrs(&self, of: ModItem) -> &Attrs {
self.attrs.get(&of).unwrap_or(&self.empty_attrs) self.attrs.get(&AttrOwner::ModItem(of)).unwrap_or(&Attrs::EMPTY)
} }
/// Returns the lowered inner items that `ast` corresponds to. /// Returns the lowered inner items that `ast` corresponds to.
@ -169,6 +166,14 @@ impl ItemTree {
let ptr = map.get(id); let ptr = map.get(id);
ptr.to_node(&root) ptr.to_node(&root)
} }
fn data(&self) -> &ItemTreeData {
self.data.as_ref().expect("attempted to access data of empty ItemTree")
}
fn data_mut(&mut self) -> &mut ItemTreeData {
self.data.get_or_insert_with(Box::default)
}
} }
/// Trait implemented by all nodes in the item tree. /// Trait implemented by all nodes in the item tree.
@ -246,7 +251,7 @@ macro_rules! mod_items {
} }
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
&tree.$fld[index] &tree.data().$fld[index]
} }
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
@ -266,7 +271,7 @@ macro_rules! mod_items {
type Output = $typ; type Output = $typ;
fn index(&self, index: Idx<$typ>) -> &Self::Output { fn index(&self, index: Idx<$typ>) -> &Self::Output {
&self.$fld[index] &self.data().$fld[index]
} }
} }
)+ )+
@ -296,7 +301,7 @@ macro_rules! impl_index {
type Output = $t; type Output = $t;
fn index(&self, index: Idx<$t>) -> &Self::Output { fn index(&self, index: Idx<$t>) -> &Self::Output {
&self.$fld[index] &self.data().$fld[index]
} }
} }
)+ )+

View file

@ -66,6 +66,10 @@ impl Ctx {
self.tree self.tree
} }
fn data(&mut self) -> &mut ItemTreeData {
self.tree.data_mut()
}
fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> { fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> {
assert!(inner || self.inner_items.is_empty()); assert!(inner || self.inner_items.is_empty());
@ -124,7 +128,7 @@ impl Ctx {
} }
fn add_attrs(&mut self, item: ModItem, attrs: Attrs) { fn add_attrs(&mut self, item: ModItem, attrs: Attrs) {
match self.tree.attrs.entry(item) { match self.tree.attrs.entry(AttrOwner::ModItem(item)) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
*entry.get_mut() = entry.get().merge(attrs); *entry.get_mut() = entry.get().merge(attrs);
} }
@ -169,7 +173,7 @@ impl Ctx {
ast::StructKind::Unit => StructDefKind::Unit, ast::StructKind::Unit => StructDefKind::Unit,
}; };
let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
Some(id(self.tree.structs.alloc(res))) Some(id(self.data().structs.alloc(res)))
} }
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
@ -190,7 +194,7 @@ impl Ctx {
let start = self.next_field_idx(); let start = self.next_field_idx();
for field in fields.fields() { for field in fields.fields() {
if let Some(data) = self.lower_record_field(&field) { if let Some(data) = self.lower_record_field(&field) {
self.tree.fields.alloc(data); self.data().fields.alloc(data);
} }
} }
let end = self.next_field_idx(); let end = self.next_field_idx();
@ -209,7 +213,7 @@ impl Ctx {
let start = self.next_field_idx(); let start = self.next_field_idx();
for (i, field) in fields.fields().enumerate() { for (i, field) in fields.fields().enumerate() {
if let Some(data) = self.lower_tuple_field(i, &field) { if let Some(data) = self.lower_tuple_field(i, &field) {
self.tree.fields.alloc(data); self.data().fields.alloc(data);
} }
} }
let end = self.next_field_idx(); let end = self.next_field_idx();
@ -236,7 +240,7 @@ impl Ctx {
}; };
let ast_id = self.source_ast_id_map.ast_id(union); let ast_id = self.source_ast_id_map.ast_id(union);
let res = Union { name, visibility, generic_params, fields, ast_id }; let res = Union { name, visibility, generic_params, fields, ast_id };
Some(id(self.tree.unions.alloc(res))) Some(id(self.data().unions.alloc(res)))
} }
fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> { fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
@ -249,14 +253,14 @@ impl Ctx {
}; };
let ast_id = self.source_ast_id_map.ast_id(enum_); let ast_id = self.source_ast_id_map.ast_id(enum_);
let res = Enum { name, visibility, generic_params, variants, ast_id }; let res = Enum { name, visibility, generic_params, variants, ast_id };
Some(id(self.tree.enums.alloc(res))) Some(id(self.data().enums.alloc(res)))
} }
fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> {
let start = self.next_variant_idx(); let start = self.next_variant_idx();
for variant in variants.variants() { for variant in variants.variants() {
if let Some(data) = self.lower_variant(&variant) { if let Some(data) = self.lower_variant(&variant) {
self.tree.variants.alloc(data); self.data().variants.alloc(data);
} }
} }
let end = self.next_variant_idx(); let end = self.next_variant_idx();
@ -327,7 +331,7 @@ impl Ctx {
}; };
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
Some(id(self.tree.functions.alloc(res))) Some(id(self.data().functions.alloc(res)))
} }
fn lower_type_alias( fn lower_type_alias(
@ -341,7 +345,7 @@ impl Ctx {
let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
let ast_id = self.source_ast_id_map.ast_id(type_alias); let ast_id = self.source_ast_id_map.ast_id(type_alias);
let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id };
Some(id(self.tree.type_aliases.alloc(res))) Some(id(self.data().type_aliases.alloc(res)))
} }
fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> { fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
@ -351,7 +355,7 @@ impl Ctx {
let mutable = static_.mut_token().is_some(); let mutable = static_.mut_token().is_some();
let ast_id = self.source_ast_id_map.ast_id(static_); let ast_id = self.source_ast_id_map.ast_id(static_);
let res = Static { name, visibility, mutable, type_ref, ast_id }; let res = Static { name, visibility, mutable, type_ref, ast_id };
Some(id(self.tree.statics.alloc(res))) Some(id(self.data().statics.alloc(res)))
} }
fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> { fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
@ -360,7 +364,7 @@ impl Ctx {
let visibility = self.lower_visibility(konst); let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst); let ast_id = self.source_ast_id_map.ast_id(konst);
let res = Const { name, visibility, type_ref, ast_id }; let res = Const { name, visibility, type_ref, ast_id };
id(self.tree.consts.alloc(res)) id(self.data().consts.alloc(res))
} }
fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> { fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
@ -386,7 +390,7 @@ impl Ctx {
}; };
let ast_id = self.source_ast_id_map.ast_id(module); let ast_id = self.source_ast_id_map.ast_id(module);
let res = Mod { name, visibility, kind, ast_id }; let res = Mod { name, visibility, kind, ast_id };
Some(id(self.tree.mods.alloc(res))) Some(id(self.data().mods.alloc(res)))
} }
fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> { fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
@ -417,7 +421,7 @@ impl Ctx {
items: items.unwrap_or_default(), items: items.unwrap_or_default(),
ast_id, ast_id,
}; };
Some(id(self.tree.traits.alloc(res))) Some(id(self.data().traits.alloc(res)))
} }
fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> { fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
@ -440,7 +444,7 @@ impl Ctx {
.collect(); .collect();
let ast_id = self.source_ast_id_map.ast_id(impl_def); let ast_id = self.source_ast_id_map.ast_id(impl_def);
let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
Some(id(self.tree.impls.alloc(res))) Some(id(self.data().impls.alloc(res)))
} }
fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> { fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
@ -451,7 +455,7 @@ impl Ctx {
// Every use item can expand to many `Import`s. // Every use item can expand to many `Import`s.
let mut imports = Vec::new(); let mut imports = Vec::new();
let tree = &mut self.tree; let tree = self.tree.data_mut();
ModPath::expand_use_item( ModPath::expand_use_item(
InFile::new(self.file, use_item.clone()), InFile::new(self.file, use_item.clone()),
&self.hygiene, &self.hygiene,
@ -484,7 +488,7 @@ impl Ctx {
let is_macro_use = extern_crate.has_atom_attr("macro_use"); let is_macro_use = extern_crate.has_atom_attr("macro_use");
let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
Some(id(self.tree.extern_crates.alloc(res))) Some(id(self.data().extern_crates.alloc(res)))
} }
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
@ -511,7 +515,7 @@ impl Ctx {
let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
Some(id(self.tree.macro_calls.alloc(res))) Some(id(self.data().macro_calls.alloc(res)))
} }
fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
@ -619,10 +623,14 @@ impl Ctx {
} }
fn next_field_idx(&self) -> Idx<Field> { fn next_field_idx(&self) -> Idx<Field> {
Idx::from_raw(RawId::from(self.tree.fields.len() as u32)) Idx::from_raw(RawId::from(
self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
))
} }
fn next_variant_idx(&self) -> Idx<Variant> { fn next_variant_idx(&self) -> Idx<Variant> {
Idx::from_raw(RawId::from(self.tree.variants.len() as u32)) Idx::from_raw(RawId::from(
self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
))
} }
} }