diff --git a/cl-ast/src/lib.rs b/cl-ast/src/lib.rs index 0824635..c333f40 100644 --- a/cl-ast/src/lib.rs +++ b/cl-ast/src/lib.rs @@ -37,7 +37,7 @@ pub struct File { } // Metadata decorators -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Attrs { pub meta: Vec, } diff --git a/cl-repl/Cargo.toml b/cl-repl/Cargo.toml index 9cc11d3..e8caf3f 100644 --- a/cl-repl/Cargo.toml +++ b/cl-repl/Cargo.toml @@ -20,3 +20,4 @@ argh = "0.1.12" [dev-dependencies] cl-structures = { path = "../cl-structures" } +cl-typeck = { path = "../cl-typeck" } diff --git a/cl-repl/examples/typeck.rs b/cl-repl/examples/typeck.rs new file mode 100644 index 0000000..3302886 --- /dev/null +++ b/cl-repl/examples/typeck.rs @@ -0,0 +1,109 @@ +use cl_lexer::Lexer; +use cl_parser::Parser; +use cl_repl::repline::{error::Error as RlError, Repline}; +use cl_typeck::{name_collector::NameCollector, project::Project}; +use std::error::Error; + +fn main() -> Result<(), Box> { + let mut prj = Project::default(); + let mut tcol = NameCollector::new(&mut prj); + + println!( + "--- {} v{} 💪🦈 ---", + env!("CARGO_BIN_NAME"), + env!("CARGO_PKG_VERSION"), + ); + + read_and( + "\x1b[33m", + "cl>", + "? >", + |line| -> Result<_, Box> { + if line.trim_start().is_empty() { + query(&tcol)?; + return Ok(Response::Deny); + } + let mut parser = Parser::new(Lexer::new(line)); + let code = match parser.file() { + Ok(code) => code, + Err(e) => Err(e)?, + }; + tcol.file(&code)?; + Ok(Response::Accept) + }, + ) +} + +pub enum Response { + Accept, + Deny, + Break, +} + +fn read_and( + color: &str, + begin: &str, + again: &str, + mut f: impl FnMut(&str) -> Result>, +) -> Result<(), Box> { + let mut rl = Repline::new(color, begin, again); + loop { + let line = match rl.read() { + Err(RlError::CtrlC(_)) => break, + Err(RlError::CtrlD(line)) => { + rl.deny(); + line + } + Ok(line) => line, + Err(e) => Err(e)?, + }; + print!("\x1b[G\x1b[J"); + match f(&line) { + Ok(Response::Accept) => rl.accept(), + Ok(Response::Deny) => rl.deny(), + Ok(Response::Break) => break, + Err(e) => print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m"), + } + } + Ok(()) +} + +fn query(prj: &Project) -> Result<(), Box> { + use cl_typeck::{ + definition::{Def, DefKind}, + type_kind::TypeKind, + }; + read_and("\x1b[35m", "qy>", "? >", |line| { + if line.trim_start().is_empty() { + return Ok(Response::Break); + } + match line { + "$all\n" => println!("{prj:#?}"), + _ => { + // parse it as a path, and convert the path into a borrowed path + let path = Parser::new(Lexer::new(line)).path()?; + + let Some((type_id, path)) = prj.get_type((&path).into(), prj.module_root) else { + return Ok(Response::Deny); + }; + let Def { name, vis, meta: _, kind, source: _, module } = &prj[type_id]; + match (kind, prj.get_value(path, type_id)) { + (_, Some((val, path))) => { + println!("value {}; {path}\n{:#?}", usize::from(val), prj[val]) + } + (DefKind::Type(TypeKind::Module), None) => println!( + "{vis}mod \"{name}\" (#{}); {path}\n{:#?}", + usize::from(type_id), + module + ), + (_, None) => println!( + "type {name}(#{}); {path}\n{:#?}", + usize::from(type_id), + prj.pool[type_id] + ), + }; + } + } + Ok(Response::Accept) + }) +} diff --git a/cl-structures/src/intern_pool.rs b/cl-structures/src/intern_pool.rs index ccd1f4d..dbba9bf 100644 --- a/cl-structures/src/intern_pool.rs +++ b/cl-structures/src/intern_pool.rs @@ -89,6 +89,13 @@ impl Pool { self.pool.get_mut(index.get()) } + pub fn iter(&self) -> impl Iterator { + self.pool.iter() + } + pub fn iter_mut(&mut self) -> impl Iterator { + self.pool.iter_mut() + } + pub fn insert(&mut self, value: T) -> ID { let id = self.pool.len(); self.pool.push(value); diff --git a/cl-typeck/src/lib.rs b/cl-typeck/src/lib.rs index a7a6357..ad7a738 100644 --- a/cl-typeck/src/lib.rs +++ b/cl-typeck/src/lib.rs @@ -1,7 +1,7 @@ //! # The Conlang Type Checker //! //! As a statically typed language, Conlang requires a robust type checker to enforce correctness. - +#![feature(debug_closure_helpers)] #![warn(clippy::all)] /* @@ -19,50 +19,161 @@ pub mod key { // define the index types make_intern_key! { - /// Uniquely represents a TypeDef in the TypeDef [Pool] - TypeID, - /// Uniquely represents a ValueDef in the ValueDef [Pool] - ValueID, - /// Uniquely represents a Module in the Module [Pool] - ModuleID, + /// Uniquely represents a [Def][1] in the [Def][1] [Pool] + /// + /// [1]: crate::definition::Def + DefID, } } -pub mod typedef { - //! A TypeDef represents an item in the Type Namespace (a component of a - //! [Project](crate::project::Project)). +pub mod definition { + use crate::{key::DefID, module::Module, type_kind::TypeKind, value_kind::ValueKind}; + use cl_ast::{format::FmtPretty, Item, Meta, Visibility}; + use std::fmt::Write; - use crate::key::{TypeID, ValueID}; - use cl_ast::{Item, Visibility}; - - /// A TypeDef represents an item in the Type Namespace (a component of a - /// [Project](crate::project::Project)). - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct TypeDef { + #[derive(Clone, PartialEq, Eq)] + pub struct Def { pub name: String, - /// The expanded form of the definition, with all fields properly typed. - /// This is filled in once all types are been enumerated. - pub kind: Option, - pub definition: Item, - pub associated_values: Vec, - // TODO: Generic type parameters + pub vis: Visibility, + pub meta: Vec, + pub kind: DefKind, + pub source: Option, + pub module: Module, + } + + impl Default for Def { + fn default() -> Self { + Self { + name: Default::default(), + vis: Default::default(), + meta: Default::default(), + kind: DefKind::Type(TypeKind::Module), + source: Default::default(), + module: Default::default(), + } + } + } + + impl Def { + pub fn new_module( + name: String, + vis: Visibility, + meta: Vec, + parent: Option, + ) -> Self { + Self { + name, + vis, + meta, + kind: DefKind::Type(TypeKind::Module), + source: None, + module: Module { parent, ..Default::default() }, + } + } + } + + impl std::fmt::Debug for Def { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { name, vis, meta, kind, source, module } = self; + f.debug_struct("Def") + .field("name", &name) + .field("vis", &vis) + .field_with("meta", |f| write!(f, "{meta:?}")) + .field("kind", &kind) + .field_with("source", |f| match source { + Some(item) => write!(f.pretty(), "{{\n{item}\n}}"), + None => todo!(), + }) + .field("module", &module) + .finish() + } } + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum DefKind { + /// A type, such as a `` + Type(TypeKind), + /// A value, such as a `const`, `static`, or `fn` + Value(ValueKind), + } + + impl DefKind { + pub fn is_type(&self) -> bool { + matches!(self, Self::Type(_)) + } + pub fn ty(&self) -> Option<&TypeKind> { + match self { + DefKind::Type(t) => Some(t), + _ => None, + } + } + pub fn is_value(&self) -> bool { + matches!(self, Self::Value(_)) + } + pub fn value(&self) -> Option<&ValueKind> { + match self { + DefKind::Value(v) => Some(v), + _ => None, + } + } + } +} + +pub mod type_kind { + //! A [TypeKind] represents an item in the Type Namespace + //! (a component of a [Project](crate::project::Project)). + + use cl_ast::Visibility; + use std::{fmt::Debug, str::FromStr}; + + use crate::key::DefID; + + /// The kind of a type #[derive(Clone, Debug, PartialEq, Eq)] pub enum TypeKind { + /// A type which has not yet been resolved + Undecided, + /// An alias for an already-defined type + Alias(Option), /// A primitive type, built-in to the compiler Intrinsic(Intrinsic), - /// A user-defined structural product type - Struct(Vec<(String, Visibility, TypeID)>), - /// A user-defined union-like enum type - Enum(Vec<(String, TypeID)>), - /// An alias for an already-defined type - Alias(TypeID), + /// A user-defined abstract data type + Adt(Adt), + /// A reference to an already-defined type: &T + Ref(DefID), + /// A contiguous view of dynamically sized memory + Slice(DefID), + /// A function pointer which accepts multiple inputs and produces an output + FnPtr { args: Vec, rety: DefID }, /// The unit type Empty, + /// The never type + Never, /// The Self type SelfTy, - // TODO: union types, tuples, tuple structs maybe? + /// An untyped module + Module, + } + + /// A user-defined Abstract Data Type + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum Adt { + /// A union-like enum type + Enum(Vec<(String, DefID)>), + CLikeEnum(Vec<(String, u128)>), + /// An enum with no fields, which can never be constructed + FieldlessEnum, + + /// A structural product type with named members + Struct(Vec<(String, Visibility, DefID)>), + /// A structural product type with unnamed members + TupleStruct(Vec<(Visibility, DefID)>), + /// A structural product type of neither named nor unnamed members + UnitStruct, + + /// A choose your own undefined behavior type + /// TODO: should unions be a language feature? + Union(Vec<(String, DefID)>), } /// The set of compiler-intrinsic types. @@ -70,37 +181,70 @@ pub mod typedef { #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum Intrinsic { - /// A 32-bit two's complement integer - i32, - /// A 32-bit unsigned integer - u32, - /// A boolean (`true` or `false`) - bool, + /// An 8-bit signed integer: `#[intrinsic = "i8"]` + I8, + /// A 16-bit signed integer: `#[intrinsic = "i16"]` + I16, + /// A 32-bit signed integer: `#[intrinsic = "i32"]` + I32, + /// A 64-bit signed integer: `#[intrinsic = "i32"]` + I64, + // /// A 128-bit signed integer: `#[intrinsic = "i32"]` + // I128, + /// An 8-bit unsigned integer: `#[intrinsic = "u8"]` + U8, + /// A 16-bit unsigned integer: `#[intrinsic = "u16"]` + U16, + /// A 32-bit unsigned integer: `#[intrinsic = "u32"]` + U32, + /// A 64-bit unsigned integer: `#[intrinsic = "u64"]` + U64, + // /// A 128-bit unsigned integer: `#[intrinsic = "u128"]` + // U128, + /// A boolean (`true` or `false`): `#[intrinsic = "bool"]` + Bool, + } + + impl FromStr for Intrinsic { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "i8" => Intrinsic::I8, + "i16" => Intrinsic::I16, + "i32" => Intrinsic::I32, + "i64" => Intrinsic::I64, + "u8" => Intrinsic::U8, + "u16" => Intrinsic::U16, + "u32" => Intrinsic::U32, + "u64" => Intrinsic::U64, + "bool" => Intrinsic::Bool, + _ => Err(())?, + }) + } + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum Float { + F32 = 0x20, + F64, } } -pub mod valdef { - //! A [ValueDef] represents an item in the Value Namespace (a component of a - //! [Project](crate::project::Project)). +pub mod value_kind { + //! A [ValueKind] represents an item in the Value Namespace + //! (a component of a [Project](crate::project::Project)). use crate::typeref::TypeRef; - use cl_ast::{Block, Item}; - - /// A [ValueDef] represents an item in the Value Namespace (a component of a - /// [Project](crate::project::Project)). - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct ValueDef { - pub name: String, - /// The expanded form of the definition, with all fields properly typed - pub kind: Option, - pub definition: Item, - } + use cl_ast::Block; #[derive(Clone, Debug, PartialEq, Eq)] pub enum ValueKind { + Undecided, Const(TypeRef), Static(TypeRef), Fn { + // TODO: Store the variable bindings here! args: Vec, rety: TypeRef, body: Block, @@ -111,57 +255,532 @@ pub mod valdef { pub mod module { //! A [Module] is a node in the Module Tree (a component of a //! [Project](crate::project::Project)) - use crate::key::{ModuleID, TypeID, ValueID}; + use crate::key::DefID; use std::collections::HashMap; /// A [Module] is a node in the Module Tree (a component of a /// [Project](crate::project::Project)). #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Module { - pub parent: Option, - pub types: HashMap, - pub values: HashMap, - pub submodules: HashMap, + pub parent: Option, + pub types: HashMap, + pub values: HashMap, + } + + impl Module { + pub fn new(parent: DefID) -> Self { + Self { parent: Some(parent), ..Default::default() } + } + } +} + +pub mod path { + use cl_ast::{Path as AstPath, PathPart}; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct Path<'p> { + pub absolute: bool, + pub parts: &'p [PathPart], + } + + impl<'p> Path<'p> { + pub fn new(path: &'p AstPath) -> Self { + let AstPath { absolute, parts } = path; + Self { absolute: *absolute, parts } + } + pub fn relative(self) -> Self { + Self { absolute: false, ..self } + } + pub fn pop_front(self) -> Option { + let Self { absolute, parts } = self; + Some(Self { absolute, parts: parts.get(1..)? }) + } + pub fn is_empty(&self) -> bool { + self.parts.is_empty() + } + pub fn len(&self) -> usize { + self.parts.len() + } + pub fn front(&self) -> Option<&PathPart> { + self.parts.first() + } + } + + impl<'p> From<&'p AstPath> for Path<'p> { + fn from(value: &'p AstPath) -> Self { + Self::new(value) + } + } + impl std::fmt::Display for Path<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const SEPARATOR: &str = "::"; + let Self { absolute, parts } = self; + if *absolute { + write!(f, "{SEPARATOR}")? + } + for (idx, part) in parts.iter().enumerate() { + write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?; + } + Ok(()) + } } } pub mod project { + use crate::{ + definition::{Def, DefKind}, + key::DefID, + path::Path, + type_kind::TypeKind, + }; + use cl_ast::{Identifier, PathPart, Visibility}; use cl_structures::intern_pool::Pool; + use std::ops::{Index, IndexMut}; + + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct Project { + pub pool: Pool, + pub module_root: DefID, + } + + impl Project { + pub fn new() -> Self { + Self::default() + } + } + impl Default for Project { + fn default() -> Self { + let mut pool = Pool::default(); + let module_root = pool.insert(Def::default()); + // Insert the Never(!) type + let never = pool.insert(Def { + name: String::from("!"), + vis: Visibility::Public, + kind: DefKind::Type(TypeKind::Never), + ..Default::default() + }); + pool[module_root] + .module + .types + .insert(String::from("!"), never); + Self { pool, module_root } + } + } + + impl Project { + pub fn parent_of(&self, module: DefID) -> Option { + self[module].module.parent + } + pub fn root_of(&self, module: DefID) -> DefID { + match self.parent_of(module) { + Some(module) => self.root_of(module), + None => module, + } + } + /// Resolves a path within a module tree, finding the innermost module. + /// Returns the remaining path parts. + pub fn get_type<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> { + // TODO: Cache module lookups + if path.absolute { + self.get_type(path.relative(), self.root_of(within)) + } else if let Some(front) = path.front() { + let module = &self[within].module; + match front { + PathPart::SelfKw => self.get_type(path.pop_front()?, within), + PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?), + PathPart::Ident(Identifier(name)) => match module.types.get(name) { + Some(&submodule) => self.get_type(path.pop_front()?, submodule), + None => Some((within, path)), + }, + } + } else { + Some((within, path)) + } + } + + pub fn get_value<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> { + match path.front()? { + PathPart::Ident(Identifier(name)) => Some(( + self[within].module.values.get(name).copied()?, + path.pop_front()?, + )), + _ => None, + } + } + + #[rustfmt::skip] + pub fn insert_type(&mut self, name: String, value: Def, parent: DefID) -> Option { + let id = self.pool.insert(value); + self[parent].module.types.insert(name, id) + } + #[rustfmt::skip] + pub fn insert_value(&mut self, name: String, value: Def, parent: DefID) -> Option { + let id = self.pool.insert(value); + self[parent].module.values.insert(name, id) + } + } + + /// Implements [Index] and [IndexMut] for [Project]: `self.table[ID] -> Definition` + macro_rules! impl_index { + ($(self.$table:ident[$idx:ty] -> $out:ty),*$(,)?) => {$( + impl Index<$idx> for Project { + type Output = $out; + fn index(&self, index: $idx) -> &Self::Output { + &self.$table[index] + } + } + impl IndexMut<$idx> for Project { + fn index_mut(&mut self, index: $idx) -> &mut Self::Output { + &mut self.$table[index] + } + } + )*}; + } + impl_index! { + self.pool[DefID] -> Def, + // self.types[TypeID] -> TypeDef, + // self.values[ValueID] -> ValueDef, + } +} + +pub mod name_collector { + //! Performs step 1 of type checking: Collecting all the names of things into [Module] units use crate::{ - key::{ModuleID, TypeID, ValueID}, - module::Module, - typedef::TypeDef, - valdef::ValueDef, + definition::{Def, DefKind}, + key, + project::Project, + type_kind::{Adt, TypeKind}, + value_kind::ValueKind, }; + use cl_ast::*; + use std::ops::{Deref, DerefMut}; - #[derive(Clone, Debug, Default, PartialEq, Eq)] - pub struct Project { - pub types: Pool, - pub values: Pool, - pub modules: Pool, + /// Collects types for future use + #[derive(Debug, PartialEq, Eq)] + pub struct NameCollector<'prj> { + /// A stack of the current modules + pub mod_stack: Vec, + /// The [Project], the type checker and resolver's central data store + pub project: &'prj mut Project, + } + + impl<'prj> NameCollector<'prj> { + pub fn new(project: &'prj mut Project) -> Self { + // create a root module + Self { mod_stack: vec![project.module_root], project } + } + + /// Gets the currently traversed parent module + pub fn parent(&self) -> Option { + self.mod_stack.last().copied() + } + } + + impl Deref for NameCollector<'_> { + type Target = Project; + fn deref(&self) -> &Self::Target { + self.project + } + } + impl DerefMut for NameCollector<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.project + } + } + + impl NameCollector<'_> { + pub fn file(&mut self, f: &File) -> Result<(), &'static str> { + let parent = self.parent().ok_or("No parent to add item to")?; + + for item in &f.items { + let def = match &item.kind { + // modules + // types + ItemKind::Module(_) => { + self.module(item)?; + continue; + } + ItemKind::Enum(_) => Some(self.ty_enum(item)?), + ItemKind::Alias(_) => Some(self.ty_alias(item)?), + ItemKind::Struct(_) => Some(self.ty_struct(item)?), + // values processed by the value collector + ItemKind::Const(_) => Some(self.val_const(item)?), + ItemKind::Static(_) => Some(self.val_static(item)?), + ItemKind::Function(_) => Some(self.val_function(item)?), + ItemKind::Impl(_) => None, + }; + let Some(def) = def else { continue }; + match def.kind { + DefKind::Type(_) => { + if let Some(v) = self.insert_type(def.name.clone(), def, parent) { + panic!("Redefinition of type {} ({v:?})!", self[v].name) + } + } + DefKind::Value(_) => { + if let Some(v) = self.insert_value(def.name.clone(), def, parent) { + panic!("Redefinition of value {} ({v:?})!", self[v].name) + } + } + } + } + Ok(()) + } + + /// Collects a [Module] + pub fn module(&mut self, m: &Item) -> Result<(), &'static str> { + let Item { kind: ItemKind::Module(Module { name, kind }), vis, attrs, .. } = m else { + Err("module called on Item which was not an ItemKind::Module")? + }; + let ModuleKind::Inline(kind) = kind else { + Err("Out-of-line modules not yet supported")? + }; + let parent = self.parent().ok_or("No parent to add module to")?; + + let module = self.pool.insert(Def::new_module( + name.0.clone(), + *vis, + attrs.meta.clone(), + Some(parent), + )); + + self[parent] + .module + .types + .insert(name.0.clone(), module) + .is_some() + .then(|| panic!("Error: redefinition of module {name}")); + + self.mod_stack.push(module); + let out = self.file(kind); + self.mod_stack.pop(); + + out + } + } + /// Type collection + impl NameCollector<'_> { + /// Collects an [Item] of type [ItemKind::Enum] + pub fn ty_enum(&mut self, item: &Item) -> Result { + let Item { kind: ItemKind::Enum(Enum { name, kind }), vis, attrs, .. } = item else { + Err("Enum called on item which was not ItemKind::Enum")? + }; + let kind = match kind { + EnumKind::NoVariants => DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)), + EnumKind::Variants(_) => DefKind::Type(TypeKind::Undecided), + }; + + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind, + source: Some(item.clone()), + module: Default::default(), + }) + } + + /// Collects an [Item] of type [ItemKind::Alias] + pub fn ty_alias(&mut self, item: &Item) -> Result { + let Item { kind: ItemKind::Alias(Alias { to: name, from }), vis, attrs, .. } = item + else { + Err("Alias called on Item which was not ItemKind::Alias")? + }; + + let mut kind = match from { + Some(_) => DefKind::Type(TypeKind::Undecided), + None => DefKind::Type(TypeKind::Alias(None)), + }; + + for meta in &attrs.meta { + let Meta { name: meta_name, kind: meta_kind } = meta; + match (meta_name.0.as_str(), meta_kind) { + ("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => { + kind = DefKind::Type(TypeKind::Intrinsic( + intrinsic.parse().map_err(|_| "unknown intrinsic type")?, + )); + } + ("intrinsic", MetaKind::Plain) => { + kind = DefKind::Type(TypeKind::Intrinsic( + name.0.parse().map_err(|_| "Unknown intrinsic type")?, + )) + } + _ => {} + } + } + + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind, + source: Some(item.clone()), + module: Default::default(), + }) + } + + /// Collects an [Item] of type [ItemKind::Struct] + pub fn ty_struct(&mut self, item: &Item) -> Result { + let Item { kind: ItemKind::Struct(Struct { name, kind }), vis, attrs, .. } = item + else { + Err("Struct called on item which was not ItemKind::Struct")? + }; + let kind = match kind { + StructKind::Empty => DefKind::Type(TypeKind::Adt(Adt::UnitStruct)), + StructKind::Tuple(_) => DefKind::Type(TypeKind::Undecided), + StructKind::Struct(_) => DefKind::Type(TypeKind::Undecided), + }; + + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind, + source: Some(item.clone()), + module: Default::default(), + }) + } + } + /// Value collection + impl NameCollector<'_> { + pub fn val_const(&mut self, item: &Item) -> Result { + let Item { kind: ItemKind::Const(Const { name, .. }), vis, attrs, .. } = item else { + Err("Const called on Item which was not ItemKind::Const")? + }; + + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind: DefKind::Value(ValueKind::Undecided), + source: Some(item.clone()), + module: Default::default(), + }) + } + + pub fn val_static(&mut self, item: &Item) -> Result { + let Item { kind: ItemKind::Static(Static { name, .. }), vis, attrs, .. } = item else { + Err("Static called on Item which was not ItemKind::Static")? + }; + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind: DefKind::Type(TypeKind::Undecided), + source: Some(item.clone()), + module: Default::default(), + }) + } + + pub fn val_function(&mut self, item: &Item) -> Result { + // TODO: treat function bodies like modules with internal items + let Item { kind: ItemKind::Function(Function { name, .. }), vis, attrs, .. } = item + else { + Err("val_function called on Item which was not ItemKind::Function")? + }; + Ok(Def { + name: name.0.clone(), + vis: *vis, + meta: attrs.meta.clone(), + kind: DefKind::Value(ValueKind::Undecided), + source: Some(item.clone()), + module: Default::default(), + }) + } + } +} + +pub mod type_resolver { + //! Performs step 2 of type checking: Evaluating type definitions + #![allow(unused)] + use std::ops::{Deref, DerefMut}; + + use cl_ast::*; + + use crate::{definition::Def, key::DefID, project::Project}; + + pub struct TypeResolver<'prj> { + pub project: &'prj mut Project, + } + + impl Deref for TypeResolver<'_> { + type Target = Project; + fn deref(&self) -> &Self::Target { + self.project + } + } + impl DerefMut for TypeResolver<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.project + } + } + + impl TypeResolver<'_> { + pub fn resolve(&mut self) -> Result { + #![allow(unused)] + for typedef in self.pool.iter_mut().filter(|v| v.kind.is_type()) { + let Def { name, vis, meta: attr, kind, source: Some(ref definition), module: _ } = + typedef + else { + continue; + }; + match &definition.kind { + ItemKind::Alias(Alias { to: _, from: Some(from) }) => match &from.kind { + TyKind::Never => todo!(), + TyKind::Empty => todo!(), + TyKind::SelfTy => todo!(), + TyKind::Path(_) => todo!(), + TyKind::Tuple(_) => todo!(), + TyKind::Ref(_) => todo!(), + TyKind::Fn(_) => todo!(), + }, + ItemKind::Alias(_) => {} + ItemKind::Const(_) => todo!(), + ItemKind::Static(_) => todo!(), + ItemKind::Module(_) => todo!(), + ItemKind::Function(_) => {} + ItemKind::Struct(_) => {} + ItemKind::Enum(_) => {} + ItemKind::Impl(_) => {} + } + } + Ok(true) + } + + pub fn get_type(&self, kind: &TyKind) -> Option { + match kind { + TyKind::Never => todo!(), + TyKind::Empty => todo!(), + TyKind::SelfTy => todo!(), + TyKind::Path(_) => todo!(), + TyKind::Tuple(_) => todo!(), + TyKind::Ref(_) => todo!(), + TyKind::Fn(_) => todo!(), + } + None + } } } pub mod typeref { - //! Stores type and referencce info + //! Stores type and reference info - use crate::key::TypeID; + use crate::key::DefID; /// The Type struct represents all valid types, and can be trivially equality-compared #[derive(Clone, Debug, PartialEq, Eq)] pub struct TypeRef { /// You can only have a pointer chain 65535 pointers long. ref_depth: u16, - /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete) + /// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete) kind: RefKind, } - /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete) + /// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete) #[derive(Clone, Debug, PartialEq, Eq)] pub enum RefKind { - /// A Concrete type has an associated [TypeDef](super::typedef::TypeDef) - Concrete(TypeID), + /// A Concrete type has an associated [Def](super::definition::Def) + Concrete(DefID), /// A Generic type is a *locally unique* comparable value, /// valid only until the end of its typing context. /// This is usually the surrounding function. @@ -233,7 +852,58 @@ let rules: Hashmap> { */ -/* - Potential solution: - Store reference to type field of each type expression in the AST -*/ +pub mod rule { + use crate::{key::DefID, typeref::TypeRef}; + + pub struct Rule { + /// What is this Rule for? + pub operation: (), + /// What inputs does it take? + pub inputs: Vec, + /// What output does it produce? + pub output: TypeRef, + /// Where did this rule come from? + pub through: Origin, + } + + // TODO: Genericize + pub enum Operation { + Mul, + Div, + Rem, + Add, + Sub, + + Deref, + Neg, + Not, + At, + Tilde, + + Index, + + If, + While, + For, + } + + pub enum Origin { + /// This rule is built into the compiler + Intrinsic, + /// This rule is derived from an implementation on a type + Extrinsic(DefID), + } +} + +pub mod typeck { + #![allow(unused)] + use cl_ast::*; + + pub struct Context { + rules: (), + } + + trait TypeCheck {} +} + +//