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:
parent
7cf485fade
commit
681fbc88d3
@ -21,9 +21,10 @@ impl Path {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the defining [Sym] of this path
|
||||
pub fn as_sym(&self) -> Option<Sym> {
|
||||
match self.parts.as_slice() {
|
||||
[PathPart::Ident(name)] => Some(*name),
|
||||
[.., PathPart::Ident(name)] => Some(*name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
|
||||
"q" | "query" => query_type_expression(prj)?,
|
||||
"r" | "resolve" => resolve_all(prj)?,
|
||||
"s" | "strings" => print_strings(),
|
||||
"a" | "all" => infer_all(prj)?,
|
||||
"t" | "test" => infer_expression(prj)?,
|
||||
"h" | "help" | "" => {
|
||||
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> {
|
||||
read_and(C_RESV, "ex>", "!?>", |line| {
|
||||
if line.trim().is_empty() {
|
||||
@ -238,6 +240,24 @@ fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
|
||||
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) {
|
||||
for handle in table.debug_entry_iter() {
|
||||
let id = handle.id();
|
||||
|
@ -46,15 +46,15 @@ impl<'t, 'a> Entry<'t, 'a> {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &Table<'a> {
|
||||
pub fn inner(&self) -> &'t Table<'a> {
|
||||
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 }
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
@ -62,19 +62,19 @@ impl<'t, 'a> Entry<'t, 'a> {
|
||||
self.table.root()
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Option<&NodeKind> {
|
||||
pub fn kind(&self) -> Option<&'t NodeKind> {
|
||||
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 })
|
||||
}
|
||||
|
||||
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
|
||||
pub fn children(&self) -> Option<&'t HashMap<Sym, Handle>> {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -82,11 +82,11 @@ impl<'t, 'a> Entry<'t, 'a> {
|
||||
self.table.body(self.id)
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Option<&TypeKind> {
|
||||
pub fn ty(&self) -> Option<&'t TypeKind> {
|
||||
self.table.ty(self.id)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<&Span> {
|
||||
pub fn span(&self) -> Option<&'t Span> {
|
||||
self.table.span(self.id)
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ impl<'t, 'a> Entry<'t, 'a> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@ impl fmt::Display for Entry<'_, '_> {
|
||||
|
||||
if let Some(ty) = self.ty() {
|
||||
match ty {
|
||||
TypeKind::Uninferred => write!(f, "<_{}>", self.id),
|
||||
TypeKind::Inferred => write!(f, "<_{}>", self.id),
|
||||
TypeKind::Variable => write!(f, "<?{}>", self.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::Ref(id) => {
|
||||
f.write_str("&")?;
|
||||
|
@ -13,9 +13,9 @@ use cl_ast::*;
|
||||
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) {
|
||||
if let ("lang", MetaKind::Equals(Literal::String(s))) = (&**name, 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);
|
||||
return Ok(());
|
||||
}
|
||||
@ -206,7 +206,6 @@ type CatResult<T> = Result<T, Error>;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
BadMeta(Meta),
|
||||
Recursive(Handle),
|
||||
TypeEval(TypeEval, &'static str),
|
||||
}
|
||||
|
||||
@ -219,10 +218,7 @@ impl From<TypeEval> for Error {
|
||||
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::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
|
||||
Error::TypeEval(e, during) => write!(f, "{e}{during}"),
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ use super::error::InferenceError;
|
||||
use crate::{
|
||||
entry::Entry,
|
||||
handle::Handle,
|
||||
stage::infer::inference::Inference,
|
||||
table::{NodeKind, Table},
|
||||
type_expression::TypeExpression,
|
||||
type_kind::{Adt, TypeKind},
|
||||
type_kind::{Adt, Primitive, TypeKind},
|
||||
};
|
||||
use cl_ast::Sym;
|
||||
|
||||
@ -28,37 +29,91 @@ use cl_ast::Sym;
|
||||
*/
|
||||
|
||||
pub struct InferenceEngine<'table, 'a> {
|
||||
pub(crate) at: Handle,
|
||||
pub(super) table: &'table mut Table<'a>,
|
||||
/// The current working node
|
||||
pub(crate) at: Handle,
|
||||
/// The current breakset
|
||||
pub(crate) bset: Handle,
|
||||
/// The current returnset
|
||||
pub(crate) rset: Handle,
|
||||
}
|
||||
|
||||
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 {
|
||||
let never = table.anon_type(TypeKind::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> {
|
||||
InferenceEngine { at, table: self.table, bset: self.bset, rset: self.rset }
|
||||
InferenceEngine { at, ..self.scoped() }
|
||||
}
|
||||
|
||||
pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> {
|
||||
let bset = self.new_var();
|
||||
InferenceEngine { at: self.at, table: self.table, bset, rset: self.rset }
|
||||
InferenceEngine { bset: self.new_var(), ..self.scoped() }
|
||||
}
|
||||
|
||||
pub fn open_rset(&mut self) -> InferenceEngine<'_, 'a> {
|
||||
let rset = self.new_var();
|
||||
InferenceEngine { at: self.at, table: self.table, bset: self.bset, rset }
|
||||
InferenceEngine { rset: self.new_var(), ..self.scoped() }
|
||||
}
|
||||
|
||||
/// Constructs an [Entry] out of a [Handle], for ease of use
|
||||
pub fn entry(&self, of: Handle) -> Entry<'_, 'a> {
|
||||
self.table.entry(of)
|
||||
}
|
||||
|
||||
#[deprecated = "Use dedicated methods instead."]
|
||||
pub fn from_type_kind(&mut self, kind: TypeKind) -> Handle {
|
||||
// TODO: preserve type heirarchy (for, i.e., reference types)
|
||||
self.table.anon_type(kind)
|
||||
}
|
||||
|
||||
@ -79,16 +134,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
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() {
|
||||
Some(TypeKind::Instance(id)) => self.de_inst(*id),
|
||||
Some(TypeKind::Instance(id)) => self.def_usage(*id),
|
||||
_ => to,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new type variable representing a function (signature)
|
||||
pub fn new_fn(&mut self, args: Handle, rety: Handle) -> Handle {
|
||||
self.table.anon_type(TypeKind::FnSig { args, rety })
|
||||
pub fn get_fn(&self, at: Handle, name: Sym) -> Option<(Handle, Handle)> {
|
||||
use cl_ast::PathPart;
|
||||
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
|
||||
@ -101,11 +166,70 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
pub fn local_scope(&mut self) {
|
||||
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) {
|
||||
let mut e = self.table.entry_mut(to);
|
||||
match e.as_ref().ty() {
|
||||
Some(TypeKind::Uninferred) => {
|
||||
Some(TypeKind::Inferred) => {
|
||||
if let Some(ty) = self.table.ty(of) {
|
||||
self.table.set_ty(to, ty.clone());
|
||||
}
|
||||
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()),
|
||||
};
|
||||
}
|
||||
@ -142,11 +269,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
return false;
|
||||
};
|
||||
match ty {
|
||||
TypeKind::Uninferred => false,
|
||||
TypeKind::Inferred => false,
|
||||
TypeKind::Variable => true,
|
||||
&TypeKind::Array(h, _) => self.is_generic(h),
|
||||
&TypeKind::Instance(h) => self.is_generic(h),
|
||||
TypeKind::Intrinsic(_) => false,
|
||||
TypeKind::Primitive(_) => false,
|
||||
TypeKind::Adt(Adt::Enum(tys)) => tys
|
||||
.iter()
|
||||
.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 } => {
|
||||
self.occurs_in(this, *args) || self.occurs_in(this, *rety)
|
||||
}
|
||||
TypeKind::Uninferred
|
||||
TypeKind::Inferred
|
||||
| TypeKind::Variable
|
||||
| TypeKind::Adt(Adt::UnitStruct)
|
||||
| TypeKind::Intrinsic(_)
|
||||
| TypeKind::Primitive(_)
|
||||
| TypeKind::Empty
|
||||
| TypeKind::Never
|
||||
| TypeKind::Module => false,
|
||||
@ -296,11 +423,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
};
|
||||
|
||||
match (a, b) {
|
||||
(TypeKind::Uninferred, _) => {
|
||||
(TypeKind::Inferred, _) => {
|
||||
self.set_instance(ah, bh);
|
||||
Ok(())
|
||||
}
|
||||
(_, TypeKind::Uninferred) => self.unify(bh, ah),
|
||||
(_, TypeKind::Inferred) => self.unify(bh, ah),
|
||||
|
||||
(TypeKind::Variable, _) => {
|
||||
self.set_instance(ah, bh);
|
||||
@ -311,9 +438,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
Ok(())
|
||||
}
|
||||
(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)))
|
||||
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::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::Tuple(a), TypeKind::Tuple(b)) => {
|
||||
if a.len() != b.len() {
|
||||
@ -376,9 +523,8 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
(TypeKind::Empty, TypeKind::Empty) => Ok(()),
|
||||
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
|
||||
(TypeKind::Module, TypeKind::Module) => Ok(()),
|
||||
(a, b) if a == b => Ok(()),
|
||||
_ => Err(InferenceError::Mismatch(ah, bh)),
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,24 @@ use core::fmt;
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum InferenceError {
|
||||
AnnotationEval(crate::type_expression::Error),
|
||||
FieldCount(Handle, usize, usize),
|
||||
NotFound(Path),
|
||||
Mismatch(Handle, Handle),
|
||||
Recursive(Handle, Handle),
|
||||
}
|
||||
|
||||
impl std::error::Error for InferenceError {}
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for InferenceError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
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::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
|
||||
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -143,9 +143,9 @@ impl<'a> Table<'a> {
|
||||
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);
|
||||
self.types.insert(handle, TypeKind::Uninferred);
|
||||
self.types.insert(handle, TypeKind::Inferred);
|
||||
handle
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ impl TypeExpression for TyKind {
|
||||
match self {
|
||||
TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
|
||||
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::Array(a) => a.evaluate(table, node),
|
||||
TyKind::Slice(s) => s.evaluate(table, node),
|
||||
|
@ -11,13 +11,13 @@ mod display;
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum TypeKind {
|
||||
/// A type that is yet to be inferred!
|
||||
Uninferred,
|
||||
/// A type variable, to be filled in later
|
||||
Inferred,
|
||||
/// A type variable, to be monomorphized
|
||||
Variable,
|
||||
/// An alias for an already-defined type
|
||||
Instance(Handle),
|
||||
/// A primitive type, built-in to the compiler
|
||||
Intrinsic(Intrinsic),
|
||||
Primitive(Primitive),
|
||||
/// A user-defined aromatic data type
|
||||
Adt(Adt),
|
||||
/// A reference to an already-defined type: &T
|
||||
@ -59,42 +59,64 @@ pub enum Adt {
|
||||
/// The set of compiler-intrinsic types.
|
||||
/// These primitive types have native implementations of the basic operations.
|
||||
#[rustfmt::skip]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Intrinsic {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Primitive {
|
||||
I8, I16, I32, I64, I128, Isize, // Signed integers
|
||||
U8, U16, U32, U64, U128, Usize, // Unsigned integers
|
||||
F8, F16, F32, F64, F128, Fsize, // Floating point numbers
|
||||
Integer, Float, // Inferred int and float
|
||||
Bool, // boolean value
|
||||
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
|
||||
|
||||
impl FromStr for Intrinsic {
|
||||
impl FromStr for Primitive {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"i8" => Intrinsic::I8,
|
||||
"i16" => Intrinsic::I16,
|
||||
"i32" => Intrinsic::I32,
|
||||
"i64" => Intrinsic::I64,
|
||||
"i128" => Intrinsic::I128,
|
||||
"isize" => Intrinsic::Isize,
|
||||
"u8" => Intrinsic::U8,
|
||||
"u16" => Intrinsic::U16,
|
||||
"u32" => Intrinsic::U32,
|
||||
"u64" => Intrinsic::U64,
|
||||
"u128" => Intrinsic::U128,
|
||||
"usize" => Intrinsic::Usize,
|
||||
"f8" => Intrinsic::F8,
|
||||
"f16" => Intrinsic::F16,
|
||||
"f32" => Intrinsic::F32,
|
||||
"f64" => Intrinsic::F64,
|
||||
"f128" => Intrinsic::F128,
|
||||
"fsize" => Intrinsic::Fsize,
|
||||
"bool" => Intrinsic::Bool,
|
||||
"char" => Intrinsic::Char,
|
||||
"i8" => Primitive::I8,
|
||||
"i16" => Primitive::I16,
|
||||
"i32" => Primitive::I32,
|
||||
"i64" => Primitive::I64,
|
||||
"i128" => Primitive::I128,
|
||||
"isize" => Primitive::Isize,
|
||||
"u8" => Primitive::U8,
|
||||
"u16" => Primitive::U16,
|
||||
"u32" => Primitive::U32,
|
||||
"u64" => Primitive::U64,
|
||||
"u128" => Primitive::U128,
|
||||
"usize" => Primitive::Usize,
|
||||
"f8" => Primitive::F8,
|
||||
"f16" => Primitive::F16,
|
||||
"f32" => Primitive::F32,
|
||||
"f64" => Primitive::F64,
|
||||
"f128" => Primitive::F128,
|
||||
"fsize" => Primitive::Fsize,
|
||||
"bool" => Primitive::Bool,
|
||||
"char" => Primitive::Char,
|
||||
_ => Err(())?,
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
|
||||
|
||||
use super::{Adt, Intrinsic, TypeKind};
|
||||
use super::{Adt, Primitive, TypeKind};
|
||||
use crate::format_utils::*;
|
||||
use cl_ast::format::FmtAdapter;
|
||||
use std::fmt::{self, Display, Write};
|
||||
@ -8,10 +8,10 @@ use std::fmt::{self, Display, Write};
|
||||
impl Display for TypeKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TypeKind::Uninferred => write!(f, "_"),
|
||||
TypeKind::Inferred => write!(f, "_"),
|
||||
TypeKind::Variable => write!(f, "?"),
|
||||
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::Ref(def) => write!(f, "&{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 {
|
||||
match self {
|
||||
Intrinsic::I8 => f.write_str("i8"),
|
||||
Intrinsic::I16 => f.write_str("i16"),
|
||||
Intrinsic::I32 => f.write_str("i32"),
|
||||
Intrinsic::I64 => f.write_str("i64"),
|
||||
Intrinsic::I128 => f.write_str("i128"),
|
||||
Intrinsic::Isize => f.write_str("isize"),
|
||||
Intrinsic::U8 => f.write_str("u8"),
|
||||
Intrinsic::U16 => f.write_str("u16"),
|
||||
Intrinsic::U32 => f.write_str("u32"),
|
||||
Intrinsic::U64 => f.write_str("u64"),
|
||||
Intrinsic::U128 => f.write_str("u128"),
|
||||
Intrinsic::Usize => f.write_str("usize"),
|
||||
Intrinsic::F8 => f.write_str("f8"),
|
||||
Intrinsic::F16 => f.write_str("f16"),
|
||||
Intrinsic::F32 => f.write_str("f32"),
|
||||
Intrinsic::F64 => f.write_str("f64"),
|
||||
Intrinsic::F128 => f.write_str("f128"),
|
||||
Intrinsic::Fsize => f.write_str("fsize"),
|
||||
Intrinsic::Bool => f.write_str("bool"),
|
||||
Intrinsic::Char => f.write_str("char"),
|
||||
Primitive::I8 => f.write_str("i8"),
|
||||
Primitive::I16 => f.write_str("i16"),
|
||||
Primitive::I32 => f.write_str("i32"),
|
||||
Primitive::I64 => f.write_str("i64"),
|
||||
Primitive::I128 => f.write_str("i128"),
|
||||
Primitive::Isize => f.write_str("isize"),
|
||||
Primitive::U8 => f.write_str("u8"),
|
||||
Primitive::U16 => f.write_str("u16"),
|
||||
Primitive::U32 => f.write_str("u32"),
|
||||
Primitive::U64 => f.write_str("u64"),
|
||||
Primitive::U128 => f.write_str("u128"),
|
||||
Primitive::Usize => f.write_str("usize"),
|
||||
Primitive::F8 => f.write_str("f8"),
|
||||
Primitive::F16 => f.write_str("f16"),
|
||||
Primitive::F32 => f.write_str("f32"),
|
||||
Primitive::F64 => f.write_str("f64"),
|
||||
Primitive::F128 => f.write_str("f128"),
|
||||
Primitive::Fsize => f.write_str("fsize"),
|
||||
Primitive::Integer => f.write_str("{integer}"),
|
||||
Primitive::Float => f.write_str("{float}"),
|
||||
Primitive::Bool => f.write_str("bool"),
|
||||
Primitive::Char => f.write_str("char"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
//! # The Conlang Standard Library
|
||||
|
||||
pub mod preamble {
|
||||
pub use super::{num::*, str::str};
|
||||
pub use super::{
|
||||
num::*,
|
||||
range::{RangeExc, RangeInc},
|
||||
str::str,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod num;
|
||||
|
@ -1,51 +1,51 @@
|
||||
//! The primitive numeric types
|
||||
|
||||
#[intrinsic = "bool"]
|
||||
#[lang = "bool"]
|
||||
pub type bool;
|
||||
|
||||
#[intrinsic = "char"]
|
||||
#[lang = "char"]
|
||||
pub type char;
|
||||
|
||||
#[intrinsic = "i8"]
|
||||
#[lang = "i8"]
|
||||
pub type i8;
|
||||
|
||||
#[intrinsic = "i16"]
|
||||
#[lang = "i16"]
|
||||
pub type i16;
|
||||
|
||||
#[intrinsic = "i32"]
|
||||
#[lang = "i32"]
|
||||
pub type i32;
|
||||
|
||||
#[intrinsic = "i64"]
|
||||
#[lang = "i64"]
|
||||
pub type i64;
|
||||
|
||||
#[intrinsic = "i128"]
|
||||
#[lang = "i128"]
|
||||
pub type i128;
|
||||
|
||||
#[intrinsic = "isize"]
|
||||
#[lang = "isize"]
|
||||
pub type isize;
|
||||
|
||||
#[intrinsic = "u8"]
|
||||
#[lang = "u8"]
|
||||
pub type u8;
|
||||
|
||||
#[intrinsic = "u16"]
|
||||
#[lang = "u16"]
|
||||
pub type u16;
|
||||
|
||||
#[intrinsic = "u32"]
|
||||
#[lang = "u32"]
|
||||
pub type u32;
|
||||
|
||||
#[intrinsic = "u64"]
|
||||
#[lang = "u64"]
|
||||
pub type u64;
|
||||
|
||||
#[intrinsic = "u128"]
|
||||
#[lang = "u128"]
|
||||
pub type u128;
|
||||
|
||||
#[intrinsic = "usize"]
|
||||
#[lang = "usize"]
|
||||
pub type usize;
|
||||
|
||||
#[intrinsic = "f32"]
|
||||
#[lang = "f32"]
|
||||
pub type f32;
|
||||
|
||||
#[intrinsic = "f64"]
|
||||
#[lang = "f64"]
|
||||
pub type f64;
|
||||
|
||||
// Contains implementations for (TODO) overloaded operators on num types
|
||||
|
@ -1,9 +1,17 @@
|
||||
//! Iterable ranges
|
||||
|
||||
type T = _;
|
||||
|
||||
/// An Exclusive Range `a .. b` iterates from a to b, excluding b
|
||||
#[intrinsic = "range_exc", T]
|
||||
struct RangeExc(T, T)
|
||||
// #[lang = "range_exc", T]
|
||||
pub struct RangeExc(T, T)
|
||||
|
||||
/// An Inclusive Range `a ..= b` iterates from a to b, including b
|
||||
#[intrinsic = "range_inc", T]
|
||||
struct RangeInc(T, T)
|
||||
// #[lang = "range_inc", T]
|
||||
pub struct RangeInc(T, T)
|
||||
|
||||
impl RangeExc {
|
||||
fn next(this: &RangeInc) -> T {
|
||||
(*this).0
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! TODO: give conland a string type
|
||||
use super::num::u8;
|
||||
|
||||
// #[lang = "str"]
|
||||
type str = [u8];
|
||||
|
Loading…
x
Reference in New Issue
Block a user