Use bitflags to compress function properties

Very minor savings, only 1 MB or so
This commit is contained in:
Jonas Schievink 2021-04-03 20:58:42 +02:00
parent f7e6b186e1
commit ee4b5a34d8
11 changed files with 120 additions and 71 deletions

1
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ use crate::{
body::Expander, body::Expander,
db::DefDatabase, db::DefDatabase,
intern::Interned, intern::Interned,
item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param}, item_tree::{AssocItem, FnFlags, ItemTreeId, ModItem, Param},
type_ref::{TraitRef, TypeBound, TypeRef}, type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility, visibility::RawVisibility,
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@ -23,14 +23,9 @@ pub struct FunctionData {
pub params: Vec<Interned<TypeRef>>, pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>, pub ret_type: Interned<TypeRef>,
pub attrs: Attrs, 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 visibility: RawVisibility,
pub abi: Option<Interned<str>>,
flags: FnFlags,
} }
impl FunctionData { impl FunctionData {
@ -53,6 +48,11 @@ impl FunctionData {
.next_back() .next_back()
.map_or(false, |param| matches!(item_tree[param], Param::Varargs)); .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 { Arc::new(FunctionData {
name: func.name.clone(), name: func.name.clone(),
params: enabled_params params: enabled_params
@ -64,14 +64,45 @@ impl FunctionData {
.collect(), .collect(),
ret_type: func.ret_type.clone(), ret_type: func.ret_type.clone(),
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), 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(), 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)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -24,7 +24,7 @@ use la_arena::{Arena, Idx, RawIdx};
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ast, match_ast, SmolStr, SyntaxKind}; use syntax::{ast, match_ast, SyntaxKind};
use crate::{ use crate::{
attr::{Attrs, RawAttrs}, attr::{Attrs, RawAttrs},
@ -556,15 +556,11 @@ pub struct Function {
pub name: Name, pub name: Name,
pub visibility: RawVisibilityId, pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId, pub generic_params: GenericParamsId,
pub has_self_param: bool, pub abi: Option<Interned<str>>,
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 params: IdRange<Param>, pub params: IdRange<Param>,
pub ret_type: Interned<TypeRef>, pub ret_type: Interned<TypeRef>,
pub ast_id: FileAstId<ast::Fn>, pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -573,13 +569,20 @@ pub enum Param {
Varargs, Varargs,
} }
#[derive(Debug, Clone, PartialEq, Eq)] bitflags::bitflags! {
pub struct FunctionQualifier { /// NOTE: Shared with `FunctionData`
pub is_default: bool, pub(crate) struct FnFlags: u8 {
pub is_const: bool, const HAS_SELF_PARAM = 1 << 0;
pub is_async: bool, const HAS_BODY = 1 << 1;
pub is_unsafe: bool, const IS_DEFAULT = 1 << 2;
pub abi: Option<SmolStr>, 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)] #[derive(Debug, Clone, Eq, PartialEq)]

View file

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

View file

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

View file

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

View file

@ -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 ret = (&ctx_ret).lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into()); let generics = generics(db.upcast(), def.into());
let num_binders = generics.len(); 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 /// Build the declared type of a function. This should not need to look at the

View file

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