conlang: add lang items, remove Empty, and shuffle typeck

This commit is contained in:
2025-09-15 10:45:14 -04:00
parent ead1f351a7
commit f41e5fc49a
26 changed files with 172 additions and 154 deletions

View File

@@ -205,4 +205,8 @@ impl<'t, 'a> EntryMut<'t, 'a> {
pub fn mark_impl_item(&mut self) {
self.table.mark_impl_item(self.id)
}
pub fn mark_lang_item(&mut self, lang_item: &'static str) {
self.table.mark_lang_item(lang_item, self.id)
}
}

View File

@@ -55,8 +55,6 @@ impl fmt::Display for Entry<'_, '_> {
write!(f, "fn {} -> ", self.with_id(*args))?;
write_name_or(self.with_id(*rety), f)
}
TypeKind::Empty => write!(f, "()"),
TypeKind::Never => write!(f, "!"),
TypeKind::Module => write!(f, "module?"),
}
} else {

View File

@@ -17,8 +17,6 @@ pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
};
match source {
Source::Alias(a) => cat_alias(table, node, a)?,
Source::Enum(e) => cat_enum(table, node, e)?,
Source::Variant(v) => cat_variant(table, node, v)?,
Source::Struct(s) => cat_struct(table, node, s)?,
Source::Const(c) => cat_const(table, node, c)?,
@@ -26,21 +24,8 @@ pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
Source::Function(f) => cat_function(table, node, f)?,
Source::Local(l) => cat_local(table, node, l)?,
Source::Impl(i) => cat_impl(table, node, i)?,
_ => {}
}
if let Some(meta) = table.meta(node) {
for meta @ Meta { name, kind } in meta {
if let ("lang", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
if let Ok(prim) = s.parse() {
table.set_ty(node, TypeKind::Primitive(prim));
} else {
table.mark_lang_item(s.into(), node);
continue;
}
return Ok(());
}
}
// Source::Alias(_) => {table.mark_unchecked(node)},
_ => return Ok(()),
}
Ok(())
}
@@ -49,20 +34,6 @@ 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 Struct { name: _, gens: _, kind } = s;
// TODO: Generics
@@ -99,7 +70,6 @@ fn cat_member(
fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> {
let Enum { name: _, gens: _, variants: _ } = e;
// table.set_ty(node, kind);
Ok(())
}
@@ -107,17 +77,10 @@ fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult
fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatResult<()> {
let Variant { name, kind, body } = v;
let parent = table.parent(node).copied().unwrap_or(table.root());
match (kind, body) {
(StructKind::Empty, None) => {
table.set_ty(node, TypeKind::Adt(Adt::UnitStruct));
Ok(())
}
(StructKind::Empty, Some(c)) => {
table.set_body(node, c);
table.set_ty(node, TypeKind::Adt(Adt::UnitStruct));
Ok(())
}
(StructKind::Tuple(ty), None) => {
match (kind) {
(StructKind::Empty) => Ok(()),
(StructKind::Empty) => Ok(()),
(StructKind::Tuple(ty)) => {
let ty = TypeKind::Adt(Adt::TupleStruct(
ty.iter()
.map(|ty| ty.evaluate(table, node).map(|ty| (Visibility::Public, ty)))
@@ -126,7 +89,7 @@ fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatRe
table.set_ty(node, ty);
Ok(())
}
(StructKind::Struct(members), None) => {
(StructKind::Struct(members)) => {
let mut out = vec![];
for StructMember { vis, name, ty } in members {
let ty = ty.evaluate(table, node)?;
@@ -144,9 +107,6 @@ fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatRe
table.set_ty(node, TypeKind::Adt(Adt::Struct(out)));
Ok(())
}
(_, Some(body)) => {
panic!("Unexpected body `{body}` in enum variant `{v}`")
}
}
}

View File

@@ -81,7 +81,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
Source::Module(v) => v.infer(&mut eng),
Source::Alias(v) => v.infer(&mut eng),
Source::Enum(v) => v.infer(&mut eng),
Source::Variant(v) => v.infer(&mut eng),
// Source::Variant(v) => v.infer(&mut eng),
Source::Struct(v) => v.infer(&mut eng),
Source::Const(v) => v.infer(&mut eng),
Source::Static(v) => v.infer(&mut eng),
@@ -221,42 +221,37 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
}
/// All primitives must be predefined in the standard library.
pub fn primitive(&self, name: Sym) -> Option<Handle> {
pub fn primitive(&self, name: &'static str) -> Handle {
// TODO: keep a map of primitives in the table root
self.table.get_by_sym(self.table.root(), &name)
self.table.get_lang_item(name)
}
pub fn never(&mut self) -> Handle {
self.table.anon_type(TypeKind::Never)
self.table.get_lang_item("never")
}
pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Empty)
self.table.anon_type(TypeKind::Tuple(vec![]))
}
pub fn bool(&self) -> Handle {
self.primitive("bool".into())
.expect("There should be a type named bool.")
self.primitive("bool")
}
pub fn char(&self) -> Handle {
self.primitive("char".into())
.expect("There should be a type named char.")
self.primitive("char")
}
pub fn str(&self) -> Handle {
self.primitive("str".into())
.expect("There should be a type named str.")
self.primitive("str")
}
pub fn u32(&self) -> Handle {
self.primitive("u32".into())
.expect("There should be a type named u32.")
self.primitive("u32")
}
pub fn usize(&self) -> Handle {
self.primitive("usize".into())
.expect("There should be a type named usize.")
self.primitive("usize")
}
/// Creates a new inferred-integer literal
@@ -344,7 +339,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
&TypeKind::FnSig { args, rety } => {
is_generic_rec(this, args, seen) || is_generic_rec(this, rety, seen)
}
TypeKind::Empty | TypeKind::Never | TypeKind::Module => false,
TypeKind::Module => false,
}
}
is_generic_rec(self, ty, &mut HashSet::new())
@@ -358,12 +353,12 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
return ty;
};
let entry = self.table.entry(ty);
let Some(ty) = entry.ty().cloned() else {
let Some(tykind) = entry.ty().cloned() else {
return ty;
};
// TODO: Parent the deep clone into a new "monomorphs" branch of tree
match ty {
match tykind {
TypeKind::Variable => self.new_inferred(),
TypeKind::Array(h, s) => {
let ty = self.deep_clone(h);
@@ -405,6 +400,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Ref(ty))
}
TypeKind::Ptr(handle) => {
let ty = self.deep_clone(handle);
self.table.anon_type(TypeKind::Ptr(ty))
}
TypeKind::Slice(h) => {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Slice(ty))
@@ -418,7 +417,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
let rety = self.deep_clone(rety);
self.table.anon_type(TypeKind::FnSig { args, rety })
}
_ => self.table.anon_type(ty),
_ => ty,
}
}
@@ -472,8 +471,6 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
| TypeKind::Variable
| TypeKind::Adt(Adt::UnitStruct)
| TypeKind::Primitive(_)
| TypeKind::Empty
| TypeKind::Never
| TypeKind::Module => false,
}
}
@@ -490,6 +487,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
};
match (a, b) {
(TypeKind::Variable, TypeKind::Variable) => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Inferred, _) => {
self.set_instance(ah, bh);
Ok(())
@@ -602,12 +603,8 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.unify(a1, a2)?;
self.unify(r1, r2)
}
(TypeKind::Empty, TypeKind::Tuple(t)) | (TypeKind::Tuple(t), TypeKind::Empty)
if t.is_empty() =>
{
Ok(())
}
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(TypeKind::Primitive(Primitive::Never), _)
| (_, TypeKind::Primitive(Primitive::Never)) => Ok(()),
(a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)),
}

View File

@@ -78,16 +78,31 @@ impl<'a> Inference<'a> for Module {
impl<'a> Inference<'a> for Alias {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
Ok(e.empty())
let Self { name: _, from } = self;
// let this = e.by_name(name)?;
let alias = if let Some(from) = from {
TypeKind::Instance(e.infer(from)?)
} else {
TypeKind::Tuple(vec![])
};
// This node may be a lang item referring to a primitive.
let mut entry = e.at.to_entry_mut(e.table);
if entry.ty().is_some() {
return Ok(e.empty());
}
entry.set_ty(alias);
Ok(entry.id())
}
}
impl<'a> Inference<'a> for Const {
#[allow(unused)]
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Self { name, ty, init } = self;
let Self { name: _, ty, init } = self;
// Same as static
let node = e.by_name(name)?;
let node = e.at; //.by_name(name)?;
let ty = e.infer(ty)?;
let mut scope = e.at(node);
// infer body
@@ -103,7 +118,7 @@ impl<'a> Inference<'a> for Static {
#[allow(unused)]
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Static { mutable, name, ty, init } = self;
let node = e.by_name(name)?;
let node = e.at; //e.by_name(name)?;
let ty = e.infer(ty)?;
let mut scope = e.at(node);
// infer body
@@ -120,7 +135,7 @@ impl<'a> Inference<'a> for Function {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Self { name, gens, sign, bind, body } = self;
// bind name to signature
let node = e.by_name(name)?;
let node = e.at; // e.by_name(name)?;
let node = e.deep_clone(node);
let fnty = e.by_name(sign)?;
e.unify(node, fnty)?;
@@ -154,14 +169,15 @@ impl<'a> Inference<'a> for Function {
impl<'a> Inference<'a> for Enum {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Self { name, gens, variants } = self;
let node = e.by_name(name)?;
let Self { name: _, gens, variants } = self;
let node = e.at; //e.by_name(name)?;
let mut scope = e.at(node);
scope.infer(gens)?;
for variant in variants {
println!("Inferring {variant}");
let var_ty = scope.infer(variant)?;
scope.unify(var_ty, node)?;
scope.unify(node, var_ty)?;
}
Ok(node)
}
@@ -169,31 +185,46 @@ impl<'a> Inference<'a> for Enum {
impl<'a> Inference<'a> for Variant {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Self { name: _, kind: _, body } = self;
let ty = e.new_inferred();
let Self { name, kind: _, body } = self;
let node = e.by_name(name)?;
// TODO: evaluate kind
if let Some(body) = body {
let value = body.infer(e)?;
e.unify(ty, value)?;
// TODO: this doesn't work when some variants have bodies and some don't
if e.table.ty(node).is_some() {
println!("{node} has ty!");
return Ok(node);
}
Ok(ty)
match body {
Some(body) => {
let mut e = e.at(node);
let value = e.infer(body)?;
e.unify(node, value)?;
}
_ => {
e.table.entry_mut(node).set_ty(TypeKind::Inferred);
}
};
Ok(node)
}
}
impl<'a> Inference<'a> for Struct {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
Ok(e.new_inferred())
let Self { name, gens, kind: _ } = self;
let node = e.by_name(name)?;
let mut e = e.at(node);
e.infer(gens)?;
Ok(node)
}
}
impl<'a> Inference<'a> for Impl {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Self { gens: _, target, body } = self;
let Self { gens, target, body } = self;
// TODO: match gens to target gens
// gens.infer(e)?;
gens.infer(e)?;
let instance = target.infer(e)?;
let instance = e.def_usage(instance);
let mut scope = e.at(instance);
@@ -527,8 +558,9 @@ impl<'a> Inference<'a> for Binary {
e.unify(tail, bool)?;
Ok(bool)
}
Bk::RangeExc => todo!("Ranges in the type checker"),
Bk::RangeInc => todo!("Ranges in the type checker"),
// TODO: Don't return the generic form wholesale.
Bk::RangeExc => Ok(e.table.get_lang_item("range_exc")),
Bk::RangeInc => Ok(e.table.get_lang_item("range_exc")),
Bk::Shl | Bk::Shr => {
let shift_amount = e.u32();
e.unify(tail, shift_amount)?;

View File

@@ -7,7 +7,7 @@ use crate::{
type_kind::TypeKind,
};
use cl_ast::{
ItemKind, Sym,
ItemKind, Literal, Meta, MetaKind, Sym,
ast_visitor::{Visit, Walk},
};
@@ -57,6 +57,15 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
entry.inner.set_span(*span);
entry.inner.set_meta(&attrs.meta);
for Meta { name, kind } in &attrs.meta {
if let ("lang", MetaKind::Equals(Literal::String(s))) = (name.to_ref(), kind) {
if let Ok(prim) = s.parse() {
entry.inner.set_ty(TypeKind::Primitive(prim));
}
entry.inner.mark_lang_item(Sym::from(s).to_ref());
}
}
entry.visit_children(i);
if let (Some(name), child) = (entry.name, entry.inner.id()) {
@@ -158,8 +167,16 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
self.inner.set_source(Source::Variant(value));
self.set_name(*name);
self.visit(kind);
if let Some(body) = body {
self.inner.set_body(body);
match (kind, body) {
(cl_ast::StructKind::Empty, None) => {
self.inner.set_ty(TypeKind::Inferred);
}
(cl_ast::StructKind::Empty, Some(body)) => {
self.inner.set_body(body);
}
(cl_ast::StructKind::Tuple(_items), None) => {}
(cl_ast::StructKind::Struct(_struct_members), None) => {}
(_, Some(body)) => panic!("Unexpected body {body} in enum variant `{value}`"),
}
}

View File

@@ -57,7 +57,7 @@ pub struct Table<'a> {
sources: HashMap<Handle, Source<'a>>,
impl_targets: HashMap<Handle, Handle>,
anon_types: HashMap<TypeKind, Handle>,
lang_items: HashMap<Sym, Handle>,
lang_items: HashMap<&'static str, Handle>,
// --- Queues for algorithms ---
pub(crate) unchecked: Vec<Handle>,
@@ -129,10 +129,17 @@ impl<'a> Table<'a> {
self.impls.push(item);
}
pub fn mark_lang_item(&mut self, name: Sym, item: Handle) {
pub fn mark_lang_item(&mut self, name: &'static str, item: Handle) {
self.lang_items.insert(name, item);
}
pub fn get_lang_item(&self, name: &str) -> Handle {
match self.lang_items.get(name).copied() {
Some(handle) => handle,
None => todo!(),
}
}
pub fn handle_iter(&self) -> impl Iterator<Item = Handle> + use<> {
self.kinds.keys()
}

View File

@@ -40,8 +40,7 @@ impl TypeExpression for Ty {
impl TypeExpression for TyKind {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
match self {
TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
TyKind::Never => Ok(table.get_lang_item("never")),
TyKind::Infer => Ok(table.inferred_type()),
TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node),
@@ -97,10 +96,7 @@ impl TypeExpression for TySlice {
impl TypeExpression for TyTuple {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { types } = self;
let kind = match types.len() {
0 => TypeKind::Empty,
_ => TypeKind::Tuple(types.evaluate(table, node)?),
};
let kind = TypeKind::Tuple(types.evaluate(table, node)?);
Ok(table.anon_type(kind))
}
}

View File

@@ -32,10 +32,6 @@ pub enum TypeKind {
Tuple(Vec<Handle>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: Handle, rety: Handle },
/// The unit type
Empty,
/// The never type
Never,
/// An untyped module
Module,
}
@@ -70,6 +66,7 @@ pub enum Primitive {
Bool, // boolean value
Char, // Unicode codepoint
Str, // UTF-8 string
Never, // The never type
}
#[rustfmt::skip]
@@ -121,6 +118,7 @@ impl FromStr for Primitive {
"bool" => Primitive::Bool,
"char" => Primitive::Char,
"str" => Primitive::Str,
"never" => Primitive::Never,
_ => Err(())?,
})
}

View File

@@ -25,8 +25,6 @@ impl Display for TypeKind {
})(f.delimit_with("tuple (", ")"))
}
TypeKind::FnSig { args, rety } => write!(f, "fn (#{args}) -> #{rety}"),
TypeKind::Empty => f.write_str("()"),
TypeKind::Never => f.write_str("!"),
TypeKind::Module => f.write_str("mod"),
}
}
@@ -94,6 +92,7 @@ impl Display for Primitive {
Primitive::Bool => f.write_str("bool"),
Primitive::Char => f.write_str("char"),
Primitive::Str => f.write_str("str"),
Primitive::Never => f.write_str("!"),
}
}
}