Add basic HIR and types for structs/enums

This commit is contained in:
Florian Diebold 2018-12-24 19:07:48 +01:00
parent 5fb426cb9e
commit b5b68f2094
9 changed files with 244 additions and 29 deletions

View File

@ -95,6 +95,8 @@ salsa::database_storage! {
fn submodules() for hir::db::SubmodulesQuery;
fn infer() for hir::db::InferQuery;
fn type_for_def() for hir::db::TypeForDefQuery;
fn struct_data() for db::StructDataQuery;
fn enum_data() for db::EnumDataQuery;
}
}
}

114
crates/ra_hir/src/adt.rs Normal file
View File

@ -0,0 +1,114 @@
use ra_syntax::{SmolStr, ast::{self, NameOwner}};
use crate::{
DefId, Cancelable,
db::{HirDatabase},
ty::{Ty},
};
pub struct Struct {
def_id: DefId,
}
impl Struct {
pub(crate) fn new(def_id: DefId) -> Self {
Struct { def_id }
}
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
Ok(db.struct_data(self.def_id)?.name.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
name: SmolStr,
variant_data: VariantData,
}
impl StructData {
pub(crate) fn new(struct_def: ast::StructDef) -> StructData {
let name = struct_def
.name()
.map(|n| n.text())
.unwrap_or(SmolStr::new("[error]"));
let variant_data = VariantData::Unit; // TODO implement this
StructData { name, variant_data }
}
}
pub struct Enum {
def_id: DefId,
}
impl Enum {
pub(crate) fn new(def_id: DefId) -> Self {
Enum { def_id }
}
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
Ok(db.enum_data(self.def_id)?.name.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
name: SmolStr,
variants: Vec<(SmolStr, VariantData)>,
}
impl EnumData {
pub(crate) fn new(enum_def: ast::EnumDef) -> Self {
let name = enum_def
.name()
.map(|n| n.text())
.unwrap_or(SmolStr::new("[error]"));
let variants = Vec::new(); // TODO implement this
EnumData { name, variants }
}
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructField {
name: SmolStr,
ty: Ty,
}
/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
Struct(Vec<StructField>),
Tuple(Vec<StructField>),
Unit,
}
impl VariantData {
pub fn fields(&self) -> &[StructField] {
match *self {
VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
_ => &[],
}
}
pub fn is_struct(&self) -> bool {
if let VariantData::Struct(..) = *self {
true
} else {
false
}
}
pub fn is_tuple(&self) -> bool {
if let VariantData::Tuple(..) = *self {
true
} else {
false
}
}
pub fn is_unit(&self) -> bool {
if let VariantData::Unit = *self {
true
} else {
false
}
}
}

View File

@ -15,6 +15,7 @@ use crate::{
module::{ModuleId, ModuleTree, ModuleSource,
nameres::{ItemMap, InputModuleItems}},
ty::{InferenceResult, Ty},
adt::{StructData, EnumData},
};
salsa::query_group! {
@ -31,6 +32,16 @@ pub trait HirDatabase: SyntaxDatabase
use fn query_definitions::fn_syntax;
}
fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> {
type StructDataQuery;
use fn query_definitions::struct_data;
}
fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> {
type EnumDataQuery;
use fn query_definitions::enum_data;
}
fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
type InferQuery;
use fn query_definitions::infer;

View File

@ -25,6 +25,7 @@ pub mod source_binder;
mod krate;
mod module;
mod function;
mod adt;
mod ty;
use std::ops::Index;
@ -42,6 +43,7 @@ pub use self::{
krate::Crate,
module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
function::{Function, FnScopes},
adt::{Struct, Enum},
};
pub use self::function::FnSignatureInfo;
@ -56,6 +58,8 @@ ra_db::impl_numeric_id!(DefId);
pub(crate) enum DefKind {
Module,
Function,
Struct,
Enum,
Item,
}
@ -73,8 +77,8 @@ impl DefKind {
SyntaxKind::FN_DEF => Some(DefKind::Function),
SyntaxKind::MODULE => Some(DefKind::Module),
// These define items, but don't have their own DefKinds yet:
SyntaxKind::STRUCT_DEF => Some(DefKind::Item),
SyntaxKind::ENUM_DEF => Some(DefKind::Item),
SyntaxKind::STRUCT_DEF => Some(DefKind::Struct),
SyntaxKind::ENUM_DEF => Some(DefKind::Enum),
SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
SyntaxKind::TYPE_DEF => Some(DefKind::Item),
SyntaxKind::CONST_DEF => Some(DefKind::Item),
@ -99,6 +103,8 @@ impl DefLoc {
pub enum Def {
Module(Module),
Function(Function),
Struct(Struct),
Enum(Enum),
Item,
}
@ -114,6 +120,14 @@ impl DefId {
let function = Function::new(self);
Def::Function(function)
}
DefKind::Struct => {
let struct_def = Struct::new(self);
Def::Struct(struct_def)
}
DefKind::Enum => {
let enum_def = Enum::new(self);
Def::Enum(enum_def)
}
DefKind::Item => Def::Item,
};
Ok(res)

View File

@ -193,6 +193,8 @@ salsa::database_storage! {
fn submodules() for db::SubmodulesQuery;
fn infer() for db::InferQuery;
fn type_for_def() for db::TypeForDefQuery;
fn struct_data() for db::StructDataQuery;
fn enum_data() for db::EnumDataQuery;
}
}
}

View File

@ -19,7 +19,8 @@ use crate::{
imp::Submodule,
nameres::{InputModuleItems, ItemMap, Resolver},
},
ty::{self, InferenceResult, Ty}
ty::{self, InferenceResult, Ty},
adt::{StructData, EnumData},
};
/// Resolve `FnId` to the corresponding `SyntaxNode`
@ -45,6 +46,24 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
ty::type_for_def(db, def_id)
}
pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
let def_loc = def_id.loc(db);
assert!(def_loc.kind == DefKind::Struct);
let syntax = db.file_item(def_loc.source_item_id);
let struct_def =
ast::StructDef::cast(syntax.borrowed()).expect("struct def should point to StructDef node");
Ok(Arc::new(StructData::new(struct_def.borrowed())))
}
pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> {
let def_loc = def_id.loc(db);
assert!(def_loc.kind == DefKind::Enum);
let syntax = db.file_item(def_loc.source_item_id);
let enum_def =
ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node");
Ok(Arc::new(EnumData::new(enum_def.borrowed())))
}
pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
let mut res = SourceFileItems::new(file_id);
let source_file = db.source_file(file_id);

View File

@ -17,7 +17,7 @@ use ra_syntax::{
use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Ty {
/// The primitive boolean type. Written as `bool`.
Bool,
@ -35,8 +35,15 @@ pub enum Ty {
/// A primitive floating-point type. For example, `f64`.
Float(primitive::FloatTy),
// Structures, enumerations and unions.
// Adt(AdtDef, Substs),
/// Structures, enumerations and unions.
Adt {
/// The DefId of the struct/enum.
def_id: DefId,
/// The name, for displaying.
name: SmolStr,
// later we'll need generic substitutions here
},
/// The pointee of a string slice. Written as `str`.
Str,
@ -107,45 +114,48 @@ pub enum Ty {
type TyRef = Arc<Ty>;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FnSig {
input: Vec<Ty>,
output: Ty,
}
impl Ty {
pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> {
pub(crate) fn new(
db: &impl HirDatabase,
module: &Module,
node: ast::TypeRef,
) -> Cancelable<Self> {
use ra_syntax::ast::TypeRef::*;
Ok(match node {
ParenType(_inner) => Ty::Unknown, // TODO
TupleType(_inner) => Ty::Unknown, // TODO
NeverType(..) => Ty::Never,
PathType(inner) => {
let path = if let Some(p) = inner.path() {
let path = if let Some(p) = inner.path().and_then(Path::from_ast) {
p
} else {
return Ok(Ty::Unknown);
};
if path.qualifier().is_none() {
let name = path
.segment()
.and_then(|s| s.name_ref())
.map(|n| n.text())
.unwrap_or(SmolStr::new(""));
if path.is_ident() {
let name = &path.segments[0];
if let Some(int_ty) = primitive::IntTy::from_string(&name) {
Ty::Int(int_ty)
return Ok(Ty::Int(int_ty));
} else if let Some(uint_ty) = primitive::UintTy::from_string(&name) {
Ty::Uint(uint_ty)
return Ok(Ty::Uint(uint_ty));
} else if let Some(float_ty) = primitive::FloatTy::from_string(&name) {
Ty::Float(float_ty)
} else {
// TODO
Ty::Unknown
return Ok(Ty::Float(float_ty));
}
} else {
// TODO
Ty::Unknown
}
// Resolve in module (in type namespace)
let resolved = if let Some(r) = module.resolve_path(db, path)? {
r
} else {
return Ok(Ty::Unknown);
};
let ty = db.type_for_def(resolved)?;
ty
}
PointerType(_inner) => Ty::Unknown, // TODO
ArrayType(_inner) => Ty::Unknown, // TODO
@ -189,6 +199,7 @@ impl fmt::Display for Ty {
}
write!(f, ") -> {}", sig.output)
}
Ty::Adt { name, .. } => write!(f, "{}", name),
Ty::Unknown => write!(f, "[unknown]"),
}
}
@ -196,6 +207,7 @@ impl fmt::Display for Ty {
pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
let syntax = f.syntax(db);
let module = f.module(db)?;
let node = syntax.borrowed();
// TODO we ignore type parameters for now
let input = node
@ -204,7 +216,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
pl.params()
.map(|p| {
p.type_ref()
.map(|t| Ty::new(db, t))
.map(|t| Ty::new(db, &module, t))
.unwrap_or(Ok(Ty::Unknown))
})
.collect()
@ -213,7 +225,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
let output = node
.ret_type()
.and_then(|rt| rt.type_ref())
.map(|t| Ty::new(db, t))
.map(|t| Ty::new(db, &module, t))
.unwrap_or(Ok(Ty::Unknown))?;
let sig = FnSig { input, output };
Ok(Ty::FnPtr(Arc::new(sig)))
@ -232,6 +244,14 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
Ok(Ty::Unknown)
}
Def::Function(f) => type_for_fn(db, f),
Def::Struct(s) => Ok(Ty::Adt {
def_id,
name: s.name(db)?,
}),
Def::Enum(e) => Ok(Ty::Adt {
def_id,
name: e.name(db)?,
}),
Def::Item => {
log::debug!("trying to get type for item of unknown type {:?}", def_id);
Ok(Ty::Unknown)
@ -492,7 +512,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
};
let cast_ty = e
.type_ref()
.map(|t| Ty::new(self.db, t))
.map(|t| Ty::new(self.db, &self.module, t))
.unwrap_or(Ok(Ty::Unknown))?;
// TODO do the coercion...
cast_ty
@ -526,7 +546,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
match stmt {
ast::Stmt::LetStmt(stmt) => {
let decl_ty = if let Some(type_ref) = stmt.type_ref() {
Ty::new(self.db, type_ref)?
Ty::new(self.db, &self.module, type_ref)?
} else {
Ty::Unknown
};
@ -576,7 +596,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
continue;
};
if let Some(type_ref) = param.type_ref() {
let ty = Ty::new(db, type_ref)?;
let ty = Ty::new(db, &ctx.module, type_ref)?;
ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
} else {
// TODO self param

View File

@ -68,6 +68,29 @@ fn test() {
);
}
#[test]
fn infer_struct() {
check_inference(
r#"
struct A {
b: B,
c: C,
}
struct B;
struct C(usize);
fn test() {
let c = C(1);
B;
let a: A = A { b: B, c: C() };
a.b;
a.c;
}
"#,
"0004_struct.txt",
);
}
fn infer(content: &str) -> String {
let (db, _, file_id) = MockDatabase::with_single_file(content);
let source_file = db.source_file(file_id);

View File

@ -0,0 +1,10 @@
[86; 90) 'C(1)': [unknown]
[72; 153) '{ ...a.c; }': ()
[86; 87) 'C': C
[107; 108) 'a': A
[114; 132) 'A { b:... C() }': [unknown]
[138; 141) 'a.b': [unknown]
[147; 150) 'a.c': [unknown]
[96; 97) 'B': B
[88; 89) '1': [unknown]
[82; 83) 'c': [unknown]