From e19127facc46bd26e5054af1b05ed0c4c30cd07c Mon Sep 17 00:00:00 2001 From: John Date: Wed, 24 Jul 2024 18:22:42 -0500 Subject: [PATCH] cl-typeck: Crate-spanning refactor TODO: remove all unreferenced files TODO: Finish resolving statically known types of values TODO: Type inference --- compiler/cl-typeck/examples/typeck.rs | 162 +++++++---- compiler/cl-typeck/src/categorize.rs | 69 +++++ compiler/cl-typeck/src/entry.rs | 172 +++++++++++ compiler/cl-typeck/src/entry/display.rs | 109 +++++++ compiler/cl-typeck/src/handle.rs | 214 +------------- compiler/cl-typeck/src/import.rs | 112 ++++++++ compiler/cl-typeck/src/lib.rs | 36 +-- compiler/cl-typeck/src/module.rs | 71 ----- compiler/cl-typeck/src/name_collector.rs | 218 -------------- compiler/cl-typeck/src/node.rs | 214 -------------- compiler/cl-typeck/src/path.rs | 60 ---- compiler/cl-typeck/src/populate.rs | 179 ++++++++++++ compiler/cl-typeck/src/source.rs | 87 ++++++ compiler/cl-typeck/src/table.rs | 269 ++++++++++++++++++ compiler/cl-typeck/src/type_expression.rs | 127 +++++++++ .../src/{definition.rs => type_kind.rs} | 108 ++----- .../src/{definition => type_kind}/display.rs | 52 +--- 17 files changed, 1270 insertions(+), 989 deletions(-) create mode 100644 compiler/cl-typeck/src/categorize.rs create mode 100644 compiler/cl-typeck/src/entry.rs create mode 100644 compiler/cl-typeck/src/entry/display.rs create mode 100644 compiler/cl-typeck/src/import.rs delete mode 100644 compiler/cl-typeck/src/module.rs delete mode 100644 compiler/cl-typeck/src/name_collector.rs delete mode 100644 compiler/cl-typeck/src/node.rs delete mode 100644 compiler/cl-typeck/src/path.rs create mode 100644 compiler/cl-typeck/src/populate.rs create mode 100644 compiler/cl-typeck/src/source.rs create mode 100644 compiler/cl-typeck/src/table.rs create mode 100644 compiler/cl-typeck/src/type_expression.rs rename compiler/cl-typeck/src/{definition.rs => type_kind.rs} (56%) rename compiler/cl-typeck/src/{definition => type_kind}/display.rs (69%) diff --git a/compiler/cl-typeck/examples/typeck.rs b/compiler/cl-typeck/examples/typeck.rs index 6367d95..556eaed 100644 --- a/compiler/cl-typeck/examples/typeck.rs +++ b/compiler/cl-typeck/examples/typeck.rs @@ -1,15 +1,14 @@ +use cl_typeck::{ + entry::Entry, import::import, populate::Populator, table::Table, + type_expression::TypeExpression, +}; + use cl_ast::{ ast_visitor::{Fold, Visit}, desugar::*, }; use cl_lexer::Lexer; use cl_parser::{inliner::ModuleInliner, Parser}; -use cl_typeck::{ - handle::Handle, - name_collector::NameCollector, - project::Project, - type_resolver::resolve, -}; use repline::{error::Error as RlError, prebaked::*}; use std::{error::Error, path}; @@ -31,7 +30,7 @@ const C_LISTING: &str = "\x1b[38;5;117m"; static mut TREES: TreeManager = TreeManager::new(); fn main() -> Result<(), Box> { - let mut prj = Project::default(); + let mut prj = Table::default(); let mut parser = Parser::new(Lexer::new(STDLIB)); let code = match parser.file() { @@ -42,13 +41,14 @@ fn main() -> Result<(), Box> { } }; let code = inline_modules(code, concat!(env!("PWD"), "/stdlib")); - NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) }); + Populator::new(&mut prj).visit_file(unsafe { TREES.push(code) }); + // NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) }); main_menu(&mut prj)?; Ok(()) } -fn main_menu(prj: &mut Project) -> Result<(), RlError> { +fn main_menu(prj: &mut Table) -> Result<(), RlError> { banner(); read_and(C_MAIN, "mu>", "? >", |line| { match line.trim() { @@ -80,7 +80,7 @@ fn main_menu(prj: &mut Project) -> Result<(), RlError> { }) } -fn enter_code(prj: &mut Project) -> Result<(), RlError> { +fn enter_code(prj: &mut Table) -> Result<(), RlError> { read_and(C_CODE, "cl>", "? >", |line| { if line.trim().is_empty() { return Ok(Response::Break); @@ -89,8 +89,7 @@ fn enter_code(prj: &mut Project) -> Result<(), RlError> { let code = inline_modules(code, ""); let code = WhileElseDesugar.fold_file(code); // Safety: this is totally unsafe - NameCollector::new(prj).visit_file(unsafe { TREES.push(code) }); - + Populator::new(prj).visit_file(unsafe { TREES.push(code) }); Ok(Response::Accept) }) } @@ -113,22 +112,22 @@ fn live_desugar() -> Result<(), RlError> { }) } -fn query_type_expression(prj: &mut Project) -> Result<(), RlError> { +fn query_type_expression(prj: &mut Table) -> Result<(), RlError> { 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)?.handle_unchecked(prj); - pretty_handle(id)?; + let id = ty.evaluate(prj, prj.root())?; + pretty_handle(id.to_entry(prj))?; Ok(Response::Accept) }) } -fn get_by_id(prj: &mut Project) -> Result<(), RlError> { +fn get_by_id(prj: &mut Table) -> Result<(), RlError> { use cl_structures::index_map::MapIndex; - use cl_typeck::handle::DefID; + use cl_typeck::handle::Handle; read_and(C_BYID, "id>", "? >", |line| { if line.trim().is_empty() { return Ok(Response::Break); @@ -141,9 +140,7 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> { let mut path = parser.path().unwrap_or_default(); path.absolute = false; - let Some(handle) = DefID::from_usize(def_id).handle(prj) else { - return Ok(Response::Deny); - }; + let handle = Handle::from_usize(def_id).to_entry(prj); print!(" > {{{C_LISTING}{handle}\x1b[0m}}"); if !path.parts.is_empty() { @@ -151,65 +148,114 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> { } println!(); - let (ty, value) = handle.navigate((&path).into()); - if let (None, None) = (ty, value) { + let Some(handle) = handle.nav(&path.parts) else { Err("No results.")? - } - if let Some(t) = ty { - println!("Result: {}", t.id()); - pretty_handle(t)?; - } - if let Some(v) = value { - println!("Result in value namespace: {}", v.id()); - pretty_handle(v)?; - } + }; + + pretty_handle(handle)?; Ok(Response::Accept) }) } -fn resolve_all(prj: &mut Project) -> Result<(), Box> { - prj.resolve_imports()?; - for id in prj.pool.keys() { - resolve(prj, id)?; +fn resolve_all(prj: &mut Table) -> Result<(), Box> { + println!("Resolving imports:"); + for (id, error) in import(prj) { + eprintln!("{error} in {} ({id})", id.to_entry(prj)) } - println!("Types resolved successfully!"); + // todo!("Resolve imports"); + // prj.resolve_imports()?; + // for id in prj.pool.keys() { + // resolve(prj, id)?; + // } + // println!("Types resolved successfully!"); Ok(()) } -fn list_types(prj: &mut Project) { - println!(" name | type"); - for (idx, key) in prj.pool.keys().enumerate() { - let handle = key.handle_unchecked(prj); - let vis = handle.vis().unwrap_or_default(); +fn list_types(prj: &mut Table) { + for handle in prj.debug_handle_iter() { + let id = handle.id(); + let kind = handle.kind().unwrap(); let name = handle.name().unwrap_or("".into()); - println!("{idx:3}: {name:16}| {vis}{handle}"); + println!("{id:3}: {name:16}| {kind} {handle}"); } } -fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> { +fn pretty_handle(h: Entry) -> Result<(), std::io::Error> { use std::io::Write; - let mut stdout = std::io::stdout().lock(); - let Some(vis) = handle.vis() else { - return writeln!(stdout, "Invalid handle: {handle}"); + let mut out = std::io::stdout().lock(); + let Some(kind) = h.kind() else { + return writeln!(out, "Invalid handle: {h}"); }; - writeln!(stdout, "{C_LISTING}{}\x1b[0m: {vis}{handle}", handle.id())?; - if let Some(parent) = handle.parent() { - writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {parent}")?; + write!(out, "{C_LISTING}{kind}")?; + if let Some(name) = h.name() { + write!(out, " {name}")?; } - if let Some(types) = handle.types() { - writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?; - for (name, def) in types { - writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? + writeln!(out, "\x1b[0m: {h}")?; + + if let Some(parent) = h.parent() { + writeln!(out, "- {C_LISTING}Parent\x1b[0m: {parent}")?; + } + + if let Some(span) = h.span() { + writeln!( + out, + "- {C_LISTING}Span:\x1b[0m ({}, {})", + span.head, span.tail + )?; + } + + match h.meta() { + Some(meta) if !meta.is_empty() => { + writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?; + for meta in meta { + writeln!(out, " - {meta}")?; + } + } + _ => {} + } + + if let Some(children) = h.children() { + writeln!(out, "- {C_LISTING}Children:\x1b[0m")?; + for (name, child) in children { + writeln!( + out, + " - {C_LISTING}{name}\x1b[0m ({child}): {}", + h.with_id(*child) + )? } } - if let Some(values) = handle.values() { - writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?; - for (name, def) in values { - writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? + + if let Some(imports) = h.imports() { + writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?; + for (name, child) in imports { + writeln!( + out, + " - {C_LISTING}{name}\x1b[0m ({child}): {}", + h.with_id(*child) + )? } } - write!(stdout, "\x1b[0m") + + // let Some(vis) = handle.vis() else { + // return writeln!(stdout, "Invalid handle: {handle}"); + // }; + // writeln!(stdout, "{C_LISTING}{}\x1b[0m: {vis}{handle}", handle.id())?; + // if let Some(parent) = handle.parent() { + // } + // if let Some(types) = handle.types() { + // writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?; + // for (name, def) in types { + // } + // } + // if let Some(values) = handle.values() { + // writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?; + // for (name, def) in values { + // writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? + // } + // } + // write!(stdout, "\x1b[0m") + Ok(()) } fn inline_modules(code: cl_ast::File, path: impl AsRef) -> cl_ast::File { diff --git a/compiler/cl-typeck/src/categorize.rs b/compiler/cl-typeck/src/categorize.rs new file mode 100644 index 0000000..3e8a90c --- /dev/null +++ b/compiler/cl-typeck/src/categorize.rs @@ -0,0 +1,69 @@ +//! Categorizes all top-level entries in a table + +use crate::{ + handle::Handle, + source::Source, + table::Table, + type_expression::{Error as TypeEval, TypeExpression}, + type_kind::TypeKind, +}; +use cl_ast::*; + +/// Ensures a type entry exists for the provided handle in the table +pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> { + let Some(source) = table.source(node) else { + Err(Error::NoSource)? + }; + + match source { + Source::Root => {} + Source::Module(_) => {} + Source::Alias(a) => categorize_alias(table, node, a)?, + Source::Enum(_) => todo!(), + Source::Variant(_) => todo!(), + Source::Struct(_) => todo!(), + Source::Const(_) => todo!(), + Source::Static(_) => todo!(), + Source::Function(_) => todo!(), + Source::Local(_) => todo!(), + Source::Impl(_) => todo!(), + Source::Use(_) => todo!(), + Source::Ty(ty) => { + ty.evaluate(table, node)?; + } + } + + todo!("categorize {node} in {table:?}") +} + +pub fn categorize_alias(table: &mut Table, node: Handle, a: &Alias) -> CatResult<()> { + let kind = match &a.from { + Some(ty) => TypeKind::Alias(ty.evaluate(table, node)?), + None => TypeKind::Empty, + }; + table.set_ty(node, kind); + + Ok(()) +} + +pub fn categorize_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> { + let kind = TypeKind::Alias(c.ty.evaluate(table, node)?); + + table.set_ty(node, kind); + Ok(()) +} + +type CatResult = Result; + +pub enum Error { + NoSource, + BadMeta(Meta), + Recursive(Handle), + TypeEval(TypeEval), +} + +impl From for Error { + fn from(value: TypeEval) -> Self { + Error::TypeEval(value) + } +} diff --git a/compiler/cl-typeck/src/entry.rs b/compiler/cl-typeck/src/entry.rs new file mode 100644 index 0000000..f0b4437 --- /dev/null +++ b/compiler/cl-typeck/src/entry.rs @@ -0,0 +1,172 @@ +//! A [Handle] is an accessor for [Entries](Entry) in a [Table]. +//! +//! There are two kinds of handle: +//! - [Handle]: Provides getters for an entry's fields, and an implementation of +//! [Display](std::fmt::Display) +//! - [HandleMut]: Provides setters for an entry's fields, and an [`as_ref`](HandleMut::as_ref) +//! method to demote to a [Handle]. + +use std::collections::HashMap; + +use cl_ast::{Meta, PathPart, Sym}; +use cl_structures::span::Span; + +use crate::{ + handle::Handle, + source::Source, + table::{NodeKind, Table}, + type_kind::TypeKind, +}; + +pub mod display; + +impl Handle { + pub const fn to_entry<'t, 'a>(self, table: &'t Table<'a>) -> Entry<'t, 'a> { + Entry { id: self, table } + } + pub fn to_entry_mut<'t, 'a>(self, table: &'t mut Table<'a>) -> EntryMut<'t, 'a> { + EntryMut { id: self, table } + } +} + +#[derive(Debug)] +pub struct Entry<'t, 'a> { + table: &'t Table<'a>, + id: Handle, +} + +impl<'t, 'a> Entry<'t, 'a> { + pub const fn new(table: &'t Table<'a>, id: Handle) -> Self { + Self { table, id } + } + + pub const fn id(&self) -> Handle { + self.id + } + + pub fn inner(&self) -> &Table<'a> { + self.table + } + + pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> { + Self { table: self.table, id } + } + + pub fn nav(&self, path: &[PathPart]) -> Option> { + Some(Entry { id: self.table.nav(self.id, path)?, table: self.table }) + } + + pub const fn root(&self) -> Handle { + self.table.root() + } + + pub fn kind(&self) -> Option<&NodeKind> { + self.table.kind(self.id) + } + + pub fn parent(&self) -> Option<&Handle> { + self.table.parent(self.id) + } + + pub fn children(&self) -> Option<&HashMap> { + self.table.children(self.id) + } + + pub fn imports(&self) -> Option<&HashMap> { + self.table.imports(self.id) + } + + pub fn ty(&self) -> Option<&TypeKind> { + self.table.ty(self.id) + } + + pub fn span(&self) -> Option<&Span> { + self.table.span(self.id) + } + + pub fn meta(&self) -> Option<&'a [Meta]> { + self.table.meta(self.id) + } + + pub fn source(&self) -> Option<&Source<'a>> { + self.table.source(self.id) + } + + pub fn impl_target(&self) -> Option { + self.table.impl_target(self.id) + } + + pub fn selfty(&self) -> Option { + self.table.selfty(self.id) + } + + pub fn name(&self) -> Option { + self.table.name(self.id) + } +} + +#[derive(Debug)] +pub struct EntryMut<'t, 'a> { + table: &'t mut Table<'a>, + id: Handle, +} + +impl<'t, 'a> EntryMut<'t, 'a> { + pub fn new(table: &'t mut Table<'a>, id: Handle) -> Self { + Self { table, id } + } + + pub fn as_ref(&self) -> Entry<'_, 'a> { + Entry { table: self.table, id: self.id } + } + + pub const fn id(&self) -> Handle { + self.id + } + + /// Constructs a new Handle with the provided parent [DefID] + pub fn with_id(&mut self, parent: Handle) -> EntryMut<'_, 'a> { + EntryMut { table: self.table, id: parent } + } + + pub fn nav(&mut self, path: &[PathPart]) -> Option> { + Some(EntryMut { id: self.table.nav(self.id, path)?, table: self.table }) + } + + pub fn new_entry(&mut self, kind: NodeKind) -> EntryMut<'_, 'a> { + let id = self.table.new_entry(self.id, kind); + self.with_id(id) + } + + pub fn add_child(&mut self, name: Sym, child: Handle) -> Option { + self.table.add_child(self.id, name, child) + } + + pub fn set_ty(&mut self, kind: TypeKind) -> Option { + self.table.set_ty(self.id, kind) + } + + pub fn set_span(&mut self, span: Span) -> Option { + self.table.set_span(self.id, span) + } + + pub fn set_meta(&mut self, meta: &'a [Meta]) -> Option<&'a [Meta]> { + self.table.set_meta(self.id, meta) + } + + pub fn set_source(&mut self, source: Source<'a>) -> Option> { + self.table.set_source(self.id, source) + } + + pub fn set_impl_target(&mut self, target: Handle) -> Option { + self.table.set_impl_target(self.id, target) + } + + pub fn mark_use_item(&mut self) { + self.table.mark_use_item(self.id) + } + + pub fn mark_impl_item(&mut self) { + self.table.mark_impl_item(self.id) + } +} diff --git a/compiler/cl-typeck/src/entry/display.rs b/compiler/cl-typeck/src/entry/display.rs new file mode 100644 index 0000000..1eb35f5 --- /dev/null +++ b/compiler/cl-typeck/src/entry/display.rs @@ -0,0 +1,109 @@ +use super::*; +use crate::{format_utils::*, type_kind::Adt}; +use std::fmt::{self, Write}; + +/// Printing the name of a named type stops infinite recursion +fn write_name_or(h: Entry, f: &mut impl Write) -> fmt::Result { + match h.name() { + Some(name) => write!(f, "{name}"), + None => write!(f, "{h}"), + } +} + +impl fmt::Display for Entry<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.kind().is_none() { + return write!(f, "", self.id); + } + + if let Some(ty) = self.ty() { + match ty { + TypeKind::Alias(id) => write!(f, "= {}", self.with_id(*id))?, + TypeKind::Intrinsic(kind) => write!(f, "{kind}")?, + TypeKind::Adt(adt) => write_adt(adt, self, f)?, + &TypeKind::Ref(cnt, id) => { + for _ in 0..cnt { + f.write_str("&")?; + } + let h_id = self.with_id(id); + write_name_or(h_id, f)?; + } + TypeKind::Slice(id) => { + write_name_or(self.with_id(*id), &mut f.delimit_with("[", "]"))?; + } + &TypeKind::Array(t, cnt) => { + let mut f = f.delimit_with("[", "]"); + write_name_or(self.with_id(t), &mut f)?; + write!(f, "; {cnt}")?; + } + TypeKind::Tuple(ids) => { + let mut f = f.delimit_with("(", ")"); + for (index, &id) in ids.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write_name_or(self.with_id(id), &mut f)?; + } + } + TypeKind::FnSig { args, rety } => { + write!(f, "fn {} -> ", self.with_id(*args))?; + write_name_or(self.with_id(*rety), f)?; + } + TypeKind::Empty => write!(f, "()")?, + TypeKind::Never => write!(f, "!")?, + TypeKind::Module => write!(f, "module?")?, + } + } + + Ok(()) + } +} + +fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result { + match adt { + Adt::Enum(variants) => { + let mut variants = variants.iter(); + separate(",", || { + variants.next().map(|(name, def)| { + move |f: &mut Delimit<_>| match def { + Some(def) => { + write!(f, "\n{name}: ")?; + write_name_or(h.with_id(*def), f) + } + None => write!(f, "\n{name}"), + } + }) + })(f.delimit_with("enum {", "\n}")) + } + Adt::CLikeEnum(variants) => { + let mut variants = variants.iter(); + separate(",", || { + let (name, descrim) = variants.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}")) + })(f.delimit_with("enum {", "\n}")) + } + Adt::FieldlessEnum => write!(f, "enum"), + Adt::Struct(members) => { + let mut members = members.iter(); + separate(",", || { + let (name, vis, id) = members.next()?; + Some(move |f: &mut Delimit<_>| { + write!(f, "\n{vis}{name}: ")?; + write_name_or(h.with_id(*id), f) + }) + })(f.delimit_with("struct {", "\n}")) + } + Adt::TupleStruct(members) => { + let mut members = members.iter(); + separate(", ", || { + let (vis, def) = members.next()?; + Some(move |f: &mut Delimit<_>| { + write!(f, "{vis}")?; + write_name_or(h.with_id(*def), f) + }) + })(f.delimit_with("struct (", ")")) + } + Adt::UnitStruct => write!(f, "struct"), + Adt::Union(_) => todo!("Display union types"), + } +} diff --git a/compiler/cl-typeck/src/handle.rs b/compiler/cl-typeck/src/handle.rs index 0546f44..3d530ee 100644 --- a/compiler/cl-typeck/src/handle.rs +++ b/compiler/cl-typeck/src/handle.rs @@ -1,217 +1,13 @@ -use crate::{definition::Def, path::Path, project::Project}; use cl_structures::index_map::*; // define the index types make_index! { - /// Uniquely represents a [Def][1] in the [Def][1] [Pool] - /// - /// [1]: crate::definition::Def - DefID, + /// Uniquely represents an entry in the [item context](crate::sym_table) + Handle, } -/// A handle to a certain [Def] within a [Project] -#[derive(Clone, Copy, Debug)] -pub struct Handle<'prj, 'code> { - id: DefID, - prj: &'prj Project<'code>, -} - -impl DefID { - /// Constructs a new [Handle] from this DefID and the provided [Project]. - pub fn handle<'p, 'c>(self, prj: &'p Project<'c>) -> Option> { - Handle::new(self, prj) - } - - /// Constructs a new [Handle] from this DefID and the provided [Project] - pub fn handle_unchecked<'p, 'c>(self, prj: &'p Project<'c>) -> Handle<'p, 'c> { - Handle::new_unchecked(self, prj) - } -} - -impl<'p, 'c> Handle<'p, 'c> { - /// Constructs a new Handle from the provided [DefID] and [Project]. - /// Returns [Some]\(Handle) if the [DefID] exists in the [Project]. - pub fn new(id: DefID, prj: &'p Project<'c>) -> Option { - prj.pool.get(id).is_some().then_some(Self { id, prj }) - } - - /// Constructs a new Handle from the provided [DefID] and [Project] without checking membership. - /// Using the handle may cause panics or other unwanted (but defined) behavior. - pub fn new_unchecked(id: DefID, prj: &'p Project<'c>) -> Self { - Self { id, prj } - } - - /// Gets the [Def] this handle points to. - pub fn get(self) -> Option<&'p Def<'c>> { - self.prj.pool.get(self.id) - } - - pub fn navigate(self, path: Path) -> (Option, Option) { - match self.prj.get(path, self.id) { - Some((Some(ty), Some(vl), _)) => (Some(self.with(ty)), Some(self.with(vl))), - Some((_, Some(vl), _)) => (None, Some(self.with(vl))), - Some((Some(ty), _, _)) => (Some(self.with(ty)), None), - _ => (None, None), - } - } - - /// Gets the [Project] this handle points to. - pub fn project(self) -> &'p Project<'c> { - self.prj - } - - pub fn id(self) -> DefID { - self.id - } - - // TODO: get parent, children, etc. - - /// Gets a handle to the other ID within the same project - pub fn with(self, id: DefID) -> Self { - Self { id, ..self } - } -} - -mod emulate_def { - use super::*; - use cl_ast::{Sym, Visibility}; - use cl_structures::span::Span; - use std::collections::HashMap; - - impl<'p, 'c> Handle<'p, 'c> { - pub fn name(self) -> Option { - self.get()?.name() - } - pub fn vis(self) -> Option { - Some(self.get()?.node.vis) - } - pub fn span(self) -> Option { - Some(*self.get()?.node.span) - } - pub fn parent(self) -> Option { - self.get()?.module.parent.map(|id| self.with(id)) - } - pub fn types(self) -> Option<&'p HashMap> { - let types = &self.get()?.module.types; - (!types.is_empty()).then_some(types) - } - pub fn values(self) -> Option<&'p HashMap> { - let values = &self.get()?.module.values; - (!values.is_empty()).then_some(values) - } - } -} - -mod display { - use super::*; - use crate::{definition::*, format_utils::*}; - use std::fmt::{self, Display, Write}; - - impl Display for DefID { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } - } - - impl Display for Handle<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { id, prj } = *self; - let Some(def) = prj.pool.get(id) else { - return write!(f, ""); - }; - - // Match the type - match &def.kind { - DefKind::Undecided => write!(f, ""), - DefKind::Impl(id) => write!(f, "impl {}", self.with(*id)), - DefKind::Use(id) => write!(f, "use inside {}", self.with(*id)), - DefKind::Type(kind) => match kind { - TypeKind::Alias(None) => write!(f, "type"), - TypeKind::Alias(Some(id)) => write!(f, "{}", self.with(*id)), - TypeKind::Intrinsic(intrinsic) => write!(f, "{intrinsic}"), - TypeKind::Adt(adt) => display_adt(self, adt, f), - TypeKind::Ref(count, id) => { - for _ in 0..*count { - f.write_char('&')?; - } - self.with(*id).fmt(f) - } - TypeKind::Array(id, size) => { - write!(f.delimit_with("[", "]"), "{}; {size}", self.with(*id)) - } - TypeKind::Slice(id) => write!(f.delimit_with("[", "]"), "{}", self.with(*id)), - TypeKind::Tuple(ids) => { - let mut ids = ids.iter(); - separate(", ", || { - let id = ids.next()?; - Some(move |f: &mut Delimit<_>| write!(f, "{}", self.with(*id))) - })(f.delimit_with("(", ")")) - } - TypeKind::FnSig { args, rety } => { - write!(f, "fn {} -> {}", self.with(*args), self.with(*rety)) - } - TypeKind::Empty => write!(f, "()"), - TypeKind::Never => write!(f, "!"), - TypeKind::Module => match def.name() { - Some(name) => write!(f, "mod {name}"), - None => write!(f, "mod"), - }, - }, - - DefKind::Value(kind) => match kind { - ValueKind::Const(id) => write!(f, "const {}", self.with(*id)), - ValueKind::Static(id) => write!(f, "static {}", self.with(*id)), - ValueKind::Local(id) => write!(f, "local {}", self.with(*id)), - ValueKind::Fn(id) => write!(f, "{}", self.with(*id)), - }, - } - } - } - - /// Formats an ADT: a continuation of [Handle::fmt] - fn display_adt(handle: &Handle, adt: &Adt, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match adt { - Adt::Enum(variants) => { - let mut variants = variants.iter(); - separate(",", || { - variants.next().map(|(name, def)| { - move |f: &mut Delimit<_>| match def { - Some(def) => write!(f, "\n{name}: {}", handle.with(*def)), - None => write!(f, "\n{name}"), - } - }) - })(f.delimit_with("enum {", "\n}")) - } - Adt::CLikeEnum(variants) => { - let mut variants = variants.iter(); - separate(",", || { - let (name, descrim) = variants.next()?; - Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}")) - })(f.delimit_with("enum {", "\n}")) - } - Adt::FieldlessEnum => write!(f, "enum"), - Adt::Struct(members) => { - let mut members = members.iter(); - separate(",", || { - let (name, vis, id) = members.next()?; - Some(move |f: &mut Delimit<_>| write!(f, "\n{vis}{name}: {}", handle.with(*id))) - })(f.delimit_with("struct {", "\n}")) - } - Adt::TupleStruct(members) => { - let mut members = members.iter(); - separate(", ", || { - let (vis, def) = members.next()?; - Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", handle.with(*def))) - })(f.delimit_with("struct (", ")")) - } - Adt::UnitStruct => write!(f, "struct;"), - Adt::Union(variants) => { - let mut variants = variants.iter(); - separate(",", || { - let (name, def) = variants.next()?; - Some(move |f: &mut Delimit<_>| write!(f, "\n{name}: {}", handle.with(*def))) - })(f.delimit_with("union {", "\n}")) - } - } +impl std::fmt::Display for Handle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) } } diff --git a/compiler/cl-typeck/src/import.rs b/compiler/cl-typeck/src/import.rs new file mode 100644 index 0000000..0f9f79a --- /dev/null +++ b/compiler/cl-typeck/src/import.rs @@ -0,0 +1,112 @@ +//! An algorithm for importing external nodes + +use crate::{ + handle::Handle, + source::Source, + table::{NodeKind, Table}, +}; +use cl_ast::{PathPart, Sym, Use, UseTree}; +use core::slice; +use std::mem; + +pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> { + let pending = mem::take(&mut table.uses); + let mut failed = vec![]; + for import in pending { + if let Err(e) = import_one(table, import) { + failed.push((import, e)); + } + } + failed +} + +fn import_one<'a>(table: &mut Table<'a>, item: Handle) -> UseResult<'a, ()> { + let Some(NodeKind::Use) = table.kind(item) else { + Err(Error::ItsNoUse)? + }; + let Some(&dst) = table.parent(item) else { + Err(Error::NoParents)? + }; + let Some(code) = table.source(item) else { + Err(Error::NoSource)? + }; + let &Source::Use(tree) = code else { + Err(Error::BadSource(*code))? + }; + let Use { absolute, tree } = tree; + + eprintln!("Resolving import {item}: {code}"); + import_tree(table, if !absolute { dst } else { table.root() }, dst, tree) +} + +fn import_tree<'a>( + table: &mut Table<'a>, + src: Handle, + dst: Handle, + tree: &UseTree, +) -> UseResult<'a, ()> { + match tree { + UseTree::Tree(trees) => { + for tree in trees { + import_tree(table, src, dst, tree)? + } + Ok(()) + } + UseTree::Path(part, rest) => { + let source = table + .nav(src, slice::from_ref(part)) + .ok_or_else(|| Error::NotFound(src, part.clone()))?; + import_tree(table, source, dst, rest) + } + UseTree::Alias(src_name, dst_name) => import_name(table, src, src_name, dst, dst_name), + UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name), + UseTree::Glob => import_glob(table, src, dst), + } +} + +fn import_glob<'a>(table: &mut Table<'a>, src: Handle, dst: Handle) -> UseResult<'a, ()> { + let Table { children, imports, .. } = table; + if let Some(children) = children.get(&src) { + // TODO: check for new imports clobbering existing imports + imports.entry(dst).or_default().extend(children); + } + Ok(()) +} + +fn import_name<'a>( + table: &mut Table<'a>, + src: Handle, + src_name: &Sym, + dst: Handle, + dst_name: &Sym, +) -> UseResult<'a, ()> { + match table.get_by_sym(src, src_name) { + // TODO: check for new imports clobbering existing imports + Some(src_id) => table.add_import(dst, *dst_name, src_id), + None => Err(Error::NotFound(src, PathPart::Ident(*src_name)))?, + }; + Ok(()) +} + +pub type UseResult<'a, T> = Result>; + +#[derive(Debug)] +pub enum Error<'a> { + ItsNoUse, + NoParents, + NoSource, + BadSource(Source<'a>), + NotFound(Handle, PathPart), +} + +impl std::fmt::Display for Error<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::ItsNoUse => write!(f, "Entry is not use"), + Error::NoParents => write!(f, "Entry has no parents"), + Error::NoSource => write!(f, "Entry has no source"), + Error::BadSource(s) => write!(f, "Entry incorrectly marked as use item: {s}"), + Error::NotFound(id, part) => write!(f, "Could not traverse {id}::{part}"), + } + } +} diff --git a/compiler/cl-typeck/src/lib.rs b/compiler/cl-typeck/src/lib.rs index f88ae19..5b3b6da 100644 --- a/compiler/cl-typeck/src/lib.rs +++ b/compiler/cl-typeck/src/lib.rs @@ -67,28 +67,28 @@ Value: Either */ -pub mod handle; - -pub mod node; - -pub mod definition; - -pub mod module; - -pub mod path; - -pub mod project; - -pub mod name_collector; - -pub mod use_importer; - -pub mod type_resolver; - pub mod inference; pub(crate) mod format_utils; +pub mod table; + +pub mod handle; + +pub mod entry; + +pub mod source; + +pub mod type_kind; + +pub mod type_expression; + +pub mod populate; + +pub mod import; + +pub mod categorize; + /* LET THERE BE NOTES: diff --git a/compiler/cl-typeck/src/module.rs b/compiler/cl-typeck/src/module.rs deleted file mode 100644 index 6caf65b..0000000 --- a/compiler/cl-typeck/src/module.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! A [Module] is a node in the Module Tree (a component of a -//! [Project](crate::project::Project)) -use cl_ast::Sym; -use cl_structures::index_map::MapIndex; - -use crate::handle::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 imports: Vec, -} - -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() } - } - - pub fn get(&self, name: Sym) -> (Option, Option) { - (self.get_type(name), self.get_value(name)) - } - pub fn get_type(&self, name: Sym) -> Option { - self.types.get(&name).copied() - } - pub fn get_value(&self, name: Sym) -> Option { - self.values.get(&name).copied() - } - - /// Inserts a type with the provided [name](str) and [id](DefID) - pub fn insert_type(&mut self, name: Sym, id: DefID) -> Option { - self.types.insert(name, id) - } - - /// Inserts a value with the provided [name](str) and [id](DefID) - pub fn insert_value(&mut self, name: Sym, id: DefID) -> Option { - self.values.insert(name, id) - } -} - -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)] { - if table.is_empty() { - continue; - } - writeln!(f, "{name}:")?; - for (name, id) in table.iter() { - writeln!(f, " {name} => {id}")?; - } - } - if !imports.is_empty() { - write!(f, "Imports:")?; - for id in imports { - write!(f, "{id},")?; - } - } - Ok(()) - } -} diff --git a/compiler/cl-typeck/src/name_collector.rs b/compiler/cl-typeck/src/name_collector.rs deleted file mode 100644 index 6283e6c..0000000 --- a/compiler/cl-typeck/src/name_collector.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Performs step 1 of type checking: Collecting all the names of things into [Module] units -use crate::{ - definition::{Def, DefKind}, - handle::DefID, - module::Module as Mod, - node::{Node, NodeSource}, - project::Project as Prj, -}; -use cl_ast::{ast_visitor::Visit, *}; -use std::mem; - -#[derive(Debug)] -pub struct NameCollector<'prj, 'a> { - path: cl_ast::Path, - prj: &'prj mut Prj<'a>, - parent: DefID, - retval: Option, -} - -impl<'prj, 'a> NameCollector<'prj, 'a> { - /// Constructs a new [NameCollector] out of a [Project](Prj) - pub fn new(prj: &'prj mut Prj<'a>) -> Self { - Self { parent: prj.root, prj, path: Default::default(), retval: None } - } - /// Constructs a new [NameCollector] out of a [Project](Prj) and a parent [DefID] - pub fn with_root(prj: &'prj mut Prj<'a>, parent: DefID) -> Self { - Self { prj, parent, path: Default::default(), retval: None } - } - /// Runs the provided function with the given parent - pub fn with_parent(&mut self, parent: DefID, node: N, f: F) - where F: FnOnce(&mut Self, N) { - let parent = mem::replace(&mut self.parent, parent); - f(self, node); - self.parent = parent; - } - /// Extracts the return value from the provided function - pub fn returns(&mut self, node: N, f: F) -> Option - where F: FnOnce(&mut Self, N) { - let out = self.retval.take(); - f(self, node); - mem::replace(&mut self.retval, out) - } -} - -impl<'prj, 'a> Visit<'a> for NameCollector<'prj, 'a> { - fn visit_item(&mut self, i: &'a Item) { - let Item { extents: _, attrs, vis, kind } = i; - if let Some(def) = self.returns(kind, Self::visit_item_kind) { - self.prj[def].set_meta(&attrs.meta).set_vis(*vis); - } - } - fn visit_module(&mut self, m: &'a Module) { - let Self { prj, parent, path, retval: _ } = self; - let Module { name, kind } = m; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Module(m))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_type(*name, id); - self.path.push(PathPart::Ident(*name)); - self.with_parent(id, kind, Self::visit_module_kind); - self.path.pop(); - self.retval = Some(id); - } - fn visit_alias(&mut self, a: &'a Alias) { - let Self { prj, parent, path, retval: _ } = self; - let Alias { to: name, from: _ } = a; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Alias(a))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_type(*name, id); - - self.retval = Some(id); - } - fn visit_enum(&mut self, e: &'a Enum) { - let Self { prj, parent, path, retval: _ } = self; - let Enum { name, kind } = e; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Enum(e))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_type(*name, id); - - self.with_parent(id, kind, Self::visit_enum_kind); - self.retval = Some(id); - } - fn visit_variant(&mut self, v: &'a Variant) { - let Self { path, prj, parent, retval: _ } = self; - let Variant { name, kind } = v; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Variant(v))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_type(*name, id); - - self.with_parent(id, kind, Self::visit_variant_kind); - self.retval = Some(id); - } - fn visit_struct(&mut self, s: &'a Struct) { - let Self { prj, parent, path, retval: _ } = self; - let Struct { name, kind } = s; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Struct(s))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_type(*name, id); - - self.with_parent(id, kind, Self::visit_struct_kind); - self.retval = Some(id); - } - fn visit_const(&mut self, c: &'a Const) { - let Self { prj, parent, path, retval: _ } = self; - let Const { name, ty: _, init } = c; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Const(c))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_value(*name, id); - - self.with_parent(id, &**init, Self::visit_expr); - self.retval = Some(id); - } - fn visit_static(&mut self, s: &'a Static) { - let Self { prj, parent, path, retval: _ } = self; - let Static { name, mutable: _, ty: _, init } = s; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Static(s))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_value(*name, id); - - self.with_parent(id, &**init, Self::visit_expr); - self.retval = Some(id); - } - fn visit_function(&mut self, f: &'a Function) { - let Self { prj, parent, path, retval: _ } = self; - let Function { name, body, .. } = f; - - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Function(f))), - }; - let id = prj.pool.insert(def); - prj[*parent].module.insert_value(*name, id); - - if let Some(body) = body { - self.with_parent(id, body, Self::visit_block); - } - self.retval = Some(id); - } - fn visit_impl(&mut self, i: &'a Impl) { - let Self { prj, parent, path, retval: _ } = self; - let Impl { target: _, body } = i; - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Impl(i))), - }; - let id = prj.pool.insert(def); - - // items will get reparented after name collection, when target is available - self.with_parent(id, body, Self::visit_file); - - self.retval = Some(id); - } - fn visit_use(&mut self, u: &'a Use) { - let Self { prj, parent, path, retval } = self; - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Use(*parent), - node: Node::new(path.clone(), Some(NodeSource::Use(u))), - }; - - let id = prj.pool.insert(def); - prj[*parent].module.imports.push(id); - - *retval = Some(id); - } - fn visit_let(&mut self, l: &'a Let) { - let Self { prj, parent, path, retval: _ } = self; - let Let { name, init, .. } = l; - let def = Def { - module: Mod::new(*parent), - kind: DefKind::Undecided, - node: Node::new(path.clone(), Some(NodeSource::Local(l))), - }; - - let id = prj.pool.insert(def); - prj[*parent].module.insert_value(*name, id); - if let Some(expr) = init { - self.visit_expr(expr) - } - self.retval = Some(id) - } -} diff --git a/compiler/cl-typeck/src/node.rs b/compiler/cl-typeck/src/node.rs deleted file mode 100644 index e390ea9..0000000 --- a/compiler/cl-typeck/src/node.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! A [Node] contains the [NodeSource] and [Item] metadata for any -//! [Def](crate::definition::Def), as well as the [Path] of the -//! containing [Module]. -//! -//! [Node]s are collected by the [Node Sorcerer](sorcerer), -//! an AST visitor that pairs [NodeSource]s with their surrounding -//! context ([Path], [struct@Span], [Meta], [Visibility]) - -use cl_ast::ast::*; -use cl_structures::span::Span; -use std::fmt; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Node<'a> { - pub in_path: Path, - pub span: &'a Span, - pub meta: &'a [Meta], - pub vis: Visibility, - pub kind: Option>, -} - -impl<'a> Node<'a> { - pub fn new(path: Path, kind: Option>) -> Self { - const DUMMY_SPAN: Span = Span::dummy(); - Self { in_path: path, span: &DUMMY_SPAN, meta: &[], vis: Visibility::Public, kind } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NodeSource<'a> { - Root, - Module(&'a Module), - Alias(&'a Alias), - Enum(&'a Enum), - Variant(&'a Variant), - Struct(&'a Struct), - Const(&'a Const), - Static(&'a Static), - Function(&'a Function), - Local(&'a Let), - Impl(&'a Impl), - Use(&'a Use), - Ty(&'a TyKind), -} - -impl<'a> NodeSource<'a> { - pub fn name(&self) -> Option { - match self { - NodeSource::Root => None, - NodeSource::Module(v) => Some(v.name), - NodeSource::Alias(v) => Some(v.to), - NodeSource::Enum(v) => Some(v.name), - NodeSource::Variant(v) => Some(v.name), - NodeSource::Struct(v) => Some(v.name), - NodeSource::Const(v) => Some(v.name), - NodeSource::Static(v) => Some(v.name), - NodeSource::Function(v) => Some(v.name), - NodeSource::Local(l) => Some(l.name), - NodeSource::Impl(_) | NodeSource::Use(_) | NodeSource::Ty(_) => None, - } - } - - /// Returns `true` if this [NodeSource] defines a named value - pub fn is_named_value(&self) -> bool { - matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_)) - } - - /// Returns `true` if this [NodeSource] defines a named type - pub fn is_named_type(&self) -> bool { - matches!( - self, - Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_) - ) - } - - /// Returns `true` if this [NodeSource] refers to a [Ty] with no name - pub fn is_anon_type(&self) -> bool { - matches!(self, Self::Ty(_)) - } - - /// Returns `true` if this [NodeSource] refers to an [Impl] block - pub fn is_impl(&self) -> bool { - matches!(self, Self::Impl(_)) - } - - /// Returns `true` if this [NodeSource] refers to a [Use] import - pub fn is_use_import(&self) -> bool { - matches!(self, Self::Use(_)) - } -} - -impl fmt::Display for NodeSource<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Root => "🌳 root 🌳".fmt(f), - Self::Module(arg0) => arg0.fmt(f), - Self::Alias(arg0) => arg0.fmt(f), - Self::Enum(arg0) => arg0.fmt(f), - Self::Variant(arg0) => arg0.fmt(f), - Self::Struct(arg0) => arg0.fmt(f), - Self::Const(arg0) => arg0.fmt(f), - Self::Static(arg0) => arg0.fmt(f), - Self::Function(arg0) => arg0.fmt(f), - Self::Impl(arg0) => arg0.fmt(f), - Self::Use(arg0) => arg0.fmt(f), - Self::Ty(arg0) => arg0.fmt(f), - Self::Local(arg0) => arg0.fmt(f), - } - } -} - -pub mod sorcerer { - //! An [AST](cl_ast) analysis pass that collects [Node] entries. - - use super::{Node, NodeSource}; - use cl_ast::{ast::*, ast_visitor::visit::*}; - use cl_structures::span::Span; - use std::mem; - - /// An AST analysis pass that collects [Node]s - #[derive(Clone, Debug)] - pub struct NodeSorcerer<'a> { - path: Path, - parts: Parts<'a>, - defs: Vec>, - } - - type Parts<'a> = (&'a Span, &'a [Meta], Visibility); - - impl<'a> NodeSorcerer<'a> { - pub fn into_defs(self) -> Vec> { - self.defs - } - - fn with_parts(&mut self, s: &'a Span, a: &'a [Meta], v: Visibility, f: F) - where F: FnOnce(&mut Self) { - let parts = mem::replace(&mut self.parts, (s, a, v)); - f(self); - self.parts = parts; - } - - fn with_only_span(&mut self, span: &'a Span, f: F) - where F: FnOnce(&mut Self) { - self.with_parts(span, &[], Visibility::Public, f) - } - - fn push(&mut self, kind: NodeSource<'a>) { - let Self { path, parts, defs } = self; - let (span, meta, vis) = *parts; - - defs.push(Node { in_path: path.clone(), span, meta, vis, kind: Some(kind) }) - } - } - - impl Default for NodeSorcerer<'_> { - fn default() -> Self { - const DPARTS: Parts = (&Span::dummy(), &[], Visibility::Private); - Self { - path: Path { absolute: true, ..Default::default() }, - parts: DPARTS, - defs: Default::default(), - } - } - } - - impl<'a> Visit<'a> for NodeSorcerer<'a> { - fn visit_module(&mut self, m: &'a Module) { - let Module { name, kind } = m; - self.path.push(PathPart::Ident(*name)); - self.visit_module_kind(kind); - self.path.pop(); - } - fn visit_item(&mut self, i: &'a Item) { - let Item { extents, attrs, vis, kind } = i; - self.with_parts(extents, &attrs.meta, *vis, |v| { - v.visit_item_kind(kind); - }); - } - fn visit_ty(&mut self, t: &'a Ty) { - let Ty { extents, kind } = t; - self.with_only_span(extents, |v| { - v.push(NodeSource::Ty(kind)); - v.visit_ty_kind(kind); - }); - } - fn visit_stmt(&mut self, s: &'a Stmt) { - let Stmt { extents, kind, semi } = s; - self.with_only_span(extents, |d| { - d.visit_stmt_kind(kind); - d.visit_semi(semi); - }) - } - fn visit_item_kind(&mut self, kind: &'a ItemKind) { - match kind { - ItemKind::Module(i) => self.push(NodeSource::Module(i)), - ItemKind::Alias(i) => self.push(NodeSource::Alias(i)), - ItemKind::Enum(i) => self.push(NodeSource::Enum(i)), - ItemKind::Struct(i) => self.push(NodeSource::Struct(i)), - ItemKind::Const(i) => self.push(NodeSource::Const(i)), - ItemKind::Static(i) => self.push(NodeSource::Static(i)), - ItemKind::Function(i) => self.push(NodeSource::Function(i)), - ItemKind::Impl(i) => self.push(NodeSource::Impl(i)), - ItemKind::Use(i) => self.push(NodeSource::Use(i)), - } - or_visit_item_kind(self, kind); - } - fn visit_stmt_kind(&mut self, kind: &'a StmtKind) { - if let StmtKind::Local(l) = kind { - self.push(NodeSource::Local(l)) - } - or_visit_stmt_kind(self, kind); - } - } -} diff --git a/compiler/cl-typeck/src/path.rs b/compiler/cl-typeck/src/path.rs deleted file mode 100644 index 974f87b..0000000 --- a/compiler/cl-typeck/src/path.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! A [Path] is a borrowed view of an [AST Path](AstPath) -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 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 first(&self) -> Option<&PathPart> { - self.parts.first() - } -} - -impl<'p> From<&'p AstPath> for Path<'p> { - fn from(value: &'p AstPath) -> Self { - Self::new(value) - } -} -impl AsRef<[PathPart]> for Path<'_> { - fn as_ref(&self) -> &[PathPart] { - self.parts - } -} - -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(()) - } -} diff --git a/compiler/cl-typeck/src/populate.rs b/compiler/cl-typeck/src/populate.rs new file mode 100644 index 0000000..bb248ba --- /dev/null +++ b/compiler/cl-typeck/src/populate.rs @@ -0,0 +1,179 @@ +//! The [Populator] populates entries in the sym table, including span info +use crate::{ + entry::EntryMut, + handle::Handle, + source::Source, + table::{NodeKind, Table}, +}; +use cl_ast::{ast_visitor::Visit, ItemKind, Sym}; + +#[derive(Debug)] +pub struct Populator<'t, 'a> { + inner: EntryMut<'t, 'a>, + name: Option, // this is a hack to get around the Visitor interface +} + +impl<'t, 'a> Populator<'t, 'a> { + pub fn new(table: &'t mut Table<'a>) -> Self { + Self { inner: table.root_handle_mut(), name: None } + } + /// Constructs a new Populator with the provided parent DefID + pub fn with_id(&mut self, parent: Handle) -> Populator<'_, 'a> { + Populator { inner: self.inner.with_id(parent), name: None } + } + + pub fn new_entry(&mut self, kind: NodeKind) -> Populator<'_, 'a> { + Populator { inner: self.inner.new_entry(kind), name: None } + } + + pub fn set_name(&mut self, name: Sym) { + self.name = Some(name); + } +} + +impl<'a> Visit<'a> for Populator<'_, 'a> { + fn visit_item(&mut self, i: &'a cl_ast::Item) { + let cl_ast::Item { extents, attrs, vis, kind } = i; + // TODO: this, better, better. + let entry_kind = match kind { + ItemKind::Alias(_) => NodeKind::Type, + ItemKind::Enum(_) => NodeKind::Type, + ItemKind::Struct(_) => NodeKind::Type, + + ItemKind::Const(_) => NodeKind::Const, + ItemKind::Static(_) => NodeKind::Static, + ItemKind::Function(_) => NodeKind::Function, + + ItemKind::Module(_) => NodeKind::Module, + ItemKind::Impl(_) => NodeKind::Impl, + ItemKind::Use(_) => NodeKind::Use, + }; + + let mut entry = self.new_entry(entry_kind); + entry.inner.set_span(*extents); + entry.inner.set_meta(&attrs.meta); + + entry.visit_span(extents); + entry.visit_attrs(attrs); + entry.visit_visibility(vis); + entry.visit_item_kind(kind); + + if let (Some(name), child) = (entry.name, entry.inner.id()) { + self.inner.add_child(name, child); + } + } + + fn visit_alias(&mut self, a: &'a cl_ast::Alias) { + let cl_ast::Alias { to, from } = a; + self.inner.set_source(Source::Alias(a)); + self.set_name(*to); + + if let Some(t) = from { + self.visit_ty(t) + } + } + + fn visit_const(&mut self, c: &'a cl_ast::Const) { + let cl_ast::Const { name, ty, init } = c; + self.inner.set_source(Source::Const(c)); + self.set_name(*name); + + self.visit_ty(ty); + self.visit_expr(init); + } + + fn visit_static(&mut self, s: &'a cl_ast::Static) { + let cl_ast::Static { mutable, name, ty, init } = s; + self.inner.set_source(Source::Static(s)); + self.set_name(*name); + + self.visit_mutability(mutable); + self.visit_ty(ty); + self.visit_expr(init); + } + + fn visit_module(&mut self, m: &'a cl_ast::Module) { + let cl_ast::Module { name, kind } = m; + self.inner.set_source(Source::Module(m)); + self.set_name(*name); + + self.visit_module_kind(kind); + } + + fn visit_function(&mut self, f: &'a cl_ast::Function) { + let cl_ast::Function { name, sign, bind, body } = f; + self.inner.set_source(Source::Function(f)); + self.set_name(*name); + + self.visit_ty_fn(sign); + bind.iter().for_each(|p| self.visit_param(p)); + if let Some(b) = body { + self.visit_block(b) + } + } + + fn visit_struct(&mut self, s: &'a cl_ast::Struct) { + let cl_ast::Struct { name, kind } = s; + self.inner.set_source(Source::Struct(s)); + self.set_name(*name); + + self.visit_struct_kind(kind); + } + + fn visit_enum(&mut self, e: &'a cl_ast::Enum) { + let cl_ast::Enum { name, kind } = e; + self.inner.set_source(Source::Enum(e)); + self.set_name(*name); + + self.visit_enum_kind(kind); + } + + fn visit_impl(&mut self, i: &'a cl_ast::Impl) { + let cl_ast::Impl { target, body } = i; + self.inner.set_source(Source::Impl(i)); + self.inner.mark_impl_item(); + + self.visit_impl_kind(target); + self.visit_file(body); + } + + fn visit_use(&mut self, u: &'a cl_ast::Use) { + let cl_ast::Use { absolute: _, tree } = u; + self.inner.set_source(Source::Use(u)); + self.inner.mark_use_item(); + + self.visit_use_tree(tree); + } + + fn visit_stmt(&mut self, s: &'a cl_ast::Stmt) { + let cl_ast::Stmt { extents, kind, semi } = s; + let cl_ast::StmtKind::Local(local) = kind else { + self.visit_span(extents); + self.visit_stmt_kind(kind); + self.visit_semi(semi); + return; + }; + + let mut entry = self.new_entry(NodeKind::Local); + entry.inner.set_span(*extents); + entry.visit_let(local); + + if let (Some(name), child) = (entry.name, entry.inner.id()) { + self.inner.add_child(name, child); + } + } + + fn visit_let(&mut self, l: &'a cl_ast::Let) { + let cl_ast::Let { mutable, name, ty, init } = l; + self.inner.set_source(Source::Local(l)); + self.set_name(*name); + + self.visit_mutability(mutable); + if let Some(ty) = ty { + self.visit_ty(ty); + } + if let Some(init) = init { + self.visit_expr(init) + } + } +} diff --git a/compiler/cl-typeck/src/source.rs b/compiler/cl-typeck/src/source.rs new file mode 100644 index 0000000..5b8f587 --- /dev/null +++ b/compiler/cl-typeck/src/source.rs @@ -0,0 +1,87 @@ +//! Holds the [Source] of a definition in the AST + +use cl_ast::ast::*; +use std::fmt; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Source<'a> { + Root, + Module(&'a Module), + Alias(&'a Alias), + Enum(&'a Enum), + Variant(&'a Variant), + Struct(&'a Struct), + Const(&'a Const), + Static(&'a Static), + Function(&'a Function), + Local(&'a Let), + Impl(&'a Impl), + Use(&'a Use), + Ty(&'a TyKind), +} + +impl<'a> Source<'a> { + pub fn name(&self) -> Option { + match self { + Source::Root => None, + Source::Module(v) => Some(v.name), + Source::Alias(v) => Some(v.to), + Source::Enum(v) => Some(v.name), + Source::Variant(v) => Some(v.name), + Source::Struct(v) => Some(v.name), + Source::Const(v) => Some(v.name), + Source::Static(v) => Some(v.name), + Source::Function(v) => Some(v.name), + Source::Local(l) => Some(l.name), + Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None, + } + } + + /// Returns `true` if this [NodeSource] defines a named value + pub fn is_named_value(&self) -> bool { + matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_)) + } + + /// Returns `true` if this [NodeSource] defines a named type + pub fn is_named_type(&self) -> bool { + matches!( + self, + Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_) + ) + } + + /// Returns `true` if this [NodeSource] refers to a [Ty] with no name + pub fn is_anon_type(&self) -> bool { + matches!(self, Self::Ty(_)) + } + + /// Returns `true` if this [NodeSource] refers to an [Impl] block + pub fn is_impl(&self) -> bool { + matches!(self, Self::Impl(_)) + } + + /// Returns `true` if this [NodeSource] refers to a [Use] import + pub fn is_use_import(&self) -> bool { + matches!(self, Self::Use(_)) + } +} + +impl fmt::Display for Source<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Root => "🌳 root 🌳".fmt(f), + Self::Module(arg0) => arg0.fmt(f), + Self::Alias(arg0) => arg0.fmt(f), + Self::Enum(arg0) => arg0.fmt(f), + Self::Variant(arg0) => arg0.fmt(f), + Self::Struct(arg0) => arg0.fmt(f), + Self::Const(arg0) => arg0.fmt(f), + Self::Static(arg0) => arg0.fmt(f), + Self::Function(arg0) => arg0.fmt(f), + Self::Impl(arg0) => arg0.fmt(f), + Self::Use(arg0) => arg0.fmt(f), + Self::Ty(arg0) => arg0.fmt(f), + Self::Local(arg0) => arg0.fmt(f), + } + } +} diff --git a/compiler/cl-typeck/src/table.rs b/compiler/cl-typeck/src/table.rs new file mode 100644 index 0000000..fb3ba92 --- /dev/null +++ b/compiler/cl-typeck/src/table.rs @@ -0,0 +1,269 @@ +//! Conlang's symbol table + +use crate::{ + entry::{Entry, EntryMut}, + handle::Handle, + source::Source, + type_kind::TypeKind, +}; +use cl_ast::{Meta, PathPart, Sym}; +use cl_structures::{index_map::IndexMap, span::Span}; +use std::collections::HashMap; + +// TODO: Cycle detection external to this module + +#[derive(Debug)] +pub struct Table<'a> { + root: Handle, + /// This is the source of truth for handles + kinds: IndexMap, + parents: IndexMap, + pub(crate) children: HashMap>, + pub(crate) imports: HashMap>, + types: HashMap, + spans: HashMap, + metas: HashMap, + sources: HashMap>, + // code: HashMap, // TODO: lower sources + impl_targets: HashMap, + anon_types: HashMap, + pub(crate) impls: Vec, + pub(crate) uses: Vec, +} + +impl<'a> Table<'a> { + pub fn new() -> Self { + let mut kinds = IndexMap::new(); + let mut parents = IndexMap::new(); + let root = kinds.insert(NodeKind::Root); + assert_eq!(root, parents.insert(root)); + + Self { + root, + kinds, + parents, + children: HashMap::new(), + imports: HashMap::new(), + types: HashMap::new(), + spans: HashMap::new(), + metas: HashMap::new(), + sources: HashMap::new(), + impl_targets: HashMap::new(), + anon_types: HashMap::new(), + impls: Vec::new(), + uses: Vec::new(), + } + } + + pub fn entry(&self, handle: Handle) -> Entry<'_, 'a> { + handle.to_entry(self) + } + + pub fn entry_mut(&mut self, handle: Handle) -> EntryMut<'_, 'a> { + handle.to_entry_mut(self) + } + + pub fn new_entry(&mut self, parent: Handle, kind: NodeKind) -> Handle { + let entry = self.kinds.insert(kind); + assert_eq!(entry, self.parents.insert(parent)); + entry + } + + pub fn add_child(&mut self, parent: Handle, name: Sym, child: Handle) -> Option { + self.children.entry(parent).or_default().insert(name, child) + } + + pub fn add_import(&mut self, parent: Handle, name: Sym, import: Handle) -> Option { + self.imports.entry(parent).or_default().insert(name, import) + } + + pub fn mark_use_item(&mut self, item: Handle) { + self.uses.push(item); + } + + pub fn mark_impl_item(&mut self, item: Handle) { + self.impls.push(item); + } + + /// Returns handles to all nodes sequentially by [DefID] + pub fn debug_handle_iter(&self) -> impl Iterator> { + self.kinds.keys().map(|key| key.to_entry(self)) + } + + /// Gets the [DefID] of an anonymous type with the provided [TypeKind]. + /// If not already present, a new one is created. + pub fn anon_type(&mut self, kind: TypeKind) -> Handle { + if let Some(id) = self.anon_types.get(&kind) { + return *id; + } + let entry = self.new_entry(self.root, NodeKind::Type); + // Anonymous types require a bijective map (anon_types => Def => types) + self.types.insert(entry, kind.clone()); + self.anon_types.insert(kind, entry); + entry + } + + pub const fn root_handle(&self) -> Entry<'_, 'a> { + self.root.to_entry(self) + } + + pub fn root_handle_mut(&mut self) -> crate::entry::EntryMut<'_, 'a> { + self.root.to_entry_mut(self) + } + + // --- inherent properties --- + + pub const fn root(&self) -> Handle { + self.root + } + + pub fn kind(&self, node: Handle) -> Option<&NodeKind> { + self.kinds.get(node) + } + + pub fn parent(&self, node: Handle) -> Option<&Handle> { + self.parents.get(node) + } + + pub fn children(&self, node: Handle) -> Option<&HashMap> { + self.children.get(&node) + } + + pub fn imports(&self, node: Handle) -> Option<&HashMap> { + self.imports.get(&node) + } + + pub fn ty(&self, node: Handle) -> Option<&TypeKind> { + self.types.get(&node) + } + + pub fn span(&self, node: Handle) -> Option<&Span> { + self.spans.get(&node) + } + + pub fn meta(&self, node: Handle) -> Option<&'a [Meta]> { + self.metas.get(&node).copied() + } + + pub fn source(&self, node: Handle) -> Option<&Source<'a>> { + self.sources.get(&node) + } + + pub fn impl_target(&self, node: Handle) -> Option { + self.impl_targets.get(&node).copied() + } + + pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option { + self.types.insert(node, kind) + } + + pub fn set_span(&mut self, node: Handle, span: Span) -> Option { + self.spans.insert(node, span) + } + + pub fn set_meta(&mut self, node: Handle, meta: &'a [Meta]) -> Option<&'a [Meta]> { + self.metas.insert(node, meta) + } + + pub fn set_source(&mut self, node: Handle, source: Source<'a>) -> Option> { + self.sources.insert(node, source) + } + + pub fn set_impl_target(&mut self, node: Handle, target: Handle) -> Option { + self.impl_targets.insert(node, target) + } + + // --- derived properties --- + + /// Gets a handle to the local `Self` type, if one exists + pub fn selfty(&self, node: Handle) -> Option { + match self.kinds.get(node)? { + NodeKind::Root | NodeKind::Use => None, + NodeKind::Type => Some(node), + NodeKind::Impl => self.impl_target(node), + _ => self.selfty(*self.parent(node)?), + } + } + + pub fn name(&self, node: Handle) -> Option { + self.source(node).and_then(|s| s.name()) + } + + pub fn is_transparent(&self, node: Handle) -> bool { + !matches!( + self.kind(node), + None | Some(NodeKind::Root | NodeKind::Module) + ) + } + + pub fn get_child(&self, node: Handle, name: &Sym) -> Option { + self.children.get(&node).and_then(|c| c.get(name)).copied() + } + + pub fn get_import(&self, node: Handle, name: &Sym) -> Option { + self.imports.get(&node).and_then(|i| i.get(name)).copied() + } + + pub fn get_by_sym(&self, node: Handle, name: &Sym) -> Option { + self.get_child(node, name) + .or_else(|| self.get_import(node, name)) + .or_else(|| { + self.is_transparent(node) + .then(|| { + self.parent(node) + .and_then(|node| self.get_by_sym(*node, name)) + }) + .flatten() + }) + } + + /// Does path traversal relative to the provided `node`. + pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option { + match path { + [PathPart::SuperKw, rest @ ..] => self.nav(*self.parent(node)?, rest), + [PathPart::SelfKw, rest @ ..] => self.nav(node, rest), + [PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest), + [PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest), + [] => Some(node), + } + } +} + +impl<'a> Default for Table<'a> { + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy, Debug)] +pub enum NodeKind { + Root, + Module, + Type, + Const, + Static, + Function, + Local, + Impl, + Use, +} + +mod display { + use super::*; + use std::fmt; + impl fmt::Display for NodeKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NodeKind::Root => write!(f, "root"), + NodeKind::Module => write!(f, "mod"), + NodeKind::Type => write!(f, "type"), + NodeKind::Const => write!(f, "const"), + NodeKind::Static => write!(f, "static"), + NodeKind::Function => write!(f, "fn"), + NodeKind::Local => write!(f, "local"), + NodeKind::Use => write!(f, "use"), + NodeKind::Impl => write!(f, "impl"), + } + } + } +} diff --git a/compiler/cl-typeck/src/type_expression.rs b/compiler/cl-typeck/src/type_expression.rs new file mode 100644 index 0000000..bc0f451 --- /dev/null +++ b/compiler/cl-typeck/src/type_expression.rs @@ -0,0 +1,127 @@ +//! A [TypeExpression] is a [syntactic](cl_ast) representation of a [TypeKind], and is used to +//! construct type bindings in a [Table]'s typing context. + +use crate::{handle::Handle, table::Table, type_kind::TypeKind}; +use cl_ast::{PathPart, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple}; + +#[derive(Clone, Debug, PartialEq, Eq)] // TODO: impl Display and Error +pub enum Error { + BadPath { parent: Handle, path: Vec }, +} + +impl std::error::Error for Error {} +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::BadPath { parent, path } => { + write!(f, "No item at path {parent}")?; + for part in path { + write!(f, "::{part}")?; + } + } + } + Ok(()) + } +} + +/// A [TypeExpression] is a syntactic representation of a [TypeKind], and is used to construct +/// type bindings in a [Table]'s typing context. +pub trait TypeExpression { + /// Evaluates a type expression, recursively creating intermediate bindings. + fn evaluate(&self, table: &mut Table, node: Handle) -> Result; +} + +impl TypeExpression for Ty { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + self.kind.evaluate(table, node) + } +} + +impl TypeExpression for TyKind { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + match self { + TyKind::Never => Ok(table.anon_type(TypeKind::Never)), + TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)), + TyKind::Path(p) => p.evaluate(table, node), + TyKind::Array(a) => a.evaluate(table, node), + TyKind::Slice(s) => s.evaluate(table, node), + TyKind::Tuple(t) => t.evaluate(table, node), + TyKind::Ref(r) => r.evaluate(table, node), + TyKind::Fn(f) => f.evaluate(table, node), + } + } +} + +impl TypeExpression for cl_ast::Path { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { absolute, parts } = self; + parts.evaluate(table, if *absolute { table.root() } else { node }) + } +} + +impl TypeExpression for [PathPart] { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + table + .nav(node, self) + .ok_or_else(|| Error::BadPath { parent: node, path: self.to_owned() }) + } +} + +impl TypeExpression for TyArray { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { ty, count } = self; + let kind = TypeKind::Array(ty.evaluate(table, node)?, *count); + Ok(table.anon_type(kind)) + } +} + +impl TypeExpression for TySlice { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { ty } = self; + let kind = TypeKind::Slice(ty.evaluate(table, node)?); + Ok(table.anon_type(kind)) + } +} + +impl TypeExpression for TyTuple { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { types } = self; + let kind = match types.len() { + 0 => TypeKind::Empty, + _ => TypeKind::Tuple(types.evaluate(table, node)?), + }; + Ok(table.anon_type(kind)) + } +} + +impl TypeExpression for TyRef { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { mutable: _, count, to } = self; + let kind = TypeKind::Ref(*count, to.evaluate(table, node)?); + Ok(table.anon_type(kind)) + } +} + +impl TypeExpression for TyFn { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result { + let Self { args, rety } = self; + let kind = TypeKind::FnSig { + args: args.evaluate(table, node)?, + rety: match rety { + Some(ty) => ty.evaluate(table, node)?, + None => TyKind::Empty.evaluate(table, node)?, + }, + }; + Ok(table.anon_type(kind)) + } +} + +impl, U> TypeExpression> for [T] { + fn evaluate(&self, table: &mut Table, node: Handle) -> Result, Error> { + let mut out = Vec::with_capacity(self.len()); + for te in self { + out.push(te.evaluate(table, node)?) // try_collect is unstable + } + Ok(out) + } +} diff --git a/compiler/cl-typeck/src/definition.rs b/compiler/cl-typeck/src/type_kind.rs similarity index 56% rename from compiler/cl-typeck/src/definition.rs rename to compiler/cl-typeck/src/type_kind.rs index aba1cf0..c9eb8a3 100644 --- a/compiler/cl-typeck/src/definition.rs +++ b/compiler/cl-typeck/src/type_kind.rs @@ -1,110 +1,34 @@ -use crate::{ - handle::DefID, - module::Module, - node::{Node, NodeSource}, -}; -use cl_ast::{Meta, Sym, Visibility}; +use crate::handle::Handle; +use cl_ast::{Sym, Visibility}; use std::{fmt::Debug, str::FromStr}; mod display; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Def<'a> { - pub node: Node<'a>, - pub kind: DefKind, - pub module: Module, +pub enum EntryKind { + Variable(Option), + Operator(TypeKind), } -impl<'a> Def<'a> { - pub fn with_node(node: Node<'a>) -> Self { - Self { node, kind: DefKind::Undecided, module: Default::default() } - } -} - -impl Def<'_> { - pub fn name(&self) -> Option { - match self.node.kind { - Some(source) => source.name(), - None => None, - } - } - - pub fn is_transparent(&self) -> bool { - !matches!(self.kind, DefKind::Type(_)) - } -} - -mod builder_functions { - use super::*; - - impl<'a> Def<'a> { - pub fn set_vis(&mut self, vis: Visibility) -> &mut Self { - self.node.vis = vis; - self - } - pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self { - self.node.meta = meta; - self - } - pub fn set_kind(&mut self, kind: DefKind) -> &mut Self { - self.kind = kind; - self - } - pub fn set_source(&mut self, source: NodeSource<'a>) -> &mut Self { - self.node.kind = Some(source); - self - } - pub fn set_module(&mut self, module: Module) -> &mut Self { - self.module = module; - self - } - } -} - -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub enum DefKind { - /// An unevaluated definition - #[default] - Undecided, - /// An impl block - Impl(DefID), - /// A use tree, and its parent - Use(DefID), - /// A type, such as a `type`, `struct`, or `enum` - Type(TypeKind), - /// A value, such as a `const`, `static`, or `fn` - Value(ValueKind), -} - -/// A [ValueKind] represents an item in the Value Namespace -/// (a component of a [Project](crate::project::Project)). -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ValueKind { - Const(DefID), - Static(DefID), - Local(DefID), - Fn(DefID), -} -/// A [TypeKind] represents an item in the Type Namespace +/// A [TypeKind] represents an item /// (a component of a [Project](crate::project::Project)). #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum TypeKind { /// An alias for an already-defined type - Alias(Option), + Alias(Handle), /// A primitive type, built-in to the compiler Intrinsic(Intrinsic), /// A user-defined aromatic data type Adt(Adt), /// A reference to an already-defined type: &T - Ref(u16, DefID), + Ref(u16, Handle), /// A contiguous view of dynamically sized memory - Slice(DefID), + Slice(Handle), /// A contiguous view of statically sized memory - Array(DefID, usize), + Array(Handle, usize), /// A tuple of existing types - Tuple(Vec), + Tuple(Vec), /// A function which accepts multiple inputs and produces an output - FnSig { args: DefID, rety: DefID }, + FnSig { args: Handle, rety: Handle }, /// The unit type Empty, /// The never type @@ -117,22 +41,22 @@ pub enum TypeKind { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Adt { /// A union-like enum type - Enum(Vec<(Sym, Option)>), + Enum(Vec<(Sym, Option)>), /// A C-like enum CLikeEnum(Vec<(Sym, u128)>), /// An enum with no fields, which can never be constructed FieldlessEnum, /// A structural product type with named members - Struct(Vec<(Sym, Visibility, DefID)>), + Struct(Vec<(Sym, Visibility, Handle)>), /// A structural product type with unnamed members - TupleStruct(Vec<(Visibility, DefID)>), + TupleStruct(Vec<(Visibility, Handle)>), /// 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<(Sym, DefID)>), + Union(Vec<(Sym, Handle)>), } /// The set of compiler-intrinsic types. diff --git a/compiler/cl-typeck/src/definition/display.rs b/compiler/cl-typeck/src/type_kind/display.rs similarity index 69% rename from compiler/cl-typeck/src/definition/display.rs rename to compiler/cl-typeck/src/type_kind/display.rs index e5d4d88..dd665c6 100644 --- a/compiler/cl-typeck/src/definition/display.rs +++ b/compiler/cl-typeck/src/type_kind/display.rs @@ -1,60 +1,14 @@ //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic] -use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind}; -use crate::{format_utils::*, node::Node}; +use super::{Adt, Intrinsic, TypeKind}; +use crate::format_utils::*; use cl_ast::format::FmtAdapter; use std::fmt::{self, Display, Write}; -impl Display for Def<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self { module, node: Node { in_path: _, span: _, meta, vis, kind: source }, kind } = - self; - if !meta.is_empty() { - writeln!(f, "#{meta:?}")?; - } - if let Some(source) = source { - if let Some(name) = source.name() { - writeln!(f, "{vis}{name}:")?; - } - writeln!(f.indent(), "source:\n{source}")?; - } else { - writeln!(f, "{vis}: ")?; - } - writeln!(f, "kind: {kind}")?; - 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::Use(id) => write!(f, "use (inside {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::Local(id) => write!(f, "let ({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::Alias(def) => write!(f, "alias to #{def}"), TypeKind::Intrinsic(i) => i.fmt(f), TypeKind::Adt(a) => a.fmt(f), TypeKind::Ref(cnt, def) => {