diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 5acb266b4c1..a39df14d60a 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -199,6 +199,15 @@ impl<'a> FunctionContext<'a> { ast::Type::Array(_, _) => { self.codegen_array_checked(elements, typ[0].clone())? } + _ => unreachable!("ICE: unexpected array literal type, got {}", array.typ), + }) + } + ast::Literal::Slice(array) => { + let elements = + try_vecmap(&array.contents, |element| self.codegen_expression(element))?; + + let typ = Self::convert_type(&array.typ).flatten(); + Ok(match array.typ { ast::Type::Slice(_) => { let slice_length = self.builder.length_constant(array.contents.len() as u128); @@ -206,10 +215,7 @@ impl<'a> FunctionContext<'a> { self.codegen_array_checked(elements, typ[1].clone())?; Tree::Branch(vec![slice_length.into(), slice_contents]) } - _ => unreachable!( - "ICE: array literal type must be an array or a slice, but got {}", - array.typ - ), + _ => unreachable!("ICE: unexpected slice literal type, got {}", array.typ), }) } ast::Literal::Integer(value, typ, location) => { diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index a9ecc1a53e5..09c09daf9b9 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -70,6 +70,17 @@ impl ExpressionKind { })) } + pub fn slice(contents: Vec) -> ExpressionKind { + ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(contents))) + } + + pub fn repeated_slice(repeated_element: Expression, length: Expression) -> ExpressionKind { + ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated { + repeated_element: Box::new(repeated_element), + length: Box::new(length), + })) + } + pub fn integer(contents: FieldElement) -> ExpressionKind { ExpressionKind::Literal(Literal::Integer(contents, false)) } @@ -319,6 +330,7 @@ impl UnaryOp { #[derive(Debug, PartialEq, Eq, Clone)] pub enum Literal { Array(ArrayLiteral), + Slice(ArrayLiteral), Bool(bool), Integer(FieldElement, /*sign*/ bool), // false for positive integer and true for negative Str(String), @@ -498,6 +510,13 @@ impl Display for Literal { Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { write!(f, "[{repeated_element}; {length}]") } + Literal::Slice(ArrayLiteral::Standard(elements)) => { + let contents = vecmap(elements, ToString::to_string); + write!(f, "&[{}]", contents.join(", ")) + } + Literal::Slice(ArrayLiteral::Repeated { repeated_element, length }) => { + write!(f, "&[{repeated_element}; {length}]") + } Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), Literal::Integer(integer, sign) => { if *sign { diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 8a420c32fb8..4547dc2a176 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -83,7 +83,8 @@ impl core::fmt::Display for IntegerBitSize { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeData { FieldElement, - Array(Option, Box), // [4]Witness = Array(4, Witness) + Array(UnresolvedTypeExpression, Box), // [Field; 4] = Array(4, Field) + Slice(Box), Integer(Signedness, IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo) Bool, Expression(UnresolvedTypeExpression), @@ -151,10 +152,8 @@ impl std::fmt::Display for UnresolvedTypeData { use UnresolvedTypeData::*; match self { FieldElement => write!(f, "Field"), - Array(len, typ) => match len { - None => write!(f, "[{typ}]"), - Some(len) => write!(f, "[{typ}; {len}]"), - }, + Array(len, typ) => write!(f, "[{typ}; {len}]"), + Slice(typ) => write!(f, "[{typ}]"), Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 90716bb958d..c6606386f7b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -481,13 +481,13 @@ impl<'a> Resolver<'a> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); - let size = if size.is_none() { - Type::NotConstant - } else { - self.resolve_array_size(size, new_variables) - }; + let size = self.resolve_array_size(Some(size), new_variables); Type::Array(Box::new(size), elem) } + Slice(elem) => { + let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); + Type::Slice(elem) + } Expression(expr) => self.convert_expression_type(expr), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, @@ -1084,7 +1084,6 @@ impl<'a> Resolver<'a> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::NotConstant | Type::TraitAsType(..) | Type::Forall(_, _) => (), @@ -1095,6 +1094,10 @@ impl<'a> Resolver<'a> { Self::find_numeric_generics_in_type(element_type, found); } + Type::Slice(element_type) => { + Self::find_numeric_generics_in_type(element_type, found); + } + Type::Tuple(fields) => { for field in fields { Self::find_numeric_generics_in_type(field, found); @@ -1402,27 +1405,37 @@ impl<'a> Resolver<'a> { } } + fn resolve_array_literal(&mut self, array_literal: ArrayLiteral) -> HirArrayLiteral { + match array_literal { + ArrayLiteral::Standard(elements) => { + let elements = vecmap(elements, |elem| self.resolve_expression(elem)); + HirArrayLiteral::Standard(elements) + } + ArrayLiteral::Repeated { repeated_element, length } => { + let span = length.span; + let length = + UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { + self.errors.push(ResolverError::ParserError(Box::new(error))); + UnresolvedTypeExpression::Constant(0, span) + }); + + let length = self.convert_expression_type(length); + let repeated_element = self.resolve_expression(*repeated_element); + + HirArrayLiteral::Repeated { repeated_element, length } + } + } + } + pub fn resolve_expression(&mut self, expr: Expression) -> ExprId { let hir_expr = match expr.kind { ExpressionKind::Literal(literal) => HirExpression::Literal(match literal { Literal::Bool(b) => HirLiteral::Bool(b), - Literal::Array(ArrayLiteral::Standard(elements)) => { - let elements = vecmap(elements, |elem| self.resolve_expression(elem)); - HirLiteral::Array(HirArrayLiteral::Standard(elements)) + Literal::Array(array_literal) => { + HirLiteral::Array(self.resolve_array_literal(array_literal)) } - Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let span = length.span; - let length = UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else( - |error| { - self.errors.push(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(0, span) - }, - ); - - let length = self.convert_expression_type(length); - let repeated_element = self.resolve_expression(*repeated_element); - - HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) + Literal::Slice(array_literal) => { + HirLiteral::Slice(self.resolve_array_literal(array_literal)) } Literal::Integer(integer, sign) => HirLiteral::Integer(integer, sign), Literal::Str(str) => HirLiteral::Str(str), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3d834128688..4e2a3a8b4a2 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -128,6 +128,8 @@ pub enum TypeCheckError { UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, + #[error("Slices must have constant length")] + NonConstantSliceLength { span: Span }, #[error("Only sized types may be used in the entry point to a program")] InvalidTypeForEntryPoint { span: Span }, #[error("Mismatched number of parameters in trait implementation")] @@ -233,6 +235,7 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } + | TypeCheckError::NonConstantSliceLength { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 0b3dd022209..eb998b59755 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -48,6 +48,49 @@ impl<'interner> TypeChecker<'interner> { false } + fn check_hir_array_literal( + &mut self, + hir_array_literal: HirArrayLiteral, + ) -> (Result>, Box) { + match hir_array_literal { + HirArrayLiteral::Standard(arr) => { + let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); + + let first_elem_type = elem_types + .first() + .cloned() + .unwrap_or_else(|| self.interner.next_type_variable()); + + // Check if the array is homogeneous + for (index, elem_type) in elem_types.iter().enumerate().skip(1) { + let location = self.interner.expr_location(&arr[index]); + + elem_type.unify(&first_elem_type, &mut self.errors, || { + TypeCheckError::NonHomogeneousArray { + first_span: self.interner.expr_location(&arr[0]).span, + first_type: first_elem_type.to_string(), + first_index: index, + second_span: location.span, + second_type: elem_type.to_string(), + second_index: index + 1, + } + .add_context("elements in an array must have the same type") + }); + } + + (Ok(arr.len() as u64), Box::new(first_elem_type.clone())) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let elem_type = self.check_expression(&repeated_element); + let length = match length { + Type::Constant(length) => Ok(length), + other => Err(Box::new(other)), + }; + (length, Box::new(elem_type)) + } + } + } + /// Infers a type for a given expression, and return this type. /// As a side-effect, this function will also remember this type in the NodeInterner /// for the given expr_id key. @@ -59,64 +102,42 @@ impl<'interner> TypeChecker<'interner> { pub(crate) fn check_expression(&mut self, expr_id: &ExprId) -> Type { let typ = match self.interner.expression(expr_id) { HirExpression::Ident(ident) => self.check_ident(ident, expr_id), - HirExpression::Literal(literal) => { - match literal { - HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { - let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); - - let first_elem_type = elem_types - .first() - .cloned() - .unwrap_or_else(|| self.interner.next_type_variable()); - - let arr_type = Type::Array( - Box::new(Type::constant_variable(arr.len() as u64, self.interner)), - Box::new(first_elem_type.clone()), - ); - - // Check if the array is homogeneous - for (index, elem_type) in elem_types.iter().enumerate().skip(1) { - let location = self.interner.expr_location(&arr[index]); - - elem_type.unify(&first_elem_type, &mut self.errors, || { - TypeCheckError::NonHomogeneousArray { - first_span: self.interner.expr_location(&arr[0]).span, - first_type: first_elem_type.to_string(), - first_index: index, - second_span: location.span, - second_type: elem_type.to_string(), - second_index: index + 1, - } - .add_context("elements in an array must have the same type") + HirExpression::Literal(literal) => match literal { + HirLiteral::Array(hir_array_literal) => { + let (length, elem_type) = self.check_hir_array_literal(hir_array_literal); + Type::Array( + length.map_or_else( + |typ| typ, + |constant| Box::new(Type::constant_variable(constant, self.interner)), + ), + elem_type, + ) + } + HirLiteral::Slice(hir_array_literal) => { + let (length_type, elem_type) = self.check_hir_array_literal(hir_array_literal); + match length_type { + Ok(_length) => Type::Slice(elem_type), + Err(_non_constant) => { + self.errors.push(TypeCheckError::NonConstantSliceLength { + span: self.interner.expr_span(expr_id), }); + Type::Error } - - arr_type - } - HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) => { - let elem_type = self.check_expression(&repeated_element); - let length = match length { - Type::Constant(length) => { - Type::constant_variable(length, self.interner) - } - other => other, - }; - Type::Array(Box::new(length), Box::new(elem_type)) } - HirLiteral::Bool(_) => Type::Bool, - HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), - HirLiteral::Str(string) => { - let len = Type::Constant(string.len() as u64); - Type::String(Box::new(len)) - } - HirLiteral::FmtStr(string, idents) => { - let len = Type::Constant(string.len() as u64); - let types = vecmap(&idents, |elem| self.check_expression(elem)); - Type::FmtString(Box::new(len), Box::new(Type::Tuple(types))) - } - HirLiteral::Unit => Type::Unit, } - } + HirLiteral::Bool(_) => Type::Bool, + HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), + HirLiteral::Str(string) => { + let len = Type::Constant(string.len() as u64); + Type::String(Box::new(len)) + } + HirLiteral::FmtStr(string, idents) => { + let len = Type::Constant(string.len() as u64); + let types = vecmap(&idents, |elem| self.check_expression(elem)); + Type::FmtString(Box::new(len), Box::new(Type::Tuple(types))) + } + HirLiteral::Unit => Type::Unit, + }, HirExpression::Infix(infix_expr) => { // The type of the infix expression must be looked up from a type table let lhs_type = self.check_expression(&infix_expr.lhs); @@ -557,6 +578,7 @@ impl<'interner> TypeChecker<'interner> { // XXX: We can check the array bounds here also, but it may be better to constant fold first // and have ConstId instead of ExprId for constants Type::Array(_, base_type) => *base_type, + Type::Slice(base_type) => *base_type, Type::Error => Type::Error, typ => { let span = self.interner.expr_span(&new_lhs); diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index e90da555803..49ba3244dc9 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -256,6 +256,7 @@ impl<'interner> TypeChecker<'interner> { let typ = match lvalue_type.follow_bindings() { Type::Array(_, elem_type) => *elem_type, + Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, other => { // TODO: Need a better span here diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index b4c590de491..6a1b4385f0d 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -99,6 +99,7 @@ impl HirBinaryOp { #[derive(Debug, Clone)] pub enum HirLiteral { Array(HirArrayLiteral), + Slice(HirArrayLiteral), Bool(bool), Integer(FieldElement, bool), //true for negative integer and false for positive Str(String), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 60bc5b2470f..7fb26aa3879 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -27,6 +27,9 @@ pub enum Type { /// is either a type variable of some kind or a Type::Constant. Array(Box, Box), + /// Slice(E) is a slice of elements of type E. + Slice(Box), + /// A primitive integer type with the given sign and bit count. /// E.g. `u32` would be `Integer(Unsigned, ThirtyTwo)` Integer(Signedness, IntegerBitSize), @@ -98,11 +101,6 @@ pub enum Type { /// bind to an integer without special checks to bind it to a non-type. Constant(u64), - /// The type of a slice is an array of size NotConstant. - /// The size of an array literal is resolved to this if it ever uses operations - /// involving slices. - NotConstant, - /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with /// an invalid type would otherwise issue a new error each time it is called @@ -146,27 +144,23 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) - | Type::NotConstant + | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } } pub(crate) fn is_nested_slice(&self) -> bool { match self { - Type::Array(size, elem) => { - if let Type::NotConstant = size.as_ref() { - elem.as_ref().contains_slice() - } else { - false - } - } + Type::Slice(elem) => elem.as_ref().contains_slice(), + Type::Array(_, elem) => elem.as_ref().contains_slice(), + Type::Alias(alias, generics) => alias.borrow().get_type(generics).is_nested_slice(), _ => false, } } pub(crate) fn contains_slice(&self) -> bool { match self { - Type::Array(size, _) => matches!(size.as_ref(), Type::NotConstant), + Type::Slice(_) => true, Type::Struct(struct_typ, generics) => { let fields = struct_typ.borrow().get_fields(generics); for field in fields.iter() { @@ -453,7 +447,7 @@ pub enum TypeVariableKind { /// that can only be bound to Type::Integer, or other polymorphic integers. Integer, - /// A potentially constant array size. This will only bind to itself, Type::NotConstant, or + /// A potentially constant array size. This will only bind to itself or /// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound /// during monomorphization. Constant(u64), @@ -631,14 +625,13 @@ impl Type { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::NotConstant | Type::Forall(_, _) | Type::TraitAsType(..) => false, Type::Array(length, elem) => { elem.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) } - + Type::Slice(elem) => elem.contains_numeric_typevar(target_id), Type::Tuple(fields) => { fields.iter().any(|field| field.contains_numeric_typevar(target_id)) } @@ -696,8 +689,8 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::TraitAsType(..) - | Type::NotConstant => false, + | Type::Slice(_) + | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { let alias = alias.borrow(); @@ -766,11 +759,10 @@ impl std::fmt::Display for Type { write!(f, "Field") } Type::Array(len, typ) => { - if matches!(len.follow_bindings(), Type::NotConstant) { - write!(f, "[{typ}]") - } else { - write!(f, "[{typ}; {len}]") - } + write!(f, "[{typ}; {len}]") + } + Type::Slice(typ) => { + write!(f, "[{typ}]") } Type::Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), @@ -860,7 +852,6 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } - Type::NotConstant => write!(f, "_"), } } } @@ -916,10 +907,6 @@ impl Type { bindings.insert(target_id, (var.clone(), this)); Ok(()) } - Type::NotConstant => { - bindings.insert(target_id, (var.clone(), Type::NotConstant)); - Ok(()) - } // A TypeVariable is less specific than a MaybeConstant, so we bind // to the other type variable instead. Type::TypeVariable(new_var, kind) => { @@ -947,14 +934,8 @@ impl Type { bindings.insert(*new_target_id, (new_var.clone(), clone)); Ok(()) } - // The lengths don't match, but neither are set in stone so we can - // just set them both to NotConstant. See issue 2370 - TypeVariableKind::Constant(_) => { - // *length != target_length - bindings.insert(target_id, (var.clone(), Type::NotConstant)); - bindings.insert(*new_target_id, (new_var.clone(), Type::NotConstant)); - Ok(()) - } + // *length != target_length + TypeVariableKind::Constant(_) => Err(UnificationError), TypeVariableKind::IntegerOrField => Err(UnificationError), TypeVariableKind::Integer => Err(UnificationError), }, @@ -1164,6 +1145,8 @@ impl Type { elem_a.try_unify(elem_b, bindings) } + (Slice(elem_a), Slice(elem_b)) => elem_a.try_unify(elem_b, bindings), + (String(len_a), String(len_b)) => len_a.try_unify(len_b, bindings), (FmtString(len_a, elements_a), FmtString(len_b, elements_b)) => { @@ -1308,20 +1291,14 @@ impl Type { let this = self.follow_bindings(); let target = target.follow_bindings(); - if let (Type::Array(size1, element1), Type::Array(size2, element2)) = (&this, &target) { - let size1 = size1.follow_bindings(); - let size2 = size2.follow_bindings(); - - // If we have an array and our target is a slice - if matches!(size1, Type::Constant(_)) && matches!(size2, Type::NotConstant) { - // Still have to ensure the element types match. - // Don't need to issue an error here if not, it will be done in unify_with_coercions - let mut bindings = TypeBindings::new(); - if element1.try_unify(element2, &mut bindings).is_ok() { - convert_array_expression_to_slice(expression, this, target, interner); - Self::apply_type_bindings(bindings); - return true; - } + if let (Type::Array(_size, element1), Type::Slice(element2)) = (&this, &target) { + // Still have to ensure the element types match. + // Don't need to issue an error here if not, it will be done in unify_with_coercions + let mut bindings = TypeBindings::new(); + if element1.try_unify(element2, &mut bindings).is_ok() { + convert_array_expression_to_slice(expression, this, target, interner); + Self::apply_type_bindings(bindings); + return true; } } false @@ -1489,6 +1466,10 @@ impl Type { let element = element.substitute_helper(type_bindings, substitute_bound_typevars); Type::Array(Box::new(size), Box::new(element)) } + Type::Slice(element) => { + let element = element.substitute_helper(type_bindings, substitute_bound_typevars); + Type::Slice(Box::new(element)) + } Type::String(size) => { let size = size.substitute_helper(type_bindings, substitute_bound_typevars); Type::String(Box::new(size)) @@ -1548,7 +1529,6 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error - | Type::NotConstant | Type::Unit => self.clone(), } } @@ -1557,6 +1537,7 @@ impl Type { pub fn occurs(&self, target_id: TypeVariableId) -> bool { match self { Type::Array(len, elem) => len.occurs(target_id) || elem.occurs(target_id), + Type::Slice(elem) => elem.occurs(target_id), Type::String(len) => len.occurs(target_id), Type::FmtString(len, fields) => { let len_occurs = len.occurs(target_id); @@ -1589,7 +1570,6 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error - | Type::NotConstant | Type::Unit => false, } } @@ -1606,6 +1586,7 @@ impl Type { Array(size, elem) => { Array(Box::new(size.follow_bindings()), Box::new(elem.follow_bindings())) } + Slice(elem) => Slice(Box::new(elem.follow_bindings())), String(size) => String(Box::new(size.follow_bindings())), FmtString(size, args) => { let size = Box::new(size.follow_bindings()); @@ -1640,14 +1621,9 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - TraitAsType(..) - | FieldElement - | Integer(_, _) - | Bool - | Constant(_) - | Unit - | Error - | NotConstant => self.clone(), + TraitAsType(..) | FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Error => { + self.clone() + } } } @@ -1727,6 +1703,10 @@ impl From<&Type> for PrintableType { let typ = typ.as_ref(); PrintableType::Array { length, typ: Box::new(typ.into()) } } + Type::Slice(typ) => { + let typ = typ.as_ref(); + PrintableType::Slice { typ: Box::new(typ.into()) } + } Type::Integer(sign, bit_width) => match sign { Signedness::Unsigned => { PrintableType::UnsignedInteger { width: (*bit_width).into() } @@ -1772,7 +1752,6 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } - Type::NotConstant => unreachable!(), } } } @@ -1784,11 +1763,10 @@ impl std::fmt::Debug for Type { write!(f, "Field") } Type::Array(len, typ) => { - if matches!(len.follow_bindings(), Type::NotConstant) { - write!(f, "[{typ:?}]") - } else { - write!(f, "[{typ:?}; {len:?}]") - } + write!(f, "[{typ:?}; {len:?}]") + } + Type::Slice(typ) => { + write!(f, "[{typ:?}]") } Type::Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), @@ -1858,7 +1836,6 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } - Type::NotConstant => write!(f, "NotConstant"), } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 21b77127360..4f633cbb350 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -89,6 +89,7 @@ pub struct For { #[derive(Debug, Clone, Hash)] pub enum Literal { Array(ArrayLiteral), + Slice(ArrayLiteral), Integer(FieldElement, Type, Location), Bool(bool), Str(String), diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index 3a03177f8ec..88943be727f 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -192,6 +192,7 @@ impl<'interner> Monomorphizer<'interner> { fn element_type_at_index(ptype: &PrintableType, i: usize) -> &PrintableType { match ptype { PrintableType::Array { length: _length, typ } => typ.as_ref(), + PrintableType::Slice { typ } => typ.as_ref(), PrintableType::Tuple { types } => &types[i], PrintableType::Struct { name: _name, fields } => &fields[i].1, PrintableType::String { length: _length } => &PrintableType::UnsignedInteger { width: 8 }, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index a99a4e61d4d..d4fe2e0a2aa 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -418,9 +418,15 @@ impl<'interner> Monomorphizer<'interner> { } } HirExpression::Literal(HirLiteral::Array(array)) => match array { - HirArrayLiteral::Standard(array) => self.standard_array(expr, array)?, + HirArrayLiteral::Standard(array) => self.standard_array(expr, array, false)?, HirArrayLiteral::Repeated { repeated_element, length } => { - self.repeated_array(expr, repeated_element, length)? + self.repeated_array(expr, repeated_element, length, false)? + } + }, + HirExpression::Literal(HirLiteral::Slice(array)) => match array { + HirArrayLiteral::Standard(array) => self.standard_array(expr, array, true)?, + HirArrayLiteral::Repeated { repeated_element, length } => { + self.repeated_array(expr, repeated_element, length, true)? } }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), @@ -519,10 +525,15 @@ impl<'interner> Monomorphizer<'interner> { &mut self, array: node_interner::ExprId, array_elements: Vec, + is_slice: bool, ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); let contents = try_vecmap(array_elements, |id| self.expr(id))?; - Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + if is_slice { + Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) + } else { + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + } } fn repeated_array( @@ -530,6 +541,7 @@ impl<'interner> Monomorphizer<'interner> { array: node_interner::ExprId, repeated_element: node_interner::ExprId, length: HirType, + is_slice: bool, ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); @@ -539,7 +551,11 @@ impl<'interner> Monomorphizer<'interner> { })?; let contents = try_vecmap(0..length, |_| self.expr(repeated_element))?; - Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + if is_slice { + Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) + } else { + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + } } fn index( @@ -854,12 +870,13 @@ impl<'interner> Monomorphizer<'interner> { HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { let element = Box::new(self.convert_type(element.as_ref())); - - if let Some(length) = length.evaluate_to_u64() { - ast::Type::Array(length, element) - } else { - ast::Type::Slice(element) - } + // TODO: convert to MonomorphizationError + let length = length.evaluate_to_u64().unwrap_or(0); + ast::Type::Array(length, element) + } + HirType::Slice(element) => { + let element = Box::new(self.convert_type(element.as_ref())); + ast::Type::Slice(element) } HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); @@ -934,10 +951,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::MutableReference(Box::new(element)) } - HirType::Forall(_, _) - | HirType::Constant(_) - | HirType::NotConstant - | HirType::Error => { + HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } } @@ -1152,11 +1166,7 @@ impl<'interner> Monomorphizer<'interner> { fn append_printable_type_info_inner(typ: &Type, arguments: &mut Vec) { // Disallow printing slices and mutable references for consistency, // since they cannot be passed from ACIR into Brillig - if let HirType::Array(size, _) = typ { - if let HirType::NotConstant = **size { - unreachable!("println and format strings do not support slices. Convert the slice to an array before passing it to println"); - } - } else if matches!(typ, HirType::MutableReference(_)) { + if matches!(typ, HirType::MutableReference(_)) { unreachable!("println and format strings do not support mutable references."); } @@ -1224,6 +1234,7 @@ impl<'interner> Monomorphizer<'interner> { location: Location, ) -> ast::Expression { use ast::*; + let int_type = Type::Integer(crate::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index c3e34890ce0..c253bfe7930 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -97,6 +97,11 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } + super::ast::Literal::Slice(array) => { + write!(f, "&[")?; + self.print_comma_separated(&array.contents, f)?; + write!(f, "]") + } super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => s.fmt(f), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b83d2008530..4e184e1391b 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1664,6 +1664,7 @@ enum TypeMethodKey { /// accept only fields or integers, it is just that their names may not clash. FieldOrInt, Array, + Slice, Bool, String, FmtString, @@ -1679,6 +1680,7 @@ fn get_type_method_key(typ: &Type) -> Option { match &typ { Type::FieldElement => Some(FieldOrInt), Type::Array(_, _) => Some(Array), + Type::Slice(_) => Some(Slice), Type::Integer(_, _) => Some(FieldOrInt), Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt), @@ -1697,7 +1699,6 @@ fn get_type_method_key(typ: &Type) -> Option { | Type::Forall(_, _) | Type::Constant(_) | Type::Error - | Type::NotConstant | Type::Struct(_, _) | Type::TraitAsType(..) => None, } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index b2d9d7e6802..f2e22da3c2c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -575,6 +575,7 @@ fn parse_type_inner( format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), + slice_type(recursive_type_parser.clone()), array_type(recursive_type_parser.clone()), parenthesized_type(recursive_type_parser.clone()), tuple_type(recursive_type_parser.clone()), @@ -711,13 +712,22 @@ fn generic_type_args( fn array_type(type_parser: impl NoirParser) -> impl NoirParser { just(Token::LeftBracket) .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then(just(Token::Semicolon).ignore_then(type_expression())) .then_ignore(just(Token::RightBracket)) .map_with_span(|(element_type, size), span| { UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) }) } +fn slice_type(type_parser: impl NoirParser) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|element_type, span| { + UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) + }) +} + fn type_expression() -> impl NoirParser { recursive(|expr| { expression_with_precedence( @@ -1079,6 +1089,36 @@ where .map(|(lhs, count)| ExpressionKind::repeated_array(lhs, count)) } +fn slice_expr

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Ampersand) + .ignore_then(standard_slice(expr_parser.clone()).or(slice_sugar(expr_parser))) +} + +/// &[a, b, c, ...] +fn standard_slice

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + expression_list(expr_parser) + .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) + .validate(|elements, _span, _emit| ExpressionKind::slice(elements)) +} + +/// &[a; N] +fn slice_sugar

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + expr_parser + .clone() + .then(just(Token::Semicolon).ignore_then(expr_parser)) + .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) + .map(|(lhs, count)| ExpressionKind::repeated_slice(lhs, count)) +} + fn expression_list

(expr_parser: P) -> impl NoirParser> where P: ExprParser, @@ -1102,6 +1142,7 @@ where { choice(( if_expr(expr_no_constructors, statement.clone()), + slice_expr(expr_parser.clone()), array_expr(expr_parser.clone()), if allow_constructors { constructor(expr_parser.clone()).boxed() @@ -1293,6 +1334,50 @@ mod test { parse_all_failing(array_expr(expression()), invalid); } + fn expr_to_slice(expr: ExpressionKind) -> ArrayLiteral { + let lit = match expr { + ExpressionKind::Literal(literal) => literal, + _ => unreachable!("expected a literal"), + }; + + match lit { + Literal::Slice(arr) => arr, + _ => unreachable!("expected a slice: {:?}", lit), + } + } + + #[test] + fn parse_slice() { + let valid = vec![ + "&[0, 1, 2,3, 4]", + "&[0,1,2,3,4,]", // Trailing commas are valid syntax + "&[0;5]", + ]; + + for expr in parse_all(slice_expr(expression()), valid) { + match expr_to_slice(expr) { + ArrayLiteral::Standard(elements) => assert_eq!(elements.len(), 5), + ArrayLiteral::Repeated { length, .. } => { + assert_eq!(length.kind, ExpressionKind::integer(5i128.into())); + } + } + } + + parse_all_failing( + slice_expr(expression()), + vec!["0,1,2,3,4]", "&[[0,1,2,3,4]", "&[0,1,2,,]", "&[0,1,2,3,4"], + ); + } + + #[test] + fn parse_slice_sugar() { + let valid = vec!["&[0;7]", "&[(1, 2); 4]", "&[0;Four]", "&[2;1+3-a]"]; + parse_all(slice_expr(expression()), valid); + + let invalid = vec!["&[0;;4]", "&[1, 2; 3]"]; + parse_all_failing(slice_expr(expression()), invalid); + } + #[test] fn parse_block() { parse_with(block(fresh_statement()), "{ [0,1,2,3,4] }").unwrap(); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 572397d6527..d8a63d161b9 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -88,13 +88,24 @@ pub(super) fn array_type( ) -> impl NoirParser { just(Token::LeftBracket) .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then(just(Token::Semicolon).ignore_then(type_expression())) .then_ignore(just(Token::RightBracket)) .map_with_span(|(element_type, size), span| { UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) }) } +pub(super) fn slice_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|element_type, span| { + UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) + }) +} + pub(super) fn type_expression() -> impl NoirParser { recursive(|expr| { expression_with_precedence( diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 60f233cd86d..d3d20654adc 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -15,6 +15,10 @@ pub enum PrintableType { #[serde(rename = "type")] typ: Box, }, + Slice { + #[serde(rename = "type")] + typ: Box, + }, Tuple { types: Vec, }, @@ -50,7 +54,7 @@ pub enum PrintableType { pub enum PrintableValue { Field(FieldElement), String(String), - Vec(Vec), + Vec { array_elements: Vec, is_slice: bool }, Struct(BTreeMap), Other, } @@ -184,9 +188,12 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { (_, PrintableType::MutableReference { .. }) => { output.push_str("<>"); } - (PrintableValue::Vec(vector), PrintableType::Array { typ, .. }) => { + (PrintableValue::Vec { array_elements, is_slice }, PrintableType::Array { typ, .. }) => { + if *is_slice { + output.push('&') + } output.push('['); - let mut values = vector.iter().peekable(); + let mut values = array_elements.iter().peekable(); while let Some(value) = values.next() { output.push_str(&format!( "{}", @@ -221,9 +228,9 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { output.push_str(" }"); } - (PrintableValue::Vec(values), PrintableType::Tuple { types }) => { + (PrintableValue::Vec { array_elements, .. }, PrintableType::Tuple { types }) => { output.push('('); - let mut elems = values.iter().zip(types).peekable(); + let mut elems = array_elements.iter().zip(types).peekable(); while let Some((value, typ)) = elems.next() { output.push_str( &PrintableValueDisplay::Plain(value.clone(), typ.clone()).to_string(), @@ -322,7 +329,7 @@ pub fn decode_value( array_elements.push(decode_value(field_iterator, typ)); } - PrintableValue::Vec(array_elements) + PrintableValue::Vec { array_elements, is_slice: false } } PrintableType::Array { length: Some(length), typ } => { let length = *length as usize; @@ -331,11 +338,24 @@ pub fn decode_value( array_elements.push(decode_value(field_iterator, typ)); } - PrintableValue::Vec(array_elements) + PrintableValue::Vec { array_elements, is_slice: false } } - PrintableType::Tuple { types } => { - PrintableValue::Vec(vecmap(types, |typ| decode_value(field_iterator, typ))) + PrintableType::Slice { typ } => { + let length = field_iterator + .next() + .expect("not enough data to decode variable array length") + .to_u128() as usize; + let mut array_elements = Vec::with_capacity(length); + for _ in 0..length { + array_elements.push(decode_value(field_iterator, typ)); + } + + PrintableValue::Vec { array_elements, is_slice: true } } + PrintableType::Tuple { types } => PrintableValue::Vec { + array_elements: vecmap(types, |typ| decode_value(field_iterator, typ)), + is_slice: false, + }, PrintableType::String { length } => { let field_elements: Vec = field_iterator.take(*length as usize).collect(); diff --git a/docs/docs/how_to/merkle-proof.mdx b/docs/docs/how_to/merkle-proof.mdx index 34074659ac1..003c7019a93 100644 --- a/docs/docs/how_to/merkle-proof.mdx +++ b/docs/docs/how_to/merkle-proof.mdx @@ -14,7 +14,7 @@ in a merkle tree. use dep::std; fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message); + let leaf = std::hash::hash_to_field(message.as_slice()); let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); assert(merkle_root == root); } @@ -27,7 +27,7 @@ random oracle. If only collision resistance is needed, then one can call `std::h instead. ```rust -let leaf = std::hash::hash_to_field(message); +let leaf = std::hash::hash_to_field(message.as_slice()); ``` The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. diff --git a/docs/docs/noir/concepts/data_types/arrays.md b/docs/docs/noir/concepts/data_types/arrays.md index a8bd338e736..efce3e95d32 100644 --- a/docs/docs/noir/concepts/data_types/arrays.md +++ b/docs/docs/noir/concepts/data_types/arrays.md @@ -72,7 +72,7 @@ let element = array[0][0]; ``` However, multidimensional slices are not supported. For example, the following code will error at compile time: ```rust -let slice : [[Field]] = []; +let slice : [[Field]] = &[]; ``` ## Types diff --git a/docs/docs/noir/concepts/data_types/fields.md b/docs/docs/noir/concepts/data_types/fields.md index 99b4aa63549..a10a4810788 100644 --- a/docs/docs/noir/concepts/data_types/fields.md +++ b/docs/docs/noir/concepts/data_types/fields.md @@ -42,7 +42,7 @@ After declaring a Field, you can use these common methods on it: Transforms the field into an array of bits, Little Endian. ```rust -fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] ``` example: @@ -59,7 +59,7 @@ fn main() { Transforms the field into an array of bits, Big Endian. ```rust -fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] ``` example: diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index 4a6ee816aa2..828faf4a8f8 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -15,13 +15,19 @@ A slice is a dynamically-sized view into a sequence of elements. They can be res use dep::std::slice; fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; + let mut slice: [Field] = &[0; 2]; let mut new_slice = slice.push_back(6); new_slice.len() } ``` +To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + View the corresponding test file [here][test-file]. [test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr @@ -42,7 +48,7 @@ example: ```rust fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; + let mut slice: [Field] = &[0; 2]; let mut new_slice = slice.push_back(6); new_slice.len() @@ -62,7 +68,7 @@ fn push_front(_self: Self, _elem: T) -> Self Example: ```rust -let mut new_slice: [Field] = []; +let mut new_slice: [Field] = &[]; new_slice = new_slice.push_front(20); assert(new_slice[0] == 20); // returns true ``` @@ -112,7 +118,7 @@ fn append(mut self, other: Self) -> Self Example: ```rust -let append = [1, 2].append([3, 4, 5]); +let append = &[1, 2].append(&[3, 4, 5]); ``` ### insert @@ -145,3 +151,20 @@ Example: ```rust let (remove_slice, removed_elem) = slice.remove(3); ``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` diff --git a/docs/docs/noir/concepts/functions.md b/docs/docs/noir/concepts/functions.md index 48aba9cd058..2c9bc33fdfc 100644 --- a/docs/docs/noir/concepts/functions.md +++ b/docs/docs/noir/concepts/functions.md @@ -71,7 +71,7 @@ fn main(x : [Field]) { #[test] fn test_one() { - main([1, 2]); + main(&[1, 2]); } ``` diff --git a/docs/docs/noir/standard_library/bigint.md b/docs/docs/noir/standard_library/bigint.md index 7d7931840cf..9aa4fb77112 100644 --- a/docs/docs/noir/standard_library/bigint.md +++ b/docs/docs/noir/standard_library/bigint.md @@ -31,7 +31,7 @@ For instance the big integer 'Secpk1Fq' in the standard library refers to intege Feel free to explore the source code for the other primes: -#include_code curve_order_base noir_stdlib/src/bigint.nr rust +#include_code big_int_definition noir_stdlib/src/bigint.nr rust ## Example usage @@ -48,7 +48,7 @@ The available operations for each big integer are: Construct a big integer from its little-endian bytes representation. Example: ```rust - let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); ``` Sure, here's the formatted version of the remaining methods: diff --git a/docs/docs/noir/standard_library/containers/vec.mdx b/docs/docs/noir/standard_library/containers/vec.mdx index 1954f05bc76..fcfd7e07aa0 100644 --- a/docs/docs/noir/standard_library/containers/vec.mdx +++ b/docs/docs/noir/standard_library/containers/vec.mdx @@ -49,8 +49,8 @@ pub fn from_slice(slice: [T]) -> Self Example: ```rust -let arr: [Field] = [1, 2, 3]; -let vector_from_slice = Vec::from_slice(arr); +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); assert(vector_from_slice.len() == 3); ``` @@ -80,7 +80,7 @@ pub fn get(self, index: Field) -> T Example: ```rust -let vector: Vec = Vec::from_slice([10, 20, 30]); +let vector: Vec = Vec::from_slice(&[10, 20, 30]); assert(vector.get(1) == 20); ``` @@ -111,7 +111,7 @@ pub fn pop(&mut self) -> T Example: ```rust -let mut vector = Vec::from_slice([10, 20]); +let mut vector = Vec::from_slice(&[10, 20]); let popped_elem = vector.pop(); assert(popped_elem == 20); assert(vector.len() == 1); @@ -128,7 +128,7 @@ pub fn insert(&mut self, index: Field, elem: T) Example: ```rust -let mut vector = Vec::from_slice([10, 30]); +let mut vector = Vec::from_slice(&[10, 30]); vector.insert(1, 20); assert(vector.get(1) == 20); ``` @@ -144,7 +144,7 @@ pub fn remove(&mut self, index: Field) -> T Example: ```rust -let mut vector = Vec::from_slice([10, 20, 30]); +let mut vector = Vec::from_slice(&[10, 20, 30]); let removed_elem = vector.remove(1); assert(removed_elem == 20); assert(vector.len() == 2); diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index d67a1ac94df..6787c9f46a1 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -11,7 +11,8 @@ Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 cur ## ecdsa_secp256k1::verify_signature -Verifier for ECDSA Secp256k1 signatures +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. #include_code ecdsa_secp256k1 noir_stdlib/src/ecdsa_secp256k1.nr rust @@ -24,9 +25,20 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +#include_code ecdsa_secp256k1_slice noir_stdlib/src/ecdsa_secp256k1.nr rust + + + ## ecdsa_secp256r1::verify_signature -Verifier for ECDSA Secp256r1 signatures +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. #include_code ecdsa_secp256r1 noir_stdlib/src/ecdsa_secp256r1.nr rust @@ -40,3 +52,11 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign ``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +#include_code ecdsa_secp256r1_slice noir_stdlib/src/ecdsa_secp256r1.nr rust + + diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index b9239f822e8..f98c90a97c8 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -13,6 +13,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## sha256 Given an array of bytes, returns the resulting sha256 hash. +See sha256_slice for a version that works directly on slices. #include_code sha256 noir_stdlib/src/hash.nr rust @@ -27,9 +28,18 @@ fn main() { +## sha256_slice + +A version of sha256 specialized to slices: + +#include_code sha256_slice noir_stdlib/src/hash.nr rust + + + ## blake2s Given an array of bytes, returns an array with the Blake2 hash +See blake2s_slice for a version that works directly on slices. #include_code blake2s noir_stdlib/src/hash.nr rust @@ -44,9 +54,18 @@ fn main() { +## blake2s_slice + +A version of blake2s specialized to slices: + +#include_code blake2s_slice noir_stdlib/src/hash.nr rust + + + ## blake3 Given an array of bytes, returns an array with the Blake3 hash +See blake3_slice for a version that works directly on slices. #include_code blake3 noir_stdlib/src/hash.nr rust @@ -61,9 +80,18 @@ fn main() { +## blake3_slice + +A version of blake3 specialized to slices: + +#include_code blake3_slice noir_stdlib/src/hash.nr rust + + + ## pedersen_hash Given an array of Fields, returns the Pedersen hash. +See pedersen_hash_slice for a version that works directly on slices. #include_code pedersen_hash noir_stdlib/src/hash.nr rust @@ -73,10 +101,18 @@ example: +## pedersen_hash_slice + +Given a slice of Fields, returns the Pedersen hash. + +#include_code pedersen_hash_slice noir_stdlib/src/hash.nr rust + + ## pedersen_commitment Given an array of Fields, returns the Pedersen commitment. +See pedersen_commitment_slice for a version that works directly on slices. #include_code pedersen_commitment noir_stdlib/src/hash.nr rust @@ -86,11 +122,20 @@ example: +## pedersen_commitment_slice + +Given a slice of Fields, returns the Pedersen commitment. + +#include_code pedersen_commitment_slice noir_stdlib/src/hash.nr rust + + + ## keccak256 -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes -(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes -of the input. +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. See keccak256_slice for a version that works +directly on slices. #include_code keccak256 noir_stdlib/src/hash.nr rust @@ -100,6 +145,15 @@ example: +## keccak256_slice + +Given a slice of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). + +#include_code keccak256_slice noir_stdlib/src/hash.nr rust + + + ## poseidon Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify @@ -156,7 +210,7 @@ fn main() { ## hash_to_field ```rust -fn hash_to_field(_input : [Field; N]) -> Field {} +fn hash_to_field(_input : [Field]) -> Field {} ``` Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx index 0e0c358c6e1..2c9eb18cd34 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -10,6 +10,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## schnorr::verify_signature Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). +See schnorr::verify_signature_slice for a version that works directly on slices. #include_code schnorr_verify noir_stdlib/src/schnorr.nr rust @@ -34,3 +35,12 @@ const signature = Array.from( ``` + +## schnorr::verify_signature_slice + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) +where the message is a slice. + +#include_code schnorr_verify_slice noir_stdlib/src/schnorr.nr rust + + diff --git a/docs/docs/noir/standard_library/merkle_trees.md b/docs/docs/noir/standard_library/merkle_trees.md index fa488677884..6a9ebf72ada 100644 --- a/docs/docs/noir/standard_library/merkle_trees.md +++ b/docs/docs/noir/standard_library/merkle_trees.md @@ -42,9 +42,9 @@ fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3] let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); let pubkey_x = pubkey[0]; let pubkey_y = pubkey[1]; - let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); - let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); println(root); } ``` diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.md index 9337499dac8..a93894043dc 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.md @@ -29,8 +29,8 @@ By incorporating this attribute directly in the circuit's definition, tooling li ## Verifying Recursive Proofs ```rust -#[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` :::info diff --git a/docs/docs/noir/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md index fb6d5ae1c48..e6e7e6d40cb 100644 --- a/docs/docs/noir/standard_library/traits.md +++ b/docs/docs/noir/standard_library/traits.md @@ -32,6 +32,8 @@ impl Default for bool { .. } impl Default for [T; N] where T: Default { .. } +impl Default for [T] { .. } + impl Default for (A, B) where A: Default, B: Default { .. } @@ -46,7 +48,8 @@ impl Default for (A, B, C, D, E) ``` For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type. +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. ## `std::convert` @@ -112,6 +115,9 @@ impl Eq for bool { .. } impl Eq for [T; N] where T: Eq { .. } +impl Eq for [T] + where T: Eq { .. } + impl Eq for (A, B) where A: Eq, B: Eq { .. } @@ -154,6 +160,9 @@ impl Ord for bool { .. } impl Ord for [T; N] where T: Ord { .. } +impl Ord for [T] + where T: Ord { .. } + impl Ord for (A, B) where A: Ord, B: Ord { .. } diff --git a/docs/docs/noir/standard_library/zeroed.md b/docs/docs/noir/standard_library/zeroed.md index 97dab02dac2..f450fecdd36 100644 --- a/docs/docs/noir/standard_library/zeroed.md +++ b/docs/docs/noir/standard_library/zeroed.md @@ -18,6 +18,7 @@ This function currently supports the following types: - Bool - Uint - Array +- Slice - String - Tuple - Function diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index e7fc28ba39b..39ec40a1480 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,25 +1,24 @@ use crate::ops::{Add, Sub, Mul, Div}; use crate::cmp::Eq; -// docs:start:curve_order_base -global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, - 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; -global bn254_fr = [0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, - 0x5D, 0x58, 0x81, 0x81, 0xB6, 0x45, 0x50, 0xB8, 0x29, 0xA0, 0x31, 0xE1, 0x72, 0x4E, 0x64, 0x30]; -global secpk1_fr = [0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, - 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpk1_fq = [0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; -// docs:end:curve_order_base - +global bn254_fq = &[0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, + 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; +global bn254_fr = &[0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, + 0x5D, 0x58, 0x81, 0x81, 0xB6, 0x45, 0x50, 0xB8, 0x29, 0xA0, 0x31, 0xE1, 0x72, 0x4E, 0x64, 0x30]; +global secpk1_fr = &[0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpk1_fq = &[0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpr1_fq = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpr1_fr = &[0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; +// docs:start:big_int_definition struct BigInt { pointer: u32, modulus: u32, } +// docs:end:big_int_definition impl BigInt { #[builtin(bigint_add)] diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index 38316e5d6a8..0eed50eb42b 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -28,6 +28,16 @@ impl Eq for [T; N] where T: Eq { } } +impl Eq for [T] where T: Eq { + fn eq(self, other: [T]) -> bool { + let mut result = self.len() == other.len(); + for i in 0 .. self.len() { + result &= self[i].eq(other[i]); + } + result + } +} + impl Eq for str { fn eq(self, other: str) -> bool { let self_bytes = self.as_bytes(); @@ -213,6 +223,26 @@ impl Ord for [T; N] where T: Ord { } } +impl Ord for [T] where T: Ord { + // The first non-equal element of both arrays determines + // the ordering for the whole array. + fn cmp(self, other: [T]) -> Ordering { + let mut result = self.len().cmp(other.len()); + for i in 0 .. self.len() { + if result == Ordering::equal() { + let result_i = self[i].cmp(other[i]); + + if result_i == Ordering::less() { + result = result_i; + } else if result_i == Ordering::greater() { + result = result_i; + } + } + } + result + } +} + impl Ord for (A, B) where A: Ord, B: Ord { fn cmp(self, other: (A, B)) -> Ordering { let result = self.0.cmp(other.0); diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index deec98185ff..56354438c89 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -5,7 +5,7 @@ struct Vec { // A separate type is technically not needed but helps differentiate which operations are mutable. impl Vec { pub fn new() -> Self { - Self { slice: [] } + Self { slice: &[] } } // Create a Vec containing each element from the given slice. diff --git a/noir_stdlib/src/default.nr b/noir_stdlib/src/default.nr index 32c4f3f3b48..bd2f1ce0cd2 100644 --- a/noir_stdlib/src/default.nr +++ b/noir_stdlib/src/default.nr @@ -23,6 +23,12 @@ impl Default for [T; N] where T: Default { } } +impl Default for [T] { + fn default() -> [T] { + &[] + } +} + impl Default for (A, B) where A: Default, B: Default { fn default() -> (A, B) { (A::default(), B::default()) diff --git a/noir_stdlib/src/ecdsa_secp256k1.nr b/noir_stdlib/src/ecdsa_secp256k1.nr index b72a1acd041..f84e2221f57 100644 --- a/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir_stdlib/src/ecdsa_secp256k1.nr @@ -8,3 +8,15 @@ pub fn verify_signature( ) -> bool // docs:end:ecdsa_secp256k1 {} + +#[foreign(ecdsa_secp256k1)] +// docs:start:ecdsa_secp256k1_slice +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +// docs:end:ecdsa_secp256k1_slice +{} + diff --git a/noir_stdlib/src/ecdsa_secp256r1.nr b/noir_stdlib/src/ecdsa_secp256r1.nr index ef92bf24ae4..76e68aeeafa 100644 --- a/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir_stdlib/src/ecdsa_secp256r1.nr @@ -8,3 +8,14 @@ pub fn verify_signature( ) -> bool // docs:end:ecdsa_secp256r1 {} + +#[foreign(ecdsa_secp256r1)] +// docs:start:ecdsa_secp256r1_slice +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +// docs:end:ecdsa_secp256r1_slice +{} diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 896dae15371..1a61b5e084e 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -12,38 +12,69 @@ pub fn sha256(input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} +#[foreign(sha256)] +// docs:start:sha256_slice +pub fn sha256_slice(input: [u8]) -> [u8; 32] +// docs:end:sha256_slice +{} + #[foreign(blake2s)] // docs:start:blake2s pub fn blake2s(input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} +#[foreign(blake2s)] +// docs:start:blake2s_slice +pub fn blake2s_slice(input: [u8]) -> [u8; 32] +// docs:end:blake2s_slice +{} + #[foreign(blake3)] // docs:start:blake3 pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} +#[foreign(blake3)] +// docs:start:blake3_slice +pub fn blake3_slice(input: [u8]) -> [u8; 32] +// docs:end:blake3_slice +{} + // docs:start:pedersen_commitment struct PedersenPoint { x : Field, y : Field, } -pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint -// docs:end:pedersen_commitment -{ +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { + // docs:end:pedersen_commitment pedersen_commitment_with_separator(input, 0) } +// docs:start:pedersen_commitment_slice +pub fn pedersen_commitment_slice(input: [Field]) -> PedersenPoint { + pedersen_commitment_with_separator_slice(input, 0) +} +// docs:end:pedersen_commitment_slice + #[foreign(pedersen_commitment)] pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} +#[foreign(pedersen_commitment)] +pub fn __pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> [Field; 2] {} + pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); PedersenPoint { x: values[0], y: values[1] } } +pub fn pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> PedersenPoint { + let values = __pedersen_commitment_with_separator_slice(input, separator); + PedersenPoint { x: values[0], y: values[1] } +} + // docs:start:pedersen_hash pub fn pedersen_hash(input: [Field; N]) -> Field // docs:end:pedersen_hash @@ -51,20 +82,30 @@ pub fn pedersen_hash(input: [Field; N]) -> Field pedersen_hash_with_separator(input, 0) } +// docs:start:pedersen_hash_slice +pub fn pedersen_hash_slice(input: [Field]) -> Field +// docs:end:pedersen_hash_slice +{ + pedersen_hash_with_separator_slice(input, 0) +} + #[foreign(pedersen_hash)] pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -pub fn hash_to_field(input: [Field; N]) -> Field { - let mut inputs_as_bytes = []; +#[foreign(pedersen_hash)] +pub fn pedersen_hash_with_separator_slice(input: [Field], separator: u32) -> Field {} + +pub fn hash_to_field(inputs: [Field]) -> Field { + let mut inputs_as_bytes = &[]; - for i in 0..N { - let input_bytes = input[i].to_le_bytes(32); + for input in inputs { + let input_bytes = input.to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } } - let hashed_input = blake2s(inputs_as_bytes); + let hashed_input = blake2s_slice(inputs_as_bytes); crate::field::bytes32_to_field(hashed_input) } @@ -74,6 +115,12 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] // docs:end:keccak256 {} +#[foreign(keccak256)] +// docs:start:keccak256_slice +pub fn keccak256_slice(input: [u8], message_size: u32) -> [u8; 32] +// docs:end:keccak256_slice +{} + #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -123,49 +170,49 @@ where impl Hash for Field { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self]); + H::write(state, &[self]); } } impl Hash for u8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for u32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for u64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for bool { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } @@ -175,7 +222,7 @@ impl Hash for () { impl Hash for U128 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self.lo as Field, self.hi as Field]); + H::write(state, &[self.lo as Field, self.hi as Field]); } } @@ -187,6 +234,15 @@ impl Hash for [T; N] where T: Hash { } } +impl Hash for [T] where T: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.len().hash(state); + for elem in self { + elem.hash(state); + } + } +} + impl Hash for (A, B) where A: Hash, B: Hash { fn hash(self, state: &mut H) where H: Hasher{ self.0.hash(state); diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index db8a32d7909..1fb53701013 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -152,7 +152,7 @@ impl Hasher for MimcHasher { impl Default for MimcHasher{ fn default() -> Self{ MimcHasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/hash/pedersen.nr b/noir_stdlib/src/hash/pedersen.nr index ace6851099d..ad21e728945 100644 --- a/noir_stdlib/src/hash/pedersen.nr +++ b/noir_stdlib/src/hash/pedersen.nr @@ -1,4 +1,4 @@ -use crate::hash::{Hasher, pedersen_hash}; +use crate::hash::{Hasher, pedersen_hash_slice}; use crate::default::Default; struct PedersenHasher{ @@ -7,7 +7,7 @@ struct PedersenHasher{ impl Hasher for PedersenHasher { fn finish(self) -> Field { - pedersen_hash(self._state) + pedersen_hash_slice(self._state) } fn write(&mut self, input: [Field]){ @@ -18,7 +18,7 @@ impl Hasher for PedersenHasher { impl Default for PedersenHasher{ fn default() -> Self{ PedersenHasher{ - _state: [] + _state: &[] } } } diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr index 7f99ad36316..85a0802f630 100644 --- a/noir_stdlib/src/hash/poseidon.nr +++ b/noir_stdlib/src/hash/poseidon.nr @@ -171,7 +171,7 @@ impl Hasher for PoseidonHasher { impl Default for PoseidonHasher{ fn default() -> Self{ PoseidonHasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 52229f18dbd..5b97d809896 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -139,7 +139,7 @@ impl Hasher for Poseidon2Hasher { impl Default for Poseidon2Hasher{ fn default() -> Self{ Poseidon2Hasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/schnorr.nr b/noir_stdlib/src/schnorr.nr index 757963d40d7..c63915061cb 100644 --- a/noir_stdlib/src/schnorr.nr +++ b/noir_stdlib/src/schnorr.nr @@ -8,3 +8,15 @@ pub fn verify_signature( ) -> bool // docs:end:schnorr_verify {} + +#[foreign(schnorr_verify)] +// docs:start:schnorr_verify_slice +pub fn verify_signature_slice( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8] +) -> bool +// docs:end:schnorr_verify_slice +{} + diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr index ea8d09d14ce..164b4f96cf6 100644 --- a/noir_stdlib/src/slice.nr +++ b/noir_stdlib/src/slice.nr @@ -1,4 +1,7 @@ impl [T] { + #[builtin(array_len)] + pub fn len(self) -> u64 {} + /// Push a new element to the end of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. diff --git a/test_programs/compile_success_empty/vectors/src/main.nr b/test_programs/compile_success_empty/vectors/src/main.nr index 28187a4f619..d105ceed180 100644 --- a/test_programs/compile_success_empty/vectors/src/main.nr +++ b/test_programs/compile_success_empty/vectors/src/main.nr @@ -26,7 +26,7 @@ fn main(x: Field, y: pub Field) { assert(vector.get(3) == 3); assert(vector.len() == 4); - let mut inputs_vector = Vec::from_slice([x, y]); + let mut inputs_vector = Vec::from_slice(&[x, y]); assert(inputs_vector.get(0) == x); assert(inputs_vector.get(1) == y); } diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index 4f5594c6d11..b97f68fc280 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -1,6 +1,6 @@ // Converts an array into a slice. fn as_slice_push(xs: [T; N]) -> [T] { - let mut slice = []; + let mut slice = &[]; for elem in xs { slice = slice.push_back(elem); } diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index b6c23276c15..db269d63ac0 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -2,8 +2,8 @@ use dep::std::bigint; use dep::std::{bigint::Secpk1Fq, println}; fn main(mut x: [u8; 5], y: [u8; 5]) { - let a = bigint::Secpk1Fq::from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); - let b = bigint::Secpk1Fq::from_le_bytes([y[0], y[1], y[2], y[3], y[4]]); + let a = bigint::Secpk1Fq::from_le_bytes(&[x[0], x[1], x[2], x[3], x[4]]); + let b = bigint::Secpk1Fq::from_le_bytes(&[y[0], y[1], y[2], y[3], y[4]]); let a_bytes = a.to_le_bytes(); let b_bytes = b.to_le_bytes(); for i in 0..5 { @@ -19,8 +19,8 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { // docs:start:big_int_example fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes([y, x, 9]); + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); let c = (a + b) * b / a; let d = c.to_le_bytes(); println(d[0]); diff --git a/test_programs/execution_success/brillig_hash_to_field/src/main.nr b/test_programs/execution_success/brillig_hash_to_field/src/main.nr index 4b4177a521e..53ed85b3ddd 100644 --- a/test_programs/execution_success/brillig_hash_to_field/src/main.nr +++ b/test_programs/execution_success/brillig_hash_to_field/src/main.nr @@ -7,5 +7,5 @@ fn main(input: Field) -> pub Field { } unconstrained fn hash_to_field(input: Field) -> Field { - std::hash::hash_to_field([input]) + std::hash::hash_to_field(&[input]) } diff --git a/test_programs/execution_success/brillig_keccak/src/main.nr b/test_programs/execution_success/brillig_keccak/src/main.nr index 1e9b65a6eb4..a300bc18279 100644 --- a/test_programs/execution_success/brillig_keccak/src/main.nr +++ b/test_programs/execution_success/brillig_keccak/src/main.nr @@ -7,7 +7,7 @@ fn main(x: Field, result: [u8; 32]) { // The padding is taken care of by the program let digest = keccak256([x as u8], 1); assert(digest == result); - //#1399: variable meesage size + //#1399: variable message size let message_size = 4; let hash_a = keccak256([1, 2, 3, 4], message_size); let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); diff --git a/test_programs/execution_success/brillig_schnorr/src/main.nr b/test_programs/execution_success/brillig_schnorr/src/main.nr index 4cc79ae7e07..03c635b4f6f 100644 --- a/test_programs/execution_success/brillig_schnorr/src/main.nr +++ b/test_programs/execution_success/brillig_schnorr/src/main.nr @@ -17,7 +17,7 @@ unconstrained fn main( // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature_slice(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); diff --git a/test_programs/execution_success/brillig_slices/src/main.nr b/test_programs/execution_success/brillig_slices/src/main.nr index 847c41de25c..2cf1850f151 100644 --- a/test_programs/execution_success/brillig_slices/src/main.nr +++ b/test_programs/execution_success/brillig_slices/src/main.nr @@ -1,6 +1,6 @@ use dep::std::slice; unconstrained fn main(x: Field, y: Field) { - let mut slice: [Field] = [y, x]; + let mut slice: [Field] = &[y, x]; assert(slice.len() == 2); slice = slice.push_back(7); @@ -108,7 +108,7 @@ unconstrained fn merge_slices_else(x: Field) { } // Test returning a merged slice without a mutation unconstrained fn merge_slices_return(x: Field, y: Field) -> [Field] { - let slice = [0; 2]; + let slice = &[0; 2]; if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { @@ -117,7 +117,7 @@ unconstrained fn merge_slices_return(x: Field, y: Field) -> [Field] { } // Test mutating a slice inside of an if statement unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -128,7 +128,7 @@ unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { } // Test mutating a slice inside of a loop in an if statement unconstrained fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { for i in 0..5 { slice = slice.push_back(i as Field); diff --git a/test_programs/execution_success/hash_to_field/src/main.nr b/test_programs/execution_success/hash_to_field/src/main.nr index 5af1c5af55e..242b5ecbc18 100644 --- a/test_programs/execution_success/hash_to_field/src/main.nr +++ b/test_programs/execution_success/hash_to_field/src/main.nr @@ -1,5 +1,5 @@ use dep::std; fn main(input: Field) -> pub Field { - std::hash::hash_to_field([input]) + std::hash::hash_to_field(&[input]) } diff --git a/test_programs/execution_success/nested_array_in_slice/src/main.nr b/test_programs/execution_success/nested_array_in_slice/src/main.nr index a3007d5d0dc..0890115e95a 100644 --- a/test_programs/execution_success/nested_array_in_slice/src/main.nr +++ b/test_programs/execution_success/nested_array_in_slice/src/main.nr @@ -13,7 +13,7 @@ fn main(y: Field) { let foo_two = Foo { a: 4, b: [5, 6, 21], bar: Bar { inner: [103, 104, 105] } }; let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; - let mut x = [foo_one]; + let mut x = &[foo_one]; x = x.push_back(foo_two); x = x.push_back(foo_three); x = x.push_back(foo_four); diff --git a/test_programs/execution_success/regression_4202/src/main.nr b/test_programs/execution_success/regression_4202/src/main.nr index 37d2ee4578d..faa14f03912 100644 --- a/test_programs/execution_success/regression_4202/src/main.nr +++ b/test_programs/execution_success/regression_4202/src/main.nr @@ -1,5 +1,5 @@ fn main(input: [u32; 4]) { - let mut slice1: [u32] = [1, 2, 3, 4]; + let mut slice1: [u32] = &[1, 2, 3, 4]; if slice1[0] == 3 { slice1[1] = 4; } diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index 107af152625..9e2838c34d9 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -17,7 +17,7 @@ fn main( // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature_slice(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); diff --git a/test_programs/execution_success/slice_dynamic_index/src/main.nr b/test_programs/execution_success/slice_dynamic_index/src/main.nr index 41fc9a645c1..9b97c0721bb 100644 --- a/test_programs/execution_success/slice_dynamic_index/src/main.nr +++ b/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -4,7 +4,7 @@ fn main(x: Field) { } fn regression_dynamic_slice_index(x: Field, y: Field) { - let mut slice = []; + let mut slice = &[]; for i in 0..5 { slice = slice.push_back(i as Field); } diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr index e44edf872a4..b20e3478898 100644 --- a/test_programs/execution_success/slices/src/main.nr +++ b/test_programs/execution_success/slices/src/main.nr @@ -2,7 +2,7 @@ use dep::std::slice; use dep::std; fn main(x: Field, y: pub Field) { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; assert(slice[0] == 0); assert(slice[0] != 1); slice[0] = x; @@ -13,7 +13,7 @@ fn main(x: Field, y: pub Field) { assert(slice_plus_10[2] != 8); assert(slice_plus_10.len() == 3); - let mut new_slice = []; + let mut new_slice = &[]; for i in 0..5 { new_slice = new_slice.push_back(i); } @@ -41,7 +41,7 @@ fn main(x: Field, y: pub Field) { assert(remove_slice[3] == 3); assert(remove_slice.len() == 4); - let append = [1, 2].append([3, 4, 5]); + let append = &[1, 2].append(&[3, 4, 5]); assert(append.len() == 5); assert(append[0] == 1); assert(append[4] == 5); @@ -55,9 +55,10 @@ fn main(x: Field, y: pub Field) { regression_slice_call_result(x, y); regression_4506(); } + // Ensure that slices of struct/tuple values work. fn regression_2083() { - let y = [(1, 2)]; + let y = &[(1, 2)]; let y = y.push_back((3, 4)); // [(1, 2), (3, 4)] let y = y.push_back((5, 6)); // [(1, 2), (3, 4), (5, 6)] assert(y[2].1 == 6); @@ -86,6 +87,7 @@ fn regression_2083() { assert(x.0 == 5); assert(x.1 == 6); } + // The parameters to this function must come from witness values (inputs to main) fn regression_merge_slices(x: Field, y: Field) { merge_slices_if(x, y); @@ -146,18 +148,20 @@ fn merge_slices_else(x: Field) { assert(slice[2] == 5); assert(slice.len() == 3); } + // Test returning a merged slice without a mutation fn merge_slices_return(x: Field, y: Field) -> [Field] { - let slice = [0; 2]; + let slice = &[0; 2]; if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { slice } } + // Test mutating a slice inside of an if statement fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -166,9 +170,10 @@ fn merge_slices_mutate(x: Field, y: Field) -> [Field] { } slice } + // Test mutating a slice inside of a loop in an if statement fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { for i in 0..5 { slice = slice.push_back(i as Field); @@ -180,7 +185,7 @@ fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { } fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -199,7 +204,7 @@ fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { } fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -225,7 +230,7 @@ fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { } fn merge_slices_push_then_pop(x: Field, y: Field) { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -249,7 +254,7 @@ fn merge_slices_push_then_pop(x: Field, y: Field) { } fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -272,7 +277,7 @@ fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { } fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -298,8 +303,8 @@ fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { // Previously, we'd get a type error when trying to assign an array of a different size to // an existing array variable. Now, we infer the variable must be a slice. fn regression_2370() { - let mut slice = []; - slice = [1, 2, 3]; + let mut slice = &[]; + slice = &[1, 2, 3]; } fn regression_4418(x: Field) { @@ -327,6 +332,6 @@ fn regression_slice_call_result(x: Field, y: Field) { } fn regression_4506() { - let slice: [Field] = [1, 2, 3]; + let slice: [Field] = &[1, 2, 3]; assert(slice == slice); } diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 143ee7987f8..4de1b25f48a 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -12,7 +12,9 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let mut dbg_session = spawn_bash(Some(15000)).expect("Could not start bash session"); + let timeout_seconds = 120; + let mut dbg_session = + spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); // Set backend to `/dev/null` to force an error if nargo tries to speak to a backend. dbg_session diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 66568bec833..0e9e177e023 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -93,16 +93,27 @@ impl DebugVars { .unwrap_or_else(|| panic!("type unavailable for type id {cursor_type_id:?}")); for index in indexes.iter() { (cursor, cursor_type) = match (cursor, cursor_type) { - (PrintableValue::Vec(array), PrintableType::Array { length, typ }) => { + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Array { length, typ }, + ) => { + assert!(!*is_slice, "slice has array type"); if let Some(len) = length { if *index as u64 >= *len { panic!("unexpected field index past array length") } - if *len != array.len() as u64 { + if *len != array_elements.len() as u64 { panic!("type/array length mismatch") } } - (array.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) + (array_elements.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) + } + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Slice { typ }, + ) => { + assert!(*is_slice, "array has slice type"); + (array_elements.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) } ( PrintableValue::Struct(field_map), @@ -114,18 +125,22 @@ impl DebugVars { let (key, typ) = fields.get(*index as usize).unwrap(); (field_map.get_mut(key).unwrap(), typ) } - (PrintableValue::Vec(array), PrintableType::Tuple { types }) => { + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Tuple { types }, + ) => { + assert!(!*is_slice, "slice has tuple type"); if *index >= types.len() as u32 { panic!( "unexpected field index ({index}) past tuple length ({})", types.len() ); } - if types.len() != array.len() { + if types.len() != array_elements.len() { panic!("type/array length mismatch") } let typ = types.get(*index as usize).unwrap(); - (array.get_mut(*index as usize).unwrap(), typ) + (array_elements.get_mut(*index as usize).unwrap(), typ) } _ => { panic!("unexpected assign field of {cursor_type:?} type"); diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index f68ccbfd50e..a796eeac326 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -242,8 +242,8 @@ fn compile_success_empty_{test_name}() {{ }} // `compile_success_empty` tests should be able to compile down to an empty circuit. - let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {{ - panic!("JSON was not well-formatted {{:?}}",output.stdout) + let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ + panic!("JSON was not well-formatted {{:?}}\n\n{{:?}}", e, std::str::from_utf8(&output.stdout)) }}); let num_opcodes = &json["programs"][0]["acir_opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); diff --git a/tooling/nargo_fmt/src/rewrite/array.rs b/tooling/nargo_fmt/src/rewrite/array.rs index 77e5e756f19..db7dc4701b7 100644 --- a/tooling/nargo_fmt/src/rewrite/array.rs +++ b/tooling/nargo_fmt/src/rewrite/array.rs @@ -6,7 +6,12 @@ use crate::{ visitor::{expr::NewlineMode, FmtVisitor}, }; -pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec, array_span: Span) -> String { +pub(crate) fn rewrite( + mut visitor: FmtVisitor, + array: Vec, + array_span: Span, + is_slice: bool, +) -> String { let pattern: &[_] = &[' ', '\t']; visitor.indent.block_indent(visitor.config); @@ -75,8 +80,9 @@ pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec, array_spa } } + let open_bracket = if is_slice { "&[" } else { "[" }; crate::visitor::expr::wrap_exprs( - "[", + open_bracket, "]", items_str.trim().into(), nested_indent, diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 32d104f559b..ffb8dea5a9d 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -122,7 +122,16 @@ pub(crate) fn rewrite( format!("[{repeated}; {length}]") } Literal::Array(ArrayLiteral::Standard(exprs)) => { - super::array(visitor.fork(), exprs, span) + super::array(visitor.fork(), exprs, span, false) + } + Literal::Slice(ArrayLiteral::Repeated { repeated_element, length }) => { + let repeated = rewrite_sub_expr(visitor, shape, *repeated_element); + let length = rewrite_sub_expr(visitor, shape, *length); + + format!("&[{repeated}; {length}]") + } + Literal::Slice(ArrayLiteral::Standard(exprs)) => { + super::array(visitor.fork(), exprs, span, true) } Literal::Unit => "()".to_string(), }, diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index aaa77b0bea5..922337cdb74 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -9,12 +9,12 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) match typ.typ { UnresolvedTypeData::Array(length, element) => { let typ = rewrite(visitor, _shape, *element); - if let Some(length) = length { - let length = visitor.slice(length.span()); - format!("[{typ}; {length}]") - } else { - format!("[{typ}]") - } + let length = visitor.slice(length.span()); + format!("[{typ}; {length}]") + } + UnresolvedTypeData::Slice(element) => { + let typ = rewrite(visitor, _shape, *element); + format!("[{typ}]") } UnresolvedTypeData::Parenthesized(typ) => { let typ = rewrite(visitor, _shape, *typ); diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 26feab65d83..26edb7a2af6 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -178,7 +178,7 @@ impl AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) - | Type::NotConstant + | Type::Slice(_) | Type::Function(_, _, _) => unreachable!("Type cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"),