Update flattener

This commit is contained in:
Pranav Gaddamadugu 2023-10-12 18:09:02 -04:00 committed by Pranav Gaddamadugu
parent f494a891c4
commit cacc2154ea
5 changed files with 276 additions and 198 deletions

View File

@ -25,11 +25,12 @@ use leo_ast::{
ExpressionReconstructor,
Member,
MemberAccess,
Node,
Statement,
StructExpression,
StructVariableInitializer,
TernaryExpression,
TupleExpression,
Type,
};
// TODO: Clean up logic. To be done in a follow-up PR (feat/tuples)
@ -133,68 +134,55 @@ impl ExpressionReconstructor for Flattener<'_> {
let mut statements = Vec::new();
match (*input.if_true, *input.if_false) {
// If both expressions are identifiers which are arrays, construct ternary expressions for each of the members and an array expression for the result.
(Expression::Identifier(first), Expression::Identifier(second))
if self.arrays.contains_key(&first.name) && self.arrays.contains_key(&second.name) =>
{
let first_array = self.arrays.get(&first.name).unwrap().clone();
let second_array = self.arrays.get(&second.name).unwrap();
// Note that type checking guarantees that both expressions have the same same type. This is a sanity check.
assert_eq!(&first_array, second_array);
(Expression::Identifier(first), Expression::Identifier(second)) => {
match (self.type_table.get(&first.id()), self.type_table.get(&second.id())) {
(Some(Type::Array(first_type)), Some(Type::Array(second_type))) => {
// Note that type checking guarantees that both expressions have the same same type. This is a sanity check.
assert_eq!(first_type, second_type);
self.ternary_array(&first_type, &input.condition, &first, &second)
}
(Some(Type::Identifier(first_type)), Some(Type::Identifier(second_type))) => {
// Get the struct definitions.
let first_type = self.symbol_table.lookup_struct(first_type.name).unwrap();
let second_type = self.symbol_table.lookup_struct(second_type.name).unwrap();
// Note that type checking guarantees that both expressions have the same same type. This is a sanity check.
assert_eq!(first_type, second_type);
self.ternary_struct(first_type, &input.condition, &first, &second)
}
(Some(Type::Tuple(first_type)), Some(Type::Tuple(second_type))) => {
// Note that type checking guarantees that both expressions have the same same type. This is a sanity check.
assert_eq!(first_type, second_type);
self.ternary_tuple(&first_type, &input.condition, &first, &second)
}
_ => {
// Reconstruct the true case.
let (if_true, stmts) = self.reconstruct_expression(Expression::Identifier(first));
statements.extend(stmts);
self.ternary_array(first_array, &input.condition, &first, &second)
// Reconstruct the false case.
let (if_false, stmts) = self.reconstruct_expression(Expression::Identifier(second));
statements.extend(stmts);
let (identifier, statement) =
self.unique_simple_assign_statement(Expression::Ternary(TernaryExpression {
condition: input.condition,
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: input.span,
id: input.id,
}));
// Accumulate the new assignment statement.
statements.push(statement);
(Expression::Identifier(identifier), statements)
}
}
}
// If both expressions are identifiers which are structs, construct ternary expression for each of the members and a struct expression for the result.
(Expression::Identifier(first), Expression::Identifier(second))
if self.structs.contains_key(&first.name) && self.structs.contains_key(&second.name) =>
{
let first_struct = self.structs.get(&first.name).unwrap().clone();
let second_struct = self.structs.get(&second.name).unwrap();
// Note that type checking guarantees that both expressions have the same same type. This is a sanity check.
assert_eq!(&first_struct, second_struct);
self.ternary_struct(first_struct, &input.condition, &first, &second)
}
// If both expressions are identifiers which map to tuples, construct ternary expression over the tuples.
(Expression::Identifier(first), Expression::Identifier(second))
if self.tuples.contains_key(&first.name) && self.tuples.contains_key(&second.name) =>
{
// Note that this unwrap is safe since we check that `self.tuples` contains the key.
let first_tuple = self.tuples.get(&first.name).unwrap();
// Note that this unwrap is safe since we check that `self.tuples` contains the key.
let second_tuple = self.tuples.get(&second.name).unwrap();
// Note that type checking guarantees that both expressions have the same same type.
self.reconstruct_ternary(TernaryExpression {
condition: input.condition,
if_true: Box::new(Expression::Tuple(first_tuple.clone())),
if_false: Box::new(Expression::Tuple(second_tuple.clone())),
span: input.span,
id: input.id,
})
}
// Otherwise, create a new intermediate assignment for the ternary expression are return the assigned variable.
// Note that a new assignment must be created to flattened nested ternary expressions.
(if_true, if_false) => {
// Reconstruct the true case.
let (if_true, stmts) = self.reconstruct_expression(if_true);
statements.extend(stmts);
// Reconstruct the false case.
let (if_false, stmts) = self.reconstruct_expression(if_false);
statements.extend(stmts);
let (identifier, statement) =
self.unique_simple_assign_statement(Expression::Ternary(TernaryExpression {
condition: input.condition,
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: input.span,
id: input.id,
}));
// Accumulate the new assignment statement.
statements.push(statement);
(Expression::Identifier(identifier), statements)
(expr1, expr2) => {
println!("expr1: {:?}", expr1);
println!("expr2: {:?}", expr2);
unreachable!("SSA guarantees that the subexpressions of a ternary expression are identifiers.")
}
}
}

View File

@ -24,15 +24,6 @@ impl ProgramReconstructor for Flattener<'_> {
// First, flatten the finalize block. This allows us to initialize self.finalizes correctly.
// Note that this is safe since the finalize block is independent of the function body.
let finalize = function.finalize.map(|finalize| {
// Initialize `self.structs` with the finalize's input as necessary.
self.structs = Default::default();
for input in &finalize.input {
if let Type::Identifier(struct_name) = input.type_() {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(input.identifier().name, struct_);
}
}
// Flatten the finalize block.
let mut block = self.reconstruct_block(finalize.block).0;
@ -53,16 +44,6 @@ impl ProgramReconstructor for Flattener<'_> {
}
});
// Initialize `self.structs` with the function's input as necessary.
self.structs = Default::default();
for input in &function.input {
if let Type::Identifier(struct_name) = input.type_() {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(input.identifier().name, struct_);
}
}
// Flatten the function body.
let mut block = self.reconstruct_block(function.block).0;

View File

@ -107,13 +107,25 @@ impl StatementReconstructor for Flattener<'_> {
variant: AssertVariant::Assert(Expression::Binary(BinaryExpression {
op: BinaryOperation::Or,
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the binary expression.
let id = self.node_builder.next_id();
// Update the type table with the type of the binary expression.
self.type_table.insert(id, Type::Boolean);
id
},
// Take the logical negation of the guard.
left: Box::new(Expression::Unary(UnaryExpression {
op: UnaryOperation::Not,
receiver: Box::new(guard),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the unary expression.
let id = self.node_builder.next_id();
// Update the type table with the type of the unary expression.
self.type_table.insert(id, Type::Boolean);
id
},
})),
right: Box::new(match assert.variant {
// If the assert statement is an `assert`, use the expression as is.
@ -124,7 +136,13 @@ impl StatementReconstructor for Flattener<'_> {
op: BinaryOperation::Eq,
right: Box::new(right),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the unary expression.
let id = self.node_builder.next_id();
// Update the type table with the type of the unary expression.
self.type_table.insert(id, Type::Boolean);
id
},
}),
// If the assert statement is an `assert_ne`, construct a new inequality expression.
AssertVariant::AssertNeq(left, right) => Expression::Binary(BinaryExpression {
@ -132,7 +150,13 @@ impl StatementReconstructor for Flattener<'_> {
op: BinaryOperation::Neq,
right: Box::new(right),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the unary expression.
let id = self.node_builder.next_id();
// Update the type table with the type of the unary expression.
self.type_table.insert(id, Type::Boolean);
id
},
}),
}),
})),
@ -184,26 +208,34 @@ impl StatementReconstructor for Flattener<'_> {
elements: (0..tuple.length())
.zip_eq(tuple.elements().iter())
.map(|(i, type_)| {
let identifier = Identifier::new(
// Return the identifier as an expression.
Expression::Identifier(Identifier::new(
self.assigner.unique_symbol(lhs_identifier.name, format!("$index${i}$")),
self.node_builder.next_id(),
);
// If the output type is a struct, add it to `self.structs`.
if let Type::Identifier(struct_name) = type_ {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(identifier.name, struct_);
}
Expression::Identifier(identifier)
{
// Construct a node ID for the identifier.
let id = self.node_builder.next_id();
// Update the type table with the type.
self.type_table.insert(id, type_.clone());
id
},
))
})
.collect(),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Construct a node ID for the tuple expression.
let id = self.node_builder.next_id();
// Update the type table with the type.
self.type_table.insert(id, Type::Tuple(tuple.clone()));
id
},
};
// Add the `tuple_expression` to `self.tuples`.
self.tuples.insert(lhs_identifier.name, tuple_expression.clone());
// Update the type table with the type of the tuple expression.
self.type_table.insert(tuple_expression.id, Type::Tuple(tuple.clone()));
// Construct a new assignment statement with a tuple expression on the lhs.
(
Statement::Assign(Box::new(AssignStatement {
@ -216,23 +248,7 @@ impl StatementReconstructor for Flattener<'_> {
)
}
// Otherwise, reconstruct the assignment as is.
type_ => {
// If the function returns a struct, add it to `self.structs`.
if let Type::Identifier(struct_name) = type_ {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(lhs_identifier.name, struct_);
};
(
Statement::Assign(Box::new(AssignStatement {
place: Expression::Identifier(lhs_identifier),
value: Expression::Call(call),
span: Default::default(),
id: self.node_builder.next_id(),
})),
statements,
)
}
_ => (self.simple_assign_statement(lhs_identifier, Expression::Call(call)), statements),
}
}
// If the `rhs` is an invocation of `get` or `get_or_use` on a mapping, then check if the value type is a struct.
@ -268,22 +284,8 @@ impl StatementReconstructor for Flattener<'_> {
}
_ => unreachable!("Type checking guarantee that `arguments[0]` is the name of the mapping."),
};
// If the value type is a struct, add it to `self.structs`.
if let Type::Identifier(struct_name) = value_type {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(lhs_identifier.name, struct_);
}
// Reconstruct the assignment.
(
Statement::Assign(Box::new(AssignStatement {
place: Expression::Identifier(lhs_identifier),
value,
span: Default::default(),
id: self.node_builder.next_id(),
})),
statements,
)
(self.simple_assign_statement(lhs_identifier, value), statements)
}
(Expression::Identifier(identifier), expression) => {
(self.simple_assign_statement(identifier, expression), statements)
@ -300,7 +302,7 @@ impl StatementReconstructor for Flattener<'_> {
let function = self.symbol_table.lookup_fn_symbol(function_name).unwrap();
let output_type = match &function.output_type {
Type::Tuple(tuple) => tuple.clone(),
Type::Tuple(tuple_type) => tuple_type.clone(),
_ => unreachable!("Type checking guarantees that the output type is a tuple."),
};
@ -309,14 +311,13 @@ impl StatementReconstructor for Flattener<'_> {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("Type checking guarantees that a tuple element on the lhs is an identifier."),
};
// If the output type is a struct, add it to `self.structs`.
if let Type::Identifier(struct_name) = type_ {
// Note that this unwrap is safe since type checking guarantees that the struct exists.
let struct_ = self.symbol_table.lookup_struct(struct_name.name).unwrap().clone();
self.structs.insert(identifier.name, struct_);
}
// Add the type of each identifier to the type table.
self.type_table.insert(identifier.id, type_.clone());
});
// Set the type of the tuple expression.
self.type_table.insert(tuple.id, Type::Tuple(output_type.clone()));
(
Statement::Assign(Box::new(AssignStatement {
place: Expression::Tuple(tuple),
@ -330,11 +331,14 @@ impl StatementReconstructor for Flattener<'_> {
// If the lhs is a tuple and the rhs is a tuple, create a new assign statement for each tuple element.
(Expression::Tuple(lhs_tuple), Expression::Tuple(rhs_tuple)) => {
statements.extend(lhs_tuple.elements.into_iter().zip(rhs_tuple.elements).map(|(lhs, rhs)| {
let identifier = match &lhs {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("Type checking guarantees that `lhs` is an identifier."),
// Get the type of the rhs.
let type_ = match self.type_table.get(&lhs.id()) {
Some(type_) => type_.clone(),
None => unreachable!("Type checking guarantees that the type of the lhs is in the type table."),
};
self.update_structs(identifier, &rhs);
// Set the type of the lhs.
self.type_table.insert(rhs.id(), type_);
// Return the assign statement.
Statement::Assign(Box::new(AssignStatement {
place: lhs,
value: rhs,
@ -353,12 +357,14 @@ impl StatementReconstructor for Flattener<'_> {
let rhs_tuple = self.tuples.get(&identifier.name).unwrap().clone();
// Create a new assign statement for each tuple element.
for (lhs, rhs) in lhs_tuple.elements.into_iter().zip(rhs_tuple.elements) {
let identifier = match &lhs {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("Type checking guarantees that `lhs` is an identifier."),
// Get the type of the rhs.
let type_ = match self.type_table.get(&lhs.id()) {
Some(type_) => type_.clone(),
None => unreachable!("Type checking guarantees that the type of the lhs is in the type table."),
};
self.update_structs(identifier, &rhs);
// Set the type of the lhs.
self.type_table.insert(rhs.id(), type_);
// Return the assign statement.
statements.push(Statement::Assign(Box::new(AssignStatement {
place: lhs,
value: rhs,

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::{Assigner, SymbolTable};
use crate::{Assigner, SymbolTable, TypeTable};
use leo_ast::{
AccessExpression,
@ -31,6 +31,7 @@ use leo_ast::{
Literal,
Member,
MemberAccess,
Node,
NodeBuilder,
NonzeroNumber,
ReturnStatement,
@ -49,15 +50,17 @@ use leo_span::Symbol;
use indexmap::IndexMap;
// TODO: TypeTable should be placed behind a RefCell.
pub struct Flattener<'a> {
/// The symbol table associated with the program.
pub(crate) symbol_table: &'a SymbolTable,
/// A mapping between node IDs and their types.
pub(crate) type_table: &'a TypeTable,
/// A counter used to generate unique node IDs.
pub(crate) node_builder: &'a NodeBuilder,
/// A struct used to construct (unique) assignment statements.
pub(crate) assigner: &'a Assigner,
/// The set of variables that are structs.
pub(crate) structs: IndexMap<Symbol, Struct>,
/// A stack of condition `Expression`s visited up to the current point in the AST.
pub(crate) condition_stack: Vec<Expression>,
/// A list containing tuples of guards and expressions associated `ReturnStatement`s.
@ -67,21 +70,23 @@ pub struct Flattener<'a> {
pub(crate) returns: Vec<(Option<Expression>, ReturnStatement)>,
/// A mapping between variables and flattened tuple expressions.
pub(crate) tuples: IndexMap<Symbol, TupleExpression>,
/// A mapping from variables to array types.
pub(crate) arrays: IndexMap<Symbol, ArrayType>,
}
impl<'a> Flattener<'a> {
pub(crate) fn new(symbol_table: &'a SymbolTable, node_builder: &'a NodeBuilder, assigner: &'a Assigner) -> Self {
pub(crate) fn new(
symbol_table: &'a SymbolTable,
type_table: &'a TypeTable,
node_builder: &'a NodeBuilder,
assigner: &'a Assigner,
) -> Self {
Self {
symbol_table,
type_table,
node_builder,
assigner,
structs: IndexMap::new(),
condition_stack: Vec::new(),
returns: Vec::new(),
tuples: IndexMap::new(),
arrays: IndexMap::new(),
}
}
@ -97,12 +102,19 @@ impl<'a> Flattener<'a> {
false => {
let (first, rest) = self.condition_stack.split_first().unwrap();
Some(rest.iter().cloned().fold(first.clone(), |acc, condition| {
// Construct the binary expression.
Expression::Binary(BinaryExpression {
op: BinaryOperation::And,
left: Box::new(acc),
right: Box::new(condition),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the binary expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Boolean);
id
},
})
}))
}
@ -136,11 +148,22 @@ impl<'a> Flattener<'a> {
id: self.node_builder.next_id(),
};
let (value, stmts) = self.reconstruct_ternary(TernaryExpression {
id: {
// Create a new node ID for the ternary expression.
let id = self.node_builder.next_id();
// Get the type of the node ID.
let type_ = match self.type_table.get(&if_true.id()) {
Some(type_) => type_,
None => unreachable!("Type checking guarantees that all expressions have a type."),
};
// Set the type of the node ID.
self.type_table.insert(id, type_);
id
},
condition: Box::new(guard),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: Default::default(),
id: self.node_builder.next_id(),
});
statements.extend(stmts);
@ -167,11 +190,7 @@ impl<'a> Flattener<'a> {
}
}
/// Gets the type of the expression, if it's being tracked.
pub(crate) fn get_type(&self, expression: &Expression) -> Option<Type> {}
/// A wrapper around `assigner.unique_simple_assign_statement` that updates `self.structs`.
// TODO (@d0cd) Update to check for tuples and arrays
pub(crate) fn unique_simple_assign_statement(&mut self, expr: Expression) -> (Identifier, Statement) {
// Create a new variable for the expression.
let name = self.assigner.unique_symbol("$var", "$");
@ -180,20 +199,19 @@ impl<'a> Flattener<'a> {
// Construct the assignment statement.
let statement = self.simple_assign_statement(place, expr);
match &statement {
Statement::Assign(assign) => {
self.update_structs(&place, &assign.value);
}
_ => unreachable!("`assigner.unique_simple_assign_statement` always returns an assignment statement."),
}
(place, statement)
}
/// A wrapper around `assigner.simple_assign_statement` that tracks the type of the lhs.
pub(crate) fn simple_assign_statement(&mut self, lhs: Identifier, rhs: Expression) -> Statement {
let statement = self.assigner.simple_assign_statement(lhs, rhs, self.node_builder.next_id());
self.update_types(&statement);
statement
// Update the type table.
let type_ = match self.type_table.get(&rhs.id()) {
Some(type_) => type_,
None => unreachable!("Type checking guarantees that all expressions have a type."),
};
self.type_table.insert(lhs.id(), type_);
// Construct the statement.
self.assigner.simple_assign_statement(lhs, rhs, self.node_builder.next_id())
}
/// Folds a list of return statements into a single return statement and adds the produced statements to the block.
@ -268,7 +286,7 @@ impl<'a> Flattener<'a> {
pub(crate) fn ternary_array(
&mut self,
array: ArrayType,
array: &ArrayType,
condition: &Expression,
first: &Identifier,
second: &Identifier,
@ -286,10 +304,22 @@ impl<'a> Flattener<'a> {
IntegerType::U32,
i.to_string(),
Default::default(),
self.node_builder.next_id(),
{
// Create a new node ID for the literal.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Integer(IntegerType::U32));
id
},
))),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, array.element_type().clone());
id
},
})));
statements.push(stmt);
// Create an assignment statement for the second access expression.
@ -300,10 +330,22 @@ impl<'a> Flattener<'a> {
IntegerType::U32,
i.to_string(),
Default::default(),
self.node_builder.next_id(),
{
// Create a new node ID for the literal.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Integer(IntegerType::U32));
id
},
))),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, array.element_type().clone());
id
},
})));
statements.push(stmt);
@ -315,7 +357,13 @@ impl<'a> Flattener<'a> {
// Access the member of the second expression.
if_false: Box::new(Expression::Identifier(second)),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the ternary expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, array.element_type().clone());
id
},
});
// Accumulate any statements generated.
@ -329,7 +377,13 @@ impl<'a> Flattener<'a> {
let (expr, stmts) = self.reconstruct_array(ArrayExpression {
elements,
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a node ID for the array expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Array(array.clone()));
id
},
});
// Accumulate any statements generated.
@ -338,9 +392,6 @@ impl<'a> Flattener<'a> {
// Create a new assignment statement for the array expression.
let (identifier, statement) = self.unique_simple_assign_statement(expr);
// Mark the lhs of the assignment as an array.
self.arrays.insert(identifier.name, array);
statements.push(statement);
(Expression::Identifier(identifier), statements)
@ -348,7 +399,7 @@ impl<'a> Flattener<'a> {
pub(crate) fn ternary_struct(
&mut self,
struct_: Struct,
struct_: &Struct,
condition: &Expression,
first: &Identifier,
second: &Identifier,
@ -359,14 +410,20 @@ impl<'a> Flattener<'a> {
let members = struct_
.members
.iter()
.map(|Member { identifier, .. }| {
.map(|Member { identifier, type_, .. }| {
// Create an assignment statement for the first access expression.
let (first, stmt) =
self.unique_simple_assign_statement(Expression::Access(AccessExpression::Member(MemberAccess {
inner: Box::new(Expression::Identifier(*first)),
name: *identifier,
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
})));
statements.push(stmt);
// Create an assignment statement for the second access expression.
@ -375,7 +432,13 @@ impl<'a> Flattener<'a> {
inner: Box::new(Expression::Identifier(*second)),
name: *identifier,
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
})));
statements.push(stmt);
// Recursively reconstruct the ternary expression.
@ -386,7 +449,13 @@ impl<'a> Flattener<'a> {
// Access the member of the second expression.
if_false: Box::new(Expression::Identifier(second)),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the ternary expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
});
// Accumulate any statements generated.
@ -405,7 +474,13 @@ impl<'a> Flattener<'a> {
name: struct_.identifier,
members,
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the struct expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Identifier(struct_.identifier));
id
},
});
// Accumulate any statements generated.
@ -414,9 +489,6 @@ impl<'a> Flattener<'a> {
// Create a new assignment statement for the struct expression.
let (identifier, statement) = self.unique_simple_assign_statement(expr);
// Mark the lhs of the assignment as a struct.
self.structs.insert(identifier.name, struct_);
statements.push(statement);
(Expression::Identifier(identifier), statements)
@ -424,7 +496,7 @@ impl<'a> Flattener<'a> {
pub(crate) fn ternary_tuple(
&mut self,
tuple: TupleType,
tuple_type: &TupleType,
condition: &Expression,
first: &Identifier,
second: &Identifier,
@ -432,15 +504,24 @@ impl<'a> Flattener<'a> {
// Initialize a vector to accumulate any statements generated.
let mut statements = Vec::new();
// For each tuple element, construct a new ternary expression.
let elements = (0..tuple.length())
.map(|i| {
let elements = tuple_type
.elements()
.iter()
.enumerate()
.map(|(i, type_)| {
// Create an assignment statement for the first access expression.
let (first, stmt) =
self.unique_simple_assign_statement(Expression::Access(AccessExpression::Tuple(TupleAccess {
tuple: Box::new(Expression::Identifier(*first)),
index: NonzeroNumber::from(i),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
})));
statements.push(stmt);
// Create an assignment statement for the second access expression.
@ -449,7 +530,13 @@ impl<'a> Flattener<'a> {
tuple: Box::new(Expression::Identifier(*second)),
index: NonzeroNumber::from(i),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the access expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
})));
statements.push(stmt);
@ -461,7 +548,13 @@ impl<'a> Flattener<'a> {
// Access the member of the second expression.
if_false: Box::new(Expression::Identifier(second)),
span: Default::default(),
id: self.node_builder.next_id(),
id: {
// Create a new node ID for the ternary expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, type_.clone());
id
},
});
// Accumulate any statements generated.
@ -472,7 +565,17 @@ impl<'a> Flattener<'a> {
.collect();
// Construct the tuple expression.
let tuple = TupleExpression { elements, span: Default::default(), id: self.node_builder.next_id() };
let tuple = TupleExpression {
elements,
span: Default::default(),
id: {
// Create a new node ID for the tuple expression.
let id = self.node_builder.next_id();
// Set the type of the node ID.
self.type_table.insert(id, Type::Tuple(tuple_type.clone()));
id
},
};
let (expr, stmts) = self.reconstruct_tuple(tuple.clone());
// Accumulate any statements generated.

View File

@ -59,17 +59,17 @@ mod flatten_statement;
pub mod flattener;
pub use flattener::*;
use crate::{Assigner, Pass, SymbolTable};
use crate::{Assigner, Pass, SymbolTable, TypeTable};
use leo_ast::{Ast, NodeBuilder, ProgramReconstructor};
use leo_errors::Result;
impl<'a> Pass for Flattener<'a> {
type Input = (Ast, &'a SymbolTable, &'a NodeBuilder, &'a Assigner);
type Input = (Ast, &'a SymbolTable, &'a TypeTable, &'a NodeBuilder, &'a Assigner);
type Output = Result<Ast>;
fn do_pass((ast, st, node_builder, assigner): Self::Input) -> Self::Output {
let mut reconstructor = Flattener::new(st, node_builder, assigner);
fn do_pass((ast, st, tt, node_builder, assigner): Self::Input) -> Self::Output {
let mut reconstructor = Flattener::new(st, tt, node_builder, assigner);
let program = reconstructor.reconstruct_program(ast.into_repr());
Ok(Ast::new(program))