mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-09-11 13:56:58 +03:00
refactor type checker for removal of Copy trait
This commit is contained in:
parent
6b5f9a3926
commit
f892fe1a57
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1231,6 +1231,7 @@ name = "leo-passes"
|
||||
version = "1.5.3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"leo-ast",
|
||||
"leo-core",
|
||||
"leo-errors",
|
||||
|
@ -43,3 +43,6 @@ version = "1.5.3"
|
||||
[dependencies.leo-core]
|
||||
path = "../core"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.itertools]
|
||||
version = "0.10.3"
|
@ -57,9 +57,9 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
if let Some(circuit) = self.symbol_table.clone().lookup_circuit(&var.name) {
|
||||
Some(self.assert_expected_option(Type::Identifier(circuit.identifier), expected, circuit.span()))
|
||||
Some(self.check_expected_option(Type::Identifier(circuit.identifier), expected, circuit.span()))
|
||||
} else if let Some(var) = self.symbol_table.clone().lookup_variable(&var.name) {
|
||||
Some(self.assert_expected_option(*var.type_, expected, var.span))
|
||||
Some(self.check_expected_option(*var.type_, expected, var.span))
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()).into());
|
||||
@ -69,9 +69,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.check_expected_option(Type::Address, expected, input.span()),
|
||||
LiteralExpression::Boolean(_, _) => self.check_expected_option(Type::Boolean, expected, input.span()),
|
||||
LiteralExpression::Field(_, _) => self.check_expected_option(Type::Field, expected, input.span()),
|
||||
LiteralExpression::Integer(type_, str_content, _) => {
|
||||
match type_ {
|
||||
IntegerType::I8 => {
|
||||
@ -151,11 +151,11 @@ 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.check_expected_option(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.check_expected_option(Type::Group, expected, input.span()),
|
||||
LiteralExpression::Scalar(_, _) => self.check_expected_option(Type::Scalar, expected, input.span()),
|
||||
LiteralExpression::String(_, _) => self.check_expected_option(Type::String, expected, input.span()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
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) {
|
||||
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(
|
||||
@ -190,7 +190,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
// Check return type.
|
||||
Some(self.assert_expected_option(core_instruction.return_type(), expected, access.span()))
|
||||
Some(self.check_expected_option(core_instruction.return_type(), expected, access.span()))
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()).into());
|
||||
@ -205,7 +205,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
match input.op {
|
||||
BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => {
|
||||
// Assert equal boolean types.
|
||||
self.assert_expected_option(Type::Boolean, destination, input.span());
|
||||
self.check_expected_option(Type::Boolean, destination, input.span());
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
|
||||
@ -245,12 +245,12 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
// 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()))
|
||||
self.check_expected_type(&other, Type::Scalar, input.right.span());
|
||||
Some(self.check_expected_type(destination, Type::Group, input.span()))
|
||||
}
|
||||
(other, Some(Type::Group)) => {
|
||||
self.assert_expected_type(&other, Type::Scalar, input.left.span());
|
||||
Some(self.assert_expected_type(destination, Type::Group, input.span()))
|
||||
self.check_expected_type(&other, Type::Scalar, input.left.span());
|
||||
Some(self.check_expected_type(destination, Type::Group, input.span()))
|
||||
}
|
||||
(t1, t2) => {
|
||||
// Assert equal field or integer types.
|
||||
@ -279,17 +279,17 @@ 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()))
|
||||
self.check_expected_type(&type_, Type::Field, input.right.span());
|
||||
Some(self.check_expected_type(destination, Type::Field, input.span()))
|
||||
}
|
||||
(type_, Some(Type::Field)) => {
|
||||
self.assert_expected_type(&type_, Type::Field, input.left.span());
|
||||
Some(self.assert_expected_type(destination, Type::Field, input.span()))
|
||||
self.check_expected_type(&type_, Type::Field, input.left.span());
|
||||
Some(self.check_expected_type(destination, Type::Field, input.span()))
|
||||
}
|
||||
(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(self.check_expected_type(destination, t1, input.span()))
|
||||
}
|
||||
(None, t2) => {
|
||||
// Allow integer t2 magnitude (u8, u16, u32)
|
||||
@ -313,12 +313,12 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
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()))
|
||||
Some(self.check_expected_type(destination, Type::Boolean, input.span()))
|
||||
}
|
||||
BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Lte | BinaryOperation::Gte => {
|
||||
// Assert left and right are equal field, scalar, or integer types.
|
||||
@ -333,19 +333,19 @@ 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.check_expected_type(&t2, Type::Field, input.left.span());
|
||||
}
|
||||
(t1, Some(Type::Field)) => {
|
||||
// Assert lhs is field.
|
||||
self.assert_expected_type(&t1, Type::Field, input.right.span());
|
||||
self.check_expected_type(&t1, Type::Field, input.right.span());
|
||||
}
|
||||
(Some(Type::Scalar), t2) => {
|
||||
// Assert rhs is scalar.
|
||||
self.assert_expected_type(&t2, Type::Scalar, input.left.span());
|
||||
self.check_expected_type(&t2, Type::Scalar, input.left.span());
|
||||
}
|
||||
(t1, Some(Type::Scalar)) => {
|
||||
// Assert lhs is scalar.
|
||||
self.assert_expected_type(&t1, Type::Scalar, input.right.span());
|
||||
self.check_expected_type(&t1, Type::Scalar, input.right.span());
|
||||
}
|
||||
(Some(Type::IntegerType(_)), t2) => {
|
||||
// Assert rhs is integer.
|
||||
@ -361,7 +361,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
// Assert destination is boolean.
|
||||
Some(self.assert_expected_type(destination, Type::Boolean, input.span()))
|
||||
Some(self.check_expected_type(destination, Type::Boolean, input.span()))
|
||||
}
|
||||
BinaryOperation::AddWrapped
|
||||
| BinaryOperation::SubWrapped
|
||||
@ -411,7 +411,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
UnaryOperation::Inverse => {
|
||||
// Assert field type only.
|
||||
self.assert_expected_type(destination, Type::Field, input.span());
|
||||
self.check_expected_type(destination, Type::Field, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Negate => {
|
||||
@ -446,7 +446,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
UnaryOperation::Square => {
|
||||
// Assert field type only.
|
||||
self.assert_expected_type(destination, Type::Field, input.span());
|
||||
self.check_expected_type(destination, Type::Field, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::SquareRoot => {
|
||||
@ -470,7 +470,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
match &*input.function {
|
||||
Expression::Identifier(ident) => {
|
||||
if let Some(func) = self.symbol_table.clone().lookup_fn(ident.name) {
|
||||
let ret = self.assert_expected_option(func.output, expected, func.span());
|
||||
let ret = self.check_expected_option(func.output, expected, func.span());
|
||||
|
||||
// Check number of function arguments.
|
||||
if func.input.len() != input.arguments.len() {
|
||||
@ -510,7 +510,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
) -> Self::Output {
|
||||
if let Some(circ) = self.symbol_table.clone().lookup_circuit(&input.name.name) {
|
||||
// Check circuit type name.
|
||||
let ret = self.assert_expected_circuit(circ.identifier, additional, input.name.span());
|
||||
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
|
||||
|
||||
// Check number of circuit members.
|
||||
if circ.members.len() != input.members.len() {
|
||||
|
@ -14,13 +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 indexmap::IndexSet;
|
||||
use crate::SymbolTable;
|
||||
use leo_ast::{Identifier, IntegerType, Node, Type};
|
||||
use leo_core::*;
|
||||
use leo_errors::{emitter::Handler, TypeCheckerError};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::SymbolTable;
|
||||
use indexmap::IndexSet;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub struct TypeChecker<'a> {
|
||||
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
|
||||
@ -32,6 +33,18 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) algorithms_types: IndexSet<Symbol>,
|
||||
}
|
||||
|
||||
const ADDRESS_TYPE: Type = Type::Address;
|
||||
|
||||
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 STRING_TYPE: Type = Type::String;
|
||||
|
||||
const INT_TYPES: [Type; 10] = [
|
||||
Type::IntegerType(IntegerType::I8),
|
||||
Type::IntegerType(IntegerType::I16),
|
||||
@ -59,36 +72,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: &'a mut SymbolTable<'a>, handler: &'a Handler) -> Self {
|
||||
@ -108,18 +91,185 @@ 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))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the `expected` type is not equal to the `actual` type.
|
||||
/// Use this method when you know the expected type.
|
||||
/// `span` should be the location of the expected type.
|
||||
pub(crate) fn check_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 handler if the `expected` type is not equal to the `actual` type.
|
||||
/// Use this method when you know the actual type.
|
||||
pub(crate) fn check_expected_option(&mut 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 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) {
|
||||
self.check_type(
|
||||
| type_: &Type | expected.iter().any(|t: &Type| t == type_),
|
||||
types_string(expected),
|
||||
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_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_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_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_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_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_string(&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_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) {
|
||||
@ -137,19 +287,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));
|
||||
@ -159,84 +298,16 @@ 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(&mut 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_string(types: &[Type]) -> String {
|
||||
types.iter().map(|type_| type_.to_string()).join(", ")
|
||||
}
|
@ -1,23 +1,3 @@
|
||||
record Token {
|
||||
// The token owner.
|
||||
owner: address,
|
||||
// The Aleo balance (in gates).
|
||||
balance: u64,
|
||||
// The token amount.
|
||||
amount: u64,
|
||||
}
|
||||
|
||||
function mint(owner: address, amount: u64) -> Token {
|
||||
return Token {
|
||||
owner,
|
||||
balance: 0u64,
|
||||
amount,
|
||||
};
|
||||
}
|
||||
|
||||
function main(x: address) -> u64 {
|
||||
const c: u64 = 1u64;
|
||||
let t: Token = mint(x, c);
|
||||
|
||||
return t.balance;
|
||||
function main() -> (bool, bool) {
|
||||
return (true, false)
|
||||
}
|
23
examples/hello-world/src/token.leo
Normal file
23
examples/hello-world/src/token.leo
Normal file
@ -0,0 +1,23 @@
|
||||
record Token {
|
||||
// The token owner.
|
||||
owner: address,
|
||||
// The Aleo balance (in gates).
|
||||
balance: u64,
|
||||
// The token amount.
|
||||
amount: u64,
|
||||
}
|
||||
|
||||
function mint(owner: address, amount: u64) -> Token {
|
||||
return Token {
|
||||
owner,
|
||||
balance: 0u64,
|
||||
amount,
|
||||
};
|
||||
}
|
||||
|
||||
function main(x: address) -> u64 {
|
||||
const c: u64 = 1u64;
|
||||
let t: Token = mint(x, c);
|
||||
|
||||
return t.balance;
|
||||
}
|
Loading…
Reference in New Issue
Block a user