[fine] Increasingly elaborate alternate types
This commit is contained in:
parent
5f67aa72d5
commit
8b9a69b898
1 changed files with 130 additions and 5 deletions
|
|
@ -148,7 +148,7 @@ pub enum Type {
|
|||
Object(TreeRef, Rc<str>),
|
||||
|
||||
// An alternate is one or another type.
|
||||
Alternate(Box<Type>, Box<Type>),
|
||||
Alternate(Box<[Type]>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
|
|
@ -158,6 +158,26 @@ impl Type {
|
|||
_ => 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 {
|
||||
|
|
@ -211,7 +231,70 @@ impl fmt::Display for Type {
|
|||
List(t) => write!(f, "list<{t}>"),
|
||||
Object(_, name) => write!(f, "{} instance", 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
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO: This is wrong; we because of numeric literals etc.
|
||||
match (from, to) {
|
||||
|
|
@ -1105,8 +1216,22 @@ impl<'a> Semantics<'a> {
|
|||
(Type::Unreachable, _) => true,
|
||||
|
||||
// Alternates convert if either side can convert.
|
||||
(_, Type::Alternate(left, right)) => {
|
||||
self.can_convert(from, left) || self.can_convert(from, right)
|
||||
(Type::Alternate(lts), Type::Alternate(_)) => {
|
||||
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
|
||||
|
|
@ -1948,7 +2073,7 @@ impl<'a> Semantics<'a> {
|
|||
// 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 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> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue