Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4d6b94b570 | |||
| fe2b816f27 | |||
| e19127facc | |||
| b7ad285a11 | 
| @@ -15,7 +15,7 @@ resolver = "2" | |||||||
|  |  | ||||||
| [workspace.package] | [workspace.package] | ||||||
| repository = "https://git.soft.fish/j/Conlang" | repository = "https://git.soft.fish/j/Conlang" | ||||||
| version = "0.0.5" | version = "0.0.6" | ||||||
| authors = ["John Breaux <j@soft.fish>"] | authors = ["John Breaux <j@soft.fish>"] | ||||||
| edition = "2021" | edition = "2021" | ||||||
| license = "MIT" | license = "MIT" | ||||||
|   | |||||||
| @@ -1,17 +1,11 @@ | |||||||
|  | use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression}; | ||||||
|  |  | ||||||
| use cl_ast::{ | use cl_ast::{ | ||||||
|     ast_visitor::{Fold, Visit}, |     ast_visitor::{Fold, Visit}, | ||||||
|     desugar::*, |     desugar::*, | ||||||
| }; | }; | ||||||
| use cl_lexer::Lexer; | use cl_lexer::Lexer; | ||||||
| use cl_parser::{inliner::ModuleInliner, Parser}; | use cl_parser::{inliner::ModuleInliner, Parser}; | ||||||
| use cl_typeck::{ |  | ||||||
|     definition::Def, |  | ||||||
|     handle::Handle, |  | ||||||
|     name_collector::NameCollector, |  | ||||||
|     node::{Node, NodeSource}, |  | ||||||
|     project::Project, |  | ||||||
|     type_resolver::resolve, |  | ||||||
| }; |  | ||||||
| use repline::{error::Error as RlError, prebaked::*}; | use repline::{error::Error as RlError, prebaked::*}; | ||||||
| use std::{error::Error, path}; | use std::{error::Error, path}; | ||||||
|  |  | ||||||
| @@ -21,7 +15,7 @@ const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl"; | |||||||
| const STDLIB: &str = include_str!("../../../stdlib/lib.cl"); | const STDLIB: &str = include_str!("../../../stdlib/lib.cl"); | ||||||
|  |  | ||||||
| // Colors | // Colors | ||||||
| const C_MAIN: &str = ""; | const C_MAIN: &str = C_LISTING; | ||||||
| const C_RESV: &str = "\x1b[35m"; | const C_RESV: &str = "\x1b[35m"; | ||||||
| const C_CODE: &str = "\x1b[36m"; | const C_CODE: &str = "\x1b[36m"; | ||||||
| const C_BYID: &str = "\x1b[95m"; | const C_BYID: &str = "\x1b[95m"; | ||||||
| @@ -33,7 +27,7 @@ const C_LISTING: &str = "\x1b[38;5;117m"; | |||||||
| static mut TREES: TreeManager = TreeManager::new(); | static mut TREES: TreeManager = TreeManager::new(); | ||||||
|  |  | ||||||
| fn main() -> Result<(), Box<dyn Error>> { | 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 mut parser = Parser::new(Lexer::new(STDLIB)); | ||||||
|     let code = match parser.file() { |     let code = match parser.file() { | ||||||
| @@ -44,13 +38,14 @@ fn main() -> Result<(), Box<dyn Error>> { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     let code = inline_modules(code, concat!(env!("PWD"), "/stdlib")); |     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)?; |     main_menu(&mut prj)?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn main_menu(prj: &mut Project) -> Result<(), RlError> { | fn main_menu(prj: &mut Table) -> Result<(), RlError> { | ||||||
|     banner(); |     banner(); | ||||||
|     read_and(C_MAIN, "mu>", "? >", |line| { |     read_and(C_MAIN, "mu>", "? >", |line| { | ||||||
|         match line.trim() { |         match line.trim() { | ||||||
| @@ -82,7 +77,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| { |     read_and(C_CODE, "cl>", "? >", |line| { | ||||||
|         if line.trim().is_empty() { |         if line.trim().is_empty() { | ||||||
|             return Ok(Response::Break); |             return Ok(Response::Break); | ||||||
| @@ -91,8 +86,7 @@ fn enter_code(prj: &mut Project) -> Result<(), RlError> { | |||||||
|         let code = inline_modules(code, ""); |         let code = inline_modules(code, ""); | ||||||
|         let code = WhileElseDesugar.fold_file(code); |         let code = WhileElseDesugar.fold_file(code); | ||||||
|         // Safety: this is totally unsafe |         // 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) |         Ok(Response::Accept) | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
| @@ -115,22 +109,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| { |     read_and(C_RESV, "ty>", "? >", |line| { | ||||||
|         if line.trim().is_empty() { |         if line.trim().is_empty() { | ||||||
|             return Ok(Response::Break); |             return Ok(Response::Break); | ||||||
|         } |         } | ||||||
|         // parse it as a path, and convert the path into a borrowed path |         // parse it as a path, and convert the path into a borrowed path | ||||||
|         let ty = Parser::new(Lexer::new(line)).ty()?.kind; |         let ty = Parser::new(Lexer::new(line)).ty()?.kind; | ||||||
|         let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj); |         let id = ty.evaluate(prj, prj.root())?; | ||||||
|         pretty_handle(id)?; |         pretty_handle(id.to_entry(prj))?; | ||||||
|         Ok(Response::Accept) |         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_structures::index_map::MapIndex; | ||||||
|     use cl_typeck::handle::DefID; |     use cl_typeck::handle::Handle; | ||||||
|     read_and(C_BYID, "id>", "? >", |line| { |     read_and(C_BYID, "id>", "? >", |line| { | ||||||
|         if line.trim().is_empty() { |         if line.trim().is_empty() { | ||||||
|             return Ok(Response::Break); |             return Ok(Response::Break); | ||||||
| @@ -143,9 +137,7 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> { | |||||||
|         let mut path = parser.path().unwrap_or_default(); |         let mut path = parser.path().unwrap_or_default(); | ||||||
|         path.absolute = false; |         path.absolute = false; | ||||||
|  |  | ||||||
|         let Some(handle) = DefID::from_usize(def_id).handle(prj) else { |         let handle = Handle::from_usize(def_id).to_entry(prj); | ||||||
|             return Ok(Response::Deny); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         print!("  > {{{C_LISTING}{handle}\x1b[0m}}"); |         print!("  > {{{C_LISTING}{handle}\x1b[0m}}"); | ||||||
|         if !path.parts.is_empty() { |         if !path.parts.is_empty() { | ||||||
| @@ -153,71 +145,105 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> { | |||||||
|         } |         } | ||||||
|         println!(); |         println!(); | ||||||
|  |  | ||||||
|         let (ty, value) = handle.navigate((&path).into()); |         let Some(entry) = handle.nav(&path.parts) else { | ||||||
|         if let (None, None) = (ty, value) { |  | ||||||
|             Err("No results.")? |             Err("No results.")? | ||||||
|         } |         }; | ||||||
|         if let Some(t) = ty { |  | ||||||
|             println!("Result in type namespace: {}", t.id()); |         pretty_handle(entry)?; | ||||||
|             pretty_handle(t)?; |  | ||||||
|         } |  | ||||||
|         if let Some(v) = value { |  | ||||||
|             println!("Result in value namespace: {}", v.id()); |  | ||||||
|             pretty_handle(v)?; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Ok(Response::Accept) |         Ok(Response::Accept) | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> { | fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> { | ||||||
|     prj.resolve_imports()?; |     for (id, error) in import(table) { | ||||||
|     for id in prj.pool.keys() { |         eprintln!("{error} in {} ({id})", id.to_entry(table)) | ||||||
|         resolve(prj, id)?; |  | ||||||
|     } |     } | ||||||
|     println!("Types resolved successfully!"); |     for handle in table.handle_iter() { | ||||||
|  |         if let Err(error) = handle.to_entry_mut(table).categorize() { | ||||||
|  |             eprintln!("{error}"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for handle in implement(table) { | ||||||
|  |         eprintln!("Unable to reparent {} ({handle})", handle.to_entry(table)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     println!("...Resolved!"); | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn list_types(prj: &mut Project) { | fn list_types(table: &mut Table) { | ||||||
|     println!("     name\x1b[30G  type"); |     for handle in table.debug_entry_iter() { | ||||||
|     for (idx, key) in prj.pool.keys().enumerate() { |         let id = handle.id(); | ||||||
|         let Def { node: Node { vis, kind: source, .. }, .. } = &prj[key]; |         let kind = handle.kind().unwrap(); | ||||||
|         let name = match source.as_ref().map(NodeSource::name) { |         let name = handle.name().unwrap_or("".into()); | ||||||
|             Some(Some(name)) => name, |         println!("{id:3}: {name:16}| {kind}: {handle}"); | ||||||
|             _ => "".into(), |  | ||||||
|         }; |  | ||||||
|         print!( |  | ||||||
|             "{idx:3}: {vis}{name}\x1b[30G = {}", |  | ||||||
|             key.handle_unchecked(prj) |  | ||||||
|         ); |  | ||||||
|         println!(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> { | fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> { | ||||||
|     use std::io::Write; |     use std::io::Write; | ||||||
|     let mut stdout = std::io::stdout().lock(); |     let mut out = std::io::stdout().lock(); | ||||||
|     let Some(Def { module, node: Node { vis, .. }, .. }) = handle.get() else { |     let Some(kind) = entry.kind() else { | ||||||
|         return writeln!(stdout, "Invalid handle: {handle}"); |         return writeln!(out, "{entry}"); | ||||||
|     }; |     }; | ||||||
|     writeln!(stdout, "{C_LISTING}{vis}{handle}\x1b[0m: {}", handle.id())?; |     write!(out, "{C_LISTING}{kind}")?; | ||||||
|     if let Some(parent) = module.parent { |  | ||||||
|         writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {}", handle.with(parent))?; |     if let Some(name) = entry.name() { | ||||||
|  |         write!(out, " {name}")?; | ||||||
|     } |     } | ||||||
|     if !module.types.is_empty() { |     writeln!(out, "\x1b[0m ({}): {entry}", entry.id())?; | ||||||
|         writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?; |  | ||||||
|         for (name, def) in &module.types { |     if let Some(parent) = entry.parent() { | ||||||
|             writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? |         writeln!( | ||||||
|  |             out, | ||||||
|  |             "- {C_LISTING}Parent\x1b[0m: {parent} ({})", | ||||||
|  |             parent.id() | ||||||
|  |         )?; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if let Some(span) = entry.span() { | ||||||
|  |         writeln!( | ||||||
|  |             out, | ||||||
|  |             "- {C_LISTING}Span:\x1b[0m ({}, {})", | ||||||
|  |             span.head, span.tail | ||||||
|  |         )?; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     match entry.meta() { | ||||||
|  |         Some(meta) if !meta.is_empty() => { | ||||||
|  |             writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?; | ||||||
|  |             for meta in meta { | ||||||
|  |                 writeln!(out, "  - {meta}")?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         _ => {} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if let Some(children) = entry.children() { | ||||||
|  |         writeln!(out, "- {C_LISTING}Children:\x1b[0m")?; | ||||||
|  |         for (name, child) in children { | ||||||
|  |             writeln!( | ||||||
|  |                 out, | ||||||
|  |                 "  - {C_LISTING}{name}\x1b[0m ({child}): {}", | ||||||
|  |                 entry.with_id(*child) | ||||||
|  |             )? | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if !module.values.is_empty() { |  | ||||||
|         writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?; |     if let Some(imports) = entry.imports() { | ||||||
|         for (name, def) in &module.values { |         writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?; | ||||||
|             writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? |         for (name, child) in imports { | ||||||
|  |             writeln!( | ||||||
|  |                 out, | ||||||
|  |                 "  - {C_LISTING}{name}\x1b[0m ({child}): {}", | ||||||
|  |                 entry.with_id(*child) | ||||||
|  |             )? | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     write!(stdout, "\x1b[0m") |  | ||||||
|  |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File { | fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File { | ||||||
|   | |||||||
							
								
								
									
										184
									
								
								compiler/cl-typeck/src/entry.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								compiler/cl-typeck/src/entry.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | |||||||
|  | //! An [Entry] is an accessor for [nodes](Handle) in a [Table]. | ||||||
|  | //! | ||||||
|  | //! There are two kinds of entry: | ||||||
|  | //! - [Entry]: Provides getters for an entry's fields, and an implementation of | ||||||
|  | //!   [Display](std::fmt::Display) | ||||||
|  | //! - [EntryMut]: Provides setters for an entry's fields, and an [`as_ref`](EntryMut::as_ref) method | ||||||
|  | //!   to demote to an [Entry]. | ||||||
|  |  | ||||||
|  | use std::collections::HashMap; | ||||||
|  |  | ||||||
|  | use cl_ast::{Meta, PathPart, Sym}; | ||||||
|  | use cl_structures::span::Span; | ||||||
|  |  | ||||||
|  | use crate::{ | ||||||
|  |     handle::Handle, | ||||||
|  |     source::Source, | ||||||
|  |     stage::categorize as cat, | ||||||
|  |     table::{NodeKind, Table}, | ||||||
|  |     type_expression::{self as tex, TypeExpression}, | ||||||
|  |     type_kind::TypeKind, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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<'_, 'a> { | ||||||
|  |         Self { table: self.table, id } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'_, '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<Entry<'_, 'a>> { | ||||||
|  |         Some(Entry { id: *self.table.parent(self.id)?, ..*self }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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<Entry<'_, 'a>> { | ||||||
|  |         Some(Entry { id: self.table.impl_target(self.id)?, ..*self }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn selfty(&self) -> Option<Entry<'_, 'a>> { | ||||||
|  |         Some(Entry { id: self.table.selfty(self.id)?, ..*self }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Evaluates a [TypeExpression] in this entry's context | ||||||
|  |     pub fn evaluate<Out>(&mut self, ty: &impl TypeExpression<Out>) -> Result<Out, tex::Error> { | ||||||
|  |         let Self { table, id } = self; | ||||||
|  |         ty.evaluate(table, *id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn categorize(&mut self) -> Result<(), cat::Error> { | ||||||
|  |         cat::categorize(self.table, self.id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Constructs a new Handle with the provided parent [Handle] | ||||||
|  |     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) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								compiler/cl-typeck/src/entry/display.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								compiler/cl-typeck/src/entry/display.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | 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 { | ||||||
|  |         let Some(&kind) = self.kind() else { | ||||||
|  |             return write!(f, "<invalid type: {}>", self.id); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         if let Some(ty) = self.ty() { | ||||||
|  |             match ty { | ||||||
|  |                 TypeKind::Instance(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)?; | ||||||
|  |                     } | ||||||
|  |                     Ok(()) | ||||||
|  |                 } | ||||||
|  |                 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?"), | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             write!(f, "{kind}") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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, "{name}: ")?; | ||||||
|  |                             write_name_or(h.with_id(*def), f) | ||||||
|  |                         } | ||||||
|  |                         None => write!(f, "{name}"), | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |             })(f.delimit_with("enum {", "}")) | ||||||
|  |         } | ||||||
|  |         Adt::Struct(members) => { | ||||||
|  |             let mut members = members.iter(); | ||||||
|  |             separate(", ", || { | ||||||
|  |                 let (name, vis, id) = members.next()?; | ||||||
|  |                 Some(move |f: &mut Delimit<_>| { | ||||||
|  |                     write!(f, "{vis}{name}: ")?; | ||||||
|  |                     write_name_or(h.with_id(*id), f) | ||||||
|  |                 }) | ||||||
|  |             })(f.delimit_with("struct {", "}")) | ||||||
|  |         } | ||||||
|  |         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,187 +1,15 @@ | |||||||
| use crate::{definition::Def, path::Path, project::Project}; | //! A [Handle] uniquely represents an entry in the [Table](crate::table::Table) | ||||||
|  |  | ||||||
| use cl_structures::index_map::*; | use cl_structures::index_map::*; | ||||||
|  |  | ||||||
| // define the index types | // define the index types | ||||||
| make_index! { | make_index! { | ||||||
|     /// Uniquely represents a [Def][1] in the [Def][1] [Pool] |     /// Uniquely represents an entry in the [Table](crate::table::Table) | ||||||
|     /// |     Handle, | ||||||
|     /// [1]: crate::definition::Def |  | ||||||
|     DefID, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A handle to a certain [Def] within a [Project] | impl std::fmt::Display for Handle { | ||||||
| #[derive(Clone, Copy, Debug)] |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
| pub struct Handle<'prj, 'code> { |         self.0.fmt(f) | ||||||
|     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 display { |  | ||||||
|     use super::*; |  | ||||||
|     use crate::{definition::*, format_utils::*}; |  | ||||||
|     use std::fmt::{self, Display, Write}; |  | ||||||
|  |  | ||||||
|     impl Display for DefID { |  | ||||||
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  | ||||||
|             self.0.fmt(f) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl Display for Handle<'_, '_> { |  | ||||||
|         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|             let Self { id, prj } = *self; |  | ||||||
|             let Some(def) = prj.pool.get(id) else { |  | ||||||
|                 return write!(f, "<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}")) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,154 +4,71 @@ | |||||||
| //! | //! | ||||||
| //! This crate is a major work-in-progress. | //! This crate is a major work-in-progress. | ||||||
| //! | //! | ||||||
| //! # The [Project](project::Project)™ | //! # The [Table](table::Table)™ | ||||||
| //! Contains [item definitions](definition) and type expression information. | //! A directed graph of nodes and their dependencies. | ||||||
| //! | //! | ||||||
| //! *Every* definition is itself a module, and can contain arbitrarily nested items | //! Contains [item definitions](handle) and [type expression](type_expression) information. | ||||||
| //! as part of the [Module](module::Module) tree. |  | ||||||
| //! | //! | ||||||
| //! The Project keeps track of a *global intern pool* of definitions, which are | //! *Every* item is itself a module, and can contain arbitrarily nested items | ||||||
| //! trivially comparable by [DefID](key::DefID). Note that, for item definitions, | //! as part of the item graph | ||||||
| //! identical types in different modules DO NOT COMPARE EQUAL under this constraint. | //! | ||||||
| //! However, so-called "anonymous" types *do not* follow this rule, as their | //! The table, additionally, has some queues for use in external algorithms, | ||||||
| //! definitions are constructed dynamically and ensured to be unique. | //! detailed in the [stage] module. | ||||||
| // Note: it's a class invariant that named types are not added |  | ||||||
| // to the anon-types list. Please keep it that way. ♥  Thanks! |  | ||||||
| //! | //! | ||||||
| //! # Namespaces | //! # Namespaces | ||||||
| //! Within a Project, [definitions](definition::Def) are classified into two namespaces: | //! Each item in the graph is given its own namespace, which is further separated into | ||||||
| //! | //! two distinct parts: | ||||||
| //! ## Type Namespace: | //! - Children of an item are direct descendents (i.e. their `parent` is a handle to the item) | ||||||
| //! - Modules | //! - Imports of an item are indirect descendents created by `use` or `impl` directives. They are | ||||||
| //! - Structs | //!   shadowed by Children with the same name. | ||||||
| //! - Enums |  | ||||||
| //! - Type aliases |  | ||||||
| //! |  | ||||||
| //! ## Value Namespace: |  | ||||||
| //! - Functions |  | ||||||
| //! - Constants |  | ||||||
| //! - Static variables |  | ||||||
| //! |  | ||||||
| //! There is a *key* distinction between the two namespaces when it comes to |  | ||||||
| //! [Path](path::Path) traversal: Only items in the Type Namespace will be considered |  | ||||||
| //! as an intermediate path target. This means items in the Value namespace are |  | ||||||
| //! entirely *opaque*, and form a one-way barrier. Items outside a Value can be |  | ||||||
| //! referred to within the Value, but items inside a Value *cannot* be referred to |  | ||||||
| //! outside the Value. |  | ||||||
| //! | //! | ||||||
| //! # Order of operations: | //! # Order of operations: | ||||||
| //! Currently, the process of type resolution goes as follows: | //! For order-of-operations information, see the [stage] module. | ||||||
| //! |  | ||||||
| //! 1. Traverse the AST, [collecting all Items into item definitions](name_collector) |  | ||||||
| //! 2. Eagerly [resolve `use` imports](use_importer) |  | ||||||
| //! 3. Traverse all [definitions](definition::Def), and [resolve the types for every |  | ||||||
| //!    item](type_resolver) |  | ||||||
| //! 4. TODO: Construct a typed AST for expressions, and type-check them |  | ||||||
| #![warn(clippy::all)] | #![warn(clippy::all)] | ||||||
|  |  | ||||||
| /* |  | ||||||
| How do I flesh out modules in an incremental way? |  | ||||||
|  |  | ||||||
| 1. Visit all *modules* and create nodes in a module tree for them |  | ||||||
| - This can be done by holding mutable references to submodules! |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Module: |  | ||||||
|     values: Map(name -> Value), |  | ||||||
|     types: Map(name -> Type), |  | ||||||
|  |  | ||||||
| Value: Either |  | ||||||
|     function: { signature: Type, body: FunctionBody } |  | ||||||
|     static: { Mutability, ty: Type, init: ConstEvaluationResult } |  | ||||||
|     const: { ty: Type, init: ConstEvaluationResult } |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| 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(crate) mod format_utils; | ||||||
|  |  | ||||||
| /* | pub mod table; | ||||||
|  |  | ||||||
| LET THERE BE NOTES: | pub mod handle; | ||||||
|  |  | ||||||
| /// What is an inference rule? | pub mod entry; | ||||||
| /// An inference rule is a specification with a set of predicates and a judgement |  | ||||||
|  |  | ||||||
| /// Let's give every type an ID | pub mod source; | ||||||
| struct TypeID(usize); |  | ||||||
|  |  | ||||||
| /// Let's give every type some data: | pub mod type_kind; | ||||||
|  |  | ||||||
| struct TypeDef<'def> { | pub mod type_expression; | ||||||
|     name: String, |  | ||||||
|     definition: &'def Item, | pub mod stage { | ||||||
|  |     //! Type collection, evaluation, checking, and inference passes. | ||||||
|  |     //! | ||||||
|  |     //! # Order of operations | ||||||
|  |     //! 1. [mod@populate]: Populate the graph with nodes for every named item. | ||||||
|  |     //! 2. [mod@import]: Import the `use` nodes discovered in [Stage 1](populate). | ||||||
|  |     //! 3. [mod@categorize]: Categorize the nodes according to textual type information. | ||||||
|  |     //!    - Creates anonymous types (`fn(T) -> U`, `&T`, `[T]`, etc.) as necessary to fill in the | ||||||
|  |     //!      type graph | ||||||
|  |     //!    - Creates a new struct type for every enum struct-variant. | ||||||
|  |     //! 4. [mod@implement]: Import members of implementation modules into types. | ||||||
|  |  | ||||||
|  |     pub use populate::Populator; | ||||||
|  |     /// Stage 1: Populate the graph with nodes. | ||||||
|  |     pub mod populate; | ||||||
|  |  | ||||||
|  |     pub use import::import; | ||||||
|  |     /// Stage 2: Import the `use` nodes discovered in Stage 1. | ||||||
|  |     pub mod import; | ||||||
|  |  | ||||||
|  |     pub use categorize::categorize; | ||||||
|  |     /// Stage 3: Categorize the nodes according to textual type information. | ||||||
|  |     pub mod categorize; | ||||||
|  |  | ||||||
|  |     pub use implement::implement; | ||||||
|  |     /// Stage 4: Import members of `impl` blocks into their corresponding types. | ||||||
|  |     pub mod implement; | ||||||
|  |  | ||||||
|  |     // TODO: Make type inference stage 5 | ||||||
|  |     // TODO: Use the type information stored in the [table] | ||||||
|  |     pub mod infer; | ||||||
| } | } | ||||||
|  |  | ||||||
| and store them in a big vector of type descriptions: |  | ||||||
|  |  | ||||||
| struct TypeMap<'def> { |  | ||||||
|     types: Vec<TypeDef<'def>>, |  | ||||||
| } |  | ||||||
| // todo: insertion of a type should yield a TypeID |  | ||||||
| // todo: impl index with TypeID |  | ||||||
|  |  | ||||||
| Let's store type information as either a concrete type or a generic type: |  | ||||||
|  |  | ||||||
| /// The Type struct represents all valid types, and can be trivially equality-compared |  | ||||||
| pub struct Type { |  | ||||||
|     /// You can only have a pointer chain 65535 pointers long. |  | ||||||
|     ref_depth: u16, |  | ||||||
|     kind: TKind, |  | ||||||
| } |  | ||||||
| pub enum TKind { |  | ||||||
|     Concrete(TypeID), |  | ||||||
|     Generic(usize), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| And assume I can specify a rule based on its inputs and outputs: |  | ||||||
|  |  | ||||||
| Rule { |  | ||||||
|     operation: If, |  | ||||||
|     /// The inputs field is populated by |  | ||||||
|     inputs: [Concrete(BOOL), Generic(0), Generic(0)], |  | ||||||
|     outputs: Generic(0), |  | ||||||
|     /// This rule is compiler-intrinsic! |  | ||||||
|     through: None, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Rule { |  | ||||||
|     operation: Add, |  | ||||||
|     inputs: [Concrete(I32), Concrete(I32)], |  | ||||||
|     outputs: Concrete(I32), |  | ||||||
|     /// This rule is not compiler-intrinsic (it is overloaded!) |  | ||||||
|     through: Some(&ImplAddForI32::Add), |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| These rules can be stored in some kind of rule database: |  | ||||||
|  |  | ||||||
| let rules: Hashmap<Operation, Vec<Rule>> { |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|   | |||||||
| @@ -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(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,376 +0,0 @@ | |||||||
| //! A [Project] contains a tree of [Def]initions, referred to by their [Path] |  | ||||||
| use crate::{ |  | ||||||
|     definition::{Def, DefKind, TypeKind}, |  | ||||||
|     handle::DefID, |  | ||||||
|     module, |  | ||||||
|     node::{Node, NodeSource}, |  | ||||||
|     path::Path, |  | ||||||
| }; |  | ||||||
| use cl_ast::PathPart; |  | ||||||
| use cl_structures::index_map::IndexMap; |  | ||||||
| use std::{ |  | ||||||
|     collections::HashMap, |  | ||||||
|     ops::{Index, IndexMut}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| use self::evaluate::EvaluableTypeExpression; |  | ||||||
|  |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct Project<'a> { |  | ||||||
|     pub pool: IndexMap<DefID, Def<'a>>, |  | ||||||
|     /// Stores anonymous tuples, function pointer types, etc. |  | ||||||
|     pub anon_types: HashMap<TypeKind, DefID>, |  | ||||||
|     pub root: DefID, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Project<'_> { |  | ||||||
|     pub fn new() -> Self { |  | ||||||
|         Self::default() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Default for Project<'_> { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         const ROOT_PATH: cl_ast::Path = cl_ast::Path { absolute: true, parts: Vec::new() }; |  | ||||||
|  |  | ||||||
|         let mut pool = IndexMap::default(); |  | ||||||
|         let root = pool.insert(Def { |  | ||||||
|             module: Default::default(), |  | ||||||
|             kind: DefKind::Type(TypeKind::Module), |  | ||||||
|             node: Node::new(ROOT_PATH, Some(NodeSource::Root)), |  | ||||||
|         }); |  | ||||||
|         let never = pool.insert(Def { |  | ||||||
|             module: module::Module::new(root), |  | ||||||
|             kind: DefKind::Type(TypeKind::Never), |  | ||||||
|             node: Node::new(ROOT_PATH, None), |  | ||||||
|         }); |  | ||||||
|         let empty = pool.insert(Def { |  | ||||||
|             module: module::Module::new(root), |  | ||||||
|             kind: DefKind::Type(TypeKind::Empty), |  | ||||||
|             node: Node::new(ROOT_PATH, None), |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         let mut anon_types = HashMap::new(); |  | ||||||
|         anon_types.insert(TypeKind::Empty, empty); |  | ||||||
|         anon_types.insert(TypeKind::Never, never); |  | ||||||
|  |  | ||||||
|         Self { pool, root, anon_types } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> Project<'a> { |  | ||||||
|     pub fn parent_of(&self, module: DefID) -> Option<DefID> { |  | ||||||
|         self[module].module.parent |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn root_of(&self, module: DefID) -> DefID { |  | ||||||
|         match self.parent_of(module) { |  | ||||||
|             Some(module) => self.root_of(module), |  | ||||||
|             None => module, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Returns the DefID of the Self type within the given DefID's context |  | ||||||
|     pub fn selfty_of(&self, node: DefID) -> Option<DefID> { |  | ||||||
|         match self[node].kind { |  | ||||||
|             DefKind::Impl(id) => Some(id), |  | ||||||
|             DefKind::Type(_) => Some(node), |  | ||||||
|             _ => self.selfty_of(self.parent_of(node)?), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn get<'p>( |  | ||||||
|         &self, |  | ||||||
|         path: Path<'p>, |  | ||||||
|         within: DefID, |  | ||||||
|     ) -> Option<(Option<DefID>, Option<DefID>, Path<'p>)> { |  | ||||||
|         if path.absolute { |  | ||||||
|             return self.get(path.relative(), self.root_of(within)); |  | ||||||
|         } |  | ||||||
|         match path.as_ref() { |  | ||||||
|             [PathPart::SuperKw, ..] => self.get(path.pop_front()?, self.parent_of(within)?), |  | ||||||
|             [PathPart::SelfTy, ..] => self.get(path.pop_front()?, self.selfty_of(within)?), |  | ||||||
|             [PathPart::SelfKw, ..] => self.get(path.pop_front()?, within), |  | ||||||
|             [PathPart::Ident(name)] => { |  | ||||||
|                 let (ty, val) = self[within].module.get(*name); |  | ||||||
|  |  | ||||||
|                 // Transparent nodes can be looked through in reverse |  | ||||||
|                 if self[within].is_transparent() { |  | ||||||
|                     let lookback = self.parent_of(within).and_then(|p| self.get(path, p)); |  | ||||||
|                     if let Some((subty, subval, path)) = lookback { |  | ||||||
|                         return Some((ty.or(subty), val.or(subval), path)); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 Some((ty, val, path.pop_front()?)) |  | ||||||
|             } |  | ||||||
|             [PathPart::Ident(name), ..] => { |  | ||||||
|                 // TODO: This is currently too permissive, and treats undecided nodes as if they're |  | ||||||
|                 // always transparent, among other issues. |  | ||||||
|                 let (tysub, _, _) = match self[within].is_transparent() { |  | ||||||
|                     true => self.get(path.front()?, within)?, |  | ||||||
|                     false => (None, None, path), |  | ||||||
|                 }; |  | ||||||
|                 let ty = self[within].module.get_type(*name).or(tysub)?; |  | ||||||
|                 self.get(path.pop_front()?, ty) |  | ||||||
|             } |  | ||||||
|             [] => Some((Some(within), None, path)), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Resolves a path within a module tree, finding the innermost module. |  | ||||||
|     /// Returns the remaining path parts. |  | ||||||
|     pub fn get_type<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> { |  | ||||||
|         if path.absolute { |  | ||||||
|             self.get_type(path.relative(), self.root_of(within)) |  | ||||||
|         } else if let Some(front) = path.first() { |  | ||||||
|             let module = &self[within].module; |  | ||||||
|             match front { |  | ||||||
|                 PathPart::SelfKw => self.get_type(path.pop_front()?, within), |  | ||||||
|                 PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?), |  | ||||||
|                 PathPart::SelfTy => self.get_type(path.pop_front()?, self.selfty_of(within)?), |  | ||||||
|                 PathPart::Ident(name) => match module.types.get(name) { |  | ||||||
|                     Some(&submodule) => self.get_type(path.pop_front()?, submodule), |  | ||||||
|                     None => Some((within, path)), |  | ||||||
|                 }, |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Some((within, path)) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Inserts the type returned by the provided closure iff the TypeKind doesn't already exist |  | ||||||
|     /// |  | ||||||
|     /// Assumes `kind` uniquely identifies the type! |  | ||||||
|     pub fn insert_anonymous_type( |  | ||||||
|         &mut self, |  | ||||||
|         kind: TypeKind, |  | ||||||
|         def: impl FnOnce() -> Def<'a>, |  | ||||||
|     ) -> DefID { |  | ||||||
|         *(self |  | ||||||
|             .anon_types |  | ||||||
|             .entry(kind) |  | ||||||
|             .or_insert_with(|| self.pool.insert(def()))) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn evaluate<T>(&mut self, expr: &T, parent: DefID) -> Result<T::Out, String> |  | ||||||
|     where T: EvaluableTypeExpression { |  | ||||||
|         expr.evaluate(self, parent) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> Index<DefID> for Project<'a> { |  | ||||||
|     type Output = Def<'a>; |  | ||||||
|     fn index(&self, index: DefID) -> &Self::Output { |  | ||||||
|         &self.pool[index] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl IndexMut<DefID> for Project<'_> { |  | ||||||
|     fn index_mut(&mut self, index: DefID) -> &mut Self::Output { |  | ||||||
|         &mut self.pool[index] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub mod evaluate { |  | ||||||
|     //! An [EvaluableTypeExpression] is a component of a type expression tree |  | ||||||
|     //! or an intermediate result of expression evaluation. |  | ||||||
|  |  | ||||||
|     use super::*; |  | ||||||
|     use crate::module; |  | ||||||
|     use cl_ast::{Sym, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple}; |  | ||||||
|  |  | ||||||
|     /// Things that can be evaluated as a type expression |  | ||||||
|     pub trait EvaluableTypeExpression { |  | ||||||
|         /// The result of type expression evaluation |  | ||||||
|         type Out; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String>; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for Ty { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, id: DefID) -> Result<DefID, String> { |  | ||||||
|             self.kind.evaluate(prj, id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for TyKind { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> { |  | ||||||
|             let id = match self { |  | ||||||
|                 // TODO: reduce duplication here |  | ||||||
|                 TyKind::Never => prj.anon_types[&TypeKind::Never], |  | ||||||
|                 TyKind::Empty => prj.anon_types[&TypeKind::Empty], |  | ||||||
|                 // TyKind::Path must be looked up explicitly |  | ||||||
|                 TyKind::Path(path) => path.evaluate(prj, parent)?, |  | ||||||
|                 TyKind::Slice(slice) => slice.evaluate(prj, parent)?, |  | ||||||
|                 TyKind::Array(array) => array.evaluate(prj, parent)?, |  | ||||||
|                 TyKind::Tuple(tup) => tup.evaluate(prj, parent)?, |  | ||||||
|                 TyKind::Ref(tyref) => tyref.evaluate(prj, parent)?, |  | ||||||
|                 TyKind::Fn(tyfn) => tyfn.evaluate(prj, parent)?, |  | ||||||
|             }; |  | ||||||
|             // println!("{self} => {id:?}"); |  | ||||||
|  |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for Sym { |  | ||||||
|         type Out = DefID; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             prj[parent] |  | ||||||
|                 .module |  | ||||||
|                 .types |  | ||||||
|                 .get(self) |  | ||||||
|                 .copied() |  | ||||||
|                 .ok_or_else(|| format!("{self} is not a member of {:?}", prj[parent].name())) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for TySlice { |  | ||||||
|         type Out = DefID; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             let ty = self.ty.evaluate(prj, parent)?; |  | ||||||
|             let root = prj.root; |  | ||||||
|             let id = prj.insert_anonymous_type(TypeKind::Slice(ty), move || Def { |  | ||||||
|                 module: module::Module::new(root), |  | ||||||
|                 node: Node::new(Default::default(), None), |  | ||||||
|                 kind: DefKind::Type(TypeKind::Slice(ty)), |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for TyArray { |  | ||||||
|         type Out = DefID; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             let kind = TypeKind::Array(self.ty.evaluate(prj, parent)?, self.count); |  | ||||||
|             let root = prj.root; |  | ||||||
|             let id = prj.insert_anonymous_type(kind.clone(), move || Def { |  | ||||||
|                 module: module::Module::new(root), |  | ||||||
|                 node: Node::new(Default::default(), None), |  | ||||||
|                 kind: DefKind::Type(kind), |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for TyTuple { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> { |  | ||||||
|             let types = self.types.evaluate(prj, parent)?; |  | ||||||
|             let root = prj.root; |  | ||||||
|             let id = prj.insert_anonymous_type(TypeKind::Tuple(types.clone()), move || Def { |  | ||||||
|                 module: module::Module::new(root), |  | ||||||
|                 node: Node::new(Default::default(), None), |  | ||||||
|                 kind: DefKind::Type(TypeKind::Tuple(types)), |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl EvaluableTypeExpression for TyRef { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> { |  | ||||||
|             let TyRef { count, mutable: _, to } = self; |  | ||||||
|             let to = to.evaluate(prj, parent)?; |  | ||||||
|  |  | ||||||
|             let root = prj.root; |  | ||||||
|             let id = prj.insert_anonymous_type(TypeKind::Ref(*count, to), move || Def { |  | ||||||
|                 module: module::Module::new(root), |  | ||||||
|                 node: Node::new(Default::default(), None), |  | ||||||
|                 kind: DefKind::Type(TypeKind::Ref(*count, to)), |  | ||||||
|             }); |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl EvaluableTypeExpression for TyFn { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> { |  | ||||||
|             let TyFn { args, rety } = self; |  | ||||||
|  |  | ||||||
|             let args = args.evaluate(prj, parent)?; |  | ||||||
|             let rety = match rety { |  | ||||||
|                 Some(rety) => rety.evaluate(prj, parent)?, |  | ||||||
|                 _ => TyKind::Empty.evaluate(prj, parent)?, |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             let root = prj.root; |  | ||||||
|             let id = prj.insert_anonymous_type(TypeKind::FnSig { args, rety }, || Def { |  | ||||||
|                 module: module::Module::new(root), |  | ||||||
|                 node: Node::new(Default::default(), None), |  | ||||||
|                 kind: DefKind::Type(TypeKind::FnSig { args, rety }), |  | ||||||
|             }); |  | ||||||
|             Ok(id) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl EvaluableTypeExpression for cl_ast::Path { |  | ||||||
|         type Out = DefID; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             Path::from(self).evaluate(prj, parent) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl EvaluableTypeExpression for PathPart { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             match self { |  | ||||||
|                 PathPart::SuperKw => prj |  | ||||||
|                     .parent_of(parent) |  | ||||||
|                     .ok_or_else(|| "Attempt to get super of root".into()), |  | ||||||
|                 PathPart::SelfKw => Ok(parent), |  | ||||||
|                 PathPart::SelfTy => prj |  | ||||||
|                     .selfty_of(parent) |  | ||||||
|                     .ok_or_else(|| "Attempt to get Self outside a Self-able context".into()), |  | ||||||
|                 PathPart::Ident(name) => name.evaluate(prj, parent), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl<'a> EvaluableTypeExpression for Path<'a> { |  | ||||||
|         type Out = DefID; |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             let (tid, vid, path) = prj.get(*self, parent).ok_or("Failed to traverse path")?; |  | ||||||
|             if !path.is_empty() { |  | ||||||
|                 Err(format!("Could not traverse past boundary: {path}"))?; |  | ||||||
|             } |  | ||||||
|             match (tid, vid) { |  | ||||||
|                 (Some(ty), _) => Ok(ty), |  | ||||||
|                 (None, Some(val)) => Ok(val), |  | ||||||
|                 (None, None) => Err(format!("No type or value found at path {self}")), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl<T: EvaluableTypeExpression> EvaluableTypeExpression for [T] { |  | ||||||
|         type Out = Vec<T::Out>; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             let mut types = vec![]; |  | ||||||
|             for value in self { |  | ||||||
|                 types.push(value.evaluate(prj, parent)?) |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             Ok(types) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Option<T> { |  | ||||||
|         type Out = Option<T::Out>; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             Ok(match self { |  | ||||||
|                 Some(v) => Some(v.evaluate(prj, parent)?), |  | ||||||
|                 None => None, |  | ||||||
|             }) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Box<T> { |  | ||||||
|         type Out = T::Out; |  | ||||||
|  |  | ||||||
|         fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> { |  | ||||||
|             self.as_ref().evaluate(prj, parent) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										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 [Source] defines a named value | ||||||
|  |     pub fn is_named_value(&self) -> bool { | ||||||
|  |         matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns `true` if this [Source] 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 [Source] refers to a [Ty] with no name | ||||||
|  |     pub fn is_anon_type(&self) -> bool { | ||||||
|  |         matches!(self, Self::Ty(_)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns `true` if this [Source] refers to an [Impl] block | ||||||
|  |     pub fn is_impl(&self) -> bool { | ||||||
|  |         matches!(self, Self::Impl(_)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns `true` if this [Source] 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), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										229
									
								
								compiler/cl-typeck/src/stage/categorize.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								compiler/cl-typeck/src/stage/categorize.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | |||||||
|  | //! Categorizes an entry in a table according to its embedded type information | ||||||
|  |  | ||||||
|  | use crate::{ | ||||||
|  |     handle::Handle, | ||||||
|  |     source::Source, | ||||||
|  |     table::{NodeKind, Table}, | ||||||
|  |     type_expression::{Error as TypeEval, TypeExpression}, | ||||||
|  |     type_kind::{Adt, 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<()> { | ||||||
|  |     if let Some(meta) = table.meta(node) { | ||||||
|  |         for meta @ Meta { name, kind } in meta { | ||||||
|  |             if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) { | ||||||
|  |                 let kind = | ||||||
|  |                     TypeKind::Intrinsic(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?); | ||||||
|  |                 table.set_ty(node, kind); | ||||||
|  |                 return Ok(()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let Some(source) = table.source(node) else { | ||||||
|  |         return Ok(()); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     match source { | ||||||
|  |         Source::Root => Ok(()), | ||||||
|  |         Source::Module(_) => Ok(()), | ||||||
|  |         Source::Alias(a) => cat_alias(table, node, a), | ||||||
|  |         Source::Enum(e) => cat_enum(table, node, e), | ||||||
|  |         Source::Variant(_) => Ok(()), | ||||||
|  |         Source::Struct(s) => cat_struct(table, node, s), | ||||||
|  |         Source::Const(c) => cat_const(table, node, c), | ||||||
|  |         Source::Static(s) => cat_static(table, node, s), | ||||||
|  |         Source::Function(f) => cat_function(table, node, f), | ||||||
|  |         Source::Local(l) => cat_local(table, node, l), | ||||||
|  |         Source::Impl(i) => cat_impl(table, node, i), | ||||||
|  |         Source::Use(_) => Ok(()), | ||||||
|  |         Source::Ty(ty) => ty | ||||||
|  |             .evaluate(table, node) | ||||||
|  |             .map_err(|e| Error::TypeEval(e, " while categorizing a type")) | ||||||
|  |             .map(drop), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn parent(table: &Table, node: Handle) -> Handle { | ||||||
|  |     table.parent(node).copied().unwrap_or(node) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_alias(table: &mut Table, node: Handle, a: &Alias) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let kind = match &a.from { | ||||||
|  |         Some(ty) => TypeKind::Instance( | ||||||
|  |             ty.evaluate(table, parent) | ||||||
|  |                 .map_err(|e| Error::TypeEval(e, " while categorizing an alias"))?, | ||||||
|  |         ), | ||||||
|  |         None => TypeKind::Empty, | ||||||
|  |     }; | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let Struct { name: _, kind } = s; | ||||||
|  |     let kind = match kind { | ||||||
|  |         StructKind::Empty => TypeKind::Adt(Adt::UnitStruct), | ||||||
|  |         StructKind::Tuple(types) => { | ||||||
|  |             let mut out = vec![]; | ||||||
|  |             for ty in types { | ||||||
|  |                 out.push((Visibility::Public, ty.evaluate(table, parent)?)) | ||||||
|  |             } | ||||||
|  |             TypeKind::Adt(Adt::TupleStruct(out)) | ||||||
|  |         } | ||||||
|  |         StructKind::Struct(members) => { | ||||||
|  |             let mut out = vec![]; | ||||||
|  |             for m in members { | ||||||
|  |                 out.push(cat_member(table, node, m)?) | ||||||
|  |             } | ||||||
|  |             TypeKind::Adt(Adt::Struct(out)) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_member( | ||||||
|  |     table: &mut Table, | ||||||
|  |     node: Handle, | ||||||
|  |     m: &StructMember, | ||||||
|  | ) -> CatResult<(Sym, Visibility, Handle)> { | ||||||
|  |     let StructMember { vis, name, ty } = m; | ||||||
|  |     Ok((*name, *vis, ty.evaluate(table, node)?)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_enum<'a>(table: &mut Table<'a>, node: Handle, e: &'a Enum) -> CatResult<()> { | ||||||
|  |     let Enum { name: _, kind } = e; | ||||||
|  |     let kind = match kind { | ||||||
|  |         EnumKind::NoVariants => TypeKind::Adt(Adt::Enum(vec![])), | ||||||
|  |         EnumKind::Variants(variants) => { | ||||||
|  |             let mut out_vars = vec![]; | ||||||
|  |             for v in variants { | ||||||
|  |                 out_vars.push(cat_variant(table, node, v)?) | ||||||
|  |             } | ||||||
|  |             TypeKind::Adt(Adt::Enum(out_vars)) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_variant<'a>( | ||||||
|  |     table: &mut Table<'a>, | ||||||
|  |     node: Handle, | ||||||
|  |     v: &'a Variant, | ||||||
|  | ) -> CatResult<(Sym, Option<Handle>)> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let Variant { name, kind } = v; | ||||||
|  |     match kind { | ||||||
|  |         VariantKind::Plain => Ok((*name, None)), | ||||||
|  |         VariantKind::CLike(c) => todo!("enum-variant constant {c}"), | ||||||
|  |         VariantKind::Tuple(ty) => { | ||||||
|  |             let ty = ty | ||||||
|  |                 .evaluate(table, parent) | ||||||
|  |                 .map_err(|e| Error::TypeEval(e, " while categorizing a variant"))?; | ||||||
|  |             Ok((*name, Some(ty))) | ||||||
|  |         } | ||||||
|  |         VariantKind::Struct(members) => { | ||||||
|  |             let mut out = vec![]; | ||||||
|  |             for m in members { | ||||||
|  |                 out.push(cat_member(table, node, m)?) | ||||||
|  |             } | ||||||
|  |             let kind = TypeKind::Adt(Adt::Struct(out)); | ||||||
|  |  | ||||||
|  |             let mut h = node.to_entry_mut(table); | ||||||
|  |             let mut variant = h.new_entry(NodeKind::Type); | ||||||
|  |             variant.set_source(Source::Variant(v)); | ||||||
|  |             variant.set_ty(kind); | ||||||
|  |             Ok((*name, Some(variant.id()))) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let kind = TypeKind::Instance( | ||||||
|  |         c.ty.evaluate(table, parent) | ||||||
|  |             .map_err(|e| Error::TypeEval(e, " while categorizing a const"))?, | ||||||
|  |     ); | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_static(table: &mut Table, node: Handle, s: &Static) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let kind = TypeKind::Instance( | ||||||
|  |         s.ty.evaluate(table, parent) | ||||||
|  |             .map_err(|e| Error::TypeEval(e, " while categorizing a static"))?, | ||||||
|  |     ); | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let kind = TypeKind::Instance( | ||||||
|  |         f.sign | ||||||
|  |             .evaluate(table, parent) | ||||||
|  |             .map_err(|e| Error::TypeEval(e, " while categorizing a function"))?, | ||||||
|  |     ); | ||||||
|  |     table.set_ty(node, kind); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_local(table: &mut Table, node: Handle, l: &Let) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     if let Some(ty) = &l.ty { | ||||||
|  |         let kind = ty | ||||||
|  |             .evaluate(table, parent) | ||||||
|  |             .map_err(|e| Error::TypeEval(e, " while categorizing a let binding"))?; | ||||||
|  |         table.set_ty(node, TypeKind::Instance(kind)); | ||||||
|  |     } | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat_impl(table: &mut Table, node: Handle, i: &Impl) -> CatResult<()> { | ||||||
|  |     let parent = parent(table, node); | ||||||
|  |     let Impl { target, body: _ } = i; | ||||||
|  |     let target = match target { | ||||||
|  |         ImplKind::Type(t) => t.evaluate(table, parent), | ||||||
|  |         ImplKind::Trait { impl_trait: _, for_type: t } => t.evaluate(table, parent), | ||||||
|  |     }?; | ||||||
|  |  | ||||||
|  |     table.set_impl_target(node, target); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CatResult<T> = Result<T, Error>; | ||||||
|  |  | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub enum Error { | ||||||
|  |     BadMeta(Meta), | ||||||
|  |     Recursive(Handle), | ||||||
|  |     TypeEval(TypeEval, &'static str), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<TypeEval> for Error { | ||||||
|  |     fn from(value: TypeEval) -> Self { | ||||||
|  |         Error::TypeEval(value, "") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl std::fmt::Display for Error { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Error::BadMeta(meta) => write!(f, "Unknown meta attribute: #[{meta}]"), | ||||||
|  |             Error::Recursive(id) => { | ||||||
|  |                 write!(f, "Encountered recursive type without indirection: {id}") | ||||||
|  |             } | ||||||
|  |             Error::TypeEval(e, during) => write!(f, "{e}{during}"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								compiler/cl-typeck/src/stage/implement.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								compiler/cl-typeck/src/stage/implement.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | use crate::{handle::Handle, table::Table}; | ||||||
|  |  | ||||||
|  |     pub fn implement(table: &mut Table) -> Vec<Handle> { | ||||||
|  |         let pending = std::mem::take(&mut table.impls); | ||||||
|  |         let mut errors = vec![]; | ||||||
|  |         for node in pending { | ||||||
|  |             if let Err(e) = impl_one(table, node) { | ||||||
|  |                 errors.push(e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         errors | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn impl_one(table: &mut Table, node: Handle) -> Result<(), Handle> { | ||||||
|  |         let Some(target) = table.impl_target(node) else { | ||||||
|  |             Err(node)? | ||||||
|  |         }; | ||||||
|  |         let Table { children, imports, .. } = table; | ||||||
|  |         if let Some(children) = children.get(&node) { | ||||||
|  |             imports.entry(target).or_default().extend(children); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
							
								
								
									
										158
									
								
								compiler/cl-typeck/src/stage/import.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								compiler/cl-typeck/src/stage/import.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | //! 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::{collections::HashSet, mem}; | ||||||
|  |  | ||||||
|  | type Seen = HashSet<Handle>; | ||||||
|  |  | ||||||
|  | pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> { | ||||||
|  |     let pending = mem::take(&mut table.uses); | ||||||
|  |  | ||||||
|  |     let mut seen = Seen::new(); | ||||||
|  |     let mut failed = vec![]; | ||||||
|  |     for import in pending { | ||||||
|  |         let Err(e) = import_one(table, import, &mut seen) else { | ||||||
|  |             continue; | ||||||
|  |         }; | ||||||
|  |         if let Error::NotFound(_, _) = e { | ||||||
|  |             table.mark_use_item(import) | ||||||
|  |         } | ||||||
|  |         failed.push((import, e)); | ||||||
|  |     } | ||||||
|  |     failed | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn import_one<'a>(table: &mut Table<'a>, item: Handle, seen: &mut Seen) -> UseResult<'a, ()> { | ||||||
|  |     if !seen.insert(item) { | ||||||
|  |         return Ok(()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  |  | ||||||
|  |     import_tree( | ||||||
|  |         table, | ||||||
|  |         if !absolute { dst } else { table.root() }, | ||||||
|  |         dst, | ||||||
|  |         tree, | ||||||
|  |         seen, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn import_tree<'a>( | ||||||
|  |     table: &mut Table<'a>, | ||||||
|  |     src: Handle, | ||||||
|  |     dst: Handle, | ||||||
|  |     tree: &UseTree, | ||||||
|  |     seen: &mut Seen, | ||||||
|  | ) -> UseResult<'a, ()> { | ||||||
|  |     match tree { | ||||||
|  |         UseTree::Tree(trees) => trees | ||||||
|  |             .iter() | ||||||
|  |             .try_for_each(|tree| import_tree(table, src, dst, tree, seen)), | ||||||
|  |         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, seen) | ||||||
|  |         } | ||||||
|  |         UseTree::Alias(src_name, dst_name) => { | ||||||
|  |             import_name(table, src, src_name, dst, dst_name, seen) | ||||||
|  |         } | ||||||
|  |         UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name, seen), | ||||||
|  |         UseTree::Glob => import_glob(table, src, dst, seen), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn import_glob<'a>( | ||||||
|  |     table: &mut Table<'a>, | ||||||
|  |     src: Handle, | ||||||
|  |     dst: Handle, | ||||||
|  |     seen: &mut Seen, | ||||||
|  | ) -> UseResult<'a, ()> { | ||||||
|  |     let Table { children, imports, .. } = table; | ||||||
|  |  | ||||||
|  |     if let Some(c) = children.get(&src) { | ||||||
|  |         imports.entry(dst).or_default().extend(c) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     import_deps(table, src, seen)?; | ||||||
|  |  | ||||||
|  |     let Table { imports, .. } = table; | ||||||
|  |  | ||||||
|  |     // Importing imports requires some extra work, since we can't `get_many_mut` | ||||||
|  |     if let Some(i) = imports.get(&src) { | ||||||
|  |         let uses: Vec<_> = i.iter().map(|(&k, &v)| (k, v)).collect(); | ||||||
|  |         imports.entry(dst).or_default().extend(uses); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn import_name<'a>( | ||||||
|  |     table: &mut Table<'a>, | ||||||
|  |     src: Handle, | ||||||
|  |     src_name: &Sym, | ||||||
|  |     dst: Handle, | ||||||
|  |     dst_name: &Sym, | ||||||
|  |     seen: &mut Seen, | ||||||
|  | ) -> UseResult<'a, ()> { | ||||||
|  |     import_deps(table, src, seen)?; | ||||||
|  |     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(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Imports the dependencies of this node | ||||||
|  | fn import_deps<'a>(table: &mut Table<'a>, node: Handle, seen: &mut Seen) -> UseResult<'a, ()> { | ||||||
|  |     if let Some(items) = table.use_items.get(&node) { | ||||||
|  |         let out = items.clone(); | ||||||
|  |         for item in out { | ||||||
|  |             import_one(table, item, seen)?; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     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}"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										179
									
								
								compiler/cl-typeck/src/stage/populate.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								compiler/cl-typeck/src/stage/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_entry_mut(), name: None } | ||||||
|  |     } | ||||||
|  |     /// Constructs a new Populator with the provided parent Handle | ||||||
|  |     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) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										308
									
								
								compiler/cl-typeck/src/table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								compiler/cl-typeck/src/table.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,308 @@ | |||||||
|  | //! The [Table] is a monolithic data structure representing everything the type checker | ||||||
|  | //! knows about a program. | ||||||
|  | //! | ||||||
|  | //! Individual nodes in the table can be queried using the [Entry] API ([Table::entry]) | ||||||
|  | //! or modified using the [EntryMut] API ([Table::entry_mut]). | ||||||
|  | //! | ||||||
|  | //! # Contents of a "node" | ||||||
|  | //! Always present: | ||||||
|  | //! - [NodeKind]: Determines how this node will be treated during the [stages](crate::stage) of | ||||||
|  | //!   compilation | ||||||
|  | //! - [Parent node](Handle): Arranges this node in the hierarchical graph structure | ||||||
|  | //! | ||||||
|  | //! Populated as needed: | ||||||
|  | //! - Children: An associative array of [names](Sym) to child nodes in the graph. Child nodes are | ||||||
|  | //!   arranged in a *strict* tree structure, with no back edges | ||||||
|  | //! - Imports: An associative array of [names](Sym) to other nodes in the graph. Not all import | ||||||
|  | //!   nodes are back edges, but all back edges *must be* import nodes. | ||||||
|  | //! - [Types](TypeKind): Contains type information populated through type checking and inference. | ||||||
|  | //!   Nodes with unpopulated types may be considered type variables in the future. | ||||||
|  | //! - [Spans][span]: Positional information from the source text. See [cl_structures::span]. | ||||||
|  | //! - [Metas](Meta): Metadata decorators. These may have an effect throughout the compiler. | ||||||
|  | //! - [Sources](Source): Pointers back into the AST, for future analysis. | ||||||
|  | //! - Impl Targets: Sparse mapping of `impl` nodes to their corresponding targets. | ||||||
|  | //! - etc. | ||||||
|  | //! | ||||||
|  | //! [span]: struct@Span | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | /// The table is a monolithic data structure representing everything the type checker | ||||||
|  | /// knows about a program. | ||||||
|  | /// | ||||||
|  | /// See [module documentation](self). | ||||||
|  | #[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>>, | ||||||
|  |     pub(crate) use_items: HashMap<Handle, Vec<Handle>>, | ||||||
|  |     types: HashMap<Handle, TypeKind>, | ||||||
|  |     spans: HashMap<Handle, Span>, | ||||||
|  |     metas: HashMap<Handle, &'a [Meta]>, | ||||||
|  |     sources: HashMap<Handle, Source<'a>>, | ||||||
|  |     // code: HashMap<Handle, BasicBlock>, // TODO: lower sources | ||||||
|  |     impl_targets: HashMap<Handle, Handle>, | ||||||
|  |     anon_types: HashMap<TypeKind, Handle>, | ||||||
|  |  | ||||||
|  |     // --- Queues for algorithms --- | ||||||
|  |     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(), | ||||||
|  |             use_items: 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) { | ||||||
|  |         let parent = self.parents[item]; | ||||||
|  |         self.use_items.entry(parent).or_default().push(item); | ||||||
|  |         self.uses.push(item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn mark_impl_item(&mut self, item: Handle) { | ||||||
|  |         self.impls.push(item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn handle_iter(&mut self) -> impl Iterator<Item = Handle> { | ||||||
|  |         self.kinds.keys() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns handles to all nodes sequentially by [Entry] | ||||||
|  |     pub fn debug_entry_iter(&self) -> impl Iterator<Item = Entry<'_, 'a>> { | ||||||
|  |         self.kinds.keys().map(|key| key.to_entry(self)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Gets the [Handle] of an anonymous type with the provided [TypeKind]. | ||||||
|  |     /// If not already present, a new one is created. | ||||||
|  |     pub(crate) 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_entry(&self) -> Entry<'_, 'a> { | ||||||
|  |         self.root.to_entry(self) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn root_entry_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,31 @@ | |||||||
| use crate::{ | //! A [TypeKind] is a node in the [Table](crate::table::Table)'s type graph
 | ||||||
|     handle::DefID, | 
 | ||||||
|     module::Module, | use crate::handle::Handle; | ||||||
|     node::{Node, NodeSource}, | use cl_ast::{Sym, Visibility}; | ||||||
| }; |  | ||||||
| use cl_ast::{Meta, Sym, Visibility}; |  | ||||||
| use std::{fmt::Debug, str::FromStr}; | use std::{fmt::Debug, str::FromStr}; | ||||||
| 
 | 
 | ||||||
| mod display; | mod display; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, PartialEq, Eq)] | /// A [TypeKind] represents an item
 | ||||||
| pub struct Def<'a> { | /// (a component of a [Table](crate::table::Table))
 | ||||||
|     pub node: Node<'a>, |  | ||||||
|     pub kind: DefKind, |  | ||||||
|     pub module: Module, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 component of a [Project](crate::project::Project)).
 |  | ||||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||||
| pub enum TypeKind { | pub enum TypeKind { | ||||||
|     /// An alias for an already-defined type
 |     /// An alias for an already-defined type
 | ||||||
|     Alias(Option<DefID>), |     Instance(Handle), | ||||||
|     /// A primitive type, built-in to the compiler
 |     /// A primitive type, built-in to the compiler
 | ||||||
|     Intrinsic(Intrinsic), |     Intrinsic(Intrinsic), | ||||||
|     /// A user-defined aromatic data type
 |     /// A user-defined aromatic data type
 | ||||||
|     Adt(Adt), |     Adt(Adt), | ||||||
|     /// A reference to an already-defined type: &T
 |     /// A reference to an already-defined type: &T
 | ||||||
|     Ref(u16, DefID), |     Ref(u16, Handle), | ||||||
|     /// A contiguous view of dynamically sized memory
 |     /// A contiguous view of dynamically sized memory
 | ||||||
|     Slice(DefID), |     Slice(Handle), | ||||||
|     /// A contiguous view of statically sized memory
 |     /// A contiguous view of statically sized memory
 | ||||||
|     Array(DefID, usize), |     Array(Handle, usize), | ||||||
|     /// A tuple of existing types
 |     /// A tuple of existing types
 | ||||||
|     Tuple(Vec<DefID>), |     Tuple(Vec<Handle>), | ||||||
|     /// A function which accepts multiple inputs and produces an output
 |     /// A function which accepts multiple inputs and produces an output
 | ||||||
|     FnSig { args: DefID, rety: DefID }, |     FnSig { args: Handle, rety: Handle }, | ||||||
|     /// The unit type
 |     /// The unit type
 | ||||||
|     Empty, |     Empty, | ||||||
|     /// The never type
 |     /// The never type
 | ||||||
| @@ -117,22 +38,18 @@ pub enum TypeKind { | |||||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||||
| pub enum Adt { | pub enum Adt { | ||||||
|     /// A union-like enum type
 |     /// 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
 |     /// A structural product type with named members
 | ||||||
|     Struct(Vec<(Sym, Visibility, DefID)>), |     Struct(Vec<(Sym, Visibility, Handle)>), | ||||||
|     /// A structural product type with unnamed members
 |     /// 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
 |     /// A structural product type of neither named nor unnamed members
 | ||||||
|     UnitStruct, |     UnitStruct, | ||||||
| 
 | 
 | ||||||
|     /// A choose your own undefined behavior type
 |     /// A choose your own undefined behavior type
 | ||||||
|     /// TODO: should unions be a language feature?
 |     /// TODO: should unions be a language feature?
 | ||||||
|     Union(Vec<(Sym, DefID)>), |     Union(Vec<(Sym, Handle)>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// The set of compiler-intrinsic types.
 | /// The set of compiler-intrinsic types.
 | ||||||
| @@ -1,60 +1,14 @@ | |||||||
| //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
 | //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
 | ||||||
| 
 | 
 | ||||||
| use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind}; | use super::{Adt, Intrinsic, TypeKind}; | ||||||
| use crate::{format_utils::*, node::Node}; | use crate::format_utils::*; | ||||||
| use cl_ast::format::FmtAdapter; | use cl_ast::format::FmtAdapter; | ||||||
| use std::fmt::{self, Display, Write}; | 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 { | impl Display for TypeKind { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         match self { |         match self { | ||||||
|             TypeKind::Alias(def) => match def { |             TypeKind::Instance(def) => write!(f, "alias to #{def}"), | ||||||
|                 Some(def) => write!(f, "alias to #{def}"), |  | ||||||
|                 None => f.write_str("type"), |  | ||||||
|             }, |  | ||||||
|             TypeKind::Intrinsic(i) => i.fmt(f), |             TypeKind::Intrinsic(i) => i.fmt(f), | ||||||
|             TypeKind::Adt(a) => a.fmt(f), |             TypeKind::Adt(a) => a.fmt(f), | ||||||
|             TypeKind::Ref(cnt, def) => { |             TypeKind::Ref(cnt, def) => { | ||||||
| @@ -93,14 +47,6 @@ impl Display for Adt { | |||||||
|                     }) |                     }) | ||||||
|                 })(f.delimit_with("enum {", "}")) |                 })(f.delimit_with("enum {", "}")) | ||||||
|             } |             } | ||||||
|             Adt::CLikeEnum(variants) => { |  | ||||||
|                 let mut variants = variants.iter(); |  | ||||||
|                 separate(", ", || { |  | ||||||
|                     let (name, descrim) = variants.next()?; |  | ||||||
|                     Some(move |f: &mut Delimit<_>| write!(f, "{name} = {descrim}")) |  | ||||||
|                 })(f.delimit_with("enum {", "}")) |  | ||||||
|             } |  | ||||||
|             Adt::FieldlessEnum => write!(f, "enum"), |  | ||||||
|             Adt::Struct(members) => { |             Adt::Struct(members) => { | ||||||
|                 let mut members = members.iter(); |                 let mut members = members.iter(); | ||||||
|                 separate(", ", || { |                 separate(", ", || { | ||||||
| @@ -1,336 +0,0 @@ | |||||||
| //! Performs step 2 of type checking: Evaluating type definitions |  | ||||||
|  |  | ||||||
| use crate::{ |  | ||||||
|     definition::{Adt, Def, DefKind, TypeKind, ValueKind}, |  | ||||||
|     handle::DefID, |  | ||||||
|     node::{Node, NodeSource}, |  | ||||||
|     project::{evaluate::EvaluableTypeExpression, Project as Prj}, |  | ||||||
| }; |  | ||||||
| use cl_ast::*; |  | ||||||
|  |  | ||||||
| /// Evaluate a single ID |  | ||||||
| pub fn resolve(prj: &mut Prj, id: DefID) -> Result<(), &'static str> { |  | ||||||
|     let Def { node: Node { kind: Some(source), meta, .. }, kind: DefKind::Undecided, .. } = prj[id] |  | ||||||
|     else { |  | ||||||
|         return Ok(()); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let kind = match &source { |  | ||||||
|         NodeSource::Root => "root", |  | ||||||
|         NodeSource::Alias(_) => "type", |  | ||||||
|         NodeSource::Module(_) => "mod", |  | ||||||
|         NodeSource::Enum(_) => "enum", |  | ||||||
|         NodeSource::Variant(_) => "variant", |  | ||||||
|         NodeSource::Struct(_) => "struct", |  | ||||||
|         NodeSource::Const(_) => "const", |  | ||||||
|         NodeSource::Static(_) => "static", |  | ||||||
|         NodeSource::Function(_) => "fn", |  | ||||||
|         NodeSource::Impl(_) => "impl", |  | ||||||
|         NodeSource::Use(_) => "use", |  | ||||||
|         NodeSource::Local(_) => "let", |  | ||||||
|         NodeSource::Ty(_) => "ty", |  | ||||||
|     }; |  | ||||||
|     let name = prj[id].name().unwrap_or("".into()); |  | ||||||
|  |  | ||||||
|     eprintln!("Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {name}\x1b[0m\" (`{id:?}`)"); |  | ||||||
|  |  | ||||||
|     for Meta { name, kind } in meta { |  | ||||||
|         if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) { |  | ||||||
|             prj[id].kind = DefKind::Type(TypeKind::Intrinsic( |  | ||||||
|                 s.parse().map_err(|_| "Failed to parse intrinsic")?, |  | ||||||
|             )); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if DefKind::Undecided == prj[id].kind { |  | ||||||
|         prj[id].kind = source.resolve_type(prj, id)?; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     eprintln!("\x1b[33m=> {}\x1b[0m", prj[id].kind); |  | ||||||
|  |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Resolves a given node |  | ||||||
| pub trait TypeResolvable<'a> { |  | ||||||
|     /// The return type upon success |  | ||||||
|     type Out; |  | ||||||
|     /// Resolves type expressions within this node |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str>; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for NodeSource<'a> { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         match self { |  | ||||||
|             NodeSource::Root => Ok(DefKind::Type(TypeKind::Module)), |  | ||||||
|             NodeSource::Module(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Alias(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Enum(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Variant(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Struct(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Const(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Static(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Function(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Local(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Impl(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Use(v) => v.resolve_type(prj, id), |  | ||||||
|             NodeSource::Ty(v) => v.resolve_type(prj, id), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Meta { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     #[allow(unused_variables)] |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let Meta { name, kind } = self; |  | ||||||
|         match (name.as_ref(), kind) { |  | ||||||
|             ("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => Ok(DefKind::Type( |  | ||||||
|                 TypeKind::Intrinsic(intrinsic.parse().map_err(|_| "unknown intrinsic type")?), |  | ||||||
|             )), |  | ||||||
|             (_, MetaKind::Plain) => Ok(DefKind::Type(TypeKind::Intrinsic( |  | ||||||
|                 name.parse().map_err(|_| "Unknown intrinsic type")?, |  | ||||||
|             ))), |  | ||||||
|             _ => Err("Unknown meta attribute"), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Module { |  | ||||||
|     type Out = DefKind; |  | ||||||
|     #[allow(unused_variables)] |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         Ok(DefKind::Type(TypeKind::Module)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Alias { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let alias = if let Some(ty) = &self.from { |  | ||||||
|             Some( |  | ||||||
|                 ty.evaluate(prj, parent) |  | ||||||
|                     .or_else(|_| ty.evaluate(prj, id)) |  | ||||||
|                     .map_err(|_| "Unresolved type in alias")?, |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             None |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         Ok(DefKind::Type(TypeKind::Alias(alias))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Enum { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let Enum { name: _, kind } = self; |  | ||||||
|         let EnumKind::Variants(v) = kind else { |  | ||||||
|             return Ok(DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum))); |  | ||||||
|         }; |  | ||||||
|         let mut fields = vec![]; |  | ||||||
|         for Variant { name, kind: _ } in v { |  | ||||||
|             let id = prj[id].module.get_type(*name); |  | ||||||
|             fields.push((*name, id)) |  | ||||||
|         } |  | ||||||
|         Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields)))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Variant { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         // Get the grandparent of this node, for name resolution |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let grandparent = prj.parent_of(parent).unwrap_or(parent); |  | ||||||
|         let Variant { name: _, kind } = self; |  | ||||||
|  |  | ||||||
|         Ok(DefKind::Type(match kind { |  | ||||||
|             VariantKind::Plain => return Ok(DefKind::Type(TypeKind::Empty)), |  | ||||||
|             VariantKind::CLike(_) => return Ok(DefKind::Undecided), |  | ||||||
|             VariantKind::Tuple(ty) => match &ty.kind { |  | ||||||
|                 TyKind::Empty => TypeKind::Tuple(vec![]), |  | ||||||
|                 TyKind::Tuple(TyTuple { types }) => { |  | ||||||
|                     TypeKind::Tuple(types.evaluate(prj, grandparent).map_err(|e| { |  | ||||||
|                         eprintln!("{e}"); |  | ||||||
|                         "" |  | ||||||
|                     })?) |  | ||||||
|                 } |  | ||||||
|                 _ => Err("Unexpected TyKind in tuple variant")?, |  | ||||||
|             }, |  | ||||||
|             VariantKind::Struct(members) => { |  | ||||||
|                 TypeKind::Adt(Adt::Struct(members.resolve_type(prj, parent)?)) |  | ||||||
|             } |  | ||||||
|         })) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Struct { |  | ||||||
|     type Out = DefKind; |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let Struct { name: _, kind } = self; |  | ||||||
|         Ok(match kind { |  | ||||||
|             StructKind::Empty => DefKind::Type(TypeKind::Empty), |  | ||||||
|             StructKind::Tuple(types) => DefKind::Type(TypeKind::Adt(Adt::TupleStruct({ |  | ||||||
|                 let mut out = vec![]; |  | ||||||
|                 for ty in types { |  | ||||||
|                     out.push(( |  | ||||||
|                         Visibility::Public, |  | ||||||
|                         ty.evaluate(prj, parent) |  | ||||||
|                             .map_err(|_| "Unresolved type in tuple-struct member")?, |  | ||||||
|                     )); |  | ||||||
|                 } |  | ||||||
|                 out |  | ||||||
|             }))), |  | ||||||
|             StructKind::Struct(members) => { |  | ||||||
|                 DefKind::Type(TypeKind::Adt(Adt::Struct(members.resolve_type(prj, id)?))) |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a StructMember { |  | ||||||
|     type Out = (Sym, Visibility, DefID); |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let StructMember { name, vis, ty } = self; |  | ||||||
|  |  | ||||||
|         let ty = ty |  | ||||||
|             .evaluate(prj, parent) |  | ||||||
|             .map_err(|_| "Invalid type while resolving StructMember")?; |  | ||||||
|  |  | ||||||
|         Ok((*name, *vis, ty)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Const { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let Const { ty, .. } = self; |  | ||||||
|         let ty = ty |  | ||||||
|             .evaluate(prj, id) |  | ||||||
|             .map_err(|_| "Invalid type while resolving const")?; |  | ||||||
|         Ok(DefKind::Value(ValueKind::Const(ty))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Static { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let Static { ty, .. } = self; |  | ||||||
|         let ty = ty |  | ||||||
|             .evaluate(prj, parent) |  | ||||||
|             .map_err(|_| "Invalid type while resolving static")?; |  | ||||||
|         Ok(DefKind::Value(ValueKind::Static(ty))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Function { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|         let Function { sign, .. } = self; |  | ||||||
|         let sign = sign |  | ||||||
|             .evaluate(prj, parent) |  | ||||||
|             .map_err(|_| "Invalid type in function signature")?; |  | ||||||
|         Ok(DefKind::Value(ValueKind::Fn(sign))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Let { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     #[allow(unused)] |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let Let { mutable, name, ty, init } = self; |  | ||||||
|         Ok(DefKind::Undecided) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Impl { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let parent = prj.parent_of(id).unwrap_or(id); |  | ||||||
|  |  | ||||||
|         let target = match &self.target { |  | ||||||
|             ImplKind::Type(t) => t.evaluate(prj, parent), |  | ||||||
|             ImplKind::Trait { for_type, .. } => for_type.evaluate(prj, parent), |  | ||||||
|         } |  | ||||||
|         .map_err(|_| "Unresolved type in impl target")?; |  | ||||||
|  |  | ||||||
|         match prj.pool.get_many_mut([id, target]) { |  | ||||||
|             // TODO: Better error handling |  | ||||||
|             Err(_) => Err(concat!( |  | ||||||
|                 file!(), |  | ||||||
|                 line!(), |  | ||||||
|                 column!(), |  | ||||||
|                 "id and target are same" |  | ||||||
|             ))?, |  | ||||||
|             Ok([id, target]) => { |  | ||||||
|                 for (name, def) in &id.module.types { |  | ||||||
|                     target.module.insert_type(*name, *def); |  | ||||||
|                 } |  | ||||||
|                 for (name, def) in &id.module.values { |  | ||||||
|                     target.module.insert_value(*name, *def); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Ok(DefKind::Impl(target)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a Use { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         todo!("Resolve types for {self} with ID {id} in {prj:?}") |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a> TypeResolvable<'a> for &'a TyKind { |  | ||||||
|     type Out = DefKind; |  | ||||||
|  |  | ||||||
|     #[allow(unused)] |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a, T> TypeResolvable<'a> for &'a [T] |  | ||||||
| where &'a T: TypeResolvable<'a> |  | ||||||
| { |  | ||||||
|     type Out = Vec<<&'a T as TypeResolvable<'a>>::Out>; |  | ||||||
|  |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         let mut members = vec![]; |  | ||||||
|         for member in self { |  | ||||||
|             members.push(member.resolve_type(prj, id)?); |  | ||||||
|         } |  | ||||||
|         Ok(members) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<'a, T> TypeResolvable<'a> for Option<&'a T> |  | ||||||
| where &'a T: TypeResolvable<'a> |  | ||||||
| { |  | ||||||
|     type Out = Option<<&'a T as TypeResolvable<'a>>::Out>; |  | ||||||
|     fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> { |  | ||||||
|         match self { |  | ||||||
|             Some(t) => Some(t.resolve_type(prj, id)).transpose(), |  | ||||||
|             None => Ok(None), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,125 +0,0 @@ | |||||||
| //! WIP use-item importer. This performs eager import resolution on the AST |  | ||||||
| //! |  | ||||||
| //! # TODOs: |  | ||||||
| //! - [ ] Resolve imports using a graph traversal rather than linear iteration |  | ||||||
| //! - [ ] Separate imported items from natively declared items |  | ||||||
| //! - [ ] Separate the use-import pass from the project |  | ||||||
| //! - [ ] Report errors in a meaningful way |  | ||||||
| //! - [ ] Lazy import resolution using graph-edge traversal during name lookup? |  | ||||||
| //!     - It doesn't seem to me like the imports in a given scope *can change*. |  | ||||||
|  |  | ||||||
| #![allow(unused)] |  | ||||||
| use std::fmt::format; |  | ||||||
|  |  | ||||||
| use cl_ast::*; |  | ||||||
|  |  | ||||||
| use crate::{ |  | ||||||
|     definition::{Def, DefKind}, |  | ||||||
|     handle::DefID, |  | ||||||
|     node::NodeSource, |  | ||||||
|     project::Project, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| type UseResult = Result<(), String>; |  | ||||||
|  |  | ||||||
| impl<'a> Project<'a> { |  | ||||||
|     pub fn resolve_imports(&mut self) -> UseResult { |  | ||||||
|         for id in self.pool.keys() { |  | ||||||
|             self.visit_def(id)?; |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_def(&mut self, id: DefID) -> UseResult { |  | ||||||
|         let Def { kind, node, module } = &self.pool[id]; |  | ||||||
|         if let (DefKind::Use(parent), Some(NodeSource::Use(u))) = (kind, node.kind) { |  | ||||||
|             println!("Importing use item {u}"); |  | ||||||
|             self.visit_use(u, *parent); |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use(&mut self, u: &'a Use, parent: DefID) -> UseResult { |  | ||||||
|         let Use { absolute, tree } = u; |  | ||||||
|  |  | ||||||
|         self.visit_use_tree(tree, parent, if *absolute { self.root } else { parent }) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use_tree(&mut self, tree: &'a UseTree, parent: DefID, c: DefID) -> UseResult { |  | ||||||
|         match tree { |  | ||||||
|             UseTree::Tree(trees) => { |  | ||||||
|                 for tree in trees { |  | ||||||
|                     self.visit_use_tree(tree, parent, c)?; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             UseTree::Path(part, rest) => { |  | ||||||
|                 let c = self.evaluate(part, c)?; |  | ||||||
|                 self.visit_use_tree(rest, parent, c)?; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             UseTree::Name(name) => self.visit_use_leaf(name, parent, c)?, |  | ||||||
|             UseTree::Alias(from, to) => self.visit_use_alias(from, to, parent, c)?, |  | ||||||
|             UseTree::Glob => self.visit_use_glob(parent, c)?, |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use_path(&mut self) -> UseResult { |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use_leaf(&mut self, name: &'a Sym, parent: DefID, c: DefID) -> UseResult { |  | ||||||
|         self.visit_use_alias(name, name, parent, c) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use_alias( |  | ||||||
|         &mut self, |  | ||||||
|         from: &Sym, |  | ||||||
|         name: &Sym, |  | ||||||
|         parent: DefID, |  | ||||||
|         c: DefID, |  | ||||||
|     ) -> UseResult { |  | ||||||
|         let mut imported = false; |  | ||||||
|         let c_mod = &self[c].module; |  | ||||||
|         let (tid, vid) = ( |  | ||||||
|             c_mod.types.get(from).copied(), |  | ||||||
|             c_mod.values.get(from).copied(), |  | ||||||
|         ); |  | ||||||
|         let parent = &mut self[parent].module; |  | ||||||
|  |  | ||||||
|         if let Some(tid) = tid { |  | ||||||
|             parent.types.insert(*name, tid); |  | ||||||
|             imported = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if let Some(vid) = vid { |  | ||||||
|             parent.values.insert(*name, vid); |  | ||||||
|             imported = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if imported { |  | ||||||
|             Ok(()) |  | ||||||
|         } else { |  | ||||||
|             Err(format!("Identifier {name} not found in module {c}")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub fn visit_use_glob(&mut self, parent: DefID, c: DefID) -> UseResult { |  | ||||||
|         // Loop over all the items in c, and add them as items in the parent |  | ||||||
|         if parent == c { |  | ||||||
|             return Ok(()); |  | ||||||
|         } |  | ||||||
|         let [parent, c] = self |  | ||||||
|             .pool |  | ||||||
|             .get_many_mut([parent, c]) |  | ||||||
|             .expect("parent and c are not the same"); |  | ||||||
|  |  | ||||||
|         for (k, v) in &c.module.types { |  | ||||||
|             parent.module.types.entry(*k).or_insert(*v); |  | ||||||
|         } |  | ||||||
|         for (k, v) in &c.module.values { |  | ||||||
|             parent.module.values.entry(*k).or_insert(*v); |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -62,3 +62,18 @@ fn if_else() -> i32 { | |||||||
|     } |     } | ||||||
|     // block 4 |     // block 4 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | mod horrible_imports { | ||||||
|  |     mod foo { | ||||||
|  |         use super::{bar::*, baz::*}; | ||||||
|  |         struct Foo(&Foo, &Bar) | ||||||
|  |     } | ||||||
|  |     mod bar { | ||||||
|  |         use super::{foo::*, baz::*}; | ||||||
|  |         struct Bar(&Foo, &Baz) | ||||||
|  |     } | ||||||
|  |     mod baz { | ||||||
|  |         use super::{foo::*, bar::*}; | ||||||
|  |         struct Baz(&Foo, &Bar) | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user