cl-typeck: Outline all modules.

This commit is contained in:
John 2024-04-21 23:05:06 -05:00
parent 9449e5ba06
commit bf16338166
9 changed files with 1364 additions and 1440 deletions

View File

@ -0,0 +1,185 @@
use crate::{key::DefID, module::Module};
use cl_ast::{Item, Meta, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Def<'a> {
pub name: &'a str,
pub vis: Visibility,
pub meta: &'a [Meta],
pub kind: DefKind<'a>,
pub source: Option<&'a Item>,
pub module: Module<'a>,
}
mod builder_functions {
use super::*;
impl<'a> Def<'a> {
pub fn set_name(&mut self, name: &'a str) -> &mut Self {
self.name = name;
self
}
pub fn set_vis(&mut self, vis: Visibility) -> &mut Self {
self.vis = vis;
self
}
pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self {
self.meta = meta;
self
}
pub fn set_kind(&mut self, kind: DefKind<'a>) -> &mut Self {
self.kind = kind;
self
}
pub fn set_source(&mut self, source: &'a Item) -> &mut Self {
self.source = Some(source);
self
}
pub fn set_module(&mut self, module: Module<'a>) -> &mut Self {
self.module = module;
self
}
}
}
impl Default for Def<'_> {
fn default() -> Self {
Self {
name: Default::default(),
vis: Visibility::Public,
meta: Default::default(),
kind: Default::default(),
source: Default::default(),
module: Default::default(),
}
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub enum DefKind<'a> {
/// 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>),
/// 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),
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)]
pub enum TypeKind<'a> {
/// An alias for an already-defined type
Alias(Option<DefID>),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
/// A user-defined aromatic data type
Adt(Adt<'a>),
/// A reference to an already-defined type: &T
Ref(u16, DefID),
/// A contiguous view of dynamically sized memory
Slice(DefID),
/// A contiguous view of statically sized memory
Array(DefID, usize),
/// A tuple of existing types
Tuple(Vec<DefID>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: DefID, rety: DefID },
/// The unit type
Empty,
/// The never type
Never,
/// The Self type
SelfTy,
/// An untyped module
Module,
}
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt<'a> {
/// A union-like enum type
Enum(Vec<(&'a str, Option<DefID>)>),
/// A C-like enum
CLikeEnum(Vec<(&'a str, u128)>),
/// An enum with no fields, which can never be constructed
FieldlessEnum,
/// A structural product type with named members
Struct(Vec<(&'a str, Visibility, DefID)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, DefID)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(&'a str, DefID)>),
}
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic {
/// An 8-bit signed integer: `#[intrinsic = "i8"]`
I8,
/// A 16-bit signed integer: `#[intrinsic = "i16"]`
I16,
/// A 32-bit signed integer: `#[intrinsic = "i32"]`
I32,
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
I64,
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
// I128,
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
U8,
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
U16,
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
U32,
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
U64,
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
// U128,
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
Bool,
/// The unicode codepoint type: #[intrinsic = "char"]
Char,
}
impl FromStr for Intrinsic {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"i8" => Intrinsic::I8,
"i16" => Intrinsic::I16,
"i32" => Intrinsic::I32,
"i64" => Intrinsic::I64,
"u8" => Intrinsic::U8,
"u16" => Intrinsic::U16,
"u32" => Intrinsic::U32,
"u64" => Intrinsic::U64,
"bool" => Intrinsic::Bool,
"char" => Intrinsic::Char,
_ => Err(())?,
})
}
}

View File

@ -0,0 +1,15 @@
use cl_structures::intern_pool::*;
// define the index types
make_intern_key! {
/// Uniquely represents a [Def][1] in the [Def][1] [Pool]
///
/// [1]: crate::definition::Def
DefID,
}
impl std::fmt::Display for DefID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
use cl_structures::intern_pool::InternKey;
use crate::key::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<'a> {
pub parent: Option<DefID>,
pub types: HashMap<&'a str, DefID>,
pub values: HashMap<&'a str, DefID>,
pub imports: Vec<DefID>,
}
impl<'a> Module<'a> {
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: &'a str) -> (Option<DefID>, Option<DefID>) {
(self.get_type(name), self.get_value(name))
}
pub fn get_type(&self, name: &'a str) -> Option<DefID> {
self.types.get(name).copied()
}
pub fn get_value(&self, name: &'a str) -> 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: &'a str, 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: &'a str, 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(())
}
}

View File

@ -0,0 +1,245 @@
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
use crate::{
definition::{Def, DefKind},
key::DefID,
module::Module as Mod,
project::Project as Prj,
};
use cl_ast::*;
pub trait NameCollectable<'a> {
/// Collects the identifiers within this node,
/// returning a new [DefID] if any were allocated,
/// else returning the parent [DefID]
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str>;
fn collect_in_root(&'a self, c: &mut Prj<'a>) -> Result<DefID, &'static str> {
self.collect(c, c.root)
}
}
impl<'a> NameCollectable<'a> for File {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
for item in &self.items {
item.collect(c, parent)?;
}
Ok(parent)
}
}
impl<'a> NameCollectable<'a> for Item {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Item { attrs: Attrs { meta }, vis, kind, .. } = self;
let id = match kind {
ItemKind::Impl(i) => i.collect(c, parent),
ItemKind::Module(i) => i.collect(c, parent),
ItemKind::Alias(i) => i.collect(c, parent),
ItemKind::Enum(i) => i.collect(c, parent),
ItemKind::Struct(i) => i.collect(c, parent),
ItemKind::Const(i) => i.collect(c, parent),
ItemKind::Static(i) => i.collect(c, parent),
ItemKind::Function(i) => i.collect(c, parent),
ItemKind::Use(i) => i.collect(c, parent),
}?;
c[id].set_meta(meta).set_vis(*vis).set_source(self);
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Module {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Self { name: Identifier(name), kind } = self;
let module =
c.pool
.insert(Def { name, module: Mod::new(parent), ..Default::default() });
c[parent].module.types.insert(name, module);
match kind {
ModuleKind::Inline(file) => file.collect(c, module)?,
ModuleKind::Outline => todo!("Out of line modules: {name}"),
};
Ok(module)
}
}
impl<'a> NameCollectable<'a> for Impl {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Self { target: _, body } = self;
let def = Def { module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
// items will get reparented after name collection, when target is available
body.collect(c, id)?;
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;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Enum {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Enum { name: Identifier(name), .. } = self;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Struct {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Struct { name: Identifier(name), .. } = self;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Const {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Self { name: Identifier(name), init, .. } = self;
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
init.collect(c, id)?;
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Static {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Self { name: Identifier(name), init, .. } = self;
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
init.collect(c, id)?;
Ok(id)
}
}
impl<'a> NameCollectable<'a> for Function {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Self { name: Identifier(name), body, .. } = self;
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
body.collect(c, id)?;
Ok(id)
}
}
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)
}
}
impl<'a> NameCollectable<'a> for Stmt {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
self.kind.collect(c, parent)
}
}
impl<'a> NameCollectable<'a> for StmtKind {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
match self {
StmtKind::Empty => Ok(parent),
StmtKind::Local(Let { init, .. }) => init.collect(c, parent),
StmtKind::Item(item) => item.collect(c, parent),
StmtKind::Expr(expr) => expr.collect(c, parent),
}
}
}
impl<'a> NameCollectable<'a> for Expr {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
self.kind.collect(c, parent)
}
}
impl<'a> NameCollectable<'a> for ExprKind {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
match self {
ExprKind::Assign(Assign { parts, .. }) | ExprKind::Binary(Binary { parts, .. }) => {
parts.0.collect(c, parent)?;
parts.1.collect(c, parent)
}
ExprKind::Unary(Unary { tail, .. }) => tail.collect(c, parent),
ExprKind::Index(Index { head, indices }) => {
indices.collect(c, parent)?;
head.collect(c, parent)
}
ExprKind::Array(Array { values }) => values.collect(c, parent),
ExprKind::ArrayRep(ArrayRep { value, repeat }) => {
value.collect(c, parent)?;
repeat.collect(c, parent)
}
ExprKind::AddrOf(AddrOf { expr, .. }) => expr.collect(c, parent),
ExprKind::Block(block) => block.collect(c, parent),
ExprKind::Group(Group { expr }) => expr.collect(c, parent),
ExprKind::Tuple(Tuple { exprs }) => exprs.collect(c, parent),
ExprKind::While(While { cond, pass, fail })
| ExprKind::If(If { cond, pass, fail })
| ExprKind::For(For { cond, pass, fail, .. }) => {
cond.collect(c, parent)?;
pass.collect(c, parent)?;
fail.body.collect(c, parent)
}
ExprKind::Break(Break { body }) | ExprKind::Return(Return { body }) => {
body.collect(c, parent)
}
_ => Ok(parent),
}
}
}
impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for [T] {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let mut last = parent;
for expr in self {
last = expr.collect(c, parent)?;
}
Ok(last)
}
}
impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for Option<T> {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
match self {
Some(body) => body.collect(c, parent),
None => Ok(parent),
}
}
}
impl<'a, T: NameCollectable<'a>> NameCollectable<'a> for Box<T> {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
(**self).collect(c, parent)
}
}

View File

@ -0,0 +1,56 @@
//! 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 is_empty(&self) -> bool {
self.parts.is_empty()
}
pub fn len(&self) -> usize {
self.parts.len()
}
pub fn front(&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(())
}
}

View File

@ -0,0 +1,349 @@
//! A [Project] contains a tree of [Def]initions, referred to by their [Path]
use crate::{
definition::{Def, DefKind, TypeKind},
key::DefID,
module,
path::Path,
};
use cl_ast::{Identifier, PathPart, TyFn, TyKind, TyRef, TyTuple, Visibility};
use cl_structures::intern_pool::Pool;
use std::{
collections::HashMap,
ops::{Index, IndexMut},
};
use self::evaluate::EvaluableTypeExpression;
#[derive(Clone, Debug)]
pub struct Project<'a> {
pub pool: Pool<Def<'a>, DefID>,
/// Stores anonymous tuples, function pointer types, etc.
pub anon_types: HashMap<TypeKind<'a>, DefID>,
pub root: DefID,
}
impl Project<'_> {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Project<'_> {
fn default() -> Self {
let mut pool = Pool::default();
let root = pool.insert(Def {
name: "🌳 root 🌳",
kind: DefKind::Type(TypeKind::Module),
..Default::default()
});
// Insert the Never(!) type
let never = pool.insert(Def {
name: "!",
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::Never),
module: module::Module::new(root),
..Default::default()
});
let empty = pool.insert(Def {
name: "()",
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::Empty),
module: module::Module::new(root),
..Default::default()
});
// TODO: Self is not a real type!
let selfty = pool.insert(Def {
name: "Self",
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::SelfTy),
module: module::Module::new(root),
..Default::default()
});
let mut anon_types = HashMap::new();
anon_types.insert(TypeKind::Empty, empty);
anon_types.insert(TypeKind::Never, never);
anon_types.insert(TypeKind::SelfTy, selfty);
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,
}
}
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() {
[] => Some((Some(within), None, path)),
[PathPart::Ident(Identifier(name))] => {
let (ty, val) = self[within].module.get(name);
Some((ty, val, path.pop_front()?))
}
[PathPart::Ident(Identifier(name)), ..] => {
let ty = self[within].module.get_type(name)?;
self.get(path.pop_front()?, ty)
}
[PathPart::SelfKw, ..] => self.get(path.pop_front()?, within),
[PathPart::SuperKw, ..] => self.get(path.pop_front()?, self.parent_of(within)?),
}
}
/// 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.front() {
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::Ident(Identifier(name)) => match module.types.get(name.as_str()) {
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
None => Some((within, path)),
},
}
} else {
Some((within, path))
}
}
pub fn get_value<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> {
match path.front()? {
PathPart::Ident(Identifier(name)) => Some((
self[within].module.values.get(name.as_str()).copied()?,
path.pop_front()?,
)),
_ => None,
}
}
/// 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<'a>,
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 cl_ast::Ty;
/// 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::SelfTy => prj.anon_types[&TypeKind::SelfTy],
// TyKind::Path must be looked up explicitly
TyKind::Path(path) => {
let (id, path) = prj
.get_type(path.into(), parent)
.ok_or("Failed to get type")?;
if path.is_empty() {
id
} else {
let (id, path) =
prj.get_value(path, id).ok_or("Failed to get value")?;
path.is_empty()
.then_some(id)
.ok_or("Path not fully resolved")?
}
}
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 str {
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 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 {
kind: DefKind::Type(TypeKind::Tuple(types)),
module: module::Module::new(root),
..Default::default()
});
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 {
kind: DefKind::Type(TypeKind::Ref(*count, to)),
module: module::Module::new(root),
..Default::default()
});
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 {
kind: DefKind::Type(TypeKind::FnSig { args, rety }),
module: module::Module::new(root),
..Default::default()
});
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::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")?;
if path.is_empty() {
Ok(id)
} else {
let (id, path) = prj.get_value(path, id).ok_or("Failed to get value")?;
path.is_empty()
.then_some(id)
.ok_or(String::from("Path not fully resolved"))
}
}
}
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)
}
}
}

View File

@ -0,0 +1,299 @@
//! Performs step 2 of type checking: Evaluating type definitions
use crate::{
definition::{Adt, Def, DefKind, TypeKind, ValueKind},
key::DefID,
module,
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 (DefKind::Undecided, Some(source)) = (&prj[id].kind, prj[id].source) else {
return Ok(());
};
let kind = match &source.kind {
ItemKind::Alias(_) => "type",
ItemKind::Module(_) => "mod",
ItemKind::Enum(_) => "enum",
ItemKind::Struct(_) => "struct",
ItemKind::Const(_) => "const",
ItemKind::Static(_) => "static",
ItemKind::Function(_) => "fn",
ItemKind::Impl(_) => "impl",
ItemKind::Use(_) => "use",
};
eprintln!(
"Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {}\x1b[0m\" (`{id:?}`)",
prj[id].name
);
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(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str>;
}
impl<'a> TypeResolvable<'a> for Item {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { attrs: Attrs { meta }, kind, .. } = self;
for meta in meta {
if let Ok(def) = meta.resolve_type(prj, id) {
return Ok(def);
}
}
kind.resolve_type(prj, id)
}
}
impl<'a> TypeResolvable<'a> for Meta {
type Out = DefKind<'a>;
#[allow(unused_variables)]
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { name: Identifier(name), kind } = self;
match (name.as_str(), 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 ItemKind {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
if prj[id].source.map(|s| &s.kind as *const _) != Some(self as *const _) {
return Err("id is not self!");
}
match self {
ItemKind::Module(i) => i.resolve_type(prj, id),
ItemKind::Impl(i) => i.resolve_type(prj, id),
ItemKind::Alias(i) => i.resolve_type(prj, id),
ItemKind::Enum(i) => i.resolve_type(prj, id),
ItemKind::Struct(i) => i.resolve_type(prj, id),
ItemKind::Const(i) => i.resolve_type(prj, id),
ItemKind::Static(i) => i.resolve_type(prj, id),
ItemKind::Function(i) => i.resolve_type(prj, id),
ItemKind::Use(i) => i.resolve_type(prj, id),
}
}
}
impl<'a> TypeResolvable<'a> for Module {
type Out = DefKind<'a>;
#[allow(unused_variables)]
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
Ok(DefKind::Type(TypeKind::Module))
}
}
impl<'a> TypeResolvable<'a> for Impl {
type Out = DefKind<'a>;
fn resolve_type(&'a 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")?;
prj[id].module.parent = Some(target);
Ok(DefKind::Impl(target))
}
}
impl<'a> TypeResolvable<'a> for Use {
type Out = DefKind<'a>;
fn resolve_type(&'a 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 Alias {
type Out = DefKind<'a>;
fn resolve_type(&'a 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 Enum {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { name: _, kind } = self;
let EnumKind::Variants(v) = kind else {
return Ok(DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)));
};
let mut fields = vec![];
for v @ Variant { name: Identifier(name), kind: _ } in v {
let id = v.resolve_type(prj, id)?;
fields.push((name.as_str(), id))
}
Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields))))
}
}
impl<'a> TypeResolvable<'a> for Variant {
type Out = Option<DefID>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { name: Identifier(name), kind } = self;
let adt = match kind {
VariantKind::Plain => return Ok(None),
VariantKind::CLike(_) => todo!("Resolve variant info for C-like enums"),
VariantKind::Tuple(ty) => {
return ty
.evaluate(prj, parent)
.map_err(|_| "Unresolved type in enum tuple variant")
.map(Some)
}
VariantKind::Struct(members) => Adt::Struct(members.resolve_type(prj, id)?),
};
let def = Def {
name,
kind: DefKind::Type(TypeKind::Adt(adt)),
module: module::Module::new(id),
..Default::default()
};
let new_id = prj.pool.insert(def);
// Insert the struct variant type into the enum's namespace
prj[id].module.types.insert(name, new_id);
Ok(Some(new_id))
}
}
impl<'a> TypeResolvable<'a> for Struct {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { 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 StructMember {
type Out = (&'a str, Visibility, DefID);
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { name: Identifier(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 Const {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { 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 Static {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { 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 Function {
type Out = DefKind<'a>;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { sign, .. } = self;
let sign = sign
.evaluate(prj, parent)
.map_err(|_| "Invalid type in function signature")?;
Ok(DefKind::Value(ValueKind::Fn(sign)))
}
}
impl<'a, T: TypeResolvable<'a>> TypeResolvable<'a> for [T] {
type Out = Vec<T::Out>;
fn resolve_type(&'a 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>> TypeResolvable<'a> for Option<T> {
type Out = Option<T::Out>;
fn resolve_type(&'a 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),
}
}
}

View File

@ -0,0 +1,137 @@
//! 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},
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(())
}
}