From 9e90eea7b6058cf741794be3a0d85ec339e5d807 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 16 Apr 2024 23:45:24 -0500 Subject: [PATCH] cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could! --- cl-repl/examples/typeck.rs | 194 ++-- cl-typeck/src/definition/display.rs | 162 ++++ cl-typeck/src/lib.rs | 1334 ++++++++++++++++++--------- stdlib/lib.cl | 96 +- 4 files changed, 1265 insertions(+), 521 deletions(-) create mode 100644 cl-typeck/src/definition/display.rs diff --git a/cl-repl/examples/typeck.rs b/cl-repl/examples/typeck.rs index 3302886..e8694c1 100644 --- a/cl-repl/examples/typeck.rs +++ b/cl-repl/examples/typeck.rs @@ -1,37 +1,37 @@ 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 cl_typeck::{ + definition::Def, name_collector::NameCollectable, project::Project, type_resolver::resolve, +}; use std::error::Error; +const STDLIB_PATH: &str = "stdlib/lib.cl"; +const STDLIB: &str = include_str!("../../stdlib/lib.cl"); + +const C_MAIN: &str = "\x1b[30m"; +const C_RESV: &str = "\x1b[35m"; +const C_CODE: &str = "\x1b[36m"; + +/// A home for immutable intermediate ASTs +/// +/// TODO: remove this. +static mut TREES: TreeManager = TreeManager::new(); + 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"), - ); + let mut parser = Parser::new(Lexer::new(STDLIB)); + let code = match parser.file() { + Ok(code) => code, + Err(e) => { + eprintln!("{STDLIB_PATH}:{e}"); + Err(e)? + } + }; + unsafe { TREES.push(code) }.collect_in_root(&mut prj)?; - 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) - }, - ) + main_menu(&mut prj) } pub enum Response { @@ -43,10 +43,9 @@ pub enum Response { 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); + let mut rl = Repline::new(color, begin, "? >"); loop { let line = match rl.read() { Err(RlError::CtrlC(_)) => break, @@ -68,42 +67,117 @@ fn read_and( 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() { +fn main_menu(prj: &mut Project) -> Result<(), Box> { + banner(); + read_and(C_MAIN, "mu>", |line| { + match line.trim() { + "c" | "code" => enter_code(prj), + "clear" => clear(), + "e" | "exit" => return Ok(Response::Break), + "l" | "list" => list_types(prj), + "q" | "query" => query_type_expression(prj), + "r" | "resolve" => resolve_all(prj), + "h" | "help" => { + println!( + "Valid commands are: + code (c): Enter code to type-check + list (l): List all known types + query (q): Query the type system + resolve (r): Perform type resolution + help (h): Print this list + exit (e): Exit the program" + ); + return Ok(Response::Deny); + } + _ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?, + } + .map(|_| Response::Accept) + }) +} + +fn enter_code(prj: &mut Project) -> Result<(), Box> { + read_and(C_CODE, "cl>", |line| { + if line.trim().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 code = Parser::new(Lexer::new(line)).file()?; + + // Safety: this is totally unsafe + unsafe { TREES.push(code) }.collect_in_root(prj)?; - 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) }) } + +fn query_type_expression(prj: &mut Project) -> Result<(), Box> { + read_and(C_RESV, "ty>", |line| { + if line.trim().is_empty() { + return Ok(Response::Break); + } + // parse it as a path, and convert the path into a borrowed path + let ty = Parser::new(Lexer::new(line)).ty()?.kind; + let id = prj.evaluate(&ty, prj.root)?; + pretty_def(&prj[id], id); + Ok(Response::Accept) + }) +} + +fn resolve_all(prj: &mut Project) -> Result<(), Box> { + for id in prj.pool.key_iter() { + resolve(prj, id)?; + } + println!("Types resolved successfully!"); + Ok(()) +} + +fn list_types(prj: &mut Project) -> Result<(), Box> { + println!(" name\x1b[30G type"); + for (idx, Def { name, vis, kind, .. }) in prj.pool.iter().enumerate() { + print!("{idx:3}: {vis}"); + if name.is_empty() { + print!("\x1b[30m_\x1b[0m") + } + println!("{name}\x1b[30G| {kind}"); + } + Ok(()) +} + +fn pretty_def(def: &Def, id: impl Into) { + let id = id.into(); + let Def { vis, name, kind, module, meta, source: _ } = def; + for meta in *meta { + println!("#[{meta}]") + } + println!("{vis}{name} [id: {id}] = {kind}"); + println!("Module:\n\x1b[97m{module}\x1b[0m"); +} + +fn clear() -> Result<(), Box> { + println!("\x1b[H\x1b[2J"); + banner(); + Ok(()) +} + +fn banner() { + println!( + "--- {} v{} 💪🦈 ---", + env!("CARGO_BIN_NAME"), + env!("CARGO_PKG_VERSION"), + ); +} + +/// Keeps leaked references to past ASTs, for posterity:tm: +struct TreeManager { + trees: Vec<&'static cl_ast::File>, +} + +impl TreeManager { + const fn new() -> Self { + Self { trees: vec![] } + } + fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File { + let ptr = Box::leak(Box::new(tree)); + self.trees.push(ptr); + ptr + } +} diff --git a/cl-typeck/src/definition/display.rs b/cl-typeck/src/definition/display.rs new file mode 100644 index 0000000..6b7f399 --- /dev/null +++ b/cl-typeck/src/definition/display.rs @@ -0,0 +1,162 @@ +//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic] + +use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind}; +use cl_ast::format::FmtPretty; +use std::{ + fmt::{self, Display, Write}, + iter, +}; + +fn sep<'f, 's, Item, F>( + before: &'s str, + after: &'s str, + t: F, +) -> impl FnOnce(&mut fmt::Formatter<'f>) -> fmt::Result + 's +where + Item: FnMut(&mut fmt::Formatter<'f>) -> fmt::Result, + F: FnMut() -> Option + 's, +{ + move |f| { + f.write_str(before)?; + for (idx, mut disp) in iter::from_fn(t).enumerate() { + if idx > 0 { + f.write_str(", ")?; + } + disp(f)?; + } + f.write_str(after) + } +} + +impl Display for Def<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { name, vis, meta, kind, source, module } = self; + writeln!(f, "{vis}{name}: ")?; + writeln!(f, "kind: {kind}")?; + if !meta.is_empty() { + writeln!(f, "meta: {meta:?}")?; + } + if let Some(source) = source { + writeln!(f.pretty(), "source: {{\n{source}\n}}")?; + } + write!(f, "module: {module}") + } +} + +impl Display for DefKind<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DefKind::Undecided => write!(f, "undecided"), + DefKind::Impl(id) => write!(f, "impl {id}"), + DefKind::Type(kind) => write!(f, "{kind}"), + DefKind::Value(kind) => write!(f, "{kind}"), + } + } +} + +impl std::fmt::Display for ValueKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ValueKind::Const(id) => write!(f, "const ({id})"), + ValueKind::Static(id) => write!(f, "static ({id})"), + ValueKind::Fn(id) => write!(f, "fn def ({id})"), + } + } +} + +impl Display for TypeKind<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TypeKind::Alias(def) => match def { + Some(def) => write!(f, "alias to #{def}"), + None => f.write_str("type"), + }, + TypeKind::Intrinsic(i) => i.fmt(f), + TypeKind::Adt(a) => a.fmt(f), + TypeKind::Ref(cnt, def) => { + for _ in 0..*cnt { + f.write_str("&")?; + } + def.fmt(f) + } + TypeKind::Slice(def) => write!(f, "slice [#{def}]"), + TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"), + TypeKind::Tuple(defs) => { + let mut defs = defs.iter(); + sep("tuple (", ")", || { + let def = defs.next()?; + Some(move |f: &mut fmt::Formatter| write!(f, "#{def}")) + })(f) + } + TypeKind::FnSig { args, rety } => write!(f, "fn (#{args}) -> #{rety}"), + TypeKind::Empty => f.write_str("()"), + TypeKind::Never => f.write_str("!"), + TypeKind::SelfTy => f.write_str("Self"), + TypeKind::Module => f.write_str("mod"), + } + } +} + +impl Display for Adt<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Adt::Enum(variants) => { + let mut variants = variants.iter(); + sep("enum {", "}", || { + let (name, def) = variants.next()?; + Some(move |f: &mut fmt::Formatter| match def { + Some(def) => write!(f, "{name}: #{def}"), + None => write!(f, "{name}"), + }) + })(f) + } + Adt::CLikeEnum(variants) => { + let mut variants = variants.iter(); + sep("enum {", "}", || { + let (name, descrim) = variants.next()?; + Some(move |f: &mut fmt::Formatter| write!(f, "{name} = {descrim}")) + })(f) + } + Adt::FieldlessEnum => write!(f, "enum"), + Adt::Struct(members) => { + let mut members = members.iter(); + sep("struct {", "}", || { + let (name, vis, def) = members.next()?; + Some(move |f: &mut fmt::Formatter| write!(f, "{vis}{name}: #{def}")) + })(f) + } + Adt::TupleStruct(members) => { + let mut members = members.iter(); + sep("struct (", ")", || { + let (vis, def) = members.next()?; + Some(move |f: &mut fmt::Formatter| write!(f, "{vis}#{def}")) + })(f) + } + Adt::UnitStruct => write!(f, "struct ()"), + Adt::Union(variants) => { + let mut variants = variants.iter(); + sep("union {", "}", || { + let (name, def) = variants.next()?; + Some(move |f: &mut fmt::Formatter| write!(f, "{name}: #{def}")) + })(f) + } + } + } +} + +impl Display for Intrinsic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Intrinsic::I8 => f.write_str("i8"), + Intrinsic::I16 => f.write_str("i16"), + Intrinsic::I32 => f.write_str("i32"), + Intrinsic::I64 => f.write_str("i64"), + Intrinsic::U8 => f.write_str("u8"), + Intrinsic::U16 => f.write_str("u16"), + Intrinsic::U32 => f.write_str("u32"), + Intrinsic::U64 => f.write_str("u64"), + Intrinsic::Bool => f.write_str("bool"), + Intrinsic::Char => f.write_str("char"), + } + } +} diff --git a/cl-typeck/src/lib.rs b/cl-typeck/src/lib.rs index ad7a738..c1cfa82 100644 --- a/cl-typeck/src/lib.rs +++ b/cl-typeck/src/lib.rs @@ -6,11 +6,21 @@ /* -The type checker keeps track of a *global intern pool* for Types and Values -References to the intern pool are held by ID, and items cannot be freed from the pool EVER. +The type checker keeps track of a *global intern pool* for Definitions +References to the intern pool are held by DefID, and items cannot be freed from the pool EVER. -Items are inserted into their respective pools, +Definitions are classified into two namespaces: +Type Namespace: + - Modules + - Structs + - Enums + - Type aliases + +Value Namespace: + - Functions + - Constants + - Static variables */ @@ -24,127 +34,116 @@ pub mod key { /// [1]: crate::definition::Def DefID, } + + impl std::fmt::Display for DefID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } } 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::DefID, module::Module}; + use cl_ast::{Item, Meta, Visibility}; + use std::{fmt::Debug, str::FromStr}; - #[derive(Clone, PartialEq, Eq)] - pub struct Def { - pub name: String, + mod display; + + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct Def<'a> { + pub name: &'a str, pub vis: Visibility, - pub meta: Vec, - pub kind: DefKind, - pub source: Option, - pub module: Module, + pub meta: &'a [Meta], + pub kind: DefKind<'a>, + pub source: Option<&'a Item>, + pub module: Module<'a>, } - impl Default for Def { + mod builder_functions { + use super::*; + + impl<'a> Def<'a> { + pub fn set_name(&mut self, name: &'a str) -> &mut Self { + self.name = name; + self + } + pub fn set_vis(&mut self, vis: Visibility) -> &mut Self { + self.vis = vis; + self + } + pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self { + self.meta = meta; + self + } + pub fn set_kind(&mut self, kind: DefKind<'a>) -> &mut Self { + self.kind = kind; + self + } + pub fn set_source(&mut self, source: &'a Item) -> &mut Self { + self.source = Some(source); + self + } + pub fn set_module(&mut self, module: Module<'a>) -> &mut Self { + self.module = module; + self + } + } + } + + impl Default for Def<'_> { fn default() -> Self { Self { name: Default::default(), - vis: Default::default(), + vis: Visibility::Public, meta: Default::default(), - kind: DefKind::Type(TypeKind::Module), + kind: Default::default(), 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), + #[derive(Clone, Default, Debug, PartialEq, Eq)] + pub enum DefKind<'a> { + /// An unevaluated definition + #[default] + Undecided, + /// An impl block + Impl(DefID), + /// A type, such as a `type`, `struct`, or `enum` + Type(TypeKind<'a>), /// 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 + /// A [ValueKind] represents an item in the Value Namespace + /// (a component of a [Project](crate::project::Project)). #[derive(Clone, Debug, PartialEq, Eq)] - pub enum TypeKind { - /// A type which has not yet been resolved - Undecided, + pub enum ValueKind { + Const(DefID), + Static(DefID), + Fn(DefID), + } + /// A [TypeKind] represents an item in the Type Namespace + /// (a component of a [Project](crate::project::Project)). + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + pub enum TypeKind<'a> { /// An alias for an already-defined type Alias(Option), /// A primitive type, built-in to the compiler Intrinsic(Intrinsic), /// A user-defined abstract data type - Adt(Adt), + Adt(Adt<'a>), /// A reference to an already-defined type: &T - Ref(DefID), + Ref(u16, 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 }, + /// A contiguous view of statically sized memory + Array(DefID, usize), + /// A tuple of existing types + Tuple(Vec), + /// A function which accepts multiple inputs and produces an output + FnSig { args: DefID, rety: DefID }, /// The unit type Empty, /// The never type @@ -156,16 +155,17 @@ pub mod type_kind { } /// A user-defined Abstract Data Type - #[derive(Clone, Debug, PartialEq, Eq)] - pub enum Adt { + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + pub enum Adt<'a> { /// A union-like enum type - Enum(Vec<(String, DefID)>), - CLikeEnum(Vec<(String, u128)>), + Enum(Vec<(&'a str, Option)>), + /// A C-like enum + CLikeEnum(Vec<(&'a str, u128)>), /// An enum with no fields, which can never be constructed FieldlessEnum, /// A structural product type with named members - Struct(Vec<(String, Visibility, DefID)>), + Struct(Vec<(&'a str, Visibility, DefID)>), /// A structural product type with unnamed members TupleStruct(Vec<(Visibility, DefID)>), /// A structural product type of neither named nor unnamed members @@ -173,13 +173,13 @@ pub mod type_kind { /// A choose your own undefined behavior type /// TODO: should unions be a language feature? - Union(Vec<(String, DefID)>), + Union(Vec<(&'a str, DefID)>), } /// The set of compiler-intrinsic types. /// These primitive types have native implementations of the basic operations. #[allow(non_camel_case_types)] - #[derive(Clone, Debug, PartialEq, Eq)] + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Intrinsic { /// An 8-bit signed integer: `#[intrinsic = "i8"]` I8, @@ -203,6 +203,8 @@ pub mod type_kind { // U128, /// A boolean (`true` or `false`): `#[intrinsic = "bool"]` Bool, + /// The unicode codepoint type: #[intrinsic = "char"] + Char, } impl FromStr for Intrinsic { @@ -219,62 +221,62 @@ pub mod type_kind { "u32" => Intrinsic::U32, "u64" => Intrinsic::U64, "bool" => Intrinsic::Bool, + "char" => Intrinsic::Char, _ => Err(())?, }) } } - - #[derive(Clone, Debug, PartialEq, Eq)] - pub enum Float { - F32 = 0x20, - F64, - } -} - -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; - - #[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, - }, - } } pub mod module { //! A [Module] is a node in the Module Tree (a component of a //! [Project](crate::project::Project)) + use cl_structures::intern_pool::InternKey; + 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 struct Module<'a> { pub parent: Option, - pub types: HashMap, - pub values: HashMap, + pub types: HashMap<&'a str, DefID>, + pub values: HashMap<&'a str, DefID>, + pub imports: HashMap<&'a str, DefID>, } - impl Module { + impl Module<'_> { pub fn new(parent: DefID) -> Self { Self { parent: Some(parent), ..Default::default() } } + pub fn with_optional_parent(parent: Option) -> Self { + Self { parent, ..Default::default() } + } + } + + impl std::fmt::Display for Module<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { parent, types, values, imports } = self; + if let Some(parent) = parent { + writeln!(f, "Parent: {}", parent.get())?; + } + for (name, table) in [("Types", types), ("Values", values), ("Imports", imports)] { + if table.is_empty() { + continue; + } + writeln!(f, "{name}:")?; + for (name, id) in table.iter() { + writeln!(f, " {name} => {id}")?; + } + } + Ok(()) + } } } pub mod path { + use cl_ast::{Path as AstPath, PathPart}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -311,6 +313,7 @@ pub mod path { Self::new(value) } } + impl std::fmt::Display for Path<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { const SEPARATOR: &str = "::"; @@ -328,46 +331,77 @@ pub mod path { pub mod project { use crate::{ - definition::{Def, DefKind}, + definition::{Def, DefKind, TypeKind}, key::DefID, + module, path::Path, - type_kind::TypeKind, }; - use cl_ast::{Identifier, PathPart, Visibility}; + use cl_ast::{Identifier, PathPart, TyFn, TyKind, TyRef, TyTuple, Visibility}; use cl_structures::intern_pool::Pool; - use std::ops::{Index, IndexMut}; + use std::{ + collections::HashMap, + ops::{Index, IndexMut}, + }; - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct Project { - pub pool: Pool, - pub module_root: DefID, + use self::evaluate::EvaluableTypeExpression; + + #[derive(Clone, Debug)] + pub struct Project<'a> { + pub pool: Pool, DefID>, + /// Stores anonymous tuples, function pointer types, etc.\ + pub anon_types: HashMap, DefID>, + pub impls_pending: Vec<(&'a cl_ast::Impl, DefID)>, + pub root: DefID, } - impl Project { + impl Project<'_> { pub fn new() -> Self { Self::default() } } - impl Default for Project { + + 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), + let root = pool.insert(Def { + name: "🌳 root 🌳", + kind: DefKind::Type(TypeKind::Module), ..Default::default() }); - pool[module_root] - .module - .types - .insert(String::from("!"), never); - Self { pool, module_root } + + // Insert the Never(!) type + let never = pool.insert(Def { + name: "!", + vis: Visibility::Public, + kind: DefKind::Type(TypeKind::Never), + module: module::Module::new(root), + ..Default::default() + }); + let empty = pool.insert(Def { + name: "()", + vis: Visibility::Public, + kind: DefKind::Type(TypeKind::Empty), + module: module::Module::new(root), + ..Default::default() + }); + let selfty = pool.insert(Def { + name: "Self", + vis: Visibility::Public, + kind: DefKind::Type(TypeKind::SelfTy), + module: module::Module::new(root), + ..Default::default() + }); + + let mut anon_types = HashMap::new(); + anon_types.insert(TypeKind::Empty, empty); + anon_types.insert(TypeKind::Never, never); + anon_types.insert(TypeKind::SelfTy, selfty); + + Self { pool, root, anon_types, impls_pending: Default::default() } } } - impl Project { + impl<'a> Project<'a> { pub fn parent_of(&self, module: DefID) -> Option { self[module].module.parent } @@ -379,8 +413,7 @@ pub mod project { } /// 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 + pub fn get_type<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> { if path.absolute { self.get_type(path.relative(), self.root_of(within)) } else if let Some(front) = path.front() { @@ -388,7 +421,7 @@ pub mod project { 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) { + PathPart::Ident(Identifier(name)) => match module.types.get(name.as_str()) { Some(&submodule) => self.get_type(path.pop_front()?, submodule), None => Some((within, path)), }, @@ -398,10 +431,10 @@ pub mod project { } } - pub fn get_value<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> { + pub fn get_value<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> { match path.front()? { PathPart::Ident(Identifier(name)) => Some(( - self[within].module.values.get(name).copied()?, + self[within].module.values.get(name.as_str()).copied()?, path.pop_front()?, )), _ => None, @@ -409,369 +442,756 @@ pub mod project { } #[rustfmt::skip] - pub fn insert_type(&mut self, name: String, value: Def, parent: DefID) -> Option { + pub fn insert_type(&mut self, name: &'a str, value: Def<'a>, 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 { + pub fn insert_value(&mut self, name: &'a str, value: Def<'a>, parent: DefID) -> Option { let id = self.pool.insert(value); self[parent].module.values.insert(name, id) } + + /// Inserts the type returned by the provided closure iff the TypeKind doesn't already exist + /// + /// Assumes `kind` uniquely identifies the type! + pub fn insert_anonymous_type( + &mut self, + kind: TypeKind<'a>, + def: impl FnOnce() -> Def<'a>, + ) -> DefID { + *(self + .anon_types + .entry(kind) + .or_insert_with(|| self.pool.insert(def()))) + } + + pub fn evaluate(&mut self, expr: &T, parent: DefID) -> Result + where T: EvaluableTypeExpression { + expr.evaluate(self, parent) + } } - /// 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<'a> Index for Project<'a> { + type Output = Def<'a>; + fn index(&self, index: DefID) -> &Self::Output { + &self.pool[index] + } } - impl_index! { - self.pool[DefID] -> Def, - // self.types[TypeID] -> TypeDef, - // self.values[ValueID] -> ValueDef, + impl IndexMut for Project<'_> { + fn index_mut(&mut self, index: DefID) -> &mut Self::Output { + &mut self.pool[index] + } + } + + pub mod evaluate { + //! An [EvaluableTypeExpression] is a component of a type expression tree + //! or an intermediate result of expression evaluation. + + use super::*; + use cl_ast::Ty; + + /// Things that can be evaluated as a type expression + pub trait EvaluableTypeExpression { + /// The result of type expression evaluation + type Out; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result; + } + + impl EvaluableTypeExpression for Ty { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, id: DefID) -> Result { + self.kind.evaluate(prj, id) + } + } + + impl EvaluableTypeExpression for TyKind { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let id = match self { + // TODO: reduce duplication here + TyKind::Never => prj.anon_types[&TypeKind::Never], + TyKind::Empty => prj.anon_types[&TypeKind::Empty], + TyKind::SelfTy => prj.anon_types[&TypeKind::SelfTy], + // TyKind::Path must be looked up explicitly + TyKind::Path(path) => { + let (id, path) = prj + .get_type(path.into(), parent) + .ok_or("Failed to get type")?; + if path.is_empty() { + id + } else { + let (id, path) = + prj.get_value(path, id).ok_or("Failed to get value")?; + path.is_empty() + .then_some(id) + .ok_or("Path not fully resolved")? + } + } + TyKind::Tuple(tup) => tup.evaluate(prj, parent)?, + TyKind::Ref(tyref) => tyref.evaluate(prj, parent)?, + TyKind::Fn(tyfn) => tyfn.evaluate(prj, parent)?, + }; + // println!("{self} => {id:?}"); + + Ok(id) + } + } + + impl EvaluableTypeExpression for str { + type Out = DefID; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + prj[parent] + .module + .types + .get(self) + .copied() + .ok_or_else(|| format!("{self} is not a member of {}", prj[parent].name)) + } + } + + impl EvaluableTypeExpression for TyTuple { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let types = self.types.evaluate(prj, parent)?; + let root = prj.root; + let id = prj.insert_anonymous_type(TypeKind::Tuple(types.clone()), move || Def { + kind: DefKind::Type(TypeKind::Tuple(types)), + module: module::Module::new(root), + ..Default::default() + }); + + Ok(id) + } + } + impl EvaluableTypeExpression for TyRef { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let TyRef { count, mutable: _, to } = self; + let to = to.evaluate(prj, parent)?; + + let root = prj.root; + let id = prj.insert_anonymous_type(TypeKind::Ref(*count, to), move || Def { + kind: DefKind::Type(TypeKind::Ref(*count, to)), + module: module::Module::new(root), + ..Default::default() + }); + Ok(id) + } + } + impl EvaluableTypeExpression for TyFn { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let TyFn { args, rety } = self; + + let args = args.evaluate(prj, parent)?; + let rety = match rety { + Some(rety) => rety.evaluate(prj, parent)?, + _ => TyKind::Empty.evaluate(prj, parent)?, + }; + + let root = prj.root; + let id = prj.insert_anonymous_type(TypeKind::FnSig { args, rety }, || Def { + kind: DefKind::Type(TypeKind::FnSig { args, rety }), + module: module::Module::new(root), + ..Default::default() + }); + Ok(id) + } + } + + impl EvaluableTypeExpression for cl_ast::Path { + type Out = DefID; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + Path::from(self).evaluate(prj, parent) + } + } + impl<'a> EvaluableTypeExpression for Path<'a> { + type Out = DefID; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let (id, path) = prj.get_type(*self, parent).ok_or("Failed to get type")?; + + if path.is_empty() { + Ok(id) + } else { + let (id, path) = prj.get_value(path, id).ok_or("Failed to get value")?; + path.is_empty() + .then_some(id) + .ok_or(String::from("Path not fully resolved")) + } + } + } + impl EvaluableTypeExpression for [T] { + type Out = Vec; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + let mut types = vec![]; + for value in self { + types.push(value.evaluate(prj, parent)?) + } + + Ok(types) + } + } + impl EvaluableTypeExpression for Option { + type Out = Option; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + Ok(match self { + Some(v) => Some(v.evaluate(prj, parent)?), + None => None, + }) + } + } + impl EvaluableTypeExpression for Box { + type Out = T::Out; + + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + self.as_ref().evaluate(prj, parent) + } + } } } pub mod name_collector { //! Performs step 1 of type checking: Collecting all the names of things into [Module] units - use crate::{ definition::{Def, DefKind}, - key, - project::Project, - type_kind::{Adt, TypeKind}, - value_kind::ValueKind, + key::DefID, + module::Module as Mod, + project::Project as Prj, }; use cl_ast::*; - use std::ops::{Deref, DerefMut}; - /// 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, - } + pub trait NameCollectable<'a> { + /// Collects the identifiers within this node, + /// returning a new [DefID] if any were allocated, + /// else returning the parent [DefID] + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result; - 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() + fn collect_in_root(&'a self, c: &mut Prj<'a>) -> Result { + self.collect(c, c.root) } } - 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) - } - } - } + impl<'a> NameCollectable<'a> for File { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + for item in &self.items { + item.collect(c, parent)?; } - 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 + Ok(parent) } } - /// 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), - }; + impl<'a> NameCollectable<'a> for Item { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Item { attrs: Attrs { meta }, vis, kind, .. } = self; + let id = match kind { + ItemKind::Impl(i) => i.collect(c, parent), + ItemKind::Module(i) => i.collect(c, parent), + ItemKind::Alias(i) => i.collect(c, parent), + ItemKind::Enum(i) => i.collect(c, parent), + ItemKind::Struct(i) => i.collect(c, parent), + ItemKind::Const(i) => i.collect(c, parent), + ItemKind::Static(i) => i.collect(c, parent), + ItemKind::Function(i) => i.collect(c, parent), + }?; + c[id].set_meta(meta).set_vis(*vis).set_source(self); - Ok(Def { - name: name.0.clone(), - vis: *vis, - meta: attrs.meta.clone(), - kind, - source: Some(item.clone()), - module: Default::default(), - }) + Ok(id) } + } + impl<'a> NameCollectable<'a> for Module { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Self { name: Identifier(name), kind } = self; + let module = + c.pool + .insert(Def { name, module: Mod::new(parent), ..Default::default() }); + c[parent].module.types.insert(name, module); - /// 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")? + match kind { + ModuleKind::Inline(file) => file.collect(c, module)?, + ModuleKind::Outline => todo!("Out of line modules"), }; + Ok(module) + } + } + impl<'a> NameCollectable<'a> for Impl { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Self { target: _, body } = self; + let def = Def { module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); - let mut kind = match from { - Some(_) => DefKind::Type(TypeKind::Undecided), - None => DefKind::Type(TypeKind::Alias(None)), - }; + // items will get reparented after name collection, when target is available + body.collect(c, id)?; - 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(id) + } + } + impl<'a> NameCollectable<'a> for Alias { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Alias { to: Identifier(name), .. } = self; + + let def = Def { name, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.types.insert(name, id); + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Enum { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Enum { name: Identifier(name), .. } = self; + + let def = Def { name, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.types.insert(name, id); + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Struct { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Struct { name: Identifier(name), .. } = self; + + let def = Def { name, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.types.insert(name, id); + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Const { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Self { name: Identifier(name), init, .. } = self; + + let kind = DefKind::Undecided; + + let def = Def { name, kind, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.values.insert(name, id); + init.collect(c, id)?; + + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Static { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Self { name: Identifier(name), init, .. } = self; + + let kind = DefKind::Undecided; + + let def = Def { name, kind, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.values.insert(name, id); + init.collect(c, id)?; + + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Function { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let Self { name: Identifier(name), body, .. } = self; + + let kind = DefKind::Undecided; + + let def = Def { name, kind, module: Mod::new(parent), ..Default::default() }; + let id = c.pool.insert(def); + + c[parent].module.values.insert(name, id); + body.collect(c, id)?; + + Ok(id) + } + } + impl<'a> NameCollectable<'a> for Block { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + self.stmts.as_slice().collect(c, parent) + } + } + impl<'a> NameCollectable<'a> for Stmt { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + self.kind.collect(c, parent) + } + } + impl<'a> NameCollectable<'a> for StmtKind { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + match self { + StmtKind::Empty => Ok(parent), + StmtKind::Local(Let { init, .. }) => init.collect(c, parent), + StmtKind::Item(item) => item.collect(c, parent), + StmtKind::Expr(expr) => expr.collect(c, parent), } - - 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(), - }) + impl<'a> NameCollectable<'a> for Expr { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + self.kind.collect(c, parent) } - - 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(), - }) + } + impl<'a> NameCollectable<'a> for ExprKind { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + match self { + ExprKind::Assign(Assign { parts, .. }) | ExprKind::Binary(Binary { parts, .. }) => { + parts.0.collect(c, parent)?; + parts.1.collect(c, parent) + } + ExprKind::Unary(Unary { tail, .. }) => tail.collect(c, parent), + ExprKind::Index(Index { head, indices }) => { + indices.collect(c, parent)?; + head.collect(c, parent) + } + ExprKind::Array(Array { values }) => values.collect(c, parent), + ExprKind::ArrayRep(ArrayRep { value, repeat }) => { + value.collect(c, parent)?; + repeat.collect(c, parent) + } + ExprKind::AddrOf(AddrOf { expr, .. }) => expr.collect(c, parent), + ExprKind::Block(block) => block.collect(c, parent), + ExprKind::Group(Group { expr }) => expr.collect(c, parent), + ExprKind::Tuple(Tuple { exprs }) => exprs.collect(c, parent), + ExprKind::While(While { cond, pass, fail }) + | ExprKind::If(If { cond, pass, fail }) + | ExprKind::For(For { cond, pass, fail, .. }) => { + cond.collect(c, parent)?; + pass.collect(c, parent)?; + fail.body.collect(c, parent) + } + ExprKind::Break(Break { body }) | ExprKind::Return(Return { body }) => { + body.collect(c, parent) + } + _ => Ok(parent), + } } - - 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(), - }) + } + impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for [T] { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let mut last = parent; + for expr in self { + last = expr.collect(c, parent)?; + } + Ok(last) + } + } + impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for Option { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + match self { + Some(body) => body.collect(c, parent), + None => Ok(parent), + } + } + } + impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for Box { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + (**self).collect(c, parent) } } } pub mod type_resolver { //! Performs step 2 of type checking: Evaluating type definitions - #![allow(unused)] - use std::ops::{Deref, DerefMut}; + use crate::{ + definition::{Adt, Def, DefKind, TypeKind, ValueKind}, + key::DefID, + module, + project::{evaluate::EvaluableTypeExpression, Project as Prj}, + }; use cl_ast::*; - use crate::{definition::Def, key::DefID, project::Project}; + /// Evaluate a single ID + pub fn resolve(prj: &mut Prj, id: DefID) -> Result<(), &'static str> { + let (DefKind::Undecided, Some(source)) = (&prj[id].kind, prj[id].source) else { + return Ok(()); + }; + let kind = match &source.kind { + ItemKind::Alias(_) => "type", + ItemKind::Module(_) => "mod", + ItemKind::Enum(_) => "enum", + ItemKind::Struct(_) => "struct", + ItemKind::Const(_) => "const", + ItemKind::Static(_) => "static", + ItemKind::Function(_) => "fn", + ItemKind::Impl(_) => "impl", + }; + eprintln!( + "Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {}\x1b[0m\" (`{id:?}`)", + prj[id].name + ); - pub struct TypeResolver<'prj> { - pub project: &'prj mut Project, + prj[id].kind = source.resolve_type(prj, id)?; + + eprintln!("\x1b[33m=> {}\x1b[0m", prj[id].kind); + + Ok(()) } - 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 - } + /// Resolves a given node + pub trait TypeResolvable<'a> { + /// The return type upon success + type Out; + /// Resolves type expressions within this node + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result; } - 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(_) => {} + impl<'a> TypeResolvable<'a> for Item { + type Out = DefKind<'a>; + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let Self { attrs: Attrs { meta }, kind, .. } = self; + for meta in meta { + if let Ok(def) = meta.resolve_type(prj, id) { + return Ok(def); } } - Ok(true) + kind.resolve_type(prj, id) } + } - 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!(), + impl<'a> TypeResolvable<'a> for Meta { + type Out = DefKind<'a>; + + #[allow(unused_variables)] + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let Self { name: Identifier(name), kind } = self; + match (name.as_str(), kind) { + ("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => Ok(DefKind::Type( + TypeKind::Intrinsic(intrinsic.parse().map_err(|_| "unknown intrinsic type")?), + )), + (_, MetaKind::Plain) => Ok(DefKind::Type(TypeKind::Intrinsic( + name.parse().map_err(|_| "Unknown intrinsic type")?, + ))), + _ => Err("Unknown meta attribute"), + } + } + } + + impl<'a> TypeResolvable<'a> for ItemKind { + type Out = DefKind<'a>; + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + if prj[id].source.map(|s| &s.kind as *const _) != Some(self as *const _) { + return Err("id is not self!"); + } + match self { + ItemKind::Module(i) => i.resolve_type(prj, id), + ItemKind::Impl(i) => i.resolve_type(prj, id), + ItemKind::Alias(i) => i.resolve_type(prj, id), + ItemKind::Enum(i) => i.resolve_type(prj, id), + ItemKind::Struct(i) => i.resolve_type(prj, id), + ItemKind::Const(i) => i.resolve_type(prj, id), + ItemKind::Static(i) => i.resolve_type(prj, id), + ItemKind::Function(i) => i.resolve_type(prj, id), + } + } + } + + impl<'a> TypeResolvable<'a> for Module { + type Out = DefKind<'a>; + #[allow(unused_variables)] + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + Ok(DefKind::Type(TypeKind::Module)) + } + } + + impl<'a> TypeResolvable<'a> for Impl { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + + let target = match &self.target { + ImplKind::Type(t) => t.evaluate(prj, parent), + ImplKind::Trait { for_type, .. } => for_type.evaluate(prj, parent), + } + .map_err(|_| "Unresolved type in impl target")?; + + prj[id].module.parent = Some(target); + + Ok(DefKind::Impl(target)) + } + } + + impl<'a> TypeResolvable<'a> for Alias { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let alias = if let Some(ty) = &self.from { + Some( + ty.evaluate(prj, parent) + .or_else(|_| ty.evaluate(prj, id)) + .map_err(|_| "Unresolved type in alias")?, + ) + } else { + None + }; + + Ok(DefKind::Type(TypeKind::Alias(alias))) + } + } + + impl<'a> TypeResolvable<'a> for Enum { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let Self { name: _, kind } = self; + let EnumKind::Variants(v) = kind else { + return Ok(DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum))); + }; + let mut fields = vec![]; + for v @ Variant { name: Identifier(name), kind: _ } in v { + let id = v.resolve_type(prj, id)?; + fields.push((name.as_str(), id)) + } + Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields)))) + } + } + + impl<'a> TypeResolvable<'a> for Variant { + type Out = Option; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let Self { name: Identifier(name), kind } = self; + + let adt = match kind { + VariantKind::Plain => return Ok(None), + VariantKind::CLike(_) => todo!("Resolve variant info for C-like enums"), + VariantKind::Tuple(ty) => { + return ty + .evaluate(prj, parent) + .map_err(|_| "Unresolved type in enum tuple variant") + .map(Some) + } + VariantKind::Struct(members) => Adt::Struct(members.resolve_type(prj, id)?), + }; + + let def = Def { + name, + kind: DefKind::Type(TypeKind::Adt(adt)), + module: module::Module::new(id), + ..Default::default() + }; + + let new_id = prj.pool.insert(def); + // Insert the struct variant type into the enum's namespace + prj[id].module.types.insert(name, new_id); + + Ok(Some(new_id)) + } + } + + impl<'a> TypeResolvable<'a> for Struct { + type Out = DefKind<'a>; + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let Self { name: _, kind } = self; + Ok(match kind { + StructKind::Empty => DefKind::Type(TypeKind::Empty), + StructKind::Tuple(types) => DefKind::Type(TypeKind::Adt(Adt::TupleStruct({ + let mut out = vec![]; + for ty in types { + out.push(( + Visibility::Public, + ty.evaluate(prj, parent) + .map_err(|_| "Unresolved type in tuple-struct member")?, + )); + } + out + }))), + StructKind::Struct(members) => { + DefKind::Type(TypeKind::Adt(Adt::Struct(members.resolve_type(prj, id)?))) + } + }) + } + } + + impl<'a> TypeResolvable<'a> for StructMember { + type Out = (&'a str, Visibility, DefID); + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let Self { name: Identifier(name), vis, ty } = self; + + let ty = ty + .evaluate(prj, parent) + .map_err(|_| "Invalid type while resolving StructMember")?; + + Ok((name, *vis, ty)) + } + } + + impl<'a> TypeResolvable<'a> for Const { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let Self { ty, .. } = self; + let ty = ty + .evaluate(prj, id) + .map_err(|_| "Invalid type while resolving const")?; + Ok(DefKind::Value(ValueKind::Const(ty))) + } + } + impl<'a> TypeResolvable<'a> for Static { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let Self { ty, .. } = self; + let ty = ty + .evaluate(prj, parent) + .map_err(|_| "Invalid type while resolving static")?; + Ok(DefKind::Value(ValueKind::Static(ty))) + } + } + + impl<'a> TypeResolvable<'a> for Function { + type Out = DefKind<'a>; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let parent = prj.parent_of(id).unwrap_or(id); + let Self { sign, .. } = self; + let sign = sign + .evaluate(prj, parent) + .map_err(|_| "Invalid type in function signature")?; + Ok(DefKind::Value(ValueKind::Fn(sign))) + } + } + + impl<'a, T: TypeResolvable<'a>> TypeResolvable<'a> for [T] { + type Out = Vec; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + let mut members = vec![]; + for member in self { + members.push(member.resolve_type(prj, id)?); + } + Ok(members) + } + } + impl<'a, T: TypeResolvable<'a>> TypeResolvable<'a> for Option { + type Out = Option; + + fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result { + match self { + Some(t) => Some(t.resolve_type(prj, id)).transpose(), + None => Ok(None), } - None } } } pub mod typeref { - //! Stores type and reference info + //! Stores type and inference info 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](RefKind::Generic) or [Concrete](RefKind::Concrete) kind: RefKind, } @@ -853,15 +1273,15 @@ let rules: Hashmap> { */ pub mod rule { - use crate::{key::DefID, typeref::TypeRef}; + use crate::key::DefID; pub struct Rule { /// What is this Rule for? pub operation: (), /// What inputs does it take? - pub inputs: Vec, + pub inputs: Vec, /// What output does it produce? - pub output: TypeRef, + pub output: DefID, /// Where did this rule come from? pub through: Origin, } diff --git a/stdlib/lib.cl b/stdlib/lib.cl index 2d8a964..380a778 100644 --- a/stdlib/lib.cl +++ b/stdlib/lib.cl @@ -1,11 +1,99 @@ //! # The Conlang Standard Library -/// 32-bit signed integer type +#[intrinsic = "bool"] +pub type bool; + +#[intrinsic = "char"] +pub type char; + +#[intrinsic = "i8"] +pub type i8; + +#[intrinsic = "i16"] +pub type i16; + #[intrinsic = "i32"] -type i32; +pub type i32; + +#[intrinsic = "i64"] +pub type i64; + +#[intrinsic = "u8"] +pub type u8; + +#[intrinsic = "u16"] +pub type u16; -/// 32-bit unsigned integer type #[intrinsic = "u32"] -type u32; +pub type u32; +#[intrinsic = "u64"] +pub type u64; +impl bool { + const MIN: Self = false; + const MAX: Self = { + fn ret_true() -> Self { + true + } + ret_true() + }; +} + +fn if_else() -> i32 { + // block 1 + let x = 10; + let y = x + 2; + + if x > 10 + // compare x and 10 + // end block 1, goto block 2 else goto block 3 + { + // block 2 + 4 + // end block 2, goto block 4 + } else { + // block 3 + 5 + // end block 3, goto block 4 + } + // block 4 +} + +fn while_else() -> i32 { + loop { + if conditional { + body; + } else { + break { else_body }; + } + } +} + +#[cfg("test")] +mod test { + //! Tests for funky behavior + + struct UnitLike; + + struct TupleLike(super::i32, super::self::test::super::char) + + struct StructLike { + pub member1: UnitLike, + member2: TupleLike, + } + + enum NeverLike; + + enum EmptyLike { + Empty, + } + + enum Hodgepodge { + Empty, + UnitLike (), + Tuple (TupleLike), + StructEmpty {}, + StructLike { member1: UnitLike, member2: TupleLike }, + } +}