cl-typeck: Add Handle type

- Holds a DefID and a reference to the Project
- Pretty-prints def signatures
- Use handles when printing types (WIP)

Known issues:
- Printing recursive types recurses forever
- There's some unrelated name resolution stuff going on that needs investigating.

TODO:
- Better name
- HandleMut?
- More interfaces!
- Migrate everything to use handles when oop semantics are easiest
- Reject plain recursive types, and don't recurse through reference types when printing
This commit is contained in:
John 2024-07-21 05:45:40 -05:00
parent 82b71e2517
commit 9d7ab77999
2 changed files with 200 additions and 24 deletions

View File

@ -6,6 +6,7 @@ use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser}; use cl_parser::{inliner::ModuleInliner, Parser};
use cl_typeck::{ use cl_typeck::{
definition::Def, definition::Def,
handle::Handle,
name_collector::NameCollector, name_collector::NameCollector,
node::{Node, NodeSource}, node::{Node, NodeSource},
project::Project, project::Project,
@ -118,8 +119,8 @@ fn query_type_expression(prj: &mut Project) -> Result<(), RlError> {
} }
// parse it as a path, and convert the path into a borrowed path // parse it as a path, and convert the path into a borrowed path
let ty = Parser::new(Lexer::new(line)).ty()?.kind; let ty = Parser::new(Lexer::new(line)).ty()?.kind;
let id = prj.evaluate(&ty, prj.root)?; let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj);
pretty_def(&prj[id], id); pretty_handle(id)?;
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
@ -135,31 +136,43 @@ fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> {
fn list_types(prj: &mut Project) { fn list_types(prj: &mut Project) {
println!(" name\x1b[30G type"); println!(" name\x1b[30G type");
for (idx, Def { kind, node: Node { vis, kind: source, .. }, .. }) in for (idx, key) in prj.pool.keys().enumerate() {
prj.pool.values().enumerate() let Def { node: Node { vis, kind: source, .. }, .. } = &prj[key];
{ let name = match source.as_ref().map(NodeSource::name) {
print!("{idx:3}: {vis}"); Some(Some(name)) => name,
if let Some(Some(name)) = source.as_ref().map(NodeSource::name) { _ => "".into(),
print!("{name}"); };
} print!(
println!("\x1b[30G| {kind}") "{idx:3}: {vis}{name}\x1b[30G = {}",
key.handle_unchecked(prj)
);
println!();
} }
} }
fn pretty_def(def: &Def, id: impl Into<usize>) { fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> {
let id = id.into(); use std::io::Write;
let Def { module, kind, node: Node { meta, vis, kind: source, .. } } = def; let mut stdout = std::io::stdout().lock();
for meta in *meta { let Some(Def { module, node: Node { vis, .. }, .. }) = handle.get() else {
println!("#[{meta}]") return writeln!(stdout, "Invalid handle: {handle}");
};
writeln!(stdout, "{C_LISTING}{vis}{handle}\x1b[0m: {}", handle.id())?;
if let Some(parent) = module.parent {
writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {}", handle.with(parent))?;
} }
if let Some(Some(name)) = source.as_ref().map(NodeSource::name) { if !module.types.is_empty() {
print!("{vis}{name} "); writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?;
for (name, def) in &module.types {
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
} }
println!("[id: {id}] = {kind}");
if let Some(source) = source {
println!("Source:\n{C_LISTING}{source}\x1b[0m");
} }
println!("\x1b[90m{module}\x1b[0m"); if !module.values.is_empty() {
writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?;
for (name, def) in &module.values {
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
}
}
write!(stdout, "\x1b[0m")
} }
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File { fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File {

View File

@ -1,3 +1,4 @@
use crate::{definition::Def, project::Project};
use cl_structures::index_map::*; use cl_structures::index_map::*;
// define the index types // define the index types
@ -8,8 +9,170 @@ make_index! {
DefID, DefID,
} }
impl std::fmt::Display for DefID { /// 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)
}
/// 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 display {
use super::*;
use crate::{definition::*, format_utils::*};
use std::fmt::{self, Display, Write};
impl Display for DefID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) 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}"))
}
}
}
} }