Merge pull request #1857 from AleoHQ/more-tyc-bug-fixes

[Fix] Type Checking Bugs
This commit is contained in:
Collin Chin 2022-06-03 12:09:12 -04:00 committed by GitHub
commit 18b71d225b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 126 additions and 34 deletions

4
Cargo.lock generated
View File

@ -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",

View File

@ -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());

View File

@ -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> {}

View File

@ -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),

View File

@ -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(),

View File

@ -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,
}
); );

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
/*
namespace: Compile
expectation: Fail
*/
function main() -> u8 {}

View File

@ -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;
} }

View 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;
}

View 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;
}

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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"