cl-typeck: More type inference

- Renamed "intrinsic" -> "primitive"
  - I thought it was funny, but it's just annoying.
- Rename "Uninferred" -> "Inferred"
  - It's a concrete type, but an inferred one.
- Add lifetimes to the Entry API
- Categorize does not care about recursive types. You can't have a recursive AST.
- Added helpful constructors to the inference engine
- Added some overloadable operators to the inference engine
  - These are a MAJOR work in progress
- Reorganized the Inference implementation file by functionality.

stdlib:
- Updated standard library.
This commit is contained in:
John 2025-04-22 06:33:57 -04:00
parent 7cf485fade
commit 681fbc88d3
16 changed files with 925 additions and 479 deletions

View File

@ -21,9 +21,10 @@ impl Path {
} }
} }
/// Gets the defining [Sym] of this path
pub fn as_sym(&self) -> Option<Sym> { pub fn as_sym(&self) -> Option<Sym> {
match self.parts.as_slice() { match self.parts.as_slice() {
[PathPart::Ident(name)] => Some(*name), [.., PathPart::Ident(name)] => Some(*name),
_ => None, _ => None,
} }
} }

View File

@ -80,6 +80,7 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
"q" | "query" => query_type_expression(prj)?, "q" | "query" => query_type_expression(prj)?,
"r" | "resolve" => resolve_all(prj)?, "r" | "resolve" => resolve_all(prj)?,
"s" | "strings" => print_strings(), "s" | "strings" => print_strings(),
"a" | "all" => infer_all(prj)?,
"t" | "test" => infer_expression(prj)?, "t" | "test" => infer_expression(prj)?,
"h" | "help" | "" => { "h" | "help" | "" => {
println!( println!(
@ -159,6 +160,7 @@ fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
}) })
} }
#[allow(dead_code)]
fn infer_expression(prj: &mut Table) -> Result<(), RlError> { fn infer_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ex>", "!?>", |line| { read_and(C_RESV, "ex>", "!?>", |line| {
if line.trim().is_empty() { if line.trim().is_empty() {
@ -238,6 +240,24 @@ fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn infer_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
for (id, error) in InferenceEngine::new(table, table.root()).infer_all() {
match error {
InferenceError::Mismatch(a, b) => {
eprint!("Mismatched types: {}, {}", table.entry(a), table.entry(b));
}
InferenceError::Recursive(a, b) => {
eprint!("Recursive types: {}, {}", table.entry(a), table.entry(b));
}
e => eprint!("{e}"),
}
eprintln!(" in {} ({id})", id.to_entry(table))
}
println!("...Inferred!");
Ok(())
}
fn list_types(table: &mut Table) { fn list_types(table: &mut Table) {
for handle in table.debug_entry_iter() { for handle in table.debug_entry_iter() {
let id = handle.id(); let id = handle.id();

View File

@ -46,15 +46,15 @@ impl<'t, 'a> Entry<'t, 'a> {
self.id self.id
} }
pub fn inner(&self) -> &Table<'a> { pub fn inner(&self) -> &'t Table<'a> {
self.table self.table
} }
pub const fn with_id(&self, id: Handle) -> Entry<'_, 'a> { pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
Self { table: self.table, id } Self { table: self.table, id }
} }
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'_, 'a>> { pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table }) Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
} }
@ -62,19 +62,19 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.root() self.table.root()
} }
pub fn kind(&self) -> Option<&NodeKind> { pub fn kind(&self) -> Option<&'t NodeKind> {
self.table.kind(self.id) self.table.kind(self.id)
} }
pub fn parent(&self) -> Option<Entry<'_, 'a>> { pub fn parent(&self) -> Option<Entry<'t, 'a>> {
Some(Entry { id: *self.table.parent(self.id)?, ..*self }) Some(Entry { id: *self.table.parent(self.id)?, ..*self })
} }
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> { pub fn children(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.children(self.id) self.table.children(self.id)
} }
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> { pub fn imports(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.imports(self.id) self.table.imports(self.id)
} }
@ -82,11 +82,11 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.body(self.id) self.table.body(self.id)
} }
pub fn ty(&self) -> Option<&TypeKind> { pub fn ty(&self) -> Option<&'t TypeKind> {
self.table.ty(self.id) self.table.ty(self.id)
} }
pub fn span(&self) -> Option<&Span> { pub fn span(&self) -> Option<&'t Span> {
self.table.span(self.id) self.table.span(self.id)
} }
@ -94,7 +94,7 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.meta(self.id) self.table.meta(self.id)
} }
pub fn source(&self) -> Option<&Source<'a>> { pub fn source(&self) -> Option<&'t Source<'a>> {
self.table.source(self.id) self.table.source(self.id)
} }

View File

@ -18,10 +18,10 @@ impl fmt::Display for Entry<'_, '_> {
if let Some(ty) = self.ty() { if let Some(ty) = self.ty() {
match ty { match ty {
TypeKind::Uninferred => write!(f, "<_{}>", self.id), TypeKind::Inferred => write!(f, "<_{}>", self.id),
TypeKind::Variable => write!(f, "<?{}>", self.id), TypeKind::Variable => write!(f, "<?{}>", self.id),
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)), TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Intrinsic(kind) => write!(f, "{kind}"), TypeKind::Primitive(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f), TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(id) => { &TypeKind::Ref(id) => {
f.write_str("&")?; f.write_str("&")?;

View File

@ -13,9 +13,9 @@ use cl_ast::*;
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> { pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
if let Some(meta) = table.meta(node) { if let Some(meta) = table.meta(node) {
for meta @ Meta { name, kind } in meta { for meta @ Meta { name, kind } in meta {
if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) { if let ("lang", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
let kind = let kind =
TypeKind::Intrinsic(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?); TypeKind::Primitive(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?);
table.set_ty(node, kind); table.set_ty(node, kind);
return Ok(()); return Ok(());
} }
@ -206,7 +206,6 @@ type CatResult<T> = Result<T, Error>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Error { pub enum Error {
BadMeta(Meta), BadMeta(Meta),
Recursive(Handle),
TypeEval(TypeEval, &'static str), TypeEval(TypeEval, &'static str),
} }
@ -219,10 +218,7 @@ impl From<TypeEval> for Error {
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Error::BadMeta(meta) => write!(f, "Unknown meta attribute: #[{meta}]"), Error::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
Error::Recursive(id) => {
write!(f, "Encountered recursive type without indirection: {id}")
}
Error::TypeEval(e, during) => write!(f, "{e}{during}"), Error::TypeEval(e, during) => write!(f, "{e}{during}"),
} }
} }

View File

@ -2,9 +2,10 @@ use super::error::InferenceError;
use crate::{ use crate::{
entry::Entry, entry::Entry,
handle::Handle, handle::Handle,
stage::infer::inference::Inference,
table::{NodeKind, Table}, table::{NodeKind, Table},
type_expression::TypeExpression, type_expression::TypeExpression,
type_kind::{Adt, TypeKind}, type_kind::{Adt, Primitive, TypeKind},
}; };
use cl_ast::Sym; use cl_ast::Sym;
@ -28,37 +29,91 @@ use cl_ast::Sym;
*/ */
pub struct InferenceEngine<'table, 'a> { pub struct InferenceEngine<'table, 'a> {
pub(crate) at: Handle,
pub(super) table: &'table mut Table<'a>, pub(super) table: &'table mut Table<'a>,
/// The current working node
pub(crate) at: Handle,
/// The current breakset
pub(crate) bset: Handle, pub(crate) bset: Handle,
/// The current returnset
pub(crate) rset: Handle, pub(crate) rset: Handle,
} }
impl<'table, 'a> InferenceEngine<'table, 'a> { impl<'table, 'a> InferenceEngine<'table, 'a> {
/// Infers the type of an object by deferring to [`Inference::infer()`]
pub fn infer(&mut self, inferrable: &'a impl Inference<'a>) -> Result<Handle, InferenceError> {
inferrable.infer(self)
}
/// Constructs a new [`InferenceEngine`], scoped around a [`Handle`] in a [`Table`].
pub fn new(table: &'table mut Table<'a>, at: Handle) -> Self { pub fn new(table: &'table mut Table<'a>, at: Handle) -> Self {
let never = table.anon_type(TypeKind::Never); let never = table.anon_type(TypeKind::Never);
Self { at, table, bset: never, rset: never } Self { at, table, bset: never, rset: never }
} }
/// Constructs an [`InferenceEngine`] that borrows the same table as `self`,
/// but with a shortened lifetime.
pub fn scoped(&mut self) -> InferenceEngine<'_, 'a> {
InferenceEngine { at: self.at, table: self.table, bset: self.bset, rset: self.rset }
}
pub fn infer_all(&mut self) -> Vec<(Handle, InferenceError)> {
let iter = self.table.handle_iter();
let mut res = Vec::new();
for handle in iter {
let mut eng = self.at(handle);
// TODO: use sources instead of bodies, and infer the type globally
let Some(body) = eng.table.body(handle) else {
continue;
};
eprintln!("Evaluating body {body}");
match body.infer(&mut eng) {
Ok(ty) => println!("=> {}", eng.table.entry(ty)),
Err(e) => {
match &e {
&InferenceError::Mismatch(a, b) => {
eprintln!(
"=> Mismatched types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
&InferenceError::Recursive(a, b) => {
eprintln!(
"=> Recursive types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
e => eprintln!("=> {e}"),
}
res.push((handle, e))
}
}
}
res
}
/// Constructs a new InferenceEngine with the
pub fn at(&mut self, at: Handle) -> InferenceEngine<'_, 'a> { pub fn at(&mut self, at: Handle) -> InferenceEngine<'_, 'a> {
InferenceEngine { at, table: self.table, bset: self.bset, rset: self.rset } InferenceEngine { at, ..self.scoped() }
} }
pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> { pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> {
let bset = self.new_var(); InferenceEngine { bset: self.new_var(), ..self.scoped() }
InferenceEngine { at: self.at, table: self.table, bset, rset: self.rset }
} }
pub fn open_rset(&mut self) -> InferenceEngine<'_, 'a> { pub fn open_rset(&mut self) -> InferenceEngine<'_, 'a> {
let rset = self.new_var(); InferenceEngine { rset: self.new_var(), ..self.scoped() }
InferenceEngine { at: self.at, table: self.table, bset: self.bset, rset }
} }
/// Constructs an [Entry] out of a [Handle], for ease of use
pub fn entry(&self, of: Handle) -> Entry<'_, 'a> { pub fn entry(&self, of: Handle) -> Entry<'_, 'a> {
self.table.entry(of) self.table.entry(of)
} }
#[deprecated = "Use dedicated methods instead."]
pub fn from_type_kind(&mut self, kind: TypeKind) -> Handle { pub fn from_type_kind(&mut self, kind: TypeKind) -> Handle {
// TODO: preserve type heirarchy (for, i.e., reference types)
self.table.anon_type(kind) self.table.anon_type(kind)
} }
@ -79,16 +134,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.table.anon_type(TypeKind::Instance(of)) self.table.anon_type(TypeKind::Instance(of))
} }
pub fn de_inst(&self, to: Handle) -> Handle { /// Gets the defining usage of a type without collapsing intermediates
pub fn def_usage(&self, to: Handle) -> Handle {
match self.table.entry(to).ty() { match self.table.entry(to).ty() {
Some(TypeKind::Instance(id)) => self.de_inst(*id), Some(TypeKind::Instance(id)) => self.def_usage(*id),
_ => to, _ => to,
} }
} }
/// Creates a new type variable representing a function (signature) pub fn get_fn(&self, at: Handle, name: Sym) -> Option<(Handle, Handle)> {
pub fn new_fn(&mut self, args: Handle, rety: Handle) -> Handle { use cl_ast::PathPart;
self.table.anon_type(TypeKind::FnSig { args, rety }) if let Some(&TypeKind::FnSig { args, rety }) = self
.entry(at)
.nav(&[PathPart::Ident(name)])
.as_ref()
.and_then(Entry::ty)
{
Some((args, rety))
} else {
None
}
} }
/// Creates a new type variable representing a tuple /// Creates a new type variable representing a tuple
@ -101,11 +166,70 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.table.anon_type(TypeKind::Array(ty, size)) self.table.anon_type(TypeKind::Array(ty, size))
} }
/// Creates a new type variable representing a slice of contiguous memory
pub fn new_slice(&mut self, ty: Handle) -> Handle {
self.table.anon_type(TypeKind::Slice(ty))
}
/// Creates a new reference to a type
pub fn new_ref(&mut self, to: Handle) -> Handle {
self.table.anon_type(TypeKind::Ref(to))
}
/// All primitives must be predefined in the standard library. /// All primitives must be predefined in the standard library.
pub fn primitive(&self, name: Sym) -> Option<Handle> { pub fn primitive(&self, name: Sym) -> Option<Handle> {
// TODO: keep a map of primitives in the table root
self.table.get_by_sym(self.table.root(), &name) self.table.get_by_sym(self.table.root(), &name)
} }
pub fn never(&mut self) -> Handle {
self.table.anon_type(TypeKind::Never)
}
pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Empty)
}
pub fn bool(&self) -> Handle {
self.primitive("bool".into())
.expect("There should be a type named bool.")
}
pub fn char(&self) -> Handle {
self.primitive("char".into())
.expect("There should be a type named char.")
}
pub fn str(&self) -> Handle {
self.primitive("str".into())
.expect("There should be a type named str.")
}
pub fn u32(&self) -> Handle {
self.primitive("u32".into())
.expect("There should be a type named u32.")
}
pub fn usize(&self) -> Handle {
self.primitive("usize".into())
.expect("There should be a type named usize.")
}
/// Creates a new inferred-integer literal
pub fn integer_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Local);
self.table
.set_ty(h, TypeKind::Primitive(Primitive::Integer));
h
}
/// Creates a new inferred-float literal
pub fn float_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Local);
self.table.set_ty(h, TypeKind::Primitive(Primitive::Float));
h
}
/// Enters a new scope /// Enters a new scope
pub fn local_scope(&mut self) { pub fn local_scope(&mut self) {
let scope = self.table.new_entry(self.at, NodeKind::Local); let scope = self.table.new_entry(self.at, NodeKind::Local);
@ -124,13 +248,16 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
pub fn set_instance(&mut self, to: Handle, of: Handle) { pub fn set_instance(&mut self, to: Handle, of: Handle) {
let mut e = self.table.entry_mut(to); let mut e = self.table.entry_mut(to);
match e.as_ref().ty() { match e.as_ref().ty() {
Some(TypeKind::Uninferred) => { Some(TypeKind::Inferred) => {
if let Some(ty) = self.table.ty(of) { if let Some(ty) = self.table.ty(of) {
self.table.set_ty(to, ty.clone()); self.table.set_ty(to, ty.clone());
} }
None None
} }
Some(TypeKind::Variable) => e.set_ty(TypeKind::Instance(of)), Some(TypeKind::Variable)
| Some(TypeKind::Primitive(Primitive::Float | Primitive::Integer)) => {
e.set_ty(TypeKind::Instance(of))
}
other => todo!("Cannot set {} to instance of: {other:?}", e.as_ref()), other => todo!("Cannot set {} to instance of: {other:?}", e.as_ref()),
}; };
} }
@ -142,11 +269,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
return false; return false;
}; };
match ty { match ty {
TypeKind::Uninferred => false, TypeKind::Inferred => false,
TypeKind::Variable => true, TypeKind::Variable => true,
&TypeKind::Array(h, _) => self.is_generic(h), &TypeKind::Array(h, _) => self.is_generic(h),
&TypeKind::Instance(h) => self.is_generic(h), &TypeKind::Instance(h) => self.is_generic(h),
TypeKind::Intrinsic(_) => false, TypeKind::Primitive(_) => false,
TypeKind::Adt(Adt::Enum(tys)) => tys TypeKind::Adt(Adt::Enum(tys)) => tys
.iter() .iter()
.any(|(_, ty)| ty.is_some_and(|ty| self.is_generic(ty))), .any(|(_, ty)| ty.is_some_and(|ty| self.is_generic(ty))),
@ -277,10 +404,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
TypeKind::FnSig { args, rety } => { TypeKind::FnSig { args, rety } => {
self.occurs_in(this, *args) || self.occurs_in(this, *rety) self.occurs_in(this, *args) || self.occurs_in(this, *rety)
} }
TypeKind::Uninferred TypeKind::Inferred
| TypeKind::Variable | TypeKind::Variable
| TypeKind::Adt(Adt::UnitStruct) | TypeKind::Adt(Adt::UnitStruct)
| TypeKind::Intrinsic(_) | TypeKind::Primitive(_)
| TypeKind::Empty | TypeKind::Empty
| TypeKind::Never | TypeKind::Never
| TypeKind::Module => false, | TypeKind::Module => false,
@ -296,11 +423,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
}; };
match (a, b) { match (a, b) {
(TypeKind::Uninferred, _) => { (TypeKind::Inferred, _) => {
self.set_instance(ah, bh); self.set_instance(ah, bh);
Ok(()) Ok(())
} }
(_, TypeKind::Uninferred) => self.unify(bh, ah), (_, TypeKind::Inferred) => self.unify(bh, ah),
(TypeKind::Variable, _) => { (TypeKind::Variable, _) => {
self.set_instance(ah, bh); self.set_instance(ah, bh);
@ -311,9 +438,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
Ok(()) Ok(())
} }
(TypeKind::Instance(_), _) => Err(InferenceError::Recursive(ah, bh)), (TypeKind::Instance(_), _) => Err(InferenceError::Recursive(ah, bh)),
(_, TypeKind::Variable) | (_, TypeKind::Instance(_)) => self.unify(bh, ah),
(TypeKind::Intrinsic(ia), TypeKind::Intrinsic(ib)) if ia == ib => Ok(()), (TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(Primitive::Float)) => {
Err(InferenceError::Mismatch(ah, bh))
}
// Primitives have their own set of vars which only unify with primitives.
(TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(i)) if i.is_integer() => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(f)) if f.is_float() => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Variable)
| (_, TypeKind::Instance(_))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Float)) => self.unify(bh, ah),
(TypeKind::Adt(Adt::Enum(ia)), TypeKind::Adt(Adt::Enum(ib))) (TypeKind::Adt(Adt::Enum(ia)), TypeKind::Adt(Adt::Enum(ib)))
if ia.len() == ib.len() => if ia.len() == ib.len() =>
{ {
@ -356,6 +500,9 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
} }
(TypeKind::Ref(a), TypeKind::Ref(b)) => self.unify(*a, *b), (TypeKind::Ref(a), TypeKind::Ref(b)) => self.unify(*a, *b),
(TypeKind::Slice(a), TypeKind::Slice(b)) => self.unify(*a, *b), (TypeKind::Slice(a), TypeKind::Slice(b)) => self.unify(*a, *b),
// Slice unifies with array
(TypeKind::Array(a, _), TypeKind::Slice(b)) => self.unify(*a, *b),
(TypeKind::Slice(_), TypeKind::Array(_, _)) => self.unify(bh, ah),
(TypeKind::Array(a, sa), TypeKind::Array(b, sb)) if sa == sb => self.unify(*a, *b), (TypeKind::Array(a, sa), TypeKind::Array(b, sb)) if sa == sb => self.unify(*a, *b),
(TypeKind::Tuple(a), TypeKind::Tuple(b)) => { (TypeKind::Tuple(a), TypeKind::Tuple(b)) => {
if a.len() != b.len() { if a.len() != b.len() {
@ -376,9 +523,8 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
{ {
Ok(()) Ok(())
} }
(TypeKind::Empty, TypeKind::Empty) => Ok(()),
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()), (TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(TypeKind::Module, TypeKind::Module) => Ok(()), (a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)), _ => Err(InferenceError::Mismatch(ah, bh)),
} }
} }

View File

@ -7,16 +7,24 @@ use core::fmt;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum InferenceError { pub enum InferenceError {
AnnotationEval(crate::type_expression::Error), AnnotationEval(crate::type_expression::Error),
FieldCount(Handle, usize, usize),
NotFound(Path), NotFound(Path),
Mismatch(Handle, Handle), Mismatch(Handle, Handle),
Recursive(Handle, Handle), Recursive(Handle, Handle),
} }
impl std::error::Error for InferenceError {} impl std::error::Error for InferenceError {}
#[rustfmt::skip]
impl fmt::Display for InferenceError { impl fmt::Display for InferenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
InferenceError::AnnotationEval(error) => write!(f, "{error}"), InferenceError::AnnotationEval(error) => write!(f, "{error}"),
InferenceError::FieldCount(name, want, got) => {
write!(f,
"Struct {name} {} fields! Expected {want}, got {got}",
if want < got { "has too many" } else { "is missing" }
)
}
InferenceError::NotFound(p) => write!(f, "Path not visible in scope: {p}"), InferenceError::NotFound(p) => write!(f, "Path not visible in scope: {p}"),
InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"), InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"), InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),

File diff suppressed because it is too large Load Diff

View File

@ -143,9 +143,9 @@ impl<'a> Table<'a> {
entry entry
} }
pub(crate) fn uninferred_type(&mut self) -> Handle { pub(crate) fn inferred_type(&mut self) -> Handle {
let handle = self.new_entry(self.root, NodeKind::Type); let handle = self.new_entry(self.root, NodeKind::Type);
self.types.insert(handle, TypeKind::Uninferred); self.types.insert(handle, TypeKind::Inferred);
handle handle
} }

View File

@ -42,7 +42,7 @@ impl TypeExpression for TyKind {
match self { match self {
TyKind::Never => Ok(table.anon_type(TypeKind::Never)), TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)), TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
TyKind::Infer => Ok(table.uninferred_type()), TyKind::Infer => Ok(table.inferred_type()),
TyKind::Path(p) => p.evaluate(table, node), TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node), TyKind::Array(a) => a.evaluate(table, node),
TyKind::Slice(s) => s.evaluate(table, node), TyKind::Slice(s) => s.evaluate(table, node),

View File

@ -11,13 +11,13 @@ mod display;
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind { pub enum TypeKind {
/// A type that is yet to be inferred! /// A type that is yet to be inferred!
Uninferred, Inferred,
/// A type variable, to be filled in later /// A type variable, to be monomorphized
Variable, Variable,
/// An alias for an already-defined type /// An alias for an already-defined type
Instance(Handle), Instance(Handle),
/// A primitive type, built-in to the compiler /// A primitive type, built-in to the compiler
Intrinsic(Intrinsic), Primitive(Primitive),
/// A user-defined aromatic data type /// A user-defined aromatic data type
Adt(Adt), Adt(Adt),
/// A reference to an already-defined type: &T /// A reference to an already-defined type: &T
@ -59,42 +59,64 @@ pub enum Adt {
/// The set of compiler-intrinsic types. /// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations. /// These primitive types have native implementations of the basic operations.
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic { pub enum Primitive {
I8, I16, I32, I64, I128, Isize, // Signed integers I8, I16, I32, I64, I128, Isize, // Signed integers
U8, U16, U32, U64, U128, Usize, // Unsigned integers U8, U16, U32, U64, U128, Usize, // Unsigned integers
F8, F16, F32, F64, F128, Fsize, // Floating point numbers F8, F16, F32, F64, F128, Fsize, // Floating point numbers
Integer, Float, // Inferred int and float
Bool, // boolean value Bool, // boolean value
Char, // Unicode codepoint Char, // Unicode codepoint
} }
#[rustfmt::skip]
impl Primitive {
/// Checks whether self is an integer
pub fn is_integer(self) -> bool {
matches!(
self,
| Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::Isize
| Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::Usize
| Self::Integer
)
}
/// Checks whether self is a floating point number
pub fn is_float(self) -> bool {
matches!(
self,
| Self::F8 | Self::F16 | Self::F32 | Self::F64 | Self::F128 | Self::Fsize
| Self::Float
)
}
}
// Author's note: the fsize type is a meme // Author's note: the fsize type is a meme
impl FromStr for Intrinsic { impl FromStr for Primitive {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s { Ok(match s {
"i8" => Intrinsic::I8, "i8" => Primitive::I8,
"i16" => Intrinsic::I16, "i16" => Primitive::I16,
"i32" => Intrinsic::I32, "i32" => Primitive::I32,
"i64" => Intrinsic::I64, "i64" => Primitive::I64,
"i128" => Intrinsic::I128, "i128" => Primitive::I128,
"isize" => Intrinsic::Isize, "isize" => Primitive::Isize,
"u8" => Intrinsic::U8, "u8" => Primitive::U8,
"u16" => Intrinsic::U16, "u16" => Primitive::U16,
"u32" => Intrinsic::U32, "u32" => Primitive::U32,
"u64" => Intrinsic::U64, "u64" => Primitive::U64,
"u128" => Intrinsic::U128, "u128" => Primitive::U128,
"usize" => Intrinsic::Usize, "usize" => Primitive::Usize,
"f8" => Intrinsic::F8, "f8" => Primitive::F8,
"f16" => Intrinsic::F16, "f16" => Primitive::F16,
"f32" => Intrinsic::F32, "f32" => Primitive::F32,
"f64" => Intrinsic::F64, "f64" => Primitive::F64,
"f128" => Intrinsic::F128, "f128" => Primitive::F128,
"fsize" => Intrinsic::Fsize, "fsize" => Primitive::Fsize,
"bool" => Intrinsic::Bool, "bool" => Primitive::Bool,
"char" => Intrinsic::Char, "char" => Primitive::Char,
_ => Err(())?, _ => Err(())?,
}) })
} }

View File

@ -1,6 +1,6 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic] //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Intrinsic, TypeKind}; use super::{Adt, Primitive, TypeKind};
use crate::format_utils::*; use crate::format_utils::*;
use cl_ast::format::FmtAdapter; use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
@ -8,10 +8,10 @@ use std::fmt::{self, Display, Write};
impl Display for TypeKind { impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
TypeKind::Uninferred => write!(f, "_"), TypeKind::Inferred => write!(f, "_"),
TypeKind::Variable => write!(f, "?"), TypeKind::Variable => write!(f, "?"),
TypeKind::Instance(def) => write!(f, "alias to #{def}"), TypeKind::Instance(def) => write!(f, "alias to #{def}"),
TypeKind::Intrinsic(i) => i.fmt(f), TypeKind::Primitive(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f), TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(def) => write!(f, "&{def}"), TypeKind::Ref(def) => write!(f, "&{def}"),
TypeKind::Slice(def) => write!(f, "slice [#{def}]"), TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
@ -70,29 +70,31 @@ impl Display for Adt {
} }
} }
impl Display for Intrinsic { impl Display for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Intrinsic::I8 => f.write_str("i8"), Primitive::I8 => f.write_str("i8"),
Intrinsic::I16 => f.write_str("i16"), Primitive::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"), Primitive::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"), Primitive::I64 => f.write_str("i64"),
Intrinsic::I128 => f.write_str("i128"), Primitive::I128 => f.write_str("i128"),
Intrinsic::Isize => f.write_str("isize"), Primitive::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"), Primitive::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"), Primitive::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"), Primitive::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"), Primitive::U64 => f.write_str("u64"),
Intrinsic::U128 => f.write_str("u128"), Primitive::U128 => f.write_str("u128"),
Intrinsic::Usize => f.write_str("usize"), Primitive::Usize => f.write_str("usize"),
Intrinsic::F8 => f.write_str("f8"), Primitive::F8 => f.write_str("f8"),
Intrinsic::F16 => f.write_str("f16"), Primitive::F16 => f.write_str("f16"),
Intrinsic::F32 => f.write_str("f32"), Primitive::F32 => f.write_str("f32"),
Intrinsic::F64 => f.write_str("f64"), Primitive::F64 => f.write_str("f64"),
Intrinsic::F128 => f.write_str("f128"), Primitive::F128 => f.write_str("f128"),
Intrinsic::Fsize => f.write_str("fsize"), Primitive::Fsize => f.write_str("fsize"),
Intrinsic::Bool => f.write_str("bool"), Primitive::Integer => f.write_str("{integer}"),
Intrinsic::Char => f.write_str("char"), Primitive::Float => f.write_str("{float}"),
Primitive::Bool => f.write_str("bool"),
Primitive::Char => f.write_str("char"),
} }
} }
} }

View File

@ -1,7 +1,11 @@
//! # The Conlang Standard Library //! # The Conlang Standard Library
pub mod preamble { pub mod preamble {
pub use super::{num::*, str::str}; pub use super::{
num::*,
range::{RangeExc, RangeInc},
str::str,
};
} }
pub mod num; pub mod num;

View File

@ -1,51 +1,51 @@
//! The primitive numeric types //! The primitive numeric types
#[intrinsic = "bool"] #[lang = "bool"]
pub type bool; pub type bool;
#[intrinsic = "char"] #[lang = "char"]
pub type char; pub type char;
#[intrinsic = "i8"] #[lang = "i8"]
pub type i8; pub type i8;
#[intrinsic = "i16"] #[lang = "i16"]
pub type i16; pub type i16;
#[intrinsic = "i32"] #[lang = "i32"]
pub type i32; pub type i32;
#[intrinsic = "i64"] #[lang = "i64"]
pub type i64; pub type i64;
#[intrinsic = "i128"] #[lang = "i128"]
pub type i128; pub type i128;
#[intrinsic = "isize"] #[lang = "isize"]
pub type isize; pub type isize;
#[intrinsic = "u8"] #[lang = "u8"]
pub type u8; pub type u8;
#[intrinsic = "u16"] #[lang = "u16"]
pub type u16; pub type u16;
#[intrinsic = "u32"] #[lang = "u32"]
pub type u32; pub type u32;
#[intrinsic = "u64"] #[lang = "u64"]
pub type u64; pub type u64;
#[intrinsic = "u128"] #[lang = "u128"]
pub type u128; pub type u128;
#[intrinsic = "usize"] #[lang = "usize"]
pub type usize; pub type usize;
#[intrinsic = "f32"] #[lang = "f32"]
pub type f32; pub type f32;
#[intrinsic = "f64"] #[lang = "f64"]
pub type f64; pub type f64;
// Contains implementations for (TODO) overloaded operators on num types // Contains implementations for (TODO) overloaded operators on num types

View File

@ -1,9 +1,17 @@
//! Iterable ranges //! Iterable ranges
type T = _;
/// An Exclusive Range `a .. b` iterates from a to b, excluding b /// An Exclusive Range `a .. b` iterates from a to b, excluding b
#[intrinsic = "range_exc", T] // #[lang = "range_exc", T]
struct RangeExc(T, T) pub struct RangeExc(T, T)
/// An Inclusive Range `a ..= b` iterates from a to b, including b /// An Inclusive Range `a ..= b` iterates from a to b, including b
#[intrinsic = "range_inc", T] // #[lang = "range_inc", T]
struct RangeInc(T, T) pub struct RangeInc(T, T)
impl RangeExc {
fn next(this: &RangeInc) -> T {
(*this).0
}
}

View File

@ -1,4 +1,5 @@
//! TODO: give conland a string type //! TODO: give conland a string type
use super::num::u8; use super::num::u8;
// #[lang = "str"]
type str = [u8]; type str = [u8];