diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index cde5954c..afc21fb7 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -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::*; diff --git a/fine/tests/errors/return_statement_only_function.fine b/fine/tests/errors/return_statement_only_function.fine new file mode 100644 index 00000000..a284ae0d --- /dev/null +++ b/fine/tests/errors/return_statement_only_function.fine @@ -0,0 +1,3 @@ +return "skidoo!"; + +// @check-error: a return statement can only be used inside a function \ No newline at end of file diff --git a/fine/tests/expression/errors/return_statement_mismatch.fine b/fine/tests/expression/errors/return_statement_mismatch.fine new file mode 100644 index 00000000..b2a15313 --- /dev/null +++ b/fine/tests/expression/errors/return_statement_mismatch.fine @@ -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` \ No newline at end of file