mirror of
https://github.com/AleoHQ/leo.git
synced 2024-09-21 12:07:56 +03:00
Type checking errors on multiple returns
This commit is contained in:
parent
0194e09b41
commit
9dd45c3620
@ -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
|
||||
|
@ -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()),
|
||||
}
|
||||
);
|
||||
|
22
tests/compiler/statements/loop_returns_fail.leo
Normal file
22
tests/compiler/statements/loop_returns_fail.leo
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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"
|
@ -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"
|
@ -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 | ^"
|
Loading…
Reference in New Issue
Block a user