[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>),
|
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> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue