diff --git a/Cargo.lock b/Cargo.lock
index 35c1b808e4..d7563d136d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1073,9 +1073,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.8.2"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
+checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown 0.11.2",
diff --git a/compiler/passes/src/type_checker/check_expressions.rs b/compiler/passes/src/type_checker/check_expressions.rs
index ff921d4b24..097aaf85b6 100644
--- a/compiler/passes/src/type_checker/check_expressions.rs
+++ b/compiler/passes/src/type_checker/check_expressions.rs
@@ -226,11 +226,20 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
self.visitor.assert_type(Type::Group, expected, input.right.span());
Some(Type::Group)
}
- _ => {
- self.visitor.assert_type(t1.unwrap(), expected, input.left.span());
- self.visitor.assert_type(t2.unwrap(), expected, input.right.span());
- return_incorrect_type(t1, t2, expected)
+ (Some(t1), Some(t2)) => {
+ self.visitor.assert_type(*t1, expected, input.left.span());
+ self.visitor.assert_type(*t2, expected, input.right.span());
+ return_incorrect_type(Some(*t1), Some(*t2), expected)
}
+ (Some(type_), None) => {
+ self.visitor.assert_type(*type_, expected, input.left.span());
+ None
+ }
+ (None, Some(type_)) => {
+ self.visitor.assert_type(*type_, expected, input.right.span());
+ None
+ }
+ (None, None) => None,
}
}
BinaryOperation::Div => {
@@ -272,7 +281,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
);
}
// The base is some type thats not an int or field.
- (Some(t), _) => {
+ (Some(t), _) if !matches!(t, Type::IntegerType(_) | Type::Field) => {
self.visitor
.handler
.emit_err(TypeCheckerError::incorrect_pow_base_type(t, input.left.span()).into());
diff --git a/compiler/passes/src/type_checker/check_file.rs b/compiler/passes/src/type_checker/check_file.rs
index 3258fc28f7..72d64e6f8e 100644
--- a/compiler/passes/src/type_checker/check_file.rs
+++ b/compiler/passes/src/type_checker/check_file.rs
@@ -15,33 +15,42 @@
// along with the Leo library. If not, see .
use leo_ast::*;
+use leo_errors::TypeCheckerError;
use crate::{Declaration, TypeChecker, VariableSymbol};
use super::director::Director;
-impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
- fn visit_function(&mut self, input: &'a Function) -> VisitResult {
- self.symbol_table.clear_variables();
- self.parent = Some(input.name());
- input.input.iter().for_each(|i| {
- let input_var = i.get_variable();
- self.validate_ident_type(&Some(input_var.type_));
+impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {}
- if let Err(err) = self.symbol_table.insert_variable(
- input_var.identifier.name,
- VariableSymbol {
- type_: &input_var.type_,
- span: input_var.identifier.span(),
- declaration: Declaration::Input(input_var.mode()),
- },
- ) {
- self.handler.emit_err(err);
+impl<'a> ProgramVisitorDirector<'a> for Director<'a> {
+ fn visit_function(&mut self, input: &'a Function) {
+ if let VisitResult::VisitChildren = self.visitor_ref().visit_function(input) {
+ self.visitor.has_return = false;
+ self.visitor.symbol_table.clear_variables();
+ self.visitor.parent = Some(input.name());
+ input.input.iter().for_each(|i| {
+ let input_var = i.get_variable();
+ self.visitor.validate_ident_type(&Some(input_var.type_));
+
+ if let Err(err) = self.visitor.symbol_table.insert_variable(
+ input_var.identifier.name,
+ VariableSymbol {
+ type_: &input_var.type_,
+ span: input_var.identifier.span(),
+ declaration: Declaration::Input(input_var.mode()),
+ },
+ ) {
+ self.visitor.handler.emit_err(err);
+ }
+ });
+ self.visit_block(&input.block);
+
+ if !self.visitor.has_return {
+ self.visitor
+ .handler
+ .emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()).into());
}
- });
-
- VisitResult::VisitChildren
+ }
}
}
-
-impl<'a> ProgramVisitorDirector<'a> for Director<'a> {}
diff --git a/compiler/passes/src/type_checker/check_statements.rs b/compiler/passes/src/type_checker/check_statements.rs
index 5c2e3204fc..ea24da7067 100644
--- a/compiler/passes/src/type_checker/check_statements.rs
+++ b/compiler/passes/src/type_checker/check_statements.rs
@@ -31,6 +31,7 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
let return_type = &self.visitor.symbol_table.lookup_fn(&parent).map(|f| f.output);
self.visitor.validate_ident_type(return_type);
+ self.visitor.has_return = true;
self.visit_expression(&input.expression, return_type);
}
@@ -45,6 +46,8 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
input.variable_names.iter().for_each(|v| {
self.visitor.validate_ident_type(&Some(input.type_));
+ self.visit_expression(&input.value, &Some(input.type_));
+
if let Err(err) = self.visitor.symbol_table.insert_variable(
v.identifier.name,
VariableSymbol {
@@ -55,8 +58,6 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
) {
self.visitor.handler.emit_err(err);
}
-
- self.visit_expression(&input.value, &Some(input.type_));
});
}
@@ -92,6 +93,10 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
fn visit_conditional(&mut self, input: &'a ConditionalStatement) {
self.visit_expression(&input.condition, &Some(Type::Boolean));
+ self.visit_block(&input.block);
+ if let Some(s) = input.next.as_ref() {
+ self.visit_statement(s)
+ }
}
fn visit_iteration(&mut self, input: &'a IterationStatement) {
@@ -124,8 +129,8 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
}
fn visit_block(&mut self, input: &'a Block) {
+ // creates a new sub-scope since we are in a block.
self.visitor.symbol_table.push_variable_scope();
- // have to redo the logic here so we have scoping
input.statements.iter().for_each(|stmt| {
match stmt {
Statement::Return(stmt) => self.visit_return(stmt),
diff --git a/compiler/passes/src/type_checker/checker.rs b/compiler/passes/src/type_checker/checker.rs
index 40a89ec679..274041724f 100644
--- a/compiler/passes/src/type_checker/checker.rs
+++ b/compiler/passes/src/type_checker/checker.rs
@@ -26,6 +26,7 @@ pub struct TypeChecker<'a> {
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
pub(crate) handler: &'a Handler,
pub(crate) parent: Option,
+ pub(crate) has_return: bool,
pub(crate) negate: bool,
pub(crate) account_types: IndexSet,
pub(crate) algorithms_types: IndexSet,
@@ -77,6 +78,7 @@ impl<'a> TypeChecker<'a> {
symbol_table,
handler,
parent: None,
+ has_return: false,
negate: false,
account_types: Account::types(),
algorithms_types: Algorithms::types(),
diff --git a/leo/errors/src/errors/type_checker/type_checker_error.rs b/leo/errors/src/errors/type_checker/type_checker_error.rs
index 2965891cf4..8f11c90016 100644
--- a/leo/errors/src/errors/type_checker/type_checker_error.rs
+++ b/leo/errors/src/errors/type_checker/type_checker_error.rs
@@ -142,4 +142,14 @@ create_messages!(
),
help: None,
}
+
+ /// For when a function doesn't have a return statement.
+ @formatted
+ function_has_no_return {
+ args: (func: impl Display),
+ msg: format!(
+ "The function {func} has no return statement.",
+ ),
+ help: None,
+ }
);
diff --git a/tests/compiler/group/ternary.leo b/tests/compiler/group/ternary.leo
index f3bf0e0705..72312ac337 100644
--- a/tests/compiler/group/ternary.leo
+++ b/tests/compiler/group/ternary.leo
@@ -8,4 +8,5 @@ function main(a: group, b: group, c: group) -> bool {
const r: group = true ? a : b;
console.assert(r == c);
+ return r == c;
}
\ No newline at end of file
diff --git a/tests/expectations/compiler/compiler/definition/use_decl_variable_as_assign_fail.leo.out b/tests/expectations/compiler/compiler/definition/use_decl_variable_as_assign_fail.leo.out
new file mode 100644
index 0000000000..9442d7d720
--- /dev/null
+++ b/tests/expectations/compiler/compiler/definition/use_decl_variable_as_assign_fail.leo.out
@@ -0,0 +1,5 @@
+---
+namespace: Compile
+expectation: Fail
+outputs:
+ - "Error [ETYC0372003]: Unknown variable `b`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = b;\n | ^\n"
diff --git a/tests/expectations/compiler/compiler/function/no_return_fail.leo.out b/tests/expectations/compiler/compiler/function/no_return_fail.leo.out
new file mode 100644
index 0000000000..e584776b23
--- /dev/null
+++ b/tests/expectations/compiler/compiler/function/no_return_fail.leo.out
@@ -0,0 +1,5 @@
+---
+namespace: Compile
+expectation: Fail
+outputs:
+ - "Error [ETYC0372012]: The function main has no return statement.\n --> compiler-test:3:1\n |\n 3 | function main() -> u8 {}\n | ^^^^^^^^^^^^^^^^^^^^^^^^\n"
diff --git a/tests/expectations/compiler/compiler/group/ternary.leo.out b/tests/expectations/compiler/compiler/group/ternary.leo.out
index 90fc3d3550..3e8db521b5 100644
--- a/tests/expectations/compiler/compiler/group/ternary.leo.out
+++ b/tests/expectations/compiler/compiler/group/ternary.leo.out
@@ -3,6 +3,6 @@ namespace: Compile
expectation: Pass
outputs:
- output:
- - initial_input_ast: abe58c662f85737a1bdb4b3e55c3e455e128ff543cbee00637b5b33aee554202
- initial_ast: d75014d735c603b442bf7e21a9c0c40880a44bc4a2b47d99d87b1c396290f814
- symbol_table: 5e96e4138e5643222224de143ab32b6716c4dbaf965318b48eaf9e4ba63f8586
+ - initial_input_ast: a18a82ee746e1fd1f9c6c09c36b055f9d3b44a17dd778c5081f9258d6bb9f3ae
+ initial_ast: dab5600564d366fe0b548228bd230a25aa65fa8902ea7f21cf01ba8e16df1f9f
+ symbol_table: 35465ba1d734747d0265a3b89c9cee8fe0beda4cfb42b477010c902472e57cc1
diff --git a/tests/expectations/compiler/compiler/statements/non_existant_var_exp_fail.leo.out b/tests/expectations/compiler/compiler/statements/non_existant_var_exp_fail.leo.out
new file mode 100644
index 0000000000..7cf5b586ac
--- /dev/null
+++ b/tests/expectations/compiler/compiler/statements/non_existant_var_exp_fail.leo.out
@@ -0,0 +1,5 @@
+---
+namespace: Compile
+expectation: Fail
+outputs:
+ - "Error [ETYC0372003]: Unknown variable `z`\n --> compiler-test:4:19\n |\n 4 | \tlet b: u8 = 1u8**z;\n | ^\n"
diff --git a/tests/expectations/compiler/compiler/statements/non_existant_vars_mul_fail.leo.out b/tests/expectations/compiler/compiler/statements/non_existant_vars_mul_fail.leo.out
new file mode 100644
index 0000000000..012ae3bd2c
--- /dev/null
+++ b/tests/expectations/compiler/compiler/statements/non_existant_vars_mul_fail.leo.out
@@ -0,0 +1,5 @@
+---
+namespace: Compile
+expectation: Fail
+outputs:
+ - "Error [ETYC0372003]: Unknown variable `x`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = x*z;\n | ^\nError [ETYC0372003]: Unknown variable `z`\n --> compiler-test:4:16\n |\n 4 | \tlet b: u8 = x*z;\n | ^\n"