use positional ids for fields

This commit is contained in:
Aleksey Kladov 2019-01-25 14:21:14 +03:00
parent 47e5536678
commit 87ac5f5b36
6 changed files with 144 additions and 131 deletions

View file

@ -4,6 +4,7 @@ use std::{
fmt,
marker::PhantomData,
ops::{Index, IndexMut},
iter::FromIterator,
};
pub mod map;
@ -109,3 +110,15 @@ impl<ID: ArenaId, T> IndexMut<ID> for Arena<ID, T> {
&mut self.data[idx]
}
}
impl<ID: ArenaId, T> FromIterator<T> for Arena<ID, T> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
Arena {
data: Vec::from_iter(iter),
_ty: PhantomData,
}
}
}

View file

@ -79,12 +79,13 @@ impl EnumVariant {
.to_owned();
(file_id, var)
}
pub(crate) fn variant_data(&self, db: &impl HirDatabase) -> Arc<VariantData> {
db.enum_data(self.parent).variants[self.id]
.variant_data
.clone()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct EnumVariantId(RawId);
impl_arena_id!(EnumVariantId);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
pub(crate) name: Option<Name>,
@ -94,28 +95,31 @@ pub struct EnumData {
impl EnumData {
pub(crate) fn enum_data_query(db: &impl HirDatabase, e: Enum) -> Arc<EnumData> {
let (_file_id, enum_def) = e.source(db);
let mut res = EnumData {
name: enum_def.name().map(|n| n.as_name()),
variants: Arena::default(),
};
for var in variants(&*enum_def) {
let data = EnumVariantData {
let name = enum_def.name().map(|n| n.as_name());
let variants = variants(&*enum_def)
.map(|var| EnumVariantData {
name: var.name().map(|it| it.as_name()),
variant_data: Arc::new(VariantData::new(var.flavor())),
};
res.variants.alloc(data);
}
Arc::new(res)
})
.collect();
Arc::new(EnumData { name, variants })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct EnumVariantId(RawId);
impl_arena_id!(EnumVariantId);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariantData {
pub(crate) name: Option<Name>,
pub(crate) variant_data: Arc<VariantData>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct StructFieldId(RawId);
impl_arena_id!(StructFieldId);
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructFieldData {
@ -125,45 +129,27 @@ pub struct StructFieldData {
/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
Struct(Vec<StructFieldData>),
Tuple(Vec<StructFieldData>),
pub struct VariantData(VariantDataInner);
#[derive(Debug, Clone, PartialEq, Eq)]
enum VariantDataInner {
Struct(Arena<StructFieldId, StructFieldData>),
Tuple(Arena<StructFieldId, StructFieldData>),
Unit,
}
impl VariantData {
pub fn fields(&self) -> &[StructFieldData] {
match self {
VariantData::Struct(fields) | VariantData::Tuple(fields) => fields,
_ => &[],
}
}
pub fn is_struct(&self) -> bool {
match self {
VariantData::Struct(..) => true,
_ => false,
}
}
pub fn is_tuple(&self) -> bool {
match self {
VariantData::Tuple(..) => true,
_ => false,
}
}
pub fn is_unit(&self) -> bool {
match self {
VariantData::Unit => true,
_ => false,
pub(crate) fn fields(&self) -> Option<&Arena<StructFieldId, StructFieldData>> {
match &self.0 {
VariantDataInner::Struct(fields) | VariantDataInner::Tuple(fields) => Some(fields),
_ => None,
}
}
}
impl VariantData {
fn new(flavor: StructFlavor) -> Self {
match flavor {
let inner = match flavor {
StructFlavor::Tuple(fl) => {
let fields = fl
.fields()
@ -173,7 +159,7 @@ impl VariantData {
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
})
.collect();
VariantData::Tuple(fields)
VariantDataInner::Tuple(fields)
}
StructFlavor::Named(fl) => {
let fields = fl
@ -183,16 +169,17 @@ impl VariantData {
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
})
.collect();
VariantData::Struct(fields)
VariantDataInner::Struct(fields)
}
StructFlavor::Unit => VariantData::Unit,
}
StructFlavor::Unit => VariantDataInner::Unit,
};
VariantData(inner)
}
pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
self.fields()
.iter()
.find(|f| f.name == *field_name)
.map(|f| &f.type_ref)
}
// pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
// self.fields()
// .iter()
// .find(|f| f.name == *field_name)
// .map(|f| &f.type_ref)
// }
}

View file

@ -11,7 +11,7 @@ use crate::{
db::HirDatabase,
expr::BodySyntaxMapping,
ty::{InferenceResult, VariantDef},
adt::{VariantData, EnumVariantId},
adt::{EnumVariantId, StructFieldId},
generics::GenericParams,
docs::{Documentation, Docs, docs_from_ast},
module_tree::ModuleId,
@ -177,19 +177,25 @@ impl Module {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructField {
parent: VariantDef,
name: Name,
pub(crate) id: StructFieldId,
}
impl StructField {
pub fn name(&self) -> &Name {
&self.name
pub fn name(&self, db: &impl HirDatabase) -> Name {
self.parent.variant_data(db).fields().unwrap()[self.id]
.name
.clone()
}
pub fn ty(&self, db: &impl HirDatabase) -> Option<Ty> {
db.type_for_field(self.parent, self.name.clone())
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
db.type_for_field(*self)
}
pub fn parent_def(&self, _db: &impl HirDatabase) -> VariantDef {
self.parent
}
}
@ -215,14 +221,28 @@ impl Struct {
db.struct_data(*self)
.variant_data
.fields()
.iter()
.map(|it| StructField {
.into_iter()
.flat_map(|it| it.iter())
.map(|(id, _)| StructField {
parent: (*self).into(),
name: it.name.clone(),
id,
})
.collect()
}
pub fn field(&self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
db.struct_data(*self)
.variant_data
.fields()
.into_iter()
.flat_map(|it| it.iter())
.find(|(_id, data)| data.name == *name)
.map(|(id, _)| StructField {
parent: (*self).into(),
id,
})
}
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
db.generic_params((*self).into())
}
@ -300,22 +320,29 @@ impl EnumVariant {
db.enum_data(self.parent).variants[self.id].name.clone()
}
pub fn variant_data(&self, db: &impl HirDatabase) -> Arc<VariantData> {
db.enum_data(self.parent).variants[self.id]
.variant_data
.clone()
}
pub fn fields(&self, db: &impl HirDatabase) -> Vec<StructField> {
self.variant_data(db)
.fields()
.iter()
.map(|it| StructField {
.into_iter()
.flat_map(|it| it.iter())
.map(|(id, _)| StructField {
parent: (*self).into(),
name: it.name.clone(),
id,
})
.collect()
}
pub fn field(&self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
self.variant_data(db)
.fields()
.into_iter()
.flat_map(|it| it.iter())
.find(|(_id, data)| data.name == *name)
.map(|(id, _)| StructField {
parent: (*self).into(),
id,
})
}
}
impl Docs for EnumVariant {

View file

@ -4,15 +4,15 @@ use ra_syntax::{SyntaxNode, TreeArc, SourceFile};
use ra_db::{SyntaxDatabase, CrateId, salsa};
use crate::{
MacroCallId, Name, HirFileId,
MacroCallId, HirFileId,
SourceFileItems, SourceItemId, Crate, Module, HirInterner,
query_definitions,
Function, FnSignature, FnScopes,
Struct, Enum,
Struct, Enum, StructField,
macros::MacroExpansion,
module_tree::ModuleTree,
nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, VariantDef},
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef},
adt::{StructData, EnumData},
impl_block::ModuleImplBlocks,
generics::{GenericParams, GenericDef},
@ -42,7 +42,7 @@ pub trait HirDatabase: SyntaxDatabase + AsRef<HirInterner> {
fn type_for_def(&self, def: TypableDef) -> Ty;
#[salsa::invoke(crate::ty::type_for_field)]
fn type_for_field(&self, def: VariantDef, field: Name) -> Option<Ty>;
fn type_for_field(&self, field: StructField) -> Ty;
#[salsa::invoke(query_definitions::file_items)]
fn file_items(&self, file_id: HirFileId) -> Arc<SourceFileItems>;

View file

@ -38,6 +38,7 @@ use crate::{
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
generics::GenericParams,
path::GenericArg,
adt::VariantData,
};
/// The ID of a type variable.
@ -702,19 +703,30 @@ pub enum VariantDef {
}
impl_froms!(VariantDef: Struct, EnumVariant);
pub(super) fn type_for_field(db: &impl HirDatabase, def: VariantDef, field: Name) -> Option<Ty> {
let (variant_data, generics, module) = match def {
VariantDef::Struct(s) => (s.variant_data(db), s.generic_params(db), s.module(db)),
VariantDef::EnumVariant(var) => (
var.variant_data(db),
var.parent_enum(db).generic_params(db),
var.module(db),
),
impl VariantDef {
pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
match self {
VariantDef::Struct(it) => it.field(db, name),
VariantDef::EnumVariant(it) => it.field(db, name),
}
}
pub(crate) fn variant_data(self, db: &impl HirDatabase) -> Arc<VariantData> {
match self {
VariantDef::Struct(it) => it.variant_data(db),
VariantDef::EnumVariant(it) => it.variant_data(db),
}
}
}
pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
let parent_def = field.parent_def(db);
let (generics, module) = match parent_def {
VariantDef::Struct(it) => (it.generic_params(db), it.module(db)),
VariantDef::EnumVariant(it) => (it.parent_enum(db).generic_params(db), it.module(db)),
};
// We can't have an impl block ere, right?
// let impl_block = def_id.impl_block(db);
let type_ref = variant_data.get_field_type_ref(&field)?;
Some(Ty::from_hir(db, &module, None, &generics, &type_ref))
let var_data = parent_def.variant_data(db);
let type_ref = &var_data.fields().unwrap()[field.id].type_ref;
Ty::from_hir(db, &module, None, &generics, type_ref)
}
/// The result of type inference: A mapping from expressions and patterns to types.
@ -1122,39 +1134,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
}
fn resolve_fields(&mut self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> {
let (ty, def) = self.resolve_variant(path);
match def? {
VariantDef::Struct(s) => {
let fields = s.fields(self.db);
Some((ty, fields))
}
VariantDef::EnumVariant(var) => {
let fields = var.fields(self.db);
Some((ty, fields))
}
}
}
fn infer_tuple_struct_pat(
&mut self,
path: Option<&Path>,
subpats: &[PatId],
expected: &Ty,
) -> Ty {
let (ty, fields) = self
.resolve_fields(path)
.unwrap_or((Ty::Unknown, Vec::new()));
let (ty, def) = self.resolve_variant(path);
self.unify(&ty, expected);
let substs = ty.substs().unwrap_or_else(Substs::empty);
for (i, &subpat) in subpats.iter().enumerate() {
let expected_ty = fields
.get(i)
.and_then(|field| field.ty(self.db))
.unwrap_or(Ty::Unknown)
let expected_ty = def
.and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
.map_or(Ty::Unknown, |field| field.ty(self.db))
.subst(&substs);
self.infer_pat(subpat, &expected_ty);
}
@ -1163,19 +1158,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty {
let (ty, fields) = self
.resolve_fields(path)
.unwrap_or((Ty::Unknown, Vec::new()));
let (ty, def) = self.resolve_variant(path);
self.unify(&ty, expected);
let substs = ty.substs().unwrap_or_else(Substs::empty);
for subpat in subpats {
let matching_field = fields.iter().find(|field| field.name() == &subpat.name);
let matching_field = def.and_then(|it| it.field(self.db, &subpat.name));
let expected_ty = matching_field
.and_then(|field| field.ty(self.db))
.unwrap_or(Ty::Unknown)
.map_or(Ty::Unknown, |field| field.ty(self.db))
.subst(&substs);
self.infer_pat(subpat.pat, &expected_ty);
}
@ -1420,14 +1412,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let (ty, def_id) = self.resolve_variant(path.as_ref());
let substs = ty.substs().unwrap_or_else(Substs::empty);
for field in fields {
let field_ty = if let Some(def_id) = def_id {
self.db
.type_for_field(def_id.into(), field.name.clone())
.unwrap_or(Ty::Unknown)
.subst(&substs)
} else {
Ty::Unknown
};
let field_ty = def_id
.and_then(|it| it.field(self.db, &field.name))
.map_or(Ty::Unknown, |field| field.ty(self.db))
.subst(&substs);
self.infer_expr(field.expr, &Expectation::has_type(field_ty));
}
if let Some(expr) = spread {
@ -1440,7 +1428,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let ty = receiver_ty
.autoderef(self.db)
.find_map(|derefed_ty| match derefed_ty {
// this is more complicated than necessary because type_for_field is cancelable
Ty::Tuple(fields) => {
let i = name.to_string().parse::<usize>().ok();
i.and_then(|i| fields.get(i).cloned())
@ -1449,10 +1436,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
def_id: AdtDef::Struct(s),
ref substs,
..
} => self
.db
.type_for_field(s.into(), name.clone())
.map(|ty| ty.subst(substs)),
} => s
.field(self.db, name)
.map(|field| field.ty(self.db).subst(substs)),
_ => None,
})
.unwrap_or(Ty::Unknown);

View file

@ -34,10 +34,10 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
CompletionItem::new(
CompletionKind::Reference,
ctx.source_range(),
field.name().to_string(),
field.name(ctx.db).to_string(),
)
.kind(CompletionItemKind::Field)
.set_detail(field.ty(ctx.db).map(|ty| ty.subst(substs).to_string()))
.detail(field.ty(ctx.db).subst(substs).to_string())
.add_to(acc);
}
}