diff --git a/compiler/cl-typeck/src/definition/display.rs b/compiler/cl-typeck/src/definition/display.rs index 8f60e24..a2f59c8 100644 --- a/compiler/cl-typeck/src/definition/display.rs +++ b/compiler/cl-typeck/src/definition/display.rs @@ -49,6 +49,7 @@ impl Display for DefKind<'_> { 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}"), } diff --git a/compiler/cl-typeck/src/lib.rs b/compiler/cl-typeck/src/lib.rs index ad3ed62..1d60a11 100644 --- a/compiler/cl-typeck/src/lib.rs +++ b/compiler/cl-typeck/src/lib.rs @@ -110,6 +110,8 @@ pub mod definition { 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>), /// A value, such as a `const`, `static`, or `fn` @@ -132,7 +134,7 @@ pub mod definition { Alias(Option), /// A primitive type, built-in to the compiler Intrinsic(Intrinsic), - /// A user-defined abstract data type + /// A user-defined aromatic data type Adt(Adt<'a>), /// A reference to an already-defined type: &T Ref(u16, DefID), @@ -154,7 +156,7 @@ pub mod definition { Module, } - /// A user-defined Abstract Data Type + /// A user-defined Aromatic Data Type #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Adt<'a> { /// A union-like enum type @@ -243,7 +245,7 @@ pub mod module { pub parent: Option, pub types: HashMap<&'a str, DefID>, pub values: HashMap<&'a str, DefID>, - pub imports: HashMap<&'a str, DefID>, + pub imports: Vec, } impl Module<'_> { @@ -261,7 +263,7 @@ pub mod module { if let Some(parent) = parent { writeln!(f, "Parent: {}", parent.get())?; } - for (name, table) in [("Types", types), ("Values", values), ("Imports", imports)] { + for (name, table) in [("Types", types), ("Values", values)] { if table.is_empty() { continue; } @@ -270,6 +272,12 @@ pub mod module { writeln!(f, " {name} => {id}")?; } } + if !imports.is_empty() { + write!(f, "Imports:")?; + for id in imports { + write!(f, "{id},")?; + } + } Ok(()) } } @@ -348,9 +356,8 @@ pub mod project { #[derive(Clone, Debug)] pub struct Project<'a> { pub pool: Pool, DefID>, - /// Stores anonymous tuples, function pointer types, etc.\ + /// Stores anonymous tuples, function pointer types, etc. pub anon_types: HashMap, DefID>, - pub impls_pending: Vec<(&'a cl_ast::Impl, DefID)>, pub root: DefID, } @@ -384,6 +391,7 @@ pub mod project { module: module::Module::new(root), ..Default::default() }); + // TODO: Self is not a real type! let selfty = pool.insert(Def { name: "Self", vis: Visibility::Public, @@ -397,7 +405,7 @@ pub mod project { anon_types.insert(TypeKind::Never, never); anon_types.insert(TypeKind::SelfTy, selfty); - Self { pool, root, anon_types, impls_pending: Default::default() } + Self { pool, root, anon_types } } } @@ -608,9 +616,20 @@ pub mod project { Path::from(self).evaluate(prj, parent) } } + impl EvaluableTypeExpression for PathPart { + type Out = DefID; + fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { + match self { + PathPart::SuperKw => prj + .parent_of(parent) + .ok_or_else(|| "Attempt to get super of root".into()), + PathPart::SelfKw => Ok(parent), + PathPart::Ident(Identifier(name)) => name.as_str().evaluate(prj, parent), + } + } + } impl<'a> EvaluableTypeExpression for Path<'a> { type Out = DefID; - fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result { let (id, path) = prj.get_type(*self, parent).ok_or("Failed to get type")?; @@ -714,7 +733,7 @@ pub mod name_collector { match kind { ModuleKind::Inline(file) => file.collect(c, module)?, - ModuleKind::Outline => todo!("Out of line modules"), + ModuleKind::Outline => todo!("Out of line modules: {name}"), }; Ok(module) } @@ -731,6 +750,17 @@ pub mod name_collector { Ok(id) } } + impl<'a> NameCollectable<'a> for Use { + fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { + let def = + Def { module: Mod::new(parent), kind: DefKind::Use(parent), ..Default::default() }; + + let id = c.pool.insert(def); + c[parent].module.imports.push(id); + + Ok(id) + } + } impl<'a> NameCollectable<'a> for Alias { fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { let Alias { to: Identifier(name), .. } = self; @@ -809,17 +839,6 @@ pub mod name_collector { Ok(id) } } - impl<'a> NameCollectable<'a> for Use { - fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result { - let Self { tree } = self; - todo!("Use {tree} in {parent}") - } - } - impl<'a> NameCollectable<'a> for UseTree { - fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result { - todo!("Use {self} in {parent}") - } - } impl<'a> NameCollectable<'a> for Block { fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result { self.stmts.as_slice().collect(c, parent) @@ -904,6 +923,135 @@ pub mod name_collector { } } +pub mod use_importer { + #![allow(unused)] + use std::fmt::format; + + use cl_ast::*; + + use crate::{ + definition::{Def, DefKind}, + key::DefID, + project::Project, + }; + + type UseResult = Result<(), String>; + + impl<'a> Project<'a> { + pub fn resolve_imports(&mut self) -> UseResult { + for id in self.pool.key_iter() { + self.visit_def(id)?; + } + Ok(()) + } + + pub fn visit_def(&mut self, id: DefID) -> UseResult { + let Def { name, vis, meta, kind, source, module } = &self.pool[id]; + if let (DefKind::Use(parent), Some(source)) = (kind, source) { + let Item { kind: ItemKind::Use(u), .. } = source else { + Err(format!("Not a use item: {source}"))? + }; + 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(Identifier(from), Identifier(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, + part: &'a Identifier, + parent: DefID, + c: DefID, + ) -> UseResult { + let Identifier(name) = part; + self.visit_use_alias(name, name, parent, c) + } + + pub fn visit_use_alias( + &mut self, + from: &'a str, + name: &'a str, + 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(()) + } + } +} + pub mod type_resolver { //! Performs step 2 of type checking: Evaluating type definitions