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

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

View File

@ -224,7 +224,6 @@ pub struct Ty {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TyKind { pub enum TyKind {
Never, Never,
Empty,
Infer, Infer,
Path(Path), Path(Path),
Array(TyArray), Array(TyArray),

View File

@ -165,7 +165,6 @@ impl Display for Function {
let Self { name, gens, sign: sign @ TyFn { args, rety }, bind, body } = self; let Self { name, gens, sign: sign @ TyFn { args, rety }, bind, body } = self;
let types = match args.kind { let types = match args.kind {
TyKind::Tuple(TyTuple { ref types }) => types.as_slice(), TyKind::Tuple(TyTuple { ref types }) => types.as_slice(),
TyKind::Empty => Default::default(),
_ => { _ => {
write!(f, "Invalid function signature: {sign}")?; write!(f, "Invalid function signature: {sign}")?;
Default::default() Default::default()
@ -191,7 +190,9 @@ impl Display for Function {
write!(f, "{arg}: {ty}")?; write!(f, "{arg}: {ty}")?;
} }
} }
if TyKind::Empty != rety.kind { if let TyKind::Tuple(TyTuple { types }) = &rety.kind
&& !types.as_slice().is_empty()
{
write!(f, " -> {rety}")? write!(f, " -> {rety}")?
} }
@ -295,7 +296,6 @@ impl Display for TyKind {
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 {
TyKind::Never => "!".fmt(f), TyKind::Never => "!".fmt(f),
TyKind::Empty => "()".fmt(f),
TyKind::Infer => "_".fmt(f), TyKind::Infer => "_".fmt(f),
TyKind::Path(v) => v.fmt(f), TyKind::Path(v) => v.fmt(f),
TyKind::Array(v) => v.fmt(f), TyKind::Array(v) => v.fmt(f),
@ -349,8 +349,10 @@ impl Display for TyFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { args, rety } = self; let Self { args, rety } = self;
write!(f, "fn {args}")?; write!(f, "fn {args}")?;
if TyKind::Empty != rety.kind { if let TyKind::Tuple(TyTuple { types }) = &rety.kind
write!(f, " -> {rety}")?; && !types.as_slice().is_empty()
{
write!(f, " -> {rety}")?
} }
Ok(()) Ok(())
} }

View File

@ -192,7 +192,7 @@ impl WeightOf for Ty {
impl WeightOf for TyKind { impl WeightOf for TyKind {
fn weight_of(&self) -> usize { fn weight_of(&self) -> usize {
match self { match self {
TyKind::Never | TyKind::Empty | TyKind::Infer => size_of_val(self), TyKind::Never | TyKind::Infer => size_of_val(self),
TyKind::Path(v) => v.weight_of(), TyKind::Path(v) => v.weight_of(),
TyKind::Array(v) => v.weight_of(), TyKind::Array(v) => v.weight_of(),
TyKind::Slice(v) => v.weight_of(), TyKind::Slice(v) => v.weight_of(),

View File

@ -531,7 +531,6 @@ pub fn or_fold_use_tree<F: Fold + ?Sized>(folder: &mut F, tree: UseTree) -> UseT
pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind { pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind {
match kind { match kind {
TyKind::Never => TyKind::Never, TyKind::Never => TyKind::Never,
TyKind::Empty => TyKind::Empty,
TyKind::Infer => TyKind::Infer, TyKind::Infer => TyKind::Infer,
TyKind::Path(p) => TyKind::Path(folder.fold_path(p)), TyKind::Path(p) => TyKind::Path(folder.fold_path(p)),
TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)), TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)),

View File

@ -369,7 +369,6 @@ impl Walk for TyKind {
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) { fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self { match self {
TyKind::Never => {} TyKind::Never => {}
TyKind::Empty => {}
TyKind::Infer => {} TyKind::Infer => {}
TyKind::Path(value) => value.visit_in(v), TyKind::Path(value) => value.visit_in(v),
TyKind::Array(value) => value.visit_in(v), TyKind::Array(value) => value.visit_in(v),

View File

@ -723,9 +723,12 @@ impl Interpret for Cast {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Cast { head, ty } = self; let Cast { head, ty } = self;
let value = head.interpret(env)?; let value = head.interpret(env)?;
if TyKind::Empty == ty.kind {
if let TyKind::Tuple(TyTuple { types }) = &ty.kind
&& types.as_slice().is_empty()
{
return Ok(ConValue::Empty); return Ok(ConValue::Empty);
}; }
let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else { let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else {
Err(Error::TypeError())? Err(Error::TypeError())?
}; };

View File

@ -464,13 +464,18 @@ impl Parse<'_> for Function {
let gens = Generics::parse(p)?; let gens = Generics::parse(p)?;
let ((bind, types), span) = delim(Spanned::<FnSig>::parse, PARENS, P)(p)?; let ((bind, types), span) = delim(Spanned::<FnSig>::parse, PARENS, P)(p)?;
let sign = TyFn { let sign = TyFn {
args: Box::new(match types.len() { args: Box::new(Ty {
0 => Ty { span, kind: TyKind::Empty, gens: Default::default() }, span,
_ => Ty { span, kind: TyKind::Tuple(TyTuple { types }), gens: Default::default() }, kind: TyKind::Tuple(TyTuple { types }),
gens: Default::default(),
}), }),
rety: Box::new(match p.match_type(TokenKind::Arrow, Parsing::TyFn) { rety: Box::new(match p.match_type(TokenKind::Arrow, Parsing::TyFn) {
Ok(_) => Ty::parse(p)?, Ok(_) => Ty::parse(p)?,
Err(_) => Ty { span, kind: TyKind::Empty, gens: Generics { vars: vec![] } }, Err(_) => Ty {
span,
kind: TyKind::Tuple(TyTuple { types: vec![] }),
gens: Generics { vars: vec![] },
},
}), }),
}; };
Ok(Function { Ok(Function {
@ -736,10 +741,7 @@ impl Parse<'_> for TyKind {
} }
TokenKind::LParen => { TokenKind::LParen => {
let out = TyTuple::parse(p)?; let out = TyTuple::parse(p)?;
match out.types.is_empty() { TyKind::Tuple(out)
true => TyKind::Empty,
false => TyKind::Tuple(out),
}
} }
TokenKind::Fn => TyFn::parse(p)?.into(), TokenKind::Fn => TyFn::parse(p)?.into(),
path_like!() => { path_like!() => {
@ -802,15 +804,18 @@ impl Parse<'_> for TyFn {
let span = Span(head, p.loc()); let span = Span(head, p.loc());
Ok(TyFn { Ok(TyFn {
args: Box::new(match args { args: Box::new(Ty {
t if t.is_empty() => Ty { kind: TyKind::Empty, span, gens: Default::default() }, kind: TyKind::Tuple(TyTuple { types: args }),
types => { span,
Ty { kind: TyKind::Tuple(TyTuple { types }), span, gens: Default::default() } gens: Default::default(),
}
}), }),
rety: Box::new(match p.match_type(TokenKind::Arrow, Parsing::TyFn) { rety: Box::new(match p.match_type(TokenKind::Arrow, Parsing::TyFn) {
Ok(_) => Ty::parse(p)?, Ok(_) => Ty::parse(p)?,
Err(_) => Ty { span, kind: TyKind::Empty, gens: Generics { vars: vec![] } }, Err(_) => Ty {
span,
kind: TyKind::Tuple(TyTuple { types: vec![] }),
gens: Generics { vars: vec![] },
},
}), }),
}) })
} }

View File

@ -275,7 +275,6 @@ pub mod clangify {
let TyFn { args, rety } = sign; let TyFn { args, rety } = sign;
let types = match &args.kind { let types = match &args.kind {
TyKind::Tuple(TyTuple { types }) => types.as_slice(), TyKind::Tuple(TyTuple { types }) => types.as_slice(),
TyKind::Empty => &[],
_ => panic!("Unsupported function args: {args}"), _ => panic!("Unsupported function args: {args}"),
}; };
let bind = match bind { let bind = match bind {
@ -472,7 +471,6 @@ pub mod clangify {
TyKind::Fn(TyFn { args, rety }) => { TyKind::Fn(TyFn { args, rety }) => {
y.nest("(").p(rety).p(" *").p(mutable).p(name).p(")("); y.nest("(").p(rety).p(" *").p(mutable).p(name).p(")(");
match &args.kind { match &args.kind {
TyKind::Empty => {}
TyKind::Tuple(TyTuple { types }) => { TyKind::Tuple(TyTuple { types }) => {
for (idx, ty) in types.iter().enumerate() { for (idx, ty) in types.iter().enumerate() {
if idx > 0 { if idx > 0 {
@ -799,7 +797,6 @@ pub mod clangify {
fn print(&self, y: &mut CLangifier) { fn print(&self, y: &mut CLangifier) {
match self { match self {
TyKind::Never => y.p("Never"), TyKind::Never => y.p("Never"),
TyKind::Empty => y.p("Empty"),
TyKind::Infer => y.p("auto"), TyKind::Infer => y.p("auto"),
TyKind::Path(t) => y.p(t), TyKind::Path(t) => y.p(t),
TyKind::Tuple(t) => y.p(t), TyKind::Tuple(t) => y.p(t),
@ -880,7 +877,6 @@ pub mod clangify {
// TODO: function pointer syntax // TODO: function pointer syntax
y.nest("(").p(rety).p(" *)("); y.nest("(").p(rety).p(" *)(");
match &args.kind { match &args.kind {
TyKind::Empty => y,
TyKind::Tuple(TyTuple { types }) => { TyKind::Tuple(TyTuple { types }) => {
for (idx, ty) in types.iter().enumerate() { for (idx, ty) in types.iter().enumerate() {
if idx > 0 { if idx > 0 {

View File

@ -649,7 +649,6 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
TyKind::Never => y.value("Never"), TyKind::Never => y.value("Never"),
TyKind::Empty => y.value("Empty"),
TyKind::Infer => y.value("_"), TyKind::Infer => y.value("_"),
TyKind::Path(t) => y.yaml(t), TyKind::Path(t) => y.yaml(t),
TyKind::Tuple(t) => y.yaml(t), TyKind::Tuple(t) => y.yaml(t),
@ -713,8 +712,7 @@ pub mod yamlify {
impl Yamlify for TyPtr { impl Yamlify for TyPtr {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { to } = self; let Self { to } = self;
y.key("TyPtr") y.key("TyPtr").pair("to", to);
.pair("to", to);
} }
} }
impl Yamlify for TyFn { impl Yamlify for TyFn {

View File

@ -205,4 +205,8 @@ impl<'t, 'a> EntryMut<'t, 'a> {
pub fn mark_impl_item(&mut self) { pub fn mark_impl_item(&mut self) {
self.table.mark_impl_item(self.id) 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!(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::Module => write!(f, "module?"),
} }
} else { } else {

View File

@ -17,8 +17,6 @@ pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
}; };
match source { 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::Variant(v) => cat_variant(table, node, v)?,
Source::Struct(s) => cat_struct(table, node, s)?, Source::Struct(s) => cat_struct(table, node, s)?,
Source::Const(c) => cat_const(table, node, c)?, 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::Function(f) => cat_function(table, node, f)?,
Source::Local(l) => cat_local(table, node, l)?, Source::Local(l) => cat_local(table, node, l)?,
Source::Impl(i) => cat_impl(table, node, i)?, Source::Impl(i) => cat_impl(table, node, i)?,
_ => {} // Source::Alias(_) => {table.mark_unchecked(node)},
} _ => return Ok(()),
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(());
}
}
} }
Ok(()) Ok(())
} }
@ -49,20 +34,6 @@ fn parent(table: &Table, node: Handle) -> Handle {
table.parent(node).copied().unwrap_or(node) 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<()> { fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> {
let Struct { name: _, gens: _, kind } = s; let Struct { name: _, gens: _, kind } = s;
// TODO: Generics // TODO: Generics
@ -99,7 +70,6 @@ fn cat_member(
fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> { fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> {
let Enum { name: _, gens: _, variants: _ } = e; let Enum { name: _, gens: _, variants: _ } = e;
// table.set_ty(node, kind); // table.set_ty(node, kind);
Ok(()) 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<()> { fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatResult<()> {
let Variant { name, kind, body } = v; let Variant { name, kind, body } = v;
let parent = table.parent(node).copied().unwrap_or(table.root()); let parent = table.parent(node).copied().unwrap_or(table.root());
match (kind, body) { match (kind) {
(StructKind::Empty, None) => { (StructKind::Empty) => Ok(()),
table.set_ty(node, TypeKind::Adt(Adt::UnitStruct)); (StructKind::Empty) => Ok(()),
Ok(()) (StructKind::Tuple(ty)) => {
}
(StructKind::Empty, Some(c)) => {
table.set_body(node, c);
table.set_ty(node, TypeKind::Adt(Adt::UnitStruct));
Ok(())
}
(StructKind::Tuple(ty), None) => {
let ty = TypeKind::Adt(Adt::TupleStruct( let ty = TypeKind::Adt(Adt::TupleStruct(
ty.iter() ty.iter()
.map(|ty| ty.evaluate(table, node).map(|ty| (Visibility::Public, ty))) .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); table.set_ty(node, ty);
Ok(()) Ok(())
} }
(StructKind::Struct(members), None) => { (StructKind::Struct(members)) => {
let mut out = vec![]; let mut out = vec![];
for StructMember { vis, name, ty } in members { for StructMember { vis, name, ty } in members {
let ty = ty.evaluate(table, node)?; 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))); table.set_ty(node, TypeKind::Adt(Adt::Struct(out)));
Ok(()) 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::Module(v) => v.infer(&mut eng),
Source::Alias(v) => v.infer(&mut eng), Source::Alias(v) => v.infer(&mut eng),
Source::Enum(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::Struct(v) => v.infer(&mut eng),
Source::Const(v) => v.infer(&mut eng), Source::Const(v) => v.infer(&mut eng),
Source::Static(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. /// 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 // 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 { pub fn never(&mut self) -> Handle {
self.table.anon_type(TypeKind::Never) self.table.get_lang_item("never")
} }
pub fn empty(&mut self) -> Handle { pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Empty) self.table.anon_type(TypeKind::Tuple(vec![]))
} }
pub fn bool(&self) -> Handle { pub fn bool(&self) -> Handle {
self.primitive("bool".into()) self.primitive("bool")
.expect("There should be a type named bool.")
} }
pub fn char(&self) -> Handle { pub fn char(&self) -> Handle {
self.primitive("char".into()) self.primitive("char")
.expect("There should be a type named char.")
} }
pub fn str(&self) -> Handle { pub fn str(&self) -> Handle {
self.primitive("str".into()) self.primitive("str")
.expect("There should be a type named str.")
} }
pub fn u32(&self) -> Handle { pub fn u32(&self) -> Handle {
self.primitive("u32".into()) self.primitive("u32")
.expect("There should be a type named u32.")
} }
pub fn usize(&self) -> Handle { pub fn usize(&self) -> Handle {
self.primitive("usize".into()) self.primitive("usize")
.expect("There should be a type named usize.")
} }
/// Creates a new inferred-integer literal /// Creates a new inferred-integer literal
@ -344,7 +339,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
&TypeKind::FnSig { args, rety } => { &TypeKind::FnSig { args, rety } => {
is_generic_rec(this, args, seen) || is_generic_rec(this, rety, seen) 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()) is_generic_rec(self, ty, &mut HashSet::new())
@ -358,12 +353,12 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
return ty; return ty;
}; };
let entry = self.table.entry(ty); let entry = self.table.entry(ty);
let Some(ty) = entry.ty().cloned() else { let Some(tykind) = entry.ty().cloned() else {
return ty; return ty;
}; };
// TODO: Parent the deep clone into a new "monomorphs" branch of tree // TODO: Parent the deep clone into a new "monomorphs" branch of tree
match ty { match tykind {
TypeKind::Variable => self.new_inferred(), TypeKind::Variable => self.new_inferred(),
TypeKind::Array(h, s) => { TypeKind::Array(h, s) => {
let ty = self.deep_clone(h); let ty = self.deep_clone(h);
@ -405,6 +400,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
let ty = self.deep_clone(h); let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Ref(ty)) 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) => { TypeKind::Slice(h) => {
let ty = self.deep_clone(h); let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Slice(ty)) self.table.anon_type(TypeKind::Slice(ty))
@ -418,7 +417,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
let rety = self.deep_clone(rety); let rety = self.deep_clone(rety);
self.table.anon_type(TypeKind::FnSig { args, 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::Variable
| TypeKind::Adt(Adt::UnitStruct) | TypeKind::Adt(Adt::UnitStruct)
| TypeKind::Primitive(_) | TypeKind::Primitive(_)
| TypeKind::Empty
| TypeKind::Never
| TypeKind::Module => false, | TypeKind::Module => false,
} }
} }
@ -490,6 +487,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
}; };
match (a, b) { match (a, b) {
(TypeKind::Variable, TypeKind::Variable) => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Inferred, _) => { (TypeKind::Inferred, _) => {
self.set_instance(ah, bh); self.set_instance(ah, bh);
Ok(()) Ok(())
@ -602,12 +603,8 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.unify(a1, a2)?; self.unify(a1, a2)?;
self.unify(r1, r2) self.unify(r1, r2)
} }
(TypeKind::Empty, TypeKind::Tuple(t)) | (TypeKind::Tuple(t), TypeKind::Empty) (TypeKind::Primitive(Primitive::Never), _)
if t.is_empty() => | (_, TypeKind::Primitive(Primitive::Never)) => Ok(()),
{
Ok(())
}
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(a, b) if a == b => Ok(()), (a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)), _ => Err(InferenceError::Mismatch(ah, bh)),
} }

View File

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

View File

@ -7,7 +7,7 @@ use crate::{
type_kind::TypeKind, type_kind::TypeKind,
}; };
use cl_ast::{ use cl_ast::{
ItemKind, Sym, ItemKind, Literal, Meta, MetaKind, Sym,
ast_visitor::{Visit, Walk}, ast_visitor::{Visit, Walk},
}; };
@ -57,6 +57,15 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
entry.inner.set_span(*span); entry.inner.set_span(*span);
entry.inner.set_meta(&attrs.meta); 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); entry.visit_children(i);
if let (Some(name), child) = (entry.name, entry.inner.id()) { 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.inner.set_source(Source::Variant(value));
self.set_name(*name); self.set_name(*name);
self.visit(kind); self.visit(kind);
if let Some(body) = body { match (kind, body) {
self.inner.set_body(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>>, sources: HashMap<Handle, Source<'a>>,
impl_targets: HashMap<Handle, Handle>, impl_targets: HashMap<Handle, Handle>,
anon_types: HashMap<TypeKind, Handle>, anon_types: HashMap<TypeKind, Handle>,
lang_items: HashMap<Sym, Handle>, lang_items: HashMap<&'static str, Handle>,
// --- Queues for algorithms --- // --- Queues for algorithms ---
pub(crate) unchecked: Vec<Handle>, pub(crate) unchecked: Vec<Handle>,
@ -129,10 +129,17 @@ impl<'a> Table<'a> {
self.impls.push(item); 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); 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<> { pub fn handle_iter(&self) -> impl Iterator<Item = Handle> + use<> {
self.kinds.keys() self.kinds.keys()
} }

View File

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

View File

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

View File

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

View File

@ -1,5 +1,8 @@
//! Implements format string evaluation in weak Conlang
fn f(__fmt: str) -> str {
/// Formats a string
fn f(__fmt: &str) -> &str {
let __out = ""; let __out = "";
let __expr = ""; let __expr = "";
let __label = ""; let __label = "";

View File

@ -4,13 +4,11 @@
// These two functions shouldn't actually be polymorphic, but // These two functions shouldn't actually be polymorphic, but
// the AST interpreter doesn't know about type annotations // the AST interpreter doesn't know about type annotations
// or operator overloading. // or operator overloading.
#[generic("T")] pub fn max<T>(a: T, b: T) -> T {
pub fn max(a: T, b: T) -> T {
(if a < b { b } else { a }) (if a < b { b } else { a })
} }
#[generic("T")] pub fn min<T>(a: T, b: T) -> T {
pub fn min(a: T, b: T) -> T {
(if a > b { b } else { a }) (if a > b { b } else { a })
} }

View File

@ -13,11 +13,11 @@ pub fn lfsr_next() {
} }
/// Returns a pseudorandom byte /// Returns a pseudorandom byte
pub fn rand() -> u8 { pub fn rand() -> u64 {
for _ in 0..8 { for _ in 0..8 {
lfsr_next() lfsr_next()
} };
state & 0xff (state & 0xff) as u64
} }
// Prints a maze out of diagonal box drawing characters, ['', ''] // Prints a maze out of diagonal box drawing characters, ['', '']

View File

@ -25,5 +25,7 @@ pub mod result;
pub mod range; pub mod range;
pub mod never;
// #[cfg("test")] // #[cfg("test")]
// mod test; // mod test;

4
stdlib/std/never.cl Normal file
View File

@ -0,0 +1,4 @@
//! Never: the return type of an infinite loop
#[lang = "never"]
type Never = !;

View File

@ -1,6 +1,7 @@
//! The optional type, representing the presence or absence of a thing. //! The optional type, representing the presence or absence of a thing.
use super::preamble::*; use super::preamble::*;
#[lang = "option"]
pub enum Option<T> { pub enum Option<T> {
Some(T), Some(T),
None, None,

View File

@ -1,6 +1,7 @@
//! The Result type, indicating a fallible operation. //! The Result type, indicating a fallible operation.
use super::preamble::*; use super::preamble::*;
#[lang = "result"]
pub enum Result<T, E> { pub enum Result<T, E> {
Ok(T), Ok(T),
Err(E), Err(E),