mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-28 01:01:53 +03:00
Merge pull request #1857 from AleoHQ/more-tyc-bug-fixes
[Fix] Type Checking Bugs
This commit is contained in:
commit
18b71d225b
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1073,9 +1073,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.8.2"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
|
@ -226,11 +226,20 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
|
|||||||
self.visitor.assert_type(Type::Group, expected, input.right.span());
|
self.visitor.assert_type(Type::Group, expected, input.right.span());
|
||||||
Some(Type::Group)
|
Some(Type::Group)
|
||||||
}
|
}
|
||||||
_ => {
|
(Some(t1), Some(t2)) => {
|
||||||
self.visitor.assert_type(t1.unwrap(), expected, input.left.span());
|
self.visitor.assert_type(*t1, expected, input.left.span());
|
||||||
self.visitor.assert_type(t2.unwrap(), expected, input.right.span());
|
self.visitor.assert_type(*t2, expected, input.right.span());
|
||||||
return_incorrect_type(t1, t2, expected)
|
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 => {
|
BinaryOperation::Div => {
|
||||||
@ -272,7 +281,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// The base is some type thats not an int or field.
|
// The base is some type thats not an int or field.
|
||||||
(Some(t), _) => {
|
(Some(t), _) if !matches!(t, Type::IntegerType(_) | Type::Field) => {
|
||||||
self.visitor
|
self.visitor
|
||||||
.handler
|
.handler
|
||||||
.emit_err(TypeCheckerError::incorrect_pow_base_type(t, input.left.span()).into());
|
.emit_err(TypeCheckerError::incorrect_pow_base_type(t, input.left.span()).into());
|
||||||
|
@ -15,20 +15,25 @@
|
|||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use leo_ast::*;
|
use leo_ast::*;
|
||||||
|
use leo_errors::TypeCheckerError;
|
||||||
|
|
||||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||||
|
|
||||||
use super::director::Director;
|
use super::director::Director;
|
||||||
|
|
||||||
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {}
|
||||||
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
|
|
||||||
self.symbol_table.clear_variables();
|
impl<'a> ProgramVisitorDirector<'a> for Director<'a> {
|
||||||
self.parent = Some(input.name());
|
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| {
|
input.input.iter().for_each(|i| {
|
||||||
let input_var = i.get_variable();
|
let input_var = i.get_variable();
|
||||||
self.validate_ident_type(&Some(input_var.type_));
|
self.visitor.validate_ident_type(&Some(input_var.type_));
|
||||||
|
|
||||||
if let Err(err) = self.symbol_table.insert_variable(
|
if let Err(err) = self.visitor.symbol_table.insert_variable(
|
||||||
input_var.identifier.name,
|
input_var.identifier.name,
|
||||||
VariableSymbol {
|
VariableSymbol {
|
||||||
type_: &input_var.type_,
|
type_: &input_var.type_,
|
||||||
@ -36,12 +41,16 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
|||||||
declaration: Declaration::Input(input_var.mode()),
|
declaration: Declaration::Input(input_var.mode()),
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
self.handler.emit_err(err);
|
self.visitor.handler.emit_err(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
self.visit_block(&input.block);
|
||||||
|
|
||||||
VisitResult::VisitChildren
|
if !self.visitor.has_return {
|
||||||
|
self.visitor
|
||||||
|
.handler
|
||||||
|
.emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProgramVisitorDirector<'a> for Director<'a> {}
|
|
||||||
|
@ -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);
|
let return_type = &self.visitor.symbol_table.lookup_fn(&parent).map(|f| f.output);
|
||||||
self.visitor.validate_ident_type(return_type);
|
self.visitor.validate_ident_type(return_type);
|
||||||
|
self.visitor.has_return = true;
|
||||||
|
|
||||||
self.visit_expression(&input.expression, return_type);
|
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| {
|
input.variable_names.iter().for_each(|v| {
|
||||||
self.visitor.validate_ident_type(&Some(input.type_));
|
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(
|
if let Err(err) = self.visitor.symbol_table.insert_variable(
|
||||||
v.identifier.name,
|
v.identifier.name,
|
||||||
VariableSymbol {
|
VariableSymbol {
|
||||||
@ -55,8 +58,6 @@ impl<'a> StatementVisitorDirector<'a> for Director<'a> {
|
|||||||
) {
|
) {
|
||||||
self.visitor.handler.emit_err(err);
|
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) {
|
fn visit_conditional(&mut self, input: &'a ConditionalStatement) {
|
||||||
self.visit_expression(&input.condition, &Some(Type::Boolean));
|
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) {
|
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) {
|
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();
|
self.visitor.symbol_table.push_variable_scope();
|
||||||
// have to redo the logic here so we have scoping
|
|
||||||
input.statements.iter().for_each(|stmt| {
|
input.statements.iter().for_each(|stmt| {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Return(stmt) => self.visit_return(stmt),
|
Statement::Return(stmt) => self.visit_return(stmt),
|
||||||
|
@ -26,6 +26,7 @@ pub struct TypeChecker<'a> {
|
|||||||
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
|
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
|
||||||
pub(crate) handler: &'a Handler,
|
pub(crate) handler: &'a Handler,
|
||||||
pub(crate) parent: Option<Symbol>,
|
pub(crate) parent: Option<Symbol>,
|
||||||
|
pub(crate) has_return: bool,
|
||||||
pub(crate) negate: bool,
|
pub(crate) negate: bool,
|
||||||
pub(crate) account_types: IndexSet<Symbol>,
|
pub(crate) account_types: IndexSet<Symbol>,
|
||||||
pub(crate) algorithms_types: IndexSet<Symbol>,
|
pub(crate) algorithms_types: IndexSet<Symbol>,
|
||||||
@ -77,6 +78,7 @@ impl<'a> TypeChecker<'a> {
|
|||||||
symbol_table,
|
symbol_table,
|
||||||
handler,
|
handler,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
has_return: false,
|
||||||
negate: false,
|
negate: false,
|
||||||
account_types: Account::types(),
|
account_types: Account::types(),
|
||||||
algorithms_types: Algorithms::types(),
|
algorithms_types: Algorithms::types(),
|
||||||
|
@ -142,4 +142,14 @@ create_messages!(
|
|||||||
),
|
),
|
||||||
help: None,
|
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,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
input_file: inputs/dummy.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(y: bool) -> bool {
|
||||||
|
let b: u8 = b;
|
||||||
|
return y == true;
|
||||||
|
}
|
6
tests/compiler/function/no_return_fail.leo
Normal file
6
tests/compiler/function/no_return_fail.leo
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main() -> u8 {}
|
@ -8,4 +8,5 @@ function main(a: group, b: group, c: group) -> bool {
|
|||||||
const r: group = true ? a : b;
|
const r: group = true ? a : b;
|
||||||
|
|
||||||
console.assert(r == c);
|
console.assert(r == c);
|
||||||
|
return r == c;
|
||||||
}
|
}
|
10
tests/compiler/statements/non_existant_var_exp_fail.leo
Normal file
10
tests/compiler/statements/non_existant_var_exp_fail.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
input_file: inputs/dummy.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(k: bool) -> bool {
|
||||||
|
let b: u8 = 1u8**z;
|
||||||
|
return k == true;
|
||||||
|
}
|
10
tests/compiler/statements/non_existant_vars_mul_fail.leo
Normal file
10
tests/compiler/statements/non_existant_vars_mul_fail.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
input_file: inputs/dummy.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(k: bool) -> bool {
|
||||||
|
let b: u8 = x*z;
|
||||||
|
return k == true;
|
||||||
|
}
|
@ -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"
|
@ -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"
|
@ -3,6 +3,6 @@ namespace: Compile
|
|||||||
expectation: Pass
|
expectation: Pass
|
||||||
outputs:
|
outputs:
|
||||||
- output:
|
- output:
|
||||||
- initial_input_ast: abe58c662f85737a1bdb4b3e55c3e455e128ff543cbee00637b5b33aee554202
|
- initial_input_ast: a18a82ee746e1fd1f9c6c09c36b055f9d3b44a17dd778c5081f9258d6bb9f3ae
|
||||||
initial_ast: d75014d735c603b442bf7e21a9c0c40880a44bc4a2b47d99d87b1c396290f814
|
initial_ast: dab5600564d366fe0b548228bd230a25aa65fa8902ea7f21cf01ba8e16df1f9f
|
||||||
symbol_table: 5e96e4138e5643222224de143ab32b6716c4dbaf965318b48eaf9e4ba63f8586
|
symbol_table: 35465ba1d734747d0265a3b89c9cee8fe0beda4cfb42b477010c902472e57cc1
|
||||||
|
@ -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"
|
@ -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"
|
Loading…
Reference in New Issue
Block a user