Merge pull request #2100 from AleoHQ/feat/tuple

Supports tuples.
This commit is contained in:
Collin Chin 2022-11-22 20:49:25 -05:00 committed by GitHub
commit 40e56f8613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
275 changed files with 2296 additions and 676 deletions

16
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,
];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

@ -10,4 +10,5 @@ program test.aleo {
let t: (bool, bool) = (a, b);
return (t.0, t.-1); // Index `t.-1` is invalid.
}}
}
}

View File

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

View 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) -> () {}
}

View File

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

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

View File

@ -13,4 +13,5 @@ program test.aleo {
let c: u8 = a + b;
let d: u8 = a - b;
return (c, d);
}}
}
}

View File

@ -8,4 +8,5 @@ input_file:
program test.aleo {
transition main(a: u8, b: u8) -> (u8, u8) {
return (a + b, b + a);
}}
}
}

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

View File

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

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

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

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Fail
input_file:
- inputs/bool_bool.in
*/
program test.aleo {
function main(a: bool, b: bool) -> () {
return ();
}}

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

View File

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

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

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

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

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

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

View File

@ -0,0 +1,12 @@
/*
namespace: Compile
expectation: Pass
*/
program test.aleo {
transition baz() -> u8 {
let a: (u8, u16) = (1u8, 2u16);
return 1u8 + 1u8;
}
}

View File

@ -0,0 +1,11 @@
/*
namespace: Compile
expectation: Fail
*/
program test.aleo {
transition foo(a: (u8, u16)) -> (u8, u16) {
return a;
}
}

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

View File

@ -0,0 +1,13 @@
/*
namespace: Compile
expectation: Fail
*/
program test.aleo {
record Token {
owner: address,
gates: u64,
amounts: (u64, u64),
}
}

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

View File

@ -0,0 +1,15 @@
/*
namespace: Compile
expectation: Fail
*/
program test.aleo {
struct A {
mem: (u8, u16)
}
struct B {
mems: (A, A)
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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