From 91b4cc31072678935d2470f967fabda6e1d30eb3 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 15:56:01 -0800 Subject: [PATCH 01/24] Reorganize access expressions --- compiler/ast/src/expressions/access.rs | 71 ------------------- .../access/associated_function_access.rs | 0 .../{ => expressions}/access/member_access.rs | 0 .../ast/src/{ => expressions}/access/mod.rs | 3 - .../{ => expressions}/access/tuple_access.rs | 0 5 files changed, 74 deletions(-) delete mode 100644 compiler/ast/src/expressions/access.rs rename compiler/ast/src/{ => expressions}/access/associated_function_access.rs (100%) rename compiler/ast/src/{ => expressions}/access/member_access.rs (100%) rename compiler/ast/src/{ => expressions}/access/mod.rs (92%) rename compiler/ast/src/{ => expressions}/access/tuple_access.rs (100%) diff --git a/compiler/ast/src/expressions/access.rs b/compiler/ast/src/expressions/access.rs deleted file mode 100644 index d63f227170..0000000000 --- a/compiler/ast/src/expressions/access.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2019-2023 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 . - -use crate::{access::*, Node}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// An access expressions, extracting a smaller part out of a whole. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum AccessExpression { - // /// An `array[index]` expression. - // Array(ArrayAccess), - // /// An expression accessing a range of an array. - // ArrayRange(ArrayRangeAccess), - /// Access to an associated variable of a struct e.g `u8::MAX`. - AssociatedConstant(AssociatedConstant), - /// Access to an associated function of a struct e.g `Pedersen64::hash()`. - AssociatedFunction(AssociatedFunction), - /// An expression accessing a field in a structure, e.g., `struct_var.field`. - Member(MemberAccess), - /// Access to a tuple field using its position, e.g., `tuple.1`. - Tuple(TupleAccess), -} - -impl Node for AccessExpression { - fn span(&self) -> Span { - match self { - AccessExpression::AssociatedConstant(n) => n.span(), - AccessExpression::AssociatedFunction(n) => n.span(), - AccessExpression::Member(n) => n.span(), - AccessExpression::Tuple(n) => n.span(), - } - } - - fn set_span(&mut self, span: Span) { - match self { - AccessExpression::AssociatedConstant(n) => n.set_span(span), - AccessExpression::AssociatedFunction(n) => n.set_span(span), - AccessExpression::Member(n) => n.set_span(span), - AccessExpression::Tuple(n) => n.set_span(span), - } - } -} - -impl fmt::Display for AccessExpression { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use AccessExpression::*; - - match self { - AssociatedConstant(access) => access.fmt(f), - AssociatedFunction(access) => access.fmt(f), - Member(access) => access.fmt(f), - Tuple(access) => access.fmt(f), - } - } -} diff --git a/compiler/ast/src/access/associated_function_access.rs b/compiler/ast/src/expressions/access/associated_function_access.rs similarity index 100% rename from compiler/ast/src/access/associated_function_access.rs rename to compiler/ast/src/expressions/access/associated_function_access.rs diff --git a/compiler/ast/src/access/member_access.rs b/compiler/ast/src/expressions/access/member_access.rs similarity index 100% rename from compiler/ast/src/access/member_access.rs rename to compiler/ast/src/expressions/access/member_access.rs diff --git a/compiler/ast/src/access/mod.rs b/compiler/ast/src/expressions/access/mod.rs similarity index 92% rename from compiler/ast/src/access/mod.rs rename to compiler/ast/src/expressions/access/mod.rs index 62dfb44f47..03cee084ce 100644 --- a/compiler/ast/src/access/mod.rs +++ b/compiler/ast/src/expressions/access/mod.rs @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -mod associated_constant_access; -pub use associated_constant_access::*; - mod associated_function_access; pub use associated_function_access::*; diff --git a/compiler/ast/src/access/tuple_access.rs b/compiler/ast/src/expressions/access/tuple_access.rs similarity index 100% rename from compiler/ast/src/access/tuple_access.rs rename to compiler/ast/src/expressions/access/tuple_access.rs From 506f59d65a26806ef51b3f6f2ab694d1c2ee1a45 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 15:58:06 -0800 Subject: [PATCH 02/24] Remove constant access expression --- .../src/access/associated_constant_access.rs | 40 ------------- compiler/ast/src/expressions/access/mod.rs | 58 ++++++++++++++++++- compiler/ast/src/expressions/mod.rs | 12 ++-- compiler/ast/src/lib.rs | 3 - 4 files changed, 61 insertions(+), 52 deletions(-) delete mode 100644 compiler/ast/src/access/associated_constant_access.rs diff --git a/compiler/ast/src/access/associated_constant_access.rs b/compiler/ast/src/access/associated_constant_access.rs deleted file mode 100644 index f0dba58799..0000000000 --- a/compiler/ast/src/access/associated_constant_access.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2023 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 . - -use crate::{Identifier, Node, Type}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// An access expression to an struct constant., e.g. `u8::MAX`. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssociatedConstant { - /// The inner struct type. - pub ty: Type, - /// The struct constant that is being accessed. - pub name: Identifier, - /// The span for the entire expression `Foo::bar()`. - pub span: Span, -} - -impl fmt::Display for AssociatedConstant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}::{}", self.ty, self.name) - } -} - -crate::simple_node_impl!(AssociatedConstant); diff --git a/compiler/ast/src/expressions/access/mod.rs b/compiler/ast/src/expressions/access/mod.rs index 03cee084ce..5d5427aba8 100644 --- a/compiler/ast/src/expressions/access/mod.rs +++ b/compiler/ast/src/expressions/access/mod.rs @@ -14,11 +14,63 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -mod associated_function_access; +pub mod associated_function_access; pub use associated_function_access::*; -mod member_access; +pub mod member_access; pub use member_access::*; -mod tuple_access; +pub mod tuple_access; pub use tuple_access::*; + +use crate::{Node}; + +use leo_span::Span; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// An access expressions, extracting a smaller part out of a whole. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum AccessExpression { + // /// An `array[index]` expression. + // Array(ArrayAccess), + // /// An expression accessing a range of an array. + // ArrayRange(ArrayRangeAccess), + /// Access to an associated function of a struct e.g `Pedersen64::hash()`. + AssociatedFunction(AssociatedFunction), + /// An expression accessing a field in a structure, e.g., `struct_var.field`. + Member(MemberAccess), + /// Access to a tuple field using its position, e.g., `tuple.1`. + Tuple(TupleAccess), +} + +impl Node for AccessExpression { + fn span(&self) -> Span { + match self { + AccessExpression::AssociatedFunction(n) => n.span(), + AccessExpression::Member(n) => n.span(), + AccessExpression::Tuple(n) => n.span(), + } + } + + fn set_span(&mut self, span: Span) { + match self { + AccessExpression::AssociatedFunction(n) => n.set_span(span), + AccessExpression::Member(n) => n.set_span(span), + AccessExpression::Tuple(n) => n.set_span(span), + } + } +} + +impl fmt::Display for AccessExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use AccessExpression::*; + + match self { + AssociatedFunction(access) => access.fmt(f), + Member(access) => access.fmt(f), + Tuple(access) => access.fmt(f), + } + } +} diff --git a/compiler/ast/src/expressions/mod.rs b/compiler/ast/src/expressions/mod.rs index 0627d161f0..e1cdf6060b 100644 --- a/compiler/ast/src/expressions/mod.rs +++ b/compiler/ast/src/expressions/mod.rs @@ -14,12 +14,6 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{Identifier, Node}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - mod access; pub use access::*; @@ -50,6 +44,12 @@ pub use unit::*; mod literal; pub use literal::*; +use crate::{Identifier, Node}; +use leo_span::Span; + +use serde::{Deserialize, Serialize}; +use std::fmt; + /// Expression that evaluates to a value. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Expression { diff --git a/compiler/ast/src/lib.rs b/compiler/ast/src/lib.rs index 0e5e71a7b2..a8c25e506c 100644 --- a/compiler/ast/src/lib.rs +++ b/compiler/ast/src/lib.rs @@ -24,9 +24,6 @@ #![doc = include_str!("../README.md")] extern crate core; -pub mod access; -pub use self::access::*; - pub mod r#struct; pub use self::r#struct::*; From 0cee89007dc2fc247ac70911dad39d85d2ddfae6 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:01:40 -0800 Subject: [PATCH 03/24] Update parser --- compiler/parser/src/parser/expression.rs | 17 +++++++---------- errors/src/errors/parser/parser_errors.rs | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index b8737aa90a..808ac45c74 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -319,25 +319,22 @@ impl ParserContext<'_> { let member_name = self.expect_identifier()?; // Check if there are arguments. - Ok(Expression::Access(if self.check(&Token::LeftParen) { + if self.check(&Token::LeftParen) { // Parse the arguments let (args, _, end) = self.parse_expr_tuple()?; // Return the struct function. - AccessExpression::AssociatedFunction(AssociatedFunction { + Ok(Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction { span: module_name.span() + end, ty: type_, name: member_name, args, - }) + }))) } else { - // Return the struct constant. - AccessExpression::AssociatedConstant(AssociatedConstant { - span: module_name.span() + member_name.span(), - ty: type_, - name: member_name, - }) - })) + // Attempted to parse an associated constant, e.g `Foo::MAX`. + // These are not supported in the Leo language. + Err(ParserError::invalid_associated_access(&module_name, module_name.span()).into()) + } } /// Parses a tuple of `Expression` AST nodes. diff --git a/errors/src/errors/parser/parser_errors.rs b/errors/src/errors/parser/parser_errors.rs index 2022c93145..f44c44a61d 100644 --- a/errors/src/errors/parser/parser_errors.rs +++ b/errors/src/errors/parser/parser_errors.rs @@ -210,7 +210,7 @@ create_messages!( @formatted invalid_associated_access { args: (name: impl Display), - msg: format!("Invalid associated access call to struct {name}."), + msg: format!("Invalid associated access to struct {name}."), help: Some("Double colon `::` syntax is only supported for core functions in Leo for testnet3.".to_string()), } From 1fd83113896d3d015d962d372408dc987d70ed4f Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:03:32 -0800 Subject: [PATCH 04/24] Update Visitor --- compiler/ast/src/passes/visitor.rs | 190 ++++++++++++++++++++--------- 1 file changed, 133 insertions(+), 57 deletions(-) diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 06cc2c0c47..390f65af2b 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -23,9 +23,13 @@ use crate::*; /// A Visitor trait for expressions in the AST. pub trait ExpressionVisitor<'a> { type AdditionalInput: Default; - type Output: Default; + type ExpressionOutput: Default; - fn visit_expression(&mut self, input: &'a Expression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_expression( + &mut self, + input: &'a Expression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match input { Expression::Access(access) => self.visit_access(access, additional), Expression::Binary(binary) => self.visit_binary(binary, additional), @@ -41,81 +45,124 @@ pub trait ExpressionVisitor<'a> { } } - fn visit_access(&mut self, input: &'a AccessExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_access( + &mut self, + input: &'a AccessExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match input { - AccessExpression::AssociatedFunction(function) => { - function.args.iter().for_each(|arg| { - self.visit_expression(arg, &Default::default()); - }); - } - AccessExpression::Member(member) => { - self.visit_expression(&member.inner, additional); - } - AccessExpression::Tuple(tuple) => { - self.visit_expression(&tuple.tuple, additional); - } - _ => {} - } + AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), + AccessExpression::Member(member) => self.visit_member_access(member, additional), + AccessExpression::Tuple(tuple) => self.visit_tuple_access(tuple, additional), + }; + Default::default() + } + fn visit_associated_function( + &mut self, + input: &'a AssociatedFunction, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + input.args.iter().for_each(|arg| { self.visit_expression(arg, additional); }); Default::default() } - fn visit_binary(&mut self, input: &'a BinaryExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_binary( + &mut self, + input: &'a BinaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.left, additional); self.visit_expression(&input.right, additional); Default::default() } - fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { input.arguments.iter().for_each(|expr| { self.visit_expression(expr, additional); }); Default::default() } - fn visit_struct_init(&mut self, _input: &'a StructExpression, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_struct_init( + &mut self, + _input: &'a StructExpression, + _additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { Default::default() } - fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { unreachable!("`ErrExpression`s should not be in the AST at this phase of compilation.") } - fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_identifier( + &mut self, + _input: &'a Identifier, + _additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { Default::default() } - fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { Default::default() } - fn visit_ternary(&mut self, input: &'a TernaryExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_member_access(&mut self, input: &'a MemberAccess, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + self.visit_expression(&input.inner, additional); + Default::default() + } + + fn visit_ternary( + &mut self, + input: &'a TernaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.condition, additional); self.visit_expression(&input.if_true, additional); self.visit_expression(&input.if_false, additional); Default::default() } - fn visit_tuple(&mut self, input: &'a TupleExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_tuple( + &mut self, + input: &'a TupleExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { input.elements.iter().for_each(|expr| { self.visit_expression(expr, additional); }); Default::default() } - fn visit_unary(&mut self, input: &'a UnaryExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_tuple_access(&mut self, input: &'a TupleAccess, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + self.visit_expression(&input.tuple, additional); + Default::default() + } + + fn visit_unary( + &mut self, + input: &'a UnaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.receiver, additional); Default::default() } - fn visit_unit(&mut self, _input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_unit( + &mut self, + _input: &'a UnitExpression, + _additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { Default::default() } } /// A Visitor trait for statements in the AST. pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { - fn visit_statement(&mut self, input: &'a Statement) { + type StatementOutput: Default; + + fn visit_statement(&mut self, input: &'a Statement) -> Self::StatementOutput { match input { Statement::Assert(stmt) => self.visit_assert(stmt), Statement::Assign(stmt) => self.visit_assign(stmt), @@ -131,7 +178,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { } } - fn visit_assert(&mut self, input: &'a AssertStatement) { + fn visit_assert(&mut self, input: &'a AssertStatement) -> Self::StatementOutput { match &input.variant { AssertVariant::Assert(expr) => self.visit_expression(expr, &Default::default()), AssertVariant::AssertEq(left, right) | AssertVariant::AssertNeq(left, right) => { @@ -139,25 +186,31 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { self.visit_expression(right, &Default::default()) } }; + Default::default() } - fn visit_assign(&mut self, input: &'a AssignStatement) { + fn visit_assign(&mut self, input: &'a AssignStatement) -> Self::StatementOutput { self.visit_expression(&input.value, &Default::default()); + Default::default() } - fn visit_block(&mut self, input: &'a Block) { - input.statements.iter().for_each(|stmt| self.visit_statement(stmt)); + fn visit_block(&mut self, input: &'a Block) -> Self::StatementOutput { + input.statements.iter().for_each(|stmt| { + self.visit_statement(stmt); + }); + Default::default() } - fn visit_conditional(&mut self, input: &'a ConditionalStatement) { + fn visit_conditional(&mut self, input: &'a ConditionalStatement) -> Self::StatementOutput { self.visit_expression(&input.condition, &Default::default()); self.visit_block(&input.then); if let Some(stmt) = input.otherwise.as_ref() { self.visit_statement(stmt); } + Default::default() } - fn visit_console(&mut self, input: &'a ConsoleStatement) { + fn visit_console(&mut self, input: &'a ConsoleStatement) -> Self::StatementOutput { match &input.function { ConsoleFunction::Assert(expr) => { self.visit_expression(expr, &Default::default()); @@ -171,78 +224,101 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { self.visit_expression(right, &Default::default()); } }; + Default::default() } - fn visit_decrement(&mut self, input: &'a DecrementStatement) { + fn visit_decrement(&mut self, input: &'a DecrementStatement) -> Self::StatementOutput { self.visit_expression(&input.amount, &Default::default()); self.visit_expression(&input.index, &Default::default()); self.visit_identifier(&input.mapping, &Default::default()); + Default::default() } - fn visit_definition(&mut self, input: &'a DefinitionStatement) { + fn visit_definition(&mut self, input: &'a DefinitionStatement) -> Self::StatementOutput { self.visit_expression(&input.value, &Default::default()); + Default::default() } - fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) { + fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> Self::StatementOutput { self.visit_expression(&input.expression, &Default::default()); + Default::default() } - fn visit_increment(&mut self, input: &'a IncrementStatement) { + fn visit_increment(&mut self, input: &'a IncrementStatement) -> Self::StatementOutput { self.visit_expression(&input.amount, &Default::default()); self.visit_expression(&input.index, &Default::default()); self.visit_identifier(&input.mapping, &Default::default()); + Default::default() } - fn visit_iteration(&mut self, input: &'a IterationStatement) { + fn visit_iteration(&mut self, input: &'a IterationStatement) -> Self::StatementOutput { self.visit_expression(&input.start, &Default::default()); self.visit_expression(&input.stop, &Default::default()); self.visit_block(&input.block); + Default::default() } - fn visit_return(&mut self, input: &'a ReturnStatement) { + fn visit_return(&mut self, input: &'a ReturnStatement) -> Self::StatementOutput { self.visit_expression(&input.expression, &Default::default()); if let Some(arguments) = &input.finalize_arguments { arguments.iter().for_each(|argument| { self.visit_expression(argument, &Default::default()); }) } + Default::default() } } /// A Visitor trait for the program represented by the AST. pub trait ProgramVisitor<'a>: StatementVisitor<'a> { - fn visit_program(&mut self, input: &'a Program) { - input.imports.values().for_each(|import| self.visit_import(&import.0)); + type ProgramOutput: Default; - input - .program_scopes - .values() - .for_each(|scope| self.visit_program_scope(scope)); + fn visit_program(&mut self, input: &'a Program) -> Self::ProgramOutput { + input.imports.values().for_each(|import| { + self.visit_import(&import.0); + }); + + input.program_scopes.values().for_each(|scope| { + self.visit_program_scope(scope); + }); + + Default::default() } - fn visit_program_scope(&mut self, input: &'a ProgramScope) { - input.structs.values().for_each(|function| self.visit_struct(function)); + fn visit_program_scope(&mut self, input: &'a ProgramScope) -> Self::ProgramOutput { + input.structs.values().for_each(|function| { + self.visit_struct(function); + }); - input.mappings.values().for_each(|mapping| self.visit_mapping(mapping)); + input.mappings.values().for_each(|mapping| { + self.visit_mapping(mapping); + }); + + input.functions.values().for_each(|function| { + self.visit_function(function); + }); - input - .functions - .values() - .for_each(|function| self.visit_function(function)); + Default::default() } - fn visit_import(&mut self, input: &'a Program) { - self.visit_program(input) + fn visit_import(&mut self, input: &'a Program) -> Self::ProgramOutput { + self.visit_program(input); + Default::default() } - fn visit_struct(&mut self, _input: &'a Struct) {} + fn visit_struct(&mut self, _input: &'a Struct) -> Self::ProgramOutput { + Default::default() + } - fn visit_mapping(&mut self, _input: &'a Mapping) {} + fn visit_mapping(&mut self, _input: &'a Mapping) -> Self::ProgramOutput { + Default::default() + } - fn visit_function(&mut self, input: &'a Function) { + fn visit_function(&mut self, input: &'a Function) -> Self::ProgramOutput { self.visit_block(&input.block); if let Some(finalize) = &input.finalize { self.visit_block(&finalize.block); } + Default::default() } } From 88b5336fec3b9a5c7f64e0ac84ca90ef29c19a73 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:03:53 -0800 Subject: [PATCH 05/24] Update Reconstructor --- compiler/ast/src/passes/reconstructor.rs | 67 +++++++++++++++--------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index 3a31e77613..648704aaec 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -41,33 +41,26 @@ pub trait ExpressionReconstructor { } fn reconstruct_access(&mut self, input: AccessExpression) -> (Expression, Self::AdditionalOutput) { + match input { + AccessExpression::AssociatedFunction(function) => self.reconstruct_associated_function(function), + AccessExpression::Member(member) => self.reconstruct_member_access(member), + AccessExpression::Tuple(tuple) => self.reconstruct_tuple_access(tuple), + } + } + + fn reconstruct_associated_function(&mut self, input: AssociatedFunction) -> (Expression, Self::AdditionalOutput) { ( - Expression::Access(match input { - AccessExpression::AssociatedFunction(function) => { - 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) => AccessExpression::Member(MemberAccess { - inner: Box::new(self.reconstruct_expression(*member.inner).0), - name: member.name, - span: member.span, - }), - AccessExpression::Tuple(tuple) => AccessExpression::Tuple(TupleAccess { - tuple: Box::new(self.reconstruct_expression(*tuple.tuple).0), - index: tuple.index, - span: tuple.span, - }), - expr => expr, - }), - Default::default(), + Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction { + ty: input.ty, + name: input.name, + args: input + .args + .into_iter() + .map(|arg| self.reconstruct_expression(arg).0) + .collect(), + span: input.span, + })), + Default::default() ) } @@ -115,6 +108,17 @@ pub trait ExpressionReconstructor { (Expression::Literal(input), Default::default()) } + fn reconstruct_member_access(&mut self, input: MemberAccess) -> (Expression, Self::AdditionalOutput) { + ( + Expression::Access(AccessExpression::Member(MemberAccess { + inner: Box::new(self.reconstruct_expression(*input.inner).0), + name: input.name, + span: input.span, + })), + Default::default() + ) + } + fn reconstruct_ternary(&mut self, input: TernaryExpression) -> (Expression, Self::AdditionalOutput) { ( Expression::Ternary(TernaryExpression { @@ -141,6 +145,17 @@ pub trait ExpressionReconstructor { ) } + fn reconstruct_tuple_access(&mut self, input: TupleAccess) -> (Expression, Self::AdditionalOutput) { + ( + Expression::Access(AccessExpression::Tuple(TupleAccess { + tuple: Box::new(self.reconstruct_expression(*input.tuple).0), + index: input.index, + span: input.span, + })), + Default::default() + ) + } + fn reconstruct_unary(&mut self, input: UnaryExpression) -> (Expression, Self::AdditionalOutput) { ( Expression::Unary(UnaryExpression { From cc6835699ff7866a2fd9a31c489c4a317c273c77 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:04:26 -0800 Subject: [PATCH 06/24] Update flattener --- .../src/flattening/flatten_expression.rs | 70 ++++++------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/compiler/passes/src/flattening/flatten_expression.rs b/compiler/passes/src/flattening/flatten_expression.rs index 9743df6c30..e1894f9f38 100644 --- a/compiler/passes/src/flattening/flatten_expression.rs +++ b/compiler/passes/src/flattening/flatten_expression.rs @@ -17,60 +17,13 @@ use crate::Flattener; use itertools::Itertools; -use leo_ast::{ - AccessExpression, AssociatedFunction, Expression, ExpressionReconstructor, Member, MemberAccess, Statement, - StructExpression, StructVariableInitializer, TernaryExpression, TupleExpression, -}; +use leo_ast::{AccessExpression, AssociatedFunction, Expression, ExpressionReconstructor, Member, MemberAccess, Statement, StructExpression, StructVariableInitializer, TernaryExpression, TupleAccess, TupleExpression}; -// TODO: Clean up logic. To be done in a follow-up PR (feat/tuples) +// TODO: Clean up logic and verify statement accumulation. To be done in a follow-up PR impl ExpressionReconstructor for Flattener<'_> { type AdditionalOutput = Vec; - /// 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(); @@ -368,4 +321,23 @@ impl ExpressionReconstructor for Flattener<'_> { } } } + + fn reconstruct_tuple_access(&mut self, input: TupleAccess) -> (Expression, Self::AdditionalOutput) { + let mut statements = Vec::new(); + // 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. + let expression = 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."), + }; + (expression, statements) + } } From 21f1e939fb6a59c949dc9daf9fe47ab14da1271c Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:04:41 -0800 Subject: [PATCH 07/24] Update SymbolTableCreation --- compiler/passes/src/symbol_table_creation/creator.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/passes/src/symbol_table_creation/creator.rs b/compiler/passes/src/symbol_table_creation/creator.rs index d837d7e395..fbb063f3b3 100644 --- a/compiler/passes/src/symbol_table_creation/creator.rs +++ b/compiler/passes/src/symbol_table_creation/creator.rs @@ -40,12 +40,16 @@ impl<'a> SymbolTableCreator<'a> { impl<'a> ExpressionVisitor<'a> for SymbolTableCreator<'a> { type AdditionalInput = (); - type Output = (); + type ExpressionOutput = (); } -impl<'a> StatementVisitor<'a> for SymbolTableCreator<'a> {} +impl<'a> StatementVisitor<'a> for SymbolTableCreator<'a> { + type StatementOutput = (); +} impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { + type ProgramOutput = (); + fn visit_import(&mut self, input: &'a Program) { self.visit_program(input) } From 7df66a240a970f7ac9fa7c4369d6ff1d183688e6 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:05:15 -0800 Subject: [PATCH 08/24] Update type checker --- .../src/type_checking/check_expressions.rs | 281 ++++++++++-------- .../passes/src/type_checking/check_program.rs | 2 + .../src/type_checking/check_statements.rs | 2 + 3 files changed, 159 insertions(+), 126 deletions(-) diff --git a/compiler/passes/src/type_checking/check_expressions.rs b/compiler/passes/src/type_checking/check_expressions.rs index 2c1c274eee..d545e14b40 100644 --- a/compiler/passes/src/type_checking/check_expressions.rs +++ b/compiler/passes/src/type_checking/check_expressions.rs @@ -43,147 +43,164 @@ fn return_incorrect_type(t1: Option, t2: Option, expected: &Option ExpressionVisitor<'a> for TypeChecker<'a> { type AdditionalInput = Option; - type Output = Option; + type ExpressionOutput = Option; + + fn visit_associated_function(&mut self, input: &'a AssociatedFunction, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + // Check core struct name and function. + if let Some(core_instruction) = self.check_core_function_call(&input.ty, &input.name) { + // Check num input arguments. + if core_instruction.num_args() != input.args.len() { + // TODO: Better error messages. + self.emit_err(TypeCheckerError::incorrect_num_args_to_call( + core_instruction.num_args(), + input.args.len(), + input.span(), + )); + } - fn visit_access(&mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput) -> Self::Output { - match input { - AccessExpression::AssociatedFunction(access) => { - // Check core struct name and function. - if let Some(core_instruction) = self.check_core_function_call(&access.ty, &access.name) { - // Check num input arguments. - if core_instruction.num_args() != access.args.len() { + // Check first argument type. + if let Some(first_arg) = input.args.get(0usize) { + if let Some(first_arg_type) = self.visit_expression(first_arg, &None) { + if !core_instruction.first_arg_is_allowed_type(&first_arg_type) { // TODO: Better error messages. - self.emit_err(TypeCheckerError::incorrect_num_args_to_call( - core_instruction.num_args(), - access.args.len(), - input.span(), + self.emit_err(TypeCheckerError::invalid_type( + &first_arg_type, + input.args.get(0).unwrap().span(), )); } + } + } - // Check first argument type. - if let Some(first_arg) = access.args.get(0usize) { - if let Some(first_arg_type) = self.visit_expression(first_arg, &None) { - if !core_instruction.first_arg_is_allowed_type(&first_arg_type) { - // TODO: Better error messages. - self.emit_err(TypeCheckerError::invalid_type( - &first_arg_type, - access.args.get(0).unwrap().span(), - )); - } - } + // Check second argument type. + if let Some(second_arg) = input.args.get(1usize) { + if let Some(second_arg_type) = self.visit_expression(second_arg, &None) { + if !core_instruction.second_arg_is_allowed_type(&second_arg_type) { + // TODO: Better error messages. + self.emit_err(TypeCheckerError::invalid_type( + &second_arg_type, + input.args.get(1).unwrap().span(), + )); } + } + } - // Check second argument type. - if let Some(second_arg) = access.args.get(1usize) { - if let Some(second_arg_type) = self.visit_expression(second_arg, &None) { - if !core_instruction.second_arg_is_allowed_type(&second_arg_type) { - // TODO: Better error messages. - self.emit_err(TypeCheckerError::invalid_type( - &second_arg_type, - access.args.get(1).unwrap().span(), - )); - } - } - } + // Check return type. + return Some(self.assert_and_return_type(core_instruction.return_type(), expected, input.span())); + } else { + self.emit_err(TypeCheckerError::invalid_core_function_call(input, input.span())); + } + None + } - // Check return type. - return Some(self.assert_and_return_type(core_instruction.return_type(), expected, access.span())); - } else { - self.emit_err(TypeCheckerError::invalid_core_function_call(access, access.span())); + fn visit_member_access(&mut self, input: &'a MemberAccess, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + match *input.inner { + // If the access expression is of the form `self.`, then check the is valid. + Expression::Identifier(identifier) if identifier.name == sym::SelfLower => match input.name.name { + sym::caller => return Some(Type::Address), + _ => { + self.emit_err(TypeCheckerError::invalid_self_access(input.name.span())); } - } - AccessExpression::Tuple(access) => { - if let Some(type_) = self.visit_expression(&access.tuple, &None) { - match type_ { - Type::Tuple(tuple) => { - // Check out of range access. - let index = access.index.to_usize(); - if index > tuple.len() - 1 { - self.emit_err(TypeCheckerError::tuple_out_of_range(index, tuple.len(), access.span())); - } else { - // Lookup type of tuple index. - let actual = tuple.get(index).expect("failed to get tuple index").clone(); - if let Some(expected) = expected { - // Emit error for mismatched types. - if !actual.eq_flat(expected) { - self.emit_err(TypeCheckerError::type_should_be( - &actual, - expected, - access.span(), - )) - } + }, + _ => { + // Check that the type of `inner` in `inner.name` is a struct. + match self.visit_expression(&input.inner, &None) { + Some(Type::Identifier(identifier)) => { + // Retrieve the struct definition associated with `identifier`. + let struct_ = self.symbol_table.borrow().lookup_struct(identifier.name).cloned(); + if let Some(struct_) = struct_ { + // Check that `access.name` is a member of the struct. + match struct_.members.iter().find(|member| member.name() == input.name.name) { + // Case where `access.name` is a member of the struct. + Some(Member { type_, .. }) => { + // Check that the type of `access.name` is the same as `expected`. + return Some(self.assert_and_return_type( + type_.clone(), + expected, + input.span(), + )); + } + // Case where `access.name` is not a member of the struct. + None => { + self.emit_err(TypeCheckerError::invalid_struct_variable( + input.name, + &struct_, + input.name.span(), + )); } - - // Return type of tuple index. - return Some(actual); } + } else { + self.emit_err(TypeCheckerError::undefined_type(&input.inner, input.inner.span())); } - type_ => { - self.emit_err(TypeCheckerError::type_should_be(type_, "tuple", access.span())); - } } - self.emit_err(TypeCheckerError::invalid_core_function_call(access, access.span())); + Some(type_) => { + self.emit_err(TypeCheckerError::type_should_be(type_, "struct", input.inner.span())); + } + None => { + self.emit_err(TypeCheckerError::could_not_determine_type( + &input.inner, + input.inner.span(), + )); + } } } - AccessExpression::Member(access) => { - match *access.inner { - // If the access expression is of the form `self.`, then check the is valid. - Expression::Identifier(identifier) if identifier.name == sym::SelfLower => match access.name.name { - sym::caller => return Some(Type::Address), - _ => { - self.emit_err(TypeCheckerError::invalid_self_access(access.name.span())); - } - }, - _ => { - // Check that the type of `inner` in `inner.name` is a struct. - match self.visit_expression(&access.inner, &None) { - Some(Type::Identifier(identifier)) => { - // Retrieve the struct definition associated with `identifier`. - let struct_ = self.symbol_table.borrow().lookup_struct(identifier.name).cloned(); - if let Some(struct_) = struct_ { - // Check that `access.name` is a member of the struct. - match struct_.members.iter().find(|member| member.name() == access.name.name) { - // Case where `access.name` is a member of the struct. - Some(Member { type_, .. }) => { - // Check that the type of `access.name` is the same as `expected`. - return Some(self.assert_and_return_type( - type_.clone(), - expected, - access.span(), - )); - } - // Case where `access.name` is not a member of the struct. - None => { - self.emit_err(TypeCheckerError::invalid_struct_variable( - access.name, - &struct_, - access.name.span(), - )); - } - } - } else { - self.emit_err(TypeCheckerError::undefined_type(&access.inner, access.inner.span())); - } - } - Some(type_) => { - self.emit_err(TypeCheckerError::type_should_be(type_, "struct", access.inner.span())); - } - None => { - self.emit_err(TypeCheckerError::could_not_determine_type( - &access.inner, - access.inner.span(), - )); + } + None + } + + fn visit_tuple_access(&mut self, input: &'a TupleAccess, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + if let Some(type_) = self.visit_expression(&input.tuple, &None) { + match type_ { + Type::Tuple(tuple) => { + // Check out of range access. + let index = input.index.to_usize(); + if index > tuple.len() - 1 { + self.emit_err(TypeCheckerError::tuple_out_of_range(index, tuple.len(), input.span())); + } else { + // Lookup type of tuple index. + let actual = tuple.get(index).expect("failed to get tuple index").clone(); + if let Some(expected) = expected { + // Emit error for mismatched types. + if !actual.eq_flat(expected) { + self.emit_err(TypeCheckerError::type_should_be( + &actual, + expected, + input.span(), + )) } } + + // Return type of tuple index. + return Some(actual); } } + type_ => { + self.emit_err(TypeCheckerError::type_should_be(type_, "tuple", input.span())); + } } - AccessExpression::AssociatedConstant(..) => {} // todo: Add support for associated constants (u8::MAX). + self.emit_err(TypeCheckerError::invalid_core_function_call(input, input.span())); } None } - fn visit_binary(&mut self, input: &'a BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output { + + fn visit_access( + &mut self, + input: &'a AccessExpression, + expected: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + match input { + AccessExpression::AssociatedFunction(access) => self.visit_associated_function(access, &expected), + AccessExpression::Tuple(access) => self.visit_tuple_access(access, &expected), + AccessExpression::Member(access) => self.visit_member_access(access, &expected), + AccessExpression::AssociatedConstant(..) => {} // todo: Add support for associated constants (u8::MAX). + } + } + + fn visit_binary( + &mut self, + input: &'a BinaryExpression, + destination: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match input.op { BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => { // Only boolean types. @@ -437,7 +454,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::Output { + fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { match &*input.function { // Note that the parser guarantees that `input.function` is always an identifier. Expression::Identifier(ident) => { @@ -506,7 +523,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_struct_init(&mut self, input: &'a StructExpression, additional: &Self::AdditionalInput) -> Self::Output { + fn visit_struct_init( + &mut self, + input: &'a StructExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { let struct_ = self.symbol_table.borrow().lookup_struct(input.name.name).cloned(); if let Some(struct_) = struct_ { // Check struct type name. @@ -556,11 +577,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } // We do not want to panic on `ErrExpression`s in order to propagate as many errors as possible. - fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { Default::default() } - fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output { + fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { if let Some(var) = self.symbol_table.borrow().lookup_variable(input.name) { Some(self.assert_and_return_type(var.type_.clone(), expected, input.span())) } else { @@ -569,7 +590,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output { + fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { fn parse_integer_literal(handler: &Handler, string: &String, span: Span, type_string: &str) { if string.parse::().is_err() { handler.emit_err(TypeCheckerError::invalid_int_value(string, type_string, span)); @@ -631,7 +652,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { }) } - fn visit_ternary(&mut self, input: &'a TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output { + fn visit_ternary( + &mut self, + input: &'a TernaryExpression, + expected: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.condition, &Some(Type::Boolean)); let t1 = self.visit_expression(&input.if_true, expected); @@ -640,7 +665,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { return_incorrect_type(t1, t2, expected) } - fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output { + fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { match input.elements.len() { 0 | 1 => unreachable!("Parsing guarantees that tuple expressions have at least two elements."), _ => { @@ -677,7 +702,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output { + fn visit_unary( + &mut self, + input: &'a UnaryExpression, + destination: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match input.op { UnaryOperation::Abs => { // Only signed integer types. @@ -724,7 +753,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_unit(&mut self, input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output { + fn visit_unit(&mut self, input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { // Unit expression are only allowed inside a return statement. if !self.is_return { self.emit_err(TypeCheckerError::unit_expression_only_in_return_statements( diff --git a/compiler/passes/src/type_checking/check_program.rs b/compiler/passes/src/type_checking/check_program.rs index c8af36d342..b677507fa5 100644 --- a/compiler/passes/src/type_checking/check_program.rs +++ b/compiler/passes/src/type_checking/check_program.rs @@ -26,6 +26,8 @@ use std::collections::HashSet; // TODO: Cleanup logic for tuples. impl<'a> ProgramVisitor<'a> for TypeChecker<'a> { + type ProgramOutput = (); + fn visit_program(&mut self, input: &'a Program) { match self.is_imported { // If the program is imported, then it is not allowed to import any other programs. diff --git a/compiler/passes/src/type_checking/check_statements.rs b/compiler/passes/src/type_checking/check_statements.rs index 5c6f6a475f..c0cdea432a 100644 --- a/compiler/passes/src/type_checking/check_statements.rs +++ b/compiler/passes/src/type_checking/check_statements.rs @@ -22,6 +22,8 @@ use leo_errors::TypeCheckerError; use leo_span::{Span, Symbol}; impl<'a> StatementVisitor<'a> for TypeChecker<'a> { + type StatementOutput = (); + fn visit_statement(&mut self, input: &'a Statement) { // No statements can follow a return statement. if self.has_return { From e4e4fdd1a4098c4571b0ab68c6e29b8f9d6a0389 Mon Sep 17 00:00:00 2001 From: d0cd Date: Fri, 24 Feb 2023 16:05:33 -0800 Subject: [PATCH 09/24] WIP refactor code generation --- compiler/passes/src/code_generation/mod.rs | 2 +- .../src/code_generation/visit_expressions.rs | 119 ++++++---- .../src/code_generation/visit_program.rs | 20 +- .../src/code_generation/visit_statements.rs | 203 ++++++++---------- 4 files changed, 179 insertions(+), 165 deletions(-) diff --git a/compiler/passes/src/code_generation/mod.rs b/compiler/passes/src/code_generation/mod.rs index 1a441b0abe..8612b7b114 100644 --- a/compiler/passes/src/code_generation/mod.rs +++ b/compiler/passes/src/code_generation/mod.rs @@ -28,7 +28,7 @@ mod visit_type; use crate::SymbolTable; use crate::{CallGraph, Pass, StructGraph}; -use leo_ast::Ast; +use leo_ast::{Ast, ProgramVisitor}; use leo_errors::Result; impl<'a> Pass for CodeGenerator<'a> { diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index 0855ce3c72..f09c961107 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -17,8 +17,8 @@ use crate::CodeGenerator; use leo_ast::{ AccessExpression, AssociatedFunction, BinaryExpression, BinaryOperation, CallExpression, ErrExpression, Expression, - Identifier, Literal, MemberAccess, StructExpression, TernaryExpression, TupleExpression, Type, UnaryExpression, - UnaryOperation, UnitExpression, + ExpressionVisitor, Identifier, Literal, MemberAccess, StructExpression, TernaryExpression, TupleExpression, Type, + UnaryExpression, UnaryOperation, UnitExpression, }; use leo_span::sym; use std::borrow::Borrow; @@ -29,38 +29,33 @@ use std::fmt::Write as _; // Note: We opt for this option instead of using `Visitor` and `Director` because this pass requires // a post-order traversal of the AST. This is sufficient since this implementation is intended to be // a prototype. The production implementation will require a redesign of `Director`. -impl<'a> CodeGenerator<'a> { - pub(crate) fn visit_expression(&mut self, input: &'a Expression) -> (String, String) { - match input { - Expression::Access(expr) => self.visit_access(expr), - Expression::Binary(expr) => self.visit_binary(expr), - Expression::Call(expr) => self.visit_call(expr), - Expression::Struct(expr) => self.visit_struct_init(expr), - Expression::Err(expr) => self.visit_err(expr), - Expression::Identifier(expr) => self.visit_identifier(expr), - Expression::Literal(expr) => self.visit_value(expr), - 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), - } - } - - fn visit_identifier(&mut self, input: &'a Identifier) -> (String, String) { +impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { + type AdditionalInput = (); + type ExpressionOutput = (String, String); + + fn visit_identifier( + &mut self, + input: &'a Identifier, + _additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { (self.variable_mapping.get(&input.name).unwrap().clone(), String::new()) } - fn visit_err(&mut self, _input: &'a ErrExpression) -> (String, String) { + fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { unreachable!("`ErrExpression`s should not be in the AST at this phase of compilation.") } - fn visit_value(&mut self, input: &'a Literal) -> (String, String) { + fn visit_literal(&mut self, input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { (format!("{input}"), String::new()) } - fn visit_binary(&mut self, input: &'a BinaryExpression) -> (String, String) { - let (left_operand, left_instructions) = self.visit_expression(&input.left); - let (right_operand, right_instructions) = self.visit_expression(&input.right); + fn visit_binary( + &mut self, + input: &'a BinaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + let (left_operand, left_instructions) = self.visit_expression(&input.left, additional); + let (right_operand, right_instructions) = self.visit_expression(&input.right, additional); let opcode = match input.op { BinaryOperation::Add => String::from("add"), @@ -109,8 +104,12 @@ impl<'a> CodeGenerator<'a> { (destination_register, instructions) } - fn visit_unary(&mut self, input: &'a UnaryExpression) -> (String, String) { - let (expression_operand, expression_instructions) = self.visit_expression(&input.receiver); + fn visit_unary( + &mut self, + input: &'a UnaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + let (expression_operand, expression_instructions) = self.visit_expression(&input.receiver, additional); let opcode = match input.op { UnaryOperation::Abs => String::from("abs"), @@ -136,10 +135,14 @@ impl<'a> CodeGenerator<'a> { (destination_register, instructions) } - fn visit_ternary(&mut self, input: &'a TernaryExpression) -> (String, String) { - let (condition_operand, condition_instructions) = self.visit_expression(&input.condition); - let (if_true_operand, if_true_instructions) = self.visit_expression(&input.if_true); - let (if_false_operand, if_false_instructions) = self.visit_expression(&input.if_false); + fn visit_ternary( + &mut self, + input: &'a TernaryExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + let (condition_operand, condition_instructions) = self.visit_expression(&input.condition, additional); + let (if_true_operand, if_true_instructions) = self.visit_expression(&input.if_true, additional); + let (if_false_operand, if_false_instructions) = self.visit_expression(&input.if_false, additional); let destination_register = format!("r{}", self.next_register); let ternary_instruction = format!( @@ -158,7 +161,11 @@ impl<'a> CodeGenerator<'a> { (destination_register, instructions) } - fn visit_struct_init(&mut self, input: &'a StructExpression) -> (String, String) { + fn visit_struct_init( + &mut self, + input: &'a StructExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { // Lookup struct or record. let name = if let Some((is_record, type_)) = self.composite_mapping.get(&input.name.name) { if *is_record { @@ -180,13 +187,13 @@ impl<'a> CodeGenerator<'a> { for member in input.members.iter() { let operand = if let Some(expr) = member.expression.as_ref() { // Visit variable expression. - let (variable_operand, variable_instructions) = self.visit_expression(expr); + let (variable_operand, variable_instructions) = self.visit_expression(expr, additional); instructions.push_str(&variable_instructions); variable_operand } else { // Push operand identifier. - let (ident_operand, ident_instructions) = self.visit_identifier(&member.identifier); + let (ident_operand, ident_instructions) = self.visit_identifier(&member.identifier, additional); instructions.push_str(&ident_instructions); ident_operand @@ -209,15 +216,23 @@ impl<'a> CodeGenerator<'a> { (destination_register, instructions) } - fn visit_member_access(&mut self, input: &'a MemberAccess) -> (String, String) { - let (inner_struct, _inner_instructions) = self.visit_expression(&input.inner); + fn visit_member_access( + &mut self, + input: &'a MemberAccess, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { + let (inner_struct, _inner_instructions) = self.visit_expression(&input.inner, additional); let member_access_instruction = format!("{inner_struct}.{}", input.name); (member_access_instruction, String::new()) } // Pedersen64::hash() -> hash.ped64 - fn visit_associated_function(&mut self, input: &'a AssociatedFunction) -> (String, String) { + fn visit_associated_function( + &mut self, + input: &'a AssociatedFunction, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { // Write identifier as opcode. `Pedersen64` -> `ped64`. let symbol: &str = if let Type::Identifier(identifier) = input.ty { match identifier.name { @@ -242,7 +257,7 @@ impl<'a> CodeGenerator<'a> { // Visit each function argument and accumulate instructions from expressions. for arg in input.args.iter() { - let (arg_string, arg_instructions) = self.visit_expression(arg); + let (arg_string, arg_instructions) = self.visit_expression(arg, additional); write!(associated_function_call, "{arg_string} ").expect("failed to write associated function argument"); instructions.push_str(&arg_instructions); } @@ -259,17 +274,21 @@ impl<'a> CodeGenerator<'a> { (destination_register, instructions) } - fn visit_access(&mut self, input: &'a AccessExpression) -> (String, String) { + fn visit_access( + &mut self, + input: &'a AccessExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match input { - AccessExpression::Member(access) => self.visit_member_access(access), + AccessExpression::Member(access) => self.visit_member_access(access, additional), AccessExpression::AssociatedConstant(_) => todo!(), // Associated constants are not supported in AVM yet. - AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function), + AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), AccessExpression::Tuple(_) => todo!(), // Tuples are not supported in AVM yet. } } // TODO: Cleanup - fn visit_call(&mut self, input: &'a CallExpression) -> (String, String) { + fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { let mut call_instruction = match &input.external { Some(external) => format!(" call {external}.aleo/{}", input.function), None => format!(" call {}", input.function), @@ -277,7 +296,7 @@ impl<'a> CodeGenerator<'a> { let mut instructions = String::new(); for argument in input.arguments.iter() { - let (argument, argument_instructions) = self.visit_expression(argument); + let (argument, argument_instructions) = self.visit_expression(argument, additional); write!(call_instruction, " {argument}").expect("failed to write to string"); instructions.push_str(&argument_instructions); } @@ -330,7 +349,11 @@ impl<'a> CodeGenerator<'a> { } } - fn visit_tuple(&mut self, input: &'a TupleExpression) -> (String, String) { + fn visit_tuple( + &mut self, + input: &'a TupleExpression, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { // 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()); @@ -338,7 +361,7 @@ impl<'a> CodeGenerator<'a> { // Visit each tuple element and accumulate instructions from expressions. for element in input.elements.iter() { - let (element, element_instructions) = self.visit_expression(element); + let (element, element_instructions) = self.visit_expression(element, additional); tuple_elements.push(element); instructions.push_str(&element_instructions); } @@ -347,7 +370,11 @@ impl<'a> CodeGenerator<'a> { (tuple_elements.join(" "), instructions) } - fn visit_unit(&mut self, _input: &'a UnitExpression) -> (String, String) { + fn visit_unit( + &mut self, + _input: &'a UnitExpression, + _additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { unreachable!("`UnitExpression`s should not be visited during code generation.") } } diff --git a/compiler/passes/src/code_generation/visit_program.rs b/compiler/passes/src/code_generation/visit_program.rs index 25559ee26b..f8eb88f163 100644 --- a/compiler/passes/src/code_generation/visit_program.rs +++ b/compiler/passes/src/code_generation/visit_program.rs @@ -16,15 +16,19 @@ use crate::CodeGenerator; -use leo_ast::{functions, Function, Mapping, Mode, Program, ProgramScope, Struct, Type, Variant}; +use leo_ast::{ + functions, Function, Mapping, Mode, Program, ProgramScope, ProgramVisitor, StatementVisitor, Struct, Type, Variant, +}; use indexmap::IndexMap; use itertools::Itertools; use leo_span::{sym, Symbol}; use std::fmt::Write as _; -impl<'a> CodeGenerator<'a> { - pub(crate) fn visit_program(&mut self, input: &'a Program) -> String { +impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { + type ProgramOutput = String; + + fn visit_program(&mut self, input: &'a Program) -> Self::ProgramOutput { // Accumulate instructions into a program string. let mut program_string = String::new(); @@ -108,7 +112,7 @@ impl<'a> CodeGenerator<'a> { program_string } - fn visit_import(&mut self, import_name: &'a Symbol, import_program: &'a Program) -> String { + fn visit_import(&mut self, import_name: &'a Symbol, import_program: &'a Program) -> Self::ProgramOutput { // Load symbols into composite mapping. let _import_program_string = self.visit_program(import_program); // todo: We do not need the import program string because we generate instructions for imports separately during leo build. @@ -117,7 +121,7 @@ impl<'a> CodeGenerator<'a> { format!("import {import_name}.aleo;") } - fn visit_struct_or_record(&mut self, struct_: &'a Struct) -> String { + fn visit_struct_or_record(&mut self, struct_: &'a Struct) -> Self::ProgramOutput { if struct_.is_record { self.visit_record(struct_) } else { @@ -140,7 +144,7 @@ impl<'a> CodeGenerator<'a> { output_string } - fn visit_record(&mut self, record: &'a Struct) -> String { + fn visit_record(&mut self, record: &'a Struct) -> Self::ProgramOutput { // Add record symbol to composite types. let mut output_string = String::from("record"); self.composite_mapping @@ -165,7 +169,7 @@ impl<'a> CodeGenerator<'a> { output_string } - fn visit_function(&mut self, function: &'a Function) -> String { + fn visit_function(&mut self, function: &'a Function) -> Self::ProgramOutput { // Initialize the state of `self` with the appropriate values before visiting `function`. self.next_register = 0; self.variable_mapping = IndexMap::new(); @@ -263,7 +267,7 @@ impl<'a> CodeGenerator<'a> { function_string } - fn visit_mapping(&mut self, mapping: &'a Mapping) -> String { + fn visit_mapping(&mut self, mapping: &'a Mapping) -> Self::ProgramOutput { // Create the prefix of the mapping string, e.g. `mapping foo:`. let mut mapping_string = format!("mapping {}:\n", mapping.identifier); diff --git a/compiler/passes/src/code_generation/visit_statements.rs b/compiler/passes/src/code_generation/visit_statements.rs index 04ccb90b1b..9c48f2dca2 100644 --- a/compiler/passes/src/code_generation/visit_statements.rs +++ b/compiler/passes/src/code_generation/visit_statements.rs @@ -18,34 +18,20 @@ use crate::CodeGenerator; use leo_ast::{ AssertStatement, AssertVariant, AssignStatement, Block, ConditionalStatement, ConsoleStatement, DecrementStatement, - DefinitionStatement, Expression, ExpressionStatement, IncrementStatement, IterationStatement, Mode, Output, - ReturnStatement, Statement, + DefinitionStatement, Expression, ExpressionStatement, ExpressionVisitor, IncrementStatement, IterationStatement, + Mode, ReturnStatement, Statement, Output, StatementVisitor, }; use itertools::Itertools; use std::fmt::Write as _; -impl<'a> CodeGenerator<'a> { - fn visit_statement(&mut self, input: &'a Statement) -> String { - match input { - Statement::Assert(stmt) => self.visit_assert(stmt), - Statement::Assign(stmt) => self.visit_assign(stmt), - Statement::Block(stmt) => self.visit_block(stmt), - Statement::Conditional(stmt) => self.visit_conditional(stmt), - 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::Increment(stmt) => self.visit_increment(stmt), - Statement::Iteration(stmt) => self.visit_iteration(stmt), - Statement::Return(stmt) => self.visit_return(stmt), - } - } +impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { + type StatementOutput = String; - fn visit_assert(&mut self, input: &'a AssertStatement) -> String { + fn visit_assert(&mut self, input: &'a AssertStatement) -> Self::StatementOutput { let mut generate_assert_instruction = |name: &str, left: &'a Expression, right: &'a Expression| { - let (left_operand, left_instructions) = self.visit_expression(left); - let (right_operand, right_instructions) = self.visit_expression(right); + let (left_operand, left_instructions) = self.visit_expression(left, &()); + let (right_operand, right_instructions) = self.visit_expression(right, &()); let assert_instruction = format!(" {name} {left_operand} {right_operand};\n"); // Concatenate the instructions. @@ -57,7 +43,7 @@ impl<'a> CodeGenerator<'a> { }; match &input.variant { AssertVariant::Assert(expr) => { - let (operand, mut instructions) = self.visit_expression(expr); + let (operand, mut instructions) = self.visit_expression(expr, &()); let assert_instruction = format!(" assert.eq {operand} true;\n"); instructions.push_str(&assert_instruction); @@ -68,12 +54,94 @@ impl<'a> CodeGenerator<'a> { } } - fn visit_return(&mut self, input: &'a ReturnStatement) -> String { + fn visit_assign(&mut self, input: &'a AssignStatement) -> Self::StatementOutput { + 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::>(); + // 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." + ), + } + } + + fn visit_block(&mut self, input: &'a Block) -> Self::StatementOutput { + // For each statement in the block, visit it and add its instructions to the list. + input.statements.iter().map(|stmt| self.visit_statement(stmt)).join("") + } + + fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> Self::StatementOutput { + // TODO: Once SSA is made optional, create a Leo error informing the user to enable the SSA pass. + unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.") + } + + fn visit_console(&mut self, _: &'a ConsoleStatement) -> Self::StatementOutput { + unreachable!("Parsing guarantees that `ConsoleStatement`s are not present in the AST.") + } + + fn visit_decrement(&mut self, input: &'a DecrementStatement) -> Self::StatementOutput { + let (index, mut instructions) = self.visit_expression(&input.index, &()); + let (amount, amount_instructions) = self.visit_expression(&input.amount, &()); + instructions.push_str(&amount_instructions); + instructions.push_str(&format!(" decrement {}[{index}] by {amount};\n", input.mapping)); + + instructions + } + + fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> Self::StatementOutput { + // TODO: If SSA is made optional, then conditionally enable codegen for DefinitionStatement + unreachable!("DefinitionStatement's should not exist in SSA form.") + } + + fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> Self::StatementOutput { + match input.expression { + Expression::Call(_) => { + // Note that codegen for CallExpression in an expression statement does not return any destination registers. + self.visit_expression(&input.expression, &()).1 + } + _ => unreachable!("ExpressionStatement's can only contain CallExpression's."), + } + } + + fn visit_increment(&mut self, input: &'a IncrementStatement) -> Self::StatementOutput { + let (index, mut instructions) = self.visit_expression(&input.index, &()); + let (amount, amount_instructions) = self.visit_expression(&input.amount, &()); + instructions.push_str(&amount_instructions); + instructions.push_str(&format!(" increment {}[{index}] by {amount};\n", input.mapping)); + + instructions + } + + fn visit_iteration(&mut self, _input: &'a IterationStatement) -> Self::StatementOutput { + // TODO: Once loop unrolling is made optional, create a Leo error informing the user to enable the loop unrolling pass.. + unreachable!("`IterationStatement`s should not be in the AST at this phase of compilation."); + } + + fn visit_return(&mut self, input: &'a ReturnStatement) -> Self::StatementOutput { let mut instructions = match input.expression { // Skip empty return statements. Expression::Unit(_) => String::new(), _ => { - let (operand, mut expression_instructions) = self.visit_expression(&input.expression); + let (operand, mut expression_instructions) = self.visit_expression(&input.expression, &()); // Get the output type of the function. let output = if self.in_finalize { // Note that the first unwrap is safe, since `current_function` is set in `visit_function`. @@ -133,7 +201,7 @@ impl<'a> CodeGenerator<'a> { let mut finalize_instruction = "\n finalize".to_string(); for argument in arguments.iter() { - let (argument, argument_instructions) = self.visit_expression(argument); + let (argument, argument_instructions) = self.visit_expression(argument, &()); write!(finalize_instruction, " {argument}").expect("failed to write to string"); instructions.push_str(&argument_instructions); } @@ -144,89 +212,4 @@ impl<'a> CodeGenerator<'a> { instructions } - - fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> String { - // TODO: If SSA is made optional, then conditionally enable codegen for DefinitionStatement - // let (operand, expression_instructions) = self.visit_expression(&input.value); - // self.variable_mapping.insert(&input.variable_name.name, operand); - // expression_instructions - unreachable!("DefinitionStatement's should not exist in SSA form.") - } - - fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> String { - match input.expression { - Expression::Call(_) => { - // Note that codegen for CallExpression in an expression statement does not return any destination registers. - 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); - instructions.push_str(&amount_instructions); - instructions.push_str(&format!(" increment {}[{index}] by {amount};\n", input.mapping)); - - instructions - } - - fn visit_decrement(&mut self, input: &'a DecrementStatement) -> String { - let (index, mut instructions) = self.visit_expression(&input.index); - let (amount, amount_instructions) = self.visit_expression(&input.amount); - instructions.push_str(&amount_instructions); - instructions.push_str(&format!(" decrement {}[{index}] by {amount};\n", input.mapping)); - - instructions - } - - fn visit_assign(&mut self, input: &'a AssignStatement) -> String { - 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::>(); - // 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." - ), - } - } - - fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> String { - // TODO: Once SSA is made optional, create a Leo error informing the user to enable the SSA pass. - unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.") - } - - fn visit_iteration(&mut self, _input: &'a IterationStatement) -> String { - // TODO: Once loop unrolling is made optional, create a Leo error informing the user to enable the loop unrolling pass.. - unreachable!("`IterationStatement`s should not be in the AST at this phase of compilation."); - } - - fn visit_console(&mut self, _: &'a ConsoleStatement) -> String { - unreachable!("Parsing guarantees that `ConsoleStatement`s are not present in the AST.") - } - - pub(crate) fn visit_block(&mut self, input: &'a Block) -> String { - // For each statement in the block, visit it and add its instructions to the list. - input.statements.iter().map(|stmt| self.visit_statement(stmt)).join("") - } } From 751804f861899ebe47f701b27f9dd4008700f970 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 12:25:22 -0800 Subject: [PATCH 10/24] Clean up type checking --- compiler/passes/src/type_checking/check_expressions.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/passes/src/type_checking/check_expressions.rs b/compiler/passes/src/type_checking/check_expressions.rs index d545e14b40..fca54f8256 100644 --- a/compiler/passes/src/type_checking/check_expressions.rs +++ b/compiler/passes/src/type_checking/check_expressions.rs @@ -192,7 +192,6 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { AccessExpression::AssociatedFunction(access) => self.visit_associated_function(access, &expected), AccessExpression::Tuple(access) => self.visit_tuple_access(access, &expected), AccessExpression::Member(access) => self.visit_member_access(access, &expected), - AccessExpression::AssociatedConstant(..) => {} // todo: Add support for associated constants (u8::MAX). } } From af3e652c85175c2dccfcd9e5847bc6c33b1693ff Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 12:27:12 -0800 Subject: [PATCH 11/24] Clean up flattening --- compiler/passes/src/flattening/flatten_expression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/passes/src/flattening/flatten_expression.rs b/compiler/passes/src/flattening/flatten_expression.rs index e1894f9f38..d43fcffd0b 100644 --- a/compiler/passes/src/flattening/flatten_expression.rs +++ b/compiler/passes/src/flattening/flatten_expression.rs @@ -325,7 +325,7 @@ impl ExpressionReconstructor for Flattener<'_> { fn reconstruct_tuple_access(&mut self, input: TupleAccess) -> (Expression, Self::AdditionalOutput) { let mut statements = Vec::new(); // Reconstruct the tuple expression. - let (expr, stmts) = self.reconstruct_expression(*tuple.tuple); + let (expr, stmts) = self.reconstruct_expression(*input.tuple); // Accumulate any statements produced. statements.extend(stmts); @@ -334,7 +334,7 @@ impl ExpressionReconstructor for Flattener<'_> { let expression = 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() + self.tuples.get(&identifier.name).unwrap().elements[input.index.to_usize()].clone() } _ => unreachable!("SSA guarantees that subexpressions are identifiers or literals."), }; From 2b6bbce575b0dc54cd1bc3993729bf9cf3b66870 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 12:54:54 -0800 Subject: [PATCH 12/24] Code generation uses visitor framework --- .../src/code_generation/visit_expressions.rs | 3 +- .../src/code_generation/visit_program.rs | 91 +++++++++---------- .../src/code_generation/visit_statements.rs | 2 +- 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index f09c961107..f6ce1a9215 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -281,9 +281,8 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { ) -> Self::ExpressionOutput { match input { AccessExpression::Member(access) => self.visit_member_access(access, additional), - AccessExpression::AssociatedConstant(_) => todo!(), // Associated constants are not supported in AVM yet. AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), - AccessExpression::Tuple(_) => todo!(), // Tuples are not supported in AVM yet. + AccessExpression::Tuple(_) => unreachable!("Tuples should have been flattened in previous compiler passes."), } } diff --git a/compiler/passes/src/code_generation/visit_program.rs b/compiler/passes/src/code_generation/visit_program.rs index f8eb88f163..85e19eb91b 100644 --- a/compiler/passes/src/code_generation/visit_program.rs +++ b/compiler/passes/src/code_generation/visit_program.rs @@ -22,7 +22,7 @@ use leo_ast::{ use indexmap::IndexMap; use itertools::Itertools; -use leo_span::{sym, Symbol}; +use leo_span::{sym}; use std::fmt::Write as _; impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { @@ -37,8 +37,8 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { program_string.push_str( &input .imports - .iter() - .map(|(identifier, (imported_program, _))| self.visit_import(identifier, imported_program)) + .values() + .map(|(imported_program, _)| self.visit_import(imported_program)) .join("\n"), ); @@ -68,7 +68,7 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { .map(|name| { match program_scope.structs.get(&name) { // If the struct is found, it is a local struct. - Some(struct_) => self.visit_struct_or_record(struct_), + Some(struct_) => self.visit_struct(struct_), // If the struct is not found, it is an imported struct. None => String::new(), } @@ -112,61 +112,58 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { program_string } - fn visit_import(&mut self, import_name: &'a Symbol, import_program: &'a Program) -> Self::ProgramOutput { + // TODO: Fix once imports are redesigned . + fn visit_import(&mut self, input: &'a Program) -> Self::ProgramOutput { // Load symbols into composite mapping. - let _import_program_string = self.visit_program(import_program); + let _ = self.visit_program(input); // todo: We do not need the import program string because we generate instructions for imports separately during leo build. // Generate string for import statement. - format!("import {import_name}.aleo;") - } - - fn visit_struct_or_record(&mut self, struct_: &'a Struct) -> Self::ProgramOutput { - if struct_.is_record { - self.visit_record(struct_) - } else { - self.visit_struct(struct_) - } + // Note that the unwrap is safe since parsing guarantees that there is exactly one program scope. + format!("import {}.aleo;", input.program_scopes.keys().next().unwrap().name) } fn visit_struct(&mut self, struct_: &'a Struct) -> String { - // Add private symbol to composite types. - self.composite_mapping - .insert(&struct_.identifier.name, (false, String::from("private"))); // todo: private by default here. + match struct_.is_record { + true => { + // Add record symbol to composite types. + let mut output_string = String::from("record"); + self.composite_mapping + .insert(&struct_.identifier.name, (true, output_string.clone())); + writeln!(output_string, " {}:", struct_.identifier).expect("failed to write to string"); // todo: check if this is safe from name conflicts. + + // Construct and append the record variables. + for var in struct_.members.iter() { + let mode = match var.mode { + Mode::Constant => "constant", + Mode::Public => "public", + Mode::None | Mode::Private => "private", + }; + writeln!( + output_string, + " {} as {}.{mode};", // todo: CAUTION private record variables only. + var.identifier, var.type_ + ) + .expect("failed to write to string"); + } - let mut output_string = format!("struct {}:\n", struct_.identifier); // todo: check if this is safe from name conflicts. + output_string + }, + false => { + // Add private symbol to composite types. + self.composite_mapping + .insert(&struct_.identifier.name, (false, String::from("private"))); // todo: private by default here. - // Construct and append the record variables. - for var in struct_.members.iter() { - writeln!(output_string, " {} as {};", var.identifier, var.type_,).expect("failed to write to string"); - } + let mut output_string = format!("struct {}:\n", struct_.identifier); // todo: check if this is safe from name conflicts. - output_string - } + // Construct and append the record variables. + for var in struct_.members.iter() { + writeln!(output_string, " {} as {};", var.identifier, var.type_,).expect("failed to write to string"); + } - fn visit_record(&mut self, record: &'a Struct) -> Self::ProgramOutput { - // Add record symbol to composite types. - let mut output_string = String::from("record"); - self.composite_mapping - .insert(&record.identifier.name, (true, output_string.clone())); - writeln!(output_string, " {}:", record.identifier).expect("failed to write to string"); // todo: check if this is safe from name conflicts. - - // Construct and append the record variables. - for var in record.members.iter() { - let mode = match var.mode { - Mode::Constant => "constant", - Mode::Public => "public", - Mode::None | Mode::Private => "private", - }; - writeln!( - output_string, - " {} as {}.{mode};", // todo: CAUTION private record variables only. - var.identifier, var.type_ - ) - .expect("failed to write to string"); + output_string + }, } - - output_string } fn visit_function(&mut self, function: &'a Function) -> Self::ProgramOutput { diff --git a/compiler/passes/src/code_generation/visit_statements.rs b/compiler/passes/src/code_generation/visit_statements.rs index 9c48f2dca2..61f116fb25 100644 --- a/compiler/passes/src/code_generation/visit_statements.rs +++ b/compiler/passes/src/code_generation/visit_statements.rs @@ -19,7 +19,7 @@ use crate::CodeGenerator; use leo_ast::{ AssertStatement, AssertVariant, AssignStatement, Block, ConditionalStatement, ConsoleStatement, DecrementStatement, DefinitionStatement, Expression, ExpressionStatement, ExpressionVisitor, IncrementStatement, IterationStatement, - Mode, ReturnStatement, Statement, Output, StatementVisitor, + Mode, ReturnStatement, Output, StatementVisitor, }; use itertools::Itertools; From 355570a439462319e989813ae0e86da357eca8a5 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 12:57:08 -0800 Subject: [PATCH 13/24] Cleanup, fmt, clippy --- compiler/ast/src/expressions/access/mod.rs | 2 +- compiler/ast/src/passes/reconstructor.rs | 6 +-- compiler/ast/src/passes/visitor.rs | 16 ++++++-- compiler/parser/src/parser/expression.rs | 14 ++++--- .../src/code_generation/visit_expressions.rs | 4 +- .../src/code_generation/visit_program.rs | 11 +++--- .../src/code_generation/visit_statements.rs | 2 +- .../src/flattening/flatten_expression.rs | 5 ++- .../rename_expression.rs | 1 - .../src/type_checking/check_expressions.rs | 37 ++++++++++--------- 10 files changed, 59 insertions(+), 39 deletions(-) diff --git a/compiler/ast/src/expressions/access/mod.rs b/compiler/ast/src/expressions/access/mod.rs index 5d5427aba8..fe194553c9 100644 --- a/compiler/ast/src/expressions/access/mod.rs +++ b/compiler/ast/src/expressions/access/mod.rs @@ -23,7 +23,7 @@ pub use member_access::*; pub mod tuple_access; pub use tuple_access::*; -use crate::{Node}; +use crate::Node; use leo_span::Span; diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index 648704aaec..d73b3df230 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -60,7 +60,7 @@ pub trait ExpressionReconstructor { .collect(), span: input.span, })), - Default::default() + Default::default(), ) } @@ -115,7 +115,7 @@ pub trait ExpressionReconstructor { name: input.name, span: input.span, })), - Default::default() + Default::default(), ) } @@ -152,7 +152,7 @@ pub trait ExpressionReconstructor { index: input.index, span: input.span, })), - Default::default() + Default::default(), ) } diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 390f65af2b..6e415b0a96 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -63,7 +63,9 @@ pub trait ExpressionVisitor<'a> { input: &'a AssociatedFunction, additional: &Self::AdditionalInput, ) -> Self::ExpressionOutput { - input.args.iter().for_each(|arg| { self.visit_expression(arg, additional); }); + input.args.iter().for_each(|arg| { + self.visit_expression(arg, additional); + }); Default::default() } @@ -108,7 +110,11 @@ pub trait ExpressionVisitor<'a> { Default::default() } - fn visit_member_access(&mut self, input: &'a MemberAccess, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_member_access( + &mut self, + input: &'a MemberAccess, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.inner, additional); Default::default() } @@ -135,7 +141,11 @@ pub trait ExpressionVisitor<'a> { Default::default() } - fn visit_tuple_access(&mut self, input: &'a TupleAccess, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_tuple_access( + &mut self, + input: &'a TupleAccess, + additional: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { self.visit_expression(&input.tuple, additional); Default::default() } diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index 808ac45c74..20d0b068fb 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -324,12 +324,14 @@ impl ParserContext<'_> { let (args, _, end) = self.parse_expr_tuple()?; // Return the struct function. - Ok(Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction { - span: module_name.span() + end, - ty: type_, - name: member_name, - args, - }))) + Ok(Expression::Access(AccessExpression::AssociatedFunction( + AssociatedFunction { + span: module_name.span() + end, + ty: type_, + name: member_name, + args, + }, + ))) } else { // Attempted to parse an associated constant, e.g `Foo::MAX`. // These are not supported in the Leo language. diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index f6ce1a9215..2e120b5fbd 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -282,7 +282,9 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { match input { AccessExpression::Member(access) => self.visit_member_access(access, additional), AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), - AccessExpression::Tuple(_) => unreachable!("Tuples should have been flattened in previous compiler passes."), + AccessExpression::Tuple(_) => { + unreachable!("Tuples should have been flattened in previous compiler passes.") + } } } diff --git a/compiler/passes/src/code_generation/visit_program.rs b/compiler/passes/src/code_generation/visit_program.rs index 85e19eb91b..3e786c4641 100644 --- a/compiler/passes/src/code_generation/visit_program.rs +++ b/compiler/passes/src/code_generation/visit_program.rs @@ -22,7 +22,7 @@ use leo_ast::{ use indexmap::IndexMap; use itertools::Itertools; -use leo_span::{sym}; +use leo_span::sym; use std::fmt::Write as _; impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { @@ -144,11 +144,11 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { " {} as {}.{mode};", // todo: CAUTION private record variables only. var.identifier, var.type_ ) - .expect("failed to write to string"); + .expect("failed to write to string"); } output_string - }, + } false => { // Add private symbol to composite types. self.composite_mapping @@ -158,11 +158,12 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { // Construct and append the record variables. for var in struct_.members.iter() { - writeln!(output_string, " {} as {};", var.identifier, var.type_,).expect("failed to write to string"); + writeln!(output_string, " {} as {};", var.identifier, var.type_,) + .expect("failed to write to string"); } output_string - }, + } } } diff --git a/compiler/passes/src/code_generation/visit_statements.rs b/compiler/passes/src/code_generation/visit_statements.rs index 61f116fb25..5372342021 100644 --- a/compiler/passes/src/code_generation/visit_statements.rs +++ b/compiler/passes/src/code_generation/visit_statements.rs @@ -19,7 +19,7 @@ use crate::CodeGenerator; use leo_ast::{ AssertStatement, AssertVariant, AssignStatement, Block, ConditionalStatement, ConsoleStatement, DecrementStatement, DefinitionStatement, Expression, ExpressionStatement, ExpressionVisitor, IncrementStatement, IterationStatement, - Mode, ReturnStatement, Output, StatementVisitor, + Mode, Output, ReturnStatement, StatementVisitor, }; use itertools::Itertools; diff --git a/compiler/passes/src/flattening/flatten_expression.rs b/compiler/passes/src/flattening/flatten_expression.rs index d43fcffd0b..6b7b0a7a21 100644 --- a/compiler/passes/src/flattening/flatten_expression.rs +++ b/compiler/passes/src/flattening/flatten_expression.rs @@ -17,7 +17,10 @@ use crate::Flattener; use itertools::Itertools; -use leo_ast::{AccessExpression, AssociatedFunction, Expression, ExpressionReconstructor, Member, MemberAccess, Statement, StructExpression, StructVariableInitializer, TernaryExpression, TupleAccess, TupleExpression}; +use leo_ast::{ + AccessExpression, Expression, ExpressionReconstructor, Member, MemberAccess, Statement, StructExpression, + StructVariableInitializer, TernaryExpression, TupleAccess, TupleExpression, +}; // TODO: Clean up logic and verify statement accumulation. To be done in a follow-up PR diff --git a/compiler/passes/src/static_single_assignment/rename_expression.rs b/compiler/passes/src/static_single_assignment/rename_expression.rs index bd7bb54881..b09dfec356 100644 --- a/compiler/passes/src/static_single_assignment/rename_expression.rs +++ b/compiler/passes/src/static_single_assignment/rename_expression.rs @@ -82,7 +82,6 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> { statements, ) } - expr => (expr, Vec::new()), }; let (place, statement) = self.assigner.unique_simple_assign_statement(Expression::Access(expr)); statements.push(statement); diff --git a/compiler/passes/src/type_checking/check_expressions.rs b/compiler/passes/src/type_checking/check_expressions.rs index fca54f8256..f250859867 100644 --- a/compiler/passes/src/type_checking/check_expressions.rs +++ b/compiler/passes/src/type_checking/check_expressions.rs @@ -45,7 +45,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { type AdditionalInput = Option; type ExpressionOutput = Option; - fn visit_associated_function(&mut self, input: &'a AssociatedFunction, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_associated_function( + &mut self, + input: &'a AssociatedFunction, + expected: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { // Check core struct name and function. if let Some(core_instruction) = self.check_core_function_call(&input.ty, &input.name) { // Check num input arguments. @@ -92,7 +96,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_member_access(&mut self, input: &'a MemberAccess, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_member_access( + &mut self, + input: &'a MemberAccess, + expected: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { match *input.inner { // If the access expression is of the form `self.`, then check the is valid. Expression::Identifier(identifier) if identifier.name == sym::SelfLower => match input.name.name { @@ -113,11 +121,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { // Case where `access.name` is a member of the struct. Some(Member { type_, .. }) => { // Check that the type of `access.name` is the same as `expected`. - return Some(self.assert_and_return_type( - type_.clone(), - expected, - input.span(), - )); + return Some(self.assert_and_return_type(type_.clone(), expected, input.span())); } // Case where `access.name` is not a member of the struct. None => { @@ -147,7 +151,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_tuple_access(&mut self, input: &'a TupleAccess, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_tuple_access( + &mut self, + input: &'a TupleAccess, + expected: &Self::AdditionalInput, + ) -> Self::ExpressionOutput { if let Some(type_) = self.visit_expression(&input.tuple, &None) { match type_ { Type::Tuple(tuple) => { @@ -161,11 +169,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { if let Some(expected) = expected { // Emit error for mismatched types. if !actual.eq_flat(expected) { - self.emit_err(TypeCheckerError::type_should_be( - &actual, - expected, - input.span(), - )) + self.emit_err(TypeCheckerError::type_should_be(&actual, expected, input.span())) } } @@ -182,16 +186,15 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_access( &mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput, ) -> Self::ExpressionOutput { match input { - AccessExpression::AssociatedFunction(access) => self.visit_associated_function(access, &expected), - AccessExpression::Tuple(access) => self.visit_tuple_access(access, &expected), - AccessExpression::Member(access) => self.visit_member_access(access, &expected), + AccessExpression::AssociatedFunction(access) => self.visit_associated_function(access, expected), + AccessExpression::Tuple(access) => self.visit_tuple_access(access, expected), + AccessExpression::Member(access) => self.visit_member_access(access, expected), } } From 34f56a30094dfd9f1d97d49db850773ac2a48611 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 13:09:05 -0800 Subject: [PATCH 14/24] Update parser error --- errors/src/errors/parser/parser_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/src/errors/parser/parser_errors.rs b/errors/src/errors/parser/parser_errors.rs index f44c44a61d..94a76f7e2b 100644 --- a/errors/src/errors/parser/parser_errors.rs +++ b/errors/src/errors/parser/parser_errors.rs @@ -210,7 +210,7 @@ create_messages!( @formatted invalid_associated_access { args: (name: impl Display), - msg: format!("Invalid associated access to struct {name}."), + msg: format!("Invalid associated access to {name}."), help: Some("Double colon `::` syntax is only supported for core functions in Leo for testnet3.".to_string()), } From dbfe07b9de120b3a9cbb8dc2b4d0e5aa7d79c61d Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 13:13:34 -0800 Subject: [PATCH 15/24] Regen expectations --- tests/expectations/parser/unreachable/expect_ident.out | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/expectations/parser/unreachable/expect_ident.out b/tests/expectations/parser/unreachable/expect_ident.out index f14f2acbe1..042ffe6f60 100644 --- a/tests/expectations/parser/unreachable/expect_ident.out +++ b/tests/expectations/parser/unreachable/expect_ident.out @@ -30,9 +30,9 @@ outputs: - "Error [EPAR0370009]: unexpected string: expected 'identifier', found '<='\n --> test:1:4\n |\n 1 | x::<=\n | ^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found '>'\n --> test:1:4\n |\n 1 | x::>\n | ^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found '..'\n --> test:1:4\n |\n 1 | x::..\n | ^^" - - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:4\n |\n 1 | x::as\n | ^^" + - "Error [EPAR0370022]: Invalid associated access to x.\n --> test:1:1\n |\n 1 | x::as\n | ^\n |\n = Double colon `::` syntax is only supported for core functions in Leo for testnet3." - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'console'\n --> test:1:4\n |\n 1 | x::console\n | ^^^^^^^" - - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:4\n |\n 1 | x::const\n | ^^^^^" + - "Error [EPAR0370022]: Invalid associated access to x.\n --> test:1:1\n |\n 1 | x::const\n | ^\n |\n = Double colon `::` syntax is only supported for core functions in Leo for testnet3." - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'let'\n --> test:1:4\n |\n 1 | x::let\n | ^^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'for'\n --> test:1:4\n |\n 1 | x::for\n | ^^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'if'\n --> test:1:4\n |\n 1 | x::if\n | ^^" @@ -50,7 +50,7 @@ outputs: - "Error [EPAR0370009]: unexpected string: expected 'identifier', found '&'\n --> test:1:4\n |\n 1 | x::&\n | ^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'return'\n --> test:1:4\n |\n 1 | x::return\n | ^^^^^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'self'\n --> test:1:4\n |\n 1 | x::self\n | ^^^^" - - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:4\n |\n 1 | x::Self\n | ^^^^" + - "Error [EPAR0370022]: Invalid associated access to x.\n --> test:1:1\n |\n 1 | x::Self\n | ^\n |\n = Double colon `::` syntax is only supported for core functions in Leo for testnet3." - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'true'\n --> test:1:4\n |\n 1 | x::true\n | ^^^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'false'\n --> test:1:4\n |\n 1 | x::false\n | ^^^^^" - "Error [EPAR0370009]: unexpected string: expected 'identifier', found '0'\n --> test:1:4\n |\n 1 | x::0\n | ^" From 6624c69e0118121b172e333cfcc481984e4b77c0 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 17:42:36 -0800 Subject: [PATCH 16/24] Revert to simplified visitor --- compiler/ast/src/passes/visitor.rs | 169 ++++++++--------------------- 1 file changed, 47 insertions(+), 122 deletions(-) diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 6e415b0a96..f2a1bea24c 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -23,13 +23,9 @@ use crate::*; /// A Visitor trait for expressions in the AST. pub trait ExpressionVisitor<'a> { type AdditionalInput: Default; - type ExpressionOutput: Default; + type Output: Default; - fn visit_expression( - &mut self, - input: &'a Expression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_expression(&mut self, input: &'a Expression, additional: &Self::AdditionalInput) -> Self::Output { match input { Expression::Access(access) => self.visit_access(access, additional), Expression::Binary(binary) => self.visit_binary(binary, additional), @@ -45,11 +41,7 @@ pub trait ExpressionVisitor<'a> { } } - fn visit_access( - &mut self, - input: &'a AccessExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_access(&mut self, input: &'a AccessExpression, additional: &Self::AdditionalInput) -> Self::Output { match input { AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), AccessExpression::Member(member) => self.visit_member_access(member, additional), @@ -62,117 +54,79 @@ pub trait ExpressionVisitor<'a> { &mut self, input: &'a AssociatedFunction, additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + ) -> Self::Output { input.args.iter().for_each(|arg| { self.visit_expression(arg, additional); }); Default::default() } - fn visit_binary( - &mut self, - input: &'a BinaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_binary(&mut self, input: &'a BinaryExpression, additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.left, additional); self.visit_expression(&input.right, additional); Default::default() } - fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::Output { input.arguments.iter().for_each(|expr| { self.visit_expression(expr, additional); }); Default::default() } - fn visit_struct_init( - &mut self, - _input: &'a StructExpression, - _additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_struct_init(&mut self, _input: &'a StructExpression, _additional: &Self::AdditionalInput) -> Self::Output { Default::default() } - fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output { unreachable!("`ErrExpression`s should not be in the AST at this phase of compilation.") } - fn visit_identifier( - &mut self, - _input: &'a Identifier, - _additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output { Default::default() } - fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output { Default::default() } - fn visit_member_access( - &mut self, - input: &'a MemberAccess, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_member_access(&mut self, input: &'a MemberAccess, additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.inner, additional); Default::default() } - fn visit_ternary( - &mut self, - input: &'a TernaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_ternary(&mut self, input: &'a TernaryExpression, additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.condition, additional); self.visit_expression(&input.if_true, additional); self.visit_expression(&input.if_false, additional); Default::default() } - fn visit_tuple( - &mut self, - input: &'a TupleExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_tuple(&mut self, input: &'a TupleExpression, additional: &Self::AdditionalInput) -> Self::Output { input.elements.iter().for_each(|expr| { self.visit_expression(expr, additional); }); Default::default() } - fn visit_tuple_access( - &mut self, - input: &'a TupleAccess, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_tuple_access(&mut self, input: &'a TupleAccess, additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.tuple, additional); Default::default() } - fn visit_unary( - &mut self, - input: &'a UnaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_unary(&mut self, input: &'a UnaryExpression, additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.receiver, additional); Default::default() } - fn visit_unit( - &mut self, - _input: &'a UnitExpression, - _additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_unit(&mut self, _input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output { Default::default() } } /// A Visitor trait for statements in the AST. pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { - type StatementOutput: Default; - - fn visit_statement(&mut self, input: &'a Statement) -> Self::StatementOutput { + fn visit_statement(&mut self, input: &'a Statement) { match input { Statement::Assert(stmt) => self.visit_assert(stmt), Statement::Assign(stmt) => self.visit_assign(stmt), @@ -188,7 +142,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { } } - fn visit_assert(&mut self, input: &'a AssertStatement) -> Self::StatementOutput { + fn visit_assert(&mut self, input: &'a AssertStatement) { match &input.variant { AssertVariant::Assert(expr) => self.visit_expression(expr, &Default::default()), AssertVariant::AssertEq(left, right) | AssertVariant::AssertNeq(left, right) => { @@ -196,31 +150,25 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { self.visit_expression(right, &Default::default()) } }; - Default::default() } - fn visit_assign(&mut self, input: &'a AssignStatement) -> Self::StatementOutput { + fn visit_assign(&mut self, input: &'a AssignStatement) { self.visit_expression(&input.value, &Default::default()); - Default::default() } - fn visit_block(&mut self, input: &'a Block) -> Self::StatementOutput { - input.statements.iter().for_each(|stmt| { - self.visit_statement(stmt); - }); - Default::default() + fn visit_block(&mut self, input: &'a Block) { + input.statements.iter().for_each(|stmt| self.visit_statement(stmt)); } - fn visit_conditional(&mut self, input: &'a ConditionalStatement) -> Self::StatementOutput { + fn visit_conditional(&mut self, input: &'a ConditionalStatement) { self.visit_expression(&input.condition, &Default::default()); self.visit_block(&input.then); if let Some(stmt) = input.otherwise.as_ref() { self.visit_statement(stmt); } - Default::default() } - fn visit_console(&mut self, input: &'a ConsoleStatement) -> Self::StatementOutput { + fn visit_console(&mut self, input: &'a ConsoleStatement) { match &input.function { ConsoleFunction::Assert(expr) => { self.visit_expression(expr, &Default::default()); @@ -234,101 +182,78 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { self.visit_expression(right, &Default::default()); } }; - Default::default() } - fn visit_decrement(&mut self, input: &'a DecrementStatement) -> Self::StatementOutput { + fn visit_decrement(&mut self, input: &'a DecrementStatement) { self.visit_expression(&input.amount, &Default::default()); self.visit_expression(&input.index, &Default::default()); self.visit_identifier(&input.mapping, &Default::default()); - Default::default() } - fn visit_definition(&mut self, input: &'a DefinitionStatement) -> Self::StatementOutput { + fn visit_definition(&mut self, input: &'a DefinitionStatement) { self.visit_expression(&input.value, &Default::default()); - Default::default() } - fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> Self::StatementOutput { + fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) { self.visit_expression(&input.expression, &Default::default()); - Default::default() } - fn visit_increment(&mut self, input: &'a IncrementStatement) -> Self::StatementOutput { + fn visit_increment(&mut self, input: &'a IncrementStatement) { self.visit_expression(&input.amount, &Default::default()); self.visit_expression(&input.index, &Default::default()); self.visit_identifier(&input.mapping, &Default::default()); - Default::default() } - fn visit_iteration(&mut self, input: &'a IterationStatement) -> Self::StatementOutput { + fn visit_iteration(&mut self, input: &'a IterationStatement) { self.visit_expression(&input.start, &Default::default()); self.visit_expression(&input.stop, &Default::default()); self.visit_block(&input.block); - Default::default() } - fn visit_return(&mut self, input: &'a ReturnStatement) -> Self::StatementOutput { + fn visit_return(&mut self, input: &'a ReturnStatement) { self.visit_expression(&input.expression, &Default::default()); if let Some(arguments) = &input.finalize_arguments { arguments.iter().for_each(|argument| { self.visit_expression(argument, &Default::default()); }) } - Default::default() } } /// A Visitor trait for the program represented by the AST. pub trait ProgramVisitor<'a>: StatementVisitor<'a> { - type ProgramOutput: Default; - - fn visit_program(&mut self, input: &'a Program) -> Self::ProgramOutput { - input.imports.values().for_each(|import| { - self.visit_import(&import.0); - }); - - input.program_scopes.values().for_each(|scope| { - self.visit_program_scope(scope); - }); + fn visit_program(&mut self, input: &'a Program) { + input.imports.values().for_each(|import| self.visit_import(&import.0)); - Default::default() + input + .program_scopes + .values() + .for_each(|scope| self.visit_program_scope(scope)); } - fn visit_program_scope(&mut self, input: &'a ProgramScope) -> Self::ProgramOutput { - input.structs.values().for_each(|function| { - self.visit_struct(function); - }); - - input.mappings.values().for_each(|mapping| { - self.visit_mapping(mapping); - }); + fn visit_program_scope(&mut self, input: &'a ProgramScope) { + input.structs.values().for_each(|function| self.visit_struct(function)); - input.functions.values().for_each(|function| { - self.visit_function(function); - }); + input.mappings.values().for_each(|mapping| self.visit_mapping(mapping)); - Default::default() + input + .functions + .values() + .for_each(|function| self.visit_function(function)); } - fn visit_import(&mut self, input: &'a Program) -> Self::ProgramOutput { - self.visit_program(input); - Default::default() + fn visit_import(&mut self, input: &'a Program) { + self.visit_program(input) } - fn visit_struct(&mut self, _input: &'a Struct) -> Self::ProgramOutput { - Default::default() - } + fn visit_struct(&mut self, _input: &'a Struct) {} - fn visit_mapping(&mut self, _input: &'a Mapping) -> Self::ProgramOutput { - Default::default() - } + fn visit_mapping(&mut self, _input: &'a Mapping) {} - fn visit_function(&mut self, input: &'a Function) -> Self::ProgramOutput { + fn visit_function(&mut self, input: &'a Function) { self.visit_block(&input.block); if let Some(finalize) = &input.finalize { self.visit_block(&finalize.block); } - Default::default() } } From 71853cce414f8593011cb9ec2803cae2f5b7a510 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 17:55:36 -0800 Subject: [PATCH 17/24] Update Consumer --- compiler/ast/src/passes/consumer.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/ast/src/passes/consumer.rs b/compiler/ast/src/passes/consumer.rs index 2ebcf04667..bbae4b1ca0 100644 --- a/compiler/ast/src/passes/consumer.rs +++ b/compiler/ast/src/passes/consumer.rs @@ -39,7 +39,15 @@ pub trait ExpressionConsumer { } } - fn consume_access(&mut self, _input: AccessExpression) -> Self::Output; + fn consume_access(&mut self, input: AccessExpression) -> Self::Output { + match input { + AccessExpression::AssociatedFunction(function) => self.consume_associated_function(function), + AccessExpression::Member(member) => self.consume_member_access(member), + AccessExpression::Tuple(tuple) => self.consume_tuple_access(tuple), + } + } + + fn consume_associated_function(&mut self, _input: AssociatedFunction) -> Self::Output; fn consume_binary(&mut self, _input: BinaryExpression) -> Self::Output; @@ -55,10 +63,14 @@ pub trait ExpressionConsumer { fn consume_literal(&mut self, _input: Literal) -> Self::Output; + fn consume_member_access(&mut self, _input: MemberAccess) -> Self::Output; + fn consume_ternary(&mut self, _input: TernaryExpression) -> Self::Output; fn consume_tuple(&mut self, _input: TupleExpression) -> Self::Output; + fn consume_tuple_access(&mut self, _input: TupleAccess) -> Self::Output; + fn consume_unary(&mut self, _input: UnaryExpression) -> Self::Output; fn consume_unit(&mut self, _input: UnitExpression) -> Self::Output; From 2fb21fb2cb68211e0a6552e9005e7685adcdbfe8 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:09:36 -0800 Subject: [PATCH 18/24] Fix symbol table creation --- compiler/passes/src/symbol_table_creation/creator.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/passes/src/symbol_table_creation/creator.rs b/compiler/passes/src/symbol_table_creation/creator.rs index fbb063f3b3..d837d7e395 100644 --- a/compiler/passes/src/symbol_table_creation/creator.rs +++ b/compiler/passes/src/symbol_table_creation/creator.rs @@ -40,16 +40,12 @@ impl<'a> SymbolTableCreator<'a> { impl<'a> ExpressionVisitor<'a> for SymbolTableCreator<'a> { type AdditionalInput = (); - type ExpressionOutput = (); + type Output = (); } -impl<'a> StatementVisitor<'a> for SymbolTableCreator<'a> { - type StatementOutput = (); -} +impl<'a> StatementVisitor<'a> for SymbolTableCreator<'a> {} impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { - type ProgramOutput = (); - fn visit_import(&mut self, input: &'a Program) { self.visit_program(input) } From 369b735e125147c2fd7a8620b0e39831dcc04923 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:09:53 -0800 Subject: [PATCH 19/24] Fix tyc pass --- .../src/type_checking/check_expressions.rs | 58 +++++-------------- .../passes/src/type_checking/check_program.rs | 2 - .../src/type_checking/check_statements.rs | 2 - 3 files changed, 15 insertions(+), 47 deletions(-) diff --git a/compiler/passes/src/type_checking/check_expressions.rs b/compiler/passes/src/type_checking/check_expressions.rs index f250859867..2455b05c76 100644 --- a/compiler/passes/src/type_checking/check_expressions.rs +++ b/compiler/passes/src/type_checking/check_expressions.rs @@ -43,13 +43,13 @@ fn return_incorrect_type(t1: Option, t2: Option, expected: &Option ExpressionVisitor<'a> for TypeChecker<'a> { type AdditionalInput = Option; - type ExpressionOutput = Option; + type Output = Option; fn visit_associated_function( &mut self, input: &'a AssociatedFunction, expected: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + ) -> Self::Output { // Check core struct name and function. if let Some(core_instruction) = self.check_core_function_call(&input.ty, &input.name) { // Check num input arguments. @@ -96,11 +96,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_member_access( - &mut self, - input: &'a MemberAccess, - expected: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_member_access(&mut self, input: &'a MemberAccess, expected: &Self::AdditionalInput) -> Self::Output { match *input.inner { // If the access expression is of the form `self.`, then check the is valid. Expression::Identifier(identifier) if identifier.name == sym::SelfLower => match input.name.name { @@ -151,11 +147,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_tuple_access( - &mut self, - input: &'a TupleAccess, - expected: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_tuple_access(&mut self, input: &'a TupleAccess, expected: &Self::AdditionalInput) -> Self::Output { if let Some(type_) = self.visit_expression(&input.tuple, &None) { match type_ { Type::Tuple(tuple) => { @@ -186,11 +178,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { None } - fn visit_access( - &mut self, - input: &'a AccessExpression, - expected: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_access(&mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput) -> Self::Output { match input { AccessExpression::AssociatedFunction(access) => self.visit_associated_function(access, expected), AccessExpression::Tuple(access) => self.visit_tuple_access(access, expected), @@ -198,11 +186,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_binary( - &mut self, - input: &'a BinaryExpression, - destination: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_binary(&mut self, input: &'a BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output { match input.op { BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => { // Only boolean types. @@ -456,7 +440,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::Output { match &*input.function { // Note that the parser guarantees that `input.function` is always an identifier. Expression::Identifier(ident) => { @@ -525,11 +509,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_struct_init( - &mut self, - input: &'a StructExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_struct_init(&mut self, input: &'a StructExpression, additional: &Self::AdditionalInput) -> Self::Output { let struct_ = self.symbol_table.borrow().lookup_struct(input.name.name).cloned(); if let Some(struct_) = struct_ { // Check struct type name. @@ -579,11 +559,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } // We do not want to panic on `ErrExpression`s in order to propagate as many errors as possible. - fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output { Default::default() } - fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output { if let Some(var) = self.symbol_table.borrow().lookup_variable(input.name) { Some(self.assert_and_return_type(var.type_.clone(), expected, input.span())) } else { @@ -592,7 +572,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output { fn parse_integer_literal(handler: &Handler, string: &String, span: Span, type_string: &str) { if string.parse::().is_err() { handler.emit_err(TypeCheckerError::invalid_int_value(string, type_string, span)); @@ -654,11 +634,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { }) } - fn visit_ternary( - &mut self, - input: &'a TernaryExpression, - expected: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_ternary(&mut self, input: &'a TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.condition, &Some(Type::Boolean)); let t1 = self.visit_expression(&input.if_true, expected); @@ -667,7 +643,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { return_incorrect_type(t1, t2, expected) } - fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output { match input.elements.len() { 0 | 1 => unreachable!("Parsing guarantees that tuple expressions have at least two elements."), _ => { @@ -704,11 +680,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_unary( - &mut self, - input: &'a UnaryExpression, - destination: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output { match input.op { UnaryOperation::Abs => { // Only signed integer types. @@ -755,7 +727,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { } } - fn visit_unit(&mut self, input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + 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( diff --git a/compiler/passes/src/type_checking/check_program.rs b/compiler/passes/src/type_checking/check_program.rs index b677507fa5..c8af36d342 100644 --- a/compiler/passes/src/type_checking/check_program.rs +++ b/compiler/passes/src/type_checking/check_program.rs @@ -26,8 +26,6 @@ use std::collections::HashSet; // TODO: Cleanup logic for tuples. impl<'a> ProgramVisitor<'a> for TypeChecker<'a> { - type ProgramOutput = (); - fn visit_program(&mut self, input: &'a Program) { match self.is_imported { // If the program is imported, then it is not allowed to import any other programs. diff --git a/compiler/passes/src/type_checking/check_statements.rs b/compiler/passes/src/type_checking/check_statements.rs index c0cdea432a..5c6f6a475f 100644 --- a/compiler/passes/src/type_checking/check_statements.rs +++ b/compiler/passes/src/type_checking/check_statements.rs @@ -22,8 +22,6 @@ use leo_errors::TypeCheckerError; use leo_span::{Span, Symbol}; impl<'a> StatementVisitor<'a> for TypeChecker<'a> { - type StatementOutput = (); - fn visit_statement(&mut self, input: &'a Statement) { // No statements can follow a return statement. if self.has_return { From fa0b6064418e22fa29813ae830b6ca016d882588 Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:10:12 -0800 Subject: [PATCH 20/24] Fix SSA pass --- .../rename_expression.rs | 108 +++++++++--------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/compiler/passes/src/static_single_assignment/rename_expression.rs b/compiler/passes/src/static_single_assignment/rename_expression.rs index b09dfec356..c2521cfdd3 100644 --- a/compiler/passes/src/static_single_assignment/rename_expression.rs +++ b/compiler/passes/src/static_single_assignment/rename_expression.rs @@ -32,63 +32,37 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> { /// Consumes an access expression, accumulating any statements that are generated. fn consume_access(&mut self, input: AccessExpression) -> Self::Output { let (expr, mut statements) = match input { - AccessExpression::AssociatedFunction(function) => { - let mut statements = Vec::new(); - ( - AccessExpression::AssociatedFunction(AssociatedFunction { - ty: function.ty, - name: function.name, - args: function - .args - .into_iter() - .map(|arg| { - let (arg, mut stmts) = self.consume_expression(arg); - statements.append(&mut stmts); - arg - }) - .collect(), - span: function.span, - }), - statements, - ) - } - AccessExpression::Member(member) => { - // TODO: Create AST node for native access expressions? - // If the access expression is of the form `self.`, then don't rename it. - if let Expression::Identifier(Identifier { name, .. }) = *member.inner { - if name == sym::SelfLower { - return (Expression::Access(AccessExpression::Member(member)), Vec::new()); - } - } - - let (expr, statements) = self.consume_expression(*member.inner); - ( - AccessExpression::Member(MemberAccess { - inner: Box::new(expr), - name: member.name, - span: member.span, - }), - statements, - ) - } - AccessExpression::Tuple(tuple) => { - let (expr, statements) = self.consume_expression(*tuple.tuple); - ( - AccessExpression::Tuple(TupleAccess { - tuple: Box::new(expr), - index: tuple.index, - span: tuple.span, - }), - statements, - ) - } + AccessExpression::AssociatedFunction(function) => self.consume_associated_function(function), + AccessExpression::Member(member) => self.consume_member_access(member), + AccessExpression::Tuple(tuple) => self.consume_tuple_access(tuple), }; - let (place, statement) = self.assigner.unique_simple_assign_statement(Expression::Access(expr)); + let (place, statement) = self.assigner.unique_simple_assign_statement(expr); statements.push(statement); (Expression::Identifier(place), statements) } + fn consume_associated_function(&mut self, input: AssociatedFunction) -> Self::Output { + let mut statements = Vec::new(); + ( + Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction { + ty: input.ty, + name: input.name, + args: input + .args + .into_iter() + .map(|arg| { + let (arg, mut stmts) = self.consume_expression(arg); + statements.append(&mut stmts); + arg + }) + .collect(), + span: input.span, + })), + statements, + ) + } + /// Consumes a binary expression, accumulating any statements that are generated. fn consume_binary(&mut self, input: BinaryExpression) -> Self::Output { // Reconstruct the lhs of the binary expression. @@ -250,6 +224,26 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> { (Expression::Literal(input), Default::default()) } + fn consume_member_access(&mut self, input: MemberAccess) -> Self::Output { + // TODO: Create AST node for native access expressions? + // If the access expression is of the form `self.`, then don't rename it. + if let Expression::Identifier(Identifier { name, .. }) = *input.inner { + if name == sym::SelfLower { + return (Expression::Access(AccessExpression::Member(input)), Vec::new()); + } + } + + let (expr, statements) = self.consume_expression(*input.inner); + ( + Expression::Access(AccessExpression::Member(MemberAccess { + inner: Box::new(expr), + name: input.name, + span: input.span, + })), + statements, + ) + } + /// Consumes a ternary expression, accumulating any statements that are generated. fn consume_ternary(&mut self, input: TernaryExpression) -> Self::Output { // Reconstruct the condition of the ternary expression. @@ -304,6 +298,18 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> { (Expression::Identifier(place), statements) } + fn consume_tuple_access(&mut self, input: TupleAccess) -> Self::Output { + let (expr, statements) = self.consume_expression(*input.tuple); + ( + Expression::Access(AccessExpression::Tuple(TupleAccess { + tuple: Box::new(expr), + index: input.index, + span: input.span, + })), + statements, + ) + } + /// Consumes a unary expression, accumulating any statements that are generated. fn consume_unary(&mut self, input: UnaryExpression) -> Self::Output { // Reconstruct the operand of the unary expression. From fc7e764b50c0de491a98ecc03e15103d2e4faafe Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:10:34 -0800 Subject: [PATCH 21/24] Refactor code generation to use Consumer --- ...expressions.rs => generate_expressions.rs} | 126 +++---- .../{visit_program.rs => generate_program.rs} | 338 ++++++++++-------- ...t_statements.rs => generate_statements.rs} | 93 ++--- .../passes/src/code_generation/generator.rs | 47 ++- compiler/passes/src/code_generation/mod.rs | 14 +- .../passes/src/code_generation/visit_type.rs | 56 --- .../common/symbol_table/function_symbol.rs | 8 +- 7 files changed, 328 insertions(+), 354 deletions(-) rename compiler/passes/src/code_generation/{visit_expressions.rs => generate_expressions.rs} (74%) rename compiler/passes/src/code_generation/{visit_program.rs => generate_program.rs} (78%) rename compiler/passes/src/code_generation/{visit_statements.rs => generate_statements.rs} (68%) delete mode 100644 compiler/passes/src/code_generation/visit_type.rs diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/generate_expressions.rs similarity index 74% rename from compiler/passes/src/code_generation/visit_expressions.rs rename to compiler/passes/src/code_generation/generate_expressions.rs index 2e120b5fbd..85538ed960 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/generate_expressions.rs @@ -15,47 +15,35 @@ // along with the Leo library. If not, see . use crate::CodeGenerator; + use leo_ast::{ AccessExpression, AssociatedFunction, BinaryExpression, BinaryOperation, CallExpression, ErrExpression, Expression, - ExpressionVisitor, Identifier, Literal, MemberAccess, StructExpression, TernaryExpression, TupleExpression, Type, - UnaryExpression, UnaryOperation, UnitExpression, + ExpressionConsumer, Identifier, Literal, MemberAccess, StructExpression, TernaryExpression, TupleAccess, + TupleExpression, Type, UnaryExpression, UnaryOperation, UnitExpression, }; use leo_span::sym; use std::borrow::Borrow; use std::fmt::Write as _; -/// Implement the necessary methods to visit nodes in the AST. -// Note: We opt for this option instead of using `Visitor` and `Director` because this pass requires -// a post-order traversal of the AST. This is sufficient since this implementation is intended to be -// a prototype. The production implementation will require a redesign of `Director`. -impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { - type AdditionalInput = (); - type ExpressionOutput = (String, String); - - fn visit_identifier( - &mut self, - input: &'a Identifier, - _additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { +impl ExpressionConsumer for CodeGenerator<'_> { + type Output = (String, String); + + fn consume_identifier(&mut self, input: Identifier) -> Self::Output { (self.variable_mapping.get(&input.name).unwrap().clone(), String::new()) } - fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn consume_err(&mut self, _input: ErrExpression) -> Self::Output { unreachable!("`ErrExpression`s should not be in the AST at this phase of compilation.") } - fn visit_literal(&mut self, input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn consume_literal(&mut self, input: Literal) -> Self::Output { (format!("{input}"), String::new()) } - fn visit_binary( - &mut self, - input: &'a BinaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { - let (left_operand, left_instructions) = self.visit_expression(&input.left, additional); - let (right_operand, right_instructions) = self.visit_expression(&input.right, additional); + fn consume_binary(&mut self, input: BinaryExpression) -> Self::Output { + let (left_operand, left_instructions) = self.consume_expression(*input.left); + let (right_operand, right_instructions) = self.consume_expression(*input.right); let opcode = match input.op { BinaryOperation::Add => String::from("add"), @@ -104,12 +92,8 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (destination_register, instructions) } - fn visit_unary( - &mut self, - input: &'a UnaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { - let (expression_operand, expression_instructions) = self.visit_expression(&input.receiver, additional); + fn consume_unary(&mut self, input: UnaryExpression) -> Self::Output { + let (expression_operand, expression_instructions) = self.consume_expression(*input.receiver); let opcode = match input.op { UnaryOperation::Abs => String::from("abs"), @@ -135,14 +119,10 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (destination_register, instructions) } - fn visit_ternary( - &mut self, - input: &'a TernaryExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { - let (condition_operand, condition_instructions) = self.visit_expression(&input.condition, additional); - let (if_true_operand, if_true_instructions) = self.visit_expression(&input.if_true, additional); - let (if_false_operand, if_false_instructions) = self.visit_expression(&input.if_false, additional); + fn consume_ternary(&mut self, input: TernaryExpression) -> Self::Output { + let (condition_operand, condition_instructions) = self.consume_expression(*input.condition); + let (if_true_operand, if_true_instructions) = self.consume_expression(*input.if_true); + let (if_false_operand, if_false_instructions) = self.consume_expression(*input.if_false); let destination_register = format!("r{}", self.next_register); let ternary_instruction = format!( @@ -161,11 +141,7 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (destination_register, instructions) } - fn visit_struct_init( - &mut self, - input: &'a StructExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn consume_struct_init(&mut self, input: StructExpression) -> Self::Output { // Lookup struct or record. let name = if let Some((is_record, type_)) = self.composite_mapping.get(&input.name.name) { if *is_record { @@ -184,16 +160,16 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { let mut struct_init_instruction = String::from(" cast "); // Visit each struct member and accumulate instructions from expressions. - for member in input.members.iter() { - let operand = if let Some(expr) = member.expression.as_ref() { + for member in input.members.into_iter() { + let operand = if let Some(expr) = member.expression { // Visit variable expression. - let (variable_operand, variable_instructions) = self.visit_expression(expr, additional); + let (variable_operand, variable_instructions) = self.consume_expression(expr); instructions.push_str(&variable_instructions); variable_operand } else { // Push operand identifier. - let (ident_operand, ident_instructions) = self.visit_identifier(&member.identifier, additional); + let (ident_operand, ident_instructions) = self.consume_identifier(member.identifier); instructions.push_str(&ident_instructions); ident_operand @@ -216,23 +192,15 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (destination_register, instructions) } - fn visit_member_access( - &mut self, - input: &'a MemberAccess, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { - let (inner_struct, _inner_instructions) = self.visit_expression(&input.inner, additional); + fn consume_member_access(&mut self, input: MemberAccess) -> Self::Output { + let (inner_struct, _inner_instructions) = self.consume_expression(*input.inner); let member_access_instruction = format!("{inner_struct}.{}", input.name); (member_access_instruction, String::new()) } // Pedersen64::hash() -> hash.ped64 - fn visit_associated_function( - &mut self, - input: &'a AssociatedFunction, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn consume_associated_function(&mut self, input: AssociatedFunction) -> Self::Output { // Write identifier as opcode. `Pedersen64` -> `ped64`. let symbol: &str = if let Type::Identifier(identifier) = input.ty { match identifier.name { @@ -256,8 +224,8 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { let mut instructions = String::new(); // Visit each function argument and accumulate instructions from expressions. - for arg in input.args.iter() { - let (arg_string, arg_instructions) = self.visit_expression(arg, additional); + for arg in input.args.into_iter() { + let (arg_string, arg_instructions) = self.consume_expression(arg); write!(associated_function_call, "{arg_string} ").expect("failed to write associated function argument"); instructions.push_str(&arg_instructions); } @@ -274,14 +242,10 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (destination_register, instructions) } - fn visit_access( - &mut self, - input: &'a AccessExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn consume_access(&mut self, input: AccessExpression) -> Self::Output { match input { - AccessExpression::Member(access) => self.visit_member_access(access, additional), - AccessExpression::AssociatedFunction(function) => self.visit_associated_function(function, additional), + AccessExpression::Member(access) => self.consume_member_access(access), + AccessExpression::AssociatedFunction(function) => self.consume_associated_function(function), AccessExpression::Tuple(_) => { unreachable!("Tuples should have been flattened in previous compiler passes.") } @@ -289,15 +253,15 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { } // TODO: Cleanup - fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::ExpressionOutput { + fn consume_call(&mut self, input: CallExpression) -> Self::Output { let mut call_instruction = match &input.external { Some(external) => format!(" call {external}.aleo/{}", input.function), None => format!(" call {}", input.function), }; let mut instructions = String::new(); - for argument in input.arguments.iter() { - let (argument, argument_instructions) = self.visit_expression(argument, additional); + for argument in input.arguments.into_iter() { + let (argument, argument_instructions) = self.consume_expression(argument); write!(call_instruction, " {argument}").expect("failed to write to string"); instructions.push_str(&argument_instructions); } @@ -350,19 +314,15 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { } } - fn visit_tuple( - &mut self, - input: &'a TupleExpression, - additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { + fn consume_tuple(&mut self, input: TupleExpression) -> Self::Output { // 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(); // Visit each tuple element and accumulate instructions from expressions. - for element in input.elements.iter() { - let (element, element_instructions) = self.visit_expression(element, additional); + for element in input.elements.into_iter() { + let (element, element_instructions) = self.consume_expression(element); tuple_elements.push(element); instructions.push_str(&element_instructions); } @@ -371,11 +331,11 @@ impl<'a> ExpressionVisitor<'a> for CodeGenerator<'a> { (tuple_elements.join(" "), instructions) } - fn visit_unit( - &mut self, - _input: &'a UnitExpression, - _additional: &Self::AdditionalInput, - ) -> Self::ExpressionOutput { - unreachable!("`UnitExpression`s should not be visited during code generation.") + fn consume_unit(&mut self, _input: UnitExpression) -> Self::Output { + unreachable!("`UnitExpression`s should not exist during code generation.") + } + + fn consume_tuple_access(&mut self, _input: TupleAccess) -> Self::Output { + todo!() } } diff --git a/compiler/passes/src/code_generation/visit_program.rs b/compiler/passes/src/code_generation/generate_program.rs similarity index 78% rename from compiler/passes/src/code_generation/visit_program.rs rename to compiler/passes/src/code_generation/generate_program.rs index 3e786c4641..6351a78005 100644 --- a/compiler/passes/src/code_generation/visit_program.rs +++ b/compiler/passes/src/code_generation/generate_program.rs @@ -17,7 +17,8 @@ use crate::CodeGenerator; use leo_ast::{ - functions, Function, Mapping, Mode, Program, ProgramScope, ProgramVisitor, StatementVisitor, Struct, Type, Variant, + functions, Function, FunctionConsumer, ImportConsumer, Mapping, MappingConsumer, Mode, Program, ProgramConsumer, + ProgramScope, StatementConsumer, Struct, StructConsumer, Type, Variant, }; use indexmap::IndexMap; @@ -25,155 +26,16 @@ use itertools::Itertools; use leo_span::sym; use std::fmt::Write as _; -impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { - type ProgramOutput = String; +impl FunctionConsumer for CodeGenerator<'_> { + type Output = String; - fn visit_program(&mut self, input: &'a Program) -> Self::ProgramOutput { - // Accumulate instructions into a program string. - let mut program_string = String::new(); - - if !input.imports.is_empty() { - // Visit each import statement and produce a Aleo import instruction. - program_string.push_str( - &input - .imports - .values() - .map(|(imported_program, _)| self.visit_import(imported_program)) - .join("\n"), - ); - - // Newline separator. - program_string.push('\n'); - } - - // Retrieve the program scope. - // Note that type checking guarantees that there is exactly one program scope. - let program_scope: &ProgramScope = input.program_scopes.values().next().unwrap(); - - // Print the program id. - writeln!(program_string, "program {};", program_scope.program_id) - .expect("Failed to write program id to string."); - - // Newline separator. - program_string.push('\n'); - - // Get the post-order ordering of the composite data types. - // Note that the unwrap is safe since type checking guarantees that the struct dependency graph is acyclic. - let order = self.struct_graph.post_order().unwrap(); - - // Visit each `Struct` or `Record` in the post-ordering and produce an Aleo struct or record. - program_string.push_str( - &order - .into_iter() - .map(|name| { - match program_scope.structs.get(&name) { - // If the struct is found, it is a local struct. - Some(struct_) => self.visit_struct(struct_), - // If the struct is not found, it is an imported struct. - None => String::new(), - } - }) - .join("\n"), - ); - - // Newline separator. - program_string.push('\n'); - - // Visit each mapping in the Leo AST and produce an Aleo mapping declaration. - program_string.push_str( - &program_scope - .mappings - .values() - .map(|mapping| self.visit_mapping(mapping)) - .join("\n"), - ); - - // Visit each function in the program scope and produce an Aleo function. - // Note that in the function inlining pass, we reorder the functions such that they are in post-order. - // In other words, a callee function precedes its caller function in the program scope. - program_string.push_str( - &program_scope - .functions - .values() - .map(|function| { - // Set the `is_transition_function` flag. - self.is_transition_function = matches!(function.variant, Variant::Transition); - - let function_string = self.visit_function(function); - - // Unset the `is_transition_function` flag. - self.is_transition_function = false; - - function_string - }) - .join("\n"), - ); - - program_string - } - - // TODO: Fix once imports are redesigned . - fn visit_import(&mut self, input: &'a Program) -> Self::ProgramOutput { - // Load symbols into composite mapping. - let _ = self.visit_program(input); - // todo: We do not need the import program string because we generate instructions for imports separately during leo build. - - // Generate string for import statement. - // Note that the unwrap is safe since parsing guarantees that there is exactly one program scope. - format!("import {}.aleo;", input.program_scopes.keys().next().unwrap().name) - } - - fn visit_struct(&mut self, struct_: &'a Struct) -> String { - match struct_.is_record { - true => { - // Add record symbol to composite types. - let mut output_string = String::from("record"); - self.composite_mapping - .insert(&struct_.identifier.name, (true, output_string.clone())); - writeln!(output_string, " {}:", struct_.identifier).expect("failed to write to string"); // todo: check if this is safe from name conflicts. - - // Construct and append the record variables. - for var in struct_.members.iter() { - let mode = match var.mode { - Mode::Constant => "constant", - Mode::Public => "public", - Mode::None | Mode::Private => "private", - }; - writeln!( - output_string, - " {} as {}.{mode};", // todo: CAUTION private record variables only. - var.identifier, var.type_ - ) - .expect("failed to write to string"); - } - - output_string - } - false => { - // Add private symbol to composite types. - self.composite_mapping - .insert(&struct_.identifier.name, (false, String::from("private"))); // todo: private by default here. - - let mut output_string = format!("struct {}:\n", struct_.identifier); // todo: check if this is safe from name conflicts. - - // Construct and append the record variables. - for var in struct_.members.iter() { - writeln!(output_string, " {} as {};", var.identifier, var.type_,) - .expect("failed to write to string"); - } - - output_string - } - } - } - - fn visit_function(&mut self, function: &'a Function) -> Self::ProgramOutput { - // Initialize the state of `self` with the appropriate values before visiting `function`. + fn consume_function(&mut self, function: Function) -> Self::Output { + // Initialize the state of `self` with the appropriate values before consumeing `function`. self.next_register = 0; self.variable_mapping = IndexMap::new(); // TODO: Figure out a better way to initialize. - self.variable_mapping.insert(&sym::SelfLower, "self".to_string()); - self.current_function = Some(function); + self.variable_mapping.insert(sym::SelfLower, "self".to_string()); + self.current_function = Some(function.identifier.name); // Construct the header of the function. // If a function is a program function, generate an Aleo `function`, @@ -186,23 +48,23 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { }; // Construct and append the input declarations of the function. - for input in function.input.iter() { + for input in function.input.into_iter() { let register_string = format!("r{}", self.next_register); self.next_register += 1; let type_string = match input { functions::Input::Internal(input) => { self.variable_mapping - .insert(&input.identifier.name, register_string.clone()); + .insert(input.identifier.name, register_string.clone()); let visibility = match (self.is_transition_function, input.mode) { (true, Mode::None) => Mode::Private, _ => input.mode, }; - self.visit_type_with_visibility(&input.type_, visibility) + self.consume_type_with_visibility(&input.type_, visibility) } functions::Input::External(input) => { self.variable_mapping - .insert(&input.identifier.name, register_string.clone()); + .insert(input.identifier.name, register_string.clone()); format!("{}.aleo/{}.record", input.program_name, input.record) } }; @@ -212,11 +74,11 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { } // Construct and append the function body. - let block_string = self.visit_block(&function.block); + let block_string = self.consume_block(function.block); function_string.push_str(&block_string); // If the finalize block exists, generate the appropriate bytecode. - if let Some(finalize) = &function.finalize { + if let Some(finalize) = function.finalize { // Clear the register count. self.next_register = 0; self.in_finalize = true; @@ -224,12 +86,12 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { // Clear the variable mapping. // TODO: Figure out a better way to initialize. self.variable_mapping = IndexMap::new(); - self.variable_mapping.insert(&sym::SelfLower, "self".to_string()); + self.variable_mapping.insert(sym::SelfLower, "self".to_string()); function_string.push_str(&format!("\nfinalize {}:\n", finalize.identifier)); // Construct and append the input declarations of the finalize block. - for input in finalize.input.iter() { + for input in finalize.input.into_iter() { let register_string = format!("r{}", self.next_register); self.next_register += 1; @@ -237,17 +99,17 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { let type_string = match input { functions::Input::Internal(input) => { self.variable_mapping - .insert(&input.identifier.name, register_string.clone()); + .insert(input.identifier.name, register_string.clone()); let visibility = match (self.is_transition_function, input.mode) { (true, Mode::None) => Mode::Public, _ => input.mode, }; - self.visit_type_with_visibility(&input.type_, visibility) + self.consume_type_with_visibility(&input.type_, visibility) } functions::Input::External(input) => { self.variable_mapping - .insert(&input.program_name.name, register_string.clone()); + .insert(input.program_name.name, register_string.clone()); format!("{}.aleo/{}.record", input.program_name, input.record) } }; @@ -257,15 +119,85 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { } // Construct and append the finalize block body. - function_string.push_str(&self.visit_block(&finalize.block)); + function_string.push_str(&self.consume_block(finalize.block)); self.in_finalize = false; } function_string } +} - fn visit_mapping(&mut self, mapping: &'a Mapping) -> Self::ProgramOutput { +impl StructConsumer for CodeGenerator<'_> { + type Output = String; + + fn consume_struct(&mut self, struct_: Struct) -> String { + match struct_.is_record { + true => { + // Add record symbol to composite types. + let mut output_string = String::from("record"); + self.composite_mapping + .insert(struct_.identifier.name, (true, output_string.clone())); + writeln!(output_string, " {}:", struct_.identifier).expect("failed to write to string"); // todo: check if this is safe from name conflicts. + + // Construct and append the record variables. + for var in struct_.members.iter() { + let mode = match var.mode { + Mode::Constant => "constant", + Mode::Public => "public", + Mode::None | Mode::Private => "private", + }; + writeln!( + output_string, + " {} as {}.{mode};", // todo: CAUTION private record variables only. + var.identifier, var.type_ + ) + .expect("failed to write to string"); + } + + output_string + } + false => { + // Add private symbol to composite types. + self.composite_mapping + .insert(struct_.identifier.name, (false, String::from("private"))); // todo: private by default here. + + let mut output_string = format!("struct {}:\n", struct_.identifier); // todo: check if this is safe from name conflicts. + + // Construct and append the record variables. + for var in struct_.members.iter() { + writeln!(output_string, " {} as {};", var.identifier, var.type_,) + .expect("failed to write to string"); + } + + output_string + } + } + } +} + +impl ImportConsumer for CodeGenerator<'_> { + type Output = String; + + // TODO: Fix once imports are redesigned . + fn consume_import(&mut self, input: Program) -> Self::Output { + // Get the name of the imported program. + // Note that the unwrap is safe since parsing guarantees that there is exactly one program scope. + let name = input.program_scopes.keys().next().unwrap().name; + + // Load symbols into composite mapping. + let _ = self.consume_program(input); + // todo: We do not need the import program string because we generate instructions for imports separately during leo build. + + // Generate string for import statement. + format!("import {}.aleo;", name) + } +} + +impl MappingConsumer for CodeGenerator<'_> { + type Output = String; + + fn consume_mapping(&mut self, mapping: Mapping) -> Self::Output { // Create the prefix of the mapping string, e.g. `mapping foo:`. let mut mapping_string = format!("mapping {}:\n", mapping.identifier); @@ -297,3 +229,91 @@ impl<'a> ProgramVisitor<'a> for CodeGenerator<'a> { mapping_string } } + +impl ProgramConsumer for CodeGenerator<'_> { + type Output = String; + + fn consume_program(&mut self, input: Program) -> Self::Output { + // Accumulate instructions into a program string. + let mut program_string = String::new(); + + if !input.imports.is_empty() { + // Visit each import statement and produce a Aleo import instruction. + program_string.push_str( + &input + .imports + .into_values() + .map(|(imported_program, _)| self.consume_import(imported_program)) + .join("\n"), + ); + + // Newline separator. + program_string.push('\n'); + } + + // Retrieve the program scope. + // Note that type checking guarantees that there is exactly one program scope. + let mut program_scope: ProgramScope = input.program_scopes.into_values().next().unwrap(); + + // Print the program id. + writeln!(program_string, "program {};", program_scope.program_id) + .expect("Failed to write program id to string."); + + // Newline separator. + program_string.push('\n'); + + // Get the post-order ordering of the composite data types. + // Note that the unwrap is safe since type checking guarantees that the struct dependency graph is acyclic. + let order = self.struct_graph.post_order().unwrap(); + + // Visit each `Struct` or `Record` in the post-ordering and produce an Aleo struct or record. + program_string.push_str( + &order + .into_iter() + .map(|name| { + match program_scope.structs.remove(&name) { + // If the struct is found, it is a local struct. + Some(struct_) => self.consume_struct(struct_), + // If the struct is not found, it is an imported struct. + None => String::new(), + } + }) + .join("\n"), + ); + + // Newline separator. + program_string.push('\n'); + + // Visit each mapping in the Leo AST and produce an Aleo mapping declaration. + program_string.push_str( + &program_scope + .mappings + .into_values() + .map(|mapping| self.consume_mapping(mapping)) + .join("\n"), + ); + + // Visit each function in the program scope and produce an Aleo function. + // Note that in the function inlining pass, we reorder the functions such that they are in post-order. + // In other words, a callee function precedes its caller function in the program scope. + program_string.push_str( + &program_scope + .functions + .into_values() + .map(|function| { + // Set the `is_transition_function` flag. + self.is_transition_function = matches!(function.variant, Variant::Transition); + + let function_string = self.consume_function(function); + + // Unset the `is_transition_function` flag. + self.is_transition_function = false; + + function_string + }) + .join("\n"), + ); + + program_string + } +} diff --git a/compiler/passes/src/code_generation/visit_statements.rs b/compiler/passes/src/code_generation/generate_statements.rs similarity index 68% rename from compiler/passes/src/code_generation/visit_statements.rs rename to compiler/passes/src/code_generation/generate_statements.rs index 5372342021..d1d0247fda 100644 --- a/compiler/passes/src/code_generation/visit_statements.rs +++ b/compiler/passes/src/code_generation/generate_statements.rs @@ -18,20 +18,20 @@ use crate::CodeGenerator; use leo_ast::{ AssertStatement, AssertVariant, AssignStatement, Block, ConditionalStatement, ConsoleStatement, DecrementStatement, - DefinitionStatement, Expression, ExpressionStatement, ExpressionVisitor, IncrementStatement, IterationStatement, - Mode, Output, ReturnStatement, StatementVisitor, + DefinitionStatement, Expression, ExpressionConsumer, ExpressionStatement, IncrementStatement, IterationStatement, + Mode, Output, ReturnStatement, StatementConsumer, }; use itertools::Itertools; use std::fmt::Write as _; -impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { - type StatementOutput = String; +impl StatementConsumer for CodeGenerator<'_> { + type Output = String; - fn visit_assert(&mut self, input: &'a AssertStatement) -> Self::StatementOutput { - let mut generate_assert_instruction = |name: &str, left: &'a Expression, right: &'a Expression| { - let (left_operand, left_instructions) = self.visit_expression(left, &()); - let (right_operand, right_instructions) = self.visit_expression(right, &()); + fn consume_assert(&mut self, input: AssertStatement) -> Self::Output { + let mut generate_assert_instruction = |name: &str, left: Expression, right: Expression| { + let (left_operand, left_instructions) = self.consume_expression(left); + let (right_operand, right_instructions) = self.consume_expression(right); let assert_instruction = format!(" {name} {left_operand} {right_operand};\n"); // Concatenate the instructions. @@ -41,9 +41,9 @@ impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { instructions }; - match &input.variant { + match input.variant { AssertVariant::Assert(expr) => { - let (operand, mut instructions) = self.visit_expression(expr, &()); + let (operand, mut instructions) = self.consume_expression(expr); let assert_instruction = format!(" assert.eq {operand} true;\n"); instructions.push_str(&assert_instruction); @@ -54,22 +54,22 @@ impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { } } - fn visit_assign(&mut self, input: &'a AssignStatement) -> Self::StatementOutput { + fn consume_assign(&mut self, input: AssignStatement) -> Self::Output { match (&input.place, &input.value) { (Expression::Identifier(identifier), _) => { - let (operand, expression_instructions) = self.visit_expression(&input.value, &()); - self.variable_mapping.insert(&identifier.name, operand); + let (operand, expression_instructions) = self.consume_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, &()); + let (operand, expression_instructions) = self.consume_expression(input.value); // Split out the destinations from the tuple. let operands = operand.split(' ').collect::>(); // 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()) + self.variable_mapping.insert(identifier.name, operand.to_string()) } _ => { unreachable!("Type checking ensures that tuple elements on the lhs are always identifiers.") @@ -84,71 +84,80 @@ impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { } } - fn visit_block(&mut self, input: &'a Block) -> Self::StatementOutput { - // For each statement in the block, visit it and add its instructions to the list. - input.statements.iter().map(|stmt| self.visit_statement(stmt)).join("") + fn consume_block(&mut self, input: Block) -> Self::Output { + // For each statement in the block, consume it and add its instructions to the list. + input + .statements + .into_iter() + .map(|stmt| self.consume_statement(stmt)) + .join("") } - fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> Self::StatementOutput { + fn consume_conditional(&mut self, _input: ConditionalStatement) -> Self::Output { // TODO: Once SSA is made optional, create a Leo error informing the user to enable the SSA pass. unreachable!("`ConditionalStatement`s should not be in the AST at this phase of compilation.") } - fn visit_console(&mut self, _: &'a ConsoleStatement) -> Self::StatementOutput { + fn consume_console(&mut self, _: ConsoleStatement) -> Self::Output { unreachable!("Parsing guarantees that `ConsoleStatement`s are not present in the AST.") } - fn visit_decrement(&mut self, input: &'a DecrementStatement) -> Self::StatementOutput { - let (index, mut instructions) = self.visit_expression(&input.index, &()); - let (amount, amount_instructions) = self.visit_expression(&input.amount, &()); + fn consume_decrement(&mut self, input: DecrementStatement) -> Self::Output { + let (index, mut instructions) = self.consume_expression(input.index); + let (amount, amount_instructions) = self.consume_expression(input.amount); instructions.push_str(&amount_instructions); instructions.push_str(&format!(" decrement {}[{index}] by {amount};\n", input.mapping)); instructions } - fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> Self::StatementOutput { + fn consume_definition(&mut self, _input: DefinitionStatement) -> Self::Output { // TODO: If SSA is made optional, then conditionally enable codegen for DefinitionStatement unreachable!("DefinitionStatement's should not exist in SSA form.") } - fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> Self::StatementOutput { - match input.expression { + fn consume_expression_statement(&mut self, input: ExpressionStatement) -> Self::Output { + match &input.expression { Expression::Call(_) => { // Note that codegen for CallExpression in an expression statement does not return any destination registers. - self.visit_expression(&input.expression, &()).1 + self.consume_expression(input.expression).1 } _ => unreachable!("ExpressionStatement's can only contain CallExpression's."), } } - fn visit_increment(&mut self, input: &'a IncrementStatement) -> Self::StatementOutput { - let (index, mut instructions) = self.visit_expression(&input.index, &()); - let (amount, amount_instructions) = self.visit_expression(&input.amount, &()); + fn consume_increment(&mut self, input: IncrementStatement) -> Self::Output { + let (index, mut instructions) = self.consume_expression(input.index); + let (amount, amount_instructions) = self.consume_expression(input.amount); instructions.push_str(&amount_instructions); instructions.push_str(&format!(" increment {}[{index}] by {amount};\n", input.mapping)); instructions } - fn visit_iteration(&mut self, _input: &'a IterationStatement) -> Self::StatementOutput { + fn consume_iteration(&mut self, _input: IterationStatement) -> Self::Output { // TODO: Once loop unrolling is made optional, create a Leo error informing the user to enable the loop unrolling pass.. unreachable!("`IterationStatement`s should not be in the AST at this phase of compilation."); } - fn visit_return(&mut self, input: &'a ReturnStatement) -> Self::StatementOutput { + fn consume_return(&mut self, input: ReturnStatement) -> Self::Output { let mut instructions = match input.expression { // Skip empty return statements. Expression::Unit(_) => String::new(), - _ => { - let (operand, mut expression_instructions) = self.visit_expression(&input.expression, &()); + expression => { + let (operand, mut expression_instructions) = self.consume_expression(expression); + // Note that the first unwrap is safe, since `current_function` is set in `consume_function`. + // Note that the second unwrap is safe, since type checking guarantees that the function is defined. + let function_symbol = self + .symbol_table + .lookup_fn_symbol(self.current_function.unwrap()) + .unwrap(); // Get the output type of the function. let output = if self.in_finalize { - // Note that the first unwrap is safe, since `current_function` is set in `visit_function`. - self.current_function.unwrap().finalize.as_ref().unwrap().output.iter() + // Note that this unwrap is safe since symbol table construction will store information about the finalize block if it exists. + function_symbol.finalize.as_ref().unwrap().output.iter() } else { - // Note that this unwrap is safe, since `current_function` is set in `visit_function`. - self.current_function.unwrap().output.iter() + function_symbol.output.iter() }; let instructions = operand .split(' ') @@ -176,7 +185,7 @@ impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { format!( " output {} as {};\n", operand, - self.visit_type_with_visibility(&output.type_, visibility) + self.consume_type_with_visibility(&output.type_, visibility) ) } Output::External(output) => { @@ -197,11 +206,11 @@ impl<'a> StatementVisitor<'a> for CodeGenerator<'a> { // Output a finalize instruction if needed. // TODO: Check formatting. - if let Some(arguments) = &input.finalize_arguments { + if let Some(arguments) = input.finalize_arguments { let mut finalize_instruction = "\n finalize".to_string(); - for argument in arguments.iter() { - let (argument, argument_instructions) = self.visit_expression(argument, &()); + for argument in arguments.into_iter() { + let (argument, argument_instructions) = self.consume_expression(argument); write!(finalize_instruction, " {argument}").expect("failed to write to string"); instructions.push_str(&argument_instructions); } diff --git a/compiler/passes/src/code_generation/generator.rs b/compiler/passes/src/code_generation/generator.rs index 4734945b6c..0dc140e838 100644 --- a/compiler/passes/src/code_generation/generator.rs +++ b/compiler/passes/src/code_generation/generator.rs @@ -17,7 +17,7 @@ use crate::SymbolTable; use crate::{CallGraph, StructGraph}; -use leo_ast::Function; +use leo_ast::{Mode, Type}; use leo_span::Symbol; use indexmap::IndexMap; @@ -31,14 +31,14 @@ pub struct CodeGenerator<'a> { pub(crate) _call_graph: &'a CallGraph, /// A counter to track the next available register. pub(crate) next_register: u64, - /// Reference to the current function. - pub(crate) current_function: Option<&'a Function>, + /// The name of the current function. + pub(crate) current_function: Option, /// Mapping of variables to registers. - pub(crate) variable_mapping: IndexMap<&'a Symbol, String>, + pub(crate) variable_mapping: IndexMap, /// Mapping of composite names to a tuple containing metadata associated with the name. /// The first element of the tuple indicate whether the composite is a record or not. /// The second element of the tuple is a string modifier used for code generation. - pub(crate) composite_mapping: IndexMap<&'a Symbol, (bool, String)>, + pub(crate) composite_mapping: IndexMap, /// Are we traversing a transition function? pub(crate) is_transition_function: bool, /// Are we traversing a finalize block? @@ -62,3 +62,40 @@ impl<'a> CodeGenerator<'a> { } } } + +impl CodeGenerator<'_> { + fn consume_type(&mut self, input: &Type) -> String { + match input { + Type::Address + | Type::Boolean + | Type::Field + | Type::Group + | Type::Scalar + | Type::String + | Type::Integer(..) => format!("{input}"), + Type::Identifier(ident) => format!("{ident}"), + Type::Mapping(_) => { + unreachable!("Mapping types are not supported at this phase of compilation") + } + Type::Tuple(_) => { + 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"), + } + } + + pub(crate) fn consume_type_with_visibility(&mut self, type_: &Type, visibility: Mode) -> String { + match type_ { + // When the type is a record. + // Note that this unwrap is safe because all composite types have been added to the mapping. + Type::Identifier(identifier) if self.composite_mapping.get(&identifier.name).unwrap().0 => { + format!("{identifier}.record") + } + _ => match visibility { + Mode::None => self.consume_type(type_), + _ => format!("{}.{visibility}", self.consume_type(type_)), + }, + } + } +} diff --git a/compiler/passes/src/code_generation/mod.rs b/compiler/passes/src/code_generation/mod.rs index 8612b7b114..b1d9d8a844 100644 --- a/compiler/passes/src/code_generation/mod.rs +++ b/compiler/passes/src/code_generation/mod.rs @@ -17,27 +17,25 @@ pub mod generator; pub use generator::*; -mod visit_expressions; +mod generate_expressions; -mod visit_program; +mod generate_program; -mod visit_statements; - -mod visit_type; +mod generate_statements; use crate::SymbolTable; use crate::{CallGraph, Pass, StructGraph}; -use leo_ast::{Ast, ProgramVisitor}; +use leo_ast::{Ast, ProgramConsumer}; use leo_errors::Result; impl<'a> Pass for CodeGenerator<'a> { - type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph); + type Input = (Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph); type Output = Result; fn do_pass((ast, symbol_table, struct_graph, call_graph): Self::Input) -> Self::Output { let mut generator = Self::new(symbol_table, struct_graph, call_graph); - let bytecode = generator.visit_program(ast.as_repr()); + let bytecode = generator.consume_program(ast.into_repr()); Ok(bytecode) } diff --git a/compiler/passes/src/code_generation/visit_type.rs b/compiler/passes/src/code_generation/visit_type.rs deleted file mode 100644 index f93746df93..0000000000 --- a/compiler/passes/src/code_generation/visit_type.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2023 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 . - -use crate::CodeGenerator; - -use leo_ast::{Mode, Type}; - -impl<'a> CodeGenerator<'a> { - fn visit_type(&mut self, input: &'a Type) -> String { - match input { - Type::Address - | Type::Boolean - | Type::Field - | Type::Group - | Type::Scalar - | Type::String - | Type::Integer(..) => format!("{input}"), - Type::Identifier(ident) => format!("{ident}"), - Type::Mapping(_) => { - unreachable!("Mapping types are not supported at this phase of compilation") - } - Type::Tuple(_) => { - 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"), - } - } - - pub(crate) fn visit_type_with_visibility(&mut self, type_: &'a Type, visibility: Mode) -> String { - match type_ { - // When the type is a record. - // Note that this unwrap is safe because all composite types have been added to the mapping. - Type::Identifier(identifier) if self.composite_mapping.get(&identifier.name).unwrap().0 => { - format!("{identifier}.record") - } - _ => match visibility { - Mode::None => self.visit_type(type_), - _ => format!("{}.{visibility}", self.visit_type(type_)), - }, - } - } -} diff --git a/compiler/passes/src/common/symbol_table/function_symbol.rs b/compiler/passes/src/common/symbol_table/function_symbol.rs index 2e3482afbe..ec4c103548 100644 --- a/compiler/passes/src/common/symbol_table/function_symbol.rs +++ b/compiler/passes/src/common/symbol_table/function_symbol.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use leo_ast::{Function, Input, Type, Variant}; +use leo_ast::{Function, Input, Output, Type, Variant}; use leo_span::Span; use crate::SymbolTable; @@ -24,6 +24,8 @@ use crate::SymbolTable; pub struct FinalizeData { /// The inputs to the finalize block. pub(crate) input: Vec, + /// The outputs of the finalize block. + pub(crate) output: Vec, /// The output type of the finalize block. pub(crate) output_type: Type, } @@ -41,6 +43,8 @@ pub struct FunctionSymbol { pub(crate) _span: Span, /// The inputs to the function. pub(crate) input: Vec, + /// The outputs of the funciton. + pub(crate) output: Vec, /// Metadata associated with the finalize block. pub(crate) finalize: Option, } @@ -53,8 +57,10 @@ impl SymbolTable { variant: func.variant, _span: func.span, input: func.input.clone(), + output: func.output.clone(), finalize: func.finalize.as_ref().map(|finalize| FinalizeData { input: finalize.input.clone(), + output: finalize.output.clone(), output_type: finalize.output_type.clone(), }), } From 5b8597ae0e53cb873240a98d014da1874a7aac0b Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:10:56 -0800 Subject: [PATCH 22/24] Connect to compiler --- compiler/compiler/src/compiler.rs | 2 +- compiler/compiler/tests/utilities/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/compiler/src/compiler.rs b/compiler/compiler/src/compiler.rs index 3b6676b787..fc8a1601cf 100644 --- a/compiler/compiler/src/compiler.rs +++ b/compiler/compiler/src/compiler.rs @@ -239,7 +239,7 @@ impl<'a> Compiler<'a> { self.parse_program()?; let (symbol_table, struct_graph, call_graph) = self.compiler_stages()?; - let bytecode = CodeGenerator::do_pass((&self.ast, &symbol_table, &struct_graph, &call_graph))?; + let bytecode = CodeGenerator::do_pass((std::mem::take(&mut self.ast), &symbol_table, &struct_graph, &call_graph))?; Ok((symbol_table, bytecode)) } diff --git a/compiler/compiler/tests/utilities/mod.rs b/compiler/compiler/tests/utilities/mod.rs index 754aafce7a..a41e026747 100644 --- a/compiler/compiler/tests/utilities/mod.rs +++ b/compiler/compiler/tests/utilities/mod.rs @@ -197,7 +197,7 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result Date: Sat, 25 Feb 2023 19:14:08 -0800 Subject: [PATCH 23/24] Fmt --- compiler/compiler/src/compiler.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/compiler/src/compiler.rs b/compiler/compiler/src/compiler.rs index fc8a1601cf..8108304095 100644 --- a/compiler/compiler/src/compiler.rs +++ b/compiler/compiler/src/compiler.rs @@ -239,7 +239,8 @@ impl<'a> Compiler<'a> { self.parse_program()?; let (symbol_table, struct_graph, call_graph) = self.compiler_stages()?; - let bytecode = CodeGenerator::do_pass((std::mem::take(&mut self.ast), &symbol_table, &struct_graph, &call_graph))?; + let bytecode = + CodeGenerator::do_pass((std::mem::take(&mut self.ast), &symbol_table, &struct_graph, &call_graph))?; Ok((symbol_table, bytecode)) } From 3532dcbfb000668e4298a32d110ee60bd8fce5bc Mon Sep 17 00:00:00 2001 From: d0cd Date: Sat, 25 Feb 2023 19:17:33 -0800 Subject: [PATCH 24/24] Regen expectations --- tests/expectations/compiler/finalize/decrement.out | 6 +++--- tests/expectations/compiler/finalize/finalize.out | 6 +++--- tests/expectations/compiler/finalize/increment.out | 6 +++--- tests/expectations/compiler/finalize/mapping.out | 6 +++--- tests/expectations/compiler/function/self.out | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/expectations/compiler/finalize/decrement.out b/tests/expectations/compiler/finalize/decrement.out index 42b6e9f74c..d62b906baf 100644 --- a/tests/expectations/compiler/finalize/decrement.out +++ b/tests/expectations/compiler/finalize/decrement.out @@ -4,7 +4,7 @@ expectation: Pass outputs: - initial_ast: 6fa465e63f2b8e880d621cb1758b3d1c0edfa9ce09e6d4f0f28bbe6c2ca2b955 unrolled_ast: 6fa465e63f2b8e880d621cb1758b3d1c0edfa9ce09e6d4f0f28bbe6c2ca2b955 - ssa_ast: 6fa465e63f2b8e880d621cb1758b3d1c0edfa9ce09e6d4f0f28bbe6c2ca2b955 - flattened_ast: 4b8969d1adf68074bc7a8458a9146e128041bf929f2f6a4e76a16ad2769b81b1 - inlined_ast: 4b8969d1adf68074bc7a8458a9146e128041bf929f2f6a4e76a16ad2769b81b1 + ssa_ast: 83005eb82be89fc7518d652b8c3eeb88c6bce51e94dc1db358ec265143a814c4 + flattened_ast: 53aeb6357226cd003f5fa9a8c2e297ac0451b3cc2cddd44cc23886a994e2c6f7 + inlined_ast: 53aeb6357226cd003f5fa9a8c2e297ac0451b3cc2cddd44cc23886a994e2c6f7 bytecode: 39aa8516297ece27331b633a72466d2ff0122d36beca663a48bc07589e2d3e15 diff --git a/tests/expectations/compiler/finalize/finalize.out b/tests/expectations/compiler/finalize/finalize.out index 92e62f3c8c..9d46876825 100644 --- a/tests/expectations/compiler/finalize/finalize.out +++ b/tests/expectations/compiler/finalize/finalize.out @@ -4,7 +4,7 @@ expectation: Pass outputs: - initial_ast: 9ffd4f34e261ee315a2ee29353e707b35c300e928aca532743142a538957c4ce unrolled_ast: 9ffd4f34e261ee315a2ee29353e707b35c300e928aca532743142a538957c4ce - ssa_ast: 9ea8f3743b9bcf1584319472ca0a80707f117616d687d4c401b34fb10b44703a - flattened_ast: a4eca8b80af9863d59ebfb837fa5dae061fca7d52315d3a9f5778e6dc4b75716 - inlined_ast: a4eca8b80af9863d59ebfb837fa5dae061fca7d52315d3a9f5778e6dc4b75716 + ssa_ast: 45732818a878f68843cdc52543359c55020a24869c487f96206447401d45b158 + flattened_ast: 9ce312238cbb1d5f5ec463d22d24994fcdc7ebd3eeb1573d3810b575f73ff5a8 + inlined_ast: 9ce312238cbb1d5f5ec463d22d24994fcdc7ebd3eeb1573d3810b575f73ff5a8 bytecode: 6db857dc2b80ea257d141b3980404e050024771f95c5f9b74f899145b2001432 diff --git a/tests/expectations/compiler/finalize/increment.out b/tests/expectations/compiler/finalize/increment.out index 94daa97618..3dd44e98b4 100644 --- a/tests/expectations/compiler/finalize/increment.out +++ b/tests/expectations/compiler/finalize/increment.out @@ -4,7 +4,7 @@ expectation: Pass outputs: - initial_ast: 19378936c22e4e747e16e132bbc727115598dfbd17068349cb300525cde35556 unrolled_ast: 19378936c22e4e747e16e132bbc727115598dfbd17068349cb300525cde35556 - ssa_ast: 19378936c22e4e747e16e132bbc727115598dfbd17068349cb300525cde35556 - flattened_ast: c55a0edeb6a52dd728e5500ff5b1d387186321c8a3d68f2d0638628bbb05696e - inlined_ast: c55a0edeb6a52dd728e5500ff5b1d387186321c8a3d68f2d0638628bbb05696e + ssa_ast: f1f2590afb2f3bea2ae75b97e9dee062b444c282863ea5139edae6f315b8af62 + flattened_ast: 5189aac15cf39f9ff6c6d6d0200231fde37fcb02b0c6ef367d194eca80bd6bb4 + inlined_ast: 5189aac15cf39f9ff6c6d6d0200231fde37fcb02b0c6ef367d194eca80bd6bb4 bytecode: 49afa4d378578bc680308083733b31b8272f9c952fe8dbc133398676e3f0d2ba diff --git a/tests/expectations/compiler/finalize/mapping.out b/tests/expectations/compiler/finalize/mapping.out index 9ff2ac704a..87660b8d56 100644 --- a/tests/expectations/compiler/finalize/mapping.out +++ b/tests/expectations/compiler/finalize/mapping.out @@ -4,7 +4,7 @@ expectation: Pass outputs: - initial_ast: 6e57e1c0c5d4df7eaf414939293c79c156c77f9ee78064dd35572f0d54ec2b85 unrolled_ast: 6e57e1c0c5d4df7eaf414939293c79c156c77f9ee78064dd35572f0d54ec2b85 - ssa_ast: 4c6468604104edfe6a6d2a5f4d80c557b001bc60b1e4098f80eda119afdbb292 - flattened_ast: bb26f3622ed2fa2f301ac07069ae281a5e5bea1d52fec8cdb03b03131ad0b4f6 - inlined_ast: bb26f3622ed2fa2f301ac07069ae281a5e5bea1d52fec8cdb03b03131ad0b4f6 + ssa_ast: 6d5d564760e6aed4d916bcf35ab56f10ea200310adb995dfe50361127c7f9dcd + flattened_ast: 9bc00dc5debb2e76d2aaffaa63adf9fd31893c9da41ca8a5d2fbef117caa3ef4 + inlined_ast: 9bc00dc5debb2e76d2aaffaa63adf9fd31893c9da41ca8a5d2fbef117caa3ef4 bytecode: 1da5a78fcb6f77bd197de7dce1e7e94e7a9d30a6ec26703a645b25ab7c65cc08 diff --git a/tests/expectations/compiler/function/self.out b/tests/expectations/compiler/function/self.out index 971867230d..2695624a94 100644 --- a/tests/expectations/compiler/function/self.out +++ b/tests/expectations/compiler/function/self.out @@ -4,7 +4,7 @@ expectation: Pass outputs: - initial_ast: a1273726570fc7a7cc2a27f2689258df5f6fb26964aab84c89f5a771d4841ea3 unrolled_ast: a1273726570fc7a7cc2a27f2689258df5f6fb26964aab84c89f5a771d4841ea3 - ssa_ast: 028b589972bfceff9fb375e832f0595848ec7b7ec7a791995c7d360f0397e68a - flattened_ast: ef3ca55705d6cf89c60d2f7b62a11bb6aef71a03dc92f86ddd7ea61ff8faca72 - inlined_ast: ef3ca55705d6cf89c60d2f7b62a11bb6aef71a03dc92f86ddd7ea61ff8faca72 + ssa_ast: 844aec479c08e75446b5d345af8bcd2c90147722b413a643938440d8e0cd2a8c + flattened_ast: 6d9c45aa6ccabf909d909d29721c3e8ef6cdc29c9e7a5cfd1fae188ae8f97cc4 + inlined_ast: 6d9c45aa6ccabf909d909d29721c3e8ef6cdc29c9e7a5cfd1fae188ae8f97cc4 bytecode: e62ba6ed16c820d4f4a8c2569bf96add46e3b8ce999e5fc77fa99c1769ca2dbd