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:
		| @@ -1,7 +1,4 @@ | ||||
| use cl_typeck::{ | ||||
|     entry::Entry, import::import, populate::Populator, table::Table, | ||||
|     type_expression::TypeExpression, | ||||
| }; | ||||
| use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression}; | ||||
|  | ||||
| use cl_ast::{ | ||||
|     ast_visitor::{Fold, Visit}, | ||||
| @@ -18,7 +15,7 @@ const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl"; | ||||
| const STDLIB: &str = include_str!("../../../stdlib/lib.cl"); | ||||
|  | ||||
| // Colors | ||||
| const C_MAIN: &str = ""; | ||||
| const C_MAIN: &str = C_LISTING; | ||||
| const C_RESV: &str = "\x1b[35m"; | ||||
| const C_CODE: &str = "\x1b[36m"; | ||||
| const C_BYID: &str = "\x1b[95m"; | ||||
| @@ -148,56 +145,65 @@ fn get_by_id(prj: &mut Table) -> Result<(), RlError> { | ||||
|         } | ||||
|         println!(); | ||||
|  | ||||
|         let Some(handle) = handle.nav(&path.parts) else { | ||||
|         let Some(entry) = handle.nav(&path.parts) else { | ||||
|             Err("No results.")? | ||||
|         }; | ||||
|  | ||||
|         pretty_handle(handle)?; | ||||
|         pretty_handle(entry)?; | ||||
|  | ||||
|         Ok(Response::Accept) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| fn resolve_all(prj: &mut Table) -> Result<(), Box<dyn Error>> { | ||||
|     println!("Resolving imports:"); | ||||
|     for (id, error) in import(prj) { | ||||
|         eprintln!("{error} in {} ({id})", id.to_entry(prj)) | ||||
| fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> { | ||||
|     for (id, error) in import(table) { | ||||
|         eprintln!("{error} in {} ({id})", id.to_entry(table)) | ||||
|     } | ||||
|     // todo!("Resolve imports"); | ||||
|     // prj.resolve_imports()?; | ||||
|     // for id in prj.pool.keys() { | ||||
|     //     resolve(prj, id)?; | ||||
|     // } | ||||
|     // println!("Types resolved successfully!"); | ||||
|     for handle in table.handle_iter() { | ||||
|         if let Err(error) = handle.to_entry_mut(table).categorize() { | ||||
|             eprintln!("{error}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for handle in implement(table) { | ||||
|         eprintln!("Unable to reparent {} ({handle})", handle.to_entry(table)) | ||||
|     } | ||||
|  | ||||
|     println!("...Resolved!"); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn list_types(prj: &mut Table) { | ||||
|     for handle in prj.debug_handle_iter() { | ||||
| fn list_types(table: &mut Table) { | ||||
|     for handle in table.debug_entry_iter() { | ||||
|         let id = handle.id(); | ||||
|         let kind = handle.kind().unwrap(); | ||||
|         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; | ||||
|     let mut out = std::io::stdout().lock(); | ||||
|     let Some(kind) = h.kind() else { | ||||
|         return writeln!(out, "Invalid handle: {h}"); | ||||
|     let Some(kind) = entry.kind() else { | ||||
|         return writeln!(out, "{entry}"); | ||||
|     }; | ||||
|     write!(out, "{C_LISTING}{kind}")?; | ||||
|     if let Some(name) = h.name() { | ||||
|  | ||||
|     if let Some(name) = entry.name() { | ||||
|         write!(out, " {name}")?; | ||||
|     } | ||||
|     writeln!(out, "\x1b[0m: {h}")?; | ||||
|     writeln!(out, "\x1b[0m ({}): {entry}", entry.id())?; | ||||
|  | ||||
|     if let Some(parent) = h.parent() { | ||||
|         writeln!(out, "- {C_LISTING}Parent\x1b[0m: {parent}")?; | ||||
|     if let Some(parent) = entry.parent() { | ||||
|         writeln!( | ||||
|             out, | ||||
|             "- {C_LISTING}Parent\x1b[0m: {parent} ({})", | ||||
|             parent.id() | ||||
|         )?; | ||||
|     } | ||||
|  | ||||
|     if let Some(span) = h.span() { | ||||
|     if let Some(span) = entry.span() { | ||||
|         writeln!( | ||||
|             out, | ||||
|             "- {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() => { | ||||
|             writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?; | ||||
|             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")?; | ||||
|         for (name, child) in children { | ||||
|             writeln!( | ||||
|                 out, | ||||
|                 "  - {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")?; | ||||
|         for (name, child) in imports { | ||||
|             writeln!( | ||||
|                 out, | ||||
|                 "  - {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(()) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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: | ||||
| //! - [Handle]: Provides getters for an entry's fields, and an implementation of | ||||
| //! There are two kinds of entry: | ||||
| //! - [Entry]: Provides getters for an entry's fields, and an implementation of | ||||
| //!   [Display](std::fmt::Display) | ||||
| //! - [HandleMut]: Provides setters for an entry's fields, and an [`as_ref`](HandleMut::as_ref) | ||||
| //!   method to demote to a [Handle]. | ||||
| //! - [EntryMut]: Provides setters for an entry's fields, and an [`as_ref`](EntryMut::as_ref) method | ||||
| //!   to demote to an [Entry]. | ||||
|  | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| @@ -14,11 +14,13 @@ use cl_structures::span::Span; | ||||
| use crate::{ | ||||
|     handle::Handle, | ||||
|     source::Source, | ||||
|     stage::categorize as cat, | ||||
|     table::{NodeKind, Table}, | ||||
|     type_expression::{self as tex, TypeExpression}, | ||||
|     type_kind::TypeKind, | ||||
| }; | ||||
|  | ||||
| pub mod display; | ||||
| mod display; | ||||
|  | ||||
| impl Handle { | ||||
|     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 | ||||
|     } | ||||
|  | ||||
|     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 } | ||||
|     } | ||||
|  | ||||
|     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 }) | ||||
|     } | ||||
|  | ||||
| @@ -64,8 +66,8 @@ impl<'t, 'a> Entry<'t, 'a> { | ||||
|         self.table.kind(self.id) | ||||
|     } | ||||
|  | ||||
|     pub fn parent(&self) -> Option<&Handle> { | ||||
|         self.table.parent(self.id) | ||||
|     pub fn parent(&self) -> Option<Entry<'_, 'a>> { | ||||
|         Some(Entry { id: *self.table.parent(self.id)?, ..*self }) | ||||
|     } | ||||
|  | ||||
|     pub fn children(&self) -> Option<&HashMap<Sym, Handle>> { | ||||
| @@ -92,12 +94,12 @@ impl<'t, 'a> Entry<'t, 'a> { | ||||
|         self.table.source(self.id) | ||||
|     } | ||||
|  | ||||
|     pub fn impl_target(&self) -> Option<Handle> { | ||||
|         self.table.impl_target(self.id) | ||||
|     pub fn impl_target(&self) -> Option<Entry<'_, 'a>> { | ||||
|         Some(Entry { id: self.table.impl_target(self.id)?, ..*self }) | ||||
|     } | ||||
|  | ||||
|     pub fn selfty(&self) -> Option<Handle> { | ||||
|         self.table.selfty(self.id) | ||||
|     pub fn selfty(&self) -> Option<Entry<'_, 'a>> { | ||||
|         Some(Entry { id: self.table.selfty(self.id)?, ..*self }) | ||||
|     } | ||||
|  | ||||
|     pub fn name(&self) -> Option<Sym> { | ||||
| @@ -124,7 +126,17 @@ impl<'t, 'a> EntryMut<'t, 'a> { | ||||
|         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> { | ||||
|         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<'_, '_> { | ||||
|     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); | ||||
|         } | ||||
|         }; | ||||
|  | ||||
|         if let Some(ty) = self.ty() { | ||||
|             match ty { | ||||
|                 TypeKind::Alias(id) => write!(f, "= {}", self.with_id(*id))?, | ||||
|                 TypeKind::Intrinsic(kind) => write!(f, "{kind}")?, | ||||
|                 TypeKind::Adt(adt) => write_adt(adt, self, f)?, | ||||
|                 TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)), | ||||
|                 TypeKind::Intrinsic(kind) => write!(f, "{kind}"), | ||||
|                 TypeKind::Adt(adt) => write_adt(adt, self, f), | ||||
|                 &TypeKind::Ref(cnt, id) => { | ||||
|                     for _ in 0..cnt { | ||||
|                         f.write_str("&")?; | ||||
|                     } | ||||
|                     let h_id = self.with_id(id); | ||||
|                     write_name_or(h_id, f)?; | ||||
|                     write_name_or(h_id, f) | ||||
|                 } | ||||
|                 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) => { | ||||
|                     let mut f = f.delimit_with("[", "]"); | ||||
|                     write_name_or(self.with_id(t), &mut f)?; | ||||
|                     write!(f, "; {cnt}")?; | ||||
|                     write!(f, "; {cnt}") | ||||
|                 } | ||||
|                 TypeKind::Tuple(ids) => { | ||||
|                     let mut f = f.delimit_with("(", ")"); | ||||
| @@ -44,18 +44,19 @@ impl fmt::Display for Entry<'_, '_> { | ||||
|                         } | ||||
|                         write_name_or(self.with_id(id), &mut f)?; | ||||
|                     } | ||||
|                     Ok(()) | ||||
|                 } | ||||
|                 TypeKind::FnSig { args, rety } => { | ||||
|                     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::Never => write!(f, "!")?, | ||||
|                 TypeKind::Module => write!(f, "module?")?, | ||||
|                 TypeKind::Empty => write!(f, "()"), | ||||
|                 TypeKind::Never => write!(f, "!"), | ||||
|                 TypeKind::Module => write!(f, "module?"), | ||||
|             } | ||||
|         } else { | ||||
|             write!(f, "{kind}") | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -67,31 +68,23 @@ fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result { | ||||
|                 variants.next().map(|(name, def)| { | ||||
|                     move |f: &mut Delimit<_>| match def { | ||||
|                         Some(def) => { | ||||
|                             write!(f, "\n{name}: ")?; | ||||
|                             write!(f, "{name}: ")?; | ||||
|                             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) => { | ||||
|             let mut members = members.iter(); | ||||
|             separate(", ", || { | ||||
|                 let (name, vis, id) = members.next()?; | ||||
|                 Some(move |f: &mut Delimit<_>| { | ||||
|                     write!(f, "\n{vis}{name}: ")?; | ||||
|                     write!(f, "{vis}{name}: ")?; | ||||
|                     write_name_or(h.with_id(*id), f) | ||||
|                 }) | ||||
|             })(f.delimit_with("struct {", "\n}")) | ||||
|             })(f.delimit_with("struct {", "}")) | ||||
|         } | ||||
|         Adt::TupleStruct(members) => { | ||||
|             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::*; | ||||
|  | ||||
| // define the index types | ||||
| make_index! { | ||||
|     /// Uniquely represents an entry in the [item context](crate::sym_table) | ||||
|     /// Uniquely represents an entry in the [Table](crate::table::Table) | ||||
|     Handle, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -4,71 +4,28 @@ | ||||
| //! | ||||
| //! This crate is a major work-in-progress. | ||||
| //! | ||||
| //! # The [Project](project::Project)™ | ||||
| //! Contains [item definitions](definition) and type expression information. | ||||
| //! # The [Table](table::Table)™ | ||||
| //! A directed graph of nodes and their dependencies. | ||||
| //! | ||||
| //! *Every* definition is itself a module, and can contain arbitrarily nested items | ||||
| //! as part of the [Module](module::Module) tree. | ||||
| //! Contains [item definitions](handle) and [type expression](type_expression) information. | ||||
| //! | ||||
| //! The Project keeps track of a *global intern pool* of definitions, which are | ||||
| //! trivially comparable by [DefID](key::DefID). Note that, for item definitions, | ||||
| //! identical types in different modules DO NOT COMPARE EQUAL under this constraint. | ||||
| //! However, so-called "anonymous" types *do not* follow this rule, as their | ||||
| //! definitions are constructed dynamically and ensured to be unique. | ||||
| // Note: it's a class invariant that named types are not added | ||||
| // to the anon-types list. Please keep it that way. ♥  Thanks! | ||||
| //! *Every* item is itself a module, and can contain arbitrarily nested items | ||||
| //! as part of the item graph | ||||
| //! | ||||
| //! The table, additionally, has some queues for use in external algorithms, | ||||
| //! detailed in the [stage] module. | ||||
| //! | ||||
| //! # Namespaces | ||||
| //! Within a Project, [definitions](definition::Def) are classified into two namespaces: | ||||
| //! | ||||
| //! ## Type Namespace: | ||||
| //! - Modules | ||||
| //! - Structs | ||||
| //! - 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. | ||||
| //! Each item in the graph is given its own namespace, which is further separated into | ||||
| //! two distinct parts: | ||||
| //! - Children of an item are direct descendents (i.e. their `parent` is a handle to the item) | ||||
| //! - Imports of an item are indirect descendents created by `use` or `impl` directives. They are | ||||
| //!   shadowed by Children with the same name. | ||||
| //! | ||||
| //! # Order of operations: | ||||
| //! Currently, the process of type resolution goes as follows: | ||||
| //! | ||||
| //! 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 | ||||
| //! For order-of-operations information, see the [stage] module. | ||||
| #![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 mod table; | ||||
| @@ -83,75 +40,35 @@ pub mod type_kind; | ||||
|  | ||||
| pub mod type_expression; | ||||
|  | ||||
| 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 use populate::Populator; | ||||
|     /// Stage 1: Populate the graph with nodes. | ||||
|     pub mod populate; | ||||
|  | ||||
|     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; | ||||
|  | ||||
| /* | ||||
|     pub use implement::implement; | ||||
|     /// Stage 4: Import members of `impl` blocks into their corresponding types. | ||||
|     pub mod implement; | ||||
|  | ||||
| LET THERE BE NOTES: | ||||
|  | ||||
| /// What is an inference rule? | ||||
| /// An inference rule is a specification with a set of predicates and a judgement | ||||
|  | ||||
| /// 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, | ||||
|     // TODO: Make type inference stage 5 | ||||
|     // TODO: Use the type information stored in the [table] | ||||
|     pub mod infer; | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|         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 { | ||||
|         matches!( | ||||
|             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 { | ||||
|         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 { | ||||
|         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 { | ||||
|         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 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>)> { | ||||
|     let pending = mem::take(&mut table.uses); | ||||
| 
 | ||||
|     let mut seen = Seen::new(); | ||||
|     let mut failed = vec![]; | ||||
|     for import in pending { | ||||
|         if let Err(e) = import_one(table, import) { | ||||
|             failed.push((import, e)); | ||||
|         let Err(e) = import_one(table, import, &mut seen) else { | ||||
|             continue; | ||||
|         }; | ||||
|         if let Error::NotFound(_, _) = e { | ||||
|             table.mark_use_item(import) | ||||
|         } | ||||
|         failed.push((import, e)); | ||||
|     } | ||||
|     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 { | ||||
|         Err(Error::ItsNoUse)? | ||||
|     }; | ||||
| @@ -35,8 +47,13 @@ fn import_one<'a>(table: &mut Table<'a>, item: Handle) -> UseResult<'a, ()> { | ||||
|     }; | ||||
|     let Use { absolute, tree } = tree; | ||||
| 
 | ||||
|     eprintln!("Resolving import {item}: {code}"); | ||||
|     import_tree(table, if !absolute { dst } else { table.root() }, dst, tree) | ||||
|     import_tree( | ||||
|         table, | ||||
|         if !absolute { dst } else { table.root() }, | ||||
|         dst, | ||||
|         tree, | ||||
|         seen, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| fn import_tree<'a>( | ||||
| @@ -44,32 +61,48 @@ fn import_tree<'a>( | ||||
|     src: Handle, | ||||
|     dst: Handle, | ||||
|     tree: &UseTree, | ||||
|     seen: &mut Seen, | ||||
| ) -> UseResult<'a, ()> { | ||||
|     match tree { | ||||
|         UseTree::Tree(trees) => { | ||||
|             for tree in trees { | ||||
|                 import_tree(table, src, dst, tree)? | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
|         UseTree::Tree(trees) => trees | ||||
|             .iter() | ||||
|             .try_for_each(|tree| import_tree(table, src, dst, tree, seen)), | ||||
|         UseTree::Path(part, rest) => { | ||||
|             let source = table | ||||
|                 .nav(src, slice::from_ref(part)) | ||||
|                 .ok_or_else(|| Error::NotFound(src, part.clone()))?; | ||||
|             import_tree(table, source, dst, rest) | ||||
|             import_tree(table, source, dst, rest, seen) | ||||
|         } | ||||
|         UseTree::Alias(src_name, dst_name) => import_name(table, src, src_name, dst, dst_name), | ||||
|         UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name), | ||||
|         UseTree::Glob => import_glob(table, src, dst), | ||||
|         UseTree::Alias(src_name, dst_name) => { | ||||
|             import_name(table, src, src_name, dst, dst_name, seen) | ||||
|         } | ||||
|         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; | ||||
|     if let Some(children) = children.get(&src) { | ||||
|         // TODO: check for new imports clobbering existing imports
 | ||||
|         imports.entry(dst).or_default().extend(children); | ||||
| 
 | ||||
|     if let Some(c) = children.get(&src) { | ||||
|         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(()) | ||||
| } | ||||
| 
 | ||||
| @@ -79,7 +112,9 @@ fn import_name<'a>( | ||||
|     src_name: &Sym, | ||||
|     dst: Handle, | ||||
|     dst_name: &Sym, | ||||
|     seen: &mut Seen, | ||||
| ) -> UseResult<'a, ()> { | ||||
|     import_deps(table, src, seen)?; | ||||
|     match table.get_by_sym(src, src_name) { | ||||
|         // TODO: check for new imports clobbering existing imports
 | ||||
|         Some(src_id) => table.add_import(dst, *dst_name, src_id), | ||||
| @@ -88,6 +123,17 @@ fn import_name<'a>( | ||||
|     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>>; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| @@ -15,9 +15,9 @@ pub struct Populator<'t, 'a> { | ||||
| 
 | ||||
| impl<'t, 'a> Populator<'t, 'a> { | ||||
|     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> { | ||||
|         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::{ | ||||
|     entry::{Entry, EntryMut}, | ||||
| @@ -12,6 +37,10 @@ use std::collections::HashMap; | ||||
|  | ||||
| // 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)] | ||||
| pub struct Table<'a> { | ||||
|     root: Handle, | ||||
| @@ -20,13 +49,16 @@ pub struct Table<'a> { | ||||
|     parents: IndexMap<Handle, Handle>, | ||||
|     pub(crate) children: 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>, | ||||
|     spans: HashMap<Handle, Span>, | ||||
|     metas: HashMap<Handle, &'a [Meta]>, | ||||
|     sources: HashMap<Handle, Source<'a>>, | ||||
|     // code: HashMap<DefID, BasicBlock>, // TODO: lower sources | ||||
|     // code: HashMap<Handle, BasicBlock>, // TODO: lower sources | ||||
|     impl_targets: HashMap<Handle, Handle>, | ||||
|     anon_types: HashMap<TypeKind, Handle>, | ||||
|  | ||||
|     // --- Queues for algorithms --- | ||||
|     pub(crate) impls: Vec<Handle>, | ||||
|     pub(crate) uses: Vec<Handle>, | ||||
| } | ||||
| @@ -44,6 +76,7 @@ impl<'a> Table<'a> { | ||||
|             parents, | ||||
|             children: HashMap::new(), | ||||
|             imports: HashMap::new(), | ||||
|             use_items: HashMap::new(), | ||||
|             types: HashMap::new(), | ||||
|             spans: HashMap::new(), | ||||
|             metas: HashMap::new(), | ||||
| @@ -78,6 +111,8 @@ impl<'a> Table<'a> { | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|     } | ||||
|  | ||||
| @@ -85,14 +120,18 @@ impl<'a> Table<'a> { | ||||
|         self.impls.push(item); | ||||
|     } | ||||
|  | ||||
|     /// Returns handles to all nodes sequentially by [DefID] | ||||
|     pub fn debug_handle_iter(&self) -> impl Iterator<Item = Entry<'_, 'a>> { | ||||
|     pub fn handle_iter(&mut self) -> impl Iterator<Item = Handle> { | ||||
|         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)) | ||||
|     } | ||||
|  | ||||
|     /// 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. | ||||
|     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) { | ||||
|             return *id; | ||||
|         } | ||||
| @@ -103,11 +142,11 @@ impl<'a> Table<'a> { | ||||
|         entry | ||||
|     } | ||||
|  | ||||
|     pub const fn root_handle(&self) -> Entry<'_, 'a> { | ||||
|     pub const fn root_entry(&self) -> Entry<'_, 'a> { | ||||
|         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) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,17 @@ | ||||
| //! A [TypeKind] is a node in the [Table](crate::table::Table)'s type graph | ||||
|  | ||||
| use crate::handle::Handle; | ||||
| use cl_ast::{Sym, Visibility}; | ||||
| use std::{fmt::Debug, str::FromStr}; | ||||
|  | ||||
| mod display; | ||||
|  | ||||
| pub enum EntryKind { | ||||
|     Variable(Option<Handle>), | ||||
|     Operator(TypeKind), | ||||
| } | ||||
|  | ||||
| /// 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)] | ||||
| pub enum TypeKind { | ||||
|     /// An alias for an already-defined type | ||||
|     Alias(Handle), | ||||
|     Instance(Handle), | ||||
|     /// A primitive type, built-in to the compiler | ||||
|     Intrinsic(Intrinsic), | ||||
|     /// A user-defined aromatic data type | ||||
| @@ -42,10 +39,6 @@ pub enum TypeKind { | ||||
| pub enum Adt { | ||||
|     /// A union-like enum type | ||||
|     Enum(Vec<(Sym, Option<Handle>)>), | ||||
|     /// A C-like enum | ||||
|     CLikeEnum(Vec<(Sym, u128)>), | ||||
|     /// An enum with no fields, which can never be constructed | ||||
|     FieldlessEnum, | ||||
|  | ||||
|     /// A structural product type with named members | ||||
|     Struct(Vec<(Sym, Visibility, Handle)>), | ||||
|   | ||||
| @@ -8,7 +8,7 @@ use std::fmt::{self, Display, Write}; | ||||
| impl Display for TypeKind { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         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::Adt(a) => a.fmt(f), | ||||
|             TypeKind::Ref(cnt, def) => { | ||||
| @@ -47,14 +47,6 @@ impl Display for Adt { | ||||
|                     }) | ||||
|                 })(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) => { | ||||
|                 let mut members = members.iter(); | ||||
|                 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(()) | ||||
|     } | ||||
| } | ||||
| @@ -62,3 +62,18 @@ fn if_else() -> i32 { | ||||
|     } | ||||
|     // 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) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user