diff --git a/crates/nargo/tests/test_data/higher-order-functions/src/main.nr b/crates/nargo/tests/test_data/higher-order-functions/src/main.nr index 356de9eb6d9..70b281951a8 100644 --- a/crates/nargo/tests/test_data/higher-order-functions/src/main.nr +++ b/crates/nargo/tests/test_data/higher-order-functions/src/main.nr @@ -44,6 +44,8 @@ fn test_array_functions() { let descending = myarray.sort_via(|a, b| a > b); constrain descending == [3, 2, 1]; + + constrain evens.map(|n| n / 2) == myarray; } fn foo() -> [u32; 2] { diff --git a/crates/noirc_frontend/src/hir/resolution/errors.rs b/crates/noirc_frontend/src/hir/resolution/errors.rs index 6b52ab03b46..4d08388ff74 100644 --- a/crates/noirc_frontend/src/hir/resolution/errors.rs +++ b/crates/noirc_frontend/src/hir/resolution/errors.rs @@ -2,7 +2,7 @@ pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::{Ident, Shared, StructType, Type}; +use crate::{parser::ParserError, Ident, Shared, StructType, Type}; use super::import::PathResolutionError; @@ -59,6 +59,8 @@ pub enum ResolverError { actual: usize, expected: usize, }, + #[error("{0}")] + ParserError(ParserError), } impl ResolverError { @@ -239,6 +241,7 @@ impl From for Diagnostic { span, ) } + ResolverError::ParserError(error) => error.into(), } } } diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 3fe8dadf38e..a6da1b3df1b 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -12,7 +12,7 @@ // // XXX: Resolver does not check for unused functions use crate::hir_def::expr::{ - HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, + HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirExpression, HirForExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, @@ -93,6 +93,7 @@ pub struct Resolver<'a> { struct ResolverMeta { num_times_used: usize, ident: HirIdent, + warn_if_unused: bool, } impl<'a> Resolver<'a> { @@ -171,11 +172,7 @@ impl<'a> Resolver<'a> { fn check_for_unused_variables_in_local_scope(decl_map: Scope, unused_vars: &mut Vec) { let unused_variables = decl_map.filter(|(variable_name, metadata)| { let has_underscore_prefix = variable_name.starts_with('_'); // XXX: This is used for development mode, and will be removed - - if metadata.num_times_used == 0 && !has_underscore_prefix { - return true; - } - false + metadata.warn_if_unused && metadata.num_times_used == 0 && !has_underscore_prefix }); unused_vars.extend(unused_variables.map(|(_, meta)| meta.ident)); } @@ -195,6 +192,17 @@ impl<'a> Resolver<'a> { mutable: bool, allow_shadowing: bool, definition: DefinitionKind, + ) -> HirIdent { + self.add_variable_decl_inner(name, mutable, allow_shadowing, true, definition) + } + + fn add_variable_decl_inner( + &mut self, + name: Ident, + mutable: bool, + allow_shadowing: bool, + warn_if_unused: bool, + definition: DefinitionKind, ) -> HirIdent { if definition.is_global() { return self.add_global_variable_decl(name, definition); @@ -203,7 +211,7 @@ impl<'a> Resolver<'a> { let id = self.interner.push_definition(name.0.contents.clone(), mutable, definition); let location = Location::new(name.span(), self.file); let ident = HirIdent { location, id }; - let resolver_meta = ResolverMeta { num_times_used: 0, ident }; + let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; let scope = self.scopes.get_mut_scope(); let old_value = scope.add_key_value(name.0.contents.clone(), resolver_meta); @@ -242,12 +250,12 @@ impl<'a> Resolver<'a> { if let Some(id) = stmt_id { let hir_let_stmt = self.interner.let_statement(&id); ident = hir_let_stmt.ident(); - resolver_meta = ResolverMeta { num_times_used: 0, ident }; + resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused: true }; } else { let id = self.interner.push_definition(name.0.contents.clone(), false, definition); let location = Location::new(name.span(), self.file); ident = HirIdent { location, id }; - resolver_meta = ResolverMeta { num_times_used: 0, ident }; + resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused: true }; } let old_global_value = scope.add_key_value(name.0.contents.clone(), resolver_meta); @@ -576,12 +584,13 @@ impl<'a> Resolver<'a> { let attributes = func.attribute().cloned(); - let mut generics = vecmap(&self.generics, |(name, typevar, _)| match &*typevar.borrow() { - TypeBinding::Unbound(id) => (*id, typevar.clone()), - TypeBinding::Bound(binding) => { - unreachable!("Expected {} to be unbound, but it is bound to {}", name, binding) - } - }); + let mut generics = + vecmap(self.generics.clone(), |(name, typevar, _)| match &*typevar.borrow() { + TypeBinding::Unbound(id) => (*id, typevar.clone()), + TypeBinding::Bound(binding) => { + unreachable!("Expected {} to be unbound, but it is bound to {}", name, binding) + } + }); let mut parameters = vec![]; let mut parameter_types = vec![]; @@ -599,6 +608,8 @@ impl<'a> Resolver<'a> { let return_type = Box::new(self.resolve_type(func.return_type())); + self.declare_numeric_generics(¶meter_types, &return_type); + if func.name() == "main" && *return_type != Type::Unit && func.def.return_visibility != noirc_abi::AbiVisibility::Public @@ -632,6 +643,85 @@ impl<'a> Resolver<'a> { } } + fn declare_numeric_generics(&mut self, params: &[Type], return_type: &Type) { + if self.generics.is_empty() { + return; + } + + for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { + // Declare any generics to let users use numeric generics in scope. + // Don't issue a warning if these are unused + // + // We can fail to find the generic in self.generics if it is an implicit one created + // by the compiler. This can happen when, e.g. elliding array lengths using the slice + // syntax [T]. + if let Some((name, _, span)) = + self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) + { + let ident = Ident::new(name.to_string(), *span); + let definition = DefinitionKind::GenericType(type_variable); + self.add_variable_decl_inner(ident, false, false, false, definition); + } + } + } + + fn find_numeric_generics( + parameters: &[Type], + return_type: &Type, + ) -> Vec<(String, TypeVariable)> { + let mut found = HashMap::new(); + for parameter in parameters { + Self::find_numeric_generics_in_type(parameter, &mut found); + } + Self::find_numeric_generics_in_type(return_type, &mut found); + found.into_iter().collect() + } + + fn find_numeric_generics_in_type(typ: &Type, found: &mut HashMap>) { + match typ { + Type::FieldElement(_) + | Type::Integer(_, _, _) + | Type::Bool(_) + | Type::String(_) + | Type::Unit + | Type::Error + | Type::TypeVariable(_) + | Type::PolymorphicInteger(_, _) + | Type::Constant(_) + | Type::NamedGeneric(_, _) + | Type::Forall(_, _) => (), + + Type::Array(length, _) => { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + found.insert(name.to_string(), type_variable.clone()); + } + } + + Type::Tuple(fields) => { + for field in fields { + Self::find_numeric_generics_in_type(field, found); + } + } + Type::Function(parameters, return_type) => { + for parameter in parameters { + Self::find_numeric_generics_in_type(parameter, found); + } + Self::find_numeric_generics_in_type(return_type, found); + } + Type::Struct(struct_type, generics) => { + for (i, generic) in generics.iter().enumerate() { + if let Type::NamedGeneric(type_variable, name) = generic { + if struct_type.borrow().generic_is_numeric(i) { + found.insert(name.to_string(), type_variable.clone()); + } + } else { + Self::find_numeric_generics_in_type(generic, found); + } + } + } + } + } + pub fn resolve_global_let(&mut self, let_stmt: crate::LetStatement) -> HirStatement { let expression = self.resolve_expression(let_stmt.expression); let definition = DefinitionKind::Global(expression); @@ -696,12 +786,22 @@ impl<'a> Resolver<'a> { ExpressionKind::Literal(literal) => HirExpression::Literal(match literal { Literal::Bool(b) => HirLiteral::Bool(b), Literal::Array(ArrayLiteral::Standard(elements)) => { - HirLiteral::Array(vecmap(elements, |elem| self.resolve_expression(elem))) + let elements = vecmap(elements, |elem| self.resolve_expression(elem)); + HirLiteral::Array(HirArrayLiteral::Standard(elements)) } Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let len = self.eval_array_length(&length); - let elem = self.resolve_expression(*repeated_element); - HirLiteral::Array(vec![elem; len.try_into().unwrap()]) + let span = length.span; + let length = UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else( + |error| { + self.errors.push(ResolverError::ParserError(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::Integer(integer) => HirLiteral::Integer(integer), Literal::Str(str) => HirLiteral::Str(str), @@ -877,7 +977,7 @@ impl<'a> Resolver<'a> { } Pattern::Tuple(fields, span) => { let fields = vecmap(fields, |field| { - self.resolve_pattern_mutable(field, mutable, definition) + self.resolve_pattern_mutable(field, mutable, definition.clone()) }); HirPattern::Tuple(fields, span) } @@ -887,7 +987,7 @@ impl<'a> Resolver<'a> { // shadowing here lets us avoid further errors if we define ERROR_IDENT // multiple times. let name = ERROR_IDENT.into(); - let identifier = this.add_variable_decl(name, false, true, definition); + let identifier = this.add_variable_decl(name, false, true, definition.clone()); HirPattern::Identifier(identifier) }; @@ -901,7 +1001,7 @@ impl<'a> Resolver<'a> { }; let resolve_field = |this: &mut Self, pattern| { - this.resolve_pattern_mutable(pattern, mutable, definition) + this.resolve_pattern_mutable(pattern, mutable, definition.clone()) }; let typ = struct_type.clone(); @@ -1062,11 +1162,6 @@ impl<'a> Resolver<'a> { self.interner.push_expr(hir_block) } - fn eval_array_length(&mut self, length: &Expression) -> u64 { - let result = self.try_eval_array_length(length); - self.unwrap_array_length_eval_result(result, length.span) - } - fn eval_global_as_array_length(&mut self, global: StmtId) -> u64 { let stmt = match self.interner.statement(&global) { HirStatement::Let(let_expr) => let_expr, @@ -1078,14 +1173,7 @@ impl<'a> Resolver<'a> { let length = stmt.expression; let span = self.interner.expr_span(&length); let result = self.try_eval_array_length_id(length, span); - self.unwrap_array_length_eval_result(result, span) - } - fn unwrap_array_length_eval_result( - &mut self, - result: Result>, - span: Span, - ) -> u64 { match result.map(|length| length.try_into()) { Ok(Ok(length_value)) => return length_value, Ok(Err(_cast_err)) => self.push_err(ResolverError::IntegerTooLarge { span }), @@ -1095,94 +1183,6 @@ impl<'a> Resolver<'a> { 0 } - /// This function is a mini interpreter inside name resolution. - /// We should eventually get rid of it and only have 1 evaluator - the existing - /// one inside the ssa pass. Doing this would mean ssa would need to handle these - /// sugared array forms but would let users use any comptime expressions, including functions, - /// inside array lengths. - fn try_eval_array_length( - &mut self, - length: &Expression, - ) -> Result> { - let span = length.span; - match &length.kind { - ExpressionKind::Literal(Literal::Integer(int)) => { - int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span })) - } - ExpressionKind::Variable(path) => { - let ident = self.get_ident_from_path(path.clone()); - self.try_eval_array_length_ident(ident.id, span) - } - ExpressionKind::Prefix(operator) => { - let value = self.try_eval_array_length(&operator.rhs)?; - match operator.operator { - crate::UnaryOp::Minus => Ok(0 - value), - crate::UnaryOp::Not => Ok(!value), - } - } - ExpressionKind::Infix(operator) => { - let lhs = self.try_eval_array_length(&operator.lhs)?; - let rhs = self.try_eval_array_length(&operator.rhs)?; - match operator.operator.contents { - crate::BinaryOpKind::Add => Ok(lhs + rhs), - crate::BinaryOpKind::Subtract => Ok(lhs - rhs), - crate::BinaryOpKind::Multiply => Ok(lhs * rhs), - crate::BinaryOpKind::Divide => Ok(lhs / rhs), - crate::BinaryOpKind::ShiftRight => Ok(lhs >> rhs), - crate::BinaryOpKind::ShiftLeft => Ok(lhs << rhs), - crate::BinaryOpKind::Modulo => Ok(lhs % rhs), - crate::BinaryOpKind::And => Ok(lhs & rhs), - crate::BinaryOpKind::Or => Ok(lhs | rhs), - crate::BinaryOpKind::Xor => Ok(lhs ^ rhs), - - crate::BinaryOpKind::Equal - | crate::BinaryOpKind::NotEqual - | crate::BinaryOpKind::Less - | crate::BinaryOpKind::LessEqual - | crate::BinaryOpKind::Greater - | crate::BinaryOpKind::GreaterEqual => { - Err(Some(ResolverError::InvalidArrayLengthExpr { span })) - } - } - } - - ExpressionKind::Literal(_) - | ExpressionKind::Block(_) - | ExpressionKind::Index(_) - | ExpressionKind::Call(_) - | ExpressionKind::MethodCall(_) - | ExpressionKind::Constructor(_) - | ExpressionKind::MemberAccess(_) - | ExpressionKind::Cast(_) - | ExpressionKind::For(_) - | ExpressionKind::If(_) - | ExpressionKind::Lambda(_) - | ExpressionKind::Tuple(_) => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), - - ExpressionKind::Error => Err(None), - } - } - - fn try_eval_array_length_ident( - &mut self, - id: DefinitionId, - span: Span, - ) -> Result> { - if id == DefinitionId::dummy_id() { - return Err(None); // error already reported - } - - let definition = self.interner.definition(id); - - use DefinitionKind::{Global, Local}; - match definition.kind { - Global(rhs) | Local(Some(rhs)) if !definition.mutable => { - self.try_eval_array_length_id(rhs, span) - } - _ => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), - } - } - fn try_eval_array_length_id( &self, rhs: ExprId, diff --git a/crates/noirc_frontend/src/hir/scope/mod.rs b/crates/noirc_frontend/src/hir/scope/mod.rs index d10cdd2f8af..85b7e2e62e9 100644 --- a/crates/noirc_frontend/src/hir/scope/mod.rs +++ b/crates/noirc_frontend/src/hir/scope/mod.rs @@ -137,14 +137,17 @@ impl ScopeForest { fn extend_current_scope_tree(&mut self) { self.current_scope_tree().push_scope() } + fn remove_scope_tree_extension(&mut self) -> Scope { self.current_scope_tree().pop_scope() } + /// Starting a function requires a new scope tree, as you do not want the functions scope to /// have access to the scope of the caller pub fn start_function(&mut self) { self.0.push(ScopeTree::default()) } + /// Ending a function requires that we removes it's whole tree of scope /// This is by design the current scope, which is the last element in the vector pub fn end_function(&mut self) -> ScopeTree { diff --git a/crates/noirc_frontend/src/hir/type_check/expr.rs b/crates/noirc_frontend/src/hir/type_check/expr.rs index 314f5ee8529..1d040b476e9 100644 --- a/crates/noirc_frontend/src/hir/type_check/expr.rs +++ b/crates/noirc_frontend/src/hir/type_check/expr.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ hir_def::{ - expr::{self, HirBinaryOp, HirExpression, HirLiteral}, + expr::{self, HirArrayLiteral, HirBinaryOp, HirExpression, HirLiteral}, types::Type, }, node_interner::{ExprId, FuncId, NodeInterner}, @@ -38,7 +38,7 @@ pub(crate) fn type_check_expression( } HirExpression::Literal(literal) => { match literal { - HirLiteral::Array(arr) => { + HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { let elem_types = vecmap(&arr, |arg| type_check_expression(interner, arg, errors)); @@ -68,6 +68,10 @@ pub(crate) fn type_check_expression( arr_type } + HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) => { + let elem_type = type_check_expression(interner, &repeated_element, errors); + Type::Array(Box::new(length), Box::new(elem_type)) + } HirLiteral::Bool(_) => Type::Bool(CompTime::new(interner)), HirLiteral::Integer(_) => { let id = interner.next_type_variable_id(); diff --git a/crates/noirc_frontend/src/hir_def/expr.rs b/crates/noirc_frontend/src/hir_def/expr.rs index a34fbe73721..b9ee6634cc7 100644 --- a/crates/noirc_frontend/src/hir_def/expr.rs +++ b/crates/noirc_frontend/src/hir_def/expr.rs @@ -76,12 +76,18 @@ impl HirBinaryOp { #[derive(Debug, Clone)] pub enum HirLiteral { - Array(Vec), + Array(HirArrayLiteral), Bool(bool), Integer(FieldElement), Str(String), } +#[derive(Debug, Clone)] +pub enum HirArrayLiteral { + Standard(Vec), + Repeated { repeated_element: ExprId, length: Type }, +} + #[derive(Debug, Clone)] pub struct HirPrefixExpression { pub operator: UnaryOp, diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index 88fe16d3fb9..e75f2dc79ca 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -197,6 +197,14 @@ impl StructType { self.fields.keys().cloned().collect() } + /// True if the given index is the same index as a generic type of this struct + /// which is expected to be a numeric generic. + /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. + pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { + let target_id = self.generics[index_of_generic].0; + self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) + } + /// Instantiate this struct type, returning a Vec of the new generic args (in /// the same order as self.generics) pub fn instantiate(&self, interner: &mut NodeInterner) -> Vec { @@ -532,6 +540,57 @@ impl Type { pub fn is_field(&self) -> bool { matches!(self.follow_bindings(), Type::FieldElement(_)) } + + fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { + // True if the given type is a NamedGeneric with the target_id + let named_generic_id_matches_target = |typ: &Type| { + if let Type::NamedGeneric(type_variable, _) = typ { + match &*type_variable.borrow() { + TypeBinding::Bound(_) => { + unreachable!("Named generics should not be bound until monomorphization") + } + TypeBinding::Unbound(id) => target_id == *id, + } + } else { + false + } + }; + + match self { + Type::FieldElement(_) + | Type::Integer(_, _, _) + | Type::Bool(_) + | Type::String(_) + | Type::Unit + | Type::Error + | Type::TypeVariable(_) + | Type::PolymorphicInteger(_, _) + | Type::Constant(_) + | Type::NamedGeneric(_, _) + | Type::Forall(_, _) => false, + + Type::Array(length, elem) => { + elem.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) + } + + Type::Tuple(fields) => { + fields.iter().any(|field| field.contains_numeric_typevar(target_id)) + } + Type::Function(parameters, return_type) => { + parameters.iter().any(|parameter| parameter.contains_numeric_typevar(target_id)) + || return_type.contains_numeric_typevar(target_id) + } + Type::Struct(struct_type, generics) => { + generics.iter().enumerate().any(|(i, generic)| { + if named_generic_id_matches_target(generic) { + struct_type.borrow().generic_is_numeric(i) + } else { + generic.contains_numeric_typevar(target_id) + } + }) + } + } + } } impl std::fmt::Display for Type { diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 01a87f832ca..d852db9ac24 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -251,11 +251,24 @@ impl<'interner> Monomorphizer<'interner> { let typ = Self::convert_type(&self.interner.id_type(expr)); Literal(Integer(value, typ)) } - HirExpression::Literal(HirLiteral::Array(array)) => { + 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::Block(block) => self.block(block.0), HirExpression::Prefix(prefix) => ast::Expression::Unary(ast::Unary { @@ -479,23 +492,36 @@ impl<'interner> Monomorphizer<'interner> { fn ident(&mut self, ident: HirIdent, expr_id: node_interner::ExprId) -> ast::Expression { let definition = self.interner.definition(ident.id); - match definition.kind { + match &definition.kind { DefinitionKind::Function(func_id) => { let mutable = definition.mutable; let location = Some(ident.location); let name = definition.name.clone(); let typ = self.interner.id_type(expr_id); - let definition = self.lookup_function(func_id, expr_id, &typ); + let definition = self.lookup_function(*func_id, expr_id, &typ); let typ = Self::convert_type(&typ); 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_infer(*expr_id), DefinitionKind::Local(_) => { let ident = self.local_ident(&ident).unwrap(); ast::Expression::Ident(ident) } + DefinitionKind::GenericType(type_variable) => { + let value = match &*type_variable.borrow() { + TypeBinding::Unbound(_) => { + unreachable!("Unbound type variable used in expression") + } + TypeBinding::Bound(binding) => binding.evaluate_to_u64().unwrap_or_else(|| { + panic!("Non-numeric type variable used in expression expecting a value") + }), + }; + + let value = FieldElement::from(value as u128); + ast::Expression::Literal(ast::Literal::Integer(value, ast::Type::Field)) + } } } diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 4917a1647da..5516f12d3e4 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -18,7 +18,7 @@ use crate::hir_def::{ function::{FuncMeta, HirFunction}, stmt::HirStatement, }; -use crate::{Shared, TypeBinding, TypeBindings, TypeVariableId}; +use crate::{Shared, TypeBinding, TypeBindings, TypeVariable, TypeVariableId}; /// The node interner is the central storage location of all nodes in Noir's Hir (the /// various node types can be found in hir_def). The interner is also used to collect @@ -199,7 +199,7 @@ impl DefinitionInfo { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum DefinitionKind { Function(FuncId), Global(ExprId), @@ -207,6 +207,10 @@ pub enum DefinitionKind { /// Locals may be defined in let statements or parameters, /// in which case they will not have an associated ExprId Local(Option), + + /// Generic types in functions (T, U in `fn foo(...)` are declared as variables + /// in scope in case they resolve to numeric generics later. + GenericType(TypeVariable), } impl DefinitionKind { @@ -221,6 +225,7 @@ impl DefinitionKind { DefinitionKind::Function(_) => None, DefinitionKind::Global(id) => Some(id), DefinitionKind::Local(id) => id, + DefinitionKind::GenericType(_) => None, } } } @@ -393,13 +398,12 @@ impl NodeInterner { mutable: bool, definition: DefinitionKind, ) -> DefinitionId { - let id = self.definitions.len(); - self.definitions.push(DefinitionInfo { name, mutable, kind: definition }); - - let id = DefinitionId(id); + let id = DefinitionId(self.definitions.len()); if let DefinitionKind::Function(func_id) = definition { self.function_definition_ids.insert(func_id, id); } + + self.definitions.push(DefinitionInfo { name, mutable, kind: definition }); id } diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index f22339aec59..52c6b6c218c 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -20,6 +20,19 @@ impl [T; N] { a } + // Apply a function to each element of an array, returning a new array + // containing the mapped elements. + fn map(self, f: fn(T) -> U) -> [U; N] { + let first_elem = f(self[0]); + let mut ret = [first_elem; N]; + + for i in 1 .. self.len() { + ret[i] = f(self[i]); + } + + ret + } + // Apply a function to each element of the array and an accumulator value, // returning the final accumulated value. This function is also sometimes // called `foldl`, `fold_left`, `reduce`, or `inject`.