[fine] Some fixes around return statements

This commit is contained in:
John Doty 2024-01-15 09:04:28 -08:00
parent 44bc072b04
commit 55749af917
3 changed files with 59 additions and 4 deletions

View file

@ -858,7 +858,7 @@ impl<'a> Semantics<'a> {
(Type::Error, _) => Some(Type::Error),
(_, Some(Type::Error)) => Some(Type::Error),
(Type::Unreachable, None) => Some(Type::Unreachable),
(Type::Unreachable, None) => Some(Type::Nothing),
(Type::Unreachable, Some(t)) => Some(t),
(t, Some(Type::Unreachable)) => Some(t),
@ -1004,13 +1004,14 @@ impl<'a> Semantics<'a> {
for p in param_list.child_trees() {
let param = &self.syntax_tree[p];
// TODO: Shouldn't this just be type_of(param)?
// TODO: Missing type expression means it's a generic function.
let parameter_type = param.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?;
parameter_types.push(Box::new(self.type_of(parameter_type)));
}
let return_type = match tree.child_tree_of_kind(self.syntax_tree, TreeKind::ReturnType) {
Some(t) => self.type_of(t.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?),
let return_type = match tree.child_of_kind(self.syntax_tree, TreeKind::ReturnType) {
Some(t) => self.type_of(t),
None => Type::Nothing,
};
let return_type = Box::new(return_type);
@ -1114,7 +1115,7 @@ pub fn check(s: &Semantics) {
TreeKind::LetStatement => {
let _ = s.environment_of(t);
}
TreeKind::ReturnStatement => {}
TreeKind::ReturnStatement => check_return_statement(s, tree),
TreeKind::ExpressionStatement
| TreeKind::LiteralExpression
| TreeKind::GroupingExpression
@ -1169,6 +1170,49 @@ fn check_function_decl(s: &Semantics, t: TreeRef, tree: &Tree) {
}
}
fn check_return_statement(s: &Semantics, tree: &Tree) {
assert_eq!(tree.kind, TreeKind::ReturnStatement);
let mut enclosing_function = tree.parent;
while let Some(fp) = enclosing_function {
let fpt = &s.syntax_tree[fp];
if fpt.kind == TreeKind::FunctionDecl {
break;
}
enclosing_function = fpt.parent;
}
let Some(enclosing_function) = enclosing_function else {
s.report_error_tree(
tree,
"a return statement can only be used inside a function",
);
return;
};
let function_type = s.type_of(enclosing_function);
match function_type {
Type::Function(_, expected_type) => {
let actual_type = if let Some(expr) = tree.nth_tree(1) {
s.type_of(expr)
} else {
Type::Error
};
if !expected_type.compatible_with(&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}`"));
}
}
Type::Error => (),
_ => s.internal_compiler_error(
Some(enclosing_function),
"a return statement in here expected this to yield a function type",
),
}
// OK this one is a little bit messed up because it reaches *up*, sorry.
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -0,0 +1,3 @@
return "skidoo!";
// @check-error: a return statement can only be used inside a function

View file

@ -0,0 +1,8 @@
fun test() -> f64 {
if false {
return "no way!";
}
23.0
}
// @check-error: callers of this function expect a value of type `f64` but this statement returns a value of type `string`