diff --git a/crates/noirc_frontend/src/monomorphization/ast.rs b/crates/noirc_frontend/src/monomorphization/ast.rs index 7cb6f5ef5bc..7920676aa7d 100644 --- a/crates/noirc_frontend/src/monomorphization/ast.rs +++ b/crates/noirc_frontend/src/monomorphization/ast.rs @@ -171,7 +171,7 @@ pub struct BinaryStatement { #[derive(Debug, Clone)] pub enum LValue { Ident(Ident), - Index { array: Box, index: Box, location: Location }, + Index { array: Box, index: Box, element_type: Type, location: Location }, MemberAccess { object: Box, field_index: usize }, } diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 961cfd18320..cb06be7c1c6 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -11,6 +11,7 @@ use acvm::FieldElement; use iter_extended::{btree_map, vecmap}; use noirc_abi::FunctionSignature; +use noirc_errors::Location; use std::collections::{BTreeMap, HashMap, VecDeque}; use crate::{ @@ -179,7 +180,7 @@ impl<'interner> Monomorphizer<'interner> { let return_type = Self::convert_type(meta.return_type()); let parameters = self.parameters(meta.parameters); - let body = self.expr_infer(*self.interner.function(&f).as_expr()); + let body = self.expr(*self.interner.function(&f).as_expr()); let unconstrained = meta.is_unconstrained; let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; @@ -235,12 +236,7 @@ impl<'interner> Monomorphizer<'interner> { } } - fn expr_infer(&mut self, expr: node_interner::ExprId) -> ast::Expression { - let expected_type = self.interner.id_type(expr); - self.expr(expr, &expected_type) - } - - fn expr(&mut self, expr: node_interner::ExprId, typ: &HirType) -> ast::Expression { + fn expr(&mut self, expr: node_interner::ExprId) -> ast::Expression { use ast::Expression::Literal; use ast::Literal::*; @@ -252,65 +248,49 @@ impl<'interner> Monomorphizer<'interner> { let typ = Self::convert_type(&self.interner.id_type(expr)); Literal(Integer(value, typ)) } - HirExpression::Literal(HirLiteral::Array(HirArrayLiteral::Standard(array))) => { - let element_type = Self::convert_type(&self.interner.id_type(array[0])); - let contents = vecmap(array, |id| self.expr_infer(id)); - Literal(Array(ast::ArrayLiteral { contents, element_type })) - } - HirExpression::Literal(HirLiteral::Array(HirArrayLiteral::Repeated { - repeated_element, - length, - })) => { - let element_type = Self::convert_type(&self.interner.id_type(repeated_element)); - let contents = self.expr_infer(repeated_element); - let length = length - .evaluate_to_u64() - .expect("Length of array is unknown when evaluating numeric generic"); - - let contents = vec![contents; length as usize]; - Literal(Array(ast::ArrayLiteral { contents, element_type })) - } + HirExpression::Literal(HirLiteral::Array(array)) => match array { + HirArrayLiteral::Standard(array) => self.standard_array(array), + HirArrayLiteral::Repeated { repeated_element, length } => { + self.repeated_array(repeated_element, length) + } + }, HirExpression::Block(block) => self.block(block.0), HirExpression::Prefix(prefix) => ast::Expression::Unary(ast::Unary { operator: prefix.operator, - rhs: Box::new(self.expr_infer(prefix.rhs)), + rhs: Box::new(self.expr(prefix.rhs)), }), HirExpression::Infix(infix) => { - let lhs = Box::new(self.expr_infer(infix.lhs)); - let rhs = Box::new(self.expr_infer(infix.rhs)); + let lhs = Box::new(self.expr(infix.lhs)); + let rhs = Box::new(self.expr(infix.rhs)); let operator = infix.operator.kind; let location = self.interner.expr_location(&expr); ast::Expression::Binary(ast::Binary { lhs, rhs, operator, location }) } - HirExpression::Index(index) => ast::Expression::Index(ast::Index { - collection: Box::new(self.expr_infer(index.collection)), - index: Box::new(self.expr_infer(index.index)), - location: self.interner.expr_location(&expr), - }), + HirExpression::Index(index) => self.index(expr, index), HirExpression::MemberAccess(access) => { let field_index = self.interner.get_field_index(expr); - let expr = Box::new(self.expr_infer(access.lhs)); + let expr = Box::new(self.expr(access.lhs)); ast::Expression::ExtractTupleField(expr, field_index) } HirExpression::Call(call) => self.function_call(call, expr), HirExpression::Cast(cast) => ast::Expression::Cast(ast::Cast { - lhs: Box::new(self.expr_infer(cast.lhs)), + lhs: Box::new(self.expr(cast.lhs)), r#type: Self::convert_type(&cast.r#type), }), HirExpression::For(for_expr) => { - let start = self.expr_infer(for_expr.start_range); - let end = self.expr_infer(for_expr.end_range); + let start = self.expr(for_expr.start_range); + let end = self.expr(for_expr.end_range); let index_variable = self.next_local_id(); self.define_local(for_expr.identifier.id, index_variable); - let block = Box::new(self.expr_infer(for_expr.block)); + let block = Box::new(self.expr(for_expr.block)); ast::Expression::For(ast::For { index_variable, @@ -323,9 +303,9 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::If(if_expr) => { - let cond = self.expr(if_expr.condition, &HirType::Bool(CompTime::No(None))); - let then = self.expr(if_expr.consequence, typ); - let else_ = if_expr.alternative.map(|alt| Box::new(self.expr(alt, typ))); + let cond = self.expr(if_expr.condition); + let then = self.expr(if_expr.consequence); + let else_ = if_expr.alternative.map(|alt| Box::new(self.expr(alt))); ast::Expression::If(ast::If { condition: Box::new(cond), consequence: Box::new(then), @@ -335,10 +315,10 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Tuple(fields) => { - let fields = vecmap(fields, |id| self.expr(id, typ)); + let fields = vecmap(fields, |id| self.expr(id)); ast::Expression::Tuple(fields) } - HirExpression::Constructor(constructor) => self.constructor(constructor, typ), + HirExpression::Constructor(constructor) => self.constructor(constructor, expr), HirExpression::Lambda(lambda) => self.lambda(lambda), @@ -349,23 +329,125 @@ impl<'interner> Monomorphizer<'interner> { } } + fn standard_array(&mut self, array: Vec) -> ast::Expression { + let element_type = Self::convert_type(&self.interner.id_type(array[0])); + let contents = vecmap(array, |id| self.expr(id)); + Self::aos_to_soa(contents, element_type) + } + + fn repeated_array( + &mut self, + repeated_element: node_interner::ExprId, + length: HirType, + ) -> ast::Expression { + let element_type = Self::convert_type(&self.interner.id_type(repeated_element)); + let contents = self.expr(repeated_element); + let length = length + .evaluate_to_u64() + .expect("Length of array is unknown when evaluating numeric generic"); + + let contents = vec![contents; length as usize]; + Self::aos_to_soa(contents, element_type) + } + + /// Convert an array in (potentially) array of structs form into struct of arrays form. + /// This will do nothing if the given array element type is a primitive type like Field. + /// + /// + /// TODO Remove side effects from clones + fn aos_to_soa( + array_contents: Vec, + element_type: ast::Type, + ) -> ast::Expression { + match element_type { + ast::Type::Field + | ast::Type::Integer(_, _) + | ast::Type::Bool + | ast::Type::Unit + | ast::Type::Function(_, _) => { + ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { + contents: array_contents, + element_type, + })) + } + + ast::Type::Tuple(elements) => ast::Expression::Tuple(vecmap( + elements.into_iter().enumerate(), + |(i, element_type)| { + let contents = vecmap(&array_contents, |element| { + ast::Expression::ExtractTupleField(Box::new(element.clone()), i) + }); + + Self::aos_to_soa(contents, element_type) + }, + )), + + ast::Type::Array(_, _) | ast::Type::String(_) => { + unreachable!("Nested arrays and arrays of strings are not supported") + } + } + } + + fn index(&mut self, id: node_interner::ExprId, index: HirIndexExpression) -> ast::Expression { + let element_type = Self::convert_type(&self.interner.id_type(id)); + + let collection = Box::new(self.expr(index.collection)); + let index = Box::new(self.expr(index.index)); + let location = self.interner.expr_location(&id); + Self::aos_to_soa_index(collection, index, element_type, location) + } + + /// Unpack an array index into an array of structs into a struct of arrays index if needed. + /// E.g. transforms my_pair_array[i] into (my_pair1_array[i], my_pair2_array[i]) + fn aos_to_soa_index( + collection: Box, + index: Box, + element_type: ast::Type, + location: Location, + ) -> ast::Expression { + match element_type { + ast::Type::Field + | ast::Type::Integer(_, _) + | ast::Type::Bool + | ast::Type::Unit + | ast::Type::Function(_, _) => { + ast::Expression::Index(ast::Index { collection, index, location }) + } + + ast::Type::Tuple(elements) => { + let elements = elements.into_iter().enumerate(); + ast::Expression::Tuple(vecmap(elements, |(i, element_type)| { + // collection should itself be a tuple of arrays + let collection = + Box::new(ast::Expression::ExtractTupleField(collection.clone(), i)); + + Self::aos_to_soa_index(collection, index.clone(), element_type, location) + })) + } + + ast::Type::Array(_, _) | ast::Type::String(_) => { + unreachable!("Nested arrays and arrays of strings are not supported") + } + } + } + fn statement(&mut self, id: StmtId) -> ast::Expression { match self.interner.statement(&id) { HirStatement::Let(let_statement) => self.let_statement(let_statement), HirStatement::Constrain(constrain) => { - let expr = self.expr(constrain.0, &HirType::Bool(CompTime::No(None))); + let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); ast::Expression::Constrain(Box::new(expr), location) } HirStatement::Assign(assign) => self.assign(assign), - HirStatement::Expression(expr) => self.expr_infer(expr), - HirStatement::Semi(expr) => ast::Expression::Semi(Box::new(self.expr_infer(expr))), + HirStatement::Expression(expr) => self.expr(expr), + HirStatement::Semi(expr) => ast::Expression::Semi(Box::new(self.expr(expr))), HirStatement::Error => unreachable!(), } } fn let_statement(&mut self, let_statement: HirLetStatement) -> ast::Expression { - let expr = self.expr_infer(let_statement.expression); + let expr = self.expr(let_statement.expression); let expected_type = self.interner.id_type(let_statement.expression); self.unpack_pattern(let_statement.pattern, expr, &expected_type) } @@ -373,9 +455,10 @@ impl<'interner> Monomorphizer<'interner> { fn constructor( &mut self, constructor: HirConstructorExpression, - typ: &HirType, + id: node_interner::ExprId, ) -> ast::Expression { - let field_types = unwrap_struct_type(typ); + let typ = self.interner.id_type(id); + let field_types = unwrap_struct_type(&typ); // Create let bindings for each field value first to preserve evaluation order before // they are reordered and packed into the resulting tuple @@ -388,7 +471,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = Self::convert_type(field_type); field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); - let expression = Box::new(self.expr(expr_id, field_type)); + let expression = Box::new(self.expr(expr_id)); new_exprs.push(ast::Expression::Let(ast::Let { id: new_id, @@ -508,7 +591,7 @@ impl<'interner> Monomorphizer<'interner> { let ident = ast::Ident { location, mutable, definition, name, typ }; ast::Expression::Ident(ident) } - DefinitionKind::Global(expr_id) => self.expr_infer(*expr_id), + DefinitionKind::Global(expr_id) => self.expr(*expr_id), DefinitionKind::Local(_) => { let ident = self.local_ident(&ident).unwrap(); ast::Expression::Ident(ident) @@ -538,10 +621,10 @@ impl<'interner> Monomorphizer<'interner> { HirType::String(size) => ast::Type::String(size.evaluate_to_u64().unwrap_or(0)), HirType::Unit => ast::Type::Unit, - HirType::Array(size, element) => { - let size = size.evaluate_to_u64().unwrap_or(0); + HirType::Array(length, element) => { + let length = length.evaluate_to_u64().unwrap_or(0); let element = Self::convert_type(element.as_ref()); - ast::Type::Array(size, Box::new(element)) + Self::aos_to_soa_type(length, element) } HirType::PolymorphicInteger(_, binding) @@ -586,13 +669,33 @@ impl<'interner> Monomorphizer<'interner> { } } + /// Converts arrays of structs (AOS) into structs of arrays (SOA). + /// This is required since our SSA pass does not support arrays of structs. + fn aos_to_soa_type(length: u64, element: ast::Type) -> ast::Type { + match element { + ast::Type::Field + | ast::Type::Integer(_, _) + | ast::Type::Bool + | ast::Type::Unit + | ast::Type::Function(_, _) => ast::Type::Array(length, Box::new(element)), + + ast::Type::Tuple(elements) => { + ast::Type::Tuple(vecmap(elements, |typ| Self::aos_to_soa_type(length, typ))) + } + + ast::Type::Array(_, _) | ast::Type::String(_) => { + unreachable!("Nested arrays and arrays of strings are not supported") + } + } + } + fn function_call( &mut self, call: HirCallExpression, id: node_interner::ExprId, ) -> ast::Expression { - let func = Box::new(self.expr_infer(call.func)); - let arguments = vecmap(&call.arguments, |id| self.expr_infer(*id)); + let func = Box::new(self.expr(call.func)); + let arguments = vecmap(&call.arguments, |id| self.expr(*id)); let return_type = self.interner.id_type(id); let return_type = Self::convert_type(&return_type); let location = call.location; @@ -706,23 +809,80 @@ impl<'interner> Monomorphizer<'interner> { } fn assign(&mut self, assign: HirAssignStatement) -> ast::Expression { - let expression = Box::new(self.expr_infer(assign.expression)); - let lvalue = self.lvalue(assign.lvalue); - ast::Expression::Assign(ast::Assign { lvalue, expression }) + let expression = Box::new(self.expr(assign.expression)); + let (lvalue, index_lvalue) = self.lvalue(assign.lvalue); + + match index_lvalue { + Some((index, element_type, location)) => { + Self::aos_to_soa_assign(expression, Box::new(lvalue), index, element_type, location) + } + None => ast::Expression::Assign(ast::Assign { expression, lvalue }), + } } - fn lvalue(&mut self, lvalue: HirLValue) -> ast::LValue { + /// Returns the lvalue along with an optional LValue::Index to add to the end, if needed. + /// This is added to the end separately as part of converting arrays of structs to structs + /// of arrays. + fn lvalue( + &mut self, + lvalue: HirLValue, + ) -> (ast::LValue, Option<(Box, ast::Type, Location)>) { match lvalue { - HirLValue::Ident(ident, _) => ast::LValue::Ident(self.local_ident(&ident).unwrap()), + HirLValue::Ident(ident, _) => { + let lvalue = ast::LValue::Ident(self.local_ident(&ident).unwrap()); + (lvalue, None) + } HirLValue::MemberAccess { object, field_index, .. } => { - let object = Box::new(self.lvalue(*object)); - ast::LValue::MemberAccess { object, field_index: field_index.unwrap() } + let field_index = field_index.unwrap(); + let (object, index) = self.lvalue(*object); + let object = Box::new(object); + let lvalue = ast::LValue::MemberAccess { object, field_index }; + (lvalue, index) } - HirLValue::Index { array, index, .. } => { - let array = Box::new(self.lvalue(*array)); + HirLValue::Index { array, index, typ } => { let location = self.interner.expr_location(&index); - let index = Box::new(self.expr_infer(index)); - ast::LValue::Index { array, index, location } + + let (array, prev_index) = self.lvalue(*array); + assert!( + prev_index.is_none(), + "Nested arrays are currently unsupported in noir: location is {location:?}" + ); + + let index = Box::new(self.expr(index)); + let element_type = Self::convert_type(&typ); + (array, Some((index, element_type, location))) + } + } + } + + fn aos_to_soa_assign( + expression: Box, + lvalue: Box, + index: Box, + typ: ast::Type, + location: Location, + ) -> ast::Expression { + match typ { + ast::Type::Tuple(fields) => { + let fields = fields.into_iter().enumerate(); + ast::Expression::Block(vecmap(fields, |(i, field)| { + let expression = ast::Expression::ExtractTupleField(expression.clone(), i); + let lvalue = + ast::LValue::MemberAccess { object: lvalue.clone(), field_index: i }; + Self::aos_to_soa_assign( + Box::new(expression), + Box::new(lvalue), + index.clone(), + field, + location, + ) + })) + } + + // No changes if the element_type is not a tuple + element_type => { + let lvalue = ast::LValue::Index { array: lvalue, index, element_type, location }; + ast::Expression::Assign(ast::Assign { lvalue, expression }) } } } @@ -738,7 +898,7 @@ impl<'interner> Monomorphizer<'interner> { })); let parameters = self.parameters(parameters); - let body = self.expr_infer(lambda.body); + let body = self.expr(lambda.body); let id = self.next_function_id(); let return_type = ret_type.clone(); diff --git a/crates/noirc_frontend/src/monomorphization/printer.rs b/crates/noirc_frontend/src/monomorphization/printer.rs index b3c67f62a16..2479f916822 100644 --- a/crates/noirc_frontend/src/monomorphization/printer.rs +++ b/crates/noirc_frontend/src/monomorphization/printer.rs @@ -82,7 +82,7 @@ impl AstPrinter { Ok(()) } - fn print_literal( + pub fn print_literal( &mut self, literal: &super::ast::Literal, f: &mut Formatter,