mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 23:23:50 +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]]
|
||||
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",
|
||||
|
@ -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());
|
||||
|
@ -15,20 +15,25 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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());
|
||||
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {}
|
||||
|
||||
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.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,
|
||||
VariableSymbol {
|
||||
type_: &input_var.type_,
|
||||
@ -36,12 +41,16 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
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);
|
||||
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),
|
||||
|
@ -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<Symbol>,
|
||||
pub(crate) has_return: bool,
|
||||
pub(crate) negate: bool,
|
||||
pub(crate) account_types: IndexSet<Symbol>,
|
||||
pub(crate) algorithms_types: IndexSet<Symbol>,
|
||||
@ -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(),
|
||||
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
||||
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
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: abe58c662f85737a1bdb4b3e55c3e455e128ff543cbee00637b5b33aee554202
|
||||
initial_ast: d75014d735c603b442bf7e21a9c0c40880a44bc4a2b47d99d87b1c396290f814
|
||||
symbol_table: 5e96e4138e5643222224de143ab32b6716c4dbaf965318b48eaf9e4ba63f8586
|
||||
- initial_input_ast: a18a82ee746e1fd1f9c6c09c36b055f9d3b44a17dd778c5081f9258d6bb9f3ae
|
||||
initial_ast: dab5600564d366fe0b548228bd230a25aa65fa8902ea7f21cf01ba8e16df1f9f
|
||||
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