add type-inference module

This commit is contained in:
collin 2020-11-12 10:28:24 -08:00
parent 667392237f
commit 69a8b61efe
48 changed files with 2984 additions and 191 deletions

14
Cargo.lock generated
View File

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

View File

@ -37,7 +37,7 @@ members = [
"package",
"state",
"symbol-table",
# "type-inference",
"type-inference",
]
[dependencies.leo-ast]

View File

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

View File

@ -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<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
///
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<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
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);

View File

@ -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),
_ => {}
}
}

View File

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

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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

View File

@ -14,7 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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() {

View File

@ -14,7 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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

View File

@ -180,10 +180,10 @@ pub(crate) fn expect_compiler_error(program: EdwardsTestCompiler) -> CompilerErr
let mut cs = TestConstraintSystem::<Fq>::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(_)))

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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() {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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);
}

View File

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

43
type-inference/Cargo.toml Normal file
View File

@ -0,0 +1,43 @@
[package]
name = "leo-type-inference"
version = "1.0.4"
authors = [ "The Aleo Team <hello@aleo.org>" ]
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"

View File

@ -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 <https://www.gnu.org/licenses/>.
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::*;

View File

@ -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 <https://www.gnu.org/licenses/>.
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<Type>, 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<TypeVariablePairs, TypeAssertionError> {
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(),
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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, TypeAssertionError> {
TypeVariablePairs::new(self.left.to_owned(), self.right.to_owned(), &self.span)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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<Type>,
span: Span,
}
impl TypeMembership {
///
/// Returns a `TypeMembership` predicate from given and set `Type`s.
///
pub fn new(given: Type, set: Vec<Type>, 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
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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<TypeVariablePair>);
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<Self, TypeAssertionError> {
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<Item = Type>,
right_types: impl Iterator<Item = Type>,
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(())
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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::*;

View File

@ -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 <https://www.gnu.org/licenses/>.
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),
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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),
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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())
}
}

37
type-inference/src/lib.rs Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
//! 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::*;

File diff suppressed because it is too large Load Diff

View File

@ -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 <https://www.gnu.org/licenses/>.
pub mod frame;
pub use self::frame::*;
pub mod scope;
pub use self::scope::*;
pub mod variable_table;
pub use self::variable_table::*;

View File

@ -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 <https://www.gnu.org/licenses/>.
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<Scope>) -> 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<Type> {
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<Type> {
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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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<String, Type>);
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<Type> {
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())
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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<Frame>,
}
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<Item = &'a Circuit>) -> 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<Item = &'a Function>) -> 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(())
}
}

View File

@ -0,0 +1,3 @@
function main() {
let a = [1u8; 0]; // Empty arrays are illegal in Leo programs since arrays cannot be resized.
}

View File

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

View File

@ -0,0 +1,5 @@
function main() {
let a: u8 = 1;
let b = [...a];
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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();
}

View File

@ -0,0 +1,4 @@
function main() {
let a = 1u8;
let b = a::foo(); // Variable `a` is not a circuit type.
}

View File

@ -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 <https://www.gnu.org/licenses/>.
use crate::TestTypeInference;
#[test]
fn test_invalid_circuit() {
let bytes = include_bytes!("invalid_circuit.leo");
let check = TestTypeInference::new(bytes);
check.expect_error();
}

View File

View File

@ -0,0 +1,4 @@
function main() {
let a = 1u8;
let b = a(); // Variable `a` is not a function.
}

View File

@ -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 <https://www.gnu.org/licenses/>.
use crate::TestTypeInference;
#[test]
fn test_invalid_function() {
let bytes = include_bytes!("invalid_function.leo");
let check = TestTypeInference::new(bytes);
check.expect_error();
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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()
}

View File

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

View File

@ -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 <https://www.gnu.org/licenses/>.
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();
}

View File

@ -0,0 +1,4 @@
function main() {
let a = 1u8;
let a = 2u8; // Redefining variables with the same name is unsafe in Leo.
}

View File

@ -0,0 +1,3 @@
function main() {
let (a, a) = (2u8, 2u8); // Defining multiple variables with the same name is unsafe in Leo.
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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();
}

View File

@ -0,0 +1,3 @@
function main() {
let (a, b): (u8, u8) = 1; // A tuple of values must be used when defining two variables.
}

View File

@ -0,0 +1,3 @@
function main() {
let (a, b): (u8, u8) = (1, 2, 3); // Cannot assign 2 variables to 3 values.
}