[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 => {
|
TokenKind::EqualEqual => {
|
||||||
compile_simple_binary_expression(c, tr, |c, arg_type| {
|
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);
|
||||||
c.push(Instruction::Discard);
|
c.push(Instruction::Discard);
|
||||||
Instruction::PushTrue
|
Instruction::PushTrue
|
||||||
|
|
|
||||||
|
|
@ -957,34 +957,31 @@ impl<'a> Semantics<'a> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_compat(&self, a: &Type, b: &Type) -> bool {
|
pub fn can_convert(&self, from: &Type, to: &Type) -> bool {
|
||||||
// TODO: Convert this into "can become" or something.
|
|
||||||
// TODO: This is wrong; we because of numeric literals etc.
|
// TODO: This is wrong; we because of numeric literals etc.
|
||||||
match (a, b) {
|
match (from, to) {
|
||||||
(Type::F64, Type::F64) => true,
|
(Type::F64, Type::F64) => true,
|
||||||
(Type::String, Type::String) => true,
|
(Type::String, Type::String) => true,
|
||||||
(Type::Bool, Type::Bool) => true,
|
(Type::Bool, Type::Bool) => true,
|
||||||
(Type::Unreachable, Type::Unreachable) => true,
|
(Type::Unreachable, Type::Unreachable) => true,
|
||||||
(Type::Nothing, Type::Nothing) => true,
|
(Type::Nothing, Type::Nothing) => true,
|
||||||
|
|
||||||
(Type::List(a), Type::List(b)) => self.type_compat(a, b),
|
(Type::List(from), Type::List(to)) => self.can_convert(from, to),
|
||||||
(Type::Function(a_args, a_ret), Type::Function(b_args, b_ret)) => {
|
(Type::Function(from_args, from_ret), Type::Function(to_args, to_ret)) => {
|
||||||
a_args.len() == b_args.len()
|
from_args.len() == from_args.len()
|
||||||
&& self.type_compat(a_ret, b_ret)
|
&& self.can_convert(to_ret, from_ret)
|
||||||
&& a_args
|
&& from_args
|
||||||
.iter()
|
.iter()
|
||||||
.zip(b_args.iter())
|
.zip(to_args.iter())
|
||||||
.all(|(a, b)| self.type_compat(a, b))
|
.all(|(from, to)| self.can_convert(from, to))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::Object(ca, _), Type::Object(cb, _)) => {
|
(Type::Object(c_from, _), Type::Object(c_to, _)) => {
|
||||||
// TODO: If we were doing structural comparisons here...
|
// TODO: Structural comparisons. All that matters is that
|
||||||
// maybe? MAYBE?
|
// 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
|
c_from == c_to
|
||||||
// subsets { ..:int, ..:int } can be { ..:int } etc.
|
|
||||||
//
|
|
||||||
ca == cb
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid introducing more errors
|
// Avoid introducing more errors
|
||||||
|
|
@ -1203,7 +1200,7 @@ impl<'a> Semantics<'a> {
|
||||||
|
|
||||||
if left_type.is_error() || right_type.is_error() {
|
if left_type.is_error() || right_type.is_error() {
|
||||||
Some(Type::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)))
|
Some(Type::Assignment(Box::new(left_type)))
|
||||||
} else {
|
} else {
|
||||||
self.report_error(
|
self.report_error(
|
||||||
|
|
@ -1349,7 +1346,7 @@ impl<'a> Semantics<'a> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self.type_compat(&cond_type, &Type::Bool) {
|
if !self.can_convert(&cond_type, &Type::Bool) {
|
||||||
if !cond_type.is_error() {
|
if !cond_type.is_error() {
|
||||||
self.report_error_tree_ref(cond_tree, "conditions must yield a boolean");
|
self.report_error_tree_ref(cond_tree, "conditions must yield a boolean");
|
||||||
}
|
}
|
||||||
|
|
@ -1365,14 +1362,20 @@ impl<'a> Semantics<'a> {
|
||||||
|
|
||||||
(then_type, else_type) => {
|
(then_type, else_type) => {
|
||||||
let else_type = else_type.unwrap_or(Type::Nothing);
|
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(
|
self.report_error_tree(
|
||||||
tree,
|
tree,
|
||||||
format!("the type of the 'then' branch ('{then_type}') must match the type of the 'else' branch ('{else_type}')"),
|
format!("the type of the 'then' branch ('{then_type}') must match the type of the 'else' branch ('{else_type}')"),
|
||||||
);
|
);
|
||||||
Some(Type::Error)
|
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() {
|
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(
|
self.report_error_tree_ref(
|
||||||
*t,
|
*t,
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -1446,7 +1451,9 @@ impl<'a> Semantics<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
|
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(
|
self.report_error_tree_ref(
|
||||||
*t,
|
*t,
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -1636,10 +1643,15 @@ impl<'a> Semantics<'a> {
|
||||||
Some(child_type)
|
Some(child_type)
|
||||||
} else if child_type.is_error() {
|
} else if child_type.is_error() {
|
||||||
Some(list_type)
|
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 {
|
} else {
|
||||||
if !self.type_compat(&child_type, &list_type) {
|
// Even if there is some incompatibility we stick
|
||||||
self.report_error_tree_ref(ct, format!("list element of type {child_type} is not compatible with the list type {list_type}"));
|
// 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)
|
Some(list_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1647,7 +1659,6 @@ impl<'a> Semantics<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let element_type = element_type.unwrap_or_else(|| Type::TypeVariable(t));
|
let element_type = element_type.unwrap_or_else(|| Type::TypeVariable(t));
|
||||||
|
|
||||||
Some(Type::List(Box::new(element_type)))
|
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) {
|
if let Some(body) = tree.child_of_kind(s.syntax_tree, TreeKind::Block) {
|
||||||
let body_type = s.type_of(body);
|
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.
|
// Just work very hard to get an appropriate error span.
|
||||||
let (start, end) = return_type_tree
|
let (start, end) = return_type_tree
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
|
|
@ -1928,7 +1939,7 @@ fn check_return_statement(s: &Semantics, tree: &Tree) {
|
||||||
Type::Error
|
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}'"));
|
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...
|
// Check individual bindings...
|
||||||
for f in class.fields.iter() {
|
for f in class.fields.iter() {
|
||||||
if let Some((field_tree, expr_type)) = field_bindings.get(&*f.name) {
|
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(
|
s.report_error_tree_ref(
|
||||||
*field_tree,
|
*field_tree,
|
||||||
format!(
|
format!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue