dead code elimination pass

This commit is contained in:
Protryon 2021-03-07 09:45:09 -08:00
parent 1fbd337fec
commit ba9444ecc4
32 changed files with 1016 additions and 158 deletions

View File

@ -31,7 +31,7 @@ impl<'a, 'b> ExpressionVisitor<'a> for ConstantFolding<'a, 'b> {
span: expr.span().cloned(),
value: const_value,
});
let folded_expr = self.program.scope.alloc_expression(folded_expr);
let folded_expr = self.program.context.alloc_expression(folded_expr);
input.set(folded_expr);
VisitResult::SkipChildren
} else {
@ -45,10 +45,10 @@ impl<'a, 'b> StatementVisitor<'a> for ConstantFolding<'a, 'b> {}
impl<'a, 'b> ProgramVisitor<'a> for ConstantFolding<'a, 'b> {}
impl<'a, 'b> AsgPass<'a> for ConstantFolding<'a, 'b> {
fn do_pass(asg: &Program<'a>) -> Result<(), FormattedError> {
let pass = ConstantFolding { program: asg };
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError> {
let pass = ConstantFolding { program: &asg };
let mut director = VisitorDirector::new(pass);
director.visit_program(asg).ok();
Ok(())
director.visit_program(&asg).ok();
Ok(asg)
}
}

View File

@ -0,0 +1,71 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use std::cell::Cell;
use leo_asg::*;
pub struct DeadCodeElimination {}
impl<'a> ReconstructingReducerExpression<'a> for DeadCodeElimination {}
impl<'a> ReconstructingReducerProgram<'a> for DeadCodeElimination {}
impl<'a> ReconstructingReducerStatement<'a> for DeadCodeElimination {
// clobbers asg!!
fn reduce_statement_alloc(
&mut self,
context: AsgContext<'a>,
_input: &'a Statement<'a>,
value: Statement<'a>,
) -> &'a Statement<'a> {
match &value {
Statement::Conditional(conditional) => match conditional.condition.get().const_value() {
Some(ConstValue::Boolean(true)) => conditional.result.get(),
Some(ConstValue::Boolean(false)) => {
if let Some(if_false) = conditional.next.get() {
if_false
} else {
context.alloc_statement(Statement::Empty(conditional.span.clone()))
}
}
_ => context.alloc_statement(value),
},
_ => context.alloc_statement(value),
}
}
fn reduce_block(&mut self, input: BlockStatement<'a>, mut statements: Vec<&'a Statement<'a>>) -> Statement<'a> {
let first_return = statements.iter().position(|x| matches!(x, Statement::Return(_)));
if let Some(first_return) = first_return {
statements.truncate(first_return + 1);
}
Statement::Block(BlockStatement {
parent: input.parent,
span: input.span,
statements: statements.into_iter().map(Cell::new).collect(),
scope: input.scope,
})
}
}
impl<'a> AsgPass<'a> for DeadCodeElimination {
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError> {
let pass = DeadCodeElimination {};
let mut director = ReconstructingDirector::new(asg.context, pass);
Ok(director.reduce_program(asg))
}
}

View File

@ -16,3 +16,6 @@
pub mod constant_folding;
pub use constant_folding::*;
pub mod dead_code_elimination;
pub use dead_code_elimination::*;

View File

@ -18,7 +18,7 @@ use std::{cell::Cell, unimplemented};
use typed_arena::Arena;
use crate::ArenaNode;
use crate::{ArenaNode, Circuit, Expression, Function, Scope, Statement, Variable};
pub struct AsgContextInner<'a> {
pub arena: &'a Arena<ArenaNode<'a>>,
@ -41,6 +41,54 @@ impl<'a> AsgContextInner<'a> {
self.next_id.replace(next_id + 1);
next_id
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_expression(&'a self, expr: Expression<'a>) -> &'a Expression<'a> {
match self.arena.alloc(ArenaNode::Expression(expr)) {
ArenaNode::Expression(e) => e,
_ => unimplemented!(),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_statement(&'a self, statement: Statement<'a>) -> &'a Statement<'a> {
match self.arena.alloc(ArenaNode::Statement(statement)) {
ArenaNode::Statement(e) => e,
_ => unimplemented!(),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_variable(&'a self, variable: Variable<'a>) -> &'a Variable<'a> {
match self.arena.alloc(ArenaNode::Variable(variable)) {
ArenaNode::Variable(e) => e,
_ => unimplemented!(),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_scope(&'a self, scope: Scope<'a>) -> &'a Scope<'a> {
match self.arena.alloc(ArenaNode::Scope(scope)) {
ArenaNode::Scope(e) => e,
_ => unimplemented!(),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_circuit(&'a self, circuit: Circuit<'a>) -> &'a Circuit<'a> {
match self.arena.alloc(ArenaNode::Circuit(circuit)) {
ArenaNode::Circuit(e) => e,
_ => unimplemented!(),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_function(&'a self, function: Function<'a>) -> &'a Function<'a> {
match self.arena.alloc(ArenaNode::Function(function)) {
ArenaNode::Function(e) => e,
_ => unimplemented!(),
}
}
}
pub type AsgContext<'a> = &'a AsgContextInner<'a>;

View File

@ -137,7 +137,7 @@ impl<'a> FromAst<'a, leo_ast::ArrayInitExpression> for ArrayInitExpression<'a> {
element: Cell::new(
output
.map(Expression::ArrayInit)
.map(|expr| &*scope.alloc_expression(expr))
.map(|expr| &*scope.context.alloc_expression(expr))
.unwrap_or_else(|| element.take().unwrap()),
),
len: dimension,

View File

@ -90,6 +90,12 @@ pub enum Expression<'a> {
Call(CallExpression<'a>),
}
impl<'a> Expression<'a> {
pub fn ptr_eq(&self, other: &Expression<'a>) -> bool {
std::ptr::eq(self as *const Expression<'a>, other as *const Expression<'a>)
}
}
impl<'a> Node for Expression<'a> {
fn span(&self) -> Option<&Span> {
use Expression::*;
@ -282,57 +288,58 @@ impl<'a> FromAst<'a, leo_ast::Expression> for &'a Expression<'a> {
use leo_ast::Expression::*;
let expression = match value {
Identifier(identifier) => Self::from_ast(scope, identifier, expected_type)?,
Value(value) => {
scope.alloc_expression(Constant::from_ast(scope, value, expected_type).map(Expression::Constant)?)
}
Value(value) => scope
.context
.alloc_expression(Constant::from_ast(scope, value, expected_type).map(Expression::Constant)?),
Binary(binary) => scope
.context
.alloc_expression(BinaryExpression::from_ast(scope, binary, expected_type).map(Expression::Binary)?),
Unary(unary) => {
scope.alloc_expression(UnaryExpression::from_ast(scope, unary, expected_type).map(Expression::Unary)?)
}
Ternary(conditional) => scope.alloc_expression(
Unary(unary) => scope
.context
.alloc_expression(UnaryExpression::from_ast(scope, unary, expected_type).map(Expression::Unary)?),
Ternary(conditional) => scope.context.alloc_expression(
TernaryExpression::from_ast(scope, conditional, expected_type).map(Expression::Ternary)?,
),
Cast(cast) => {
scope.alloc_expression(CastExpression::from_ast(scope, cast, expected_type).map(Expression::Cast)?)
}
Cast(cast) => scope
.context
.alloc_expression(CastExpression::from_ast(scope, cast, expected_type).map(Expression::Cast)?),
ArrayInline(array_inline) => scope.alloc_expression(
ArrayInline(array_inline) => scope.context.alloc_expression(
ArrayInlineExpression::from_ast(scope, array_inline, expected_type).map(Expression::ArrayInline)?,
),
ArrayInit(array_init) => scope.alloc_expression(
ArrayInit(array_init) => scope.context.alloc_expression(
ArrayInitExpression::from_ast(scope, array_init, expected_type).map(Expression::ArrayInit)?,
),
ArrayAccess(array_access) => scope.alloc_expression(
ArrayAccess(array_access) => scope.context.alloc_expression(
ArrayAccessExpression::from_ast(scope, array_access, expected_type).map(Expression::ArrayAccess)?,
),
ArrayRangeAccess(array_range_access) => scope.alloc_expression(
ArrayRangeAccess(array_range_access) => scope.context.alloc_expression(
ArrayRangeAccessExpression::from_ast(scope, array_range_access, expected_type)
.map(Expression::ArrayRangeAccess)?,
),
TupleInit(tuple_init) => scope.alloc_expression(
TupleInit(tuple_init) => scope.context.alloc_expression(
TupleInitExpression::from_ast(scope, tuple_init, expected_type).map(Expression::TupleInit)?,
),
TupleAccess(tuple_access) => scope.alloc_expression(
TupleAccess(tuple_access) => scope.context.alloc_expression(
TupleAccessExpression::from_ast(scope, tuple_access, expected_type).map(Expression::TupleAccess)?,
),
CircuitInit(circuit_init) => scope.alloc_expression(
CircuitInit(circuit_init) => scope.context.alloc_expression(
CircuitInitExpression::from_ast(scope, circuit_init, expected_type).map(Expression::CircuitInit)?,
),
CircuitMemberAccess(circuit_member) => scope.alloc_expression(
CircuitMemberAccess(circuit_member) => scope.context.alloc_expression(
CircuitAccessExpression::from_ast(scope, circuit_member, expected_type)
.map(Expression::CircuitAccess)?,
),
CircuitStaticFunctionAccess(circuit_member) => scope.alloc_expression(
CircuitStaticFunctionAccess(circuit_member) => scope.context.alloc_expression(
CircuitAccessExpression::from_ast(scope, circuit_member, expected_type)
.map(Expression::CircuitAccess)?,
),
Call(call) => {
scope.alloc_expression(CallExpression::from_ast(scope, call, expected_type).map(Expression::Call)?)
}
Call(call) => scope
.context
.alloc_expression(CallExpression::from_ast(scope, call, expected_type).map(Expression::Call)?),
};
expression.enforce_parents(&expression);
Ok(expression)

View File

@ -156,7 +156,7 @@ impl<'a> FromAst<'a, leo_ast::Identifier> for &'a Expression<'a> {
Some(v) => v,
None => {
if value.name.starts_with("aleo1") {
return Ok(scope.alloc_expression(Expression::Constant(Constant {
return Ok(scope.context.alloc_expression(Expression::Constant(Constant {
parent: Cell::new(None),
span: Some(value.span.clone()),
value: ConstValue::Address(value.name.clone()),
@ -172,7 +172,7 @@ impl<'a> FromAst<'a, leo_ast::Identifier> for &'a Expression<'a> {
span: Some(value.span.clone()),
variable,
};
let expression = scope.alloc_expression(Expression::VariableRef(variable_ref));
let expression = scope.context.alloc_expression(Expression::VariableRef(variable_ref));
if let Some(expected_type) = expected_type {
let type_ = expression

View File

@ -38,7 +38,7 @@ pub const STATE_LEAF_PSEUDO_CIRCUIT: &str = "$InputStateLeaf";
impl<'a> Input<'a> {
fn make_header(scope: &'a Scope<'a>, name: &str) -> &'a Circuit<'a> {
scope.alloc_circuit(Circuit {
scope.context.alloc_circuit(Circuit {
id: scope.context.get_id(),
name: RefCell::new(Identifier::new(name.to_string())),
members: RefCell::new(IndexMap::new()),
@ -67,7 +67,7 @@ impl<'a> Input<'a> {
CircuitMember::Variable(Type::Circuit(state_leaf)),
);
let container_circuit = input_scope.alloc_circuit(Circuit {
let container_circuit = input_scope.context.alloc_circuit(Circuit {
id: scope.context.get_id(),
name: RefCell::new(Identifier::new(CONTAINER_PSEUDO_CIRCUIT.to_string())),
members: RefCell::new(container_members),
@ -82,7 +82,7 @@ impl<'a> Input<'a> {
state,
state_leaf,
container_circuit,
container: input_scope.alloc_variable(RefCell::new(crate::InnerVariable {
container: input_scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
id: scope.context.get_id(),
name: Identifier::new("input".to_string()),
type_: Type::Circuit(container_circuit),

View File

@ -97,7 +97,7 @@ impl<'a> Asg<'a> {
) -> Result<Self, AsgConvertError> {
Ok(Self {
context,
asg: InternalProgram::new(context, ast.as_ref(), resolver)?,
asg: Program::new(context, ast.as_ref(), resolver)?,
})
}
@ -106,6 +106,10 @@ impl<'a> Asg<'a> {
&self.asg
}
pub fn into_repr(self) -> Program<'a> {
self.asg
}
// /// Serializes the ast into a JSON string.
// pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
// serde_json::to_string_pretty(&self.asg)
@ -127,7 +131,7 @@ pub fn load_asg<'a, T: ImportResolver<'a>>(
// Parses the Leo file and constructs a grammar ast.
let ast = leo_parser::parse_ast("input.leo", content)?;
InternalProgram::new(context, ast.as_repr(), resolver)
Program::new(context, ast.as_repr(), resolver)
}
pub fn new_alloc_context<'a>() -> Arena<ArenaNode<'a>> {

View File

@ -18,5 +18,5 @@ use crate::Program;
pub use leo_ast::FormattedError;
pub trait AsgPass<'a> {
fn do_pass(asg: &Program<'a>) -> Result<(), FormattedError>;
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError>;
}

View File

@ -56,7 +56,7 @@ impl<'a> Circuit<'a> {
pub(super) fn init(scope: &'a Scope<'a>, value: &leo_ast::Circuit) -> Result<&'a Circuit<'a>, AsgConvertError> {
let new_scope = scope.make_subscope();
let circuit = scope.alloc_circuit(Circuit {
let circuit = scope.context.alloc_circuit(Circuit {
id: scope.context.get_id(),
name: RefCell::new(value.circuit_name.clone()),
members: RefCell::new(IndexMap::new()),

View File

@ -99,7 +99,7 @@ impl<'a> Function<'a> {
mutable,
..
}) => {
let variable = scope.alloc_variable(RefCell::new(crate::InnerVariable {
let variable = scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
id: scope.context.get_id(),
name: identifier.clone(),
type_: scope.resolve_ast_type(&type_)?,
@ -117,7 +117,7 @@ impl<'a> Function<'a> {
if qualifier != FunctionQualifier::Static && scope.circuit_self.get().is_none() {
return Err(AsgConvertError::invalid_self_in_global(&value.span));
}
let function = scope.alloc_function(Function {
let function = scope.context.alloc_function(Function {
id: scope.context.get_id(),
name: RefCell::new(value.identifier.clone()),
output,
@ -138,7 +138,7 @@ impl<'a> Function<'a> {
pub(super) fn fill_from_ast(self: &'a Function<'a>, value: &leo_ast::Function) -> Result<(), AsgConvertError> {
if self.qualifier != FunctionQualifier::Static {
let circuit = self.circuit.get();
let self_variable = self.scope.alloc_variable(RefCell::new(crate::InnerVariable {
let self_variable = self.scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
id: self.scope.context.get_id(),
name: Identifier::new("self".to_string()),
type_: Type::Circuit(circuit.as_ref().unwrap()),
@ -176,7 +176,7 @@ impl<'a> Function<'a> {
}
self.body
.replace(Some(self.scope.alloc_statement(Statement::Block(main_block))));
.replace(Some(self.scope.context.alloc_statement(Statement::Block(main_block))));
Ok(())
}

View File

@ -32,7 +32,7 @@ use std::cell::{Cell, RefCell};
/// Stores the Leo program abstract semantic graph (ASG).
#[derive(Clone)]
pub struct InternalProgram<'a> {
pub struct Program<'a> {
pub context: AsgContext<'a>,
/// The unique id of the program.
@ -55,8 +55,6 @@ pub struct InternalProgram<'a> {
pub scope: &'a Scope<'a>,
}
pub type Program<'a> = InternalProgram<'a>;
/// Enumerates what names are imported from a package.
#[derive(Clone)]
enum ImportSymbol {
@ -123,7 +121,7 @@ fn resolve_import_package_access(
}
}
impl<'a> InternalProgram<'a> {
impl<'a> Program<'a> {
/// Returns a new Leo program ASG from the given Leo program AST and its imports.
///
/// Stages:
@ -225,7 +223,7 @@ impl<'a> InternalProgram<'a> {
_ => unimplemented!(),
};
let scope = import_scope.alloc_scope(Scope {
let scope = import_scope.context.alloc_scope(Scope {
context,
input: Cell::new(Some(Input::new(import_scope))), // we use import_scope to avoid recursive scope ref here
id: context.get_id(),
@ -273,7 +271,7 @@ impl<'a> InternalProgram<'a> {
circuits.insert(name.name.clone(), asg_circuit);
}
Ok(InternalProgram {
Ok(Program {
context,
id: context.get_id(),
name: program.name.clone(),
@ -371,7 +369,7 @@ pub fn reform_ast<'a>(program: &Program<'a>) -> leo_ast::Program {
}
}
impl<'a> Into<leo_ast::Program> for &InternalProgram<'a> {
impl<'a> Into<leo_ast::Program> for &Program<'a> {
fn into(self) -> leo_ast::Program {
leo_ast::Program {
name: self.name.clone(),

View File

@ -26,6 +26,12 @@ pub use monoidal_director::*;
mod monoidal_reducer;
pub use monoidal_reducer::*;
mod reconstructing_reducer;
pub use reconstructing_reducer::*;
mod reconstructing_director;
pub use reconstructing_director::*;
mod visitor;
pub use visitor::*;

View File

@ -176,6 +176,7 @@ impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T,
Statement::Expression(s) => self.reduce_expression_statement(s),
Statement::Iteration(s) => self.reduce_iteration(s),
Statement::Return(s) => self.reduce_return(s),
Statement::Empty(_) => T::default(),
};
self.reducer.reduce_statement(input, value)
@ -272,15 +273,14 @@ impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T,
}
}
#[allow(dead_code)]
impl<'a, T: Monoid, R: MonoidalReducerProgram<'a, T>> MonoidalDirector<'a, T, R> {
fn reduce_function(&mut self, input: &'a Function<'a>) -> T {
pub fn reduce_function(&mut self, input: &'a Function<'a>) -> T {
let body = input.body.get().map(|s| self.reduce_statement(s)).unwrap_or_default();
self.reducer.reduce_function(input, body)
}
fn reduce_circuit_member(&mut self, input: &CircuitMember<'a>) -> T {
pub fn reduce_circuit_member(&mut self, input: &CircuitMember<'a>) -> T {
let function = match input {
CircuitMember::Function(f) => Some(self.reduce_function(f)),
_ => None,
@ -289,7 +289,7 @@ impl<'a, T: Monoid, R: MonoidalReducerProgram<'a, T>> MonoidalDirector<'a, T, R>
self.reducer.reduce_circuit_member(input, function)
}
fn reduce_circuit(&mut self, input: &'a Circuit<'a>) -> T {
pub fn reduce_circuit(&mut self, input: &'a Circuit<'a>) -> T {
let members = input
.members
.borrow()
@ -300,7 +300,7 @@ impl<'a, T: Monoid, R: MonoidalReducerProgram<'a, T>> MonoidalDirector<'a, T, R>
self.reducer.reduce_circuit(input, members)
}
fn reduce_program(&mut self, input: &Program<'a>) -> T {
pub fn reduce_program(&mut self, input: &Program<'a>) -> T {
let imported_modules = input
.imported_modules
.iter()

View File

@ -157,13 +157,7 @@ pub trait MonoidalReducerProgram<'a, T: Monoid>: MonoidalReducerStatement<'a, T>
T::default().append_all(members.into_iter())
}
fn reduce_program(
&mut self,
input: &InternalProgram,
imported_modules: Vec<T>,
functions: Vec<T>,
circuits: Vec<T>,
) -> T {
fn reduce_program(&mut self, input: &Program, imported_modules: Vec<T>, functions: Vec<T>, circuits: Vec<T>) -> T {
T::default()
.append_all(imported_modules.into_iter())
.append_all(functions.into_iter())

View File

@ -0,0 +1,345 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::{expression::*, program::*, statement::*, AsgContext};
/*
reconstructing director tries to maintain a normalized ASG but may require renormalization under the following circumstances:
* breaking strict reducer model (i.e. live mutations)
* dropping or duplicating branches
*/
pub struct ReconstructingDirector<'a, R: ReconstructingReducerExpression<'a>> {
context: AsgContext<'a>,
reducer: R,
}
impl<'a, R: ReconstructingReducerExpression<'a>> ReconstructingDirector<'a, R> {
pub fn new(context: AsgContext<'a>, reducer: R) -> Self {
Self { context, reducer }
}
pub fn reducer(self) -> R {
self.reducer
}
pub fn reduce_expression(&mut self, input: &'a Expression<'a>) -> &'a Expression<'a> {
let value = match input.clone() {
Expression::ArrayAccess(e) => self.reduce_array_access(e),
Expression::ArrayInit(e) => self.reduce_array_init(e),
Expression::ArrayInline(e) => self.reduce_array_inline(e),
Expression::ArrayRangeAccess(e) => self.reduce_array_range_access(e),
Expression::Binary(e) => self.reduce_binary(e),
Expression::Call(e) => self.reduce_call(e),
Expression::CircuitAccess(e) => self.reduce_circuit_access(e),
Expression::CircuitInit(e) => self.reduce_circuit_init(e),
Expression::Ternary(e) => self.reduce_ternary_expression(e),
Expression::Cast(e) => self.reduce_cast_expression(e),
Expression::Constant(e) => self.reduce_constant(e),
Expression::TupleAccess(e) => self.reduce_tuple_access(e),
Expression::TupleInit(e) => self.reduce_tuple_init(e),
Expression::Unary(e) => self.reduce_unary(e),
Expression::VariableRef(e) => {
{
let mut variable = e.variable.borrow_mut();
let index = variable.references.iter().position(|x| (*x).ptr_eq(input));
if let Some(index) = index {
variable.references.remove(index);
}
}
self.reduce_variable_ref(e)
}
};
let allocated = self
.context
.alloc_expression(self.reducer.reduce_expression(input, value));
if let Expression::VariableRef(reference) = allocated {
let mut variable = reference.variable.borrow_mut();
variable.references.push(allocated);
}
allocated
}
pub fn reduce_array_access(&mut self, input: ArrayAccessExpression<'a>) -> Expression<'a> {
let array = self.reduce_expression(input.array.get());
let index = self.reduce_expression(input.index.get());
self.reducer.reduce_array_access(input, array, index)
}
pub fn reduce_array_init(&mut self, input: ArrayInitExpression<'a>) -> Expression<'a> {
let element = self.reduce_expression(input.element.get());
self.reducer.reduce_array_init(input, element)
}
pub fn reduce_array_inline(&mut self, input: ArrayInlineExpression<'a>) -> Expression<'a> {
let elements = input
.elements
.iter()
.map(|(x, spread)| (self.reduce_expression(x.get()), *spread))
.collect();
self.reducer.reduce_array_inline(input, elements)
}
pub fn reduce_array_range_access(&mut self, input: ArrayRangeAccessExpression<'a>) -> Expression<'a> {
let array = self.reduce_expression(input.array.get());
let left = input.left.get().map(|e| self.reduce_expression(e));
let right = input.right.get().map(|e| self.reduce_expression(e));
self.reducer.reduce_array_range_access(input, array, left, right)
}
pub fn reduce_binary(&mut self, input: BinaryExpression<'a>) -> Expression<'a> {
let left = self.reduce_expression(input.left.get());
let right = self.reduce_expression(input.right.get());
self.reducer.reduce_binary(input, left, right)
}
pub fn reduce_call(&mut self, input: CallExpression<'a>) -> Expression<'a> {
let target = input.target.get().map(|e| self.reduce_expression(e));
let arguments = input
.arguments
.iter()
.map(|e| self.reduce_expression(e.get()))
.collect();
self.reducer.reduce_call(input, target, arguments)
}
pub fn reduce_circuit_access(&mut self, input: CircuitAccessExpression<'a>) -> Expression<'a> {
let target = input.target.get().map(|e| self.reduce_expression(e));
self.reducer.reduce_circuit_access(input, target)
}
pub fn reduce_circuit_init(&mut self, input: CircuitInitExpression<'a>) -> Expression<'a> {
let values = input
.values
.iter()
.map(|(ident, e)| (ident.clone(), self.reduce_expression(e.get())))
.collect();
self.reducer.reduce_circuit_init(input, values)
}
pub fn reduce_ternary_expression(&mut self, input: TernaryExpression<'a>) -> Expression<'a> {
let condition = self.reduce_expression(input.condition.get());
let if_true = self.reduce_expression(input.if_true.get());
let if_false = self.reduce_expression(input.if_false.get());
self.reducer
.reduce_ternary_expression(input, condition, if_true, if_false)
}
pub fn reduce_cast_expression(&mut self, input: CastExpression<'a>) -> Expression<'a> {
let inner = self.reduce_expression(input.inner.get());
self.reducer.reduce_cast_expression(input, inner)
}
pub fn reduce_constant(&mut self, input: Constant<'a>) -> Expression<'a> {
self.reducer.reduce_constant(input)
}
pub fn reduce_tuple_access(&mut self, input: TupleAccessExpression<'a>) -> Expression<'a> {
let tuple_ref = self.reduce_expression(input.tuple_ref.get());
self.reducer.reduce_tuple_access(input, tuple_ref)
}
pub fn reduce_tuple_init(&mut self, input: TupleInitExpression<'a>) -> Expression<'a> {
let values = input.elements.iter().map(|e| self.reduce_expression(e.get())).collect();
self.reducer.reduce_tuple_init(input, values)
}
pub fn reduce_unary(&mut self, input: UnaryExpression<'a>) -> Expression<'a> {
let inner = self.reduce_expression(input.inner.get());
self.reducer.reduce_unary(input, inner)
}
pub fn reduce_variable_ref(&mut self, input: VariableRef<'a>) -> Expression<'a> {
self.reducer.reduce_variable_ref(input)
}
}
impl<'a, R: ReconstructingReducerStatement<'a>> ReconstructingDirector<'a, R> {
pub fn reduce_statement(&mut self, input: &'a Statement<'a>) -> &'a Statement<'a> {
let value = match input.clone() {
Statement::Assign(s) => self.reduce_assign(s),
Statement::Block(s) => self.reduce_block(s),
Statement::Conditional(s) => self.reduce_conditional_statement(s),
Statement::Console(s) => self.reduce_console(s),
Statement::Definition(s) => self.reduce_definition(s),
Statement::Expression(s) => self.reduce_expression_statement(s),
Statement::Iteration(s) => self.reduce_iteration(s),
Statement::Return(s) => self.reduce_return(s),
x @ Statement::Empty(_) => x,
};
self.reducer.reduce_statement_alloc(self.context, input, value)
}
pub fn reduce_assign_access(&mut self, input: AssignAccess<'a>) -> AssignAccess<'a> {
match &input {
AssignAccess::ArrayRange(left, right) => {
let left = left.get().map(|e| self.reduce_expression(e));
let right = right.get().map(|e| self.reduce_expression(e));
self.reducer.reduce_assign_access_range(input, left, right)
}
AssignAccess::ArrayIndex(index) => {
let index = self.reduce_expression(index.get());
self.reducer.reduce_assign_access_index(input, index)
}
_ => self.reducer.reduce_assign_access(input),
}
}
pub fn reduce_assign(&mut self, input: AssignStatement<'a>) -> Statement<'a> {
let accesses = input
.target_accesses
.iter()
.map(|x| self.reduce_assign_access(x.clone()))
.collect();
let value = self.reduce_expression(input.value.get());
self.reducer.reduce_assign(input, accesses, value)
}
pub fn reduce_block(&mut self, input: BlockStatement<'a>) -> Statement<'a> {
let statements = input
.statements
.iter()
.map(|x| self.reduce_statement(x.get()))
.collect();
self.reducer.reduce_block(input, statements)
}
pub fn reduce_conditional_statement(&mut self, input: ConditionalStatement<'a>) -> Statement<'a> {
let condition = self.reduce_expression(input.condition.get());
let if_true = self.reduce_statement(input.result.get());
let if_false = input.next.get().map(|s| self.reduce_statement(s));
self.reducer
.reduce_conditional_statement(input, condition, if_true, if_false)
}
pub fn reduce_formatted_string(&mut self, input: FormattedString<'a>) -> FormattedString<'a> {
let parameters = input
.parameters
.iter()
.map(|e| self.reduce_expression(e.get()))
.collect();
self.reducer.reduce_formatted_string(input, parameters)
}
pub fn reduce_console(&mut self, input: ConsoleStatement<'a>) -> Statement<'a> {
match &input.function {
ConsoleFunction::Assert(argument) => {
let argument = self.reduce_expression(argument.get());
self.reducer.reduce_console_assert(input, argument)
}
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
let formatted = self.reduce_formatted_string(f.clone());
self.reducer.reduce_console_log(input, formatted)
}
}
}
pub fn reduce_definition(&mut self, input: DefinitionStatement<'a>) -> Statement<'a> {
let value = self.reduce_expression(input.value.get());
self.reducer.reduce_definition(input, value)
}
pub fn reduce_expression_statement(&mut self, input: ExpressionStatement<'a>) -> Statement<'a> {
let value = self.reduce_expression(input.expression.get());
self.reducer.reduce_expression_statement(input, value)
}
pub fn reduce_iteration(&mut self, input: IterationStatement<'a>) -> Statement<'a> {
let start = self.reduce_expression(input.start.get());
let stop = self.reduce_expression(input.stop.get());
let body = self.reduce_statement(input.body.get());
self.reducer.reduce_iteration(input, start, stop, body)
}
pub fn reduce_return(&mut self, input: ReturnStatement<'a>) -> Statement<'a> {
let value = self.reduce_expression(input.expression.get());
self.reducer.reduce_return(input, value)
}
}
#[allow(dead_code)]
impl<'a, R: ReconstructingReducerProgram<'a>> ReconstructingDirector<'a, R> {
fn reduce_function(&mut self, input: &'a Function<'a>) -> &'a Function<'a> {
let body = input.body.get().map(|s| self.reduce_statement(s));
self.reducer.reduce_function(input, body)
}
pub fn reduce_circuit_member(&mut self, input: CircuitMember<'a>) -> CircuitMember<'a> {
match input {
CircuitMember::Function(function) => {
let function = self.reduce_function(function);
self.reducer.reduce_circuit_member_function(input, function)
}
CircuitMember::Variable(_) => self.reducer.reduce_circuit_member_variable(input),
}
}
pub fn reduce_circuit(&mut self, input: &'a Circuit<'a>) -> &'a Circuit<'a> {
let members = input
.members
.borrow()
.iter()
.map(|(_, member)| self.reduce_circuit_member(member.clone()))
.collect();
self.reducer.reduce_circuit(input, members)
}
pub fn reduce_program(&mut self, input: Program<'a>) -> Program<'a> {
let imported_modules = input
.imported_modules
.iter()
.map(|(module, import)| (module.clone(), self.reduce_program(import.clone())))
.collect();
let functions = input
.functions
.iter()
.map(|(name, f)| (name.clone(), self.reduce_function(f)))
.collect();
let circuits = input
.circuits
.iter()
.map(|(name, c)| (name.clone(), self.reduce_circuit(c)))
.collect();
self.reducer
.reduce_program(input, imported_modules, functions, circuits)
}
}

View File

@ -0,0 +1,402 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use std::cell::Cell;
use leo_ast::Identifier;
use crate::{expression::*, program::*, statement::*, AsgContext};
#[allow(unused_variables)]
pub trait ReconstructingReducerExpression<'a> {
fn reduce_expression(&mut self, input: &'a Expression<'a>, value: Expression<'a>) -> Expression<'a> {
value
}
fn reduce_array_access(
&mut self,
input: ArrayAccessExpression<'a>,
array: &'a Expression<'a>,
index: &'a Expression<'a>,
) -> Expression<'a> {
Expression::ArrayAccess(ArrayAccessExpression {
parent: input.parent,
array: Cell::new(array),
index: Cell::new(index),
span: input.span,
})
}
fn reduce_array_init(&mut self, input: ArrayInitExpression<'a>, element: &'a Expression<'a>) -> Expression<'a> {
Expression::ArrayInit(ArrayInitExpression {
parent: input.parent,
element: Cell::new(element),
len: input.len,
span: input.span,
})
}
fn reduce_array_inline(
&mut self,
input: ArrayInlineExpression<'a>,
elements: Vec<(&'a Expression<'a>, bool)>,
) -> Expression<'a> {
Expression::ArrayInline(ArrayInlineExpression {
parent: input.parent,
elements: elements.into_iter().map(|x| (Cell::new(x.0), x.1)).collect(),
span: input.span,
})
}
fn reduce_array_range_access(
&mut self,
input: ArrayRangeAccessExpression<'a>,
array: &'a Expression<'a>,
left: Option<&'a Expression<'a>>,
right: Option<&'a Expression<'a>>,
) -> Expression<'a> {
Expression::ArrayRangeAccess(ArrayRangeAccessExpression {
parent: input.parent,
array: Cell::new(array),
left: Cell::new(left),
right: Cell::new(right),
span: input.span,
})
}
fn reduce_binary(
&mut self,
input: BinaryExpression<'a>,
left: &'a Expression<'a>,
right: &'a Expression<'a>,
) -> Expression<'a> {
Expression::Binary(BinaryExpression {
parent: input.parent,
left: Cell::new(left),
right: Cell::new(right),
span: input.span,
operation: input.operation,
})
}
fn reduce_call(
&mut self,
input: CallExpression<'a>,
target: Option<&'a Expression<'a>>,
arguments: Vec<&'a Expression<'a>>,
) -> Expression<'a> {
Expression::Call(CallExpression {
parent: input.parent,
function: input.function,
target: Cell::new(target),
arguments: arguments.into_iter().map(Cell::new).collect(),
span: input.span,
})
}
fn reduce_circuit_access(
&mut self,
input: CircuitAccessExpression<'a>,
target: Option<&'a Expression<'a>>,
) -> Expression<'a> {
Expression::CircuitAccess(CircuitAccessExpression {
parent: input.parent,
circuit: input.circuit,
target: Cell::new(target),
member: input.member,
span: input.span,
})
}
fn reduce_circuit_init(
&mut self,
input: CircuitInitExpression<'a>,
values: Vec<(Identifier, &'a Expression<'a>)>,
) -> Expression<'a> {
Expression::CircuitInit(CircuitInitExpression {
parent: input.parent,
circuit: input.circuit,
values: values.into_iter().map(|x| (x.0, Cell::new(x.1))).collect(),
span: input.span,
})
}
fn reduce_ternary_expression(
&mut self,
input: TernaryExpression<'a>,
condition: &'a Expression<'a>,
if_true: &'a Expression<'a>,
if_false: &'a Expression<'a>,
) -> Expression<'a> {
Expression::Ternary(TernaryExpression {
parent: input.parent,
condition: Cell::new(condition),
if_true: Cell::new(if_true),
if_false: Cell::new(if_false),
span: input.span,
})
}
fn reduce_cast_expression(&mut self, input: CastExpression<'a>, inner: &'a Expression<'a>) -> Expression<'a> {
Expression::Cast(CastExpression {
parent: input.parent,
inner: Cell::new(inner),
target_type: input.target_type,
span: input.span,
})
}
fn reduce_constant(&mut self, input: Constant<'a>) -> Expression<'a> {
Expression::Constant(input)
}
fn reduce_tuple_access(
&mut self,
input: TupleAccessExpression<'a>,
tuple_ref: &'a Expression<'a>,
) -> Expression<'a> {
Expression::TupleAccess(TupleAccessExpression {
parent: input.parent,
tuple_ref: Cell::new(tuple_ref),
index: input.index,
span: input.span,
})
}
fn reduce_tuple_init(&mut self, input: TupleInitExpression<'a>, values: Vec<&'a Expression<'a>>) -> Expression<'a> {
Expression::TupleInit(TupleInitExpression {
parent: input.parent,
elements: values.into_iter().map(Cell::new).collect(),
span: input.span,
})
}
fn reduce_unary(&mut self, input: UnaryExpression<'a>, inner: &'a Expression<'a>) -> Expression<'a> {
Expression::Unary(UnaryExpression {
parent: input.parent,
inner: Cell::new(inner),
span: input.span,
operation: input.operation,
})
}
fn reduce_variable_ref(&mut self, input: VariableRef<'a>) -> Expression<'a> {
Expression::VariableRef(input)
}
}
#[allow(unused_variables)]
pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a> {
fn reduce_statement_alloc(
&mut self,
context: AsgContext<'a>,
input: &'a Statement<'a>,
value: Statement<'a>,
) -> &'a Statement<'a> {
context.alloc_statement(value)
}
fn reduce_statement(&mut self, input: &'a Statement<'a>, value: Statement<'a>) -> Statement<'a> {
value
}
fn reduce_assign_access_range(
&mut self,
input: AssignAccess<'a>,
left: Option<&'a Expression<'a>>,
right: Option<&'a Expression<'a>>,
) -> AssignAccess<'a> {
AssignAccess::ArrayRange(Cell::new(left), Cell::new(right))
}
fn reduce_assign_access_index(&mut self, input: AssignAccess<'a>, index: &'a Expression<'a>) -> AssignAccess<'a> {
AssignAccess::ArrayIndex(Cell::new(index))
}
fn reduce_assign_access(&mut self, input: AssignAccess<'a>) -> AssignAccess<'a> {
input
}
fn reduce_assign(
&mut self,
input: AssignStatement<'a>,
accesses: Vec<AssignAccess<'a>>,
value: &'a Expression<'a>,
) -> Statement<'a> {
Statement::Assign(AssignStatement {
parent: input.parent,
span: input.span,
operation: input.operation,
target_accesses: accesses,
target_variable: input.target_variable,
value: Cell::new(value),
})
}
fn reduce_block(&mut self, input: BlockStatement<'a>, statements: Vec<&'a Statement<'a>>) -> Statement<'a> {
Statement::Block(BlockStatement {
parent: input.parent,
span: input.span,
statements: statements.into_iter().map(Cell::new).collect(),
scope: input.scope,
})
}
fn reduce_conditional_statement(
&mut self,
input: ConditionalStatement<'a>,
condition: &'a Expression<'a>,
if_true: &'a Statement<'a>,
if_false: Option<&'a Statement<'a>>,
) -> Statement<'a> {
Statement::Conditional(ConditionalStatement {
parent: input.parent,
span: input.span,
condition: Cell::new(condition),
result: Cell::new(if_true),
next: Cell::new(if_false),
})
}
fn reduce_formatted_string(
&mut self,
input: FormattedString<'a>,
parameters: Vec<&'a Expression<'a>>,
) -> FormattedString<'a> {
FormattedString {
span: input.span,
parts: input.parts,
parameters: parameters.into_iter().map(Cell::new).collect(),
}
}
fn reduce_console_assert(&mut self, input: ConsoleStatement<'a>, argument: &'a Expression<'a>) -> Statement<'a> {
assert!(matches!(input.function, ConsoleFunction::Assert(_)));
Statement::Console(ConsoleStatement {
parent: input.parent,
span: input.span,
function: ConsoleFunction::Assert(Cell::new(argument)),
})
}
fn reduce_console_log(&mut self, input: ConsoleStatement<'a>, argument: FormattedString<'a>) -> Statement<'a> {
assert!(!matches!(input.function, ConsoleFunction::Assert(_)));
Statement::Console(ConsoleStatement {
parent: input.parent,
span: input.span,
function: match input.function {
ConsoleFunction::Assert(_) => unimplemented!(),
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(argument),
ConsoleFunction::Error(_) => ConsoleFunction::Error(argument),
ConsoleFunction::Log(_) => ConsoleFunction::Log(argument),
},
})
}
fn reduce_definition(&mut self, input: DefinitionStatement<'a>, value: &'a Expression<'a>) -> Statement<'a> {
Statement::Definition(DefinitionStatement {
parent: input.parent,
span: input.span,
variables: input.variables,
value: Cell::new(value),
})
}
fn reduce_expression_statement(
&mut self,
input: ExpressionStatement<'a>,
expression: &'a Expression<'a>,
) -> Statement<'a> {
Statement::Expression(ExpressionStatement {
parent: input.parent,
span: input.span,
expression: Cell::new(expression),
})
}
fn reduce_iteration(
&mut self,
input: IterationStatement<'a>,
start: &'a Expression<'a>,
stop: &'a Expression<'a>,
body: &'a Statement<'a>,
) -> Statement<'a> {
Statement::Iteration(IterationStatement {
parent: input.parent,
span: input.span,
variable: input.variable,
start: Cell::new(start),
stop: Cell::new(stop),
body: Cell::new(body),
})
}
fn reduce_return(&mut self, input: ReturnStatement<'a>, value: &'a Expression<'a>) -> Statement<'a> {
Statement::Return(ReturnStatement {
parent: input.parent,
span: input.span,
expression: Cell::new(value),
})
}
}
#[allow(unused_variables)]
pub trait ReconstructingReducerProgram<'a>: ReconstructingReducerStatement<'a> {
// todo: this is kind of hacky
fn reduce_function(&mut self, input: &'a Function<'a>, body: Option<&'a Statement<'a>>) -> &'a Function<'a> {
input.body.set(body);
input
}
fn reduce_circuit_member_variable(&mut self, input: CircuitMember<'a>) -> CircuitMember<'a> {
input
}
fn reduce_circuit_member_function(
&mut self,
input: CircuitMember<'a>,
function: &'a Function<'a>,
) -> CircuitMember<'a> {
CircuitMember::Function(function)
}
// todo: this is kind of hacky
fn reduce_circuit(&mut self, input: &'a Circuit<'a>, members: Vec<CircuitMember<'a>>) -> &'a Circuit<'a> {
let mut input_members = input.members.borrow_mut();
for ((name, input_member), member) in input_members.iter_mut().zip(members) {
*input_member = member;
}
input
}
fn reduce_program(
&mut self,
input: Program<'a>,
imported_modules: Vec<(String, Program<'a>)>,
functions: Vec<(String, &'a Function<'a>)>,
circuits: Vec<(String, &'a Circuit<'a>)>,
) -> Program<'a> {
Program {
context: input.context,
id: input.id,
name: input.name,
imported_modules: imported_modules.into_iter().collect(),
functions: functions.into_iter().collect(),
circuits: circuits.into_iter().collect(),
scope: input.scope,
}
}
}

View File

@ -248,6 +248,7 @@ impl<'a, R: StatementVisitor<'a>> VisitorDirector<'a, R> {
Statement::Expression(s) => self.visit_expression_statement(s),
Statement::Iteration(s) => self.visit_iteration(s),
Statement::Return(s) => self.visit_return(s),
Statement::Empty(_) => Ok(()),
},
x => x.into(),
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{ArenaNode, AsgContext, AsgConvertError, Circuit, Expression, Function, Input, Statement, Type, Variable};
use crate::{AsgContext, AsgConvertError, Circuit, Function, Input, Type, Variable};
use indexmap::IndexMap;
use std::cell::{Cell, RefCell};
@ -51,48 +51,6 @@ pub struct Scope<'a> {
#[allow(clippy::mut_from_ref)]
impl<'a> Scope<'a> {
pub fn alloc_expression(&'a self, expr: Expression<'a>) -> &'a mut Expression<'a> {
match self.context.arena.alloc(ArenaNode::Expression(expr)) {
ArenaNode::Expression(e) => e,
_ => unimplemented!(),
}
}
pub fn alloc_statement(&'a self, statement: Statement<'a>) -> &'a mut Statement<'a> {
match self.context.arena.alloc(ArenaNode::Statement(statement)) {
ArenaNode::Statement(e) => e,
_ => unimplemented!(),
}
}
pub fn alloc_variable(&'a self, variable: Variable<'a>) -> &'a mut Variable<'a> {
match self.context.arena.alloc(ArenaNode::Variable(variable)) {
ArenaNode::Variable(e) => e,
_ => unimplemented!(),
}
}
pub fn alloc_scope(&'a self, scope: Scope<'a>) -> &'a mut Scope<'a> {
match self.context.arena.alloc(ArenaNode::Scope(scope)) {
ArenaNode::Scope(e) => e,
_ => unimplemented!(),
}
}
pub fn alloc_circuit(&'a self, circuit: Circuit<'a>) -> &'a mut Circuit<'a> {
match self.context.arena.alloc(ArenaNode::Circuit(circuit)) {
ArenaNode::Circuit(e) => e,
_ => unimplemented!(),
}
}
pub fn alloc_function(&'a self, function: Function<'a>) -> &'a mut Function<'a> {
match self.context.arena.alloc(ArenaNode::Function(function)) {
ArenaNode::Function(e) => e,
_ => unimplemented!(),
}
}
///
/// Returns a reference to the variable corresponding to the name.
///
@ -195,7 +153,7 @@ impl<'a> Scope<'a> {
/// Returns a new scope given a parent scope.
///
pub fn make_subscope(self: &'a Scope<'a>) -> &'a Scope<'a> {
self.alloc_scope(Scope::<'a> {
self.context.alloc_scope(Scope::<'a> {
context: self.context,
id: self.context.get_id(),
parent_scope: Cell::new(Some(self)),

View File

@ -217,7 +217,7 @@ impl<'a> FromAst<'a, leo_ast::AssignStatement> for &'a Statement<'a> {
}
let value = <&Expression<'a>>::from_ast(scope, &statement.value, target_type)?;
let statement = scope.alloc_statement(Statement::Assign(AssignStatement {
let statement = scope.context.alloc_statement(Statement::Assign(AssignStatement {
parent: Cell::new(None),
span: Some(statement.span.clone()),
operation: statement.operation.clone(),

View File

@ -40,7 +40,7 @@ impl<'a> FromAst<'a, leo_ast::ConditionalStatement> for ConditionalStatement<'a>
_expected_type: Option<PartialType<'a>>,
) -> Result<Self, AsgConvertError> {
let condition = <&Expression<'a>>::from_ast(scope, &statement.condition, Some(Type::Boolean.into()))?;
let result = scope.alloc_statement(Statement::Block(BlockStatement::from_ast(
let result = scope.context.alloc_statement(Statement::Block(BlockStatement::from_ast(
scope,
&statement.block,
None,

View File

@ -89,7 +89,7 @@ impl<'a> FromAst<'a, leo_ast::DefinitionStatement> for &'a Statement<'a> {
}
for (variable, type_) in statement.variable_names.iter().zip(output_types.into_iter()) {
variables.push(&*scope.alloc_variable(RefCell::new(InnerVariable {
variables.push(&*scope.context.alloc_variable(RefCell::new(InnerVariable {
id: scope.context.get_id(),
name: variable.identifier.clone(),
type_:
@ -109,12 +109,14 @@ impl<'a> FromAst<'a, leo_ast::DefinitionStatement> for &'a Statement<'a> {
.insert(variable.borrow().name.name.clone(), *variable);
}
let statement = scope.alloc_statement(Statement::Definition(DefinitionStatement {
parent: Cell::new(None),
span: Some(statement.span.clone()),
variables: variables.clone(),
value: Cell::new(value),
}));
let statement = scope
.context
.alloc_statement(Statement::Definition(DefinitionStatement {
parent: Cell::new(None),
span: Some(statement.span.clone()),
variables: variables.clone(),
value: Cell::new(value),
}));
for variable in variables {
variable.borrow_mut().assignments.push(statement);

View File

@ -57,7 +57,7 @@ impl<'a> FromAst<'a, leo_ast::IterationStatement> for &'a Statement<'a> {
let expected_index_type = Some(PartialType::Integer(None, Some(IntegerType::U32)));
let start = <&Expression<'a>>::from_ast(scope, &statement.start, expected_index_type.clone())?;
let stop = <&Expression<'a>>::from_ast(scope, &statement.stop, expected_index_type)?;
let variable = scope.alloc_variable(RefCell::new(InnerVariable {
let variable = scope.context.alloc_variable(RefCell::new(InnerVariable {
id: scope.context.get_id(),
name: statement.variable.clone(),
type_: start
@ -74,17 +74,21 @@ impl<'a> FromAst<'a, leo_ast::IterationStatement> for &'a Statement<'a> {
.borrow_mut()
.insert(statement.variable.name.clone(), variable);
let statement = scope.alloc_statement(Statement::Iteration(IterationStatement {
let statement = scope.context.alloc_statement(Statement::Iteration(IterationStatement {
parent: Cell::new(None),
span: Some(statement.span.clone()),
variable,
stop: Cell::new(stop),
start: Cell::new(start),
body: Cell::new(scope.alloc_statement(Statement::Block(crate::BlockStatement::from_ast(
scope,
&statement.block,
None,
)?))),
body: Cell::new(
scope
.context
.alloc_statement(Statement::Block(crate::BlockStatement::from_ast(
scope,
&statement.block,
None,
)?)),
),
}));
variable.borrow_mut().assignments.push(statement);
Ok(statement)

View File

@ -54,6 +54,7 @@ pub enum Statement<'a> {
Console(ConsoleStatement<'a>),
Expression(ExpressionStatement<'a>),
Block(BlockStatement<'a>),
Empty(Option<Span>),
}
impl<'a> Node for Statement<'a> {
@ -68,6 +69,7 @@ impl<'a> Node for Statement<'a> {
Console(s) => s.span(),
Expression(s) => s.span(),
Block(s) => s.span(),
Empty(s) => s.as_ref(),
}
}
}
@ -80,24 +82,32 @@ impl<'a> FromAst<'a, leo_ast::Statement> for &'a Statement<'a> {
) -> Result<&'a Statement<'a>, AsgConvertError> {
use leo_ast::Statement::*;
Ok(match value {
Return(statement) => {
scope.alloc_statement(Statement::Return(ReturnStatement::from_ast(scope, statement, None)?))
}
Return(statement) => scope
.context
.alloc_statement(Statement::Return(ReturnStatement::from_ast(scope, statement, None)?)),
Definition(statement) => Self::from_ast(scope, statement, None)?,
Assign(statement) => Self::from_ast(scope, statement, None)?,
Conditional(statement) => scope.alloc_statement(Statement::Conditional(ConditionalStatement::from_ast(
scope, statement, None,
)?)),
Conditional(statement) => {
scope
.context
.alloc_statement(Statement::Conditional(ConditionalStatement::from_ast(
scope, statement, None,
)?))
}
Iteration(statement) => Self::from_ast(scope, statement, None)?,
Console(statement) => {
scope.alloc_statement(Statement::Console(ConsoleStatement::from_ast(scope, statement, None)?))
}
Expression(statement) => scope.alloc_statement(Statement::Expression(ExpressionStatement::from_ast(
scope, statement, None,
)?)),
Block(statement) => {
scope.alloc_statement(Statement::Block(BlockStatement::from_ast(scope, statement, None)?))
Console(statement) => scope
.context
.alloc_statement(Statement::Console(ConsoleStatement::from_ast(scope, statement, None)?)),
Expression(statement) => {
scope
.context
.alloc_statement(Statement::Expression(ExpressionStatement::from_ast(
scope, statement, None,
)?))
}
Block(statement) => scope
.context
.alloc_statement(Statement::Block(BlockStatement::from_ast(scope, statement, None)?)),
})
}
}
@ -114,6 +124,7 @@ impl<'a> Into<leo_ast::Statement> for &Statement<'a> {
Console(statement) => leo_ast::Statement::Console(statement.into()),
Expression(statement) => leo_ast::Statement::Expression(statement.into()),
Block(statement) => leo_ast::Statement::Block(statement.into()),
Empty(_) => unimplemented!(),
}
}
}

View File

@ -32,7 +32,7 @@ fn load_asg_imports<'a, T: ImportResolver<'a>>(
imports: &mut T,
) -> Result<Program<'a>, AsgConvertError> {
let ast = parse_ast(&TESTING_FILEPATH, program_string)?;
InternalProgram::new(context, &ast.as_repr(), imports)
Program::new(context, &ast.as_repr(), imports)
}
fn mocked_resolver(_context: AsgContext<'_>) -> MockedImportResolver<'_> {

View File

@ -26,8 +26,8 @@ use crate::{
};
use indexmap::IndexMap;
pub use leo_asg::{new_context, AsgContext as Context, AsgContext};
use leo_asg::{Asg, AsgPass, FormattedError};
use leo_ast::{Input, LeoError, MainInput, Program};
use leo_asg::{Asg, AsgPass, FormattedError, Program};
use leo_ast::{Input, LeoError, MainInput, Program as AstProgram};
use leo_input::LeoInputParser;
use leo_package::inputs::InputPairs;
use leo_parser::parse_ast;
@ -64,10 +64,10 @@ pub struct Compiler<'a, F: PrimeField, G: GroupType<F>> {
program_name: String,
main_file_path: PathBuf,
output_directory: PathBuf,
program: Program,
program: AstProgram,
program_input: Input,
context: AsgContext<'a>,
asg: Option<Asg<'a>>,
asg: Option<Program<'a>>,
file_contents: RefCell<IndexMap<String, Rc<Vec<String>>>>,
options: CompilerOptions,
_engine: PhantomData<F>,
@ -88,7 +88,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
program_name: package_name.clone(),
main_file_path,
output_directory,
program: Program::new(package_name),
program: AstProgram::new(package_name),
program_input: Input::new(),
asg: None,
context,
@ -253,15 +253,22 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
tracing::debug!("ASG generation complete");
// Store the ASG.
self.asg = Some(asg);
self.asg = Some(asg.into_repr());
self.do_asg_passes().map_err(CompilerError::AsgPassError)?;
Ok(())
}
fn do_asg_passes(&self) -> Result<(), FormattedError> {
fn do_asg_passes(&mut self) -> Result<(), FormattedError> {
assert!(self.asg.is_some());
if self.options.constant_folding_enabled {
leo_asg_passes::ConstantFolding::do_pass(self.asg.as_ref().unwrap().as_repr())?;
let asg = self.asg.take().unwrap();
self.asg = Some(leo_asg_passes::ConstantFolding::do_pass(asg)?);
}
if self.options.dead_code_elimination_enabled {
let asg = self.asg.take().unwrap();
self.asg = Some(leo_asg_passes::DeadCodeElimination::do_pass(asg)?);
}
Ok(())
@ -271,8 +278,6 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
/// Synthesizes the circuit with program input to verify correctness.
///
pub fn compile_constraints<CS: ConstraintSystem<F>>(&self, cs: &mut CS) -> Result<OutputBytes, CompilerError> {
self.do_asg_passes().map_err(CompilerError::AsgPassError)?;
generate_constraints::<F, G, CS>(cs, &self.asg.as_ref().unwrap(), &self.program_input).map_err(|mut error| {
if let Some(path) = error.get_path().map(|x| x.to_string()) {
let content = match self.resolve_content(&path) {
@ -289,8 +294,6 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
/// Synthesizes the circuit for test functions with program input.
///
pub fn compile_test_constraints(self, input_pairs: InputPairs) -> Result<(u32, u32), CompilerError> {
self.do_asg_passes().map_err(CompilerError::AsgPassError)?;
generate_test_constraints::<F, G>(
&self.asg.as_ref().unwrap(),
input_pairs,

View File

@ -17,7 +17,7 @@
//! Generates R1CS constraints for a compiled Leo program.
use crate::{errors::CompilerError, ConstrainedProgram, GroupType, OutputBytes, OutputFile};
use leo_asg::Asg;
use leo_asg::Program;
use leo_ast::{Input, LeoError};
use leo_input::LeoInputParser;
use leo_package::inputs::InputPairs;
@ -28,10 +28,9 @@ use std::path::Path;
pub fn generate_constraints<'a, F: PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
cs: &mut CS,
asg: &Asg<'a>,
program: &Program<'a>,
input: &Input,
) -> Result<OutputBytes, CompilerError> {
let program = asg.as_repr();
let mut resolved_program = ConstrainedProgram::<F, G>::new(program.clone());
let main = {
@ -49,12 +48,11 @@ pub fn generate_constraints<'a, F: PrimeField, G: GroupType<F>, CS: ConstraintSy
}
pub fn generate_test_constraints<'a, F: PrimeField, G: GroupType<F>>(
asg: &Asg<'a>,
program: &Program<'a>,
input: InputPairs,
main_file_path: &Path,
output_directory: &Path,
) -> Result<(u32, u32), CompilerError> {
let program = asg.as_repr();
let mut resolved_program = ConstrainedProgram::<F, G>::new(program.clone());
let program_name = program.name.clone();

View File

@ -75,13 +75,13 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
// Store a new variable for every allocated main function input
self.store(input_variable.id, input_value);
}
arguments.push(Cell::new(&*function.scope.alloc_expression(Expression::VariableRef(
leo_asg::VariableRef {
arguments.push(Cell::new(&*function.scope.context.alloc_expression(
Expression::VariableRef(leo_asg::VariableRef {
parent: Cell::new(None),
span: Some(input_variable.get().borrow().name.span.clone()),
variable: input_variable.get(),
},
))));
}),
)));
}
let span = function.span.clone().unwrap_or_default();

View File

@ -17,12 +17,14 @@
#[derive(Clone)]
pub struct CompilerOptions {
pub constant_folding_enabled: bool,
pub dead_code_elimination_enabled: bool,
}
impl Default for CompilerOptions {
fn default() -> Self {
CompilerOptions {
constant_folding_enabled: true,
dead_code_elimination_enabled: true,
}
}
}

View File

@ -92,6 +92,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
results.extend(result);
}
Statement::Empty(_) => (),
};
Ok(results)

View File

@ -35,7 +35,7 @@ impl<'a> ImportParser<'a> {
return self.parse_package(context, package.path(), remaining_segments, span);
}
let program = Self::parse_import_file(package, span)?;
let asg = leo_asg::InternalProgram::new(context, &program, self)?;
let asg = leo_asg::Program::new(context, &program, self)?;
Ok(asg)
}