8315: Try to reduce ItemTree size further r=jonas-schievink a=jonas-schievink

This was mostly a failed experiment, but still seems like the right thing to do. Memory reduction is mostly negligible.

bors r+

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-04-03 19:01:03 +00:00 committed by GitHub
commit b78f1a0a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 128 additions and 75 deletions

1
Cargo.lock generated

@ -498,6 +498,7 @@ version = "0.0.0"
dependencies = [
"anymap",
"base_db",
"bitflags",
"cfg",
"cov-mark",
"dashmap",

@ -20,22 +20,21 @@ impl HirDisplay for Function {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
let data = f.db.function_data(self.id);
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let qual = &data.qualifier;
if qual.is_default {
if data.is_default() {
write!(f, "default ")?;
}
if qual.is_const {
if data.is_const() {
write!(f, "const ")?;
}
if qual.is_async {
if data.is_async() {
write!(f, "async ")?;
}
if qual.is_unsafe {
if data.is_unsafe() {
write!(f, "unsafe ")?;
}
if let Some(abi) = &qual.abi {
if let Some(abi) = &data.abi {
// FIXME: String escape?
write!(f, "extern \"{}\" ", abi)?;
write!(f, "extern \"{}\" ", &**abi)?;
}
write!(f, "fn {}", data.name)?;
@ -68,7 +67,7 @@ impl HirDisplay for Function {
write!(f, ", ")?;
} else {
first = false;
if data.has_self_param {
if data.has_self_param() {
write_self_param(type_ref, f)?;
continue;
}
@ -88,7 +87,7 @@ impl HirDisplay for Function {
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !qual.is_async {
let ret_type = if !data.is_async() {
&data.ret_type
} else {
match &*data.ret_type {

@ -832,7 +832,7 @@ impl Function {
}
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
if !db.function_data(self.id).has_self_param {
if !db.function_data(self.id).has_self_param() {
return None;
}
Some(SelfParam { func: self.id })
@ -864,7 +864,7 @@ impl Function {
}
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).qualifier.is_unsafe
db.function_data(self.id).is_unsafe()
}
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
@ -878,7 +878,7 @@ impl Function {
///
/// This is false in the case of required (not provided) trait methods.
pub fn has_body(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).has_body
db.function_data(self.id).has_body()
}
/// A textual representation of the HIR of this function for debugging purposes.

@ -10,6 +10,7 @@ edition = "2018"
doctest = false
[dependencies]
bitflags = "1.2.1"
cov-mark = { version = "1.1", features = ["thread-local"] }
dashmap = { version = "4.0.2", features = ["raw-api"] }
log = "0.4.8"

@ -10,7 +10,7 @@ use crate::{
body::Expander,
db::DefDatabase,
intern::Interned,
item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
item_tree::{AssocItem, FnFlags, ItemTreeId, ModItem, Param},
type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@ -23,14 +23,9 @@ pub struct FunctionData {
pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>,
pub attrs: Attrs,
/// True if the first param is `self`. This is relevant to decide whether this
/// can be called as a method.
pub has_self_param: bool,
pub has_body: bool,
pub qualifier: FunctionQualifier,
pub is_in_extern_block: bool,
pub is_varargs: bool,
pub visibility: RawVisibility,
pub abi: Option<Interned<str>>,
flags: FnFlags,
}
impl FunctionData {
@ -53,6 +48,11 @@ impl FunctionData {
.next_back()
.map_or(false, |param| matches!(item_tree[param], Param::Varargs));
let mut flags = func.flags;
if is_varargs {
flags |= FnFlags::IS_VARARGS;
}
Arc::new(FunctionData {
name: func.name.clone(),
params: enabled_params
@ -64,14 +64,45 @@ impl FunctionData {
.collect(),
ret_type: func.ret_type.clone(),
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
has_self_param: func.has_self_param,
has_body: func.has_body,
qualifier: func.qualifier.clone(),
is_in_extern_block: func.is_in_extern_block,
is_varargs,
visibility: item_tree[func.visibility].clone(),
abi: func.abi.clone(),
flags,
})
}
pub fn has_body(&self) -> bool {
self.flags.contains(FnFlags::HAS_BODY)
}
/// True if the first param is `self`. This is relevant to decide whether this
/// can be called as a method.
pub fn has_self_param(&self) -> bool {
self.flags.contains(FnFlags::HAS_SELF_PARAM)
}
pub fn is_default(&self) -> bool {
self.flags.contains(FnFlags::IS_DEFAULT)
}
pub fn is_const(&self) -> bool {
self.flags.contains(FnFlags::IS_CONST)
}
pub fn is_async(&self) -> bool {
self.flags.contains(FnFlags::IS_ASYNC)
}
pub fn is_unsafe(&self) -> bool {
self.flags.contains(FnFlags::IS_UNSAFE)
}
pub fn is_in_extern_block(&self) -> bool {
self.flags.contains(FnFlags::IS_IN_EXTERN_BLOCK)
}
pub fn is_varargs(&self) -> bool {
self.flags.contains(FnFlags::IS_VARARGS)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]

@ -24,7 +24,7 @@ use la_arena::{Arena, Idx, RawIdx};
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{ast, match_ast, SmolStr, SyntaxKind};
use syntax::{ast, match_ast, SyntaxKind};
use crate::{
attr::{Attrs, RawAttrs},
@ -529,7 +529,7 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
/// A desugared `use` import.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Import {
pub path: ModPath,
pub path: Interned<ModPath>,
pub alias: Option<ImportAlias>,
pub visibility: RawVisibilityId,
pub is_glob: bool,
@ -556,15 +556,11 @@ pub struct Function {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub has_self_param: bool,
pub has_body: bool,
pub qualifier: FunctionQualifier,
/// Whether the function is located in an `extern` block (*not* whether it is an
/// `extern "abi" fn`).
pub is_in_extern_block: bool,
pub abi: Option<Interned<str>>,
pub params: IdRange<Param>,
pub ret_type: Interned<TypeRef>,
pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@ -573,13 +569,20 @@ pub enum Param {
Varargs,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionQualifier {
pub is_default: bool,
pub is_const: bool,
pub is_async: bool,
pub is_unsafe: bool,
pub abi: Option<SmolStr>,
bitflags::bitflags! {
/// NOTE: Shared with `FunctionData`
pub(crate) struct FnFlags: u8 {
const HAS_SELF_PARAM = 1 << 0;
const HAS_BODY = 1 << 1;
const IS_DEFAULT = 1 << 2;
const IS_CONST = 1 << 3;
const IS_ASYNC = 1 << 4;
const IS_UNSAFE = 1 << 5;
/// Whether the function is located in an `extern` block (*not* whether it is an
/// `extern "abi" fn`).
const IS_IN_EXTERN_BLOCK = 1 << 6;
const IS_VARARGS = 1 << 7;
}
}
#[derive(Debug, Clone, Eq, PartialEq)]

@ -395,39 +395,51 @@ impl Ctx {
ret_type
};
let has_body = func.body().is_some();
let abi = func.abi().map(|abi| {
// FIXME: Abi::abi() -> Option<SyntaxToken>?
match abi.syntax().last_token() {
Some(tok) if tok.kind() == SyntaxKind::STRING => {
// FIXME: Better way to unescape?
Interned::new_str(tok.text().trim_matches('"'))
}
_ => {
// `extern` default to be `extern "C"`.
Interned::new_str("C")
}
}
});
let ast_id = self.source_ast_id_map.ast_id(func);
let qualifier = FunctionQualifier {
is_default: func.default_token().is_some(),
is_const: func.const_token().is_some(),
is_async: func.async_token().is_some(),
is_unsafe: func.unsafe_token().is_some(),
abi: func.abi().map(|abi| {
// FIXME: Abi::abi() -> Option<SyntaxToken>?
match abi.syntax().last_token() {
Some(tok) if tok.kind() == SyntaxKind::STRING => {
// FIXME: Better way to unescape?
tok.text().trim_matches('"').into()
}
_ => {
// `extern` default to be `extern "C"`.
"C".into()
}
}
}),
};
let mut flags = FnFlags::empty();
if func.body().is_some() {
flags |= FnFlags::HAS_BODY;
}
if has_self_param {
flags |= FnFlags::HAS_SELF_PARAM;
}
if func.default_token().is_some() {
flags |= FnFlags::IS_DEFAULT;
}
if func.const_token().is_some() {
flags |= FnFlags::IS_CONST;
}
if func.async_token().is_some() {
flags |= FnFlags::IS_ASYNC;
}
if func.unsafe_token().is_some() {
flags |= FnFlags::IS_UNSAFE;
}
let mut res = Function {
name,
visibility,
generic_params: GenericParamsId::EMPTY,
has_self_param,
has_body,
qualifier,
is_in_extern_block: false,
abi,
params,
ret_type: Interned::new(ret_type),
ast_id,
flags,
};
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
@ -577,7 +589,7 @@ impl Ctx {
&self.hygiene,
|path, _use_tree, is_glob, alias| {
imports.push(id(tree.imports.alloc(Import {
path,
path: Interned::new(path),
alias,
visibility,
is_glob,
@ -640,8 +652,10 @@ impl Ctx {
ast::ExternItem::Fn(ast) => {
let func_id = self.lower_function(&ast)?;
let func = &mut self.data().functions[func_id.index];
func.qualifier.is_unsafe = is_intrinsic_fn_unsafe(&func.name);
func.is_in_extern_block = true;
if is_intrinsic_fn_unsafe(&func.name) {
func.flags |= FnFlags::IS_UNSAFE;
}
func.flags |= FnFlags::IS_IN_EXTERN_BLOCK;
func_id.into()
}
ast::ExternItem::Static(ast) => {

@ -23,6 +23,7 @@ use crate::{
attr::Attrs,
db::DefDatabase,
derive_macro_as_call_id,
intern::Interned,
item_scope::{ImportType, PerNsGlobImports},
item_tree::{
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem,
@ -139,7 +140,7 @@ enum ImportSource {
#[derive(Clone, Debug, Eq, PartialEq)]
struct Import {
path: ModPath,
path: Interned<ModPath>,
alias: Option<ImportAlias>,
visibility: RawVisibility,
is_glob: bool,
@ -181,7 +182,10 @@ impl Import {
let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
let visibility = &tree[it.visibility];
Self {
path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
path: Interned::new(ModPath::from_segments(
PathKind::Plain,
iter::once(it.name.clone()),
)),
alias: it.alias.clone(),
visibility: visibility.clone(),
is_glob: false,

@ -91,7 +91,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
fn validate_func(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
if data.is_in_extern_block {
if data.is_in_extern_block() {
cov_mark::hit!(extern_func_incorrect_case_ignored);
return;
}

@ -32,7 +32,7 @@ impl<'a, 'b> UnsafeValidator<'a, 'b> {
let def = self.owner;
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
let is_unsafe = match self.owner {
DefWithBodyId::FunctionId(it) => db.function_data(it).qualifier.is_unsafe,
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
};
if is_unsafe
@ -86,7 +86,7 @@ fn walk_unsafe(
match expr {
&Expr::Call { callee, .. } => {
if let Some(func) = infer[callee].as_fn_def(db) {
if db.function_data(func).qualifier.is_unsafe {
if db.function_data(func).is_unsafe() {
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
}
}
@ -103,7 +103,7 @@ fn walk_unsafe(
Expr::MethodCall { .. } => {
if infer
.method_resolution(current)
.map(|func| db.function_data(func).qualifier.is_unsafe)
.map(|func| db.function_data(func).is_unsafe())
.unwrap_or(false)
{
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });

@ -1054,7 +1054,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let ret = (&ctx_ret).lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into());
let num_binders = generics.len();
Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs))
Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs()))
}
/// Build the declared type of a function. This should not need to look at the

@ -675,7 +675,7 @@ fn is_valid_candidate(
}
}
if let Some(receiver_ty) = receiver_ty {
if !data.has_self_param {
if !data.has_self_param() {
return false;
}
let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {