refactor type checker for removal of Copy trait

This commit is contained in:
collin 2022-07-07 12:20:34 -07:00
parent 6b5f9a3926
commit f892fe1a57
6 changed files with 259 additions and 181 deletions

1
Cargo.lock generated
View File

@ -1231,6 +1231,7 @@ name = "leo-passes"
version = "1.5.3"
dependencies = [
"indexmap",
"itertools",
"leo-ast",
"leo-core",
"leo-errors",

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

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

View File

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

View File

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

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