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 = [
"anymap",
"base_db",
"bitflags",
"cfg",
"cov-mark",
"dashmap",

View file

@ -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 {

View file

@ -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.

View file

@ -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"

View file

@ -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)]

View file

@ -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},
@ -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)]

View file

@ -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);
@ -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) => {

View file

@ -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;
}

View file

@ -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 });

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 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

View file

@ -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) {