[fine] Increasingly elaborate alternate types

This commit is contained in:
John Doty 2024-02-07 07:45:30 -08:00
parent 5f67aa72d5
commit 8b9a69b898

View file

@ -148,7 +148,7 @@ pub enum Type {
Object(TreeRef, Rc<str>), Object(TreeRef, Rc<str>),
// An alternate is one or another type. // An alternate is one or another type.
Alternate(Box<Type>, Box<Type>), Alternate(Box<[Type]>),
} }
impl Type { impl Type {
@ -158,6 +158,26 @@ impl Type {
_ => false, _ => false,
} }
} }
fn discriminant_number(&self) -> i8 {
match self {
Type::Error => 0,
Type::Unreachable => 1,
Type::Assignment(_) => 2,
Type::TypeVariable(_) => 3,
Type::Nothing => 4,
Type::F64 => 5,
Type::I64 => 6,
Type::String => 7,
Type::Bool => 8,
Type::Function(_, _) => 9,
Type::Method(_, _, _) => 10,
Type::List(_) => 11,
Type::Class(_, _) => 12,
Type::Object(_, _) => 13,
Type::Alternate(_) => 14,
}
}
} }
impl fmt::Debug for Type { impl fmt::Debug for Type {
@ -211,7 +231,70 @@ impl fmt::Display for Type {
List(t) => write!(f, "list<{t}>"), List(t) => write!(f, "list<{t}>"),
Object(_, name) => write!(f, "{} instance", name), Object(_, name) => write!(f, "{} instance", name),
Class(_, name) => write!(f, "class {}", name), Class(_, name) => write!(f, "class {}", name),
Alternate(l, r) => write!(f, "{l} or {r}"), Alternate(ts) => {
let mut first = true;
for t in ts.iter() {
if !first {
write!(f, " or ")?;
}
write!(f, "{t}")?;
first = false;
}
Ok(())
}
}
}
}
impl std::cmp::PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl std::cmp::Eq for Type {}
impl std::cmp::PartialOrd for Type {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for Type {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
let li = self.discriminant_number();
let ri = other.discriminant_number();
if li < ri {
Ordering::Less
} else if li > ri {
Ordering::Greater
} else {
match (self, other) {
(Type::Assignment(x), Type::Assignment(y)) => x.cmp(y),
// NOTE: This is wrong! Type variables *cannot* be compared
// like this without binding information.
(Type::TypeVariable(x), Type::TypeVariable(y)) => x.index().cmp(&y.index()),
(Type::Function(la, lr), Type::Function(ra, rr)) => {
let lv = (la, lr);
let rv = (ra, rr);
lv.cmp(&rv)
}
(Type::Method(lo, la, lr), Type::Method(ro, ra, rr)) => {
let lv = (lo, la, lr);
let rv = (ro, ra, rr);
lv.cmp(&rv)
}
(Type::List(x), Type::List(y)) => x.cmp(y),
(Type::Class(lt, _), Type::Class(rt, _)) => lt.index().cmp(&rt.index()),
(Type::Object(lt, _), Type::Object(rt, _)) => lt.index().cmp(&rt.index()),
(Type::Alternate(ll), Type::Alternate(rr)) => ll.cmp(rr),
_ => Ordering::Equal,
}
} }
} }
} }
@ -1070,6 +1153,34 @@ impl<'a> Semantics<'a> {
result result
} }
pub fn build_alternate(&self, left: &Type, right: &Type) -> Type {
if left.is_error() {
left.clone()
} else if right.is_error() {
right.clone()
} else if self.can_convert(left, right) {
right.clone()
} else if self.can_convert(right, left) {
left.clone()
} else {
let mut types: Vec<Type> = Vec::new();
if let Type::Alternate(ts) = left {
types.extend_from_slice(&*ts);
} else {
types.push(left.clone());
}
if let Type::Alternate(ts) = right {
types.extend_from_slice(&*ts);
} else {
types.push(right.clone());
}
types.sort();
types.dedup();
Type::Alternate(types.into())
}
}
pub fn can_convert(&self, from: &Type, to: &Type) -> bool { pub fn can_convert(&self, from: &Type, to: &Type) -> bool {
// TODO: This is wrong; we because of numeric literals etc. // TODO: This is wrong; we because of numeric literals etc.
match (from, to) { match (from, to) {
@ -1105,8 +1216,22 @@ impl<'a> Semantics<'a> {
(Type::Unreachable, _) => true, (Type::Unreachable, _) => true,
// Alternates convert if either side can convert. // Alternates convert if either side can convert.
(_, Type::Alternate(left, right)) => { (Type::Alternate(lts), Type::Alternate(_)) => {
self.can_convert(from, left) || self.can_convert(from, right) for lt in lts.iter() {
if !self.can_convert(lt, to) {
return false;
}
}
true
}
(_, Type::Alternate(rts)) => {
for rt in rts.iter() {
if self.can_convert(from, rt) {
return true;
}
}
false
} }
// TODO: Unification on type variables! :D // TODO: Unification on type variables! :D
@ -1948,7 +2073,7 @@ impl<'a> Semantics<'a> {
// TODO: IDEA: nth_tree returns a bogus tree if not a tree, stop returning Option? // TODO: IDEA: nth_tree returns a bogus tree if not a tree, stop returning Option?
let left = self.type_of(tree.nth_tree(0)?); let left = self.type_of(tree.nth_tree(0)?);
let right = self.type_of(tree.nth_tree(2)?); let right = self.type_of(tree.nth_tree(2)?);
Some(Type::Alternate(Box::new(left), Box::new(right))) Some(self.build_alternate(&left, &right))
} }
fn type_of_match_expression(&self, tree: &Tree) -> Option<Type> { fn type_of_match_expression(&self, tree: &Tree) -> Option<Type> {