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 submodules() for hir::db::SubmodulesQuery;
fn infer() for hir::db::InferQuery; fn infer() for hir::db::InferQuery;
fn type_for_def() for hir::db::TypeForDefQuery; 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, module::{ModuleId, ModuleTree, ModuleSource,
nameres::{ItemMap, InputModuleItems}}, nameres::{ItemMap, InputModuleItems}},
ty::{InferenceResult, Ty}, ty::{InferenceResult, Ty},
adt::{StructData, EnumData},
}; };
salsa::query_group! { salsa::query_group! {
@ -31,6 +32,16 @@ pub trait HirDatabase: SyntaxDatabase
use fn query_definitions::fn_syntax; 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>> { fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
type InferQuery; type InferQuery;
use fn query_definitions::infer; use fn query_definitions::infer;

View file

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

View file

@ -193,6 +193,8 @@ salsa::database_storage! {
fn submodules() for db::SubmodulesQuery; fn submodules() for db::SubmodulesQuery;
fn infer() for db::InferQuery; fn infer() for db::InferQuery;
fn type_for_def() for db::TypeForDefQuery; 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, imp::Submodule,
nameres::{InputModuleItems, ItemMap, Resolver}, nameres::{InputModuleItems, ItemMap, Resolver},
}, },
ty::{self, InferenceResult, Ty} ty::{self, InferenceResult, Ty},
adt::{StructData, EnumData},
}; };
/// Resolve `FnId` to the corresponding `SyntaxNode` /// 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) 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> { pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
let mut res = SourceFileItems::new(file_id); let mut res = SourceFileItems::new(file_id);
let source_file = db.source_file(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}; 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 { pub enum Ty {
/// The primitive boolean type. Written as `bool`. /// The primitive boolean type. Written as `bool`.
Bool, Bool,
@ -35,8 +35,15 @@ pub enum Ty {
/// A primitive floating-point type. For example, `f64`. /// A primitive floating-point type. For example, `f64`.
Float(primitive::FloatTy), Float(primitive::FloatTy),
// Structures, enumerations and unions. /// Structures, enumerations and unions.
// Adt(AdtDef, Substs), 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`. /// The pointee of a string slice. Written as `str`.
Str, Str,
@ -107,45 +114,48 @@ pub enum Ty {
type TyRef = Arc<Ty>; type TyRef = Arc<Ty>;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FnSig { pub struct FnSig {
input: Vec<Ty>, input: Vec<Ty>,
output: Ty, output: Ty,
} }
impl 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::*; use ra_syntax::ast::TypeRef::*;
Ok(match node { Ok(match node {
ParenType(_inner) => Ty::Unknown, // TODO ParenType(_inner) => Ty::Unknown, // TODO
TupleType(_inner) => Ty::Unknown, // TODO TupleType(_inner) => Ty::Unknown, // TODO
NeverType(..) => Ty::Never, NeverType(..) => Ty::Never,
PathType(inner) => { PathType(inner) => {
let path = if let Some(p) = inner.path() { let path = if let Some(p) = inner.path().and_then(Path::from_ast) {
p p
} else { } else {
return Ok(Ty::Unknown); return Ok(Ty::Unknown);
}; };
if path.qualifier().is_none() { if path.is_ident() {
let name = path let name = &path.segments[0];
.segment()
.and_then(|s| s.name_ref())
.map(|n| n.text())
.unwrap_or(SmolStr::new(""));
if let Some(int_ty) = primitive::IntTy::from_string(&name) { 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) { } 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) { } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) {
Ty::Float(float_ty) return Ok(Ty::Float(float_ty));
} else {
// TODO
Ty::Unknown
} }
} 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 PointerType(_inner) => Ty::Unknown, // TODO
ArrayType(_inner) => Ty::Unknown, // TODO ArrayType(_inner) => Ty::Unknown, // TODO
@ -189,6 +199,7 @@ impl fmt::Display for Ty {
} }
write!(f, ") -> {}", sig.output) write!(f, ") -> {}", sig.output)
} }
Ty::Adt { name, .. } => write!(f, "{}", name),
Ty::Unknown => write!(f, "[unknown]"), 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> { pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
let syntax = f.syntax(db); let syntax = f.syntax(db);
let module = f.module(db)?;
let node = syntax.borrowed(); let node = syntax.borrowed();
// TODO we ignore type parameters for now // TODO we ignore type parameters for now
let input = node let input = node
@ -204,7 +216,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
pl.params() pl.params()
.map(|p| { .map(|p| {
p.type_ref() p.type_ref()
.map(|t| Ty::new(db, t)) .map(|t| Ty::new(db, &module, t))
.unwrap_or(Ok(Ty::Unknown)) .unwrap_or(Ok(Ty::Unknown))
}) })
.collect() .collect()
@ -213,7 +225,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
let output = node let output = node
.ret_type() .ret_type()
.and_then(|rt| rt.type_ref()) .and_then(|rt| rt.type_ref())
.map(|t| Ty::new(db, t)) .map(|t| Ty::new(db, &module, t))
.unwrap_or(Ok(Ty::Unknown))?; .unwrap_or(Ok(Ty::Unknown))?;
let sig = FnSig { input, output }; let sig = FnSig { input, output };
Ok(Ty::FnPtr(Arc::new(sig))) 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) Ok(Ty::Unknown)
} }
Def::Function(f) => type_for_fn(db, f), 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 => { Def::Item => {
log::debug!("trying to get type for item of unknown type {:?}", def_id); log::debug!("trying to get type for item of unknown type {:?}", def_id);
Ok(Ty::Unknown) Ok(Ty::Unknown)
@ -492,7 +512,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}; };
let cast_ty = e let cast_ty = e
.type_ref() .type_ref()
.map(|t| Ty::new(self.db, t)) .map(|t| Ty::new(self.db, &self.module, t))
.unwrap_or(Ok(Ty::Unknown))?; .unwrap_or(Ok(Ty::Unknown))?;
// TODO do the coercion... // TODO do the coercion...
cast_ty cast_ty
@ -526,7 +546,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
match stmt { match stmt {
ast::Stmt::LetStmt(stmt) => { ast::Stmt::LetStmt(stmt) => {
let decl_ty = if let Some(type_ref) = stmt.type_ref() { 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 { } else {
Ty::Unknown Ty::Unknown
}; };
@ -576,7 +596,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
continue; continue;
}; };
if let Some(type_ref) = param.type_ref() { 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); ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
} else { } else {
// TODO self param // 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 { fn infer(content: &str) -> String {
let (db, _, file_id) = MockDatabase::with_single_file(content); let (db, _, file_id) = MockDatabase::with_single_file(content);
let source_file = db.source_file(file_id); 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]