Type checking errors on multiple returns

This commit is contained in:
Pranav Gaddamadugu 2022-07-24 21:32:36 -07:00
parent 0194e09b41
commit 9dd45c3620
8 changed files with 125 additions and 1 deletions

View File

@ -22,6 +22,23 @@ use leo_errors::TypeCheckerError;
use std::cell::RefCell;
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_statement(&mut self, input: &'a Statement) {
if self.has_return {
self.emit_err(TypeCheckerError::unreachable_code_after_return(input.span()));
return;
}
match input {
Statement::Return(stmt) => self.visit_return(stmt),
Statement::Definition(stmt) => self.visit_definition(stmt),
Statement::Assign(stmt) => self.visit_assign(stmt),
Statement::Conditional(stmt) => self.visit_conditional(stmt),
Statement::Iteration(stmt) => self.visit_iteration(stmt),
Statement::Console(stmt) => self.visit_console(stmt),
Statement::Block(stmt) => self.visit_block(stmt),
}
}
fn visit_return(&mut self, input: &'a ReturnStatement) {
// we can safely unwrap all self.parent instances because
// statements should always have some parent block
@ -94,10 +111,30 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_conditional(&mut self, input: &'a ConditionalStatement) {
self.visit_expression(&input.condition, &Some(Type::Boolean));
let mut if_block_has_return = false;
let mut else_block_has_return = false;
// Set the `has_return` flag for the if-block.
let previous_has_return = core::mem::replace(&mut self.has_return, if_block_has_return);
self.visit_block(&input.block);
// Store the `has_return` flag for the if-block.
if_block_has_return = self.has_return;
if let Some(s) = input.next.as_ref() {
self.visit_statement(s)
// Set the `has_return` flag for the else-block.
self.has_return = else_block_has_return;
self.visit_statement(s);
// Store the `has_return` flag for the else-block.
else_block_has_return = self.has_return;
}
// Restore the previous `has_return` flag.
self.has_return = previous_has_return || (if_block_has_return && else_block_has_return);
}
fn visit_iteration(&mut self, input: &'a IterationStatement) {
@ -124,8 +161,16 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
self.handler.emit_err(err);
}
let prior_has_return = core::mem::take(&mut self.has_return);
input.block.statements.iter().for_each(|s| self.visit_statement(s));
if self.has_return {
self.emit_err(TypeCheckerError::loop_body_contains_return(input.span()));
}
self.has_return = prior_has_return;
// Restore the previous scope.
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table

View File

@ -259,4 +259,18 @@ create_messages!(
msg: format!("Tuples are only allowed as function return types."),
help: None,
}
@formatted
unreachable_code_after_return {
args: (),
msg: format!("Cannot reach the following statement."),
help: Some("Remove the unreachable code.".to_string()),
}
@formatted
loop_body_contains_return {
args: (),
msg: format!("Loop body contains a return statement or always returns."),
help: Some("Remove the code in the loop body that always returns.".to_string()),
}
);

View File

@ -0,0 +1,22 @@
/*
namespace: Compile
expectation: Fail
input_file: inputs/u32_3.in
*/
function main(x: u32) -> bool {
for i: u32 in 0u32..9u32 {
return false;
}
for i: u32 in 0u32..9u32 {
if (x == 0u32) {
return false;
} else {
return true;
}
}
return x == 1u32;
}

View File

@ -0,0 +1,12 @@
/*
namespace: Compile
expectation: Fail
input_file:
- inputs/u32_3.in
*/
function main(x: u32) -> u32 {
return x;
let double: u32 = x + x;
return double;
}

View File

@ -0,0 +1,16 @@
/*
namespace: Compile
expectation: Fail
input_file:
- inputs/u32_3.in
*/
function main(x: u32) -> bool {
if x == 3u32 {
return true;
} else {
return false;
}
let double = x + x;
return double;
}

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372027]: Loop body contains a return statement or always returns.\n --> compiler-test:5:5\n |\n 5 | for i: u32 in 0u32..9u32 {\n 6 | return false;\n 7 | }\n | ^\n |\n = Remove the code in the loop body that always returns.\nError [ETYC0372027]: Loop body contains a return statement or always returns.\n --> compiler-test:9:5\n |\n 9 | for i: u32 in 0u32..9u32 {\n 10 | if (x == 0u32) {\n 11 | return false;\n 12 | } else {\n 13 | return true;\n 14 | }\n 15 | }\n | ^\n |\n = Remove the code in the loop body that always returns.\n"

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372026]: Cannot reach the following statement.\n --> compiler-test:5:5\n |\n 5 | let double: u32 = x + x;\n | ^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Remove the unreachable code.\nError [ETYC0372026]: Cannot reach the following statement.\n --> compiler-test:6:5\n |\n 6 | return double;\n | ^^^^^^^^^^^^^\n |\n = Remove the unreachable code.\n"

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [EPAR0370005]: expected : -- found '='\n --> compiler-test:9:16\n |\n 9 | let double = x + x;\n | ^"