mirror of
https://github.com/AleoHQ/leo.git
synced 2024-09-11 15:05:33 +03:00
commit
40e56f8613
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -368,9 +368,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.74"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
|
||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@ -1107,12 +1107,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.1"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b"
|
||||
checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
@ -1184,6 +1185,7 @@ version = "1.5.3"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"leo-errors",
|
||||
"leo-span",
|
||||
"serde",
|
||||
@ -1769,6 +1771,12 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15eb2c6e362923af47e13c23ca5afb859e83d54452c55b0b9ac763b8f7c1ac16"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
|
@ -30,6 +30,9 @@ version = "1.6.0"
|
||||
version = "1.9"
|
||||
features = [ "serde-1" ]
|
||||
|
||||
[dependencies.itertools]
|
||||
version = "0.10.5"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = [ "derive", "rc" ]
|
||||
|
@ -38,12 +38,15 @@ pub use err::*;
|
||||
mod ternary;
|
||||
pub use ternary::*;
|
||||
|
||||
mod tuple_init;
|
||||
pub use tuple_init::*;
|
||||
mod tuple;
|
||||
pub use tuple::*;
|
||||
|
||||
mod unary;
|
||||
pub use unary::*;
|
||||
|
||||
mod unit;
|
||||
pub use unit::*;
|
||||
|
||||
mod literal;
|
||||
pub use literal::*;
|
||||
|
||||
@ -71,6 +74,8 @@ pub enum Expression {
|
||||
Tuple(TupleExpression),
|
||||
/// An unary expression.
|
||||
Unary(UnaryExpression),
|
||||
/// A unit expression e.g. `()`
|
||||
Unit(UnitExpression),
|
||||
}
|
||||
|
||||
impl Node for Expression {
|
||||
@ -87,6 +92,7 @@ impl Node for Expression {
|
||||
Ternary(n) => n.span(),
|
||||
Tuple(n) => n.span(),
|
||||
Unary(n) => n.span(),
|
||||
Unit(n) => n.span(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +109,7 @@ impl Node for Expression {
|
||||
Ternary(n) => n.set_span(span),
|
||||
Tuple(n) => n.set_span(span),
|
||||
Unary(n) => n.set_span(span),
|
||||
Unit(n) => n.set_span(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,6 +128,7 @@ impl fmt::Display for Expression {
|
||||
Ternary(n) => n.fmt(f),
|
||||
Tuple(n) => n.fmt(f),
|
||||
Unary(n) => n.fmt(f),
|
||||
Unit(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A tuple construction expression, e.g., `(foo, false, 42)`.
|
||||
// TODO: Consider a restricted interface for constructing a tuple expression.
|
||||
|
||||
/// A tuple expression, e.g., `(foo, false, 42)`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct TupleExpression {
|
||||
/// The elements of the tuple.
|
32
compiler/ast/src/expressions/unit.rs
Normal file
32
compiler/ast/src/expressions/unit.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2019-2022 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 super::*;
|
||||
|
||||
/// Represents a unit expression.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UnitExpression {
|
||||
/// The span of the unit expression.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for UnitExpression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("()")
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_node_impl!(UnitExpression);
|
@ -35,6 +35,7 @@ pub trait ExpressionConsumer {
|
||||
Expression::Ternary(ternary) => self.consume_ternary(ternary),
|
||||
Expression::Tuple(tuple) => self.consume_tuple(tuple),
|
||||
Expression::Unary(unary) => self.consume_unary(unary),
|
||||
Expression::Unit(unit) => self.consume_unit(unit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +60,8 @@ pub trait ExpressionConsumer {
|
||||
fn consume_tuple(&mut self, _input: TupleExpression) -> Self::Output;
|
||||
|
||||
fn consume_unary(&mut self, _input: UnaryExpression) -> Self::Output;
|
||||
|
||||
fn consume_unit(&mut self, _input: UnitExpression) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A Consumer trait for statements in the AST.
|
||||
@ -73,6 +76,7 @@ pub trait StatementConsumer {
|
||||
Statement::Console(stmt) => self.consume_console(stmt),
|
||||
Statement::Decrement(stmt) => self.consume_decrement(stmt),
|
||||
Statement::Definition(stmt) => self.consume_definition(stmt),
|
||||
Statement::Expression(stmt) => self.consume_expression_statement(stmt),
|
||||
Statement::Finalize(stmt) => self.consume_finalize(stmt),
|
||||
Statement::Increment(stmt) => self.consume_increment(stmt),
|
||||
Statement::Iteration(stmt) => self.consume_iteration(*stmt),
|
||||
@ -92,6 +96,8 @@ pub trait StatementConsumer {
|
||||
|
||||
fn consume_definition(&mut self, input: DefinitionStatement) -> Self::Output;
|
||||
|
||||
fn consume_expression_statement(&mut self, input: ExpressionStatement) -> Self::Output;
|
||||
|
||||
fn consume_finalize(&mut self, input: FinalizeStatement) -> Self::Output;
|
||||
|
||||
fn consume_increment(&mut self, input: IncrementStatement) -> Self::Output;
|
||||
|
@ -36,6 +36,7 @@ pub trait ExpressionReconstructor {
|
||||
Expression::Ternary(ternary) => self.reconstruct_ternary(ternary),
|
||||
Expression::Tuple(tuple) => self.reconstruct_tuple(tuple),
|
||||
Expression::Unary(unary) => self.reconstruct_unary(unary),
|
||||
Expression::Unit(unit) => self.reconstruct_unit(unit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +151,10 @@ pub trait ExpressionReconstructor {
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_unit(&mut self, input: UnitExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Unit(input), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A Reconstructor trait for statements in the AST.
|
||||
@ -165,6 +170,7 @@ pub trait StatementReconstructor: ExpressionReconstructor {
|
||||
Statement::Console(stmt) => self.reconstruct_console(stmt),
|
||||
Statement::Decrement(stmt) => self.reconstruct_decrement(stmt),
|
||||
Statement::Definition(stmt) => self.reconstruct_definition(stmt),
|
||||
Statement::Expression(stmt) => self.reconstruct_expression_statement(stmt),
|
||||
Statement::Finalize(stmt) => self.reconstruct_finalize(stmt),
|
||||
Statement::Increment(stmt) => self.reconstruct_increment(stmt),
|
||||
Statement::Iteration(stmt) => self.reconstruct_iteration(*stmt),
|
||||
@ -245,7 +251,7 @@ pub trait StatementReconstructor: ExpressionReconstructor {
|
||||
(
|
||||
Statement::Definition(DefinitionStatement {
|
||||
declaration_type: input.declaration_type,
|
||||
variable_name: input.variable_name,
|
||||
place: input.place,
|
||||
type_: input.type_,
|
||||
value: self.reconstruct_expression(input.value).0,
|
||||
span: input.span,
|
||||
@ -254,6 +260,16 @@ pub trait StatementReconstructor: ExpressionReconstructor {
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_expression_statement(&mut self, input: ExpressionStatement) -> (Statement, Self::AdditionalOutput) {
|
||||
(
|
||||
Statement::Expression(ExpressionStatement {
|
||||
expression: self.reconstruct_expression(input.expression).0,
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_finalize(&mut self, input: FinalizeStatement) -> (Statement, Self::AdditionalOutput) {
|
||||
(
|
||||
Statement::Finalize(FinalizeStatement {
|
||||
|
@ -37,6 +37,7 @@ pub trait ExpressionVisitor<'a> {
|
||||
Expression::Ternary(ternary) => self.visit_ternary(ternary, additional),
|
||||
Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
|
||||
Expression::Unary(unary) => self.visit_unary(unary, additional),
|
||||
Expression::Unit(unit) => self.visit_unit(unit, additional),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +107,10 @@ pub trait ExpressionVisitor<'a> {
|
||||
self.visit_expression(&input.receiver, additional);
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_unit(&mut self, _input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor trait for statements in the AST.
|
||||
@ -118,6 +123,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
|
||||
Statement::Console(stmt) => self.visit_console(stmt),
|
||||
Statement::Decrement(stmt) => self.visit_decrement(stmt),
|
||||
Statement::Definition(stmt) => self.visit_definition(stmt),
|
||||
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
|
||||
Statement::Finalize(stmt) => self.visit_finalize(stmt),
|
||||
Statement::Increment(stmt) => self.visit_increment(stmt),
|
||||
Statement::Iteration(stmt) => self.visit_iteration(stmt),
|
||||
@ -167,6 +173,10 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
|
||||
self.visit_expression(&input.value, &Default::default());
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) {
|
||||
self.visit_expression(&input.expression, &Default::default());
|
||||
}
|
||||
|
||||
fn visit_finalize(&mut self, input: &'a FinalizeStatement) {
|
||||
input.arguments.iter().for_each(|expr| {
|
||||
self.visit_expression(expr, &Default::default());
|
||||
|
@ -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::{Expression, Identifier, Node, Type};
|
||||
use crate::{Expression, Node, Type};
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -29,7 +29,7 @@ pub struct DefinitionStatement {
|
||||
/// What sort of declaration is this? `let` or `const`?.
|
||||
pub declaration_type: DeclarationType,
|
||||
/// The bindings / variable names to declare.
|
||||
pub variable_name: Identifier,
|
||||
pub place: Expression,
|
||||
/// The types of the bindings, if specified, or inferred otherwise.
|
||||
pub type_: Type,
|
||||
/// An initializer value for the bindings.
|
||||
@ -41,7 +41,7 @@ pub struct DefinitionStatement {
|
||||
impl fmt::Display for DefinitionStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} ", self.declaration_type)?;
|
||||
write!(f, "{}", self.variable_name)?;
|
||||
write!(f, "{}", self.place)?;
|
||||
write!(f, ": {}", self.type_)?;
|
||||
write!(f, " = {};", self.value)
|
||||
}
|
||||
|
38
compiler/ast/src/statement/expression.rs
Normal file
38
compiler/ast/src/statement/expression.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2019-2022 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::{Expression, Node};
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// An expression statement, `foo(a);`.
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub struct ExpressionStatement {
|
||||
/// The expression associated with the statement.
|
||||
pub expression: Expression,
|
||||
/// The span.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for ExpressionStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{};", self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_node_impl!(ExpressionStatement);
|
@ -32,6 +32,9 @@ pub use decrement::*;
|
||||
pub mod definition;
|
||||
pub use definition::*;
|
||||
|
||||
pub mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
pub mod finalize;
|
||||
pub use finalize::*;
|
||||
|
||||
@ -66,6 +69,8 @@ pub enum Statement {
|
||||
Decrement(DecrementStatement),
|
||||
/// A binding or set of bindings / variables to declare.
|
||||
Definition(DefinitionStatement),
|
||||
/// An expression statement
|
||||
Expression(ExpressionStatement),
|
||||
/// A finalize statement.
|
||||
Finalize(FinalizeStatement),
|
||||
/// An increment statement.
|
||||
@ -95,6 +100,7 @@ impl fmt::Display for Statement {
|
||||
Statement::Console(x) => x.fmt(f),
|
||||
Statement::Decrement(x) => x.fmt(f),
|
||||
Statement::Definition(x) => x.fmt(f),
|
||||
Statement::Expression(x) => x.fmt(f),
|
||||
Statement::Finalize(x) => x.fmt(f),
|
||||
Statement::Increment(x) => x.fmt(f),
|
||||
Statement::Iteration(x) => x.fmt(f),
|
||||
@ -113,6 +119,7 @@ impl Node for Statement {
|
||||
Console(n) => n.span(),
|
||||
Decrement(n) => n.span(),
|
||||
Definition(n) => n.span(),
|
||||
Expression(n) => n.span(),
|
||||
Finalize(n) => n.span(),
|
||||
Increment(n) => n.span(),
|
||||
Iteration(n) => n.span(),
|
||||
@ -129,6 +136,7 @@ impl Node for Statement {
|
||||
Console(n) => n.set_span(span),
|
||||
Decrement(n) => n.set_span(span),
|
||||
Definition(n) => n.set_span(span),
|
||||
Expression(n) => n.set_span(span),
|
||||
Finalize(n) => n.set_span(span),
|
||||
Increment(n) => n.set_span(span),
|
||||
Iteration(n) => n.set_span(span),
|
||||
|
@ -15,27 +15,16 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Type;
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, ops::Deref};
|
||||
|
||||
// TODO: Consider defining a safe interface for constructing a tuple type.
|
||||
|
||||
/// A type list of at least two types.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Tuple(pub Vec<Type>);
|
||||
|
||||
impl Tuple {
|
||||
/// Returns a new `Type::Tuple` enumeration.
|
||||
pub fn try_new(elements: Vec<Type>, span: Span) -> Result<Type> {
|
||||
match elements.len() {
|
||||
0 => Err(AstError::empty_tuple(span).into()),
|
||||
1 => Err(AstError::one_element_tuple(span).into()),
|
||||
_ => Ok(Type::Tuple(Tuple(elements))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Tuple {
|
||||
type Target = Vec<Type>;
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
use crate::{Identifier, IntegerType, MappingType, Tuple};
|
||||
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
@ -69,9 +70,9 @@ impl Type {
|
||||
(Type::Mapping(left), Type::Mapping(right)) => {
|
||||
left.key.eq_flat(&right.key) && left.value.eq_flat(&right.value)
|
||||
}
|
||||
(Type::Tuple(left), Type::Tuple(right)) => left
|
||||
(Type::Tuple(left), Type::Tuple(right)) if left.len() == right.len() => left
|
||||
.iter()
|
||||
.zip(right.iter())
|
||||
.zip_eq(right.iter())
|
||||
.all(|(left_type, right_type)| left_type.eq_flat(right_type)),
|
||||
(Type::Identifier(left), Type::Identifier(right)) => left.matches(right),
|
||||
_ => false,
|
||||
|
@ -224,7 +224,7 @@ impl<'a> Compiler<'a> {
|
||||
self.parse_program()?;
|
||||
let symbol_table = self.compiler_stages()?;
|
||||
|
||||
let bytecode = CodeGenerator::do_pass((&self.ast, self.handler))?;
|
||||
let bytecode = CodeGenerator::do_pass((&self.ast, &symbol_table))?;
|
||||
|
||||
Ok((symbol_table, bytecode))
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ fn temp_dir() -> PathBuf {
|
||||
.into_path()
|
||||
}
|
||||
|
||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>, handler: &Handler) -> Result<String, LeoError> {
|
||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, LeoError> {
|
||||
let st = parsed.symbol_table_pass()?;
|
||||
let st = parsed.type_checker_pass(st)?;
|
||||
let st = parsed.loop_unrolling_pass(st)?;
|
||||
@ -201,7 +201,7 @@ fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>, handler: &Handler) -> R
|
||||
parsed.flattening_pass(&st, assigner)?;
|
||||
|
||||
// Compile Leo program to bytecode.
|
||||
let bytecode = CodeGenerator::do_pass((&parsed.ast, handler))?;
|
||||
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st))?;
|
||||
|
||||
Ok(bytecode)
|
||||
}
|
||||
@ -241,7 +241,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
|
||||
// Compile the program to bytecode.
|
||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed, handler))?;
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
|
||||
// Run snarkvm package.
|
||||
{
|
||||
|
@ -423,12 +423,20 @@ impl ParserContext<'_> {
|
||||
return Ok(Expression::Literal(Literal::Group(Box::new(GroupLiteral::Tuple(gt)))));
|
||||
}
|
||||
|
||||
let (mut tuple, trailing, span) = self.parse_expr_tuple()?;
|
||||
let (mut elements, trailing, span) = self.parse_expr_tuple()?;
|
||||
|
||||
if !trailing && tuple.len() == 1 {
|
||||
Ok(tuple.swap_remove(0))
|
||||
} else {
|
||||
Ok(Expression::Tuple(TupleExpression { elements: tuple, span }))
|
||||
match elements.len() {
|
||||
// If the tuple expression is empty, return a `UnitExpression`.
|
||||
0 => Ok(Expression::Unit(UnitExpression { span })),
|
||||
1 => match trailing {
|
||||
// If there is one element in the tuple but no trailing comma, e.g `(foo)`, return the element.
|
||||
false => Ok(elements.swap_remove(0)),
|
||||
// If there is one element in the tuple and a trailing comma, e.g `(foo,)`, emit an error since tuples must have at least two elements.
|
||||
true => Err(ParserError::tuple_must_have_at_least_two_elements("expression", span).into()),
|
||||
},
|
||||
// Otherwise, return a tuple expression.
|
||||
// Note: This is the only place where `TupleExpression` is constructed in the parser.
|
||||
_ => Ok(Expression::Tuple(TupleExpression { elements, span })),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,11 +99,12 @@ impl ParserContext<'_> {
|
||||
|
||||
Ok(Statement::Assign(Box::new(AssignStatement { span, place, value })))
|
||||
} else {
|
||||
// Error on `expr;` but recover as an empty block `{}`.
|
||||
self.expect(&Token::Semicolon)?;
|
||||
let span = place.span() + self.prev_token.span;
|
||||
self.emit_err(ParserError::expr_stmts_disallowed(span));
|
||||
Ok(Statement::dummy(span))
|
||||
// Parse the expression as a statement.
|
||||
let end = self.expect(&Token::Semicolon)?;
|
||||
Ok(Statement::Expression(ExpressionStatement {
|
||||
span: place.span() + end,
|
||||
expression: place,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +117,12 @@ impl ParserContext<'_> {
|
||||
/// Returns a [`ReturnStatement`] AST node if the next tokens represent a return statement.
|
||||
fn parse_return_statement(&mut self) -> Result<ReturnStatement> {
|
||||
let start = self.expect(&Token::Return)?;
|
||||
let expression = self.parse_expression()?;
|
||||
let expression = match self.token.token {
|
||||
// If the next token is a semicolon, implicitly return a unit expression, `()`.
|
||||
Token::Semicolon => Expression::Unit(UnitExpression { span: self.token.span }),
|
||||
// Otherwise, attempt to parse an expression.
|
||||
_ => self.parse_expression()?,
|
||||
};
|
||||
self.expect(&Token::Semicolon)?;
|
||||
let span = start + expression.span();
|
||||
Ok(ReturnStatement { span, expression })
|
||||
@ -291,7 +297,9 @@ impl ParserContext<'_> {
|
||||
};
|
||||
|
||||
// Parse variable name and type.
|
||||
let (variable_name, type_) = self.parse_typed_ident()?;
|
||||
let place = self.parse_expression()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_type()?.0;
|
||||
|
||||
self.expect(&Token::Assign)?;
|
||||
let value = self.parse_expression()?;
|
||||
@ -300,7 +308,7 @@ impl ParserContext<'_> {
|
||||
Ok(DefinitionStatement {
|
||||
span: decl_span + value.span(),
|
||||
declaration_type: decl_type,
|
||||
variable_name,
|
||||
place,
|
||||
type_,
|
||||
value,
|
||||
})
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use leo_errors::Result;
|
||||
use leo_errors::{ParserError, Result};
|
||||
|
||||
pub(super) const TYPE_TOKENS: &[Token] = &[
|
||||
Token::Address,
|
||||
@ -78,6 +78,17 @@ impl ParserContext<'_> {
|
||||
pub fn parse_type(&mut self) -> Result<(Type, Span)> {
|
||||
if let Some(ident) = self.eat_identifier() {
|
||||
Ok((Type::Identifier(ident), ident.span))
|
||||
} else if self.token.token == Token::LeftParen {
|
||||
let (types, _, span) = self.parse_paren_comma_list(|p| p.parse_type().map(Some))?;
|
||||
match types.len() {
|
||||
// If the parenthetical block is empty, e.g. `()` or `( )`, it should be parsed into `Unit` types.
|
||||
0 => Ok((Type::Unit, span)),
|
||||
// If the parenthetical block contains a single type, e.g. `(u8)`, emit an error, since tuples must have at least two elements.
|
||||
1 => Err(ParserError::tuple_must_have_at_least_two_elements("type", span).into()),
|
||||
// Otherwise, parse it into a `Tuple` type.
|
||||
// Note: This is the only place where `Tuple` type is constructed in the parser.
|
||||
_ => Ok((Type::Tuple(Tuple(types.into_iter().map(|t| t.0).collect())), span)),
|
||||
}
|
||||
} else {
|
||||
self.parse_primitive_type()
|
||||
}
|
||||
|
@ -14,14 +14,16 @@
|
||||
// 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::SymbolTable;
|
||||
|
||||
use leo_ast::Function;
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_span::Symbol;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub struct CodeGenerator<'a> {
|
||||
_handler: &'a Handler,
|
||||
/// The symbol table for the program.
|
||||
pub(crate) symbol_table: &'a SymbolTable,
|
||||
/// A counter to track the next available register.
|
||||
pub(crate) next_register: u64,
|
||||
/// Reference to the current function.
|
||||
@ -40,10 +42,10 @@ pub struct CodeGenerator<'a> {
|
||||
|
||||
impl<'a> CodeGenerator<'a> {
|
||||
/// Initializes a new `CodeGenerator`.
|
||||
pub fn new(handler: &'a Handler) -> Self {
|
||||
pub fn new(symbol_table: &'a SymbolTable) -> Self {
|
||||
// Initialize variable mapping.
|
||||
Self {
|
||||
_handler: handler,
|
||||
symbol_table,
|
||||
next_register: 0,
|
||||
current_function: None,
|
||||
variable_mapping: IndexMap::new(),
|
||||
|
@ -25,20 +25,18 @@ mod visit_statements;
|
||||
|
||||
mod visit_type;
|
||||
|
||||
use crate::Pass;
|
||||
use crate::{Pass, SymbolTable};
|
||||
|
||||
use leo_ast::Ast;
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_errors::Result;
|
||||
|
||||
impl<'a> Pass for CodeGenerator<'a> {
|
||||
type Input = (&'a Ast, &'a Handler);
|
||||
type Input = (&'a Ast, &'a SymbolTable);
|
||||
type Output = Result<String>;
|
||||
|
||||
fn do_pass((ast, handler): Self::Input) -> Self::Output {
|
||||
let mut generator = Self::new(handler);
|
||||
fn do_pass((ast, symbol_table): Self::Input) -> Self::Output {
|
||||
let mut generator = Self::new(symbol_table);
|
||||
let bytecode = generator.visit_program(ast.as_repr());
|
||||
handler.last_err()?;
|
||||
|
||||
Ok(bytecode)
|
||||
}
|
||||
|
@ -18,9 +18,10 @@ use crate::CodeGenerator;
|
||||
use leo_ast::{
|
||||
AccessExpression, AssociatedFunction, BinaryExpression, BinaryOperation, CallExpression, ErrExpression, Expression,
|
||||
Identifier, Literal, MemberAccess, StructExpression, TernaryExpression, TupleExpression, Type, UnaryExpression,
|
||||
UnaryOperation,
|
||||
UnaryOperation, UnitExpression,
|
||||
};
|
||||
use leo_span::sym;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
@ -41,6 +42,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
Expression::Ternary(expr) => self.visit_ternary(expr),
|
||||
Expression::Tuple(expr) => self.visit_tuple(expr),
|
||||
Expression::Unary(expr) => self.visit_unary(expr),
|
||||
Expression::Unit(expr) => self.visit_unit(expr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,6 +277,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Cleanup
|
||||
fn visit_call(&mut self, input: &'a CallExpression) -> (String, String) {
|
||||
let mut call_instruction = match &input.external {
|
||||
Some(external) => format!(" call {external}.aleo/{} ", input.function),
|
||||
@ -288,19 +291,53 @@ impl<'a> CodeGenerator<'a> {
|
||||
instructions.push_str(&argument_instructions);
|
||||
}
|
||||
|
||||
// Push destination register to call instruction.
|
||||
let destination_register = format!("r{}", self.next_register);
|
||||
writeln!(call_instruction, "into {destination_register};").expect("failed to write to string");
|
||||
instructions.push_str(&call_instruction);
|
||||
// Lookup the function return type.
|
||||
let function_name = match input.function.borrow() {
|
||||
Expression::Identifier(identifier) => identifier.name,
|
||||
_ => unreachable!("Parsing guarantees that all `input.function` is always an identifier."),
|
||||
};
|
||||
let return_type = &self
|
||||
.symbol_table
|
||||
.borrow()
|
||||
.functions
|
||||
.get(&function_name)
|
||||
.unwrap()
|
||||
.output_type;
|
||||
match return_type {
|
||||
Type::Unit => (String::new(), instructions), // Do nothing
|
||||
Type::Tuple(tuple) => match tuple.len() {
|
||||
0 | 1 => unreachable!("Parsing guarantees that a tuple type has at least two elements"),
|
||||
len => {
|
||||
let mut destinations = Vec::new();
|
||||
for _ in 0..len {
|
||||
let destination_register = format!("r{}", self.next_register);
|
||||
destinations.push(destination_register);
|
||||
self.next_register += 1;
|
||||
}
|
||||
let destinations = destinations.join(" ");
|
||||
writeln!(call_instruction, "into {destinations};", destinations = destinations)
|
||||
.expect("failed to write to string");
|
||||
instructions.push_str(&call_instruction);
|
||||
|
||||
// Increment the register counter.
|
||||
self.next_register += 1;
|
||||
(destinations, call_instruction)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Push destination register to call instruction.
|
||||
let destination_register = format!("r{}", self.next_register);
|
||||
writeln!(call_instruction, "into {destination_register};").expect("failed to write to string");
|
||||
instructions.push_str(&call_instruction);
|
||||
|
||||
(destination_register, instructions)
|
||||
// Increment the register counter.
|
||||
self.next_register += 1;
|
||||
|
||||
(destination_register, instructions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_tuple(&mut self, input: &'a TupleExpression) -> (String, String) {
|
||||
// Need to return a single string here so we will join the tuple elements with '\n'
|
||||
// Need to return a single string here so we will join the tuple elements with ' '
|
||||
// and split them after this method is called.
|
||||
let mut tuple_elements = Vec::with_capacity(input.elements.len());
|
||||
let mut instructions = String::new();
|
||||
@ -313,6 +350,10 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
|
||||
// CAUTION: does not return the destination_register.
|
||||
(tuple_elements.join("\n"), instructions)
|
||||
(tuple_elements.join(" "), instructions)
|
||||
}
|
||||
|
||||
fn visit_unit(&mut self, _input: &'a UnitExpression) -> (String, String) {
|
||||
unreachable!("`UnitExpression`s should not be visited during code generation.")
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ use crate::CodeGenerator;
|
||||
|
||||
use leo_ast::{
|
||||
AssignStatement, Block, ConditionalStatement, ConsoleFunction, ConsoleStatement, DecrementStatement,
|
||||
DefinitionStatement, Expression, FinalizeStatement, IncrementStatement, IterationStatement, Mode, Output,
|
||||
ReturnStatement, Statement,
|
||||
DefinitionStatement, Expression, ExpressionStatement, FinalizeStatement, IncrementStatement, IterationStatement,
|
||||
Mode, Output, ReturnStatement, Statement,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
@ -34,6 +34,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
Statement::Console(stmt) => self.visit_console(stmt),
|
||||
Statement::Decrement(stmt) => self.visit_decrement(stmt),
|
||||
Statement::Definition(stmt) => self.visit_definition(stmt),
|
||||
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
|
||||
Statement::Finalize(stmt) => self.visit_finalize(stmt),
|
||||
Statement::Increment(stmt) => self.visit_increment(stmt),
|
||||
Statement::Iteration(stmt) => self.visit_iteration(stmt),
|
||||
@ -44,7 +45,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
fn visit_return(&mut self, input: &'a ReturnStatement) -> String {
|
||||
match input.expression {
|
||||
// Skip empty return statements.
|
||||
Expression::Tuple(ref tuple) if tuple.elements.is_empty() => String::new(),
|
||||
Expression::Unit(_) => String::new(),
|
||||
_ => {
|
||||
let (operand, mut expression_instructions) = self.visit_expression(&input.expression);
|
||||
// Get the output type of the function.
|
||||
@ -56,7 +57,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
self.current_function.unwrap().output.iter()
|
||||
};
|
||||
let instructions = operand
|
||||
.split('\n')
|
||||
.split(' ')
|
||||
.into_iter()
|
||||
.zip_eq(output)
|
||||
.map(|(operand, output)| {
|
||||
@ -110,6 +111,14 @@ impl<'a> CodeGenerator<'a> {
|
||||
unreachable!("DefinitionStatement's should not exist in SSA form.")
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> String {
|
||||
println!("ExpressionStatement: {:?}", input);
|
||||
match input.expression {
|
||||
Expression::Call(_) => self.visit_expression(&input.expression).1,
|
||||
_ => unreachable!("ExpressionStatement's can only contain CallExpression's."),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_increment(&mut self, input: &'a IncrementStatement) -> String {
|
||||
let (index, mut instructions) = self.visit_expression(&input.index);
|
||||
let (amount, amount_instructions) = self.visit_expression(&input.amount);
|
||||
@ -143,12 +152,29 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, input: &'a AssignStatement) -> String {
|
||||
match &input.place {
|
||||
Expression::Identifier(identifier) => {
|
||||
match (&input.place, &input.value) {
|
||||
(Expression::Identifier(identifier), _) => {
|
||||
let (operand, expression_instructions) = self.visit_expression(&input.value);
|
||||
self.variable_mapping.insert(&identifier.name, operand);
|
||||
expression_instructions
|
||||
}
|
||||
(Expression::Tuple(tuple), Expression::Call(_)) => {
|
||||
let (operand, expression_instructions) = self.visit_expression(&input.value);
|
||||
// Split out the destinations from the tuple.
|
||||
let operands = operand.split(' ').collect::<Vec<_>>();
|
||||
// Add the destinations to the variable mapping.
|
||||
tuple.elements.iter().zip_eq(operands).for_each(|(element, operand)| {
|
||||
match element {
|
||||
Expression::Identifier(identifier) => {
|
||||
self.variable_mapping.insert(&identifier.name, operand.to_string())
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Type checking ensures that tuple elements on the lhs are always identifiers.")
|
||||
}
|
||||
};
|
||||
});
|
||||
expression_instructions
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"Code generation for the left-hand side of an assignment is only implemented for `Identifier`s."
|
||||
),
|
||||
|
@ -33,7 +33,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
unreachable!("Mapping types are not supported at this phase of compilation")
|
||||
}
|
||||
Type::Tuple(_) => {
|
||||
unreachable!("Tuple types are not supported at this phase of compilation")
|
||||
unreachable!("Tuple types should not be visited at this phase of compilation")
|
||||
}
|
||||
Type::Err => unreachable!("Error types should not exist at this phase of compilation"),
|
||||
Type::Unit => unreachable!("Unit types are not supported at this phase of compilation"),
|
||||
|
@ -18,8 +18,8 @@ use crate::Flattener;
|
||||
use itertools::Itertools;
|
||||
|
||||
use leo_ast::{
|
||||
AccessExpression, Expression, ExpressionReconstructor, Member, MemberAccess, Statement, StructExpression,
|
||||
StructVariableInitializer, TernaryExpression, TupleExpression,
|
||||
AccessExpression, AssociatedFunction, Expression, ExpressionReconstructor, Member, MemberAccess, Statement,
|
||||
StructExpression, StructVariableInitializer, TernaryExpression, TupleExpression,
|
||||
};
|
||||
|
||||
// TODO: Clean up logic. To be done in a follow-up PR (feat/tuples)
|
||||
@ -27,6 +27,78 @@ use leo_ast::{
|
||||
impl ExpressionReconstructor for Flattener<'_> {
|
||||
type AdditionalOutput = Vec<Statement>;
|
||||
|
||||
/// Replaces a tuple access expression with the appropriate expression.
|
||||
fn reconstruct_access(&mut self, input: AccessExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
let mut statements = Vec::new();
|
||||
(
|
||||
match input {
|
||||
AccessExpression::AssociatedFunction(function) => {
|
||||
Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction {
|
||||
ty: function.ty,
|
||||
name: function.name,
|
||||
args: function
|
||||
.args
|
||||
.into_iter()
|
||||
.map(|arg| self.reconstruct_expression(arg).0)
|
||||
.collect(),
|
||||
span: function.span,
|
||||
}))
|
||||
}
|
||||
AccessExpression::Member(member) => Expression::Access(AccessExpression::Member(MemberAccess {
|
||||
inner: Box::new(self.reconstruct_expression(*member.inner).0),
|
||||
name: member.name,
|
||||
span: member.span,
|
||||
})),
|
||||
AccessExpression::Tuple(tuple) => {
|
||||
// Reconstruct the tuple expression.
|
||||
let (expr, stmts) = self.reconstruct_expression(*tuple.tuple);
|
||||
|
||||
// Accumulate any statements produced.
|
||||
statements.extend(stmts);
|
||||
|
||||
// Lookup the expression in the tuple map.
|
||||
match expr {
|
||||
Expression::Identifier(identifier) => {
|
||||
// Note that this unwrap is safe since TYC guarantees that all tuples are declared and indices are valid.
|
||||
self.tuples.get(&identifier.name).unwrap().elements[tuple.index.to_usize()].clone()
|
||||
}
|
||||
_ => unreachable!("SSA guarantees that subexpressions are identifiers or literals."),
|
||||
}
|
||||
}
|
||||
expr => Expression::Access(expr),
|
||||
},
|
||||
statements,
|
||||
)
|
||||
}
|
||||
|
||||
/// Reconstructs a struct init expression, flattening any tuples in the expression.
|
||||
fn reconstruct_struct_init(&mut self, input: StructExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
let mut statements = Vec::new();
|
||||
let mut members = Vec::with_capacity(input.members.len());
|
||||
|
||||
// Reconstruct and flatten the argument expressions.
|
||||
for member in input.members.into_iter() {
|
||||
// Note that this unwrap is safe since SSA guarantees that all struct variable initializers are of the form `<name>: <expr>`.
|
||||
let (expr, stmts) = self.reconstruct_expression(member.expression.unwrap());
|
||||
// Accumulate any statements produced.
|
||||
statements.extend(stmts);
|
||||
// Accumulate the struct members.
|
||||
members.push(StructVariableInitializer {
|
||||
identifier: member.identifier,
|
||||
expression: Some(expr),
|
||||
});
|
||||
}
|
||||
|
||||
(
|
||||
Expression::Struct(StructExpression {
|
||||
name: input.name,
|
||||
members,
|
||||
span: input.span,
|
||||
}),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
|
||||
/// Reconstructs ternary expressions over tuples and structs, accumulating any statements that are generated.
|
||||
/// This is necessary because Aleo instructions does not support ternary expressions over composite data types.
|
||||
/// For example, the ternary expression `cond ? (a, b) : (c, d)` is flattened into the following:
|
||||
@ -254,6 +326,22 @@ impl ExpressionReconstructor for Flattener<'_> {
|
||||
|
||||
(Expression::Identifier(identifier), statements)
|
||||
}
|
||||
// If both expressions are identifiers which map to tuples, construct ternary expression over the tuples.
|
||||
(Expression::Identifier(first), Expression::Identifier(second))
|
||||
if self.tuples.contains_key(&first.name) && self.tuples.contains_key(&second.name) =>
|
||||
{
|
||||
// Note that this unwrap is safe since we check that `self.tuples` contains the key.
|
||||
let first_tuple = self.tuples.get(&first.name).unwrap();
|
||||
// Note that this unwrap is safe since we check that `self.tuples` contains the key.
|
||||
let second_tuple = self.tuples.get(&second.name).unwrap();
|
||||
// Note that type checking guarantees that both expressions have the same same type.
|
||||
self.reconstruct_ternary(TernaryExpression {
|
||||
condition: input.condition,
|
||||
if_true: Box::new(Expression::Tuple(first_tuple.clone())),
|
||||
if_false: Box::new(Expression::Tuple(second_tuple.clone())),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
// Otherwise, create a new intermediate assignment for the ternary expression are return the assigned variable.
|
||||
// Note that a new assignment must be created to flattened nested ternary expressions.
|
||||
(if_true, if_false) => {
|
||||
|
@ -16,10 +16,7 @@
|
||||
|
||||
use crate::Flattener;
|
||||
|
||||
use leo_ast::{
|
||||
Finalize, FinalizeStatement, Function, ProgramReconstructor, ReturnStatement, Statement, StatementReconstructor,
|
||||
Type,
|
||||
};
|
||||
use leo_ast::{Finalize, FinalizeStatement, Function, ProgramReconstructor, Statement, StatementReconstructor, Type};
|
||||
|
||||
impl ProgramReconstructor for Flattener<'_> {
|
||||
/// Flattens a function's body and finalize block, if it exists.
|
||||
@ -34,7 +31,6 @@ impl ProgramReconstructor for Flattener<'_> {
|
||||
self.structs.insert(input.identifier().name, struct_name.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten the finalize block.
|
||||
let mut block = self.reconstruct_block(finalize.block).0;
|
||||
|
||||
@ -42,18 +38,7 @@ impl ProgramReconstructor for Flattener<'_> {
|
||||
let returns = self.clear_early_returns();
|
||||
|
||||
// If the finalize block contains return statements, then we fold them into a single return statement.
|
||||
if !returns.is_empty() {
|
||||
let (expression, stmts) = self.fold_guards("ret$", returns);
|
||||
|
||||
// Add all of the accumulated statements to the end of the block.
|
||||
block.statements.extend(stmts);
|
||||
|
||||
// Add the `ReturnStatement` to the end of the block.
|
||||
block.statements.push(Statement::Return(ReturnStatement {
|
||||
expression,
|
||||
span: Default::default(),
|
||||
}));
|
||||
}
|
||||
self.fold_returns(&mut block, returns);
|
||||
|
||||
// Initialize `self.finalizes` with the appropriate number of vectors.
|
||||
self.finalizes = vec![vec![]; finalize.input.len()];
|
||||
@ -83,18 +68,7 @@ impl ProgramReconstructor for Flattener<'_> {
|
||||
let returns = self.clear_early_returns();
|
||||
|
||||
// If the function contains return statements, then we fold them into a single return statement.
|
||||
if !returns.is_empty() {
|
||||
let (expression, stmts) = self.fold_guards("ret$", returns);
|
||||
|
||||
// Add all of the accumulated statements to the end of the block.
|
||||
block.statements.extend(stmts);
|
||||
|
||||
// Add the `ReturnStatement` to the end of the block.
|
||||
block.statements.push(Statement::Return(ReturnStatement {
|
||||
expression,
|
||||
span: Default::default(),
|
||||
}));
|
||||
}
|
||||
self.fold_returns(&mut block, returns);
|
||||
|
||||
// If the function has a finalize block, then type checking guarantees that it has at least one finalize statement.
|
||||
if finalize.is_some() {
|
||||
|
@ -15,11 +15,13 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Flattener;
|
||||
use itertools::Itertools;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use leo_ast::{
|
||||
AssignStatement, BinaryExpression, BinaryOperation, Block, ConditionalStatement, ConsoleFunction, ConsoleStatement,
|
||||
DefinitionStatement, Expression, ExpressionReconstructor, FinalizeStatement, IterationStatement, Node,
|
||||
ReturnStatement, Statement, StatementReconstructor, UnaryExpression, UnaryOperation,
|
||||
DefinitionStatement, Expression, ExpressionReconstructor, FinalizeStatement, Identifier, IterationStatement, Node,
|
||||
ReturnStatement, Statement, StatementReconstructor, TupleExpression, Type, UnaryExpression, UnaryOperation,
|
||||
};
|
||||
|
||||
impl StatementReconstructor for Flattener<'_> {
|
||||
@ -28,29 +30,189 @@ impl StatementReconstructor for Flattener<'_> {
|
||||
/// Note that new statements are only produced if the right hand side is a ternary expression over structs.
|
||||
/// Otherwise, the statement is returned as is.
|
||||
fn reconstruct_assign(&mut self, assign: AssignStatement) -> (Statement, Self::AdditionalOutput) {
|
||||
let lhs = match assign.place {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("`AssignStatement`s can only have `Identifier`s on the left hand side."),
|
||||
};
|
||||
// Flatten the rhs of the assignment.
|
||||
let (value, mut statements) = self.reconstruct_expression(assign.value);
|
||||
match (assign.place, value) {
|
||||
// If the lhs is an identifier and the rhs is a tuple, then add the tuple to `self.tuples`.
|
||||
(Expression::Identifier(identifier), Expression::Tuple(tuple)) => {
|
||||
self.tuples.insert(identifier.name, tuple);
|
||||
// Note that tuple assignments are removed from the AST.
|
||||
(Statement::dummy(Default::default()), statements)
|
||||
}
|
||||
// If the lhs is an identifier and the rhs is an identifier that is a tuple, then add it to `self.tuples`.
|
||||
(Expression::Identifier(lhs_identifier), Expression::Identifier(rhs_identifier))
|
||||
if self.tuples.contains_key(&rhs_identifier.name) =>
|
||||
{
|
||||
// Lookup the entry in `self.tuples` and add it for the lhs of the assignment.
|
||||
// Note that the `unwrap` is safe since the match arm checks that the entry exists.
|
||||
self.tuples.insert(
|
||||
lhs_identifier.name,
|
||||
self.tuples.get(&rhs_identifier.name).unwrap().clone(),
|
||||
);
|
||||
// Note that tuple assignments are removed from the AST.
|
||||
(Statement::dummy(Default::default()), statements)
|
||||
}
|
||||
// If the lhs is an identifier and the rhs is a function call that produces a tuple, then add it to `self.tuples`.
|
||||
(Expression::Identifier(lhs_identifier), Expression::Call(call)) => {
|
||||
// Retrieve the entry in the symbol table for the function call.
|
||||
// Note that this unwrap is safe since type checking ensures that the function exists.
|
||||
let function_name = match call.function.borrow() {
|
||||
Expression::Identifier(rhs_identifier) => rhs_identifier.name,
|
||||
_ => unreachable!("Parsing guarantees that `function` is an identifier."),
|
||||
};
|
||||
|
||||
let (value, statements) = match assign.value {
|
||||
// If the rhs of the assignment is ternary expression, reconstruct it.
|
||||
Expression::Ternary(ternary) => self.reconstruct_ternary(ternary),
|
||||
// Otherwise return the original statement.
|
||||
value => (value, Default::default()),
|
||||
};
|
||||
let function = self.symbol_table.borrow().functions.get(&function_name).unwrap();
|
||||
match &function.output_type {
|
||||
// If the function returns a tuple, reconstruct the assignment and add an entry to `self.tuples`.
|
||||
Type::Tuple(tuple) => {
|
||||
// Create a new tuple expression with unique identifiers for each index of the lhs.
|
||||
let tuple_expression = TupleExpression {
|
||||
elements: (0..tuple.len())
|
||||
.zip_eq(tuple.0.iter())
|
||||
.map(|(i, type_)| {
|
||||
let identifier = Identifier::new(
|
||||
self.assigner.unique_symbol(lhs_identifier.name, format!("$index${i}$")),
|
||||
);
|
||||
|
||||
// Update the `self.structs` if the rhs is a struct.
|
||||
self.update_structs(&lhs, &value);
|
||||
// If the output type is a struct, add it to `self.structs`.
|
||||
if let Type::Identifier(struct_name) = type_ {
|
||||
self.structs.insert(identifier.name, struct_name.name);
|
||||
}
|
||||
|
||||
(
|
||||
Statement::Assign(Box::new(AssignStatement {
|
||||
place: Expression::Identifier(lhs),
|
||||
value,
|
||||
span: assign.span,
|
||||
})),
|
||||
statements,
|
||||
)
|
||||
Expression::Identifier(identifier)
|
||||
})
|
||||
.collect(),
|
||||
span: Default::default(),
|
||||
};
|
||||
// Add the `tuple_expression` to `self.tuples`.
|
||||
self.tuples.insert(lhs_identifier.name, tuple_expression.clone());
|
||||
// Construct a new assignment statement with a tuple expression on the lhs.
|
||||
(
|
||||
Statement::Assign(Box::new(AssignStatement {
|
||||
place: Expression::Tuple(tuple_expression),
|
||||
value: Expression::Call(call),
|
||||
span: Default::default(),
|
||||
})),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
// Otherwise, reconstruct the assignment as is.
|
||||
type_ => {
|
||||
// If the function returns a struct, add it to `self.structs`.
|
||||
if let Type::Identifier(struct_name) = type_ {
|
||||
self.structs.insert(lhs_identifier.name, struct_name.name);
|
||||
};
|
||||
(
|
||||
Statement::Assign(Box::new(AssignStatement {
|
||||
place: Expression::Identifier(lhs_identifier),
|
||||
value: Expression::Call(call),
|
||||
span: Default::default(),
|
||||
})),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
(Expression::Identifier(identifier), expression) => {
|
||||
self.update_structs(&identifier, &expression);
|
||||
(
|
||||
self.assigner.simple_assign_statement(identifier, expression),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
// If the lhs is a tuple and the rhs is a function call, then return the reconstructed statement.
|
||||
(Expression::Tuple(tuple), Expression::Call(call)) => {
|
||||
// Retrieve the entry in the symbol table for the function call.
|
||||
// Note that this unwrap is safe since type checking ensures that the function exists.
|
||||
let function_name = match call.function.borrow() {
|
||||
Expression::Identifier(rhs_identifier) => rhs_identifier.name,
|
||||
_ => unreachable!("Parsing guarantees that `function` is an identifier."),
|
||||
};
|
||||
|
||||
let function = self.symbol_table.borrow().functions.get(&function_name).unwrap();
|
||||
|
||||
let output_type = match &function.output_type {
|
||||
Type::Tuple(tuple) => tuple.clone(),
|
||||
_ => unreachable!("Type checking guarantees that the output type is a tuple."),
|
||||
};
|
||||
|
||||
tuple
|
||||
.elements
|
||||
.iter()
|
||||
.zip_eq(output_type.0.iter())
|
||||
.for_each(|(identifier, type_)| {
|
||||
let identifier = match identifier {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!(
|
||||
"Type checking guarantees that a tuple element on the lhs is an identifier."
|
||||
),
|
||||
};
|
||||
// If the output type is a struct, add it to `self.structs`.
|
||||
if let Type::Identifier(struct_name) = type_ {
|
||||
self.structs.insert(identifier.name, struct_name.name);
|
||||
}
|
||||
});
|
||||
|
||||
(
|
||||
Statement::Assign(Box::new(AssignStatement {
|
||||
place: Expression::Tuple(tuple),
|
||||
value: Expression::Call(call),
|
||||
span: Default::default(),
|
||||
})),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
// If the lhs is a tuple and the rhs is a tuple, create a new assign statement for each tuple element.
|
||||
(Expression::Tuple(lhs_tuple), Expression::Tuple(rhs_tuple)) => {
|
||||
statements.extend(lhs_tuple.elements.into_iter().zip(rhs_tuple.elements.into_iter()).map(
|
||||
|(lhs, rhs)| {
|
||||
let identifier = match &lhs {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("Type checking guarantees that `lhs` is an identifier."),
|
||||
};
|
||||
self.update_structs(identifier, &rhs);
|
||||
Statement::Assign(Box::new(AssignStatement {
|
||||
place: lhs,
|
||||
value: rhs,
|
||||
span: Default::default(),
|
||||
}))
|
||||
},
|
||||
));
|
||||
(Statement::dummy(Default::default()), statements)
|
||||
}
|
||||
// If the lhs is a tuple and the rhs is an identifier that is a tuple, create a new assign statement for each tuple element.
|
||||
(Expression::Tuple(lhs_tuple), Expression::Identifier(identifier))
|
||||
if self.tuples.contains_key(&identifier.name) =>
|
||||
{
|
||||
// Lookup the entry in `self.tuples`.
|
||||
// Note that the `unwrap` is safe since the match arm checks that the entry exists.
|
||||
let rhs_tuple = self.tuples.get(&identifier.name).unwrap().clone();
|
||||
// Create a new assign statement for each tuple element.
|
||||
for (lhs, rhs) in lhs_tuple.elements.into_iter().zip(rhs_tuple.elements.into_iter()) {
|
||||
let identifier = match &lhs {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("Type checking guarantees that `lhs` is an identifier."),
|
||||
};
|
||||
self.update_structs(identifier, &rhs);
|
||||
|
||||
statements.push(Statement::Assign(Box::new(AssignStatement {
|
||||
place: lhs,
|
||||
value: rhs,
|
||||
span: Default::default(),
|
||||
})));
|
||||
}
|
||||
(Statement::dummy(Default::default()), statements)
|
||||
}
|
||||
// If the lhs of an assignment is a tuple, then the rhs can be one of the following:
|
||||
// - A function call that produces a tuple. (handled above)
|
||||
// - A tuple. (handled above)
|
||||
// - An identifier that is a tuple. (handled above)
|
||||
// - A ternary expression that produces a tuple. (handled when the rhs is flattened above)
|
||||
(Expression::Tuple(_), _) => {
|
||||
unreachable!("`Type checking guarantees that the rhs of an assignment to a tuple is a tuple.`")
|
||||
}
|
||||
_ => unreachable!("`AssignStatement`s can only have `Identifier`s or `Tuple`s on the left hand side."),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we want to flatten nested blocks? They do not affect code generation but it would regularize the AST structure.
|
||||
@ -214,6 +376,7 @@ impl StatementReconstructor for Flattener<'_> {
|
||||
// For each finalize argument, add it and its associated guard to the appropriate list of finalize arguments.
|
||||
// Note that type checking guarantees that the number of arguments in a finalize statement is equal to the number of arguments in to the finalize block.
|
||||
for (i, argument) in input.arguments.into_iter().enumerate() {
|
||||
// Note that the argument is not reconstructed.
|
||||
// Note that this unwrap is safe since we initialize `self.finalizes` with a number of vectors equal to the number of finalize arguments.
|
||||
self.finalizes.get_mut(i).unwrap().push((guard.clone(), argument));
|
||||
}
|
||||
@ -232,8 +395,18 @@ impl StatementReconstructor for Flattener<'_> {
|
||||
// Construct the associated guard.
|
||||
let guard = self.construct_guard();
|
||||
|
||||
// Add it to the list of return statements.
|
||||
self.returns.push((guard, input.expression));
|
||||
// Add it to `self.returns`.
|
||||
// Note that SSA guarantees that `input.expression` is either a literal or identifier.
|
||||
match input.expression {
|
||||
// If the input is an identifier that maps to a tuple, add the corresponding tuple to `self.returns`
|
||||
Expression::Identifier(identifier) if self.tuples.contains_key(&identifier.name) => {
|
||||
// Note that the `unwrap` is safe since the match arm checks that the entry exists in `self.tuples`.
|
||||
let tuple = self.tuples.get(&identifier.name).unwrap().clone();
|
||||
self.returns.push((guard, Expression::Tuple(tuple)))
|
||||
}
|
||||
// Otherwise, add the expression directly.
|
||||
_ => self.returns.push((guard, input.expression)),
|
||||
};
|
||||
|
||||
(Statement::dummy(Default::default()), Default::default())
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
use crate::{Assigner, SymbolTable};
|
||||
|
||||
use leo_ast::{
|
||||
AccessExpression, BinaryExpression, BinaryOperation, Expression, ExpressionReconstructor, Identifier, Member,
|
||||
Statement, TernaryExpression, Type,
|
||||
AccessExpression, BinaryExpression, BinaryOperation, Block, Expression, ExpressionReconstructor, Identifier,
|
||||
Member, ReturnStatement, Statement, TernaryExpression, TupleExpression, Type,
|
||||
};
|
||||
use leo_span::Symbol;
|
||||
|
||||
@ -26,7 +26,6 @@ use indexmap::IndexMap;
|
||||
|
||||
pub struct Flattener<'a> {
|
||||
/// The symbol table associated with the program.
|
||||
/// This table is used to lookup struct definitions, when they are folded.
|
||||
pub(crate) symbol_table: &'a SymbolTable,
|
||||
/// An struct used to construct (unique) assignment statements.
|
||||
pub(crate) assigner: Assigner,
|
||||
@ -44,6 +43,8 @@ pub struct Flattener<'a> {
|
||||
/// Note that finalizes are inserted in the order they are encountered during a pre-order traversal of the AST.
|
||||
/// Note that type checking guarantees that there is at most one finalize in a basic block.
|
||||
pub(crate) finalizes: Vec<Vec<(Option<Expression>, Expression)>>,
|
||||
/// A mapping between variables and flattened tuple expressions.
|
||||
pub(crate) tuples: IndexMap<Symbol, TupleExpression>,
|
||||
}
|
||||
|
||||
impl<'a> Flattener<'a> {
|
||||
@ -55,6 +56,7 @@ impl<'a> Flattener<'a> {
|
||||
condition_stack: Vec::new(),
|
||||
returns: Vec::new(),
|
||||
finalizes: Vec::new(),
|
||||
tuples: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,6 +70,24 @@ impl<'a> Flattener<'a> {
|
||||
core::mem::take(&mut self.finalizes)
|
||||
}
|
||||
|
||||
/// Constructs a guard from the current state of the condition stack.
|
||||
pub(crate) fn construct_guard(&mut self) -> Option<Expression> {
|
||||
match self.condition_stack.is_empty() {
|
||||
true => None,
|
||||
false => {
|
||||
let (first, rest) = self.condition_stack.split_first().unwrap();
|
||||
Some(rest.iter().cloned().fold(first.clone(), |acc, condition| {
|
||||
Expression::Binary(BinaryExpression {
|
||||
op: BinaryOperation::And,
|
||||
left: Box::new(acc),
|
||||
right: Box::new(condition),
|
||||
span: Default::default(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fold guards and expressions into a single expression.
|
||||
/// Note that this function assumes that at least one guard is present.
|
||||
pub(crate) fn fold_guards(
|
||||
@ -84,7 +104,7 @@ impl<'a> Flattener<'a> {
|
||||
// Helper to construct and store ternary assignments. e.g `$ret$0 = $var$0 ? $var$1 : $var$2`
|
||||
let mut construct_ternary_assignment = |guard: Expression, if_true: Expression, if_false: Expression| {
|
||||
let place = Identifier {
|
||||
name: self.assigner.unique_symbol(prefix),
|
||||
name: self.assigner.unique_symbol(prefix, "$"),
|
||||
span: Default::default(),
|
||||
};
|
||||
let (value, stmts) = self.reconstruct_ternary(TernaryExpression {
|
||||
@ -176,21 +196,21 @@ impl<'a> Flattener<'a> {
|
||||
self.assigner.simple_assign_statement(lhs, rhs)
|
||||
}
|
||||
|
||||
/// Constructs a conjunction of all the conditions in the stack.
|
||||
pub(crate) fn construct_guard(&self) -> Option<Expression> {
|
||||
match self.condition_stack.is_empty() {
|
||||
true => None,
|
||||
false => {
|
||||
let (first, rest) = self.condition_stack.split_first().unwrap();
|
||||
Some(rest.iter().cloned().fold(first.clone(), |acc, condition| {
|
||||
Expression::Binary(BinaryExpression {
|
||||
op: BinaryOperation::And,
|
||||
left: Box::new(acc),
|
||||
right: Box::new(condition),
|
||||
span: Default::default(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
/// Folds a list of return statements into a single return statement and adds the produced statements to the block.
|
||||
pub(crate) fn fold_returns(&mut self, block: &mut Block, returns: Vec<(Option<Expression>, Expression)>) {
|
||||
if !returns.is_empty() {
|
||||
let (expression, stmts) = self.fold_guards("ret$", returns);
|
||||
|
||||
// TODO: Flatten tuples in the return statements.
|
||||
|
||||
// Add all of the accumulated statements to the end of the block.
|
||||
block.statements.extend(stmts);
|
||||
|
||||
// Add the `ReturnStatement` to the end of the block.
|
||||
block.statements.push(Statement::Return(ReturnStatement {
|
||||
expression,
|
||||
span: Default::default(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,9 @@
|
||||
// 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 itertools::Itertools;
|
||||
use leo_ast::*;
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::unroller::Unroller;
|
||||
use crate::{VariableSymbol, VariableType};
|
||||
@ -50,15 +52,37 @@ impl StatementReconstructor for Unroller<'_> {
|
||||
VariableType::Mut
|
||||
};
|
||||
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
input.variable_name.name,
|
||||
VariableSymbol {
|
||||
type_: input.type_.clone(),
|
||||
span: input.span(),
|
||||
declaration,
|
||||
let insert_variable = |symbol: Symbol, type_: Type, span: Span, declaration: VariableType| {
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
symbol,
|
||||
VariableSymbol {
|
||||
type_,
|
||||
span,
|
||||
declaration,
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
};
|
||||
|
||||
// Insert the variables in the into the symbol table.
|
||||
match &input.place {
|
||||
Expression::Identifier(identifier) => insert_variable(identifier.name, input.type_.clone(), identifier.span, declaration),
|
||||
Expression::Tuple(tuple_expression) => {
|
||||
let tuple_type = match input.type_ {
|
||||
Type::Tuple(ref tuple_type) => tuple_type,
|
||||
_ => unreachable!("Type checking guarantees that if the lhs is a tuple, its associated type is also a tuple.")
|
||||
};
|
||||
tuple_expression.elements.iter().zip_eq(tuple_type.0.iter()).for_each(|(expression, type_)| {
|
||||
let identifier = match expression {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("Type checking guarantees that if the lhs is a tuple, all of its elements are identifiers.")
|
||||
};
|
||||
insert_variable(identifier.name, type_.clone(), identifier.span, declaration)
|
||||
});
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
_ => unreachable!("Type checking guarantees that the lhs of a `DefinitionStatement` is either an identifier or tuple.")
|
||||
|
||||
}
|
||||
}
|
||||
(Statement::Definition(input), Default::default())
|
||||
|
@ -191,7 +191,7 @@ impl<'a> Unroller<'a> {
|
||||
type_: input.type_.clone(),
|
||||
value: Expression::Literal(value),
|
||||
span: Default::default(),
|
||||
variable_name: input.variable,
|
||||
place: Expression::Identifier(input.variable),
|
||||
})
|
||||
.0,
|
||||
];
|
||||
|
@ -27,9 +27,9 @@ pub struct Assigner {
|
||||
|
||||
impl Assigner {
|
||||
/// Return a new unique `Symbol` from a `&str`.
|
||||
pub(crate) fn unique_symbol(&mut self, arg: impl Display) -> Symbol {
|
||||
pub(crate) fn unique_symbol(&mut self, arg: impl Display, separator: impl Display) -> Symbol {
|
||||
self.counter += 1;
|
||||
Symbol::intern(&format!("{arg}${}", self.counter - 1))
|
||||
Symbol::intern(&format!("{}{}{}", arg, separator, self.counter - 1))
|
||||
}
|
||||
|
||||
/// Constructs the assignment statement `place = expr;`.
|
||||
@ -46,7 +46,7 @@ impl Assigner {
|
||||
/// For example, `expr` is transformed into `$var$0 = expr;`.
|
||||
pub(crate) fn unique_simple_assign_statement(&mut self, expr: Expression) -> (Identifier, Statement) {
|
||||
// Create a new variable for the expression.
|
||||
let name = self.unique_symbol("$var");
|
||||
let name = self.unique_symbol("$var", "$");
|
||||
|
||||
let place = Identifier {
|
||||
name,
|
||||
|
@ -15,16 +15,17 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::StaticSingleAssigner;
|
||||
use indexmap::IndexMap;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use leo_ast::{
|
||||
AccessExpression, AssociatedFunction, BinaryExpression, CallExpression, Expression, ExpressionConsumer, Identifier,
|
||||
Literal, MemberAccess, Statement, Struct, StructExpression, StructVariableInitializer, TernaryExpression,
|
||||
TupleAccess, TupleExpression, UnaryExpression,
|
||||
TupleAccess, TupleExpression, UnaryExpression, UnitExpression,
|
||||
};
|
||||
use leo_span::{sym, Symbol};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
impl ExpressionConsumer for StaticSingleAssigner<'_> {
|
||||
type Output = (Expression, Vec<Statement>);
|
||||
|
||||
@ -225,7 +226,7 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
|
||||
let name = match self.is_lhs {
|
||||
// If consuming the left-hand side of a definition or assignment, a new unique name is introduced.
|
||||
true => {
|
||||
let new_name = self.assigner.unique_symbol(identifier.name);
|
||||
let new_name = self.assigner.unique_symbol(identifier.name, "$");
|
||||
self.rename_table.update(identifier.name, new_name);
|
||||
new_name
|
||||
}
|
||||
@ -292,15 +293,16 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Note that we do not construct a new assignment statement for the tuple expression.
|
||||
// This is because tuple expressions are restricted to use in a return statement.
|
||||
(
|
||||
Expression::Tuple(TupleExpression {
|
||||
// Construct and accumulate a new assignment statement for the tuple expression.
|
||||
let (place, statement) = self
|
||||
.assigner
|
||||
.unique_simple_assign_statement(Expression::Tuple(TupleExpression {
|
||||
elements,
|
||||
span: input.span,
|
||||
}),
|
||||
statements,
|
||||
)
|
||||
}));
|
||||
statements.push(statement);
|
||||
|
||||
(Expression::Identifier(place), statements)
|
||||
}
|
||||
|
||||
/// Consumes a unary expression, accumulating any statements that are generated.
|
||||
@ -320,4 +322,8 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
|
||||
|
||||
(Expression::Identifier(place), statements)
|
||||
}
|
||||
|
||||
fn consume_unit(&mut self, input: UnitExpression) -> Self::Output {
|
||||
(Expression::Unit(input), Default::default())
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,10 @@
|
||||
use crate::{RenameTable, StaticSingleAssigner};
|
||||
|
||||
use leo_ast::{
|
||||
AssignStatement, Block, ConditionalStatement, ConsoleFunction, ConsoleStatement, DecrementStatement,
|
||||
DefinitionStatement, Expression, ExpressionConsumer, FinalizeStatement, Identifier, IncrementStatement,
|
||||
IterationStatement, ReturnStatement, Statement, StatementConsumer, TernaryExpression,
|
||||
AssignStatement, Block, CallExpression, ConditionalStatement, ConsoleFunction, ConsoleStatement,
|
||||
DecrementStatement, DefinitionStatement, Expression, ExpressionConsumer, ExpressionStatement, FinalizeStatement,
|
||||
Identifier, IncrementStatement, IterationStatement, ReturnStatement, Statement, StatementConsumer,
|
||||
TernaryExpression, TupleExpression,
|
||||
};
|
||||
use leo_span::Symbol;
|
||||
|
||||
@ -127,7 +128,7 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
|
||||
};
|
||||
|
||||
// Create a new name for the variable written to in the `ConditionalStatement`.
|
||||
let new_name = self.assigner.unique_symbol(symbol);
|
||||
let new_name = self.assigner.unique_symbol(symbol, "$");
|
||||
|
||||
let (value, stmts) = self.consume_ternary(TernaryExpression {
|
||||
condition: Box::new(condition.clone()),
|
||||
@ -223,13 +224,75 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
|
||||
// Then assign a new unique name to the left-hand-side of the definition.
|
||||
// Note that this order is necessary to ensure that the right-hand-side uses the correct name when consuming a complex assignment.
|
||||
self.is_lhs = true;
|
||||
let identifier = match self.consume_identifier(definition.variable_name).0 {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("`self.consume_identifier` will always return an `Identifier`."),
|
||||
};
|
||||
match definition.place {
|
||||
Expression::Identifier(identifier) => {
|
||||
let identifier = match self.consume_identifier(identifier).0 {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("`self.consume_identifier` will always return an `Identifier`."),
|
||||
};
|
||||
statements.push(self.assigner.simple_assign_statement(identifier, value));
|
||||
}
|
||||
Expression::Tuple(tuple) => {
|
||||
let elements = tuple.elements.into_iter().map(|element| {
|
||||
match element {
|
||||
Expression::Identifier(identifier) => {
|
||||
let identifier = match self.consume_identifier(identifier).0 {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => unreachable!("`self.consume_identifier` will always return an `Identifier`."),
|
||||
};
|
||||
Expression::Identifier(identifier)
|
||||
}
|
||||
_ => unreachable!("Type checking guarantees that the tuple elements on the lhs of a `DefinitionStatement` are always be identifiers."),
|
||||
}
|
||||
}).collect();
|
||||
statements.push(Statement::Assign(Box::new(AssignStatement {
|
||||
place: Expression::Tuple(TupleExpression {
|
||||
elements,
|
||||
span: Default::default()
|
||||
}),
|
||||
value,
|
||||
span: Default::default()
|
||||
})));
|
||||
}
|
||||
_ => unreachable!("Type checking guarantees that the left-hand-side of a `DefinitionStatement` is an identifier or tuple."),
|
||||
}
|
||||
self.is_lhs = false;
|
||||
|
||||
statements.push(self.assigner.simple_assign_statement(identifier, value));
|
||||
statements
|
||||
}
|
||||
|
||||
/// Consumes the expressions associated with `ExpressionStatement`, returning the simplified `ExpressionStatement`.
|
||||
fn consume_expression_statement(&mut self, input: ExpressionStatement) -> Self::Output {
|
||||
let mut statements = Vec::new();
|
||||
|
||||
// Extract the call expression.
|
||||
let call = match input.expression {
|
||||
Expression::Call(call) => call,
|
||||
_ => unreachable!("Type checking guarantees that expression statements are always function calls."),
|
||||
};
|
||||
|
||||
// Process the arguments, accumulating any statements produced.
|
||||
let arguments = call
|
||||
.arguments
|
||||
.into_iter()
|
||||
.map(|argument| {
|
||||
let (argument, mut stmts) = self.consume_expression(argument);
|
||||
statements.append(&mut stmts);
|
||||
argument
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Create and accumulate the new expression statement.
|
||||
// Note that we do not create a new assignment for the call expression; this is necessary for correct code generation.
|
||||
statements.push(Statement::Expression(ExpressionStatement {
|
||||
expression: Expression::Call(CallExpression {
|
||||
function: call.function,
|
||||
arguments,
|
||||
external: call.external,
|
||||
span: call.span,
|
||||
}),
|
||||
span: input.span,
|
||||
}));
|
||||
|
||||
statements
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use leo_ast::{Mode, Type};
|
||||
use leo_span::Span;
|
||||
|
||||
/// An enumeration of the different types of variable type.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum VariableType {
|
||||
Const,
|
||||
Input(Mode),
|
||||
|
@ -627,11 +627,9 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
match input.elements.len() {
|
||||
0 => Some(self.assert_and_return_type(Type::Unit, expected, input.span())),
|
||||
1 => self.visit_expression(&input.elements[0], expected),
|
||||
0 | 1 => unreachable!("Parsing guarantees that tuple expressions have at least two elements."),
|
||||
_ => {
|
||||
// Check the expected tuple types if they are known.
|
||||
|
||||
if let Some(Type::Tuple(expected_types)) = expected {
|
||||
// Check actual length is equal to expected length.
|
||||
if expected_types.len() != input.elements.len() {
|
||||
@ -646,6 +644,10 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
.iter()
|
||||
.zip(input.elements.iter())
|
||||
.for_each(|(expected, expr)| {
|
||||
// Check that the component expression is not a tuple.
|
||||
if matches!(expr, Expression::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::nested_tuple_expression(expr.span()))
|
||||
}
|
||||
self.visit_expression(expr, &Some(expected.clone()));
|
||||
});
|
||||
|
||||
@ -706,4 +708,14 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_unit(&mut self, input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
// Unit expression are only allowed inside a return statement.
|
||||
if !self.is_return {
|
||||
self.emit_err(TypeCheckerError::unit_expression_only_in_return_statements(
|
||||
input.span(),
|
||||
));
|
||||
}
|
||||
Some(Type::Unit)
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,16 @@ use leo_span::sym;
|
||||
use std::collections::HashSet;
|
||||
|
||||
// TODO: Generally, cleanup tyc logic.
|
||||
// TODO: Cleanup logic for tuples.
|
||||
|
||||
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_struct(&mut self, input: &'a Struct) {
|
||||
// Check for conflicting struct/record member names.
|
||||
let mut used = HashSet::new();
|
||||
// TODO: Better span to target duplicate member.
|
||||
if !input.members.iter().all(|Member { identifier, type_ }| {
|
||||
// TODO: Better spans.
|
||||
// Check that the member types are valid.
|
||||
self.assert_type_is_valid(input.span, type_);
|
||||
// Check that the member types are defined.
|
||||
self.assert_type_is_defined(type_, identifier.span);
|
||||
used.insert(identifier.name)
|
||||
}) {
|
||||
self.emit_err(if input.is_record {
|
||||
@ -70,8 +71,13 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
for Member { identifier, type_ } in input.members.iter() {
|
||||
// Ensure there are no tuple typed members.
|
||||
self.assert_not_tuple(identifier.span, type_);
|
||||
// Check that the member type is not a tuple.
|
||||
if matches!(type_, Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_tuple(
|
||||
if input.is_record { "record" } else { "struct" },
|
||||
identifier.span,
|
||||
));
|
||||
}
|
||||
// Ensure that there are no record members.
|
||||
self.assert_member_is_not_record(identifier.span, input.identifier.name, type_);
|
||||
}
|
||||
@ -79,7 +85,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
fn visit_mapping(&mut self, input: &'a Mapping) {
|
||||
// Check that a mapping's key type is valid.
|
||||
self.assert_type_is_valid(input.span, &input.key_type);
|
||||
self.assert_type_is_defined(&input.key_type, input.span);
|
||||
// Check that a mapping's key type is not tuple types or mapping types.
|
||||
match input.key_type {
|
||||
Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "tuple", input.span)),
|
||||
@ -89,7 +95,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
// Check that a mapping's value type is valid.
|
||||
self.assert_type_is_valid(input.span, &input.value_type);
|
||||
self.assert_type_is_defined(&input.value_type, input.span);
|
||||
// Check that a mapping's value type is not tuple types or mapping types.
|
||||
match input.value_type {
|
||||
Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "tuple", input.span)),
|
||||
@ -103,6 +109,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
// Check that the function's annotations are valid.
|
||||
// Note that Leo does not natively support any specific annotations.
|
||||
for annotation in function.annotations.iter() {
|
||||
// TODO: Change to compiler warning.
|
||||
self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
|
||||
}
|
||||
|
||||
@ -134,9 +141,12 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
// Type check the function's parameters.
|
||||
function.input.iter().for_each(|input_var| {
|
||||
// Check that the type of input parameter is valid.
|
||||
self.assert_type_is_valid(input_var.span(), &input_var.type_());
|
||||
self.assert_not_tuple(input_var.span(), &input_var.type_());
|
||||
// Check that the type of input parameter is defined.
|
||||
self.assert_type_is_defined(&input_var.type_(), input_var.span());
|
||||
// Check that the type of the input parameter is not a tuple.
|
||||
if matches!(input_var.type_(), Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input_var.span()))
|
||||
}
|
||||
|
||||
match self.is_transition_function {
|
||||
// If the function is a transition function, then check that the parameter mode is not a constant.
|
||||
@ -164,14 +174,20 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
});
|
||||
|
||||
// Type check the function's return type.
|
||||
// Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
|
||||
function.output.iter().for_each(|output_type| {
|
||||
match output_type {
|
||||
// TODO: Verify that this is not needed when the import system is updated.
|
||||
Output::External(_) => {} // Do not type check external record function outputs.
|
||||
Output::Internal(output_type) => {
|
||||
// Check that the type of output is valid.
|
||||
self.assert_type_is_valid(output_type.span, &output_type.type_);
|
||||
|
||||
// Check that the type of output is defined.
|
||||
self.assert_type_is_defined(&output_type.type_, output_type.span);
|
||||
// Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
|
||||
if matches!(&output_type.type_, Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span))
|
||||
}
|
||||
// Check that the mode of the output is valid.
|
||||
// For functions, only public and private outputs are allowed
|
||||
if output_type.mode == Mode::Const {
|
||||
self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(output_type.span));
|
||||
}
|
||||
@ -181,9 +197,6 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
self.visit_block(&function.block);
|
||||
|
||||
// Check that the return type is valid.
|
||||
self.assert_type_is_valid(function.span, &function.output_type);
|
||||
|
||||
// If the function has a return type, then check that it has a return.
|
||||
if function.output_type != Type::Unit && !self.has_return {
|
||||
self.emit_err(TypeCheckerError::missing_return(function.span));
|
||||
@ -225,15 +238,16 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
let scope_index = self.create_child_scope();
|
||||
|
||||
finalize.input.iter().for_each(|input_var| {
|
||||
// Check that the type of input parameter is valid.
|
||||
self.assert_type_is_valid(input_var.span(), &input_var.type_());
|
||||
self.assert_not_tuple(input_var.span(), &input_var.type_());
|
||||
|
||||
// Check that the type of input parameter is defined.
|
||||
self.assert_type_is_defined(&input_var.type_(), input_var.span());
|
||||
// Check that the type of input parameter is not a tuple.
|
||||
if matches!(input_var.type_(), Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::finalize_cannot_take_tuple_as_input(input_var.span()))
|
||||
}
|
||||
// Check that the input parameter is not constant or private.
|
||||
if input_var.mode() == Mode::Const || input_var.mode() == Mode::Private {
|
||||
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(input_var.span()));
|
||||
}
|
||||
|
||||
// Check for conflicting variable names.
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
input_var.identifier().name,
|
||||
@ -248,17 +262,24 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
});
|
||||
|
||||
// Type check the function's return type.
|
||||
// Note that checking that each of the component types are defined is sufficient to guarantee that the `output_type` is defined.
|
||||
finalize.output.iter().for_each(|output_type| {
|
||||
// Check that the type of output is valid.
|
||||
self.assert_type_is_valid(output_type.span(), &output_type.type_());
|
||||
|
||||
// Check that the type of output is defined.
|
||||
self.assert_type_is_defined(&output_type.type_(), output_type.span());
|
||||
// Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
|
||||
if matches!(&output_type.type_(), Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span()))
|
||||
}
|
||||
// Check that the mode of the output is valid.
|
||||
if output_type.mode() == Mode::Const {
|
||||
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(output_type.span()));
|
||||
// Note that a finalize block can have only public outputs.
|
||||
if matches!(output_type.mode(), Mode::Const | Mode::Private) {
|
||||
self.emit_err(TypeCheckerError::finalize_output_mode_must_be_public(
|
||||
output_type.span(),
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Remove when this restriction is removed.
|
||||
// TODO: Remove if this restriction is relaxed at Aleo instructions level.
|
||||
// Check that the finalize block is not empty.
|
||||
if finalize.block.statements.is_empty() {
|
||||
self.emit_err(TypeCheckerError::finalize_block_must_not_be_empty(finalize.span));
|
||||
@ -267,8 +288,8 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
// Type check the finalize block.
|
||||
self.visit_block(&finalize.block);
|
||||
|
||||
// Check that the return type is valid.
|
||||
self.assert_type_is_valid(finalize.span, &finalize.output_type);
|
||||
// Check that the return type is defined. Note that the component types are already checked.
|
||||
self.assert_type_is_defined(&finalize.output_type, finalize.span);
|
||||
|
||||
// If the function has a return type, then check that it has a return.
|
||||
if finalize.output_type != Type::Unit && !self.has_return {
|
||||
|
@ -15,9 +15,11 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{TypeChecker, VariableSymbol, VariableType};
|
||||
use itertools::Itertools;
|
||||
|
||||
use leo_ast::*;
|
||||
use leo_errors::TypeCheckerError;
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_statement(&mut self, input: &'a Statement) {
|
||||
@ -34,6 +36,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
Statement::Console(stmt) => self.visit_console(stmt),
|
||||
Statement::Decrement(stmt) => self.visit_decrement(stmt),
|
||||
Statement::Definition(stmt) => self.visit_definition(stmt),
|
||||
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
|
||||
Statement::Finalize(stmt) => self.visit_finalize(stmt),
|
||||
Statement::Increment(stmt) => self.visit_increment(stmt),
|
||||
Statement::Iteration(stmt) => self.visit_iteration(stmt),
|
||||
@ -186,20 +189,87 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
VariableType::Mut
|
||||
};
|
||||
|
||||
// Check that the type of the definition is valid.
|
||||
self.assert_type_is_valid(input.span, &input.type_);
|
||||
// Check that the type of the definition is defined.
|
||||
self.assert_type_is_defined(&input.type_, input.span);
|
||||
|
||||
// Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type.
|
||||
match &input.type_ {
|
||||
// If the type is an empty tuple, return an error.
|
||||
Type::Unit => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.span)),
|
||||
// If the type is a singleton tuple, return an error.
|
||||
Type::Tuple(tuple) => match tuple.len() {
|
||||
0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
|
||||
_ => {
|
||||
if tuple.iter().any(|type_| matches!(type_, Type::Tuple(_))) {
|
||||
self.emit_err(TypeCheckerError::nested_tuple_type(input.span))
|
||||
}
|
||||
}
|
||||
},
|
||||
Type::Mapping(_) | Type::Err => unreachable!(),
|
||||
// Otherwise, the type is valid.
|
||||
_ => (), // Do nothing
|
||||
}
|
||||
|
||||
// Check the expression on the left-hand side.
|
||||
self.visit_expression(&input.value, &Some(input.type_.clone()));
|
||||
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
input.variable_name.name,
|
||||
VariableSymbol {
|
||||
type_: input.type_.clone(),
|
||||
span: input.span(),
|
||||
declaration,
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
// TODO: Dedup with unrolling pass.
|
||||
// Helper to insert the variables into the symbol table.
|
||||
let insert_variable = |symbol: Symbol, type_: Type, span: Span, declaration: VariableType| {
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
symbol,
|
||||
VariableSymbol {
|
||||
type_,
|
||||
span,
|
||||
declaration,
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
};
|
||||
|
||||
// Insert the variables in the into the symbol table.
|
||||
match &input.place {
|
||||
Expression::Identifier(identifier) => {
|
||||
insert_variable(identifier.name, input.type_.clone(), identifier.span, declaration)
|
||||
}
|
||||
Expression::Tuple(tuple_expression) => {
|
||||
let tuple_type = match &input.type_ {
|
||||
Type::Tuple(tuple_type) => tuple_type,
|
||||
_ => unreachable!(
|
||||
"Type checking guarantees that if the lhs is a tuple, its associated type is also a tuple."
|
||||
),
|
||||
};
|
||||
tuple_expression
|
||||
.elements
|
||||
.iter()
|
||||
.zip_eq(tuple_type.0.iter())
|
||||
.for_each(|(expression, type_)| {
|
||||
let identifier = match expression {
|
||||
Expression::Identifier(identifier) => identifier,
|
||||
_ => {
|
||||
return self.emit_err(TypeCheckerError::lhs_tuple_element_must_be_an_identifier(
|
||||
expression.span(),
|
||||
))
|
||||
}
|
||||
};
|
||||
insert_variable(identifier.name, type_.clone(), identifier.span, declaration)
|
||||
});
|
||||
}
|
||||
_ => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.place.span())),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) {
|
||||
// Expression statements can only be function calls.
|
||||
if !matches!(input.expression, Expression::Call(_)) {
|
||||
self.emit_err(TypeCheckerError::expression_statement_must_be_function_call(
|
||||
input.span(),
|
||||
));
|
||||
} else {
|
||||
// Check the expression.
|
||||
// TODO: Should the output type be restricted to unit types?
|
||||
self.visit_expression(&input.expression, &None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,6 +309,12 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
.iter()
|
||||
.zip(input.arguments.iter())
|
||||
.for_each(|(expected, argument)| {
|
||||
// Check that none of the arguments are tuple expressions.
|
||||
if matches!(argument, Expression::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::finalize_statement_cannot_contain_tuples(
|
||||
argument.span(),
|
||||
));
|
||||
}
|
||||
self.visit_expression(argument, &Some(expected.type_()));
|
||||
});
|
||||
}
|
||||
@ -351,8 +427,23 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
false => f.output_type.clone(),
|
||||
});
|
||||
|
||||
// Set the `has_return` flag.
|
||||
self.has_return = true;
|
||||
|
||||
// Check that the return expression is not a nested tuple.
|
||||
if let Expression::Tuple(TupleExpression { elements, .. }) = &input.expression {
|
||||
for element in elements {
|
||||
if matches!(element, Expression::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::nested_tuple_expression(element.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the `is_return` flag.
|
||||
self.is_return = true;
|
||||
// Type check the associated expression.
|
||||
self.visit_expression(&input.expression, return_type);
|
||||
// Unset the `is_return` flag.
|
||||
self.is_return = false;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) is_finalize: bool,
|
||||
/// Whether or not we are currently traversing an imported program.
|
||||
pub(crate) is_imported: bool,
|
||||
/// Whether or not we are currently traversing a return statement.
|
||||
pub(crate) is_return: bool,
|
||||
}
|
||||
|
||||
const BOOLEAN_TYPE: Type = Type::Boolean;
|
||||
@ -98,6 +100,7 @@ impl<'a> TypeChecker<'a> {
|
||||
has_finalize: false,
|
||||
is_finalize: false,
|
||||
is_imported: false,
|
||||
is_return: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,13 +358,6 @@ impl<'a> TypeChecker<'a> {
|
||||
Type::Identifier(struct_)
|
||||
}
|
||||
|
||||
/// Emits an error if the type is a tuple.
|
||||
pub(crate) fn assert_not_tuple(&self, span: Span, type_: &Type) {
|
||||
if matches!(type_, Type::Tuple(_)) {
|
||||
self.emit_err(TypeCheckerError::tuple_not_allowed(span))
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error if the struct member is a record type.
|
||||
pub(crate) fn assert_member_is_not_record(&self, span: Span, parent: Symbol, type_: &Type) {
|
||||
match type_ {
|
||||
@ -387,8 +383,8 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error if the type is not valid.
|
||||
pub(crate) fn assert_type_is_valid(&self, span: Span, type_: &Type) {
|
||||
/// Emits an error if the type or its constituent types are not defined.
|
||||
pub(crate) fn assert_type_is_defined(&self, type_: &Type, span: Span) {
|
||||
match type_ {
|
||||
// String types are temporarily disabled.
|
||||
Type::String => {
|
||||
@ -401,13 +397,13 @@ impl<'a> TypeChecker<'a> {
|
||||
// Check that the constituent types of the tuple are valid.
|
||||
Type::Tuple(tuple_type) => {
|
||||
for type_ in tuple_type.iter() {
|
||||
self.assert_type_is_valid(span, type_)
|
||||
self.assert_type_is_defined(type_, span)
|
||||
}
|
||||
}
|
||||
// Check that the constituent types of mapping are valid.
|
||||
Type::Mapping(mapping_type) => {
|
||||
self.assert_type_is_valid(span, &mapping_type.key);
|
||||
self.assert_type_is_valid(span, &mapping_type.value);
|
||||
self.assert_type_is_defined(&mapping_type.key, span);
|
||||
self.assert_type_is_defined(&mapping_type.value, span);
|
||||
}
|
||||
_ => {} // Do nothing.
|
||||
}
|
||||
|
@ -412,8 +412,7 @@ fn analyze_source_file(src: &str, source_file_start_pos: BytePos) -> (Vec<BytePo
|
||||
let src_bytes = src.as_bytes();
|
||||
|
||||
while i < src.len() {
|
||||
let i_usize = i;
|
||||
let byte = src_bytes[i_usize];
|
||||
let byte = src_bytes[i];
|
||||
|
||||
// How much to advance to get to the next UTF-8 char in the string.
|
||||
let mut char_len = 1;
|
||||
|
@ -74,22 +74,6 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to define an empty tuple.
|
||||
@formatted
|
||||
empty_tuple {
|
||||
args: (),
|
||||
msg: "Tuples of zero elements are not allowed.",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to define a tuple dimension of one.
|
||||
@formatted
|
||||
one_element_tuple {
|
||||
args: (),
|
||||
msg: "Tuples of one element are not allowed.",
|
||||
help: Some("Try defining a single type by removing the parenthesis `( )`".to_string()),
|
||||
}
|
||||
|
||||
/// For when a user shadows a function.
|
||||
@formatted
|
||||
shadowed_function {
|
||||
|
@ -199,14 +199,6 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// Parsed an expression statement.
|
||||
@formatted
|
||||
expr_stmts_disallowed {
|
||||
args: (),
|
||||
msg: "Expression statements are not supported.",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// Parsed an unknown method call on the type of an expression.
|
||||
@formatted
|
||||
invalid_method_call {
|
||||
@ -270,4 +262,11 @@ create_messages!(
|
||||
msg: "Invalid network identifier. The only supported identifier is `aleo`.",
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
tuple_must_have_at_least_two_elements {
|
||||
args: (kind: impl Display),
|
||||
msg: format!("A tuple {kind} must have at least two elements."),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
@ -17,6 +17,8 @@
|
||||
use crate::create_messages;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
// TODO: Consolidate errors.
|
||||
|
||||
create_messages!(
|
||||
/// InputError enum that represents all the errors for the inputs part of `leo-ast` crate.
|
||||
TypeCheckerError,
|
||||
@ -253,13 +255,6 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
tuple_not_allowed {
|
||||
args: (),
|
||||
msg: format!("Tuples are only allowed as function return types."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
unreachable_code_after_return {
|
||||
args: (),
|
||||
@ -317,6 +312,13 @@ create_messages!(
|
||||
help: Some("Add a `public` modifier to the input variable declaration or remove the visibility modifier entirely.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
finalize_output_mode_must_be_public {
|
||||
args: (),
|
||||
msg: format!("An output of a finalize block must be public."),
|
||||
help: Some("Add a `public` modifier to the output type declaration or remove the visibility modifier entirely.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
finalize_in_finalize {
|
||||
args: (),
|
||||
@ -444,4 +446,83 @@ create_messages!(
|
||||
msg: format!("An imported program cannot import another program."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
// TODO: Consider chainging this to a warning.
|
||||
|
||||
@formatted
|
||||
assign_unit_expression_to_variable {
|
||||
args: (),
|
||||
msg: format!("Cannot assign a unit expression to a variable."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
nested_tuple_type {
|
||||
args: (),
|
||||
msg: format!("A tuple type cannot contain a tuple."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
composite_data_type_cannot_contain_tuple {
|
||||
args: (data_type: impl Display),
|
||||
msg: format!("A {data_type} cannot contain a tuple."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
function_cannot_take_tuple_as_input {
|
||||
args: (),
|
||||
msg: format!("A function cannot take in a tuple as input."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
finalize_cannot_take_tuple_as_input {
|
||||
args: (),
|
||||
msg: format!("A finalize block cannot take in a tuple as input."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
nested_tuple_expression {
|
||||
args: (),
|
||||
msg: format!("A tuple expression cannot contain another tuple expression."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
finalize_statement_cannot_contain_tuples {
|
||||
args: (),
|
||||
msg: format!("A finalize statement cannot contain tuple expressions."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
expression_statement_must_be_function_call {
|
||||
args: (),
|
||||
msg: format!("An expression statement must be a function call."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
lhs_tuple_element_must_be_an_identifier {
|
||||
args: (),
|
||||
msg: format!("Tuples on the left-hand side of a `DefinitionStatement` can only contain identifiers."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
lhs_must_be_identifier_or_tuple {
|
||||
args: (),
|
||||
msg: format!("The left-hand side of a `DefinitionStatement` can only be an identifier or tuple. Note that a tuple must contain at least two elements."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
unit_expression_only_in_return_statements {
|
||||
args: (),
|
||||
msg: format!("Unit expressions can only be used in return statements."),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
44
tests/compiler/function/flatten_tuples_of_structs.leo
Normal file
44
tests/compiler/function/flatten_tuples_of_structs.leo
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
struct Extra {
|
||||
c: u8,
|
||||
}
|
||||
|
||||
struct Data {
|
||||
a: u8,
|
||||
b: u8,
|
||||
c: Extra,
|
||||
}
|
||||
|
||||
function foo(a: u8, b: u8) -> (u8, u8, Data) {
|
||||
let extra: Extra = Extra { c: a };
|
||||
let data: Data = Data { a: a, b: b, c: extra };
|
||||
if (a == b) {
|
||||
return (a, b, data);
|
||||
}
|
||||
let c: u8 = a + b;
|
||||
let d: u8 = a - b;
|
||||
|
||||
return (c, d, data);
|
||||
}
|
||||
|
||||
transition bar(flag1: bool, flag2: bool, a: u8, b: u8) -> (u8, u8, Data) {
|
||||
let start: (u8, u8, Data) = foo(a, b);
|
||||
if flag1 {
|
||||
start = foo(start.0, start.2.c.c);
|
||||
} else {
|
||||
|
||||
if flag2 {
|
||||
start = foo(start.1, start.2.b);
|
||||
} else {
|
||||
start = foo(start.2.a, start.1);
|
||||
}
|
||||
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
16
tests/compiler/statements/expr_statement.leo
Normal file
16
tests/compiler/statements/expr_statement.leo
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
|
||||
function foo(a: u8, b: u8) -> () {
|
||||
console.assert_eq(a, b);
|
||||
}
|
||||
|
||||
transition main(a: u8, b: u8) -> u8 {
|
||||
foo(a, b);
|
||||
return a + b;
|
||||
}
|
||||
}
|
26
tests/compiler/statements/expr_statement_fail.leo
Normal file
26
tests/compiler/statements/expr_statement_fail.leo
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
|
||||
struct Foo {
|
||||
a: u8,
|
||||
}
|
||||
|
||||
transition foo(flag: bool, a: u8, b: u8, foo: Foo, i: i8) -> u8 {
|
||||
a + b;
|
||||
flag ? a : b;
|
||||
foo.a;
|
||||
Foo {
|
||||
a: a,
|
||||
};
|
||||
a;
|
||||
1u8;
|
||||
-i8;
|
||||
();
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
@ -10,4 +10,5 @@ program test.aleo {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.-1); // Index `t.-1` is invalid.
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,5 @@ program test.aleo {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.2); // Index `t.2` is out of bounds.
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
20
tests/compiler/tuple/assign_unit_fail.leo
Normal file
20
tests/compiler/tuple/assign_unit_fail.leo
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
|
||||
transition foo(a: u8) -> u8 {
|
||||
let b: () = ();
|
||||
return a + a;
|
||||
}
|
||||
|
||||
transition baz(a: u8) -> u8 {
|
||||
let b: () = bar();
|
||||
return a + a;
|
||||
}
|
||||
|
||||
transition bar(a: u8) -> () {}
|
||||
}
|
||||
|
@ -6,8 +6,9 @@ input_file:
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
transition main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, bool) = (a, 1u64); // We should be declaring to a boolean, not a u64.
|
||||
|
||||
return (t.0, t.1);
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
26
tests/compiler/tuple/function_call_returns_tuple.leo
Normal file
26
tests/compiler/tuple/function_call_returns_tuple.leo
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function foo(a: u8, b: u8) -> (u8, u8) {
|
||||
if (a == b) {
|
||||
return (a, b);
|
||||
}
|
||||
let c: u8 = a + b;
|
||||
let d: u8 = a - b;
|
||||
return (c, d);
|
||||
}
|
||||
|
||||
transition bar(flag: bool, a: u8, b: u8) -> (u8, u8) {
|
||||
let start: (u8, u8) = foo(a, b);
|
||||
if flag {
|
||||
start = foo(start.0, start.1);
|
||||
} else {
|
||||
|
||||
start = foo(start.1, start.0);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
@ -13,4 +13,5 @@ program test.aleo {
|
||||
let c: u8 = a + b;
|
||||
let d: u8 = a - b;
|
||||
return (c, d);
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -8,4 +8,5 @@ input_file:
|
||||
program test.aleo {
|
||||
transition main(a: u8, b: u8) -> (u8, u8) {
|
||||
return (a + b, b + a);
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
12
tests/compiler/tuple/function_return_nothing.leo
Normal file
12
tests/compiler/tuple/function_return_nothing.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition main(a: bool, b: bool) -> () {
|
||||
return;
|
||||
}
|
||||
}
|
@ -6,6 +6,11 @@ input_file:
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function main(a: bool, b: bool) -> (bool) {
|
||||
transition main(a: bool, b: bool) -> (bool) {
|
||||
return (a);
|
||||
}}
|
||||
}
|
||||
|
||||
transition foo(a: bool, b: bool) -> (bool) {
|
||||
return (b,);
|
||||
}
|
||||
}
|
||||
|
12
tests/compiler/tuple/function_return_unit.leo
Normal file
12
tests/compiler/tuple/function_return_unit.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition main(a: bool, b: bool) -> () {
|
||||
return ();
|
||||
}
|
||||
}
|
10
tests/compiler/tuple/function_return_varying_modes.leo
Normal file
10
tests/compiler/tuple/function_return_varying_modes.leo
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition main(a: u8, b: u8) -> (public u8, u8) {
|
||||
return (a + b, b + a);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function main(a: bool, b: bool) -> () {
|
||||
return ();
|
||||
}}
|
17
tests/compiler/tuple/function_unit_input_fail.leo
Normal file
17
tests/compiler/tuple/function_unit_input_fail.leo
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function foo(a: ()) -> u8 {
|
||||
console.assert_eq(1u8, 2u8);
|
||||
return 3u8;
|
||||
}
|
||||
|
||||
transition bar(a: u8, b: u8) -> u8 {
|
||||
foo(());
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
@ -6,8 +6,9 @@ input_file:
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function main(a: bool, b: bool) -> (bool, u64) {
|
||||
transition main(a: bool, b: bool) -> (bool, u64) {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.1); // The second element should be type u64 as in the function declaration.
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
10
tests/compiler/tuple/return_with_different_modes.leo
Normal file
10
tests/compiler/tuple/return_with_different_modes.leo
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition main(a: u8, b: u8) -> (public u8, u8) {
|
||||
return (a + b, b + a);
|
||||
}
|
||||
}
|
15
tests/compiler/tuple/singleton_tuple_fail.leo
Normal file
15
tests/compiler/tuple/singleton_tuple_fail.leo
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
// TODO: Compilation should pass, but warnings should be emitted.
|
||||
|
||||
program test.aleo {
|
||||
transition foo(a: u8, b: u8) -> u8 {
|
||||
let c: (u8) = (a);
|
||||
let d: (u8) = (3u8 + 4u8);
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
14
tests/compiler/tuple/tuple_access.leo
Normal file
14
tests/compiler/tuple/tuple_access.leo
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
|
||||
program test.aleo {
|
||||
transition baz(foo: u8, bar: u8) -> u8 {
|
||||
let a: (u8, u8) = (foo, bar);
|
||||
let result: u8 = a.0 + a.1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
18
tests/compiler/tuple/tuple_destructure.leo
Normal file
18
tests/compiler/tuple/tuple_destructure.leo
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
|
||||
program test.aleo {
|
||||
function bax(baq: u8) -> (u8, u8) {
|
||||
return (baq + baq, baq * baq);
|
||||
}
|
||||
|
||||
transition baz(foo: u8, bar: u8) -> u8 {
|
||||
let (a, b): (u8, u8) = (foo, bar);
|
||||
let (c, d): (u8, u8) = bax(bar);
|
||||
let result: u8 = a + b + c + d;
|
||||
return result;
|
||||
}
|
||||
}
|
13
tests/compiler/tuple/tuple_in_assignment.leo
Normal file
13
tests/compiler/tuple/tuple_in_assignment.leo
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition baz(foo: u8, bar: u16) -> u8 {
|
||||
let a: (u8, u16) = (foo, bar);
|
||||
a = (3u8, 4u16);
|
||||
return 1u8 + 1u8;
|
||||
}
|
||||
}
|
||||
|
12
tests/compiler/tuple/tuple_in_definition.leo
Normal file
12
tests/compiler/tuple/tuple_in_definition.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition baz() -> u8 {
|
||||
let a: (u8, u16) = (1u8, 2u16);
|
||||
return 1u8 + 1u8;
|
||||
}
|
||||
}
|
||||
|
11
tests/compiler/tuple/tuple_in_function_param.leo
Normal file
11
tests/compiler/tuple/tuple_in_function_param.leo
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition foo(a: (u8, u16)) -> (u8, u16) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
17
tests/compiler/tuple/tuple_in_loop.leo
Normal file
17
tests/compiler/tuple/tuple_in_loop.leo
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition foo(a: u8, b: u8, flag: bool) -> u8 {
|
||||
let start: (u8, u8) = (a, b);
|
||||
for i: u8 in 0u8..16u8 {
|
||||
start = (start.0 + start.1, start.1 + 1u8);
|
||||
if flag {
|
||||
start = (start.1, start.0 + start.0);
|
||||
}
|
||||
}
|
||||
return start.0 + start.1;
|
||||
}
|
||||
}
|
13
tests/compiler/tuple/tuple_in_record_fail.leo
Normal file
13
tests/compiler/tuple/tuple_in_record_fail.leo
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
record Token {
|
||||
owner: address,
|
||||
gates: u64,
|
||||
amounts: (u64, u64),
|
||||
}
|
||||
}
|
||||
|
11
tests/compiler/tuple/tuple_in_return_type.leo
Normal file
11
tests/compiler/tuple/tuple_in_return_type.leo
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition bar(a: u8) -> (u8, (u8, u8)) {
|
||||
return (a, (a + a, a * a));
|
||||
}
|
||||
}
|
||||
|
15
tests/compiler/tuple/tuple_in_struct_fail.leo
Normal file
15
tests/compiler/tuple/tuple_in_struct_fail.leo
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
struct A {
|
||||
mem: (u8, u16)
|
||||
}
|
||||
|
||||
struct B {
|
||||
mems: (A, A)
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,9 @@ input_file:
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
transition main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, u64) = (a, b); // We should expect a boolean, not a u64.
|
||||
|
||||
return (t.0, t.1);
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
41
tests/compiler/tuple/unit.leo
Normal file
41
tests/compiler/tuple/unit.leo
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
|
||||
transition foo(a: u8, b: u8) -> () {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
return ();
|
||||
}
|
||||
|
||||
transition bar(a: u8, b: u8) -> () {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
return;
|
||||
}
|
||||
|
||||
transition baz(a: u8, b: u8) -> () {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
}
|
||||
|
||||
transition floo(a: u8, b: u8) {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
return ();
|
||||
}
|
||||
|
||||
transition blar(a: u8, b: u8) {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
return;
|
||||
}
|
||||
|
||||
transition blaz(a: u8, b: u8) {
|
||||
console.assert_eq(a, b);
|
||||
console.assert_eq(b, a);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 30ff54da2da7a73c10f6cc96ea951755d57840fe3bcd0c9d6c68b8ed6c4024e2
|
||||
initial_ast: a4bcf661e9661a1d9981c74efaca0886dd31270a9b1a505afd9a0353d3fbef86
|
||||
unrolled_ast: a4bcf661e9661a1d9981c74efaca0886dd31270a9b1a505afd9a0353d3fbef86
|
||||
initial_ast: 328cfc8f311133cb9f2622be2f93a1b624ff7f290dae03c0c4fedd6a139770ff
|
||||
unrolled_ast: 328cfc8f311133cb9f2622be2f93a1b624ff7f290dae03c0c4fedd6a139770ff
|
||||
ssa_ast: 798b6c449008ed6a38d603593dd3edf53aa30827e4ad2e0db6ef754999d1d807
|
||||
flattened_ast: 305593c39dc0c26ccccb1ed5f1e4fdb932af847cab04990449c0193bc7a2c20f
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 613969730f6ac4ff47e6975f79edf83ac2d5398d029657cbe28d53dd74847d1c
|
||||
initial_ast: b781ab4e896a31f33b4c80137639326117147b9499f3e6d086ac5d9c495a2ac0
|
||||
unrolled_ast: b781ab4e896a31f33b4c80137639326117147b9499f3e6d086ac5d9c495a2ac0
|
||||
initial_ast: 002375784372b4d6b83e0e181998cebd7e25dca957d1c935a08f9227d21ba373
|
||||
unrolled_ast: 002375784372b4d6b83e0e181998cebd7e25dca957d1c935a08f9227d21ba373
|
||||
ssa_ast: f128dc2ee3b1a636526c27b196e0b755b244cd9d8e52067541214b7909f38cf0
|
||||
flattened_ast: 1675206b4e0435049515729daa4468b6d4aab041812bf20758f74b79c40259aa
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 508ac917fe0d0779f2d43ae7695945dbe1fd00c457f08716dc51bbb2fe14e452
|
||||
initial_ast: f85497aee759dfa93d5e40be89ce95a3011523e2fa2ffa8c6ba23ffe50476fdc
|
||||
unrolled_ast: f85497aee759dfa93d5e40be89ce95a3011523e2fa2ffa8c6ba23ffe50476fdc
|
||||
initial_ast: f3e09111dcb009c66349bd98ad3ff8bebf753a184e2dafff711a521a43b3b2fc
|
||||
unrolled_ast: f3e09111dcb009c66349bd98ad3ff8bebf753a184e2dafff711a521a43b3b2fc
|
||||
ssa_ast: fda8333d6142536467e05fb5129198882eb028e6a2c0c6ed1d2339b9a716aba1
|
||||
flattened_ast: ab7783ad36c7540c555836b66e7c6b07f7681824dfcb58d5bbd3f0ea5fbf6bbd
|
||||
|
@ -5,7 +5,7 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 64247a73944a1639b17e3fd8ae0777b6725a754160afb476f9b0b6b8495d9884
|
||||
- initial_input_ast: 9546ede7c01cbe3a4cbedf2296fbc6605f657c2e1843e8f50ef683bc3eedd18a
|
||||
initial_ast: c21c646c2e7f7b776a057934e4893c2411259c7cd94061dd8006a0ed284ba669
|
||||
unrolled_ast: c21c646c2e7f7b776a057934e4893c2411259c7cd94061dd8006a0ed284ba669
|
||||
initial_ast: 1baa54d7c29ab84a48f3d52359d0a7c64a3929fd6c3975afe375d8c7c8420da7
|
||||
unrolled_ast: 1baa54d7c29ab84a48f3d52359d0a7c64a3929fd6c3975afe375d8c7c8420da7
|
||||
ssa_ast: 38d2140f8bc0308859260c927be943d2671ce80eb9ef4c22b42a4090ffab9728
|
||||
flattened_ast: a0e0a2c74ebd61346d568368f55cacaa7417070467925dbfc10754b5c1fa4437
|
||||
|
@ -7,7 +7,7 @@ outputs:
|
||||
- initial_input_ast: 0451346a1d2b8c41fd8d6e016a3fc18a61229489550227f58f359ff06332e7b7
|
||||
- initial_input_ast: 5ccafdeac9624b759f4fd6897adbec48d73986d63247fbbadbffa3cf84470674
|
||||
- initial_input_ast: ff196123ef62fc63cd552315d870c2407c085734c28fd440be7a1a0bb0dc114e
|
||||
initial_ast: 5c952bb0bbe8a3847db42d4e4f5f4d7aac86ad3cbc48f6899971d5b3dea7d8cb
|
||||
unrolled_ast: 5c952bb0bbe8a3847db42d4e4f5f4d7aac86ad3cbc48f6899971d5b3dea7d8cb
|
||||
initial_ast: 1c81e28b5e127045508de4847ae63f322bbe7099d259e517dca07468873a19e3
|
||||
unrolled_ast: 1c81e28b5e127045508de4847ae63f322bbe7099d259e517dca07468873a19e3
|
||||
ssa_ast: 8d96cba8107bd0d1a71cd355a9b1aa46f18b5ed45ee874315ef97e29e305bb2d
|
||||
flattened_ast: 4dce24b3f5f0df6010c894eda15c02dcef029a04bd0048b30ff70e6647b986d1
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: a30505e4422e13fcbf395f44b70bfd5fbe3a59c5328814405df5cfeaab639d55
|
||||
initial_ast: 81312b812ffd99514218a2b97a285a071355acd771dd73da553716d4a6088a24
|
||||
unrolled_ast: 81312b812ffd99514218a2b97a285a071355acd771dd73da553716d4a6088a24
|
||||
initial_ast: a7d914dc1bcd9c5db46a6c8eca1210a5fbe19634f5d753aceac23d498679d3d2
|
||||
unrolled_ast: a7d914dc1bcd9c5db46a6c8eca1210a5fbe19634f5d753aceac23d498679d3d2
|
||||
ssa_ast: 3bf4465fa7037bae8c4ddf07fd4a1a67e72558865b22fe4e1108a6d00d11fa75
|
||||
flattened_ast: efd6c65caf99fb00467b08626d3aaa8bc93186e8424fc5c23610ecf6a9c7dae2
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 9df63ce5d0366e8ba31fb07e696dc2e67f64371f629c66d3a9ddb715c923692e
|
||||
initial_ast: b8769ff525e6f258bb27fe13eb1c0828f5ceaee62f2bc0a5537dbd6e26dbf5a3
|
||||
unrolled_ast: b8769ff525e6f258bb27fe13eb1c0828f5ceaee62f2bc0a5537dbd6e26dbf5a3
|
||||
initial_ast: 69b992df47acf68e90bd8b613e60212d16172e8edeedb0f4b4b39353c38adc61
|
||||
unrolled_ast: 69b992df47acf68e90bd8b613e60212d16172e8edeedb0f4b4b39353c38adc61
|
||||
ssa_ast: 04ed79c5f4a1faf52032b353d8f8297a467d8e02ed447f7f81e393b3ddf24ed3
|
||||
flattened_ast: 0c95bcbb644f61776a20fb9b885b6cb48f9adb552192d7acf5a80670ccde21e0
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 81e7b4b48e21c631f656aa65b6d19ebb7d784b43229356b918f908a046734261
|
||||
initial_ast: 8e87f090c0609b94b233cfb06a5e04668522a1d64ee3df7690da3626dd7de722
|
||||
unrolled_ast: 8e87f090c0609b94b233cfb06a5e04668522a1d64ee3df7690da3626dd7de722
|
||||
initial_ast: 953d5e9d7689faeea239ad13c6653805e1a13281f3ac3f37dbea106449d23a5f
|
||||
unrolled_ast: 953d5e9d7689faeea239ad13c6653805e1a13281f3ac3f37dbea106449d23a5f
|
||||
ssa_ast: 232eaa57f15cacf6dc99d9a0599915b1adee632e5de070dfa6c5aa9e117e5d61
|
||||
flattened_ast: 0e223b52044c42ab29c340998ee76946a5ebcab27b7311c19b26b2072276b3c5
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 3cb982a5d4144e548fca897ceb686ad1f638971bb22fff7b935363eacc1b3473
|
||||
initial_ast: 0409d264a9e7132f14f312781b404b0a4ba7a9835af7145bd82e74e90f20dba7
|
||||
unrolled_ast: 0409d264a9e7132f14f312781b404b0a4ba7a9835af7145bd82e74e90f20dba7
|
||||
initial_ast: 4407c172fe97be9aa387a6fd94549386e803dfd7b8a83ca0936279b853fd1312
|
||||
unrolled_ast: 4407c172fe97be9aa387a6fd94549386e803dfd7b8a83ca0936279b853fd1312
|
||||
ssa_ast: 7801e83d9bc93fa26a769c94cc7a08b8676f761869da8e6ca4523e5d144cb5e6
|
||||
flattened_ast: 2bbafd8b601c9475cb180e254dabbf08a2d9da07c63cadd6b21252a38e4129c5
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 81e7b4b48e21c631f656aa65b6d19ebb7d784b43229356b918f908a046734261
|
||||
initial_ast: f6dd9e4cab9891cb96d73505558bb9294dcff1756ebee57fb6c44c3424bce63d
|
||||
unrolled_ast: f6dd9e4cab9891cb96d73505558bb9294dcff1756ebee57fb6c44c3424bce63d
|
||||
initial_ast: ad1967ac1c839fae18c5c7a46a3f1a038d7f6379662ce73b5ff81838e9fecb06
|
||||
unrolled_ast: ad1967ac1c839fae18c5c7a46a3f1a038d7f6379662ce73b5ff81838e9fecb06
|
||||
ssa_ast: 3d812d01adde60b0a3201ecea2ac6e3b8589ed5b9a00994522835a579c11af55
|
||||
flattened_ast: 2ad8be7ffefae31b19fbb3cddc9f7c3615225185b54d2c20e6456fe9d8502614
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 3cb982a5d4144e548fca897ceb686ad1f638971bb22fff7b935363eacc1b3473
|
||||
initial_ast: 302d16dc5e96221e8a683499eb9535d643ab076d99e0cd8a4b7eccff7f1d89b6
|
||||
unrolled_ast: 302d16dc5e96221e8a683499eb9535d643ab076d99e0cd8a4b7eccff7f1d89b6
|
||||
initial_ast: d2dc132a022976ed2e21401d332b4ea766426097eb1be7e33082473ade6e4d95
|
||||
unrolled_ast: d2dc132a022976ed2e21401d332b4ea766426097eb1be7e33082473ade6e4d95
|
||||
ssa_ast: fd34527ae5871a81df9dc16df2e5030f0195cffdf6dea4f78ed19aedea6da621
|
||||
flattened_ast: 151a5163d81bdd8d15ad4af804e3a8b6e8ed6e5c97fd7470a13c83b68f979d6c
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 81e7b4b48e21c631f656aa65b6d19ebb7d784b43229356b918f908a046734261
|
||||
initial_ast: c7837681390498ab152504151a8aca4b46618e4c035b9b265bc6937ef55224e8
|
||||
unrolled_ast: c7837681390498ab152504151a8aca4b46618e4c035b9b265bc6937ef55224e8
|
||||
initial_ast: b8c180b1cead8f5d3aa420e03dc135e2c82220c31e3d46cb31a3a3377d8322ab
|
||||
unrolled_ast: b8c180b1cead8f5d3aa420e03dc135e2c82220c31e3d46cb31a3a3377d8322ab
|
||||
ssa_ast: 70f05a3e659eb20d8e605e1c9b91338ee90c123f7453a240bf1a3950e5815042
|
||||
flattened_ast: d54cbd75ce1a0d7e6dd679659ccd4307f77bffc19f6234415225df9bcef09879
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 3cb982a5d4144e548fca897ceb686ad1f638971bb22fff7b935363eacc1b3473
|
||||
initial_ast: 1aabdddc327e544526ccdeba2f44080b544ee07f2374eca4fea4dad7ff6b54ad
|
||||
unrolled_ast: 1aabdddc327e544526ccdeba2f44080b544ee07f2374eca4fea4dad7ff6b54ad
|
||||
initial_ast: aaa2271be04607379f94fb121c50c8990d4a0b68ba5257220102db26b91a0f14
|
||||
unrolled_ast: aaa2271be04607379f94fb121c50c8990d4a0b68ba5257220102db26b91a0f14
|
||||
ssa_ast: de05aeb7675088006960519444a10897077b9080ebe1ce5e6e3f2439536101c5
|
||||
flattened_ast: ba2389349ba5155169389732da800d08def0aa26882c6a0a93e8fab257dc9a2b
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 46d3cef7b6dd6e951fe93d550206bdd658d6d435f71c776a39ae3b443770d33d
|
||||
initial_ast: 54cff476c6e46b52a00015597c70c32f23cecae6e3086d167c26ef26820f6577
|
||||
unrolled_ast: 54cff476c6e46b52a00015597c70c32f23cecae6e3086d167c26ef26820f6577
|
||||
initial_ast: cad5c306b9b28181bd6b0c6b2eed216219ebcb60b96554c11bdd241b226aaf73
|
||||
unrolled_ast: cad5c306b9b28181bd6b0c6b2eed216219ebcb60b96554c11bdd241b226aaf73
|
||||
ssa_ast: 1b2af30d0034ea32bd630884142157796f6c8f8f9e2ef7e9701ed62a2f92424b
|
||||
flattened_ast: c100fdd0403a9d8d6a38609d37f4e36ce54e3d6257db1d19d1e973274326906b
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 7155146c3f0887e6298bfabe9cad16d78c150419e8d0d584616d5dd76c5c3bac
|
||||
initial_ast: 8c9dfdb9055c528b1656ae95fc7763c79d3399127c49c22be15c716ad8b80b88
|
||||
unrolled_ast: 8c9dfdb9055c528b1656ae95fc7763c79d3399127c49c22be15c716ad8b80b88
|
||||
initial_ast: 9a4877e6514d54a55c8a76dbd4de9e27d43d137477c7d93470d45a61f6017861
|
||||
unrolled_ast: 9a4877e6514d54a55c8a76dbd4de9e27d43d137477c7d93470d45a61f6017861
|
||||
ssa_ast: 44237ce1986b38c34c5d2a624676e64c53257648436d82b9d333d6ab0c37102d
|
||||
flattened_ast: c5d401aa71f99eabd1db84264069cb3a904019b93282296020a4e2db537cbcba
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 591fe9942b59bad76b636a1c9e6ebe93ad85df562b09b7a900acfe12a9caffe2
|
||||
initial_ast: c1a7388455ac3e97ca3a063ad7812ff3ee27be822768d35a03ab608b1648c2d1
|
||||
unrolled_ast: c1a7388455ac3e97ca3a063ad7812ff3ee27be822768d35a03ab608b1648c2d1
|
||||
initial_ast: 3c9a4fde69b75a022863bb1f29026bc4fdac5eca0ad0ec5e3ecb7364e7a17499
|
||||
unrolled_ast: 3c9a4fde69b75a022863bb1f29026bc4fdac5eca0ad0ec5e3ecb7364e7a17499
|
||||
ssa_ast: 4f51f745379cb8078a6512104b27f778d6a36cd4bc92e6e06b74f95d8204ba37
|
||||
flattened_ast: 1fd5c458c8f61a818f6409f20e430c37d7a9d4a1aceae7a96b370fa9dca03c94
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 6b64b3a4fd7cafc2ead15efb8a91f8fc102947ccf4c091e4b6e54df82811fe82
|
||||
initial_ast: 784374ed8ef0e9feae88329064908c5dab22ee9c7f5828e09f4980ca862e372a
|
||||
unrolled_ast: 784374ed8ef0e9feae88329064908c5dab22ee9c7f5828e09f4980ca862e372a
|
||||
initial_ast: 2ef0225f6f5b08bec4cbac785f486c667251c285c2e3e221c63cd2d9d8c4d240
|
||||
unrolled_ast: 2ef0225f6f5b08bec4cbac785f486c667251c285c2e3e221c63cd2d9d8c4d240
|
||||
ssa_ast: 406dfc7b88282780532453da30e06d04fb6398fbb5f8934aa6951bc57e785af2
|
||||
flattened_ast: 0ab17f84c7bb560a48f49bce7e29384f3439028f2fcb55f93649fa7e615a66fa
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 1e9c68e82f6c0dc9eaa4babbc5cb9e46d79f8f0661607b48efd2e9870a636f33
|
||||
initial_ast: afc9c5673e33e40261e666fb353fcb5632f4b2fec015be8689d4e55efca47907
|
||||
unrolled_ast: afc9c5673e33e40261e666fb353fcb5632f4b2fec015be8689d4e55efca47907
|
||||
initial_ast: a5f32b136e224ace47e695dacb7d481975a343cdcd5b822652b8ce4bace9bdc4
|
||||
unrolled_ast: a5f32b136e224ace47e695dacb7d481975a343cdcd5b822652b8ce4bace9bdc4
|
||||
ssa_ast: cfbd02fec7cde8cb7de3cabe033207e0aa025d0c1eadf5b27f4aeff4b2f48c30
|
||||
flattened_ast: c86be4a932e4a91d25b8cca98ebadb1875d30a7409585b1cbeab3c7bf511e7fa
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 1e9c68e82f6c0dc9eaa4babbc5cb9e46d79f8f0661607b48efd2e9870a636f33
|
||||
initial_ast: 86b9e70b72058d64fb1461e72d9be08e9a9c776feae3233ae3aac7c947bd5726
|
||||
unrolled_ast: 86b9e70b72058d64fb1461e72d9be08e9a9c776feae3233ae3aac7c947bd5726
|
||||
initial_ast: 95f0769ebd6f1f6170771b5b4a2a8f333577f285531e64a3c2899e022d83b26c
|
||||
unrolled_ast: 95f0769ebd6f1f6170771b5b4a2a8f333577f285531e64a3c2899e022d83b26c
|
||||
ssa_ast: 535712b468cd7472f115e1a3a4edd8e8e57ab80afb8fbb5922fcf0e41af9c6ee
|
||||
flattened_ast: 3843c47a4d735398cbdda45f1815a14fce9e83dcab0cc318b1f11b5b21d95a39
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 1e9c68e82f6c0dc9eaa4babbc5cb9e46d79f8f0661607b48efd2e9870a636f33
|
||||
initial_ast: a2e29f76757bd9ca5ede2fbcb1383e3f6bddc809b870637db0e3e53f644de255
|
||||
unrolled_ast: a2e29f76757bd9ca5ede2fbcb1383e3f6bddc809b870637db0e3e53f644de255
|
||||
initial_ast: 08935ec63b16ea46fdc71ecf009d17664e1df123a7b8927933ecb8b6ebcc84d3
|
||||
unrolled_ast: 08935ec63b16ea46fdc71ecf009d17664e1df123a7b8927933ecb8b6ebcc84d3
|
||||
ssa_ast: 05f1c0703a0987f866b19bcbc72a1e1cf4d7253a1fc75b1474b9f49aafb26cc4
|
||||
flattened_ast: 699fdee0dcb831f86fb19c14b4f0387aec3ddfe4c6658a77e3cc7b450cc30e15
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 9dff7172de13bf9c5c1bf0e225ebb3132da11ea695a97692edacd36b18e5d86c
|
||||
initial_ast: 67395cdd81b7d95fe82ae4c021fb24f68cbf7b6c34f70210dba63e812611b7f2
|
||||
unrolled_ast: 67395cdd81b7d95fe82ae4c021fb24f68cbf7b6c34f70210dba63e812611b7f2
|
||||
initial_ast: efb41e70f83aa7e2d78fe401a2515f43840c2679c46dd8556315a736414c68d8
|
||||
unrolled_ast: efb41e70f83aa7e2d78fe401a2515f43840c2679c46dd8556315a736414c68d8
|
||||
ssa_ast: 03c6805324171292b0291c7578681fa9a4c69e06a5463693ffc12984806e0e29
|
||||
flattened_ast: 45786b6a26579552c3b7142eec3cd0dc87d7c703ad250b7811bfdd269fc3c073
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: a6d4afdd7375c43967b7a3be380ac83f7b1a351203a2f521ca8ce9824f29df71
|
||||
initial_ast: df6d46969b3d8046ff7ceb0c8395a5726d004b29bf7b3c22254938219809d7a7
|
||||
unrolled_ast: df6d46969b3d8046ff7ceb0c8395a5726d004b29bf7b3c22254938219809d7a7
|
||||
initial_ast: fae67b0524629123386d97abe3d416217bf3603fa7e80d7fff171188b7a9cd92
|
||||
unrolled_ast: fae67b0524629123386d97abe3d416217bf3603fa7e80d7fff171188b7a9cd92
|
||||
ssa_ast: 9f1ccb67dd1845e23cc51eaa7de1fa1de0ab2035d4a14ef6290f24e8b890511b
|
||||
flattened_ast: 2858a14218cb5f670950c60b32dae9c579fe73638553ea3eb56cae7073fc2039
|
||||
|
@ -4,7 +4,7 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 4e24333952c4eaea2c19106c9651e0bef29519e51632cc17f3ba1d07123306eb
|
||||
initial_ast: e4e85067d7ebcd9e8f9a075b1dbec886d9668637642b1aa15742497914633908
|
||||
unrolled_ast: e4e85067d7ebcd9e8f9a075b1dbec886d9668637642b1aa15742497914633908
|
||||
initial_ast: e545f85a38342de5173ef77a87c688a1ec6ad9964d48731c167925f68693c62e
|
||||
unrolled_ast: e545f85a38342de5173ef77a87c688a1ec6ad9964d48731c167925f68693c62e
|
||||
ssa_ast: 61769373206b7e2a87db43b9c6e35657749a373910584e137ceee4cf175ae9b6
|
||||
flattened_ast: af9344ccab440497931207afc1d7efca6f5f6591b00f468848fc6296bfa1dc89
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372036]: Cannot use a `finalize` statement without a `finalize` block.\n --> compiler-test:5:15\n |\n 5 | async finalize(a, b);\n | ^^^^^^^^^^^^^^\nError [ETYC0372044]: Function must contain a `finalize` statement on all execution paths.\n --> compiler-test:9:5\n |\n 9 | function bar(a: u8, b: u8) -> u8 {\n 10 | return a + b;\n 11 | }\n | ^\nError [ETYC0372032]: Only transition functions can have a `finalize` block.\n --> compiler-test:13:5\n |\n 13 | finalize bar(a: u8, b: u8) -> u8 {\n 14 | return a + b;\n 15 | }\n | ^\n |\n = Remove the `finalize` block or use the keyword `transition` instead of `function`.\nError [ETYC0372032]: Only transition functions can have a `finalize` block.\n --> compiler-test:22:5\n |\n 22 | finalize mint_public(receiver: address, amount: u64) {\n 23 | increment(account, receiver, amount);\n 24 | }\n | ^\n |\n = Remove the `finalize` block or use the keyword `transition` instead of `function`.\nError [ETYC0372005]: Unknown variable `account`\n --> compiler-test:23:19\n |\n 23 | increment(account, receiver, amount);\n | ^^^^^^^\nError [ETYC0372004]: Could not determine the type of `account`\n --> compiler-test:23:19\n |\n 23 | increment(account, receiver, amount);\n | ^^^^^^^\n"
|
||||
- "Error [ETYC0372036]: Cannot use a `finalize` statement without a `finalize` block.\n --> compiler-test:5:15\n |\n 5 | async finalize(a, b);\n | ^^^^^^^^^^^^^^\nError [ETYC0372044]: Function must contain a `finalize` statement on all execution paths.\n --> compiler-test:9:5\n |\n 9 | function bar(a: u8, b: u8) -> u8 {\n 10 | return a + b;\n 11 | }\n | ^\nError [ETYC0372031]: Only transition functions can have a `finalize` block.\n --> compiler-test:13:5\n |\n 13 | finalize bar(a: u8, b: u8) -> u8 {\n 14 | return a + b;\n 15 | }\n | ^\n |\n = Remove the `finalize` block or use the keyword `transition` instead of `function`.\nError [ETYC0372031]: Only transition functions can have a `finalize` block.\n --> compiler-test:22:5\n |\n 22 | finalize mint_public(receiver: address, amount: u64) {\n 23 | increment(account, receiver, amount);\n 24 | }\n | ^\n |\n = Remove the `finalize` block or use the keyword `transition` instead of `function`.\nError [ETYC0372005]: Unknown variable `account`\n --> compiler-test:23:19\n |\n 23 | increment(account, receiver, amount);\n | ^^^^^^^\nError [ETYC0372004]: Could not determine the type of `account`\n --> compiler-test:23:19\n |\n 23 | increment(account, receiver, amount);\n | ^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372033]: An input to a finalize block must be public.\n --> compiler-test:10:62\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n | ^^^^^^\n |\n = Add a `public` modifier to the input variable declaration or remove the visibility modifier entirely.\nError [ETYC0372033]: An input to a finalize block must be public.\n --> compiler-test:10:87\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n | ^^^\n |\n = Add a `public` modifier to the input variable declaration or remove the visibility modifier entirely.\nError [ETYC0372038]: Function must return a value.\n --> compiler-test:10:5\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n 11 | increment(account, receiver, amount);\n 12 | }\n | ^\n"
|
||||
- "Error [ETYC0372032]: An input to a finalize block must be public.\n --> compiler-test:10:62\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n | ^^^^^^\n |\n = Add a `public` modifier to the input variable declaration or remove the visibility modifier entirely.\nError [ETYC0372033]: An output of a finalize block must be public.\n --> compiler-test:10:87\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n | ^^^\n |\n = Add a `public` modifier to the output type declaration or remove the visibility modifier entirely.\nError [ETYC0372038]: Function must return a value.\n --> compiler-test:10:5\n |\n 10 | finalize mint_public (public receiver: address, constant amount: u64) -> constant u64 {\n 11 | increment(account, receiver, amount);\n 12 | }\n | ^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '('\n --> compiler-test:4:18\n |\n 4 | mapping foo: (u32, u32) => u32;\n | ^"
|
||||
- "Error [ETYC0372030]: A mapping's key cannot be a tuple\n --> compiler-test:4:5\n |\n 4 | mapping foo: (u32, u32) => u32;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nError [ETYC0372017]: The type `baz` is not found in the current scope.\n --> compiler-test:6:5\n |\n 6 | mapping floo: baz => u8;\n | ^^^^^^^^^^^^^^^^^^^^^^^^\nError [ETYC0372017]: The type `foo` is not found in the current scope.\n --> compiler-test:8:5\n |\n 8 | mapping bar: foo => baz;\n | ^^^^^^^^^^^^^^^^^^^^^^^^\nError [ETYC0372017]: The type `baz` is not found in the current scope.\n --> compiler-test:8:5\n |\n 8 | mapping bar: foo => baz;\n | ^^^^^^^^^^^^^^^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EAST0372009]: struct `bar` shadowed by\n --> compiler-test:5:5\n |\n 5 | mapping bar: u8 => u8;\n | ^^^^^^^^^^^^^^^^^^^^^^\nError [EAST0372009]: struct `bar` shadowed by\n --> compiler-test:7:5\n |\n 7 | transition bar(a: u8) -> u8 {\n 8 | return a + 1u8;\n 9 | }\n | ^\n"
|
||||
- "Error [EAST0372007]: struct `bar` shadowed by\n --> compiler-test:5:5\n |\n 5 | mapping bar: u8 => u8;\n | ^^^^^^^^^^^^^^^^^^^^^^\nError [EAST0372007]: struct `bar` shadowed by\n --> compiler-test:7:5\n |\n 7 | transition bar(a: u8) -> u8 {\n 8 | return a + 1u8;\n 9 | }\n | ^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372028]: Unknown annotation: `@test`.\n --> compiler-test:4:5\n |\n 4 | @test\n | ^^^^^\nError [ETYC0372028]: Unknown annotation: `@program`.\n --> compiler-test:9:5\n |\n 9 | @program\n | ^^^^^^^^\n"
|
||||
- "Error [ETYC0372027]: Unknown annotation: `@test`.\n --> compiler-test:4:5\n |\n 4 | @test\n | ^^^^^\nError [ETYC0372027]: Unknown annotation: `@program`.\n --> compiler-test:9:5\n |\n 9 | @program\n | ^^^^^^^^\n"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user