From 69a8b61efe51fcb983b7471d9635fd25adf39ba0 Mon Sep 17 00:00:00 2001 From: collin Date: Thu, 12 Nov 2020 10:28:24 -0800 Subject: [PATCH] add type-inference module --- Cargo.lock | 14 + Cargo.toml | 2 +- compiler/Cargo.toml | 6 +- compiler/src/compiler.rs | 22 +- compiler/src/errors/compiler.rs | 8 +- compiler/tests/boolean/mod.rs | 29 +- compiler/tests/circuits/mod.rs | 170 +-- .../core/packages/unstable/blake2s/mod.rs | 39 +- compiler/tests/function/mod.rs | 37 +- compiler/tests/mod.rs | 8 +- compiler/tests/mutability/mod.rs | 32 +- compiler/tests/statements/mod.rs | 16 +- compiler/tests/syntax/mod.rs | 28 +- type-inference/Cargo.toml | 43 + type-inference/src/assertions/mod.rs | 27 + .../src/assertions/type_assertion.rs | 74 + .../src/assertions/type_equality.rs | 68 + .../src/assertions/type_membership.rs | 71 + .../src/assertions/type_variable_pair.rs | 137 ++ type-inference/src/errors/frame.rs | 240 ++++ type-inference/src/errors/mod.rs | 30 + type-inference/src/errors/scope.rs | 42 + type-inference/src/errors/type_assertion.rs | 89 ++ type-inference/src/errors/type_inference.rs | 42 + type-inference/src/errors/variable_table.rs | 53 + type-inference/src/lib.rs | 37 + type-inference/src/objects/frame.rs | 1210 +++++++++++++++++ type-inference/src/objects/mod.rs | 24 + type-inference/src/objects/scope.rs | 81 ++ type-inference/src/objects/variable_table.rs | 67 + type-inference/src/type_inference.rs | 138 ++ type-inference/tests/arrays/empty_array.leo | 3 + .../tests/arrays/invalid_array_access.leo | 5 + .../tests/arrays/invalid_spread.leo | 5 + type-inference/tests/arrays/mod.rs | 44 + .../tests/circuits/invalid_circuit.leo | 4 + type-inference/tests/circuits/mod.rs | 25 + type-inference/tests/empty.leo | 0 .../tests/functions/invalid_function.leo | 4 + type-inference/tests/functions/mod.rs | 25 + type-inference/tests/mod.rs | 84 ++ .../tests/tuples/invalid_tuple_access.leo | 5 + type-inference/tests/tuples/mod.rs | 25 + .../tests/variables/duplicate_variable.leo | 4 + .../variables/duplicate_variable_multi.leo | 3 + type-inference/tests/variables/mod.rs | 49 + .../tests/variables/not_enough_values.leo | 3 + .../tests/variables/too_many_values.leo | 3 + 48 files changed, 2984 insertions(+), 191 deletions(-) create mode 100644 type-inference/Cargo.toml create mode 100644 type-inference/src/assertions/mod.rs create mode 100644 type-inference/src/assertions/type_assertion.rs create mode 100644 type-inference/src/assertions/type_equality.rs create mode 100644 type-inference/src/assertions/type_membership.rs create mode 100644 type-inference/src/assertions/type_variable_pair.rs create mode 100644 type-inference/src/errors/frame.rs create mode 100644 type-inference/src/errors/mod.rs create mode 100644 type-inference/src/errors/scope.rs create mode 100644 type-inference/src/errors/type_assertion.rs create mode 100644 type-inference/src/errors/type_inference.rs create mode 100644 type-inference/src/errors/variable_table.rs create mode 100644 type-inference/src/lib.rs create mode 100644 type-inference/src/objects/frame.rs create mode 100644 type-inference/src/objects/mod.rs create mode 100644 type-inference/src/objects/scope.rs create mode 100644 type-inference/src/objects/variable_table.rs create mode 100644 type-inference/src/type_inference.rs create mode 100644 type-inference/tests/arrays/empty_array.leo create mode 100644 type-inference/tests/arrays/invalid_array_access.leo create mode 100644 type-inference/tests/arrays/invalid_spread.leo create mode 100644 type-inference/tests/arrays/mod.rs create mode 100644 type-inference/tests/circuits/invalid_circuit.leo create mode 100644 type-inference/tests/circuits/mod.rs create mode 100644 type-inference/tests/empty.leo create mode 100644 type-inference/tests/functions/invalid_function.leo create mode 100644 type-inference/tests/functions/mod.rs create mode 100644 type-inference/tests/mod.rs create mode 100644 type-inference/tests/tuples/invalid_tuple_access.leo create mode 100644 type-inference/tests/tuples/mod.rs create mode 100644 type-inference/tests/variables/duplicate_variable.leo create mode 100644 type-inference/tests/variables/duplicate_variable_multi.leo create mode 100644 type-inference/tests/variables/mod.rs create mode 100644 type-inference/tests/variables/not_enough_values.leo create mode 100644 type-inference/tests/variables/too_many_values.leo diff --git a/Cargo.lock b/Cargo.lock index 67ffa6943f..7d91cdf1b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1313,6 +1313,7 @@ dependencies = [ "leo-package", "leo-state", "leo-symbol-table", + "leo-type-inference", "num-bigint", "pest", "rand", @@ -1494,6 +1495,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "leo-type-inference" +version = "1.0.4" +dependencies = [ + "leo-ast", + "leo-grammar", + "leo-imports", + "leo-symbol-table", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "libc" version = "0.2.80" diff --git a/Cargo.toml b/Cargo.toml index 1b6e880f5a..0258db4932 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ members = [ "package", "state", "symbol-table", -# "type-inference", + "type-inference", ] [dependencies.leo-ast] diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index e9e94ea3b5..7cb81be57c 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -53,9 +53,9 @@ version = "1.0.4" path = "../symbol-table" version = "1.0.4" -#[dependencies.leo-type-inference] -#path = "../type-inference" -#version = "1.0.4" +[dependencies.leo-type-inference] +path = "../type-inference" +version = "1.0.4" [dependencies.snarkos-curves] version = "1.1.3" diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index ea2cc9ef09..00ec57fe90 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -30,7 +30,7 @@ use leo_input::LeoInputParser; use leo_package::inputs::InputPairs; use leo_state::verify_local_data_commitment; use leo_symbol_table::SymbolTable; -// use leo_type_inference::TypeInference; +use leo_type_inference::TypeInference; use snarkos_dpc::{base_dpc::instantiated::Components, SystemParameters}; use snarkos_errors::gadgets::SynthesisError; @@ -206,19 +206,19 @@ impl> Compiler { /// pub(crate) fn check_program(&self) -> Result<(), CompilerError> { // Create a new symbol table from the program, imported_programs, and program_input. - let _symbol_table = + let symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input).map_err(|mut e| { e.set_path(&self.main_file_path); e })?; - // // Run type inference check on program. - // TypeInference::new(&self.program, symbol_table).map_err(|mut e| { - // e.set_path(&self.main_file_path); - // - // e - // })?; + // Run type inference check on program. + TypeInference::new(&self.program, symbol_table).map_err(|mut e| { + e.set_path(&self.main_file_path); + + e + })?; tracing::debug!("Program checks complete"); @@ -253,10 +253,10 @@ impl> Compiler { self.imported_programs = ImportParser::parse(&self.program)?; // Create a new symbol table from the program, imported programs, and program input. - let _symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?; + let symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?; - // // Run type inference check on program. - // TypeInference::new(&self.program, symbol_table)?; + // Run type inference check on program. + TypeInference::new(&self.program, symbol_table)?; tracing::debug!("Program parsing complete\n{:#?}", self.program); diff --git a/compiler/src/errors/compiler.rs b/compiler/src/errors/compiler.rs index a29d733999..65f652de59 100644 --- a/compiler/src/errors/compiler.rs +++ b/compiler/src/errors/compiler.rs @@ -20,7 +20,7 @@ use leo_imports::ImportParserError; use leo_input::InputParserError; use leo_state::LocalDataVerificationError; use leo_symbol_table::SymbolTableError; -// use leo_type_inference::TypeInferenceError; +use leo_type_inference::TypeInferenceError; use bincode::Error as SerdeError; use std::path::{Path, PathBuf}; @@ -71,8 +71,8 @@ pub enum CompilerError { #[error("{}", _0)] SymbolTableError(#[from] SymbolTableError), - // #[error("{}", _0)] - // TypeInferenceError(#[from] TypeInferenceError), + #[error("{}", _0)] + TypeInferenceError(#[from] TypeInferenceError), } impl CompilerError { @@ -82,7 +82,7 @@ impl CompilerError { CompilerError::FunctionError(error) => error.set_path(path), CompilerError::OutputStringError(error) => error.set_path(path), CompilerError::SymbolTableError(error) => error.set_path(path), - // CompilerError::TypeInferenceError(error) => error.set_path(path), + CompilerError::TypeInferenceError(error) => error.set_path(path), _ => {} } } diff --git a/compiler/tests/boolean/mod.rs b/compiler/tests/boolean/mod.rs index ac221986d0..8015cfd335 100644 --- a/compiler/tests/boolean/mod.rs +++ b/compiler/tests/boolean/mod.rs @@ -17,6 +17,7 @@ use crate::{ assert_satisfied, expect_compiler_error, + expect_type_inference_error, get_output, parse_program, parse_program_with_input, @@ -126,13 +127,13 @@ fn test_false_or_false() { assert_satisfied(program); } -// #[test] -// fn test_true_or_u32() { -// let bytes = include_bytes!("true_or_u32.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_true_or_u32() { + let bytes = include_bytes!("true_or_u32.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} // Boolean and && @@ -160,13 +161,13 @@ fn test_false_and_false() { assert_satisfied(program); } -// #[test] -// fn test_true_and_u32() { -// let bytes = include_bytes!("true_and_u32.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_true_and_u32() { + let bytes = include_bytes!("true_and_u32.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} // All diff --git a/compiler/tests/circuits/mod.rs b/compiler/tests/circuits/mod.rs index df63410d1e..33d0d41ea9 100644 --- a/compiler/tests/circuits/mod.rs +++ b/compiler/tests/circuits/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, expect_compiler_error, parse_program}; +use crate::{assert_satisfied, expect_compiler_error, expect_type_inference_error, parse_program}; // Expressions @@ -34,13 +34,13 @@ fn test_inline_fail() { expect_compiler_error(program); } -// #[test] -// fn test_inline_undefined() { -// let bytes = include_bytes!("inline_undefined.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_inline_undefined() { + let bytes = include_bytes!("inline_undefined.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} // Members @@ -52,13 +52,13 @@ fn test_member_variable() { assert_satisfied(program); } -// #[test] -// fn test_member_variable_fail() { -// let bytes = include_bytes!("member_variable_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_member_variable_fail() { + let bytes = include_bytes!("member_variable_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_member_variable_and_function() { @@ -76,21 +76,21 @@ fn test_member_function() { assert_satisfied(program); } -// #[test] -// fn test_member_function_fail() { -// let bytes = include_bytes!("member_function_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_member_function_fail() { + let bytes = include_bytes!("member_function_fail.leo"); + let error = parse_program(bytes).err().unwrap(); -// #[test] -// fn test_member_function_invalid() { -// let bytes = include_bytes!("member_function_invalid.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } + expect_type_inference_error(error); +} + +#[test] +fn test_member_function_invalid() { + let bytes = include_bytes!("member_function_invalid.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_member_function_nested() { @@ -116,31 +116,31 @@ fn test_member_static_function_nested() { assert_satisfied(program); } -// #[test] -// fn test_member_static_function_invalid() { -// let bytes = include_bytes!("member_static_function_invalid.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error) -// } +#[test] +fn test_member_static_function_invalid() { + let bytes = include_bytes!("member_static_function_invalid.leo"); + let error = parse_program(bytes).err().unwrap(); -// #[test] -// fn test_member_static_function_undefined() { -// let bytes = include_bytes!("member_static_function_undefined.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error) -// } + expect_type_inference_error(error) +} + +#[test] +fn test_member_static_function_undefined() { + let bytes = include_bytes!("member_static_function_undefined.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error) +} // Mutability -// #[test] -// fn test_mutate_function_fail() { -// let bytes = include_bytes!("mut_function_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_mutate_function_fail() { + let bytes = include_bytes!("mut_function_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_mutate_self_variable() { @@ -158,29 +158,29 @@ fn test_mutate_self_variable_fail() { expect_compiler_error(program); } -// #[test] -// fn test_mutate_self_function_fail() { -// let bytes = include_bytes!("mut_self_function_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_mutate_self_function_fail() { + let bytes = include_bytes!("mut_self_function_fail.leo"); + let error = parse_program(bytes).err().unwrap(); -// #[test] -// fn test_mutate_self_static_function_fail() { -// let bytes = include_bytes!("mut_self_static_function_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } + expect_type_inference_error(error); +} -// #[test] -// fn test_mutate_static_function_fail() { -// let bytes = include_bytes!("mut_static_function_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_mutate_self_static_function_fail() { + let bytes = include_bytes!("mut_self_static_function_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} + +#[test] +fn test_mutate_static_function_fail() { + let bytes = include_bytes!("mut_static_function_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_mutate_variable() { @@ -200,13 +200,13 @@ fn test_mutate_variable_fail() { // Self -// #[test] -// fn test_self_fail() { -// let bytes = include_bytes!("self_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_self_fail() { + let bytes = include_bytes!("self_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_self_member_pass() { @@ -224,13 +224,13 @@ fn test_self_member_invalid() { let _err = expect_compiler_error(program); } -// #[test] -// fn test_self_member_undefined() { -// let bytes = include_bytes!("self_member_undefined.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_self_member_undefined() { + let bytes = include_bytes!("self_member_undefined.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} // All diff --git a/compiler/tests/core/packages/unstable/blake2s/mod.rs b/compiler/tests/core/packages/unstable/blake2s/mod.rs index 1c1476c495..2b308db42e 100644 --- a/compiler/tests/core/packages/unstable/blake2s/mod.rs +++ b/compiler/tests/core/packages/unstable/blake2s/mod.rs @@ -14,7 +14,14 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, generate_main_input, get_output, parse_program, parse_program_with_input}; +use crate::{ + assert_satisfied, + expect_type_inference_error, + generate_main_input, + get_output, + parse_program, + parse_program_with_input, +}; use leo_ast::InputValue; use leo_input::types::{IntegerType, U8Type, UnsignedIntegerType}; @@ -22,22 +29,22 @@ use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; use snarkos_algorithms::prf::blake2s::Blake2s as B2SPRF; use snarkos_models::algorithms::PRF; -// -// #[test] -// fn test_arguments_length_fail() { -// let program_bytes = include_bytes!("arguments_length_fail.leo"); -// let error = parse_program(program_bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } -// #[test] -// fn test_arguments_type_fail() { -// let program_bytes = include_bytes!("arguments_type_fail.leo"); -// let error = parse_program(program_bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_arguments_length_fail() { + let program_bytes = include_bytes!("arguments_length_fail.leo"); + let error = parse_program(program_bytes).err().unwrap(); + + expect_type_inference_error(error); +} + +#[test] +fn test_arguments_type_fail() { + let program_bytes = include_bytes!("arguments_type_fail.leo"); + let error = parse_program(program_bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_blake2s_input() { diff --git a/compiler/tests/function/mod.rs b/compiler/tests/function/mod.rs index a5a4e2c9db..0c62b34e76 100644 --- a/compiler/tests/function/mod.rs +++ b/compiler/tests/function/mod.rs @@ -14,7 +14,14 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, expect_compiler_error, get_output, parse_program, parse_program_with_input}; +use crate::{ + assert_satisfied, + expect_compiler_error, + expect_type_inference_error, + get_output, + parse_program, + parse_program_with_input, +}; use leo_compiler::errors::{CompilerError, ExpressionError, FunctionError, StatementError}; #[test] @@ -110,13 +117,13 @@ fn test_scope_fail() { } } -// #[test] -// fn test_undefined() { -// let bytes = include_bytes!("undefined.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_undefined() { + let bytes = include_bytes!("undefined.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_value_unchanged() { @@ -126,13 +133,13 @@ fn test_value_unchanged() { assert_satisfied(program); } -// #[test] -// fn test_array_input() { -// let bytes = include_bytes!("array_input.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error) -// } +#[test] +fn test_array_input() { + let bytes = include_bytes!("array_input.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error) +} // Test return multidimensional arrays diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index 57cb8f3d2c..a18efa9428 100644 --- a/compiler/tests/mod.rs +++ b/compiler/tests/mod.rs @@ -180,10 +180,10 @@ pub(crate) fn expect_compiler_error(program: EdwardsTestCompiler) -> CompilerErr let mut cs = TestConstraintSystem::::new(); program.generate_constraints_helper(&mut cs).unwrap_err() } -// -// pub(crate) fn expect_type_inference_error(error: CompilerError) { -// assert!(matches!(error, CompilerError::TypeInferenceError(_))) -// } + +pub(crate) fn expect_type_inference_error(error: CompilerError) { + assert!(matches!(error, CompilerError::TypeInferenceError(_))) +} pub(crate) fn expect_symbol_table_error(error: CompilerError) { assert!(matches!(error, CompilerError::SymbolTableError(_))) diff --git a/compiler/tests/mutability/mod.rs b/compiler/tests/mutability/mod.rs index 861ffd6a64..8442357044 100644 --- a/compiler/tests/mutability/mod.rs +++ b/compiler/tests/mutability/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, expect_compiler_error, generate_main_input, parse_program}; +use crate::{assert_satisfied, expect_compiler_error, expect_type_inference_error, generate_main_input, parse_program}; use leo_ast::InputValue; #[test] @@ -89,21 +89,21 @@ fn test_circuit_variable_mut() { assert_satisfied(program); } -// #[test] -// fn test_circuit_function_mut() { -// let bytes = include_bytes!("circuit_function_mut.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } -// -// #[test] -// fn test_circuit_static_function_mut() { -// let bytes = include_bytes!("circuit_static_function_mut.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_circuit_function_mut() { + let bytes = include_bytes!("circuit_function_mut.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} + +#[test] +fn test_circuit_static_function_mut() { + let bytes = include_bytes!("circuit_static_function_mut.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} #[test] fn test_function_input() { diff --git a/compiler/tests/statements/mod.rs b/compiler/tests/statements/mod.rs index b5599d2dd5..4680bee86d 100644 --- a/compiler/tests/statements/mod.rs +++ b/compiler/tests/statements/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, generate_main_input, parse_program}; +use crate::{assert_satisfied, expect_type_inference_error, generate_main_input, parse_program}; use leo_ast::InputValue; pub mod conditional; @@ -57,10 +57,10 @@ fn test_iteration_basic() { assert_satisfied(program); } -// #[test] -// fn test_num_returns_fail() { -// let bytes = include_bytes!("num_returns_fail.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// expect_type_inference_error(error); -// } +#[test] +fn test_num_returns_fail() { + let bytes = include_bytes!("num_returns_fail.leo"); + let error = parse_program(bytes).err().unwrap(); + + expect_type_inference_error(error); +} diff --git a/compiler/tests/syntax/mod.rs b/compiler/tests/syntax/mod.rs index 9cd6aaf3f5..55b7292d2a 100644 --- a/compiler/tests/syntax/mod.rs +++ b/compiler/tests/syntax/mod.rs @@ -18,7 +18,7 @@ use crate::{expect_compiler_error, parse_input, parse_program}; use leo_compiler::errors::{CompilerError, ExpressionError, FunctionError, StatementError}; use leo_grammar::ParserError; use leo_input::InputParserError; -// use leo_type_inference::errors::{FrameError, TypeAssertionError, TypeInferenceError}; +use leo_type_inference::errors::{FrameError, TypeAssertionError, TypeInferenceError}; pub mod identifiers; @@ -75,16 +75,16 @@ fn input_syntax_error() { } } -// #[test] -// fn test_compare_mismatched_types() { -// let bytes = include_bytes!("compare_mismatched_types.leo"); -// let error = parse_program(bytes).err().unwrap(); -// -// // Expect a type inference error. -// match error { -// CompilerError::TypeInferenceError(TypeInferenceError::FrameError(FrameError::TypeAssertionError( -// TypeAssertionError::Error(_), -// ))) => {} -// error => panic!("Expected type inference error, found {}", error), -// } -// } +#[test] +fn test_compare_mismatched_types() { + let bytes = include_bytes!("compare_mismatched_types.leo"); + let error = parse_program(bytes).err().unwrap(); + + // Expect a type inference error. + match error { + CompilerError::TypeInferenceError(TypeInferenceError::FrameError(FrameError::TypeAssertionError( + TypeAssertionError::Error(_), + ))) => {} + error => panic!("Expected type inference error, found {}", error), + } +} diff --git a/type-inference/Cargo.toml b/type-inference/Cargo.toml new file mode 100644 index 0000000000..a0f38f7418 --- /dev/null +++ b/type-inference/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "leo-type-inference" +version = "1.0.4" +authors = [ "The Aleo Team " ] +description = "Checks that a program is correct using type inference" +homepage = "https://aleo.org" +repository = "https://github.com/AleoHQ/leo" +keywords = [ + "aleo", + "cryptography", + "leo", + "programming-language", + "zero-knowledge" +] +categories = [ "cryptography::croptocurrencies", "web-programming" ] +include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ] +license = "GPL-3.0" +edition = "2018" + +[dependencies.leo-ast] +path = "../ast" +version = "1.0.4" + +[dependencies.leo-imports] +path = "../imports" +version = "1.0.4" + +[dependencies.leo-grammar] +path = "../grammar" +version = "1.0.4" + +[dependencies.leo-symbol-table] +path = "../symbol-table" +version = "1.0.4" + +[dependencies.serde_json] +version = "1.0" + +[dependencies.serde] +version = "1.0" + +[dependencies.thiserror] +version = "1.0" \ No newline at end of file diff --git a/type-inference/src/assertions/mod.rs b/type-inference/src/assertions/mod.rs new file mode 100644 index 0000000000..d98b3aad82 --- /dev/null +++ b/type-inference/src/assertions/mod.rs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +pub mod type_assertion; +pub use self::type_assertion::*; + +pub mod type_equality; +pub use self::type_equality::*; + +pub mod type_membership; +pub use self::type_membership::*; + +pub mod type_variable_pair; +pub use self::type_variable_pair::*; diff --git a/type-inference/src/assertions/type_assertion.rs b/type-inference/src/assertions/type_assertion.rs new file mode 100644 index 0000000000..8baf98f5b0 --- /dev/null +++ b/type-inference/src/assertions/type_assertion.rs @@ -0,0 +1,74 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{TypeAssertionError, TypeEquality, TypeMembership, TypeVariablePairs}; +use leo_ast::Span; +use leo_symbol_table::{Type, TypeVariable}; + +use serde::{Deserialize, Serialize}; + +/// A predicate that evaluates equality between two `Types`s. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum TypeAssertion { + Equality(TypeEquality), + Membership(TypeMembership), +} + +impl TypeAssertion { + /// + /// Returns a `TypeAssertion::Equality` predicate from given left and right `Types`s. + /// + pub fn new_equality(left: Type, right: Type, span: &Span) -> Self { + Self::Equality(TypeEquality::new(left, right, span)) + } + + /// + /// Returns a `TypeAssertion::Membership` predicate from given and set `Type`s. + /// + pub fn new_membership(given: Type, set: Vec, span: &Span) -> Self { + Self::Membership(TypeMembership::new(given, set, span)) + } + + /// + /// Returns one or more `TypeVariablePairs` generated by the given `TypeAssertion`. + /// + pub fn pairs(&self) -> Result { + match self { + TypeAssertion::Equality(equality) => equality.pairs(), + TypeAssertion::Membership(membership) => Err(TypeAssertionError::membership_pairs(membership)), + } + } + + /// + /// Substitutes the given type for self if self is equal to the type variable. + /// + pub fn substitute(&mut self, variable: &TypeVariable, type_: &Type) { + match self { + TypeAssertion::Equality(equality) => equality.substitute(variable, type_), + TypeAssertion::Membership(membership) => membership.substitute(variable, type_), + } + } + + /// + /// Checks if the `TypeAssertion` is satisfied. + /// + pub fn evaluate(&self) -> Result<(), TypeAssertionError> { + match self { + TypeAssertion::Equality(equality) => equality.evaluate(), + TypeAssertion::Membership(membership) => membership.evaluate(), + } + } +} diff --git a/type-inference/src/assertions/type_equality.rs b/type-inference/src/assertions/type_equality.rs new file mode 100644 index 0000000000..f20fd696ce --- /dev/null +++ b/type-inference/src/assertions/type_equality.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{TypeAssertionError, TypeVariablePairs}; +use leo_ast::Span; +use leo_symbol_table::{Type, TypeVariable}; + +use serde::{Deserialize, Serialize}; + +/// A predicate that evaluates equality between two `Type`s. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct TypeEquality { + left: Type, + right: Type, + span: Span, +} + +impl TypeEquality { + /// + /// Returns a `TypeEquality` predicate from given left and right `Types`s + /// + pub fn new(left: Type, right: Type, span: &Span) -> Self { + Self { + left, + right, + span: span.to_owned(), + } + } + + /// + /// Substitutes the given `TypeVariable` for each `Types` in the `TypeEquality`. + /// + pub fn substitute(&mut self, variable: &TypeVariable, type_: &Type) { + self.left.substitute(variable, type_); + self.right.substitute(variable, type_); + } + + /// + /// Checks if the `self.left` == `self.right`. + /// + pub fn evaluate(&self) -> Result<(), TypeAssertionError> { + if self.left.eq(&self.right) { + Ok(()) + } else { + Err(TypeAssertionError::equality_failed(&self.left, &self.right, &self.span)) + } + } + + /// + /// Returns the (type variable, type) pair from this assertion. + /// + pub fn pairs(&self) -> Result { + TypeVariablePairs::new(self.left.to_owned(), self.right.to_owned(), &self.span) + } +} diff --git a/type-inference/src/assertions/type_membership.rs b/type-inference/src/assertions/type_membership.rs new file mode 100644 index 0000000000..5df0edb0c7 --- /dev/null +++ b/type-inference/src/assertions/type_membership.rs @@ -0,0 +1,71 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TypeAssertionError; +use leo_ast::Span; +use leo_symbol_table::{Type, TypeVariable}; + +use serde::{Deserialize, Serialize}; + +/// A predicate that evaluates to true if the given type is equal to a member in the set vector of types. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct TypeMembership { + given: Type, + set: Vec, + span: Span, +} + +impl TypeMembership { + /// + /// Returns a `TypeMembership` predicate from given and set `Type`s. + /// + pub fn new(given: Type, set: Vec, span: &Span) -> Self { + Self { + given, + set, + span: span.to_owned(), + } + } + + /// + /// Substitutes the given `TypeVariable` for each `Type` in the `TypeMembership`. + /// + pub fn substitute(&mut self, variable: &TypeVariable, type_: &Type) { + self.given.substitute(variable, type_) + } + + /// + /// Returns true if the given type is equal to a member of the set. + /// + pub fn evaluate(&self) -> Result<(), TypeAssertionError> { + if self.set.contains(&self.given) { + Ok(()) + } else { + Err(TypeAssertionError::membership_failed( + &self.given, + &self.set, + &self.span, + )) + } + } + + /// + /// Returns the self.span. + /// + pub fn span(&self) -> &Span { + &self.span + } +} diff --git a/type-inference/src/assertions/type_variable_pair.rs b/type-inference/src/assertions/type_variable_pair.rs new file mode 100644 index 0000000000..e7cca2b2e6 --- /dev/null +++ b/type-inference/src/assertions/type_variable_pair.rs @@ -0,0 +1,137 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TypeAssertionError; +use leo_ast::Span; +use leo_symbol_table::{get_array_element_type, Type, TypeVariable}; + +/// A type variable -> type pair. +pub struct TypeVariablePair(TypeVariable, Type); + +impl TypeVariablePair { + pub fn first(&self) -> &TypeVariable { + &self.0 + } + + pub fn second(&self) -> &Type { + &self.1 + } +} + +/// A vector of `TypeVariablePair`s. +pub struct TypeVariablePairs(Vec); + +impl Default for TypeVariablePairs { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl TypeVariablePairs { + /// + /// Returns a new `TypeVariablePairs` struct from the given left and right types. + /// + pub fn new(left: Type, right: Type, span: &Span) -> Result { + let mut pairs = Self::default(); + + // Push all `TypeVariablePair`s. + pairs.push_pairs(left, right, span)?; + + Ok(pairs) + } + + /// + /// Returns true if the self vector has no pairs. + /// + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// + /// Returns the self vector of pairs. + /// + pub fn get_pairs(&self) -> &[TypeVariablePair] { + &self.0 + } + + /// + /// Pushes a new `TypeVariablePair` struct to self. + /// + pub fn push(&mut self, variable: TypeVariable, type_: Type) { + // Create a new type variable -> type pair. + let pair = TypeVariablePair(variable, type_); + + // Push the pair to the self vector. + self.0.push(pair); + } + + /// + /// Checks if the given left or right type contains a `TypeVariable`. + /// If a `TypeVariable` is found, create a new `TypeVariablePair` between the given left + /// and right type. + /// + pub fn push_pairs(&mut self, left: Type, right: Type, span: &Span) -> Result<(), TypeAssertionError> { + match (left, right) { + (Type::TypeVariable(variable), type_) => { + self.push(variable, type_); + Ok(()) + } + (type_, Type::TypeVariable(variable)) => { + self.push(variable, type_); + Ok(()) + } + (Type::Array(left_type), Type::Array(right_type)) => self.push_pairs_array(*left_type, *right_type, span), + (Type::Tuple(left_types), Type::Tuple(right_types)) => { + self.push_pairs_tuple(left_types.into_iter(), right_types.into_iter(), span) + } + (_, _) => Ok(()), // No `TypeVariable` found so we do not push any pairs. + } + } + + /// + /// Checks if the given left or right array type contains a `TypeVariable`. + /// If a `TypeVariable` is found, create a new `TypeVariablePair` between the given left + /// and right type. + /// + fn push_pairs_array(&mut self, left_type: Type, right_type: Type, span: &Span) -> Result<(), TypeAssertionError> { + // Get both array element types before comparison. + let array1_element = get_array_element_type(&left_type); + let array2_element = get_array_element_type(&right_type); + + // Compare the array element types. + self.push_pairs(array1_element.to_owned(), array2_element.to_owned(), span) + } + + /// + /// Checks if any given left or right tuple type contains a `TypeVariable`. + /// If a `TypeVariable` is found, create a new `TypeVariablePair` between the given left + /// and right type. + /// + fn push_pairs_tuple( + &mut self, + left_types: impl Iterator, + right_types: impl Iterator, + span: &Span, + ) -> Result<(), TypeAssertionError> { + // Iterate over each left == right pair of types. + for (left, right) in left_types.into_iter().zip(right_types) { + // Check for `TypeVariablePair`s. + self.push_pairs(left, right, span)?; + } + + Ok(()) + } +} diff --git a/type-inference/src/errors/frame.rs b/type-inference/src/errors/frame.rs new file mode 100644 index 0000000000..36e8576832 --- /dev/null +++ b/type-inference/src/errors/frame.rs @@ -0,0 +1,240 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{ScopeError, TypeAssertionError}; +use leo_ast::{Error as FormattedError, Expression, Identifier, Span}; +use leo_symbol_table::{Type, TypeError}; + +use std::path::Path; + +/// Errors encountered when tracking variable names in a program. +#[derive(Debug, Error)] +pub enum FrameError { + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + ScopeError(#[from] ScopeError), + + #[error("{}", _0)] + TypeAssertionError(#[from] TypeAssertionError), + + #[error("{}", _0)] + TypeError(#[from] TypeError), +} + +impl FrameError { + /// + /// Set the filepath for the error stacktrace + /// + pub fn set_path(&mut self, path: &Path) { + match self { + FrameError::Error(error) => error.set_path(path), + FrameError::ScopeError(error) => error.set_path(path), + FrameError::TypeAssertionError(error) => error.set_path(path), + FrameError::TypeError(error) => error.set_path(path), + } + } + + /// + /// Return a new formatted error with a given message and span information + /// + fn new_from_span(message: String, span: &Span) -> Self { + FrameError::Error(FormattedError::new_from_span(message, span.to_owned())) + } + + /// + /// Attempted to access the index of a non-array type. + /// + pub fn array_access(actual: &Type, span: &Span) -> Self { + let message = format!("Cannot access the index of non-array type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to access the `Self` type outside of a circuit context. + /// + pub fn circuit_self(span: &Span) -> Self { + let message = "The `Self` keyword is only valid inside a circuit context.".to_string(); + + Self::new_from_span(message, span) + } + + /// + /// Two variables have been defined with the same name. + /// + pub fn duplicate_variable(name: &str, span: &Span) -> Self { + let message = format!("Duplicate variable definition found for `{}`", name); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to create an empty array in a Leo program. + /// + /// Arrays in Leo are not resizeable so defining empty arrays are effectively dead code. + /// + pub fn empty_array(span: &Span) -> Self { + let message = "Cannot create an empty array in a Leo program.".to_string(); + + Self::new_from_span(message, span) + } + + /// + /// Expected a circuit name but found a different type. + /// + pub fn invalid_circuit(type_: Type, span: &Span) -> Self { + let message = format!("Expected a circuit type. Found type `{}`.", type_); + + Self::new_from_span(message, span) + } + + /// + /// Expected a function name but found a different expression. + /// + pub fn invalid_function(expression: &Expression, span: &Span) -> Self { + let message = format!("Expected a function name. Found expression `{}`.", expression); + + Self::new_from_span(message, span) + } + + /// + /// Expected a usize number for the index. + /// + pub fn invalid_index(actual: String, span: &Span) -> Self { + let message = format!("Expected constant number for index, found `{}`", actual); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to call non-static member using `::`. + /// + pub fn invalid_member_access(identifier: &Identifier) -> Self { + let message = format!("non-static member `{}` must be accessed using `.` syntax.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to use the spread operator on a non-array type. + /// + pub fn invalid_spread(actual: Type, span: &Span) -> Self { + let message = format!( + "The spread operator `...` can only be applied to array types. Found type `{}`.", + actual + ); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to call static member using `.`. + /// + pub fn invalid_static_access(identifier: &Identifier) -> Self { + let message = format!("Static member `{}` must be accessed using `::` syntax.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to create a circuit with the incorrect number of member variables. + /// + pub fn num_circuit_variables(expected: usize, actual: usize, span: &Span) -> Self { + let message = format!("Circuit expected {} variables, found {} variables.", expected, actual); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to call a function with the incorrect number of inputs. + /// + pub fn num_inputs(expected: usize, actual: usize, span: &Span) -> Self { + let message = format!( + "Function expected {} input variables, found {} inputs.", + expected, actual + ); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to access the index of a non-tuple type. + /// + pub fn tuple_access(actual: &Type, span: &Span) -> Self { + let message = format!("Cannot access the index of non-tuple type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to call a circuit type that is not defined in the current context. + /// + pub fn undefined_circuit(identifier: &Identifier) -> Self { + let message = format!("The circuit `{}` is not defined.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to call a circuit function that is not defined in the current context. + /// + pub fn undefined_circuit_function(identifier: &Identifier) -> Self { + let message = format!("The circuit function `{}` is not defined.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to call a function that is not defined in the current context. + /// + pub fn undefined_function(identifier: &Identifier) -> Self { + let message = format!("The function `{}` is not defined.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to call a variable that is not defined in the current context. + /// + pub fn undefined_variable(identifier: &Identifier) -> Self { + let message = format!("The variable `{}` is not defined.", identifier); + + Self::new_from_span(message, &identifier.span) + } + + /// + /// Attempted to assign a tuple of variables to a single value. + /// + pub fn not_enough_values(span: &Span) -> Self { + let message = "Expected a tuple type for multiple defined variables".to_string(); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to assign a tuple with a different number of variables than values. + /// + pub fn invalid_number_of_values(expected: usize, actual: usize, span: &Span) -> Self { + let message = format!( + "Incorrect number of defined variables. Expected `{}`, found `{}`.", + expected, actual + ); + + Self::new_from_span(message, span) + } +} diff --git a/type-inference/src/errors/mod.rs b/type-inference/src/errors/mod.rs new file mode 100644 index 0000000000..629a558e2f --- /dev/null +++ b/type-inference/src/errors/mod.rs @@ -0,0 +1,30 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +pub mod frame; +pub use self::frame::*; + +pub mod scope; +pub use self::scope::*; + +pub mod type_assertion; +pub use self::type_assertion::*; + +pub mod type_inference; +pub use self::type_inference::*; + +pub mod variable_table; +pub use self::variable_table::*; diff --git a/type-inference/src/errors/scope.rs b/type-inference/src/errors/scope.rs new file mode 100644 index 0000000000..e31c3aaea4 --- /dev/null +++ b/type-inference/src/errors/scope.rs @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::VariableTableError; +use leo_ast::Error as FormattedError; + +use std::path::Path; + +/// Errors encountered when evaluating variables in a scope. +#[derive(Debug, Error)] +pub enum ScopeError { + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + VariableTableError(#[from] VariableTableError), +} + +impl ScopeError { + /// + /// Set the filepath for the error stacktrace. + /// + pub fn set_path(&mut self, path: &Path) { + match self { + ScopeError::Error(error) => error.set_path(path), + ScopeError::VariableTableError(error) => error.set_path(path), + } + } +} diff --git a/type-inference/src/errors/type_assertion.rs b/type-inference/src/errors/type_assertion.rs new file mode 100644 index 0000000000..a2da4a6c08 --- /dev/null +++ b/type-inference/src/errors/type_assertion.rs @@ -0,0 +1,89 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TypeMembership; + +use leo_ast::{Error as FormattedError, Span}; +use leo_symbol_table::Type; + +use std::path::Path; + +/// Errors encountered when attempting to solve a type assertion. +#[derive(Debug, Error)] +pub enum TypeAssertionError { + #[error("{}", _0)] + Error(#[from] FormattedError), +} + +impl TypeAssertionError { + /// + /// Set the filepath for the error stacktrace. + /// + pub fn set_path(&mut self, path: &Path) { + match self { + TypeAssertionError::Error(error) => error.set_path(path), + } + } + + /// + /// Returns a new formatted error with a given message and span information. + /// + fn new_from_span(message: String, span: &Span) -> Self { + TypeAssertionError::Error(FormattedError::new_from_span(message, span.to_owned())) + } + + /// + /// Found mismatched types during program parsing. + /// + pub fn equality_failed(left: &Type, right: &Type, span: &Span) -> Self { + let message = format!("Mismatched types. Expected type `{}`, found type `{}`.", left, right); + + Self::new_from_span(message, span) + } + + /// + /// Given type is not a member of the set of expected types. + /// + pub fn membership_failed(given: &Type, set: &[Type], span: &Span) -> Self { + let message = format!( + "Mismatched types. Given type `{}` is not in the expected type set `{:?}`.", + given, set + ); + + Self::new_from_span(message, span) + } + + /// + /// Attempted to generate pairs from a membership assertion. + /// + pub fn membership_pairs(membership: &TypeMembership) -> Self { + let message = "Cannot generate a type variable -> type pair for the given type membership".to_string(); + + Self::new_from_span(message, membership.span()) + } + + /// + /// Mismatched array type dimensions. + /// + pub fn array_dimensions(dimensions1: &[usize], dimensions2: &[usize], span: &Span) -> Self { + let message = format!( + "Expected array with dimensions `{:?}`, found array with dimensions `{:?}`.", + dimensions1, dimensions2 + ); + + Self::new_from_span(message, span) + } +} diff --git a/type-inference/src/errors/type_inference.rs b/type-inference/src/errors/type_inference.rs new file mode 100644 index 0000000000..dac08fc59b --- /dev/null +++ b/type-inference/src/errors/type_inference.rs @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::FrameError; +use leo_ast::Error as FormattedError; + +use std::path::Path; + +/// Errors encountered when running type inference checks. +#[derive(Debug, Error)] +pub enum TypeInferenceError { + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + FrameError(#[from] FrameError), +} + +impl TypeInferenceError { + /// + /// Set the filepath for the error stacktrace. + /// + pub fn set_path(&mut self, path: &Path) { + match self { + TypeInferenceError::Error(error) => error.set_path(path), + TypeInferenceError::FrameError(error) => error.set_path(path), + } + } +} diff --git a/type-inference/src/errors/variable_table.rs b/type-inference/src/errors/variable_table.rs new file mode 100644 index 0000000000..bb26dfb7e6 --- /dev/null +++ b/type-inference/src/errors/variable_table.rs @@ -0,0 +1,53 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use leo_ast::{Error as FormattedError, Span}; + +use std::path::Path; + +/// Errors encountered when tracking variable names in a program. +#[derive(Debug, Error)] +pub enum VariableTableError { + #[error("{}", _0)] + Error(#[from] FormattedError), +} + +impl VariableTableError { + /// + /// Set the filepath for the error stacktrace + /// + pub fn set_path(&mut self, path: &Path) { + match self { + VariableTableError::Error(error) => error.set_path(path), + } + } + + /// + /// Return a new formatted error with a given message and span information + /// + fn new_from_span(message: String, span: Span) -> Self { + VariableTableError::Error(FormattedError::new_from_span(message, span)) + } + + /// + /// Attempted to lookup a variable name that does not exist in the table. + /// + pub fn undefined_variable_name(name: &str, span: &Span) -> Self { + let message = format!("Cannot find variable `{}` in this scope.", name); + + Self::new_from_span(message, span.clone()) + } +} diff --git a/type-inference/src/lib.rs b/type-inference/src/lib.rs new file mode 100644 index 0000000000..ab20d42409 --- /dev/null +++ b/type-inference/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +//! A type inference check for a Leo program. +//! +//! This module contains the [`TypeInference`] type, which stores information needed to run a type +//! inference check over a program. +//! +//! A new [`TypeInference`] type can be created from a [`LeoAst`] type and a [`Symbol Table`]. + +#[macro_use] +extern crate thiserror; + +pub mod assertions; +pub use self::assertions::*; + +pub mod type_inference; +pub use self::type_inference::*; + +pub mod errors; +pub use self::errors::*; + +pub mod objects; +pub use self::objects::*; diff --git a/type-inference/src/objects/frame.rs b/type-inference/src/objects/frame.rs new file mode 100644 index 0000000000..0daeba20dd --- /dev/null +++ b/type-inference/src/objects/frame.rs @@ -0,0 +1,1210 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{FrameError, Scope, TypeAssertion}; +use leo_ast::{ + ArrayDimensions, + Assignee, + AssigneeAccess, + CircuitVariableDefinition, + ConditionalNestedOrEndStatement, + ConditionalStatement, + Declare, + Expression, + Function, + Identifier, + IntegerType, + PositiveNumber, + RangeOrExpression, + Span, + SpreadOrExpression, + Statement, + Variables, +}; +use leo_symbol_table::{Attribute, CircuitFunctionType, CircuitType, FunctionType, SymbolTable, Type, TypeVariable}; + +/// A vector of `TypeAssertion` predicates created from a function body. +#[derive(Clone)] +pub struct Frame { + pub function_type: FunctionType, + pub self_type: Option, + pub scopes: Vec, + pub statements: Vec, + pub type_assertions: Vec, + pub user_defined_types: SymbolTable, +} + +impl Frame { + /// + /// Collects a vector of `TypeAssertion` predicates from a function. + /// + pub fn new_function( + function: Function, + self_type: Option, + parent_scope: Option, + user_defined_types: SymbolTable, + ) -> Result { + let name = &function.identifier.name; + + // Get function type from symbol table. + let function_type = user_defined_types.get_function_type(name).unwrap().clone(); + + // Create a new scope for the function variables. + let mut scope = Scope::new(parent_scope); + + // Initialize function inputs as variables. + scope.insert_function_inputs(&function_type.inputs)?; + + // Create new list of scopes for frame. + let scopes = vec![scope]; + + // Create new frame struct. + // Update variables when encountering let/const variable definitions. + let mut frame = Self { + function_type, + self_type, + scopes, + statements: function.statements, + type_assertions: vec![], + user_defined_types, + }; + + // Create type assertions for function statements + frame.parse_statements()?; + + Ok(frame) + } + + /// + /// Collects vector of `TypeAssertion` predicates from a circuit function. + /// + pub fn new_circuit_function( + function: Function, + self_type: CircuitType, + parent_scope: Scope, + user_defined_types: SymbolTable, + ) -> Result { + let identifier = &function.identifier; + + // Find function name in circuit members. + let circuit_function_type = self_type.member_function_type(identifier).unwrap().to_owned(); + + // Create a new scope for the function variables. + let mut scope = Scope::new(Some(parent_scope)); + + // Initialize function inputs as variables. + scope.insert_function_inputs(&circuit_function_type.function.inputs)?; + + // Create new list of scopes for frame. + let scopes = vec![scope]; + + // Create new frame struct. + // Update variables when encountering let/const variable definitions. + let mut frame = Self { + function_type: circuit_function_type.function, + self_type: Some(self_type), + scopes, + statements: function.statements, + type_assertions: Vec::new(), + user_defined_types, + }; + + // Create type assertions for function statements + frame.parse_statements()?; + + Ok(frame) + } + + /// + /// Pushes a new variable `Scope` to the list of scopes in the current `Frame`. + /// + fn push_scope(&mut self, scope: Scope) { + self.scopes.push(scope) + } + + /// + /// Removes and returns the most recent `Scope` from the list of scopes in the current `Frame`. + /// + fn pop_scope(&mut self) -> Option { + self.scopes.pop() + } + + /// + /// Insert a variable into the symbol table in the current scope. + /// + fn insert_variable(&mut self, name: String, type_: Type, span: &Span) -> Result<(), FrameError> { + // Modify the current scope. + let scope = self.scopes.last_mut().unwrap(); + + // Insert the variable name -> type. + match scope.variables.insert(name.clone(), type_) { + Some(_type) => Err(FrameError::duplicate_variable(&name, span)), + None => Ok(()), + } + } + + /// + /// Get a variable's type from the symbol table in the current scope. + /// + fn get_variable(&self, name: &str) -> Option<&Type> { + // Lookup in the current scope. + let scope = self.scopes.last().unwrap(); + + // Get the variable by name. + scope.get_variable(name) + } + + /// + /// Get a function's type from the user defined types in the current scope. + /// + fn get_function(&self, name: &str) -> Option<&FunctionType> { + self.user_defined_types.get_function_type(name) + } + + /// + /// Get a circuit's type from the user defined types in the current scope. + /// + fn get_circuit(&self, name: &str) -> Option<&CircuitType> { + self.user_defined_types.get_circuit_type(name) + } + + /// + /// Creates a new equality type assertion between the given types. + /// + fn assert_equal(&mut self, left: Type, right: Type, span: &Span) { + let type_assertion = TypeAssertion::new_equality(left, right, span); + + self.type_assertions.push(type_assertion); + } + + /// + /// Creates a new membership type assertion between a given and set of types. + /// + fn assert_membership(&mut self, given: Type, set: Vec, span: &Span) { + let type_assertion = TypeAssertion::new_membership(given, set, span); + + self.type_assertions.push(type_assertion); + } + + /// + /// Creates a new membership type assertion between a given and the set of negative integer types. + /// + fn assert_negative_integer(&mut self, given: &Type, span: &Span) { + let negative_integer_types = Type::negative_integer_types(); + + self.assert_membership(given.clone(), negative_integer_types, span) + } + + /// + /// Creates a new membership type assertion between a given and the set of all integer types. + /// + fn assert_integer(&mut self, given: &Type, span: &Span) { + let integer_types = Type::integer_types(); + + self.assert_membership(given.clone(), integer_types, span) + } + + /// + /// Creates a new membership type assertion between a given and the set of index types. + /// + fn assert_index(&mut self, given: &Type, span: &Span) { + let index_types = Type::index_types(); + + self.assert_membership(given.clone(), index_types, span) + } + + /// + /// Collects a vector of `TypeAssertion` predicates from a vector of statements. + /// + fn parse_statements(&mut self) -> Result<(), FrameError> { + for statement in self.statements.clone() { + self.parse_statement(&statement)?; + } + + Ok(()) + } + + /// + /// Collects a vector of `TypeAssertion` predicates from a statement. + /// + fn parse_statement(&mut self, statement: &Statement) -> Result<(), FrameError> { + match statement { + Statement::Return(expression, span) => self.parse_return(expression, span), + Statement::Definition(declare, variables, expression, span) => { + self.parse_definition(declare, variables, expression, span) + } + Statement::Assign(assignee, expression, span) => self.parse_assign(assignee, expression, span), + Statement::Conditional(conditional, span) => self.parse_statement_conditional(conditional, span), + Statement::Iteration(identifier, from_to, statements, span) => { + self.parse_iteration(identifier, from_to, statements, span) + } + Statement::Expression(expression, span) => self.parse_statement_expression(expression, span), + Statement::Console(_console_call) => Ok(()), // Console function calls do not generate type assertions. + } + } + + /// + /// Collects `TypeAssertion` predicates from a return statement. + /// + fn parse_return(&mut self, expression: &Expression, span: &Span) -> Result<(), FrameError> { + // Get the function output type. + let output_type = &self.function_type.output.type_; + + // Create the left hand side of a type assertion. + let left = output_type.clone(); + + // Create the right hand side from the statement return expression. + let right = self.parse_expression(expression)?; + + // Create a new type assertion for the statement return. + self.assert_equal(left, right, span); + + Ok(()) + } + + /// + /// Collects `Type Assertion` predicates from a definition statement. + /// + fn parse_definition( + &mut self, + _declare: &Declare, + variables: &Variables, + expression: &Expression, + span: &Span, + ) -> Result<(), FrameError> { + // Parse the definition expression. + let actual_type = self.parse_expression(expression)?; + + // Check if an explicit type is given. + if let Some(type_) = variables.type_.clone() { + // Check the expected type. + let expected_type = match self.self_type { + Some(ref circuit_type) => Type::new_from_circuit( + &self.user_defined_types, + type_, + circuit_type.identifier.clone(), + span.clone(), + ) + .unwrap(), + None => Type::new(&self.user_defined_types, type_, span.clone()).unwrap(), + }; + + // Assert that the expected type is equal to the actual type. + self.assert_equal(expected_type, actual_type.clone(), span) + } + + // Check for multiple defined variables. + if variables.names.len() == 1 { + // Insert variable into symbol table + let variable = variables.names[0].clone(); + + self.insert_variable(variable.identifier.name, actual_type, span)?; + } else { + // Expect a tuple type. + let types = match actual_type { + Type::Tuple(types) => types, + _ => return Err(FrameError::not_enough_values(span)), + }; + + // Check number of variables == number of types. + if types.len() != variables.names.len() { + return Err(FrameError::invalid_number_of_values( + types.len(), + variables.names.len(), + span, + )); + } + + // Insert variables into symbol table + for (variable, type_) in variables.names.iter().zip(types) { + self.insert_variable(variable.identifier.name.clone(), type_, span)?; + } + } + + Ok(()) + } + + /// + /// Asserts that the assignee's type is equal to the `Expression` type. + /// + fn parse_assign(&mut self, assignee: &Assignee, expression: &Expression, span: &Span) -> Result<(), FrameError> { + // Parse assignee type. + let assignee_type = self.parse_assignee(assignee, span)?; + + // Parse expression type. + let expression_type = self.parse_expression(expression)?; + + // Assert that the assignee_type == expression_type. + self.assert_equal(assignee_type, expression_type, span); + + Ok(()) + } + + /// + /// Returns the type of the assignee. + /// + fn parse_assignee(&mut self, assignee: &Assignee, span: &Span) -> Result { + // Get the type of the assignee variable. + let mut type_ = if assignee.identifier.is_self() { + // If the variable is the self keyword, then return the self.circuit_type + let self_type = self.self_type_or_error(span)?; + + Type::Circuit(self_type.identifier) + } else { + // Otherwise, lookup the variable by name in the symbol table. + self.get_variable(&assignee.identifier.name) + .map(|type_| type_.to_owned()) + .ok_or_else(|| FrameError::undefined_variable(&assignee.identifier))? + }; + + // Iteratively evaluate assignee access types. + for access in &assignee.accesses { + let access_type = match access { + AssigneeAccess::Array(r_or_e) => self.parse_array_access(type_, r_or_e, span), + AssigneeAccess::Tuple(index, _) => self.parse_tuple_access(type_, &index, span), + AssigneeAccess::Member(identifier) => self.parse_circuit_member_access(type_, identifier, span), + }?; + + type_ = access_type; + } + + Ok(type_) + } + + /// + /// Collects `TypeAssertion` predicates from a block of statements. + /// + fn parse_block(&mut self, statements: &[Statement], _span: &Span) -> Result<(), FrameError> { + // Push new scope. + let scope = Scope::new(self.scopes.last().cloned()); + self.push_scope(scope); + + // Parse all statements. + for statement in statements { + self.parse_statement(&statement)?; + } + + // Pop out of scope. + let _scope = self.pop_scope(); + + Ok(()) + } + + /// + /// Collects `TypeAssertion` predicates from a conditional statement. + /// + /// Creates a new scope for each code block in the conditional. + /// + fn parse_statement_conditional( + &mut self, + conditional: &ConditionalStatement, + span: &Span, + ) -> Result<(), FrameError> { + // Parse the condition expression. + let condition = self.parse_expression(&conditional.condition)?; + + // Assert that the condition is a boolean type. + let boolean_type = Type::Boolean; + self.assert_equal(boolean_type, condition, span); + + // Parse conditional statements. + self.parse_block(&conditional.statements, span)?; + + // Parse conditional or end. + if let Some(cond_or_end) = &conditional.next { + self.parse_conditional_nested_or_end(cond_or_end, span)?; + } + + Ok(()) + } + + /// + /// Collects `TypeAssertion` predicates from a conditional statement. + /// + fn parse_conditional_nested_or_end( + &mut self, + cond_or_end: &ConditionalNestedOrEndStatement, + span: &Span, + ) -> Result<(), FrameError> { + match cond_or_end { + ConditionalNestedOrEndStatement::Nested(nested) => self.parse_statement_conditional(nested, span), + ConditionalNestedOrEndStatement::End(statements) => self.parse_block(statements, span), + } + } + + /// + /// Collects `TypeAssertion` predicates from an iteration statement. + /// + fn parse_iteration( + &mut self, + identifier: &Identifier, + from_to: &(Expression, Expression), + statements: &[Statement], + span: &Span, + ) -> Result<(), FrameError> { + // Insert variable into symbol table with u32 type. + let u32_type = Type::IntegerType(IntegerType::U32); + let _expect_none = self.insert_variable(identifier.name.to_owned(), u32_type.clone(), span); + + // Parse `from` and `to` expressions. + let from_type = self.parse_expression(&from_to.0)?; + let to_type = self.parse_expression(&from_to.1)?; + + // Assert `from` and `to` types are a u32 or implicit. + self.assert_equal(u32_type.clone(), from_type, span); + self.assert_equal(u32_type, to_type, span); + + // Parse block of statements. + self.parse_block(statements, span) + } + + /// + /// Asserts that the statement `UnresolvedExpression` returns an empty tuple. + /// + fn parse_statement_expression(&mut self, expression: &Expression, span: &Span) -> Result<(), FrameError> { + // Create empty tuple type. + let expected_type = Type::Tuple(Vec::new()); + + // Parse the actual type of the expression. + let actual_type = self.parse_expression(expression)?; + + self.assert_equal(expected_type, actual_type, span); + + Ok(()) + } + + /// + /// Returns the type of an expression. + /// + fn parse_expression(&mut self, expression: &Expression) -> Result { + match expression { + // Type variables + Expression::Identifier(identifier) => self.parse_identifier(identifier), + + // Explicit types + Expression::Boolean(_, _) => Ok(Type::Boolean), + Expression::Address(_, _) => Ok(Type::Address), + Expression::Field(_, _) => Ok(Type::Field), + Expression::Group(_) => Ok(Type::Group), + Expression::Implicit(name, span) => Ok(Self::parse_implicit(Identifier::new_with_span(name, span))), + Expression::Integer(integer_type, _, _) => Ok(Type::IntegerType(integer_type.clone())), + + // Number operations + Expression::Add(left_right, span) => { + self.parse_integer_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Sub(left_right, span) => { + self.parse_integer_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Mul(left_right, span) => { + self.parse_integer_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Div(left_right, span) => { + self.parse_integer_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Pow(left_right, span) => { + self.parse_integer_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Negate(expression, span) => self.parse_negate_expression(expression, span), + + // Boolean operations + Expression::Not(expression, span) => self.parse_boolean_expression(expression, span), + Expression::Or(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::And(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Eq(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Ge(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Gt(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Le(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + Expression::Lt(left_right, span) => { + self.parse_boolean_binary_expression(&left_right.0, &left_right.1, span) + } + + // Conditionals + Expression::IfElse(triplet, span) => { + self.parse_conditional_expression(&triplet.0, &triplet.1, &triplet.2, span) + } + + // Arrays + Expression::ArrayInline(expressions, span) => self.parse_array(expressions, span), + Expression::ArrayInitializer(array_w_dimensions, span) => { + self.parse_array_initializer(&array_w_dimensions.0, &array_w_dimensions.1, span) + } + Expression::ArrayAccess(array_w_index, span) => { + self.parse_expression_array_access(&array_w_index.0, &array_w_index.1, span) + } + + // Tuples + Expression::Tuple(expressions, span) => self.parse_tuple(expressions, span), + Expression::TupleAccess(tuple_w_index, span) => { + self.parse_expression_tuple_access(&tuple_w_index.0, &tuple_w_index.1, span) + } + + // Circuits + Expression::Circuit(identifier, members, span) => self.parse_circuit(identifier, members, span), + Expression::CircuitMemberAccess(expression, identifier, span) => { + self.parse_expression_circuit_member_access(expression, identifier, span) + } + Expression::CircuitStaticFunctionAccess(expression, identifier, span) => { + self.parse_static_circuit_function_access(expression, identifier, span) + } + + // Functions + Expression::FunctionCall(name, arguments, span) => self.parse_function_call(name, arguments, span), + Expression::CoreFunctionCall(name, arguments, span) => self.parse_core_function_call(name, arguments, span), + } + } + + /// + /// Returns the type of the identifier in the symbol table. + /// + fn parse_identifier(&self, identifier: &Identifier) -> Result { + // Check Self type. + if identifier.is_self() { + // Check for frame circuit self type. + let circuit_type = self.self_type_or_error(&identifier.span)?; + + // Return new type with circuit identifier. + return Ok(Type::Circuit(circuit_type.identifier)); + } + + // Check variable symbol table. + if let Some(type_) = self.get_variable(&identifier.name) { + return Ok(type_.to_owned()); + }; + + // Check function symbol table. + if let Some(function_type) = self.get_function(&identifier.name) { + return Ok(Type::Function(function_type.identifier.to_owned())); + }; + + // Check circuit symbol table. + if let Some(circuit_type) = self.get_circuit(&identifier.name) { + return Ok(Type::Circuit(circuit_type.identifier.to_owned())); + } + + Ok(Self::parse_implicit(identifier.to_owned())) + } + + /// + /// Returns a new type variable from a given identifier + /// + fn parse_implicit(identifier: Identifier) -> Type { + Type::TypeVariable(TypeVariable::from(identifier)) + } + + /// + /// Returns the type of a binary expression. + /// + fn parse_binary_expression( + &mut self, + left: &Expression, + right: &Expression, + span: &Span, + ) -> Result { + // Get the left expression type. + let left_type = self.parse_expression(left)?; + + // Get the right expression type. + let right_type = self.parse_expression(right)?; + + // Create a type assertion left_type == right_type. + self.assert_equal(left_type.clone(), right_type, span); + + Ok(left_type) + } + + /// + /// Returns the `Type` of the expression after the binary operation. + /// + /// Asserts that the `Type` is an integer. + /// + fn parse_integer_binary_expression( + &mut self, + left: &Expression, + right: &Expression, + span: &Span, + ) -> Result { + let type_ = self.parse_binary_expression(left, right, span)?; + + // Assert that the type is an integer. + self.assert_integer(&type_, span); + + Ok(type_) + } + + /// + /// Returns the `Boolean` type if the expression is a `Boolean` type. + /// + fn parse_boolean_expression(&mut self, expression: &Expression, span: &Span) -> Result { + // Return the `Boolean` type + let boolean_type = Type::Boolean; + + // Get the type of the expression + let expression_type = self.parse_expression(expression)?; + + // Assert that the type is a boolean. + self.assert_equal(boolean_type.clone(), expression_type, span); + + Ok(boolean_type) + } + + /// + /// Returns the `Type` of the expression being negated. Must be a negative integer type. + /// + fn parse_negate_expression(&mut self, expression: &Expression, span: &Span) -> Result { + // Parse the expression type. + let type_ = self.parse_expression(expression)?; + + // Assert that this integer can be negated. + self.assert_negative_integer(&type_, span); + + Ok(type_) + } + + /// + /// Returns the `Boolean` type if the binary expression is a `Boolean` type. + /// + fn parse_boolean_binary_expression( + &mut self, + left: &Expression, + right: &Expression, + span: &Span, + ) -> Result { + // Create the `Boolean` type. + let boolean_type = Type::Boolean; + + // Create a new type assertion for the binary expression + let _binary_expression_type = self.parse_binary_expression(left, right, span)?; + + // Return the `Boolean` type. + Ok(boolean_type) + } + + /// + /// Returns the type of the conditional expression. + /// + fn parse_conditional_expression( + &mut self, + condition: &Expression, + first: &Expression, + second: &Expression, + span: &Span, + ) -> Result { + // Check that the type of the condition expression is a boolean. + let _condition_type = self.parse_boolean_expression(condition, span)?; + + // Check that the types of the first and second expression are equal. + self.parse_binary_expression(first, second, span) + } + + /// + /// Returns the type of the tuple expression. + /// + fn parse_tuple(&mut self, expressions: &[Expression], _span: &Span) -> Result { + let mut types = Vec::with_capacity(expressions.len()); + + // Parse all tuple expressions. + for expression in expressions { + let type_ = self.parse_expression(expression)?; + + types.push(type_) + } + + Ok(Type::Tuple(types)) + } + + /// + /// Returns the type of the accessed tuple element when called as an expression. + /// + fn parse_expression_tuple_access( + &mut self, + expression: &Expression, + index: &PositiveNumber, + span: &Span, + ) -> Result { + // Parse the tuple expression which could be a variable with type tuple. + let type_ = self.parse_expression(expression)?; + + // Parse the tuple access. + self.parse_tuple_access(type_, index, span) + } + + /// + /// Returns the type of the accessed tuple element. + /// + fn parse_tuple_access(&mut self, type_: Type, index: &PositiveNumber, span: &Span) -> Result { + // Check the type is a tuple. + let mut elements = match type_ { + Type::Tuple(elements) => elements, + type_ => return Err(FrameError::tuple_access(&type_, span)), + }; + + // Parse index `String` to `usize`. + let index_usize = match index.to_string().parse::() { + Ok(index_usize) => index_usize, + Err(_) => return Err(FrameError::invalid_index(index.to_string(), span)), + }; + + let element_type = elements.swap_remove(index_usize); + + Ok(element_type) + } + + /// + /// Returns the type of the array expression. + /// + fn parse_array(&mut self, expressions: &[SpreadOrExpression], span: &Span) -> Result { + // Store array element type. + let mut element_type = None; + + // Parse all array elements. + for expression in expressions { + // Get the type and count of elements in each spread or expression. + let type_ = self.parse_spread_or_expression(expression, span)?; + + // Assert that array element types are the same. + if let Some(prev_type) = element_type { + self.assert_equal(prev_type, type_.clone(), span); + } + + // Update array element type. + element_type = Some(type_); + } + + // Return an error for empty arrays. + let type_ = match element_type { + Some(type_) => type_, + None => return Err(FrameError::empty_array(span)), + }; + + Ok(Type::Array(Box::new(type_))) + } + + /// + /// Returns the type of the array initializer expression. + /// + fn parse_array_initializer( + &mut self, + array: &Expression, + dimensions: &ArrayDimensions, + span: &Span, + ) -> Result { + // Get element type. + let element_type = self.parse_expression(array)?; + + // Return an error for array initializers of length 0. + if dimensions.is_zero() { + return Err(FrameError::empty_array(span)); + } + + // Return array type. + Ok(Type::Array(Box::new(element_type))) + } + + /// + /// Returns the type and count of elements in a spread or expression. + /// + fn parse_spread_or_expression(&mut self, s_or_e: &SpreadOrExpression, span: &Span) -> Result { + match s_or_e { + SpreadOrExpression::Spread(expression) => { + // Parse the type of the spread array expression. + let array_type = self.parse_expression(expression)?; + + // Check that the type is an array. + match array_type { + Type::Array(element_type) => Ok(Type::Array(element_type)), + type_ => Err(FrameError::invalid_spread(type_, span)), + } + } + SpreadOrExpression::Expression(expression) => self.parse_expression(expression), + } + } + + /// + /// Returns the type of the accessed array element when called as an expression. + /// + fn parse_expression_array_access( + &mut self, + expression: &Expression, + r_or_e: &RangeOrExpression, + span: &Span, + ) -> Result { + // Parse the array expression which could be a variable with type array. + let type_ = self.parse_expression(expression)?; + + // Parse the array access. + self.parse_array_access(type_, r_or_e, span) + } + + /// + /// Returns the type of the accessed array element. + /// + fn parse_array_access(&mut self, type_: Type, r_or_e: &RangeOrExpression, span: &Span) -> Result { + // Check the type is an array. + let element_type = match type_ { + Type::Array(type_) => type_, + type_ => return Err(FrameError::array_access(&type_, span)), + }; + + // Get the length of the array. + // let length = *dimensions.last().unwrap(); + + // Evaluate the range as an array type or the expression as the element type. + match r_or_e { + RangeOrExpression::Range(from, to) => { + if let Some(expression) = from { + // Parse the expression type. + let type_ = self.parse_expression(expression)?; + + self.assert_index(&type_, span); + } + + if let Some(expression) = to { + // Parse the expression type. + let type_ = self.parse_expression(expression)?; + + self.assert_index(&type_, span); + } + } + RangeOrExpression::Expression(expression) => { + // Parse the expression type. + let type_ = self.parse_expression(expression)?; + + // Assert the type is an index. + self.assert_index(&type_, span); + } + } + + Ok(*element_type) + } + + /// + /// Returns the Self type of the frame or an error if it does not exist. + /// + fn self_type_or_error(&self, span: &Span) -> Result { + self.self_type.clone().ok_or_else(|| FrameError::circuit_self(span)) + } + + /// + /// Returns the type of inline circuit expression. + /// + fn parse_circuit( + &mut self, + identifier: &Identifier, + members: &[CircuitVariableDefinition], + span: &Span, + ) -> Result { + // Check if identifier is Self circuit type. + let circuit_type = if identifier.is_self() { + // Get the Self type of the frame. + self.self_type_or_error(span)? + } else { + // Get circuit type. + self.user_defined_types + .get_circuit_type(&identifier.name) + .cloned() + .ok_or_else(|| FrameError::undefined_circuit(identifier))? + }; + + // Check the length of the circuit members. + if circuit_type.variables.len() != members.len() { + return Err(FrameError::num_circuit_variables( + circuit_type.variables.len(), + members.len(), + span, + )); + } + + // Assert members are circuit type member types. + for (expected_variable, actual_variable) in circuit_type.variables.iter().zip(members) { + // Parse actual variable expression. + let actual_type = self.parse_expression(&actual_variable.expression)?; + + // Assert expected variable type == actual variable type. + self.assert_equal(expected_variable.type_.clone(), actual_type, span) + } + + Ok(Type::Circuit(circuit_type.identifier)) + } + + /// + /// Returns the type of the accessed circuit member when called as an expression. + /// + fn parse_expression_circuit_member_access( + &mut self, + expression: &Expression, + identifier: &Identifier, + span: &Span, + ) -> Result { + // Parse circuit name. + let type_ = self.parse_expression(expression)?; + + // Parse the circuit member access. + self.parse_circuit_member_access(type_, identifier, span) + } + + /// + /// Returns the type of the accessed circuit member. + /// + fn parse_circuit_member_access( + &mut self, + type_: Type, + identifier: &Identifier, + span: &Span, + ) -> Result { + // Check that type is a circuit type. + let circuit_type = self.parse_circuit_name(type_, span)?; + + // Look for member with matching name. + Ok(circuit_type.member_type(&identifier)?) + } + + /// + /// Returns the type returned by calling the static circuit function. + /// + fn parse_static_circuit_function_access( + &mut self, + expression: &Expression, + identifier: &Identifier, + span: &Span, + ) -> Result { + // Parse the circuit name. + let type_ = self.parse_expression(expression)?; + + self.parse_circuit_member_access(type_, identifier, span) + } + + /// + /// Returns a `CircuitType` given a circuit expression. + /// + fn parse_circuit_name(&mut self, type_: Type, span: &Span) -> Result<&CircuitType, FrameError> { + // Check that type is a circuit type. + match type_ { + Type::Circuit(identifier) => { + // Lookup circuit identifier. + self.user_defined_types + .get_circuit_type(&identifier.name) + .ok_or_else(|| FrameError::undefined_circuit(&identifier)) + } + type_ => Err(FrameError::invalid_circuit(type_, span)), + } + } + + /// + /// Returns a `FunctionType` given a function expression. + /// + fn parse_function_name(&mut self, expression: &Expression, span: &Span) -> Result { + // Case 1: Call a function defined in the program file. + // Case 2: Call a circuit function. + // Case 3: Call a static circuit function. + // Return an Error in any other case. + match expression { + Expression::Identifier(identifier) => self.parse_program_function(identifier, span), + Expression::CircuitMemberAccess(expression, identifier, span) => { + self.parse_circuit_function(expression, identifier, span) + } + Expression::CircuitStaticFunctionAccess(expression, identifier, span) => { + self.parse_static_circuit_function(expression, identifier, span) + } + expression => Err(FrameError::invalid_function(expression, span)), + } + } + + /// + /// Returns a `FunctionType` given a function identifier. + /// + fn parse_program_function(&mut self, identifier: &Identifier, _span: &Span) -> Result { + self.user_defined_types + .get_function_type(&identifier.name) + .cloned() + .ok_or_else(|| FrameError::undefined_function(identifier)) + } + + /// + /// Returns a `CircuitFunctionType` given a circuit expression and function identifier. + /// + fn parse_circuit_function_type( + &mut self, + expression: &Expression, + identifier: &Identifier, + span: &Span, + ) -> Result<&CircuitFunctionType, FrameError> { + // Parse circuit name. + let type_ = self.parse_expression(expression)?; + + // Get circuit type. + let circuit_type = self.parse_circuit_name(type_, span)?; + + // Find circuit function by identifier. + circuit_type + .member_function_type(identifier) + .ok_or_else(|| FrameError::undefined_circuit_function(identifier)) + } + + /// + /// Returns a `FunctionType` given a circuit expression and non-static function identifier. + /// + fn parse_circuit_function( + &mut self, + expression: &Expression, + identifier: &Identifier, + span: &Span, + ) -> Result { + // Find circuit function type. + let circuit_function_type = self.parse_circuit_function_type(expression, identifier, span)?; + + // Check that the function is non-static. + if let Some(Attribute::Static) = circuit_function_type.attribute { + return Err(FrameError::invalid_static_access(identifier)); + } + + // Return the function type. + Ok(circuit_function_type.function.to_owned()) + } + + /// + /// Returns a `FunctionType` given a circuit expression and static function identifier. + /// + fn parse_static_circuit_function( + &mut self, + expression: &Expression, + identifier: &Identifier, + span: &Span, + ) -> Result { + // Find circuit function type. + let circuit_function_type = self.parse_circuit_function_type(expression, identifier, span)?; + + // Check that the function is static. + if let Some(Attribute::Static) = circuit_function_type.attribute { + Ok(circuit_function_type.function.to_owned()) + } else { + Err(FrameError::invalid_member_access(identifier)) + } + } + + /// + /// Returns the type returned by calling the function. + /// + /// Does not attempt to evaluate the function call. We are just checking types at this step. + /// + fn parse_function_call( + &mut self, + expression: &Expression, + inputs: &[Expression], + span: &Span, + ) -> Result { + // Parse the function name. + let function_type = self.parse_function_name(expression, span)?; + + // Check the length of arguments + if function_type.inputs.len() != inputs.len() { + return Err(FrameError::num_inputs(function_type.inputs.len(), inputs.len(), span)); + } + + // Assert function inputs are correct types. + for (expected_input, actual_input) in function_type.inputs.iter().zip(inputs) { + // Parse expected input type. + let expected_type = expected_input.type_(); + + // Parse actual input type. + let actual_type = self.parse_expression(actual_input)?; + + // Assert expected input type == actual input type. + self.assert_equal(expected_type, actual_type, span); + } + + // Return the function output type. + Ok(function_type.output.type_) + } + + /// + /// Returns the type returned by calling the core function. + /// + fn parse_core_function_call( + &mut self, + _name: &str, + _arguments: &[Expression], + _span: &Span, + ) -> Result { + unimplemented!("type checks for core function calls not implemented") + } + + /// + /// Returns `Ok` if all `TypeAssertions` can be solved successfully. + /// + pub(crate) fn check(self) -> Result<(), FrameError> { + let mut unsolved = self.type_assertions; + + // Solve all type equality assertions first. + let mut unsolved_membership = Vec::new(); + + while !unsolved.is_empty() { + // Pop type assertion from list + let type_assertion = unsolved.pop().unwrap(); + + // If it is a membership assertion, then skip it for now. + if let TypeAssertion::Membership(membership) = type_assertion { + unsolved_membership.push(membership); + + continue; + } + + // Collect `TypeVariablePairs` from the `TypeAssertion`. + let pairs = type_assertion.pairs()?; + + // If no pairs are found, attempt to evaluate the `TypeAssertion`. + if pairs.is_empty() { + // Evaluate the `TypeAssertion`. + type_assertion.evaluate()? + } else { + // Iterate over each `TypeVariable` -> `Type` pair. + for pair in pairs.get_pairs() { + // Substitute the `TypeVariable` for it's paired `Type` in all `TypeAssertion`s. + for original in &mut unsolved { + original.substitute(pair.first(), pair.second()) + } + + for original in &mut unsolved_membership { + original.substitute(pair.first(), pair.second()) + } + } + } + } + + // Solve all type membership assertions. + while !unsolved_membership.is_empty() { + // Pop type assertion from list + let type_assertion = unsolved_membership.pop().unwrap(); + + // Solve the membership assertion. + type_assertion.evaluate()?; + } + + Ok(()) + } +} diff --git a/type-inference/src/objects/mod.rs b/type-inference/src/objects/mod.rs new file mode 100644 index 0000000000..745c8eca50 --- /dev/null +++ b/type-inference/src/objects/mod.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +pub mod frame; +pub use self::frame::*; + +pub mod scope; +pub use self::scope::*; + +pub mod variable_table; +pub use self::variable_table::*; diff --git a/type-inference/src/objects/scope.rs b/type-inference/src/objects/scope.rs new file mode 100644 index 0000000000..34a0e20c58 --- /dev/null +++ b/type-inference/src/objects/scope.rs @@ -0,0 +1,81 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{ScopeError, VariableTable}; +use leo_symbol_table::{FunctionInputType, Type}; + +/// A structure for tracking the types of defined variables in a block of code. +#[derive(Clone, Default)] +pub struct Scope { + pub loop_variables: VariableTable, + pub variables: VariableTable, +} + +impl Scope { + /// + /// Returns a new `Scope` from an optional given `Scope`. + /// + /// The new scope will contain the variables of the optional given `Scope`. + /// + pub fn new(parent: Option) -> Self { + match parent { + Some(scope) => scope, + None => Self::default(), + } + } + + /// + /// Inserts a variable name -> type mapping into the loop variable table. + /// + pub fn insert_loop_variable(&mut self, name: String, type_: Type) -> Option { + self.loop_variables.insert(name, type_) + } + + /// + /// Inserts a variable name -> type mapping into the variable table. + /// + pub fn insert_variable(&mut self, name: String, type_: Type) -> Option { + self.variables.insert(name, type_) + } + + /// + /// Returns a reference to the type corresponding to the loop variable name. + /// + pub fn get_loop_variable(&self, name: &str) -> Option<&Type> { + self.loop_variables.get(name) + } + + /// + /// Returns a reference to the type corresponding to the variable name. + /// + /// Checks loop variables first, then non-loop variables. + /// + pub fn get_variable(&self, name: &str) -> Option<&Type> { + match self.get_loop_variable(name) { + Some(loop_variable_type) => Some(loop_variable_type), + None => self.variables.get(name), + } + } + + /// + /// Inserts a vector of function input types into the `Scope` variable table. + /// + pub fn insert_function_inputs(&mut self, function_inputs: &[FunctionInputType]) -> Result<(), ScopeError> { + self.variables + .insert_function_inputs(function_inputs) + .map_err(ScopeError::VariableTableError) + } +} diff --git a/type-inference/src/objects/variable_table.rs b/type-inference/src/objects/variable_table.rs new file mode 100644 index 0000000000..f6060b33a7 --- /dev/null +++ b/type-inference/src/objects/variable_table.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::VariableTableError; +use leo_symbol_table::{FunctionInputType, Type}; +use std::collections::HashMap; + +/// Mapping of variable names to types +#[derive(Clone)] +pub struct VariableTable(pub HashMap); + +impl VariableTable { + /// + /// Insert a name -> type pair into the variable table. + /// + /// If the variable table did not have this key present, [`None`] is returned. + /// + /// If the variable table did have this key present, the type is updated, and the old + /// type is returned. + /// + pub fn insert(&mut self, name: String, type_: Type) -> Option { + self.0.insert(name, type_) + } + + /// + /// Returns a reference to the type corresponding to the name. + /// + /// If the variable table did not have this key present, throw an undefined variable error + /// using the given span. + /// + pub fn get(&self, name: &str) -> Option<&Type> { + self.0.get(name) + } + + /// + /// Inserts a vector of function input types into the variable table. + /// + pub fn insert_function_inputs(&mut self, function_inputs: &[FunctionInputType]) -> Result<(), VariableTableError> { + for input in function_inputs { + let input_name = input.identifier().name.clone(); + let input_type = input.type_(); + + // TODO (collinc97) throw an error for duplicate function input names. + self.insert(input_name, input_type); + } + Ok(()) + } +} + +impl Default for VariableTable { + fn default() -> Self { + Self(HashMap::new()) + } +} diff --git a/type-inference/src/type_inference.rs b/type-inference/src/type_inference.rs new file mode 100644 index 0000000000..fee87bb035 --- /dev/null +++ b/type-inference/src/type_inference.rs @@ -0,0 +1,138 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{Frame, Scope, TypeInferenceError}; +use leo_ast::{Circuit, CircuitMember, Function, Program}; +use leo_symbol_table::SymbolTable; + +/// A type inference check for a Leo program. +/// +/// A [`TypeInference`] type stores a stack of frames. A new frame is created for every +/// function. Frames store type assertions that assert an expression is a type. +/// Calling the `check()` method on a [`TypeInference`] checks that all type assertions are satisfied. +pub struct TypeInference { + table: SymbolTable, + frames: Vec, +} + +impl TypeInference { + /// + /// Creates and runs a new `TypeInference` check on a given program and symbol table. + /// + /// Evaluates all `TypeAssertion` predicates. + /// + #[allow(clippy::new_ret_no_self)] + pub fn new(program: &Program, symbol_table: SymbolTable) -> Result<(), TypeInferenceError> { + let mut type_inference = Self { + table: symbol_table, + frames: Vec::new(), + }; + + type_inference.parse_program(program)?; + + type_inference.check() + } + + /// + /// Collects a vector of `TypeAssertion` predicates from a program. + /// + fn parse_program(&mut self, program: &Program) -> Result<(), TypeInferenceError> { + // Parse circuit types in program context. + self.parse_circuits(program.circuits.iter().map(|(_identifier, circuit)| circuit))?; + + // Parse functions in program context. + self.parse_functions(program.functions.iter().map(|(_identifier, function)| function)) + } + + /// + /// Collects a vector of `Frames`s from a vector of circuit functions. + /// + fn parse_circuits<'a>(&mut self, circuits: impl Iterator) -> Result<(), TypeInferenceError> { + for circuit in circuits { + self.parse_circuit(circuit)?; + } + + Ok(()) + } + + /// + /// Collects a vector of `Frames`s from a circuit function. + /// + /// Each frame collects a vector of `TypeAssertion` predicates from each function. + /// + fn parse_circuit(&mut self, circuit: &Circuit) -> Result<(), TypeInferenceError> { + let name = &circuit.circuit_name.name; + + // Get circuit type from circuit symbol table. + let circuit_type = self.table.get_circuit_type(name).unwrap().clone(); + + // Create a new function for each circuit member function. + for circuit_member in &circuit.members { + // ignore circuit member variables + if let CircuitMember::CircuitFunction(_, function) = circuit_member { + // Collect `TypeAssertion` predicates from the function. + // Pass down circuit self type and circuit variable types to each function. + let frame = Frame::new_circuit_function( + function.to_owned(), + circuit_type.clone(), + Scope::default(), + self.table.clone(), + )?; + + self.frames.push(frame) + } + } + + Ok(()) + } + + /// + /// Collects a vector of `TypeAssertion` predicates from a vector of functions. + /// + fn parse_functions<'a>(&mut self, functions: impl Iterator) -> Result<(), TypeInferenceError> { + for function in functions { + self.parse_function(function)?; + } + + Ok(()) + } + + /// + /// Collects a vector of `TypeAssertion` predicates from a function. + /// + fn parse_function(&mut self, function: &Function) -> Result<(), TypeInferenceError> { + let frame = Frame::new_function(function.to_owned(), None, None, self.table.clone())?; + + self.frames.push(frame); + + Ok(()) + } + + /// + /// Returns the result of evaluating all `TypeAssertion` predicates. + /// + /// Will attempt to substitute a `Type` for all `TypeVariable`s. + /// Returns a `LeoResolvedAst` if all `TypeAssertion` predicates are true. + /// Returns ERROR if a `TypeAssertion` predicate is false or a solution does not exist. + /// + pub fn check(self) -> Result<(), TypeInferenceError> { + for frame in self.frames { + frame.check()?; + } + + Ok(()) + } +} diff --git a/type-inference/tests/arrays/empty_array.leo b/type-inference/tests/arrays/empty_array.leo new file mode 100644 index 0000000000..7cf517014e --- /dev/null +++ b/type-inference/tests/arrays/empty_array.leo @@ -0,0 +1,3 @@ +function main() { + let a = [1u8; 0]; // Empty arrays are illegal in Leo programs since arrays cannot be resized. +} \ No newline at end of file diff --git a/type-inference/tests/arrays/invalid_array_access.leo b/type-inference/tests/arrays/invalid_array_access.leo new file mode 100644 index 0000000000..0064d41ae6 --- /dev/null +++ b/type-inference/tests/arrays/invalid_array_access.leo @@ -0,0 +1,5 @@ +function main() { + let a = (1u8, 2u8); + + let b = a[0]; // It is illegal to index into a tuple using bracket syntax. +} \ No newline at end of file diff --git a/type-inference/tests/arrays/invalid_spread.leo b/type-inference/tests/arrays/invalid_spread.leo new file mode 100644 index 0000000000..0153b3d04b --- /dev/null +++ b/type-inference/tests/arrays/invalid_spread.leo @@ -0,0 +1,5 @@ +function main() { + let a: u8 = 1; + + let b = [...a]; +} \ No newline at end of file diff --git a/type-inference/tests/arrays/mod.rs b/type-inference/tests/arrays/mod.rs new file mode 100644 index 0000000000..72ab79478e --- /dev/null +++ b/type-inference/tests/arrays/mod.rs @@ -0,0 +1,44 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TestTypeInference; + +#[test] +fn test_empty_array() { + let bytes = include_bytes!("empty_array.leo"); + + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} + +#[test] +fn test_invalid_array_access() { + let bytes = include_bytes!("invalid_array_access.leo"); + + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} + +#[test] +fn test_invalid_spread() { + let bytes = include_bytes!("invalid_spread.leo"); + + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} diff --git a/type-inference/tests/circuits/invalid_circuit.leo b/type-inference/tests/circuits/invalid_circuit.leo new file mode 100644 index 0000000000..aff73cc8d4 --- /dev/null +++ b/type-inference/tests/circuits/invalid_circuit.leo @@ -0,0 +1,4 @@ +function main() { + let a = 1u8; + let b = a::foo(); // Variable `a` is not a circuit type. +} \ No newline at end of file diff --git a/type-inference/tests/circuits/mod.rs b/type-inference/tests/circuits/mod.rs new file mode 100644 index 0000000000..8b8d585bd6 --- /dev/null +++ b/type-inference/tests/circuits/mod.rs @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TestTypeInference; + +#[test] +fn test_invalid_circuit() { + let bytes = include_bytes!("invalid_circuit.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} diff --git a/type-inference/tests/empty.leo b/type-inference/tests/empty.leo new file mode 100644 index 0000000000..e69de29bb2 diff --git a/type-inference/tests/functions/invalid_function.leo b/type-inference/tests/functions/invalid_function.leo new file mode 100644 index 0000000000..1e72c4e8d6 --- /dev/null +++ b/type-inference/tests/functions/invalid_function.leo @@ -0,0 +1,4 @@ +function main() { + let a = 1u8; + let b = a(); // Variable `a` is not a function. +} \ No newline at end of file diff --git a/type-inference/tests/functions/mod.rs b/type-inference/tests/functions/mod.rs new file mode 100644 index 0000000000..20e873cbaf --- /dev/null +++ b/type-inference/tests/functions/mod.rs @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TestTypeInference; + +#[test] +fn test_invalid_function() { + let bytes = include_bytes!("invalid_function.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} diff --git a/type-inference/tests/mod.rs b/type-inference/tests/mod.rs new file mode 100644 index 0000000000..58362fc972 --- /dev/null +++ b/type-inference/tests/mod.rs @@ -0,0 +1,84 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +pub mod arrays; +pub mod circuits; +pub mod functions; +pub mod tuples; +pub mod variables; + +use leo_grammar::Grammar; +use leo_type_inference::TypeInference; + +use leo_ast::{Input, LeoAst, Program}; +use leo_imports::ImportParser; +use leo_symbol_table::SymbolTable; +use std::path::PathBuf; + +const TEST_PROGRAM_PATH: &str = ""; +const TEST_PROGRAM_NAME: &str = "test"; + +/// A helper struct to test a `TypeInference` check. +pub struct TestTypeInference { + program: Program, + symbol_table: SymbolTable, +} + +impl TestTypeInference { + pub fn new(bytes: &[u8]) -> Self { + // Get file string from bytes. + let file_string = String::from_utf8_lossy(bytes); + + // Get test file path. + let file_path = PathBuf::from(TEST_PROGRAM_PATH); + + // Get parser syntax tree. + let ast = Grammar::new(&file_path, &*file_string).unwrap(); + + // Get typed syntax tree. + let typed = LeoAst::new(TEST_PROGRAM_NAME, &ast); + let program = typed.into_repr(); + + // Create empty import parser. + let import_parser = ImportParser::default(); + + // Create empty input. + let input = Input::new(); + + // Create symbol table. + let symbol_table = SymbolTable::new(&program, &import_parser, &input).unwrap(); + + // Store fields for new type inference check. + Self { program, symbol_table } + } + + pub fn check(self) { + TypeInference::new(&self.program, self.symbol_table).unwrap(); + } + + pub fn expect_error(self) { + assert!(TypeInference::new(&self.program, self.symbol_table).is_err()); + } +} + +#[test] +fn test_new() { + let bytes = include_bytes!("empty.leo"); + + let type_inference = TestTypeInference::new(bytes); + + type_inference.check() +} diff --git a/type-inference/tests/tuples/invalid_tuple_access.leo b/type-inference/tests/tuples/invalid_tuple_access.leo new file mode 100644 index 0000000000..1fc14e3a6b --- /dev/null +++ b/type-inference/tests/tuples/invalid_tuple_access.leo @@ -0,0 +1,5 @@ +function main() { + let a = [1u8; 3]; + + let b = a.0; // It is illegal to index into an array using dot syntax. +} \ No newline at end of file diff --git a/type-inference/tests/tuples/mod.rs b/type-inference/tests/tuples/mod.rs new file mode 100644 index 0000000000..cc0549cffe --- /dev/null +++ b/type-inference/tests/tuples/mod.rs @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TestTypeInference; + +#[test] +fn test_invalid_tuple_access() { + let bytes = include_bytes!("invalid_tuple_access.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} diff --git a/type-inference/tests/variables/duplicate_variable.leo b/type-inference/tests/variables/duplicate_variable.leo new file mode 100644 index 0000000000..a748ef4efe --- /dev/null +++ b/type-inference/tests/variables/duplicate_variable.leo @@ -0,0 +1,4 @@ +function main() { + let a = 1u8; + let a = 2u8; // Redefining variables with the same name is unsafe in Leo. +} \ No newline at end of file diff --git a/type-inference/tests/variables/duplicate_variable_multi.leo b/type-inference/tests/variables/duplicate_variable_multi.leo new file mode 100644 index 0000000000..d0fabdea07 --- /dev/null +++ b/type-inference/tests/variables/duplicate_variable_multi.leo @@ -0,0 +1,3 @@ +function main() { + let (a, a) = (2u8, 2u8); // Defining multiple variables with the same name is unsafe in Leo. +} \ No newline at end of file diff --git a/type-inference/tests/variables/mod.rs b/type-inference/tests/variables/mod.rs new file mode 100644 index 0000000000..9e7b3dd1ea --- /dev/null +++ b/type-inference/tests/variables/mod.rs @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::TestTypeInference; + +#[test] +fn test_duplicate_variable() { + let bytes = include_bytes!("duplicate_variable.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} + +#[test] +fn test_duplicate_variable_multi() { + let bytes = include_bytes!("duplicate_variable_multi.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} + +#[test] +fn test_not_enough_values() { + let bytes = include_bytes!("not_enough_values.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} + +#[test] +fn test_too_many_values() { + let bytes = include_bytes!("too_many_values.leo"); + let check = TestTypeInference::new(bytes); + + check.expect_error(); +} diff --git a/type-inference/tests/variables/not_enough_values.leo b/type-inference/tests/variables/not_enough_values.leo new file mode 100644 index 0000000000..58b1ad7244 --- /dev/null +++ b/type-inference/tests/variables/not_enough_values.leo @@ -0,0 +1,3 @@ +function main() { + let (a, b): (u8, u8) = 1; // A tuple of values must be used when defining two variables. +} \ No newline at end of file diff --git a/type-inference/tests/variables/too_many_values.leo b/type-inference/tests/variables/too_many_values.leo new file mode 100644 index 0000000000..bd3f551231 --- /dev/null +++ b/type-inference/tests/variables/too_many_values.leo @@ -0,0 +1,3 @@ +function main() { + let (a, b): (u8, u8) = (1, 2, 3); // Cannot assign 2 variables to 3 values. +} \ No newline at end of file