cl-typeck: Module imports v0.1

- NameCollects use items
- Preprocesses them
- Uses no fancy algorithms
- Doesn't respect item visibility at all
- *declaration-order-dependent* :(
- works, though! :)
- TODO: Lazy evaluation of literally any of this stuff.
This commit is contained in:
John 2024-04-21 22:43:25 -05:00
parent ef190f2d66
commit b796411742
2 changed files with 169 additions and 20 deletions

View File

@ -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}"),
}

View File

@ -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<DefID>),
/// 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<DefID>,
pub types: HashMap<&'a str, DefID>,
pub values: HashMap<&'a str, DefID>,
pub imports: HashMap<&'a str, DefID>,
pub imports: Vec<DefID>,
}
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<Def<'a>, DefID>,
/// Stores anonymous tuples, function pointer types, etc.\
/// Stores anonymous tuples, function pointer types, etc.
pub anon_types: HashMap<TypeKind<'a>, 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<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::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<Self::Out, String> {
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<DefID, &'static str> {
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<DefID, &'static str> {
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<DefID, &'static str> {
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<DefID, &'static str> {
todo!("Use {self} in {parent}")
}
}
impl<'a> NameCollectable<'a> for Block {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
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