Merge branch 'testnet3' into feat/redesign-symbol-table

This commit is contained in:
Pranav Gaddamadugu 2022-07-08 11:57:40 -07:00
commit 00b90c4a33
10 changed files with 421 additions and 312 deletions

31
Cargo.lock generated
View File

@ -194,9 +194,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.65"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
dependencies = [
"addr2line",
"cc",
@ -335,6 +335,12 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.73"
@ -497,12 +503,12 @@ dependencies = [
[[package]]
name = "criterion"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
dependencies = [
"atty",
"cast",
"cast 0.3.0",
"clap 2.34.0",
"criterion-plot",
"csv",
@ -527,7 +533,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
dependencies = [
"cast",
"cast 0.2.7",
"itertools",
]
@ -1231,6 +1237,7 @@ name = "leo-passes"
version = "1.5.3"
dependencies = [
"indexmap",
"itertools",
"leo-ast",
"leo-core",
"leo-errors",
@ -1463,9 +1470,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "object"
version = "0.28.4"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
dependencies = [
"memchr",
]
@ -1839,9 +1846,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.6"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"aho-corasick",
"memchr",
@ -1856,9 +1863,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.26"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "remove_dir_all"

View File

@ -55,7 +55,7 @@ git = "https://github.com/AleoHQ/snarkVM.git"
rev = "51633e2"
[dependencies.backtrace]
version = "0.3.65"
version = "0.3.66"
[dependencies.clap]
version = "3.2.8"

View File

@ -43,3 +43,6 @@ version = "1.5.3"
[dependencies.leo-core]
path = "../core"
version = "1.5.3"
[dependencies.itertools]
version = "0.10.3"

View File

@ -55,11 +55,99 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
}
fn visit_access(&mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput) -> Self::Output {
// CAUTION: This implementation only allows access to core circuits.
match input {
AccessExpression::AssociatedFunction(access) => {
// Check core circuit name and function.
if let Some(core_instruction) = self.check_core_circuit_call(&access.ty, &access.name) {
// Check num input arguments.
if core_instruction.num_args() != access.args.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_args_to_call(
core_instruction.num_args(),
access.args.len(),
input.span(),
)
.into(),
);
}
// Check first argument type.
if let Some(first_arg) = access.args.get(0usize) {
let first_arg_type = self.visit_expression(first_arg, &None);
self.assert_one_of_types(&first_arg_type, core_instruction.first_arg_types(), access.span());
}
// Check second argument type.
if let Some(second_arg) = access.args.get(1usize) {
let second_arg_type = self.visit_expression(second_arg, &None);
self.assert_one_of_types(&second_arg_type, core_instruction.second_arg_types(), access.span());
}
// Check return type.
Some(self.assert_and_return_type(core_instruction.return_type(), expected, access.span()))
} else {
self.handler
.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()).into());
None
}
}
_expr => None, // todo: Add support for associated constants (u8::MAX).
}
}
fn visit_circuit_init(
&mut self,
input: &'a CircuitInitExpression,
additional: &Self::AdditionalInput,
) -> Self::Output {
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
if let Some(circ) = circ {
// Check circuit type name.
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
)
.into(),
);
}
// Check circuit member types.
circ.members
.iter()
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
// Lookup circuit variable name.
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
if let Some(expr) = &actual.expression {
self.visit_expression(expr, &Some(*ty));
}
} else {
self.handler.emit_err(
TypeCheckerError::unknown_sym("circuit member variable", name, name.span()).into(),
);
};
});
Some(ret)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("circuit", &input.name.name, input.name.span()).into());
None
}
}
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(&var.name) {
Some(self.assert_expected_option(Type::Identifier(circuit.identifier), expected, var.span))
Some(self.assert_and_return_type(Type::Identifier(circuit.identifier), expected, var.span))
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(&var.name) {
Some(self.assert_expected_option(var.type_, expected, var.span))
Some(self.assert_and_return_type(var.type_, expected, var.span))
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()).into());
@ -69,9 +157,9 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_literal(&mut self, input: &'a LiteralExpression, expected: &Self::AdditionalInput) -> Self::Output {
Some(match input {
LiteralExpression::Address(_, _) => self.assert_expected_option(Type::Address, expected, input.span()),
LiteralExpression::Boolean(_, _) => self.assert_expected_option(Type::Boolean, expected, input.span()),
LiteralExpression::Field(_, _) => self.assert_expected_option(Type::Field, expected, input.span()),
LiteralExpression::Address(_, _) => self.assert_and_return_type(Type::Address, expected, input.span()),
LiteralExpression::Boolean(_, _) => self.assert_and_return_type(Type::Boolean, expected, input.span()),
LiteralExpression::Field(_, _) => self.assert_and_return_type(Type::Field, expected, input.span()),
LiteralExpression::Integer(type_, str_content, _) => {
match type_ {
IntegerType::I8 => {
@ -151,68 +239,26 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()).into()),
_ => {}
}
self.assert_expected_option(Type::IntegerType(*type_), expected, input.span())
self.assert_and_return_type(Type::IntegerType(*type_), expected, input.span())
}
LiteralExpression::Group(_) => self.assert_expected_option(Type::Group, expected, input.span()),
LiteralExpression::Scalar(_, _) => self.assert_expected_option(Type::Scalar, expected, input.span()),
LiteralExpression::String(_, _) => self.assert_expected_option(Type::String, expected, input.span()),
LiteralExpression::Group(_) => self.assert_and_return_type(Type::Group, expected, input.span()),
LiteralExpression::Scalar(_, _) => self.assert_and_return_type(Type::Scalar, expected, input.span()),
LiteralExpression::String(_, _) => self.assert_and_return_type(Type::String, expected, input.span()),
})
}
fn visit_access(&mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput) -> Self::Output {
// CAUTION: This implementation only allows access to core circuits.
match input {
AccessExpression::AssociatedFunction(access) => {
// Check core circuit name and function.
if let Some(core_instruction) = self.assert_core_circuit_call(&access.ty, &access.name) {
// Check num input arguments.
if core_instruction.num_args() != access.args.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_args_to_call(
core_instruction.num_args(),
access.args.len(),
input.span(),
)
.into(),
);
}
// Check first argument type.
if let Some(first_arg) = access.args.get(0usize) {
let first_arg_type = self.visit_expression(first_arg, &None);
self.assert_one_of_types(&first_arg_type, core_instruction.first_arg_types(), access.span());
}
// Check second argument type.
if let Some(second_arg) = access.args.get(1usize) {
let second_arg_type = self.visit_expression(second_arg, &None);
self.assert_one_of_types(&second_arg_type, core_instruction.second_arg_types(), access.span());
}
// Check return type.
Some(self.assert_expected_option(core_instruction.return_type(), expected, access.span()))
} else {
self.handler
.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()).into());
None
}
}
_expr => None, // todo: Add support for associated constants (u8::MAX).
}
}
fn visit_binary(&mut self, input: &'a BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
match input.op {
BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => {
// Assert equal boolean types.
self.assert_expected_option(Type::Boolean, destination, input.span());
// Only boolean types.
self.assert_bool_type(destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
return_incorrect_type(t1, t2, destination)
}
BinaryOperation::BitwiseAnd | BinaryOperation::BitwiseOr | BinaryOperation::Xor => {
// Assert equal boolean or integer types.
// Only boolean or integer types.
self.assert_bool_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
@ -220,7 +266,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
return_incorrect_type(t1, t2, destination)
}
BinaryOperation::Add => {
// Assert equal field, group, scalar, or integer types.
// Only field, group, scalar, or integer types.
self.assert_field_group_scalar_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
@ -228,7 +274,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
return_incorrect_type(t1, t2, destination)
}
BinaryOperation::Sub => {
// Assert equal field, group, or integer types.
// Only field, group, or integer types.
self.assert_field_group_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
@ -236,24 +282,34 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
return_incorrect_type(t1, t2, destination)
}
BinaryOperation::Mul => {
// Assert field, group or integer types.
// Operation returns field, group or integer types.
self.assert_field_group_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, &None);
let t2 = self.visit_expression(&input.right, &None);
// Allow `group` * `scalar` multiplication.
// Allow group * scalar multiplication.
match (t1, t2) {
(Some(Type::Group), other) => {
self.assert_expected_type(&other, Type::Scalar, input.right.span());
Some(self.assert_expected_type(destination, Type::Group, input.span()))
(Some(Type::Group), right) => {
// Right type must be scalar.
self.assert_scalar_type(&right, input.right.span());
// Operation returns group.
self.assert_group_type(destination, input.span());
Some(Type::Group)
}
(other, Some(Type::Group)) => {
self.assert_expected_type(&other, Type::Scalar, input.left.span());
Some(self.assert_expected_type(destination, Type::Group, input.span()))
(left, Some(Type::Group)) => {
// Left must be scalar.
self.assert_scalar_type(&left, input.left.span());
// Operation returns group.
self.assert_group_type(destination, input.span());
Some(Type::Group)
}
(t1, t2) => {
// Assert equal field or integer types.
// Otherwise, only field or integer types.
self.assert_field_int_type(destination, input.span());
return_incorrect_type(t1, t2, destination)
@ -261,7 +317,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
}
BinaryOperation::Div => {
// Assert equal field or integer types.
// Only field or integer types.
self.assert_field_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
@ -270,7 +326,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
return_incorrect_type(t1, t2, destination)
}
BinaryOperation::Pow => {
// Assert field or integer types.
// Operation returns field or integer types.
self.assert_field_int_type(destination, input.span());
let t1 = self.visit_expression(&input.left, &None);
@ -278,21 +334,37 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
// Allow field * field.
match (t1, t2) {
(Some(Type::Field), type_) => {
self.assert_expected_type(&type_, Type::Field, input.right.span());
Some(self.assert_expected_type(destination, Type::Field, input.span()))
(Some(Type::Field), right) => {
// Right must be field.
self.assert_field_type(&right, input.right.span());
// Operation returns field.
self.assert_field_type(destination, input.span());
Some(Type::Field)
}
(type_, Some(Type::Field)) => {
self.assert_expected_type(&type_, Type::Field, input.left.span());
Some(self.assert_expected_type(destination, Type::Field, input.span()))
(left, Some(Type::Field)) => {
// Left must be field.
self.assert_field_type(&left, input.left.span());
// Operation returns field.
self.assert_field_type(destination, input.span());
Some(Type::Field)
}
(Some(t1), t2) => {
// Allow integer t2 magnitude (u8, u16, u32)
self.assert_magnitude_type(&t2, input.right.span());
Some(self.assert_expected_type(destination, t1, input.span()))
(Some(left), right) => {
// Left type is checked to be an integer by above.
// Right type must be magnitude (u8, u16, u32).
self.assert_magnitude_type(&right, input.right.span());
// Operation returns left type.
self.assert_type(destination, &left, input.span());
Some(left)
}
(None, t2) => {
// Allow integer t2 magnitude (u8, u16, u32)
// Lhs type is checked to be an integer by above.
// Rhs type must be magnitude (u8, u16, u32).
self.assert_magnitude_type(&t2, input.right.span());
*destination
}
@ -305,20 +377,22 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
match (t1, t2) {
(Some(Type::IntegerType(_)), t2) => {
// Assert rhs is integer.
// Check rhs is integer and give detailed error message.
self.assert_int_type(&t2, input.left.span());
}
(t1, Some(Type::IntegerType(_))) => {
// Assert lhs is integer.
// Check lhs is integer and give detailed error message.
self.assert_int_type(&t1, input.right.span());
}
(t1, t2) => {
self.assert_eq_types(t1, t2, input.span());
self.check_eq_types(&t1, &t2, input.span());
}
}
// Assert destination is boolean.
Some(self.assert_expected_type(destination, Type::Boolean, input.span()))
// Operation returns a boolean.
self.assert_bool_type(destination, input.span());
Some(Type::Boolean)
}
BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Lte | BinaryOperation::Gte => {
// Assert left and right are equal field, scalar, or integer types.
@ -333,41 +407,43 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
(Some(Type::Field), t2) => {
// Assert rhs is field.
self.assert_expected_type(&t2, Type::Field, input.left.span());
self.assert_field_type(&t2, input.right.span());
}
(t1, Some(Type::Field)) => {
// Assert lhs is field.
self.assert_expected_type(&t1, Type::Field, input.right.span());
self.assert_field_type(&t1, input.left.span());
}
(Some(Type::Scalar), t2) => {
// Assert rhs is scalar.
self.assert_expected_type(&t2, Type::Scalar, input.left.span());
self.assert_scalar_type(&t2, input.right.span());
}
(t1, Some(Type::Scalar)) => {
// Assert lhs is scalar.
self.assert_expected_type(&t1, Type::Scalar, input.right.span());
self.assert_scalar_type(&t1, input.left.span());
}
(Some(Type::IntegerType(_)), t2) => {
// Assert rhs is integer.
self.assert_int_type(&t2, input.left.span());
self.assert_int_type(&t2, input.right.span());
}
(t1, Some(Type::IntegerType(_))) => {
// Assert lhs is integer.
self.assert_int_type(&t1, input.right.span());
self.assert_int_type(&t1, input.left.span());
}
(_, _) => {
// Not enough info to assert type.
}
}
// Assert destination is boolean.
Some(self.assert_expected_type(destination, Type::Boolean, input.span()))
// Operation returns a boolean.
self.assert_bool_type(destination, input.span());
Some(Type::Boolean)
}
BinaryOperation::AddWrapped
| BinaryOperation::SubWrapped
| BinaryOperation::DivWrapped
| BinaryOperation::MulWrapped => {
// Assert equal integer types.
// Only integer types.
self.assert_int_type(destination, input.span);
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
@ -379,12 +455,14 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
| BinaryOperation::Shr
| BinaryOperation::ShrWrapped
| BinaryOperation::PowWrapped => {
// Assert left and destination are equal integer types.
self.assert_int_type(destination, input.span);
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, &None);
// Assert left and destination are equal integer types.
self.assert_int_type(&t1, input.left.span());
self.assert_int_type(destination, input.span);
// Assert right type is a magnitude (u8, u16, u32).
let t2 = self.visit_expression(&input.right, &None);
self.assert_magnitude_type(&t2, input.right.span());
return_incorrect_type(t1, t2, destination)
@ -395,23 +473,23 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
match input.op {
UnaryOperation::Abs => {
// Assert integer type only.
// Only signed integer types.
self.assert_signed_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::AbsWrapped => {
// Assert integer type only.
// Only signed integer types.
self.assert_signed_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Double => {
// Assert field and group type only.
// Only field or group types.
self.assert_field_group_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Inverse => {
// Assert field type only.
self.assert_expected_type(destination, Type::Field, input.span());
// Only field types.
self.assert_field_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Negate => {
@ -420,37 +498,23 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
let type_ = self.visit_expression(&input.receiver, destination);
self.negate = prior_negate_state;
match type_.as_ref() {
Some(
Type::IntegerType(
IntegerType::I8
| IntegerType::I16
| IntegerType::I32
| IntegerType::I64
| IntegerType::I128,
)
| Type::Field
| Type::Group,
) => {}
Some(t) => self
.handler
.emit_err(TypeCheckerError::type_is_not_negatable(t, input.receiver.span()).into()),
_ => {}
};
// Only field, group, or signed integer types.
self.assert_field_group_signed_int_type(&type_, input.receiver.span());
type_
}
UnaryOperation::Not => {
// Assert boolean, integer types only.
// Only boolean or integer types.
self.assert_bool_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Square => {
// Assert field type only.
self.assert_expected_type(destination, Type::Field, input.span());
// Only field type.
self.assert_field_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::SquareRoot => {
// Assert field or scalar type.
// Only field or scalar type.
self.assert_field_scalar_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
@ -471,7 +535,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(ident) => {
let func = self.symbol_table.borrow().lookup_fn(&ident.name).cloned();
if let Some(func) = func {
let ret = self.assert_expected_option(func.output, expected, func.span);
let ret = self.assert_and_return_type(func.output, expected, func.span);
// Check number of function arguments.
if func.input.len() != input.arguments.len() {
@ -503,50 +567,4 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
expr => self.visit_expression(expr, expected),
}
}
fn visit_circuit_init(
&mut self,
input: &'a CircuitInitExpression,
additional: &Self::AdditionalInput,
) -> Self::Output {
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
if let Some(circ) = circ {
// Check circuit type name.
let ret = self.assert_expected_circuit(circ.identifier, additional, input.name.span());
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
)
.into(),
);
}
// Check circuit member types.
circ.members
.iter()
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
// Lookup circuit variable name.
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
if let Some(expr) = &actual.expression {
self.visit_expression(expr, &Some(*ty));
}
} else {
self.handler.emit_err(
TypeCheckerError::unknown_sym("circuit member variable", name, name.span()).into(),
);
};
});
Some(ret)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("circuit", &input.name.name, input.name.span()).into());
None
}
}
}

View File

@ -22,6 +22,7 @@ use leo_errors::{emitter::Handler, TypeCheckerError};
use leo_span::{Span, Symbol};
use indexmap::IndexSet;
use itertools::Itertools;
use std::cell::RefCell;
pub struct TypeChecker<'a> {
@ -34,6 +35,14 @@ pub struct TypeChecker<'a> {
pub(crate) algorithms_types: IndexSet<Symbol>,
}
const BOOLEAN_TYPE: Type = Type::Boolean;
const FIELD_TYPE: Type = Type::Field;
const GROUP_TYPE: Type = Type::Group;
const SCALAR_TYPE: Type = Type::Scalar;
const INT_TYPES: [Type; 10] = [
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
@ -61,36 +70,6 @@ const MAGNITUDE_TYPES: [Type; 3] = [
Type::IntegerType(IntegerType::U32),
];
const fn create_type_superset<const S: usize, const A: usize, const O: usize>(
subset: [Type; S],
additional: [Type; A],
) -> [Type; O] {
let mut superset: [Type; O] = [Type::IntegerType(IntegerType::U8); O];
let mut i = 0;
while i < S {
superset[i] = subset[i];
i += 1;
}
let mut j = 0;
while j < A {
superset[i + j] = additional[j];
j += 1;
}
superset
}
const BOOL_INT_TYPES: [Type; 11] = create_type_superset(INT_TYPES, [Type::Boolean]);
const FIELD_INT_TYPES: [Type; 11] = create_type_superset(INT_TYPES, [Type::Field]);
const FIELD_GROUP_INT_TYPES: [Type; 12] = create_type_superset(FIELD_INT_TYPES, [Type::Group]);
const FIELD_GROUP_SCALAR_INT_TYPES: [Type; 13] = create_type_superset(FIELD_GROUP_INT_TYPES, [Type::Scalar]);
const FIELD_GROUP_TYPES: [Type; 2] = [Type::Field, Type::Group];
const FIELD_SCALAR_TYPES: [Type; 2] = [Type::Field, Type::Scalar];
impl<'a> TypeChecker<'a> {
/// Returns a new type checker given a symbol table and error handler.
pub fn new(symbol_table: SymbolTable, handler: &'a Handler) -> Self {
@ -110,18 +89,198 @@ impl<'a> TypeChecker<'a> {
self.handler.emit_err(err.into());
}
/// Emits an error if the given type conflicts with a core library type.
pub(crate) fn check_ident_type(&self, type_: &Option<Type>) {
if let Some(Type::Identifier(ident)) = type_ {
if self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name) {
self.emit_err(TypeCheckerError::core_type_name_conflict(&ident.name, ident.span()));
/// Emits an error if the two given types are not equal.
pub(crate) fn check_eq_types(&self, t1: &Option<Type>, t2: &Option<Type>, span: Span) {
match (t1, t2) {
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
(Some(type_), None) | (None, Some(type_)) => {
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
}
_ => {}
}
}
/// Use this method when you know the actual type.
/// Emits an error to the handler if the `actual` type is not equal to the `expected` type.
pub(crate) fn assert_and_return_type(&self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
if let Some(expected) = expected {
if !actual.eq_flat(expected) {
self.emit_err(TypeCheckerError::type_should_be(actual, expected, span));
}
}
actual
}
/// Emits an error to the handler if the given type is invalid.
fn check_type(&self, is_valid: impl Fn(&Type) -> bool, error_string: String, type_: &Option<Type>, span: Span) {
if let Some(type_) = type_ {
if !is_valid(type_) {
self.emit_err(TypeCheckerError::expected_one_type_of(error_string, type_, span));
}
}
}
/// Emits an error to the error handler if the `actual` type is not equal to the `expected` type.
pub(crate) fn assert_type(&self, actual: &Option<Type>, expected: &Type, span: Span) {
self.check_type(
|actual: &Type| actual.eq_flat(expected),
expected.to_string(),
actual,
span,
)
}
/// Emits an error to the error handler if the actual type is not equal to any of the expected types.
pub(crate) fn assert_one_of_types(&self, actual: &Option<Type>, expected: &[Type], span: Span) {
self.check_type(
|actual: &Type| expected.iter().any(|t: &Type| t == actual),
types_to_string(expected),
actual,
span,
)
}
/// Emits an error to the handler if the given type is not a boolean.
pub(crate) fn assert_bool_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| BOOLEAN_TYPE.eq(type_),
BOOLEAN_TYPE.to_string(),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field.
pub(crate) fn assert_field_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| FIELD_TYPE.eq(type_), FIELD_TYPE.to_string(), type_, span)
}
/// Emits an error to the handler if the given type is not a group.
pub(crate) fn assert_group_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| GROUP_TYPE.eq(type_), GROUP_TYPE.to_string(), type_, span)
}
/// Emits an error to the handler if the given type is not a scalar.
pub(crate) fn assert_scalar_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| SCALAR_TYPE.eq(type_),
SCALAR_TYPE.to_string(),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| INT_TYPES.contains(type_),
types_to_string(&INT_TYPES),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a signed integer.
pub(crate) fn assert_signed_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| SIGNED_INT_TYPES.contains(type_),
types_to_string(&SIGNED_INT_TYPES),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| MAGNITUDE_TYPES.contains(type_),
types_to_string(&MAGNITUDE_TYPES),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a boolean or an integer.
pub(crate) fn assert_bool_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| BOOLEAN_TYPE.eq(type_) | INT_TYPES.contains(type_),
format!("{}, {}", BOOLEAN_TYPE, types_to_string(&INT_TYPES)),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field or integer.
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | INT_TYPES.contains(type_),
format!("{}, {}", FIELD_TYPE, types_to_string(&INT_TYPES)),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field or group.
pub(crate) fn assert_field_group_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_),
format!("{}, {}", FIELD_TYPE, GROUP_TYPE),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field or scalar.
pub(crate) fn assert_field_scalar_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | SCALAR_TYPE.eq(type_),
format!("{}, {}", FIELD_TYPE, SCALAR_TYPE),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field, group, or integer.
pub(crate) fn assert_field_group_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | INT_TYPES.contains(type_),
format!("{}, {}, {}", FIELD_TYPE, GROUP_TYPE, types_to_string(&INT_TYPES),),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field, group, or signed integer.
pub(crate) fn assert_field_group_signed_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | SIGNED_INT_TYPES.contains(type_),
format!("{}, {}, {}", FIELD_TYPE, GROUP_TYPE, types_to_string(&SIGNED_INT_TYPES),),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field, group, scalar or integer.
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| {
FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | SCALAR_TYPE.eq(type_) | INT_TYPES.contains(type_)
},
format!(
"{}, {}, {}, {}",
FIELD_TYPE,
GROUP_TYPE,
SCALAR_TYPE,
types_to_string(&INT_TYPES),
),
type_,
span,
)
}
/// Emits an error if the `circuit` is not a core library circuit.
/// Emits an error if the `function` is not supported by the circuit.
pub(crate) fn assert_core_circuit_call(&self, circuit: &Type, function: &Identifier) -> Option<CoreInstruction> {
pub(crate) fn check_core_circuit_call(&self, circuit: &Type, function: &Identifier) -> Option<CoreInstruction> {
if let Type::Identifier(ident) = circuit {
// Lookup core circuit
match CoreInstruction::from_symbols(ident.name, function.name) {
@ -139,19 +298,8 @@ impl<'a> TypeChecker<'a> {
None
}
/// Emits an error if the two given types are not equal.
pub(crate) fn assert_eq_types(&self, t1: Option<Type>, t2: Option<Type>, span: Span) {
match (t1, t2) {
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
(Some(type_), None) | (None, Some(type_)) => {
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
}
_ => {}
}
}
/// Returns the `circuit` type and emits an error if the `expected` type does not match.
pub(crate) fn assert_expected_circuit(&mut self, circuit: Identifier, expected: &Option<Type>, span: Span) -> Type {
pub(crate) fn check_expected_circuit(&mut self, circuit: Identifier, expected: &Option<Type>, span: Span) -> Type {
if let Some(Type::Identifier(expected)) = expected {
if !circuit.matches(expected) {
self.emit_err(TypeCheckerError::type_should_be(circuit.name, expected.name, span));
@ -161,84 +309,17 @@ impl<'a> TypeChecker<'a> {
Type::Identifier(circuit)
}
/// Returns the given `actual` type and emits an error if the `expected` type does not match.
pub(crate) fn assert_expected_option(&self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
if let Some(expected) = expected {
if !actual.eq_flat(expected) {
self.emit_err(TypeCheckerError::type_should_be(actual, expected, span));
/// Emits an error if the given type conflicts with a core library type.
pub(crate) fn check_ident_type(&self, type_: &Option<Type>) {
// todo: deprecate this method.
if let Some(Type::Identifier(ident)) = type_ {
if self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name) {
self.emit_err(TypeCheckerError::core_type_name_conflict(&ident.name, ident.span()));
}
}
actual
}
/// Returns the given `expected` type and emits an error if the `actual` type does not match.
/// `span` should be the location of the expected type.
pub(crate) fn assert_expected_type(&mut self, actual: &Option<Type>, expected: Type, span: Span) -> Type {
if let Some(actual) = actual {
if !actual.eq_flat(&expected) {
self.emit_err(TypeCheckerError::type_should_be(actual, expected, span));
}
}
expected
}
/// Emits an error to the error handler if the given type is not equal to any of the expected types.
pub(crate) fn assert_one_of_types(&self, type_: &Option<Type>, expected: &[Type], span: Span) {
if let Some(type_) = type_ {
if !expected.iter().any(|t: &Type| t == type_) {
self.emit_err(TypeCheckerError::expected_one_type_of(
expected.iter().map(|t| t.to_string() + ",").collect::<String>(),
type_,
span,
));
}
}
}
/// Emits an error to the handler if the given type is not a boolean or an integer.
pub(crate) fn assert_bool_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &BOOL_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or integer.
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or group.
pub(crate) fn assert_field_group_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_GROUP_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or scalar.
pub(crate) fn assert_field_scalar_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_SCALAR_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field, group, or integer.
pub(crate) fn assert_field_group_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_GROUP_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field, group, scalar or integer.
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_GROUP_SCALAR_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a signed integer.
pub(crate) fn assert_signed_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &SIGNED_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &MAGNITUDE_TYPES, span)
}
}
fn types_to_string(types: &[Type]) -> String {
types.iter().map(|type_| type_.to_string()).join(", ")
}

View File

@ -19,7 +19,7 @@ edition = "2021"
rust-version = "1.56"
[dependencies.backtrace]
version = "0.3.65"
version = "0.3.66"
[dependencies.leo-span]
path = "../span"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372009]: Expected one type from `bool,i8,i16,i32,i64,u8,u16,u32,u64,string,`, but got `u128`\n --> compiler-test:4:20\n |\n 4 | let a: group = Pedersen64::hash(1u128);\n | ^^^^^^^^^^^^^^^^^^^^^^^\n"
- "Error [ETYC0372009]: Expected one type from `bool, i8, i16, i32, i64, u8, u16, u32, u64, string`, but got `u128`\n --> compiler-test:4:20\n |\n 4 | let a: group = Pedersen64::hash(1u128);\n | ^^^^^^^^^^^^^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372003]: Found type `group` but type `scalar` was expected\n --> compiler-test:4:26\n |\n 4 | return (_, _)group * a;\n | ^\n"
- "Error [ETYC0372009]: Expected one type from `scalar`, but got `group`\n --> compiler-test:4:26\n |\n 4 | return (_, _)group * a;\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:4:20\n |\n 4 | let b: bool = -a == -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:4:26\n |\n 4 | let b: bool = -a == -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:5:20\n |\n 5 | let c: bool = -a > -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:5:25\n |\n 5 | let c: bool = -a > -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:6:20\n |\n 6 | let d: bool = -a < -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:6:25\n |\n 6 | let d: bool = -a < -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:7:20\n |\n 7 | let e: bool = -a >= -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:7:26\n |\n 7 | let e: bool = -a >= -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:8:20\n |\n 8 | let f: bool = -a <= -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:8:26\n |\n 8 | let f: bool = -a <= -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:9:18\n |\n 9 | let g: u8 = -a * -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:9:23\n |\n 9 | let g: u8 = -a * -1u8;\n | ^^^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:10:18\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^\nError [ETYC0372007]: The type `u8` is not negatable\n --> compiler-test:10:24\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^^^\n"
- "Error [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:20\n |\n 4 | let b: bool = -a == -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:26\n |\n 4 | let b: bool = -a == -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:20\n |\n 5 | let c: bool = -a > -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:25\n |\n 5 | let c: bool = -a > -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:20\n |\n 6 | let d: bool = -a < -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:25\n |\n 6 | let d: bool = -a < -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:20\n |\n 7 | let e: bool = -a >= -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:26\n |\n 7 | let e: bool = -a >= -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:20\n |\n 8 | let f: bool = -a <= -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:26\n |\n 8 | let f: bool = -a <= -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:18\n |\n 9 | let g: u8 = -a * -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:23\n |\n 9 | let g: u8 = -a * -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:18\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:24\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^^^\n"

View File

@ -22,7 +22,7 @@ name = "leo_compiler"
harness = false
[dependencies]
backtrace = "0.3.65"
backtrace = "0.3.66"
walkdir = "2.3.2"
[dependencies.serde]
@ -45,7 +45,7 @@ path = "../../leo/errors"
version = "1.5.3"
[dependencies.regex]
version = "1.5"
version = "1.6"
[dev-dependencies.criterion]
version = "0.3"