cl-typeck: Crate-spanning refactor
TODO: remove all unreferenced files TODO: Finish resolving statically known types of values TODO: Type inference
This commit is contained in:
		| @@ -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<dyn Error>> { | ||||
|     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<dyn Error>> { | ||||
|         } | ||||
|     }; | ||||
|     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<dyn Error>> { | ||||
|     prj.resolve_imports()?; | ||||
|     for id in prj.pool.keys() { | ||||
|         resolve(prj, id)?; | ||||
| fn resolve_all(prj: &mut Table) -> Result<(), Box<dyn Error>> { | ||||
|     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(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(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) | ||||
|             )? | ||||
|         } | ||||
|     } | ||||
|     write!(stdout, "\x1b[0m") | ||||
|  | ||||
|     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) | ||||
|             )? | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 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<path::Path>) -> cl_ast::File { | ||||
|   | ||||
							
								
								
									
										69
									
								
								compiler/cl-typeck/src/categorize.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								compiler/cl-typeck/src/categorize.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<T> = Result<T, Error>; | ||||
|  | ||||
| pub enum Error { | ||||
|     NoSource, | ||||
|     BadMeta(Meta), | ||||
|     Recursive(Handle), | ||||
|     TypeEval(TypeEval), | ||||
| } | ||||
|  | ||||
| impl From<TypeEval> for Error { | ||||
|     fn from(value: TypeEval) -> Self { | ||||
|         Error::TypeEval(value) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										172
									
								
								compiler/cl-typeck/src/entry.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								compiler/cl-typeck/src/entry.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Entry<'t, 'a>> { | ||||
|         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<Sym, Handle>> { | ||||
|         self.table.children(self.id) | ||||
|     } | ||||
|  | ||||
|     pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> { | ||||
|         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<Handle> { | ||||
|         self.table.impl_target(self.id) | ||||
|     } | ||||
|  | ||||
|     pub fn selfty(&self) -> Option<Handle> { | ||||
|         self.table.selfty(self.id) | ||||
|     } | ||||
|  | ||||
|     pub fn name(&self) -> Option<Sym> { | ||||
|         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<EntryMut<'_, 'a>> { | ||||
|         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<Handle> { | ||||
|         self.table.add_child(self.id, name, child) | ||||
|     } | ||||
|  | ||||
|     pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> { | ||||
|         self.table.set_ty(self.id, kind) | ||||
|     } | ||||
|  | ||||
|     pub fn set_span(&mut self, span: Span) -> Option<Span> { | ||||
|         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<Source<'a>> { | ||||
|         self.table.set_source(self.id, source) | ||||
|     } | ||||
|  | ||||
|     pub fn set_impl_target(&mut self, target: Handle) -> Option<Handle> { | ||||
|         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) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										109
									
								
								compiler/cl-typeck/src/entry/display.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								compiler/cl-typeck/src/entry/display.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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, "<invalid type: {}>", 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"), | ||||
|     } | ||||
| } | ||||
| @@ -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<'p, 'c>> { | ||||
|         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<Self> { | ||||
|         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<Self>, Option<Self>) { | ||||
|         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<Sym> { | ||||
|             self.get()?.name() | ||||
|         } | ||||
|         pub fn vis(self) -> Option<Visibility> { | ||||
|             Some(self.get()?.node.vis) | ||||
|         } | ||||
|         pub fn span(self) -> Option<Span> { | ||||
|             Some(*self.get()?.node.span) | ||||
|         } | ||||
|         pub fn parent(self) -> Option<Self> { | ||||
|             self.get()?.module.parent.map(|id| self.with(id)) | ||||
|         } | ||||
|         pub fn types(self) -> Option<&'p HashMap<Sym, DefID>> { | ||||
|             let types = &self.get()?.module.types; | ||||
|             (!types.is_empty()).then_some(types) | ||||
|         } | ||||
|         pub fn values(self) -> Option<&'p HashMap<Sym, DefID>> { | ||||
|             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 { | ||||
| impl std::fmt::Display for Handle { | ||||
|     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, "<invalid type: {id}>"); | ||||
|             }; | ||||
|  | ||||
|             // Match the type | ||||
|             match &def.kind { | ||||
|                 DefKind::Undecided => write!(f, "<undecided>"), | ||||
|                 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}")) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										112
									
								
								compiler/cl-typeck/src/import.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								compiler/cl-typeck/src/import.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<T, Error<'a>>; | ||||
|  | ||||
| #[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}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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<DefID>, | ||||
|     pub types: HashMap<Sym, DefID>, | ||||
|     pub values: HashMap<Sym, DefID>, | ||||
|     pub imports: Vec<DefID>, | ||||
| } | ||||
|  | ||||
| impl Module { | ||||
|     pub fn new(parent: DefID) -> Self { | ||||
|         Self { parent: Some(parent), ..Default::default() } | ||||
|     } | ||||
|     pub fn with_optional_parent(parent: Option<DefID>) -> Self { | ||||
|         Self { parent, ..Default::default() } | ||||
|     } | ||||
|  | ||||
|     pub fn get(&self, name: Sym) -> (Option<DefID>, Option<DefID>) { | ||||
|         (self.get_type(name), self.get_value(name)) | ||||
|     } | ||||
|     pub fn get_type(&self, name: Sym) -> Option<DefID> { | ||||
|         self.types.get(&name).copied() | ||||
|     } | ||||
|     pub fn get_value(&self, name: Sym) -> Option<DefID> { | ||||
|         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<DefID> { | ||||
|         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<DefID> { | ||||
|         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(()) | ||||
|     } | ||||
| } | ||||
| @@ -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<DefID>, | ||||
| } | ||||
|  | ||||
| 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<F, N>(&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<F, N>(&mut self, node: N, f: F) -> Option<DefID> | ||||
|     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) | ||||
|     } | ||||
| } | ||||
| @@ -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<NodeSource<'a>>, | ||||
| } | ||||
|  | ||||
| impl<'a> Node<'a> { | ||||
|     pub fn new(path: Path, kind: Option<NodeSource<'a>>) -> 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<Sym> { | ||||
|         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<Node<'a>>, | ||||
|     } | ||||
|  | ||||
|     type Parts<'a> = (&'a Span, &'a [Meta], Visibility); | ||||
|  | ||||
|     impl<'a> NodeSorcerer<'a> { | ||||
|         pub fn into_defs(self) -> Vec<Node<'a>> { | ||||
|             self.defs | ||||
|         } | ||||
|  | ||||
|         fn with_parts<F>(&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<F>(&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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<Self> { | ||||
|         let Self { absolute, parts } = self; | ||||
|         Some(Self { absolute, parts: parts.get(1..)? }) | ||||
|     } | ||||
|     pub fn front(self) -> Option<Self> { | ||||
|         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(()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										179
									
								
								compiler/cl-typeck/src/populate.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								compiler/cl-typeck/src/populate.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Sym>, // 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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										87
									
								
								compiler/cl-typeck/src/source.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								compiler/cl-typeck/src/source.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Sym> { | ||||
|         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), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										269
									
								
								compiler/cl-typeck/src/table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								compiler/cl-typeck/src/table.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Handle, NodeKind>, | ||||
|     parents: IndexMap<Handle, Handle>, | ||||
|     pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>, | ||||
|     pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>, | ||||
|     types: HashMap<Handle, TypeKind>, | ||||
|     spans: HashMap<Handle, Span>, | ||||
|     metas: HashMap<Handle, &'a [Meta]>, | ||||
|     sources: HashMap<Handle, Source<'a>>, | ||||
|     // code: HashMap<DefID, BasicBlock>, // TODO: lower sources | ||||
|     impl_targets: HashMap<Handle, Handle>, | ||||
|     anon_types: HashMap<TypeKind, Handle>, | ||||
|     pub(crate) impls: Vec<Handle>, | ||||
|     pub(crate) uses: Vec<Handle>, | ||||
| } | ||||
|  | ||||
| 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<Handle> { | ||||
|         self.children.entry(parent).or_default().insert(name, child) | ||||
|     } | ||||
|  | ||||
|     pub fn add_import(&mut self, parent: Handle, name: Sym, import: Handle) -> Option<Handle> { | ||||
|         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<Item = Entry<'_, 'a>> { | ||||
|         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<Sym, Handle>> { | ||||
|         self.children.get(&node) | ||||
|     } | ||||
|  | ||||
|     pub fn imports(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> { | ||||
|         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<Handle> { | ||||
|         self.impl_targets.get(&node).copied() | ||||
|     } | ||||
|  | ||||
|     pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> { | ||||
|         self.types.insert(node, kind) | ||||
|     } | ||||
|  | ||||
|     pub fn set_span(&mut self, node: Handle, span: Span) -> Option<Span> { | ||||
|         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<Source<'a>> { | ||||
|         self.sources.insert(node, source) | ||||
|     } | ||||
|  | ||||
|     pub fn set_impl_target(&mut self, node: Handle, target: Handle) -> Option<Handle> { | ||||
|         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<Handle> { | ||||
|         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<Sym> { | ||||
|         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<Handle> { | ||||
|         self.children.get(&node).and_then(|c| c.get(name)).copied() | ||||
|     } | ||||
|  | ||||
|     pub fn get_import(&self, node: Handle, name: &Sym) -> Option<Handle> { | ||||
|         self.imports.get(&node).and_then(|i| i.get(name)).copied() | ||||
|     } | ||||
|  | ||||
|     pub fn get_by_sym(&self, node: Handle, name: &Sym) -> Option<Handle> { | ||||
|         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<Handle> { | ||||
|         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"), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										127
									
								
								compiler/cl-typeck/src/type_expression.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								compiler/cl-typeck/src/type_expression.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<PathPart> }, | ||||
| } | ||||
|  | ||||
| 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<Out = Handle> { | ||||
|     /// Evaluates a type expression, recursively creating intermediate bindings. | ||||
|     fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Out, Error>; | ||||
| } | ||||
|  | ||||
| impl TypeExpression for Ty { | ||||
|     fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> { | ||||
|         self.kind.evaluate(table, node) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TypeExpression for TyKind { | ||||
|     fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<Handle, Error> { | ||||
|         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<T: TypeExpression<U>, U> TypeExpression<Vec<U>> for [T] { | ||||
|     fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Vec<U>, 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) | ||||
|     } | ||||
| } | ||||
| @@ -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<Handle>), | ||||
|     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<Sym> { | ||||
|         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<DefID>), | ||||
|     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<DefID>), | ||||
|     Tuple(Vec<Handle>), | ||||
|     /// 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<DefID>)>), | ||||
|     Enum(Vec<(Sym, Option<Handle>)>), | ||||
|     /// 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.
 | ||||
| @@ -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) => { | ||||
		Reference in New Issue
	
	Block a user