[fine] Name the type comparison method properly
This commit is contained in:
parent
e66ffbb5ae
commit
36ed61b8bb
2 changed files with 43 additions and 32 deletions
|
|
@ -449,7 +449,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
|||
}
|
||||
TokenKind::EqualEqual => {
|
||||
compile_simple_binary_expression(c, tr, |c, arg_type| {
|
||||
if c.semantics.type_compat(&arg_type, &Type::Nothing) {
|
||||
if c.semantics.can_convert(&arg_type, &Type::Nothing) {
|
||||
c.push(Instruction::Discard);
|
||||
c.push(Instruction::Discard);
|
||||
Instruction::PushTrue
|
||||
|
|
|
|||
|
|
@ -957,34 +957,31 @@ impl<'a> Semantics<'a> {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn type_compat(&self, a: &Type, b: &Type) -> bool {
|
||||
// TODO: Convert this into "can become" or something.
|
||||
pub fn can_convert(&self, from: &Type, to: &Type) -> bool {
|
||||
// TODO: This is wrong; we because of numeric literals etc.
|
||||
match (a, b) {
|
||||
match (from, to) {
|
||||
(Type::F64, Type::F64) => true,
|
||||
(Type::String, Type::String) => true,
|
||||
(Type::Bool, Type::Bool) => true,
|
||||
(Type::Unreachable, Type::Unreachable) => true,
|
||||
(Type::Nothing, Type::Nothing) => true,
|
||||
|
||||
(Type::List(a), Type::List(b)) => self.type_compat(a, b),
|
||||
(Type::Function(a_args, a_ret), Type::Function(b_args, b_ret)) => {
|
||||
a_args.len() == b_args.len()
|
||||
&& self.type_compat(a_ret, b_ret)
|
||||
&& a_args
|
||||
(Type::List(from), Type::List(to)) => self.can_convert(from, to),
|
||||
(Type::Function(from_args, from_ret), Type::Function(to_args, to_ret)) => {
|
||||
from_args.len() == from_args.len()
|
||||
&& self.can_convert(to_ret, from_ret)
|
||||
&& from_args
|
||||
.iter()
|
||||
.zip(b_args.iter())
|
||||
.all(|(a, b)| self.type_compat(a, b))
|
||||
.zip(to_args.iter())
|
||||
.all(|(from, to)| self.can_convert(from, to))
|
||||
}
|
||||
|
||||
(Type::Object(ca, _), Type::Object(cb, _)) => {
|
||||
// TODO: If we were doing structural comparisons here...
|
||||
// maybe? MAYBE?
|
||||
(Type::Object(c_from, _), Type::Object(c_to, _)) => {
|
||||
// TODO: Structural comparisons. All that matters is that
|
||||
// c_to has a subset of fields and methods, and the
|
||||
// fields and methods are all compatible.
|
||||
//
|
||||
// Like if this is directional we can look for field
|
||||
// subsets { ..:int, ..:int } can be { ..:int } etc.
|
||||
//
|
||||
ca == cb
|
||||
c_from == c_to
|
||||
}
|
||||
|
||||
// Avoid introducing more errors
|
||||
|
|
@ -1203,7 +1200,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
if left_type.is_error() || right_type.is_error() {
|
||||
Some(Type::Error)
|
||||
} else if self.type_compat(&left_type, &right_type) {
|
||||
} else if self.can_convert(&right_type, &left_type) {
|
||||
Some(Type::Assignment(Box::new(left_type)))
|
||||
} else {
|
||||
self.report_error(
|
||||
|
|
@ -1349,7 +1346,7 @@ impl<'a> Semantics<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
if !self.type_compat(&cond_type, &Type::Bool) {
|
||||
if !self.can_convert(&cond_type, &Type::Bool) {
|
||||
if !cond_type.is_error() {
|
||||
self.report_error_tree_ref(cond_tree, "conditions must yield a boolean");
|
||||
}
|
||||
|
|
@ -1365,14 +1362,20 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
(then_type, else_type) => {
|
||||
let else_type = else_type.unwrap_or(Type::Nothing);
|
||||
if !self.type_compat(&then_type, &else_type) {
|
||||
if self.can_convert(&then_type, &else_type) {
|
||||
Some(else_type)
|
||||
// I know, I know, as of this writing there is no real
|
||||
// reason to ever check this, because this relationship
|
||||
// is symmetrical. But someday it won't be, and you'll be
|
||||
// glad I had the forethought to be thorough.
|
||||
} else if self.can_convert(&else_type, &then_type) {
|
||||
Some(then_type)
|
||||
} else {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("the type of the 'then' branch ('{then_type}') must match the type of the 'else' branch ('{else_type}')"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
} else {
|
||||
Some(then_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1416,7 +1419,9 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
|
||||
if !self.type_compat(&a, p) {
|
||||
// a here is the type of the argument expression; p is
|
||||
// the declared type of the parameter.
|
||||
if !self.can_convert(&a, p) {
|
||||
self.report_error_tree_ref(
|
||||
*t,
|
||||
format!(
|
||||
|
|
@ -1446,7 +1451,9 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
|
||||
if !self.type_compat(&a, p) {
|
||||
// a here is the type of the argument expression; p is
|
||||
// the declared type of the parameter.
|
||||
if !self.can_convert(&a, p) {
|
||||
self.report_error_tree_ref(
|
||||
*t,
|
||||
format!(
|
||||
|
|
@ -1636,10 +1643,15 @@ impl<'a> Semantics<'a> {
|
|||
Some(child_type)
|
||||
} else if child_type.is_error() {
|
||||
Some(list_type)
|
||||
} else if self.can_convert(&child_type, &list_type) {
|
||||
Some(list_type)
|
||||
} else if self.can_convert(&list_type, &child_type) {
|
||||
Some(child_type)
|
||||
} else {
|
||||
if !self.type_compat(&child_type, &list_type) {
|
||||
self.report_error_tree_ref(ct, format!("list element of type {child_type} is not compatible with the list type {list_type}"));
|
||||
}
|
||||
// Even if there is some incompatibility we stick
|
||||
// with the list type, preferring to guess what was
|
||||
// intended rather than just error out.
|
||||
self.report_error_tree_ref(ct, format!("list element of type {child_type} is not compatible with the list type {list_type}"));
|
||||
Some(list_type)
|
||||
}
|
||||
}
|
||||
|
|
@ -1647,7 +1659,6 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
let element_type = element_type.unwrap_or_else(|| Type::TypeVariable(t));
|
||||
|
||||
Some(Type::List(Box::new(element_type)))
|
||||
}
|
||||
|
||||
|
|
@ -1878,7 +1889,7 @@ fn check_function_decl(s: &Semantics, t: TreeRef, tree: &Tree) {
|
|||
|
||||
if let Some(body) = tree.child_of_kind(s.syntax_tree, TreeKind::Block) {
|
||||
let body_type = s.type_of(body);
|
||||
if !s.type_compat(&body_type, &return_type) {
|
||||
if !s.can_convert(&body_type, &return_type) {
|
||||
// Just work very hard to get an appropriate error span.
|
||||
let (start, end) = return_type_tree
|
||||
.map(|t| {
|
||||
|
|
@ -1928,7 +1939,7 @@ fn check_return_statement(s: &Semantics, tree: &Tree) {
|
|||
Type::Error
|
||||
};
|
||||
|
||||
if !s.type_compat(&expected_type, &actual_type) {
|
||||
if !s.can_convert(&actual_type, &expected_type) {
|
||||
s.report_error_tree(tree, format!("callers of this function expect a value of type '{expected_type}' but this statement returns a value of type '{actual_type}'"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1974,7 +1985,7 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
|
|||
// Check individual bindings...
|
||||
for f in class.fields.iter() {
|
||||
if let Some((field_tree, expr_type)) = field_bindings.get(&*f.name) {
|
||||
if !s.type_compat(&f.field_type, expr_type) {
|
||||
if !s.can_convert(expr_type, &f.field_type) {
|
||||
s.report_error_tree_ref(
|
||||
*field_tree,
|
||||
format!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue