cl-typeck: Crate-spanning refactor
TODO: remove all unreferenced files TODO: Finish resolving statically known types of values TODO: Type inference
This commit is contained in:
parent
b7ad285a11
commit
e19127facc
@ -1,15 +1,14 @@
|
||||
use cl_typeck::{
|
||||
entry::Entry, import::import, populate::Populator, table::Table,
|
||||
type_expression::TypeExpression,
|
||||
};
|
||||
|
||||
use cl_ast::{
|
||||
ast_visitor::{Fold, Visit},
|
||||
desugar::*,
|
||||
};
|
||||
use cl_lexer::Lexer;
|
||||
use cl_parser::{inliner::ModuleInliner, Parser};
|
||||
use cl_typeck::{
|
||||
handle::Handle,
|
||||
name_collector::NameCollector,
|
||||
project::Project,
|
||||
type_resolver::resolve,
|
||||
};
|
||||
use repline::{error::Error as RlError, prebaked::*};
|
||||
use std::{error::Error, path};
|
||||
|
||||
@ -31,7 +30,7 @@ const C_LISTING: &str = "\x1b[38;5;117m";
|
||||
static mut TREES: TreeManager = TreeManager::new();
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut prj = Project::default();
|
||||
let mut prj = Table::default();
|
||||
|
||||
let mut parser = Parser::new(Lexer::new(STDLIB));
|
||||
let code = match parser.file() {
|
||||
@ -42,13 +41,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
};
|
||||
let code = inline_modules(code, concat!(env!("PWD"), "/stdlib"));
|
||||
NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) });
|
||||
Populator::new(&mut prj).visit_file(unsafe { TREES.push(code) });
|
||||
// NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) });
|
||||
|
||||
main_menu(&mut prj)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main_menu(prj: &mut Project) -> Result<(), RlError> {
|
||||
fn main_menu(prj: &mut Table) -> Result<(), RlError> {
|
||||
banner();
|
||||
read_and(C_MAIN, "mu>", "? >", |line| {
|
||||
match line.trim() {
|
||||
@ -80,7 +80,7 @@ fn main_menu(prj: &mut Project) -> Result<(), RlError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn enter_code(prj: &mut Project) -> Result<(), RlError> {
|
||||
fn enter_code(prj: &mut Table) -> Result<(), RlError> {
|
||||
read_and(C_CODE, "cl>", "? >", |line| {
|
||||
if line.trim().is_empty() {
|
||||
return Ok(Response::Break);
|
||||
@ -89,8 +89,7 @@ fn enter_code(prj: &mut Project) -> Result<(), RlError> {
|
||||
let code = inline_modules(code, "");
|
||||
let code = WhileElseDesugar.fold_file(code);
|
||||
// Safety: this is totally unsafe
|
||||
NameCollector::new(prj).visit_file(unsafe { TREES.push(code) });
|
||||
|
||||
Populator::new(prj).visit_file(unsafe { TREES.push(code) });
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
@ -113,22 +112,22 @@ fn live_desugar() -> Result<(), RlError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn query_type_expression(prj: &mut Project) -> Result<(), RlError> {
|
||||
fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
|
||||
read_and(C_RESV, "ty>", "? >", |line| {
|
||||
if line.trim().is_empty() {
|
||||
return Ok(Response::Break);
|
||||
}
|
||||
// parse it as a path, and convert the path into a borrowed path
|
||||
let ty = Parser::new(Lexer::new(line)).ty()?.kind;
|
||||
let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj);
|
||||
pretty_handle(id)?;
|
||||
let id = ty.evaluate(prj, prj.root())?;
|
||||
pretty_handle(id.to_entry(prj))?;
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
|
||||
fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
|
||||
use cl_structures::index_map::MapIndex;
|
||||
use cl_typeck::handle::DefID;
|
||||
use cl_typeck::handle::Handle;
|
||||
read_and(C_BYID, "id>", "? >", |line| {
|
||||
if line.trim().is_empty() {
|
||||
return Ok(Response::Break);
|
||||
@ -141,9 +140,7 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
|
||||
let mut path = parser.path().unwrap_or_default();
|
||||
path.absolute = false;
|
||||
|
||||
let Some(handle) = DefID::from_usize(def_id).handle(prj) else {
|
||||
return Ok(Response::Deny);
|
||||
};
|
||||
let handle = Handle::from_usize(def_id).to_entry(prj);
|
||||
|
||||
print!(" > {{{C_LISTING}{handle}\x1b[0m}}");
|
||||
if !path.parts.is_empty() {
|
||||
@ -151,65 +148,114 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
|
||||
}
|
||||
println!();
|
||||
|
||||
let (ty, value) = handle.navigate((&path).into());
|
||||
if let (None, None) = (ty, value) {
|
||||
let Some(handle) = handle.nav(&path.parts) else {
|
||||
Err("No results.")?
|
||||
}
|
||||
if let Some(t) = ty {
|
||||
println!("Result: {}", t.id());
|
||||
pretty_handle(t)?;
|
||||
}
|
||||
if let Some(v) = value {
|
||||
println!("Result in value namespace: {}", v.id());
|
||||
pretty_handle(v)?;
|
||||
}
|
||||
};
|
||||
|
||||
pretty_handle(handle)?;
|
||||
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> {
|
||||
prj.resolve_imports()?;
|
||||
for id in prj.pool.keys() {
|
||||
resolve(prj, id)?;
|
||||
fn resolve_all(prj: &mut Table) -> Result<(), Box<dyn Error>> {
|
||||
println!("Resolving imports:");
|
||||
for (id, error) in import(prj) {
|
||||
eprintln!("{error} in {} ({id})", id.to_entry(prj))
|
||||
}
|
||||
println!("Types resolved successfully!");
|
||||
// todo!("Resolve imports");
|
||||
// prj.resolve_imports()?;
|
||||
// for id in prj.pool.keys() {
|
||||
// resolve(prj, id)?;
|
||||
// }
|
||||
// println!("Types resolved successfully!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn list_types(prj: &mut Project) {
|
||||
println!(" name | type");
|
||||
for (idx, key) in prj.pool.keys().enumerate() {
|
||||
let handle = key.handle_unchecked(prj);
|
||||
let vis = handle.vis().unwrap_or_default();
|
||||
fn list_types(prj: &mut Table) {
|
||||
for handle in prj.debug_handle_iter() {
|
||||
let id = handle.id();
|
||||
let kind = handle.kind().unwrap();
|
||||
let name = handle.name().unwrap_or("".into());
|
||||
println!("{idx:3}: {name:16}| {vis}{handle}");
|
||||
println!("{id:3}: {name:16}| {kind} {handle}");
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> {
|
||||
fn pretty_handle(h: Entry) -> Result<(), std::io::Error> {
|
||||
use std::io::Write;
|
||||
let mut stdout = std::io::stdout().lock();
|
||||
let Some(vis) = handle.vis() else {
|
||||
return writeln!(stdout, "Invalid handle: {handle}");
|
||||
let mut out = std::io::stdout().lock();
|
||||
let Some(kind) = h.kind() else {
|
||||
return writeln!(out, "Invalid handle: {h}");
|
||||
};
|
||||
writeln!(stdout, "{C_LISTING}{}\x1b[0m: {vis}{handle}", handle.id())?;
|
||||
if let Some(parent) = handle.parent() {
|
||||
writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {parent}")?;
|
||||
write!(out, "{C_LISTING}{kind}")?;
|
||||
if let Some(name) = h.name() {
|
||||
write!(out, " {name}")?;
|
||||
}
|
||||
if let Some(types) = handle.types() {
|
||||
writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?;
|
||||
for (name, def) in types {
|
||||
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
|
||||
writeln!(out, "\x1b[0m: {h}")?;
|
||||
|
||||
if let Some(parent) = h.parent() {
|
||||
writeln!(out, "- {C_LISTING}Parent\x1b[0m: {parent}")?;
|
||||
}
|
||||
|
||||
if let Some(span) = h.span() {
|
||||
writeln!(
|
||||
out,
|
||||
"- {C_LISTING}Span:\x1b[0m ({}, {})",
|
||||
span.head, span.tail
|
||||
)?;
|
||||
}
|
||||
|
||||
match h.meta() {
|
||||
Some(meta) if !meta.is_empty() => {
|
||||
writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?;
|
||||
for meta in meta {
|
||||
writeln!(out, " - {meta}")?;
|
||||
}
|
||||
}
|
||||
if let Some(values) = handle.values() {
|
||||
writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?;
|
||||
for (name, def) in values {
|
||||
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(children) = h.children() {
|
||||
writeln!(out, "- {C_LISTING}Children:\x1b[0m")?;
|
||||
for (name, child) in children {
|
||||
writeln!(
|
||||
out,
|
||||
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
|
||||
h.with_id(*child)
|
||||
)?
|
||||
}
|
||||
}
|
||||
write!(stdout, "\x1b[0m")
|
||||
|
||||
if let Some(imports) = h.imports() {
|
||||
writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?;
|
||||
for (name, child) in imports {
|
||||
writeln!(
|
||||
out,
|
||||
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
|
||||
h.with_id(*child)
|
||||
)?
|
||||
}
|
||||
}
|
||||
|
||||
// let Some(vis) = handle.vis() else {
|
||||
// return writeln!(stdout, "Invalid handle: {handle}");
|
||||
// };
|
||||
// writeln!(stdout, "{C_LISTING}{}\x1b[0m: {vis}{handle}", handle.id())?;
|
||||
// if let Some(parent) = handle.parent() {
|
||||
// }
|
||||
// if let Some(types) = handle.types() {
|
||||
// writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?;
|
||||
// for (name, def) in types {
|
||||
// }
|
||||
// }
|
||||
// if let Some(values) = handle.values() {
|
||||
// writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?;
|
||||
// for (name, def) in values {
|
||||
// writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
|
||||
// }
|
||||
// }
|
||||
// write!(stdout, "\x1b[0m")
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File {
|
||||
|
69
compiler/cl-typeck/src/categorize.rs
Normal file
69
compiler/cl-typeck/src/categorize.rs
Normal file
@ -0,0 +1,69 @@
|
||||
//! Categorizes all top-level entries in a table
|
||||
|
||||
use crate::{
|
||||
handle::Handle,
|
||||
source::Source,
|
||||
table::Table,
|
||||
type_expression::{Error as TypeEval, TypeExpression},
|
||||
type_kind::TypeKind,
|
||||
};
|
||||
use cl_ast::*;
|
||||
|
||||
/// Ensures a type entry exists for the provided handle in the table
|
||||
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
|
||||
let Some(source) = table.source(node) else {
|
||||
Err(Error::NoSource)?
|
||||
};
|
||||
|
||||
match source {
|
||||
Source::Root => {}
|
||||
Source::Module(_) => {}
|
||||
Source::Alias(a) => categorize_alias(table, node, a)?,
|
||||
Source::Enum(_) => todo!(),
|
||||
Source::Variant(_) => todo!(),
|
||||
Source::Struct(_) => todo!(),
|
||||
Source::Const(_) => todo!(),
|
||||
Source::Static(_) => todo!(),
|
||||
Source::Function(_) => todo!(),
|
||||
Source::Local(_) => todo!(),
|
||||
Source::Impl(_) => todo!(),
|
||||
Source::Use(_) => todo!(),
|
||||
Source::Ty(ty) => {
|
||||
ty.evaluate(table, node)?;
|
||||
}
|
||||
}
|
||||
|
||||
todo!("categorize {node} in {table:?}")
|
||||
}
|
||||
|
||||
pub fn categorize_alias(table: &mut Table, node: Handle, a: &Alias) -> CatResult<()> {
|
||||
let kind = match &a.from {
|
||||
Some(ty) => TypeKind::Alias(ty.evaluate(table, node)?),
|
||||
None => TypeKind::Empty,
|
||||
};
|
||||
table.set_ty(node, kind);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn categorize_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> {
|
||||
let kind = TypeKind::Alias(c.ty.evaluate(table, node)?);
|
||||
|
||||
table.set_ty(node, kind);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type CatResult<T> = Result<T, Error>;
|
||||
|
||||
pub enum Error {
|
||||
NoSource,
|
||||
BadMeta(Meta),
|
||||
Recursive(Handle),
|
||||
TypeEval(TypeEval),
|
||||
}
|
||||
|
||||
impl From<TypeEval> for Error {
|
||||
fn from(value: TypeEval) -> Self {
|
||||
Error::TypeEval(value)
|
||||
}
|
||||
}
|
172
compiler/cl-typeck/src/entry.rs
Normal file
172
compiler/cl-typeck/src/entry.rs
Normal file
@ -0,0 +1,172 @@
|
||||
//! A [Handle] is an accessor for [Entries](Entry) in a [Table].
|
||||
//!
|
||||
//! There are two kinds of handle:
|
||||
//! - [Handle]: Provides getters for an entry's fields, and an implementation of
|
||||
//! [Display](std::fmt::Display)
|
||||
//! - [HandleMut]: Provides setters for an entry's fields, and an [`as_ref`](HandleMut::as_ref)
|
||||
//! method to demote to a [Handle].
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use cl_ast::{Meta, PathPart, Sym};
|
||||
use cl_structures::span::Span;
|
||||
|
||||
use crate::{
|
||||
handle::Handle,
|
||||
source::Source,
|
||||
table::{NodeKind, Table},
|
||||
type_kind::TypeKind,
|
||||
};
|
||||
|
||||
pub mod display;
|
||||
|
||||
impl Handle {
|
||||
pub const fn to_entry<'t, 'a>(self, table: &'t Table<'a>) -> Entry<'t, 'a> {
|
||||
Entry { id: self, table }
|
||||
}
|
||||
pub fn to_entry_mut<'t, 'a>(self, table: &'t mut Table<'a>) -> EntryMut<'t, 'a> {
|
||||
EntryMut { id: self, table }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Entry<'t, 'a> {
|
||||
table: &'t Table<'a>,
|
||||
id: Handle,
|
||||
}
|
||||
|
||||
impl<'t, 'a> Entry<'t, 'a> {
|
||||
pub const fn new(table: &'t Table<'a>, id: Handle) -> Self {
|
||||
Self { table, id }
|
||||
}
|
||||
|
||||
pub const fn id(&self) -> Handle {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &Table<'a> {
|
||||
self.table
|
||||
}
|
||||
|
||||
pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
|
||||
Self { table: self.table, id }
|
||||
}
|
||||
|
||||
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
|
||||
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
|
||||
}
|
||||
|
||||
pub const fn root(&self) -> Handle {
|
||||
self.table.root()
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Option<&NodeKind> {
|
||||
self.table.kind(self.id)
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&Handle> {
|
||||
self.table.parent(self.id)
|
||||
}
|
||||
|
||||
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
|
||||
self.table.children(self.id)
|
||||
}
|
||||
|
||||
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> {
|
||||
self.table.imports(self.id)
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Option<&TypeKind> {
|
||||
self.table.ty(self.id)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<&Span> {
|
||||
self.table.span(self.id)
|
||||
}
|
||||
|
||||
pub fn meta(&self) -> Option<&'a [Meta]> {
|
||||
self.table.meta(self.id)
|
||||
}
|
||||
|
||||
pub fn source(&self) -> Option<&Source<'a>> {
|
||||
self.table.source(self.id)
|
||||
}
|
||||
|
||||
pub fn impl_target(&self) -> Option<Handle> {
|
||||
self.table.impl_target(self.id)
|
||||
}
|
||||
|
||||
pub fn selfty(&self) -> Option<Handle> {
|
||||
self.table.selfty(self.id)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<Sym> {
|
||||
self.table.name(self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EntryMut<'t, 'a> {
|
||||
table: &'t mut Table<'a>,
|
||||
id: Handle,
|
||||
}
|
||||
|
||||
impl<'t, 'a> EntryMut<'t, 'a> {
|
||||
pub fn new(table: &'t mut Table<'a>, id: Handle) -> Self {
|
||||
Self { table, id }
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> Entry<'_, 'a> {
|
||||
Entry { table: self.table, id: self.id }
|
||||
}
|
||||
|
||||
pub const fn id(&self) -> Handle {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Constructs a new Handle with the provided parent [DefID]
|
||||
pub fn with_id(&mut self, parent: Handle) -> EntryMut<'_, 'a> {
|
||||
EntryMut { table: self.table, id: parent }
|
||||
}
|
||||
|
||||
pub fn nav(&mut self, path: &[PathPart]) -> Option<EntryMut<'_, 'a>> {
|
||||
Some(EntryMut { id: self.table.nav(self.id, path)?, table: self.table })
|
||||
}
|
||||
|
||||
pub fn new_entry(&mut self, kind: NodeKind) -> EntryMut<'_, 'a> {
|
||||
let id = self.table.new_entry(self.id, kind);
|
||||
self.with_id(id)
|
||||
}
|
||||
|
||||
pub fn add_child(&mut self, name: Sym, child: Handle) -> Option<Handle> {
|
||||
self.table.add_child(self.id, name, child)
|
||||
}
|
||||
|
||||
pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> {
|
||||
self.table.set_ty(self.id, kind)
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) -> Option<Span> {
|
||||
self.table.set_span(self.id, span)
|
||||
}
|
||||
|
||||
pub fn set_meta(&mut self, meta: &'a [Meta]) -> Option<&'a [Meta]> {
|
||||
self.table.set_meta(self.id, meta)
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, source: Source<'a>) -> Option<Source<'a>> {
|
||||
self.table.set_source(self.id, source)
|
||||
}
|
||||
|
||||
pub fn set_impl_target(&mut self, target: Handle) -> Option<Handle> {
|
||||
self.table.set_impl_target(self.id, target)
|
||||
}
|
||||
|
||||
pub fn mark_use_item(&mut self) {
|
||||
self.table.mark_use_item(self.id)
|
||||
}
|
||||
|
||||
pub fn mark_impl_item(&mut self) {
|
||||
self.table.mark_impl_item(self.id)
|
||||
}
|
||||
}
|
109
compiler/cl-typeck/src/entry/display.rs
Normal file
109
compiler/cl-typeck/src/entry/display.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use super::*;
|
||||
use crate::{format_utils::*, type_kind::Adt};
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
/// Printing the name of a named type stops infinite recursion
|
||||
fn write_name_or(h: Entry, f: &mut impl Write) -> fmt::Result {
|
||||
match h.name() {
|
||||
Some(name) => write!(f, "{name}"),
|
||||
None => write!(f, "{h}"),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Entry<'_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.kind().is_none() {
|
||||
return write!(f, "<invalid type: {}>", self.id);
|
||||
}
|
||||
|
||||
if let Some(ty) = self.ty() {
|
||||
match ty {
|
||||
TypeKind::Alias(id) => write!(f, "= {}", self.with_id(*id))?,
|
||||
TypeKind::Intrinsic(kind) => write!(f, "{kind}")?,
|
||||
TypeKind::Adt(adt) => write_adt(adt, self, f)?,
|
||||
&TypeKind::Ref(cnt, id) => {
|
||||
for _ in 0..cnt {
|
||||
f.write_str("&")?;
|
||||
}
|
||||
let h_id = self.with_id(id);
|
||||
write_name_or(h_id, f)?;
|
||||
}
|
||||
TypeKind::Slice(id) => {
|
||||
write_name_or(self.with_id(*id), &mut f.delimit_with("[", "]"))?;
|
||||
}
|
||||
&TypeKind::Array(t, cnt) => {
|
||||
let mut f = f.delimit_with("[", "]");
|
||||
write_name_or(self.with_id(t), &mut f)?;
|
||||
write!(f, "; {cnt}")?;
|
||||
}
|
||||
TypeKind::Tuple(ids) => {
|
||||
let mut f = f.delimit_with("(", ")");
|
||||
for (index, &id) in ids.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write_name_or(self.with_id(id), &mut f)?;
|
||||
}
|
||||
}
|
||||
TypeKind::FnSig { args, rety } => {
|
||||
write!(f, "fn {} -> ", self.with_id(*args))?;
|
||||
write_name_or(self.with_id(*rety), f)?;
|
||||
}
|
||||
TypeKind::Empty => write!(f, "()")?,
|
||||
TypeKind::Never => write!(f, "!")?,
|
||||
TypeKind::Module => write!(f, "module?")?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result {
|
||||
match adt {
|
||||
Adt::Enum(variants) => {
|
||||
let mut variants = variants.iter();
|
||||
separate(",", || {
|
||||
variants.next().map(|(name, def)| {
|
||||
move |f: &mut Delimit<_>| match def {
|
||||
Some(def) => {
|
||||
write!(f, "\n{name}: ")?;
|
||||
write_name_or(h.with_id(*def), f)
|
||||
}
|
||||
None => write!(f, "\n{name}"),
|
||||
}
|
||||
})
|
||||
})(f.delimit_with("enum {", "\n}"))
|
||||
}
|
||||
Adt::CLikeEnum(variants) => {
|
||||
let mut variants = variants.iter();
|
||||
separate(",", || {
|
||||
let (name, descrim) = variants.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}"))
|
||||
})(f.delimit_with("enum {", "\n}"))
|
||||
}
|
||||
Adt::FieldlessEnum => write!(f, "enum"),
|
||||
Adt::Struct(members) => {
|
||||
let mut members = members.iter();
|
||||
separate(",", || {
|
||||
let (name, vis, id) = members.next()?;
|
||||
Some(move |f: &mut Delimit<_>| {
|
||||
write!(f, "\n{vis}{name}: ")?;
|
||||
write_name_or(h.with_id(*id), f)
|
||||
})
|
||||
})(f.delimit_with("struct {", "\n}"))
|
||||
}
|
||||
Adt::TupleStruct(members) => {
|
||||
let mut members = members.iter();
|
||||
separate(", ", || {
|
||||
let (vis, def) = members.next()?;
|
||||
Some(move |f: &mut Delimit<_>| {
|
||||
write!(f, "{vis}")?;
|
||||
write_name_or(h.with_id(*def), f)
|
||||
})
|
||||
})(f.delimit_with("struct (", ")"))
|
||||
}
|
||||
Adt::UnitStruct => write!(f, "struct"),
|
||||
Adt::Union(_) => todo!("Display union types"),
|
||||
}
|
||||
}
|
@ -1,217 +1,13 @@
|
||||
use crate::{definition::Def, path::Path, project::Project};
|
||||
use cl_structures::index_map::*;
|
||||
|
||||
// define the index types
|
||||
make_index! {
|
||||
/// Uniquely represents a [Def][1] in the [Def][1] [Pool]
|
||||
///
|
||||
/// [1]: crate::definition::Def
|
||||
DefID,
|
||||
/// Uniquely represents an entry in the [item context](crate::sym_table)
|
||||
Handle,
|
||||
}
|
||||
|
||||
/// A handle to a certain [Def] within a [Project]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Handle<'prj, 'code> {
|
||||
id: DefID,
|
||||
prj: &'prj Project<'code>,
|
||||
}
|
||||
|
||||
impl DefID {
|
||||
/// Constructs a new [Handle] from this DefID and the provided [Project].
|
||||
pub fn handle<'p, 'c>(self, prj: &'p Project<'c>) -> Option<Handle<'p, 'c>> {
|
||||
Handle::new(self, prj)
|
||||
}
|
||||
|
||||
/// Constructs a new [Handle] from this DefID and the provided [Project]
|
||||
pub fn handle_unchecked<'p, 'c>(self, prj: &'p Project<'c>) -> Handle<'p, 'c> {
|
||||
Handle::new_unchecked(self, prj)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'c> Handle<'p, 'c> {
|
||||
/// Constructs a new Handle from the provided [DefID] and [Project].
|
||||
/// Returns [Some]\(Handle) if the [DefID] exists in the [Project].
|
||||
pub fn new(id: DefID, prj: &'p Project<'c>) -> Option<Self> {
|
||||
prj.pool.get(id).is_some().then_some(Self { id, prj })
|
||||
}
|
||||
|
||||
/// Constructs a new Handle from the provided [DefID] and [Project] without checking membership.
|
||||
/// Using the handle may cause panics or other unwanted (but defined) behavior.
|
||||
pub fn new_unchecked(id: DefID, prj: &'p Project<'c>) -> Self {
|
||||
Self { id, prj }
|
||||
}
|
||||
|
||||
/// Gets the [Def] this handle points to.
|
||||
pub fn get(self) -> Option<&'p Def<'c>> {
|
||||
self.prj.pool.get(self.id)
|
||||
}
|
||||
|
||||
pub fn navigate(self, path: Path) -> (Option<Self>, Option<Self>) {
|
||||
match self.prj.get(path, self.id) {
|
||||
Some((Some(ty), Some(vl), _)) => (Some(self.with(ty)), Some(self.with(vl))),
|
||||
Some((_, Some(vl), _)) => (None, Some(self.with(vl))),
|
||||
Some((Some(ty), _, _)) => (Some(self.with(ty)), None),
|
||||
_ => (None, None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the [Project] this handle points to.
|
||||
pub fn project(self) -> &'p Project<'c> {
|
||||
self.prj
|
||||
}
|
||||
|
||||
pub fn id(self) -> DefID {
|
||||
self.id
|
||||
}
|
||||
|
||||
// TODO: get parent, children, etc.
|
||||
|
||||
/// Gets a handle to the other ID within the same project
|
||||
pub fn with(self, id: DefID) -> Self {
|
||||
Self { id, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
mod emulate_def {
|
||||
use super::*;
|
||||
use cl_ast::{Sym, Visibility};
|
||||
use cl_structures::span::Span;
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl<'p, 'c> Handle<'p, 'c> {
|
||||
pub fn name(self) -> Option<Sym> {
|
||||
self.get()?.name()
|
||||
}
|
||||
pub fn vis(self) -> Option<Visibility> {
|
||||
Some(self.get()?.node.vis)
|
||||
}
|
||||
pub fn span(self) -> Option<Span> {
|
||||
Some(*self.get()?.node.span)
|
||||
}
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
self.get()?.module.parent.map(|id| self.with(id))
|
||||
}
|
||||
pub fn types(self) -> Option<&'p HashMap<Sym, DefID>> {
|
||||
let types = &self.get()?.module.types;
|
||||
(!types.is_empty()).then_some(types)
|
||||
}
|
||||
pub fn values(self) -> Option<&'p HashMap<Sym, DefID>> {
|
||||
let values = &self.get()?.module.values;
|
||||
(!values.is_empty()).then_some(values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod display {
|
||||
use super::*;
|
||||
use crate::{definition::*, format_utils::*};
|
||||
use std::fmt::{self, Display, Write};
|
||||
|
||||
impl Display for DefID {
|
||||
impl std::fmt::Display for Handle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Handle<'_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { id, prj } = *self;
|
||||
let Some(def) = prj.pool.get(id) else {
|
||||
return write!(f, "<invalid type: {id}>");
|
||||
};
|
||||
|
||||
// Match the type
|
||||
match &def.kind {
|
||||
DefKind::Undecided => write!(f, "<undecided>"),
|
||||
DefKind::Impl(id) => write!(f, "impl {}", self.with(*id)),
|
||||
DefKind::Use(id) => write!(f, "use inside {}", self.with(*id)),
|
||||
DefKind::Type(kind) => match kind {
|
||||
TypeKind::Alias(None) => write!(f, "type"),
|
||||
TypeKind::Alias(Some(id)) => write!(f, "{}", self.with(*id)),
|
||||
TypeKind::Intrinsic(intrinsic) => write!(f, "{intrinsic}"),
|
||||
TypeKind::Adt(adt) => display_adt(self, adt, f),
|
||||
TypeKind::Ref(count, id) => {
|
||||
for _ in 0..*count {
|
||||
f.write_char('&')?;
|
||||
}
|
||||
self.with(*id).fmt(f)
|
||||
}
|
||||
TypeKind::Array(id, size) => {
|
||||
write!(f.delimit_with("[", "]"), "{}; {size}", self.with(*id))
|
||||
}
|
||||
TypeKind::Slice(id) => write!(f.delimit_with("[", "]"), "{}", self.with(*id)),
|
||||
TypeKind::Tuple(ids) => {
|
||||
let mut ids = ids.iter();
|
||||
separate(", ", || {
|
||||
let id = ids.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "{}", self.with(*id)))
|
||||
})(f.delimit_with("(", ")"))
|
||||
}
|
||||
TypeKind::FnSig { args, rety } => {
|
||||
write!(f, "fn {} -> {}", self.with(*args), self.with(*rety))
|
||||
}
|
||||
TypeKind::Empty => write!(f, "()"),
|
||||
TypeKind::Never => write!(f, "!"),
|
||||
TypeKind::Module => match def.name() {
|
||||
Some(name) => write!(f, "mod {name}"),
|
||||
None => write!(f, "mod"),
|
||||
},
|
||||
},
|
||||
|
||||
DefKind::Value(kind) => match kind {
|
||||
ValueKind::Const(id) => write!(f, "const {}", self.with(*id)),
|
||||
ValueKind::Static(id) => write!(f, "static {}", self.with(*id)),
|
||||
ValueKind::Local(id) => write!(f, "local {}", self.with(*id)),
|
||||
ValueKind::Fn(id) => write!(f, "{}", self.with(*id)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats an ADT: a continuation of [Handle::fmt]
|
||||
fn display_adt(handle: &Handle, adt: &Adt, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match adt {
|
||||
Adt::Enum(variants) => {
|
||||
let mut variants = variants.iter();
|
||||
separate(",", || {
|
||||
variants.next().map(|(name, def)| {
|
||||
move |f: &mut Delimit<_>| match def {
|
||||
Some(def) => write!(f, "\n{name}: {}", handle.with(*def)),
|
||||
None => write!(f, "\n{name}"),
|
||||
}
|
||||
})
|
||||
})(f.delimit_with("enum {", "\n}"))
|
||||
}
|
||||
Adt::CLikeEnum(variants) => {
|
||||
let mut variants = variants.iter();
|
||||
separate(",", || {
|
||||
let (name, descrim) = variants.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}"))
|
||||
})(f.delimit_with("enum {", "\n}"))
|
||||
}
|
||||
Adt::FieldlessEnum => write!(f, "enum"),
|
||||
Adt::Struct(members) => {
|
||||
let mut members = members.iter();
|
||||
separate(",", || {
|
||||
let (name, vis, id) = members.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "\n{vis}{name}: {}", handle.with(*id)))
|
||||
})(f.delimit_with("struct {", "\n}"))
|
||||
}
|
||||
Adt::TupleStruct(members) => {
|
||||
let mut members = members.iter();
|
||||
separate(", ", || {
|
||||
let (vis, def) = members.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", handle.with(*def)))
|
||||
})(f.delimit_with("struct (", ")"))
|
||||
}
|
||||
Adt::UnitStruct => write!(f, "struct;"),
|
||||
Adt::Union(variants) => {
|
||||
let mut variants = variants.iter();
|
||||
separate(",", || {
|
||||
let (name, def) = variants.next()?;
|
||||
Some(move |f: &mut Delimit<_>| write!(f, "\n{name}: {}", handle.with(*def)))
|
||||
})(f.delimit_with("union {", "\n}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
112
compiler/cl-typeck/src/import.rs
Normal file
112
compiler/cl-typeck/src/import.rs
Normal file
@ -0,0 +1,112 @@
|
||||
//! An algorithm for importing external nodes
|
||||
|
||||
use crate::{
|
||||
handle::Handle,
|
||||
source::Source,
|
||||
table::{NodeKind, Table},
|
||||
};
|
||||
use cl_ast::{PathPart, Sym, Use, UseTree};
|
||||
use core::slice;
|
||||
use std::mem;
|
||||
|
||||
pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> {
|
||||
let pending = mem::take(&mut table.uses);
|
||||
let mut failed = vec![];
|
||||
for import in pending {
|
||||
if let Err(e) = import_one(table, import) {
|
||||
failed.push((import, e));
|
||||
}
|
||||
}
|
||||
failed
|
||||
}
|
||||
|
||||
fn import_one<'a>(table: &mut Table<'a>, item: Handle) -> UseResult<'a, ()> {
|
||||
let Some(NodeKind::Use) = table.kind(item) else {
|
||||
Err(Error::ItsNoUse)?
|
||||
};
|
||||
let Some(&dst) = table.parent(item) else {
|
||||
Err(Error::NoParents)?
|
||||
};
|
||||
let Some(code) = table.source(item) else {
|
||||
Err(Error::NoSource)?
|
||||
};
|
||||
let &Source::Use(tree) = code else {
|
||||
Err(Error::BadSource(*code))?
|
||||
};
|
||||
let Use { absolute, tree } = tree;
|
||||
|
||||
eprintln!("Resolving import {item}: {code}");
|
||||
import_tree(table, if !absolute { dst } else { table.root() }, dst, tree)
|
||||
}
|
||||
|
||||
fn import_tree<'a>(
|
||||
table: &mut Table<'a>,
|
||||
src: Handle,
|
||||
dst: Handle,
|
||||
tree: &UseTree,
|
||||
) -> UseResult<'a, ()> {
|
||||
match tree {
|
||||
UseTree::Tree(trees) => {
|
||||
for tree in trees {
|
||||
import_tree(table, src, dst, tree)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
UseTree::Path(part, rest) => {
|
||||
let source = table
|
||||
.nav(src, slice::from_ref(part))
|
||||
.ok_or_else(|| Error::NotFound(src, part.clone()))?;
|
||||
import_tree(table, source, dst, rest)
|
||||
}
|
||||
UseTree::Alias(src_name, dst_name) => import_name(table, src, src_name, dst, dst_name),
|
||||
UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name),
|
||||
UseTree::Glob => import_glob(table, src, dst),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_glob<'a>(table: &mut Table<'a>, src: Handle, dst: Handle) -> UseResult<'a, ()> {
|
||||
let Table { children, imports, .. } = table;
|
||||
if let Some(children) = children.get(&src) {
|
||||
// TODO: check for new imports clobbering existing imports
|
||||
imports.entry(dst).or_default().extend(children);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import_name<'a>(
|
||||
table: &mut Table<'a>,
|
||||
src: Handle,
|
||||
src_name: &Sym,
|
||||
dst: Handle,
|
||||
dst_name: &Sym,
|
||||
) -> UseResult<'a, ()> {
|
||||
match table.get_by_sym(src, src_name) {
|
||||
// TODO: check for new imports clobbering existing imports
|
||||
Some(src_id) => table.add_import(dst, *dst_name, src_id),
|
||||
None => Err(Error::NotFound(src, PathPart::Ident(*src_name)))?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub type UseResult<'a, T> = Result<T, Error<'a>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<'a> {
|
||||
ItsNoUse,
|
||||
NoParents,
|
||||
NoSource,
|
||||
BadSource(Source<'a>),
|
||||
NotFound(Handle, PathPart),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::ItsNoUse => write!(f, "Entry is not use"),
|
||||
Error::NoParents => write!(f, "Entry has no parents"),
|
||||
Error::NoSource => write!(f, "Entry has no source"),
|
||||
Error::BadSource(s) => write!(f, "Entry incorrectly marked as use item: {s}"),
|
||||
Error::NotFound(id, part) => write!(f, "Could not traverse {id}::{part}"),
|
||||
}
|
||||
}
|
||||
}
|
@ -67,28 +67,28 @@ Value: Either
|
||||
|
||||
*/
|
||||
|
||||
pub mod handle;
|
||||
|
||||
pub mod node;
|
||||
|
||||
pub mod definition;
|
||||
|
||||
pub mod module;
|
||||
|
||||
pub mod path;
|
||||
|
||||
pub mod project;
|
||||
|
||||
pub mod name_collector;
|
||||
|
||||
pub mod use_importer;
|
||||
|
||||
pub mod type_resolver;
|
||||
|
||||
pub mod inference;
|
||||
|
||||
pub(crate) mod format_utils;
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub mod handle;
|
||||
|
||||
pub mod entry;
|
||||
|
||||
pub mod source;
|
||||
|
||||
pub mod type_kind;
|
||||
|
||||
pub mod type_expression;
|
||||
|
||||
pub mod populate;
|
||||
|
||||
pub mod import;
|
||||
|
||||
pub mod categorize;
|
||||
|
||||
/*
|
||||
|
||||
LET THERE BE NOTES:
|
||||
|
@ -1,71 +0,0 @@
|
||||
//! A [Module] is a node in the Module Tree (a component of a
|
||||
//! [Project](crate::project::Project))
|
||||
use cl_ast::Sym;
|
||||
use cl_structures::index_map::MapIndex;
|
||||
|
||||
use crate::handle::DefID;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A [Module] is a node in the Module Tree (a component of a
|
||||
/// [Project](crate::project::Project)).
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Module {
|
||||
pub parent: Option<DefID>,
|
||||
pub types: HashMap<Sym, DefID>,
|
||||
pub values: HashMap<Sym, DefID>,
|
||||
pub imports: Vec<DefID>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(parent: DefID) -> Self {
|
||||
Self { parent: Some(parent), ..Default::default() }
|
||||
}
|
||||
pub fn with_optional_parent(parent: Option<DefID>) -> Self {
|
||||
Self { parent, ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn get(&self, name: Sym) -> (Option<DefID>, Option<DefID>) {
|
||||
(self.get_type(name), self.get_value(name))
|
||||
}
|
||||
pub fn get_type(&self, name: Sym) -> Option<DefID> {
|
||||
self.types.get(&name).copied()
|
||||
}
|
||||
pub fn get_value(&self, name: Sym) -> Option<DefID> {
|
||||
self.values.get(&name).copied()
|
||||
}
|
||||
|
||||
/// Inserts a type with the provided [name](str) and [id](DefID)
|
||||
pub fn insert_type(&mut self, name: Sym, id: DefID) -> Option<DefID> {
|
||||
self.types.insert(name, id)
|
||||
}
|
||||
|
||||
/// Inserts a value with the provided [name](str) and [id](DefID)
|
||||
pub fn insert_value(&mut self, name: Sym, id: DefID) -> Option<DefID> {
|
||||
self.values.insert(name, id)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Module {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let Self { parent, types, values, imports } = self;
|
||||
if let Some(parent) = parent {
|
||||
writeln!(f, "Parent: {}", parent.get())?;
|
||||
}
|
||||
for (name, table) in [("Types", types), ("Values", values)] {
|
||||
if table.is_empty() {
|
||||
continue;
|
||||
}
|
||||
writeln!(f, "{name}:")?;
|
||||
for (name, id) in table.iter() {
|
||||
writeln!(f, " {name} => {id}")?;
|
||||
}
|
||||
}
|
||||
if !imports.is_empty() {
|
||||
write!(f, "Imports:")?;
|
||||
for id in imports {
|
||||
write!(f, "{id},")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
|
||||
use crate::{
|
||||
definition::{Def, DefKind},
|
||||
handle::DefID,
|
||||
module::Module as Mod,
|
||||
node::{Node, NodeSource},
|
||||
project::Project as Prj,
|
||||
};
|
||||
use cl_ast::{ast_visitor::Visit, *};
|
||||
use std::mem;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NameCollector<'prj, 'a> {
|
||||
path: cl_ast::Path,
|
||||
prj: &'prj mut Prj<'a>,
|
||||
parent: DefID,
|
||||
retval: Option<DefID>,
|
||||
}
|
||||
|
||||
impl<'prj, 'a> NameCollector<'prj, 'a> {
|
||||
/// Constructs a new [NameCollector] out of a [Project](Prj)
|
||||
pub fn new(prj: &'prj mut Prj<'a>) -> Self {
|
||||
Self { parent: prj.root, prj, path: Default::default(), retval: None }
|
||||
}
|
||||
/// Constructs a new [NameCollector] out of a [Project](Prj) and a parent [DefID]
|
||||
pub fn with_root(prj: &'prj mut Prj<'a>, parent: DefID) -> Self {
|
||||
Self { prj, parent, path: Default::default(), retval: None }
|
||||
}
|
||||
/// Runs the provided function with the given parent
|
||||
pub fn with_parent<F, N>(&mut self, parent: DefID, node: N, f: F)
|
||||
where F: FnOnce(&mut Self, N) {
|
||||
let parent = mem::replace(&mut self.parent, parent);
|
||||
f(self, node);
|
||||
self.parent = parent;
|
||||
}
|
||||
/// Extracts the return value from the provided function
|
||||
pub fn returns<F, N>(&mut self, node: N, f: F) -> Option<DefID>
|
||||
where F: FnOnce(&mut Self, N) {
|
||||
let out = self.retval.take();
|
||||
f(self, node);
|
||||
mem::replace(&mut self.retval, out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'prj, 'a> Visit<'a> for NameCollector<'prj, 'a> {
|
||||
fn visit_item(&mut self, i: &'a Item) {
|
||||
let Item { extents: _, attrs, vis, kind } = i;
|
||||
if let Some(def) = self.returns(kind, Self::visit_item_kind) {
|
||||
self.prj[def].set_meta(&attrs.meta).set_vis(*vis);
|
||||
}
|
||||
}
|
||||
fn visit_module(&mut self, m: &'a Module) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Module { name, kind } = m;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Module(m))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_type(*name, id);
|
||||
self.path.push(PathPart::Ident(*name));
|
||||
self.with_parent(id, kind, Self::visit_module_kind);
|
||||
self.path.pop();
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_alias(&mut self, a: &'a Alias) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Alias { to: name, from: _ } = a;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Alias(a))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_type(*name, id);
|
||||
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_enum(&mut self, e: &'a Enum) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Enum { name, kind } = e;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Enum(e))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_type(*name, id);
|
||||
|
||||
self.with_parent(id, kind, Self::visit_enum_kind);
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_variant(&mut self, v: &'a Variant) {
|
||||
let Self { path, prj, parent, retval: _ } = self;
|
||||
let Variant { name, kind } = v;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Variant(v))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_type(*name, id);
|
||||
|
||||
self.with_parent(id, kind, Self::visit_variant_kind);
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_struct(&mut self, s: &'a Struct) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Struct { name, kind } = s;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Struct(s))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_type(*name, id);
|
||||
|
||||
self.with_parent(id, kind, Self::visit_struct_kind);
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_const(&mut self, c: &'a Const) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Const { name, ty: _, init } = c;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Const(c))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_value(*name, id);
|
||||
|
||||
self.with_parent(id, &**init, Self::visit_expr);
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_static(&mut self, s: &'a Static) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Static { name, mutable: _, ty: _, init } = s;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Static(s))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_value(*name, id);
|
||||
|
||||
self.with_parent(id, &**init, Self::visit_expr);
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_function(&mut self, f: &'a Function) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Function { name, body, .. } = f;
|
||||
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Function(f))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_value(*name, id);
|
||||
|
||||
if let Some(body) = body {
|
||||
self.with_parent(id, body, Self::visit_block);
|
||||
}
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_impl(&mut self, i: &'a Impl) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Impl { target: _, body } = i;
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Impl(i))),
|
||||
};
|
||||
let id = prj.pool.insert(def);
|
||||
|
||||
// items will get reparented after name collection, when target is available
|
||||
self.with_parent(id, body, Self::visit_file);
|
||||
|
||||
self.retval = Some(id);
|
||||
}
|
||||
fn visit_use(&mut self, u: &'a Use) {
|
||||
let Self { prj, parent, path, retval } = self;
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Use(*parent),
|
||||
node: Node::new(path.clone(), Some(NodeSource::Use(u))),
|
||||
};
|
||||
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.imports.push(id);
|
||||
|
||||
*retval = Some(id);
|
||||
}
|
||||
fn visit_let(&mut self, l: &'a Let) {
|
||||
let Self { prj, parent, path, retval: _ } = self;
|
||||
let Let { name, init, .. } = l;
|
||||
let def = Def {
|
||||
module: Mod::new(*parent),
|
||||
kind: DefKind::Undecided,
|
||||
node: Node::new(path.clone(), Some(NodeSource::Local(l))),
|
||||
};
|
||||
|
||||
let id = prj.pool.insert(def);
|
||||
prj[*parent].module.insert_value(*name, id);
|
||||
if let Some(expr) = init {
|
||||
self.visit_expr(expr)
|
||||
}
|
||||
self.retval = Some(id)
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
//! A [Node] contains the [NodeSource] and [Item] metadata for any
|
||||
//! [Def](crate::definition::Def), as well as the [Path] of the
|
||||
//! containing [Module].
|
||||
//!
|
||||
//! [Node]s are collected by the [Node Sorcerer](sorcerer),
|
||||
//! an AST visitor that pairs [NodeSource]s with their surrounding
|
||||
//! context ([Path], [struct@Span], [Meta], [Visibility])
|
||||
|
||||
use cl_ast::ast::*;
|
||||
use cl_structures::span::Span;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Node<'a> {
|
||||
pub in_path: Path,
|
||||
pub span: &'a Span,
|
||||
pub meta: &'a [Meta],
|
||||
pub vis: Visibility,
|
||||
pub kind: Option<NodeSource<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node<'a> {
|
||||
pub fn new(path: Path, kind: Option<NodeSource<'a>>) -> Self {
|
||||
const DUMMY_SPAN: Span = Span::dummy();
|
||||
Self { in_path: path, span: &DUMMY_SPAN, meta: &[], vis: Visibility::Public, kind }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NodeSource<'a> {
|
||||
Root,
|
||||
Module(&'a Module),
|
||||
Alias(&'a Alias),
|
||||
Enum(&'a Enum),
|
||||
Variant(&'a Variant),
|
||||
Struct(&'a Struct),
|
||||
Const(&'a Const),
|
||||
Static(&'a Static),
|
||||
Function(&'a Function),
|
||||
Local(&'a Let),
|
||||
Impl(&'a Impl),
|
||||
Use(&'a Use),
|
||||
Ty(&'a TyKind),
|
||||
}
|
||||
|
||||
impl<'a> NodeSource<'a> {
|
||||
pub fn name(&self) -> Option<Sym> {
|
||||
match self {
|
||||
NodeSource::Root => None,
|
||||
NodeSource::Module(v) => Some(v.name),
|
||||
NodeSource::Alias(v) => Some(v.to),
|
||||
NodeSource::Enum(v) => Some(v.name),
|
||||
NodeSource::Variant(v) => Some(v.name),
|
||||
NodeSource::Struct(v) => Some(v.name),
|
||||
NodeSource::Const(v) => Some(v.name),
|
||||
NodeSource::Static(v) => Some(v.name),
|
||||
NodeSource::Function(v) => Some(v.name),
|
||||
NodeSource::Local(l) => Some(l.name),
|
||||
NodeSource::Impl(_) | NodeSource::Use(_) | NodeSource::Ty(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] defines a named value
|
||||
pub fn is_named_value(&self) -> bool {
|
||||
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] defines a named type
|
||||
pub fn is_named_type(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to a [Ty] with no name
|
||||
pub fn is_anon_type(&self) -> bool {
|
||||
matches!(self, Self::Ty(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to an [Impl] block
|
||||
pub fn is_impl(&self) -> bool {
|
||||
matches!(self, Self::Impl(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to a [Use] import
|
||||
pub fn is_use_import(&self) -> bool {
|
||||
matches!(self, Self::Use(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NodeSource<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Root => "🌳 root 🌳".fmt(f),
|
||||
Self::Module(arg0) => arg0.fmt(f),
|
||||
Self::Alias(arg0) => arg0.fmt(f),
|
||||
Self::Enum(arg0) => arg0.fmt(f),
|
||||
Self::Variant(arg0) => arg0.fmt(f),
|
||||
Self::Struct(arg0) => arg0.fmt(f),
|
||||
Self::Const(arg0) => arg0.fmt(f),
|
||||
Self::Static(arg0) => arg0.fmt(f),
|
||||
Self::Function(arg0) => arg0.fmt(f),
|
||||
Self::Impl(arg0) => arg0.fmt(f),
|
||||
Self::Use(arg0) => arg0.fmt(f),
|
||||
Self::Ty(arg0) => arg0.fmt(f),
|
||||
Self::Local(arg0) => arg0.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod sorcerer {
|
||||
//! An [AST](cl_ast) analysis pass that collects [Node] entries.
|
||||
|
||||
use super::{Node, NodeSource};
|
||||
use cl_ast::{ast::*, ast_visitor::visit::*};
|
||||
use cl_structures::span::Span;
|
||||
use std::mem;
|
||||
|
||||
/// An AST analysis pass that collects [Node]s
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NodeSorcerer<'a> {
|
||||
path: Path,
|
||||
parts: Parts<'a>,
|
||||
defs: Vec<Node<'a>>,
|
||||
}
|
||||
|
||||
type Parts<'a> = (&'a Span, &'a [Meta], Visibility);
|
||||
|
||||
impl<'a> NodeSorcerer<'a> {
|
||||
pub fn into_defs(self) -> Vec<Node<'a>> {
|
||||
self.defs
|
||||
}
|
||||
|
||||
fn with_parts<F>(&mut self, s: &'a Span, a: &'a [Meta], v: Visibility, f: F)
|
||||
where F: FnOnce(&mut Self) {
|
||||
let parts = mem::replace(&mut self.parts, (s, a, v));
|
||||
f(self);
|
||||
self.parts = parts;
|
||||
}
|
||||
|
||||
fn with_only_span<F>(&mut self, span: &'a Span, f: F)
|
||||
where F: FnOnce(&mut Self) {
|
||||
self.with_parts(span, &[], Visibility::Public, f)
|
||||
}
|
||||
|
||||
fn push(&mut self, kind: NodeSource<'a>) {
|
||||
let Self { path, parts, defs } = self;
|
||||
let (span, meta, vis) = *parts;
|
||||
|
||||
defs.push(Node { in_path: path.clone(), span, meta, vis, kind: Some(kind) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NodeSorcerer<'_> {
|
||||
fn default() -> Self {
|
||||
const DPARTS: Parts = (&Span::dummy(), &[], Visibility::Private);
|
||||
Self {
|
||||
path: Path { absolute: true, ..Default::default() },
|
||||
parts: DPARTS,
|
||||
defs: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for NodeSorcerer<'a> {
|
||||
fn visit_module(&mut self, m: &'a Module) {
|
||||
let Module { name, kind } = m;
|
||||
self.path.push(PathPart::Ident(*name));
|
||||
self.visit_module_kind(kind);
|
||||
self.path.pop();
|
||||
}
|
||||
fn visit_item(&mut self, i: &'a Item) {
|
||||
let Item { extents, attrs, vis, kind } = i;
|
||||
self.with_parts(extents, &attrs.meta, *vis, |v| {
|
||||
v.visit_item_kind(kind);
|
||||
});
|
||||
}
|
||||
fn visit_ty(&mut self, t: &'a Ty) {
|
||||
let Ty { extents, kind } = t;
|
||||
self.with_only_span(extents, |v| {
|
||||
v.push(NodeSource::Ty(kind));
|
||||
v.visit_ty_kind(kind);
|
||||
});
|
||||
}
|
||||
fn visit_stmt(&mut self, s: &'a Stmt) {
|
||||
let Stmt { extents, kind, semi } = s;
|
||||
self.with_only_span(extents, |d| {
|
||||
d.visit_stmt_kind(kind);
|
||||
d.visit_semi(semi);
|
||||
})
|
||||
}
|
||||
fn visit_item_kind(&mut self, kind: &'a ItemKind) {
|
||||
match kind {
|
||||
ItemKind::Module(i) => self.push(NodeSource::Module(i)),
|
||||
ItemKind::Alias(i) => self.push(NodeSource::Alias(i)),
|
||||
ItemKind::Enum(i) => self.push(NodeSource::Enum(i)),
|
||||
ItemKind::Struct(i) => self.push(NodeSource::Struct(i)),
|
||||
ItemKind::Const(i) => self.push(NodeSource::Const(i)),
|
||||
ItemKind::Static(i) => self.push(NodeSource::Static(i)),
|
||||
ItemKind::Function(i) => self.push(NodeSource::Function(i)),
|
||||
ItemKind::Impl(i) => self.push(NodeSource::Impl(i)),
|
||||
ItemKind::Use(i) => self.push(NodeSource::Use(i)),
|
||||
}
|
||||
or_visit_item_kind(self, kind);
|
||||
}
|
||||
fn visit_stmt_kind(&mut self, kind: &'a StmtKind) {
|
||||
if let StmtKind::Local(l) = kind {
|
||||
self.push(NodeSource::Local(l))
|
||||
}
|
||||
or_visit_stmt_kind(self, kind);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
//! A [Path] is a borrowed view of an [AST Path](AstPath)
|
||||
use cl_ast::{Path as AstPath, PathPart};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Path<'p> {
|
||||
pub absolute: bool,
|
||||
pub parts: &'p [PathPart],
|
||||
}
|
||||
|
||||
impl<'p> Path<'p> {
|
||||
pub fn new(path: &'p AstPath) -> Self {
|
||||
let AstPath { absolute, parts } = path;
|
||||
Self { absolute: *absolute, parts }
|
||||
}
|
||||
pub fn relative(self) -> Self {
|
||||
Self { absolute: false, ..self }
|
||||
}
|
||||
pub fn pop_front(self) -> Option<Self> {
|
||||
let Self { absolute, parts } = self;
|
||||
Some(Self { absolute, parts: parts.get(1..)? })
|
||||
}
|
||||
pub fn front(self) -> Option<Self> {
|
||||
let Self { absolute, parts } = self;
|
||||
Some(Self { absolute, parts: parts.get(..1)? })
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.parts.is_empty()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.parts.len()
|
||||
}
|
||||
pub fn first(&self) -> Option<&PathPart> {
|
||||
self.parts.first()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> From<&'p AstPath> for Path<'p> {
|
||||
fn from(value: &'p AstPath) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
impl AsRef<[PathPart]> for Path<'_> {
|
||||
fn as_ref(&self) -> &[PathPart] {
|
||||
self.parts
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Path<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
const SEPARATOR: &str = "::";
|
||||
let Self { absolute, parts } = self;
|
||||
if *absolute {
|
||||
write!(f, "{SEPARATOR}")?
|
||||
}
|
||||
for (idx, part) in parts.iter().enumerate() {
|
||||
write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
179
compiler/cl-typeck/src/populate.rs
Normal file
179
compiler/cl-typeck/src/populate.rs
Normal file
@ -0,0 +1,179 @@
|
||||
//! The [Populator] populates entries in the sym table, including span info
|
||||
use crate::{
|
||||
entry::EntryMut,
|
||||
handle::Handle,
|
||||
source::Source,
|
||||
table::{NodeKind, Table},
|
||||
};
|
||||
use cl_ast::{ast_visitor::Visit, ItemKind, Sym};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Populator<'t, 'a> {
|
||||
inner: EntryMut<'t, 'a>,
|
||||
name: Option<Sym>, // this is a hack to get around the Visitor interface
|
||||
}
|
||||
|
||||
impl<'t, 'a> Populator<'t, 'a> {
|
||||
pub fn new(table: &'t mut Table<'a>) -> Self {
|
||||
Self { inner: table.root_handle_mut(), name: None }
|
||||
}
|
||||
/// Constructs a new Populator with the provided parent DefID
|
||||
pub fn with_id(&mut self, parent: Handle) -> Populator<'_, 'a> {
|
||||
Populator { inner: self.inner.with_id(parent), name: None }
|
||||
}
|
||||
|
||||
pub fn new_entry(&mut self, kind: NodeKind) -> Populator<'_, 'a> {
|
||||
Populator { inner: self.inner.new_entry(kind), name: None }
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, name: Sym) {
|
||||
self.name = Some(name);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for Populator<'_, 'a> {
|
||||
fn visit_item(&mut self, i: &'a cl_ast::Item) {
|
||||
let cl_ast::Item { extents, attrs, vis, kind } = i;
|
||||
// TODO: this, better, better.
|
||||
let entry_kind = match kind {
|
||||
ItemKind::Alias(_) => NodeKind::Type,
|
||||
ItemKind::Enum(_) => NodeKind::Type,
|
||||
ItemKind::Struct(_) => NodeKind::Type,
|
||||
|
||||
ItemKind::Const(_) => NodeKind::Const,
|
||||
ItemKind::Static(_) => NodeKind::Static,
|
||||
ItemKind::Function(_) => NodeKind::Function,
|
||||
|
||||
ItemKind::Module(_) => NodeKind::Module,
|
||||
ItemKind::Impl(_) => NodeKind::Impl,
|
||||
ItemKind::Use(_) => NodeKind::Use,
|
||||
};
|
||||
|
||||
let mut entry = self.new_entry(entry_kind);
|
||||
entry.inner.set_span(*extents);
|
||||
entry.inner.set_meta(&attrs.meta);
|
||||
|
||||
entry.visit_span(extents);
|
||||
entry.visit_attrs(attrs);
|
||||
entry.visit_visibility(vis);
|
||||
entry.visit_item_kind(kind);
|
||||
|
||||
if let (Some(name), child) = (entry.name, entry.inner.id()) {
|
||||
self.inner.add_child(name, child);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_alias(&mut self, a: &'a cl_ast::Alias) {
|
||||
let cl_ast::Alias { to, from } = a;
|
||||
self.inner.set_source(Source::Alias(a));
|
||||
self.set_name(*to);
|
||||
|
||||
if let Some(t) = from {
|
||||
self.visit_ty(t)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: &'a cl_ast::Const) {
|
||||
let cl_ast::Const { name, ty, init } = c;
|
||||
self.inner.set_source(Source::Const(c));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_ty(ty);
|
||||
self.visit_expr(init);
|
||||
}
|
||||
|
||||
fn visit_static(&mut self, s: &'a cl_ast::Static) {
|
||||
let cl_ast::Static { mutable, name, ty, init } = s;
|
||||
self.inner.set_source(Source::Static(s));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_mutability(mutable);
|
||||
self.visit_ty(ty);
|
||||
self.visit_expr(init);
|
||||
}
|
||||
|
||||
fn visit_module(&mut self, m: &'a cl_ast::Module) {
|
||||
let cl_ast::Module { name, kind } = m;
|
||||
self.inner.set_source(Source::Module(m));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_module_kind(kind);
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, f: &'a cl_ast::Function) {
|
||||
let cl_ast::Function { name, sign, bind, body } = f;
|
||||
self.inner.set_source(Source::Function(f));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_ty_fn(sign);
|
||||
bind.iter().for_each(|p| self.visit_param(p));
|
||||
if let Some(b) = body {
|
||||
self.visit_block(b)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_struct(&mut self, s: &'a cl_ast::Struct) {
|
||||
let cl_ast::Struct { name, kind } = s;
|
||||
self.inner.set_source(Source::Struct(s));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_struct_kind(kind);
|
||||
}
|
||||
|
||||
fn visit_enum(&mut self, e: &'a cl_ast::Enum) {
|
||||
let cl_ast::Enum { name, kind } = e;
|
||||
self.inner.set_source(Source::Enum(e));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_enum_kind(kind);
|
||||
}
|
||||
|
||||
fn visit_impl(&mut self, i: &'a cl_ast::Impl) {
|
||||
let cl_ast::Impl { target, body } = i;
|
||||
self.inner.set_source(Source::Impl(i));
|
||||
self.inner.mark_impl_item();
|
||||
|
||||
self.visit_impl_kind(target);
|
||||
self.visit_file(body);
|
||||
}
|
||||
|
||||
fn visit_use(&mut self, u: &'a cl_ast::Use) {
|
||||
let cl_ast::Use { absolute: _, tree } = u;
|
||||
self.inner.set_source(Source::Use(u));
|
||||
self.inner.mark_use_item();
|
||||
|
||||
self.visit_use_tree(tree);
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'a cl_ast::Stmt) {
|
||||
let cl_ast::Stmt { extents, kind, semi } = s;
|
||||
let cl_ast::StmtKind::Local(local) = kind else {
|
||||
self.visit_span(extents);
|
||||
self.visit_stmt_kind(kind);
|
||||
self.visit_semi(semi);
|
||||
return;
|
||||
};
|
||||
|
||||
let mut entry = self.new_entry(NodeKind::Local);
|
||||
entry.inner.set_span(*extents);
|
||||
entry.visit_let(local);
|
||||
|
||||
if let (Some(name), child) = (entry.name, entry.inner.id()) {
|
||||
self.inner.add_child(name, child);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
||||
let cl_ast::Let { mutable, name, ty, init } = l;
|
||||
self.inner.set_source(Source::Local(l));
|
||||
self.set_name(*name);
|
||||
|
||||
self.visit_mutability(mutable);
|
||||
if let Some(ty) = ty {
|
||||
self.visit_ty(ty);
|
||||
}
|
||||
if let Some(init) = init {
|
||||
self.visit_expr(init)
|
||||
}
|
||||
}
|
||||
}
|
87
compiler/cl-typeck/src/source.rs
Normal file
87
compiler/cl-typeck/src/source.rs
Normal file
@ -0,0 +1,87 @@
|
||||
//! Holds the [Source] of a definition in the AST
|
||||
|
||||
use cl_ast::ast::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Source<'a> {
|
||||
Root,
|
||||
Module(&'a Module),
|
||||
Alias(&'a Alias),
|
||||
Enum(&'a Enum),
|
||||
Variant(&'a Variant),
|
||||
Struct(&'a Struct),
|
||||
Const(&'a Const),
|
||||
Static(&'a Static),
|
||||
Function(&'a Function),
|
||||
Local(&'a Let),
|
||||
Impl(&'a Impl),
|
||||
Use(&'a Use),
|
||||
Ty(&'a TyKind),
|
||||
}
|
||||
|
||||
impl<'a> Source<'a> {
|
||||
pub fn name(&self) -> Option<Sym> {
|
||||
match self {
|
||||
Source::Root => None,
|
||||
Source::Module(v) => Some(v.name),
|
||||
Source::Alias(v) => Some(v.to),
|
||||
Source::Enum(v) => Some(v.name),
|
||||
Source::Variant(v) => Some(v.name),
|
||||
Source::Struct(v) => Some(v.name),
|
||||
Source::Const(v) => Some(v.name),
|
||||
Source::Static(v) => Some(v.name),
|
||||
Source::Function(v) => Some(v.name),
|
||||
Source::Local(l) => Some(l.name),
|
||||
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] defines a named value
|
||||
pub fn is_named_value(&self) -> bool {
|
||||
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] defines a named type
|
||||
pub fn is_named_type(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to a [Ty] with no name
|
||||
pub fn is_anon_type(&self) -> bool {
|
||||
matches!(self, Self::Ty(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to an [Impl] block
|
||||
pub fn is_impl(&self) -> bool {
|
||||
matches!(self, Self::Impl(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this [NodeSource] refers to a [Use] import
|
||||
pub fn is_use_import(&self) -> bool {
|
||||
matches!(self, Self::Use(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Source<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Root => "🌳 root 🌳".fmt(f),
|
||||
Self::Module(arg0) => arg0.fmt(f),
|
||||
Self::Alias(arg0) => arg0.fmt(f),
|
||||
Self::Enum(arg0) => arg0.fmt(f),
|
||||
Self::Variant(arg0) => arg0.fmt(f),
|
||||
Self::Struct(arg0) => arg0.fmt(f),
|
||||
Self::Const(arg0) => arg0.fmt(f),
|
||||
Self::Static(arg0) => arg0.fmt(f),
|
||||
Self::Function(arg0) => arg0.fmt(f),
|
||||
Self::Impl(arg0) => arg0.fmt(f),
|
||||
Self::Use(arg0) => arg0.fmt(f),
|
||||
Self::Ty(arg0) => arg0.fmt(f),
|
||||
Self::Local(arg0) => arg0.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
269
compiler/cl-typeck/src/table.rs
Normal file
269
compiler/cl-typeck/src/table.rs
Normal file
@ -0,0 +1,269 @@
|
||||
//! Conlang's symbol table
|
||||
|
||||
use crate::{
|
||||
entry::{Entry, EntryMut},
|
||||
handle::Handle,
|
||||
source::Source,
|
||||
type_kind::TypeKind,
|
||||
};
|
||||
use cl_ast::{Meta, PathPart, Sym};
|
||||
use cl_structures::{index_map::IndexMap, span::Span};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO: Cycle detection external to this module
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Table<'a> {
|
||||
root: Handle,
|
||||
/// This is the source of truth for handles
|
||||
kinds: IndexMap<Handle, NodeKind>,
|
||||
parents: IndexMap<Handle, Handle>,
|
||||
pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>,
|
||||
pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>,
|
||||
types: HashMap<Handle, TypeKind>,
|
||||
spans: HashMap<Handle, Span>,
|
||||
metas: HashMap<Handle, &'a [Meta]>,
|
||||
sources: HashMap<Handle, Source<'a>>,
|
||||
// code: HashMap<DefID, BasicBlock>, // TODO: lower sources
|
||||
impl_targets: HashMap<Handle, Handle>,
|
||||
anon_types: HashMap<TypeKind, Handle>,
|
||||
pub(crate) impls: Vec<Handle>,
|
||||
pub(crate) uses: Vec<Handle>,
|
||||
}
|
||||
|
||||
impl<'a> Table<'a> {
|
||||
pub fn new() -> Self {
|
||||
let mut kinds = IndexMap::new();
|
||||
let mut parents = IndexMap::new();
|
||||
let root = kinds.insert(NodeKind::Root);
|
||||
assert_eq!(root, parents.insert(root));
|
||||
|
||||
Self {
|
||||
root,
|
||||
kinds,
|
||||
parents,
|
||||
children: HashMap::new(),
|
||||
imports: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
spans: HashMap::new(),
|
||||
metas: HashMap::new(),
|
||||
sources: HashMap::new(),
|
||||
impl_targets: HashMap::new(),
|
||||
anon_types: HashMap::new(),
|
||||
impls: Vec::new(),
|
||||
uses: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry(&self, handle: Handle) -> Entry<'_, 'a> {
|
||||
handle.to_entry(self)
|
||||
}
|
||||
|
||||
pub fn entry_mut(&mut self, handle: Handle) -> EntryMut<'_, 'a> {
|
||||
handle.to_entry_mut(self)
|
||||
}
|
||||
|
||||
pub fn new_entry(&mut self, parent: Handle, kind: NodeKind) -> Handle {
|
||||
let entry = self.kinds.insert(kind);
|
||||
assert_eq!(entry, self.parents.insert(parent));
|
||||
entry
|
||||
}
|
||||
|
||||
pub fn add_child(&mut self, parent: Handle, name: Sym, child: Handle) -> Option<Handle> {
|
||||
self.children.entry(parent).or_default().insert(name, child)
|
||||
}
|
||||
|
||||
pub fn add_import(&mut self, parent: Handle, name: Sym, import: Handle) -> Option<Handle> {
|
||||
self.imports.entry(parent).or_default().insert(name, import)
|
||||
}
|
||||
|
||||
pub fn mark_use_item(&mut self, item: Handle) {
|
||||
self.uses.push(item);
|
||||
}
|
||||
|
||||
pub fn mark_impl_item(&mut self, item: Handle) {
|
||||
self.impls.push(item);
|
||||
}
|
||||
|
||||
/// Returns handles to all nodes sequentially by [DefID]
|
||||
pub fn debug_handle_iter(&self) -> impl Iterator<Item = Entry<'_, 'a>> {
|
||||
self.kinds.keys().map(|key| key.to_entry(self))
|
||||
}
|
||||
|
||||
/// Gets the [DefID] of an anonymous type with the provided [TypeKind].
|
||||
/// If not already present, a new one is created.
|
||||
pub fn anon_type(&mut self, kind: TypeKind) -> Handle {
|
||||
if let Some(id) = self.anon_types.get(&kind) {
|
||||
return *id;
|
||||
}
|
||||
let entry = self.new_entry(self.root, NodeKind::Type);
|
||||
// Anonymous types require a bijective map (anon_types => Def => types)
|
||||
self.types.insert(entry, kind.clone());
|
||||
self.anon_types.insert(kind, entry);
|
||||
entry
|
||||
}
|
||||
|
||||
pub const fn root_handle(&self) -> Entry<'_, 'a> {
|
||||
self.root.to_entry(self)
|
||||
}
|
||||
|
||||
pub fn root_handle_mut(&mut self) -> crate::entry::EntryMut<'_, 'a> {
|
||||
self.root.to_entry_mut(self)
|
||||
}
|
||||
|
||||
// --- inherent properties ---
|
||||
|
||||
pub const fn root(&self) -> Handle {
|
||||
self.root
|
||||
}
|
||||
|
||||
pub fn kind(&self, node: Handle) -> Option<&NodeKind> {
|
||||
self.kinds.get(node)
|
||||
}
|
||||
|
||||
pub fn parent(&self, node: Handle) -> Option<&Handle> {
|
||||
self.parents.get(node)
|
||||
}
|
||||
|
||||
pub fn children(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
|
||||
self.children.get(&node)
|
||||
}
|
||||
|
||||
pub fn imports(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
|
||||
self.imports.get(&node)
|
||||
}
|
||||
|
||||
pub fn ty(&self, node: Handle) -> Option<&TypeKind> {
|
||||
self.types.get(&node)
|
||||
}
|
||||
|
||||
pub fn span(&self, node: Handle) -> Option<&Span> {
|
||||
self.spans.get(&node)
|
||||
}
|
||||
|
||||
pub fn meta(&self, node: Handle) -> Option<&'a [Meta]> {
|
||||
self.metas.get(&node).copied()
|
||||
}
|
||||
|
||||
pub fn source(&self, node: Handle) -> Option<&Source<'a>> {
|
||||
self.sources.get(&node)
|
||||
}
|
||||
|
||||
pub fn impl_target(&self, node: Handle) -> Option<Handle> {
|
||||
self.impl_targets.get(&node).copied()
|
||||
}
|
||||
|
||||
pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> {
|
||||
self.types.insert(node, kind)
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, node: Handle, span: Span) -> Option<Span> {
|
||||
self.spans.insert(node, span)
|
||||
}
|
||||
|
||||
pub fn set_meta(&mut self, node: Handle, meta: &'a [Meta]) -> Option<&'a [Meta]> {
|
||||
self.metas.insert(node, meta)
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, node: Handle, source: Source<'a>) -> Option<Source<'a>> {
|
||||
self.sources.insert(node, source)
|
||||
}
|
||||
|
||||
pub fn set_impl_target(&mut self, node: Handle, target: Handle) -> Option<Handle> {
|
||||
self.impl_targets.insert(node, target)
|
||||
}
|
||||
|
||||
// --- derived properties ---
|
||||
|
||||
/// Gets a handle to the local `Self` type, if one exists
|
||||
pub fn selfty(&self, node: Handle) -> Option<Handle> {
|
||||
match self.kinds.get(node)? {
|
||||
NodeKind::Root | NodeKind::Use => None,
|
||||
NodeKind::Type => Some(node),
|
||||
NodeKind::Impl => self.impl_target(node),
|
||||
_ => self.selfty(*self.parent(node)?),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self, node: Handle) -> Option<Sym> {
|
||||
self.source(node).and_then(|s| s.name())
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self, node: Handle) -> bool {
|
||||
!matches!(
|
||||
self.kind(node),
|
||||
None | Some(NodeKind::Root | NodeKind::Module)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_child(&self, node: Handle, name: &Sym) -> Option<Handle> {
|
||||
self.children.get(&node).and_then(|c| c.get(name)).copied()
|
||||
}
|
||||
|
||||
pub fn get_import(&self, node: Handle, name: &Sym) -> Option<Handle> {
|
||||
self.imports.get(&node).and_then(|i| i.get(name)).copied()
|
||||
}
|
||||
|
||||
pub fn get_by_sym(&self, node: Handle, name: &Sym) -> Option<Handle> {
|
||||
self.get_child(node, name)
|
||||
.or_else(|| self.get_import(node, name))
|
||||
.or_else(|| {
|
||||
self.is_transparent(node)
|
||||
.then(|| {
|
||||
self.parent(node)
|
||||
.and_then(|node| self.get_by_sym(*node, name))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
}
|
||||
|
||||
/// Does path traversal relative to the provided `node`.
|
||||
pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option<Handle> {
|
||||
match path {
|
||||
[PathPart::SuperKw, rest @ ..] => self.nav(*self.parent(node)?, rest),
|
||||
[PathPart::SelfKw, rest @ ..] => self.nav(node, rest),
|
||||
[PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest),
|
||||
[PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest),
|
||||
[] => Some(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for Table<'a> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum NodeKind {
|
||||
Root,
|
||||
Module,
|
||||
Type,
|
||||
Const,
|
||||
Static,
|
||||
Function,
|
||||
Local,
|
||||
Impl,
|
||||
Use,
|
||||
}
|
||||
|
||||
mod display {
|
||||
use super::*;
|
||||
use std::fmt;
|
||||
impl fmt::Display for NodeKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
NodeKind::Root => write!(f, "root"),
|
||||
NodeKind::Module => write!(f, "mod"),
|
||||
NodeKind::Type => write!(f, "type"),
|
||||
NodeKind::Const => write!(f, "const"),
|
||||
NodeKind::Static => write!(f, "static"),
|
||||
NodeKind::Function => write!(f, "fn"),
|
||||
NodeKind::Local => write!(f, "local"),
|
||||
NodeKind::Use => write!(f, "use"),
|
||||
NodeKind::Impl => write!(f, "impl"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
127
compiler/cl-typeck/src/type_expression.rs
Normal file
127
compiler/cl-typeck/src/type_expression.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! A [TypeExpression] is a [syntactic](cl_ast) representation of a [TypeKind], and is used to
|
||||
//! construct type bindings in a [Table]'s typing context.
|
||||
|
||||
use crate::{handle::Handle, table::Table, type_kind::TypeKind};
|
||||
use cl_ast::{PathPart, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)] // TODO: impl Display and Error
|
||||
pub enum Error {
|
||||
BadPath { parent: Handle, path: Vec<PathPart> },
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::BadPath { parent, path } => {
|
||||
write!(f, "No item at path {parent}")?;
|
||||
for part in path {
|
||||
write!(f, "::{part}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A [TypeExpression] is a syntactic representation of a [TypeKind], and is used to construct
|
||||
/// type bindings in a [Table]'s typing context.
|
||||
pub trait TypeExpression<Out = Handle> {
|
||||
/// Evaluates a type expression, recursively creating intermediate bindings.
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Out, Error>;
|
||||
}
|
||||
|
||||
impl TypeExpression for Ty {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
self.kind.evaluate(table, node)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TyKind {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
match self {
|
||||
TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
|
||||
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
|
||||
TyKind::Path(p) => p.evaluate(table, node),
|
||||
TyKind::Array(a) => a.evaluate(table, node),
|
||||
TyKind::Slice(s) => s.evaluate(table, node),
|
||||
TyKind::Tuple(t) => t.evaluate(table, node),
|
||||
TyKind::Ref(r) => r.evaluate(table, node),
|
||||
TyKind::Fn(f) => f.evaluate(table, node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for cl_ast::Path {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { absolute, parts } = self;
|
||||
parts.evaluate(table, if *absolute { table.root() } else { node })
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for [PathPart] {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
table
|
||||
.nav(node, self)
|
||||
.ok_or_else(|| Error::BadPath { parent: node, path: self.to_owned() })
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TyArray {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { ty, count } = self;
|
||||
let kind = TypeKind::Array(ty.evaluate(table, node)?, *count);
|
||||
Ok(table.anon_type(kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TySlice {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { ty } = self;
|
||||
let kind = TypeKind::Slice(ty.evaluate(table, node)?);
|
||||
Ok(table.anon_type(kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TyTuple {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { types } = self;
|
||||
let kind = match types.len() {
|
||||
0 => TypeKind::Empty,
|
||||
_ => TypeKind::Tuple(types.evaluate(table, node)?),
|
||||
};
|
||||
Ok(table.anon_type(kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TyRef {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { mutable: _, count, to } = self;
|
||||
let kind = TypeKind::Ref(*count, to.evaluate(table, node)?);
|
||||
Ok(table.anon_type(kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExpression for TyFn {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
|
||||
let Self { args, rety } = self;
|
||||
let kind = TypeKind::FnSig {
|
||||
args: args.evaluate(table, node)?,
|
||||
rety: match rety {
|
||||
Some(ty) => ty.evaluate(table, node)?,
|
||||
None => TyKind::Empty.evaluate(table, node)?,
|
||||
},
|
||||
};
|
||||
Ok(table.anon_type(kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeExpression<U>, U> TypeExpression<Vec<U>> for [T] {
|
||||
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Vec<U>, Error> {
|
||||
let mut out = Vec::with_capacity(self.len());
|
||||
for te in self {
|
||||
out.push(te.evaluate(table, node)?) // try_collect is unstable
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
}
|
@ -1,110 +1,34 @@
|
||||
use crate::{
|
||||
handle::DefID,
|
||||
module::Module,
|
||||
node::{Node, NodeSource},
|
||||
};
|
||||
use cl_ast::{Meta, Sym, Visibility};
|
||||
use crate::handle::Handle;
|
||||
use cl_ast::{Sym, Visibility};
|
||||
use std::{fmt::Debug, str::FromStr};
|
||||
|
||||
mod display;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Def<'a> {
|
||||
pub node: Node<'a>,
|
||||
pub kind: DefKind,
|
||||
pub module: Module,
|
||||
pub enum EntryKind {
|
||||
Variable(Option<Handle>),
|
||||
Operator(TypeKind),
|
||||
}
|
||||
|
||||
impl<'a> Def<'a> {
|
||||
pub fn with_node(node: Node<'a>) -> Self {
|
||||
Self { node, kind: DefKind::Undecided, module: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Def<'_> {
|
||||
pub fn name(&self) -> Option<Sym> {
|
||||
match self.node.kind {
|
||||
Some(source) => source.name(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
!matches!(self.kind, DefKind::Type(_))
|
||||
}
|
||||
}
|
||||
|
||||
mod builder_functions {
|
||||
use super::*;
|
||||
|
||||
impl<'a> Def<'a> {
|
||||
pub fn set_vis(&mut self, vis: Visibility) -> &mut Self {
|
||||
self.node.vis = vis;
|
||||
self
|
||||
}
|
||||
pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self {
|
||||
self.node.meta = meta;
|
||||
self
|
||||
}
|
||||
pub fn set_kind(&mut self, kind: DefKind) -> &mut Self {
|
||||
self.kind = kind;
|
||||
self
|
||||
}
|
||||
pub fn set_source(&mut self, source: NodeSource<'a>) -> &mut Self {
|
||||
self.node.kind = Some(source);
|
||||
self
|
||||
}
|
||||
pub fn set_module(&mut self, module: Module) -> &mut Self {
|
||||
self.module = module;
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub enum DefKind {
|
||||
/// An unevaluated definition
|
||||
#[default]
|
||||
Undecided,
|
||||
/// An impl block
|
||||
Impl(DefID),
|
||||
/// A use tree, and its parent
|
||||
Use(DefID),
|
||||
/// A type, such as a `type`, `struct`, or `enum`
|
||||
Type(TypeKind),
|
||||
/// A value, such as a `const`, `static`, or `fn`
|
||||
Value(ValueKind),
|
||||
}
|
||||
|
||||
/// A [ValueKind] represents an item in the Value Namespace
|
||||
/// (a component of a [Project](crate::project::Project)).
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ValueKind {
|
||||
Const(DefID),
|
||||
Static(DefID),
|
||||
Local(DefID),
|
||||
Fn(DefID),
|
||||
}
|
||||
/// A [TypeKind] represents an item in the Type Namespace
|
||||
/// A [TypeKind] represents an item
|
||||
/// (a component of a [Project](crate::project::Project)).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum TypeKind {
|
||||
/// An alias for an already-defined type
|
||||
Alias(Option<DefID>),
|
||||
Alias(Handle),
|
||||
/// A primitive type, built-in to the compiler
|
||||
Intrinsic(Intrinsic),
|
||||
/// A user-defined aromatic data type
|
||||
Adt(Adt),
|
||||
/// A reference to an already-defined type: &T
|
||||
Ref(u16, DefID),
|
||||
Ref(u16, Handle),
|
||||
/// A contiguous view of dynamically sized memory
|
||||
Slice(DefID),
|
||||
Slice(Handle),
|
||||
/// A contiguous view of statically sized memory
|
||||
Array(DefID, usize),
|
||||
Array(Handle, usize),
|
||||
/// A tuple of existing types
|
||||
Tuple(Vec<DefID>),
|
||||
Tuple(Vec<Handle>),
|
||||
/// A function which accepts multiple inputs and produces an output
|
||||
FnSig { args: DefID, rety: DefID },
|
||||
FnSig { args: Handle, rety: Handle },
|
||||
/// The unit type
|
||||
Empty,
|
||||
/// The never type
|
||||
@ -117,22 +41,22 @@ pub enum TypeKind {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Adt {
|
||||
/// A union-like enum type
|
||||
Enum(Vec<(Sym, Option<DefID>)>),
|
||||
Enum(Vec<(Sym, Option<Handle>)>),
|
||||
/// A C-like enum
|
||||
CLikeEnum(Vec<(Sym, u128)>),
|
||||
/// An enum with no fields, which can never be constructed
|
||||
FieldlessEnum,
|
||||
|
||||
/// A structural product type with named members
|
||||
Struct(Vec<(Sym, Visibility, DefID)>),
|
||||
Struct(Vec<(Sym, Visibility, Handle)>),
|
||||
/// A structural product type with unnamed members
|
||||
TupleStruct(Vec<(Visibility, DefID)>),
|
||||
TupleStruct(Vec<(Visibility, Handle)>),
|
||||
/// A structural product type of neither named nor unnamed members
|
||||
UnitStruct,
|
||||
|
||||
/// A choose your own undefined behavior type
|
||||
/// TODO: should unions be a language feature?
|
||||
Union(Vec<(Sym, DefID)>),
|
||||
Union(Vec<(Sym, Handle)>),
|
||||
}
|
||||
|
||||
/// The set of compiler-intrinsic types.
|
@ -1,60 +1,14 @@
|
||||
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
|
||||
|
||||
use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind};
|
||||
use crate::{format_utils::*, node::Node};
|
||||
use super::{Adt, Intrinsic, TypeKind};
|
||||
use crate::format_utils::*;
|
||||
use cl_ast::format::FmtAdapter;
|
||||
use std::fmt::{self, Display, Write};
|
||||
|
||||
impl Display for Def<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { module, node: Node { in_path: _, span: _, meta, vis, kind: source }, kind } =
|
||||
self;
|
||||
if !meta.is_empty() {
|
||||
writeln!(f, "#{meta:?}")?;
|
||||
}
|
||||
if let Some(source) = source {
|
||||
if let Some(name) = source.name() {
|
||||
writeln!(f, "{vis}{name}:")?;
|
||||
}
|
||||
writeln!(f.indent(), "source:\n{source}")?;
|
||||
} else {
|
||||
writeln!(f, "{vis}: ")?;
|
||||
}
|
||||
writeln!(f, "kind: {kind}")?;
|
||||
write!(f, "module: {module}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DefKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DefKind::Undecided => write!(f, "undecided"),
|
||||
DefKind::Impl(id) => write!(f, "impl {id}"),
|
||||
DefKind::Use(id) => write!(f, "use (inside {id})"),
|
||||
DefKind::Type(kind) => write!(f, "{kind}"),
|
||||
DefKind::Value(kind) => write!(f, "{kind}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValueKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ValueKind::Const(id) => write!(f, "const ({id})"),
|
||||
ValueKind::Static(id) => write!(f, "static ({id})"),
|
||||
ValueKind::Local(id) => write!(f, "let ({id})"),
|
||||
ValueKind::Fn(id) => write!(f, "fn def ({id})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TypeKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TypeKind::Alias(def) => match def {
|
||||
Some(def) => write!(f, "alias to #{def}"),
|
||||
None => f.write_str("type"),
|
||||
},
|
||||
TypeKind::Alias(def) => write!(f, "alias to #{def}"),
|
||||
TypeKind::Intrinsic(i) => i.fmt(f),
|
||||
TypeKind::Adt(a) => a.fmt(f),
|
||||
TypeKind::Ref(cnt, def) => {
|
Loading…
Reference in New Issue
Block a user