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