diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 2a252633a29..7506cead2e4 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use crate::token::{Attributes, Token}; use crate::{ - Distinctness, FunctionVisibility, Ident, Path, Pattern, Recoverable, Statement, StatementKind, - UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, + Distinctness, FunctionVisibility, GenericIdent, Ident, Path, Pattern, Recoverable, Statement, + StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; use acvm::FieldElement; use iter_extended::vecmap; @@ -32,7 +32,7 @@ pub enum ExpressionKind { /// A Vec of unresolved names for type variables. /// For `fn foo(...)` this corresponds to vec!["A", "B"]. -pub type UnresolvedGenerics = Vec; +pub type UnresolvedGenerics = Vec; impl ExpressionKind { pub fn into_path(self) -> Option { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index f39b71405d3..84026e6c8f3 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -233,6 +233,21 @@ impl Recoverable for Vec { } } +#[derive(Eq, PartialEq, Debug, Clone)] +pub struct GenericIdent { + pub ident: Ident, + /// "N?" prevents "N" from being used as a numeric. + /// This allows "N" to be used for both array and slice lengths. + pub prevent_numeric: bool, +} + +impl Display for GenericIdent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let question_mark = if self.prevent_numeric { "?" } else { "" }; + write!(f, "{}{}", self.ident, question_mark) + } +} + /// Trait for recoverable nodes during parsing. /// This is similar to Default but is expected /// to return an Error node of the appropriate type. diff --git a/compiler/noirc_frontend/src/ast/structure.rs b/compiler/noirc_frontend/src/ast/structure.rs index 6a32fa717f3..a3ef5d044d9 100644 --- a/compiler/noirc_frontend/src/ast/structure.rs +++ b/compiler/noirc_frontend/src/ast/structure.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::{token::SecondaryAttribute, Ident, UnresolvedGenerics, UnresolvedType}; +use crate::{token::SecondaryAttribute, GenericIdent, Ident, UnresolvedGenerics, UnresolvedType}; use iter_extended::vecmap; use noirc_errors::Span; @@ -18,7 +18,7 @@ impl NoirStruct { pub fn new( name: Ident, attributes: Vec, - generics: Vec, + generics: Vec, fields: Vec<(Ident, UnresolvedType)>, span: Span, ) -> NoirStruct { diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 775f0a5f2b4..beec5782f2f 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -4,8 +4,8 @@ use iter_extended::vecmap; use noirc_errors::Span; use crate::{ - node_interner::TraitId, BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, - Path, UnresolvedGenerics, UnresolvedType, + node_interner::TraitId, BlockExpression, Expression, FunctionReturnType, GenericIdent, Ident, + NoirFunction, Path, UnresolvedGenerics, UnresolvedType, }; /// AST node for trait definitions: @@ -13,7 +13,7 @@ use crate::{ #[derive(Clone, Debug)] pub struct NoirTrait { pub name: Ident, - pub generics: Vec, + pub generics: Vec, pub where_clause: Vec, pub span: Span, pub items: Vec, @@ -25,7 +25,7 @@ pub struct NoirTrait { pub enum TraitItem { Function { name: Ident, - generics: Vec, + generics: Vec, parameters: Vec<(Ident, UnresolvedType)>, return_type: FunctionReturnType, where_clause: Vec, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 7f36af5b30e..4e493d8d334 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -515,7 +515,7 @@ pub(crate) fn check_methods_signatures( } // We also need to bind the traits generics to the trait's generics on the impl - for (generic, binding) in the_trait.generics.iter().zip(trait_generics) { + for ((generic, _prevent_numeric), binding) in the_trait.generics.iter().zip(trait_generics) { generic.bind(binding); } @@ -623,7 +623,7 @@ pub(crate) fn check_methods_signatures( the_trait.set_methods(trait_methods); the_trait.self_type_typevar.unbind(the_trait.self_type_typevar_id); - for generic in &the_trait.generics { + for (generic, _prevent_numeric) in &the_trait.generics { generic.unbind(generic.id()); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/functions.rs b/compiler/noirc_frontend/src/hir/resolution/functions.rs index e63de9b9173..e66dd515645 100644 --- a/compiler/noirc_frontend/src/hir/resolution/functions.rs +++ b/compiler/noirc_frontend/src/hir/resolution/functions.rs @@ -24,7 +24,7 @@ pub(crate) fn resolve_function_set( mut unresolved_functions: UnresolvedFunctions, self_type: Option, trait_impl_id: Option, - impl_generics: Vec<(Rc, TypeVariable, Span)>, + impl_generics: Vec<(Rc, TypeVariable, Span, bool)>, errors: &mut Vec<(CompilationError, FileId)>, ) -> Vec<(FileId, FuncId)> { let file_id = unresolved_functions.file_id; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7f9e48353a7..ce68bb50de7 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -109,7 +109,8 @@ pub struct Resolver<'a> { /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics /// were declared in. - generics: Vec<(Rc, TypeVariable, Span)>, + /// The bool represents 'prevent_numeric', i.e. "N?" + generics: Vec<(Rc, TypeVariable, Span, bool)>, /// When resolving lambda expressions, we need to keep track of the variables /// that are captured. We do this in order to create the hidden environment @@ -509,7 +510,7 @@ impl<'a> Resolver<'a> { let env = Box::new(self.resolve_type_inner(*env, new_variables)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { Type::Function(args, ret, env) } _ => { @@ -539,8 +540,8 @@ impl<'a> Resolver<'a> { resolved_type } - fn find_generic(&self, target_name: &str) -> Option<&(Rc, TypeVariable, Span)> { - self.generics.iter().find(|(name, _, _)| name.as_ref() == target_name) + fn find_generic(&self, target_name: &str) -> Option<&(Rc, TypeVariable, Span, bool)> { + self.generics.iter().find(|(name, _, _, _)| name.as_ref() == target_name) } fn resolve_named_type( @@ -657,8 +658,8 @@ impl<'a> Resolver<'a> { fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; - if let Some((name, var, _)) = self.find_generic(name) { - return Some(Type::NamedGeneric(var.clone(), name.clone())); + if let Some((name, var, _, prevent_numeric)) = self.find_generic(name) { + return Some(Type::NamedGeneric(var.clone(), name.clone(), *prevent_numeric)); } } @@ -683,12 +684,13 @@ impl<'a> Resolver<'a> { None => { let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - new_variables.push(typevar.clone()); + let prevent_numeric = true; + new_variables.push((typevar.clone(), prevent_numeric)); // 'Named'Generic is a bit of a misnomer here, we want a type variable that // wont be bound over but this one has no name since we do not currently // require users to explicitly be generic over array lengths. - Type::NamedGeneric(typevar, Rc::new("".into())) + Type::NamedGeneric(typevar, Rc::new("".into()), prevent_numeric) } Some(length) => self.convert_expression_type(length), } @@ -775,14 +777,14 @@ impl<'a> Resolver<'a> { /// Return the current generics. /// Needed to keep referring to the same type variables across many /// methods in a single impl. - pub fn get_generics(&self) -> &[(Rc, TypeVariable, Span)] { + pub fn get_generics(&self) -> &[(Rc, TypeVariable, Span, bool)] { &self.generics } /// Set the current generics that are in scope. /// Unlike add_generics, this function will not create any new type variables, /// opting to reuse the existing ones it is directly given. - pub fn set_generics(&mut self, generics: Vec<(Rc, TypeVariable, Span)>) { + pub fn set_generics(&mut self, generics: Vec<(Rc, TypeVariable, Span, bool)>) { self.generics = generics; } @@ -802,22 +804,23 @@ impl<'a> Resolver<'a> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - let span = generic.0.span(); + let span = generic.ident.0.span(); + let prevent_numeric = generic.prevent_numeric; // Check for name collisions of this generic - let name = Rc::new(generic.0.contents.clone()); + let name = Rc::new(generic.ident.0.contents.clone()); - if let Some((_, _, first_span)) = self.find_generic(&name) { + if let Some((_, _, first_span, _)) = self.find_generic(&name) { self.errors.push(ResolverError::DuplicateDefinition { - name: generic.0.contents.clone(), + name: generic.ident.0.contents.clone(), first_span: *first_span, second_span: span, }); } else { - self.generics.push((name, typevar.clone(), span)); + self.generics.push((name, typevar.clone(), span, prevent_numeric)); } - typevar + (typevar, prevent_numeric) }) } @@ -827,23 +830,35 @@ impl<'a> Resolver<'a> { pub fn add_existing_generics(&mut self, names: &UnresolvedGenerics, generics: &Generics) { assert_eq!(names.len(), generics.len()); - for (name, typevar) in names.iter().zip(generics) { - self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); + for (name, (typevar, prevent_numeric)) in names.iter().zip(generics) { + assert!(*prevent_numeric == name.prevent_numeric); + self.add_existing_generic( + &name.ident.0.contents, + name.ident.0.span(), + typevar.clone(), + name.prevent_numeric, + ); } } - pub fn add_existing_generic(&mut self, name: &str, span: Span, typevar: TypeVariable) { + pub fn add_existing_generic( + &mut self, + name: &str, + span: Span, + typevar: TypeVariable, + prevent_numeric: bool, + ) { // Check for name collisions of this generic let rc_name = Rc::new(name.to_owned()); - if let Some((_, _, first_span)) = self.find_generic(&rc_name) { + if let Some((_, _, first_span, _)) = self.find_generic(&rc_name) { self.errors.push(ResolverError::DuplicateDefinition { name: name.to_owned(), first_span: *first_span, second_span: span, }); } else { - self.generics.push((rc_name, typevar, span)); + self.generics.push((rc_name, typevar, span, prevent_numeric)); } } @@ -899,7 +914,9 @@ impl<'a> Resolver<'a> { let attributes = func.attributes().clone(); - let mut generics = vecmap(&self.generics, |(_, typevar, _)| typevar.clone()); + let mut generics = vecmap(&self.generics, |(_, typevar, _, prevent_numeric)| { + (typevar.clone(), *prevent_numeric) + }); let mut parameters = vec![]; let mut parameter_types = vec![]; @@ -1058,12 +1075,14 @@ impl<'a> Resolver<'a> { // 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. eliding array lengths using the slice // syntax [T]. - if let Some((name, _, span)) = - self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) + if let Some((name, _, span, prevent_numeric)) = + 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); + if !*prevent_numeric { + let ident = Ident::new(name.to_string(), *span); + let definition = DefinitionKind::GenericType(type_variable); + self.add_variable_decl_inner(ident, false, false, false, definition); + } } } } @@ -1089,14 +1108,16 @@ impl<'a> Resolver<'a> { | Type::Error | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::NotConstant | Type::TraitAsType(..) | Type::Forall(_, _) => (), Type::Array(length, element_type) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { - found.insert(name.to_string(), type_variable.clone()); + if let Type::NamedGeneric(type_variable, name, prevent_numeric) = length.as_ref() { + if !prevent_numeric { + found.insert(name.to_string(), type_variable.clone()); + } } Self::find_numeric_generics_in_type(element_type, found); } @@ -1116,8 +1137,8 @@ impl<'a> Resolver<'a> { 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) { + if let Type::NamedGeneric(type_variable, name, prevent_numeric) = generic { + if struct_type.borrow().generic_is_numeric(i) && !prevent_numeric { found.insert(name.to_string(), type_variable.clone()); } } else { @@ -1127,8 +1148,8 @@ impl<'a> Resolver<'a> { } Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name) = generic { - if alias.borrow().generic_is_numeric(i) { + if let Type::NamedGeneric(type_variable, name, prevent_numeric) = generic { + if !prevent_numeric && alias.borrow().generic_is_numeric(i) { found.insert(name.to_string(), type_variable.clone()); } } else { @@ -1138,13 +1159,17 @@ impl<'a> Resolver<'a> { } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { - found.insert(name.to_string(), type_variable.clone()); + if let Type::NamedGeneric(type_variable, name, prevent_numeric) = length.as_ref() { + if !prevent_numeric { + found.insert(name.to_string(), type_variable.clone()); + } } } Type::FmtString(length, fields) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { - found.insert(name.to_string(), type_variable.clone()); + if let Type::NamedGeneric(type_variable, name, prevent_numeric) = length.as_ref() { + if !prevent_numeric { + found.insert(name.to_string(), type_variable.clone()); + } } Self::find_numeric_generics_in_type(fields, found); } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 8f966be312b..e264f50f300 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -41,8 +41,11 @@ pub(crate) fn resolve_traits( let mut all_errors = Vec::new(); for (trait_id, unresolved_trait) in traits { - let generics = vecmap(&unresolved_trait.trait_def.generics, |_| { - TypeVariable::unbound(context.def_interner.next_type_variable_id()) + let generics = vecmap(&unresolved_trait.trait_def.generics, |generic_ident| { + ( + TypeVariable::unbound(context.def_interner.next_type_variable_id()), + generic_ident.prevent_numeric, + ) }); // Resolve order @@ -121,11 +124,12 @@ fn resolve_trait_methods( let self_typevar = the_trait.self_type_typevar.clone(); let self_type = Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); let name_span = the_trait.name.span(); + let self_prevent_numeric = false; let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); resolver.add_generics(generics); resolver.add_existing_generics(&unresolved_trait.trait_def.generics, trait_generics); - resolver.add_existing_generic("Self", name_span, self_typevar); + resolver.add_existing_generic("Self", name_span, self_typevar, self_prevent_numeric); resolver.set_self_type(Some(self_type.clone())); let func_id = unresolved_trait.method_ids[&name.0.contents]; @@ -141,7 +145,9 @@ fn resolve_trait_methods( let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); let return_type = resolver.resolve_type(return_type.get_type().into_owned()); - let generics = vecmap(resolver.get_generics(), |(_, type_var, _)| type_var.clone()); + let generics = vecmap(resolver.get_generics(), |(_, type_var, _, prevent_numeric)| { + (type_var.clone(), *prevent_numeric) + }); let default_impl_list: Vec<_> = unresolved_trait .fns_with_default_impl @@ -454,7 +460,9 @@ pub(crate) fn resolve_trait_impls( methods: vecmap(&impl_methods, |(_, func_id)| *func_id), }); - let impl_generics = vecmap(impl_generics, |(_, type_variable, _)| type_variable); + let impl_generics = vecmap(impl_generics, |(_, type_variable, _, prevent_numeric)| { + (type_variable, prevent_numeric) + }); if let Err((prev_span, prev_file)) = interner.add_trait_implementation( self_type.clone(), diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index b78f07c88f2..f7278b53a49 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -325,7 +325,9 @@ impl<'interner> TypeChecker<'interner> { let the_trait = self.interner.get_trait(constraint.trait_id); assert_eq!(the_trait.generics.len(), constraint.trait_generics.len()); - for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { + for ((param, _prevent_numeric), arg) in + the_trait.generics.iter().zip(&constraint.trait_generics) + { bindings.insert(param.id(), (param.clone(), arg.clone())); } } @@ -916,7 +918,7 @@ impl<'interner> TypeChecker<'interner> { }); None } - Type::NamedGeneric(_, _) => { + Type::NamedGeneric(_, _, _) => { let func_meta = self.interner.function_meta( &self.current_function.expect("unexpected method outside a function"), ); diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 358bea86922..b40dbb8834c 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -335,8 +335,10 @@ impl<'interner> TypeChecker<'interner> { if annotated_type.is_unsigned() { self.lint_overflowing_uint(&rhs_expr, &annotated_type); } + annotated_type + } else { + expr_type } - expr_type } /// Check if an assignment is overflowing with respect to `annotated_type` diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 16b9899039f..3b501f4a025 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -147,7 +147,7 @@ impl TraitFunction { } } - pub fn generics(&self) -> &[TypeVariable] { + pub fn generics(&self) -> &[(TypeVariable, bool)] { match &self.typ { Type::Function(..) => &[], Type::Forall(generics, _) => generics, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e105da1ccf0..b5483ca3600 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -76,7 +76,9 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc), + /// + /// The `bool` refers to whether to `prevent_numeric`, i.e. for `N?`. + NamedGeneric(TypeVariable, Rc, bool), /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -145,7 +147,7 @@ impl Type { | Type::Unit | Type::TypeVariable(_, _) | Type::TraitAsType(..) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -218,8 +220,9 @@ pub struct StructType { pub location: Location, } -/// Corresponds to generic lists such as `` in the source program. -pub type Generics = Vec; +/// Corresponds to generic lists such as `` in the source program. +/// The bool is used to to prevent_numeric. +pub type Generics = Vec<(TypeVariable, bool)>; impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { @@ -268,7 +271,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.0.id(), (old.0.clone(), new.clone()))) .collect(); (typ.substitute(&substitutions), i) @@ -284,7 +287,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.0.id(), (old.0.clone(), new.clone()))) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -301,7 +304,7 @@ impl StructType { /// 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; + let target_id = self.generics[index_of_generic].0 .0; self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) } @@ -370,7 +373,7 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.0.id(), (old.0.clone(), new.clone()))) .collect(); self.typ.substitute(&substitutions) @@ -380,7 +383,7 @@ impl TypeAlias { /// 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; + let target_id = self.generics[index_of_generic].0 .0; self.typ.contains_numeric_typevar(target_id) } } @@ -461,6 +464,9 @@ pub enum TypeVariableKind { /// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound /// during monomorphization. Constant(u64), + + /// The type of a slice is an array of size NotConstant. + NotConstant, } /// A TypeVariable is a mutable reference that is either @@ -614,12 +620,18 @@ impl Type { 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") + if let Type::NamedGeneric(type_variable, _, prevent_numeric) = typ { + if *prevent_numeric { + false + } else { + match &*type_variable.borrow() { + TypeBinding::Bound(_) => { + unreachable!( + "Named generics should not be bound until monomorphization" + ) + } + TypeBinding::Unbound(id) => target_id == *id, } - TypeBinding::Unbound(id) => target_id == *id, } } else { false @@ -634,7 +646,7 @@ impl Type { | Type::Error | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::NotConstant | Type::Forall(_, _) | Type::TraitAsType(..) => false, @@ -696,7 +708,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -725,8 +737,12 @@ impl Type { /// Returns 0 if this is not a Type::Forall pub fn generic_count(&self) -> usize { match self { - Type::Forall(generics, _) => generics.len(), - Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _) => { + Type::Forall(generics, _) => { + // println!("TODO: remove: generic_count: {:?}, {:?}", self, generics); + + generics.len() + } + Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), TypeBinding::Unbound(_) => 0, @@ -739,7 +755,13 @@ impl Type { /// Takes a monomorphic type and generalizes it over each of the type variables in the /// given type bindings, ignoring what each type variable is bound to in the TypeBindings. pub(crate) fn generalize_from_substitutions(self, type_bindings: TypeBindings) -> Type { - let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _))| type_var); + let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, binding))| { + let kind = match binding { + Type::TypeVariable(_, kind) => kind, + _ => panic!("Type of TypeVariable is non-variable"), + }; + (type_var, kind == TypeVariableKind::NotConstant) + }); Type::Forall(polymorphic_type_vars, Box::new(self)) } @@ -806,6 +828,13 @@ impl std::fmt::Display for Type { write!(f, "{}", binding.borrow()) } } + Type::TypeVariable(binding, TypeVariableKind::NotConstant) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + write!(f, "_") + } else { + write!(f, "{}", binding.borrow()) + } + } Type::Struct(s, args) => { let args = vecmap(args, |arg| arg.to_string()); if args.is_empty() { @@ -841,14 +870,22 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name) => match &*binding.borrow() { - TypeBinding::Bound(binding) => binding.fmt(f), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), - TypeBinding::Unbound(_) => write!(f, "{name}"), - }, + Type::NamedGeneric(binding, name, prevent_numeric) => { + let prevent_numeric_str = if *prevent_numeric { "?" } else { "" }; + match &*binding.borrow() { + TypeBinding::Bound(binding) => binding.fmt(f), + TypeBinding::Unbound(_) if name.is_empty() => { + write!(f, "_{prevent_numeric_str}") + } + TypeBinding::Unbound(_) => write!(f, "{name}{prevent_numeric_str}"), + } + } Type::Constant(x) => x.fmt(f), Type::Forall(typevars, typ) => { - let typevars = vecmap(typevars, |var| var.id().to_string()); + let typevars = vecmap(typevars, |(var, prevent_numeric)| { + let prevent_numeric_str = if *prevent_numeric { "?" } else { "" }; + format!("{}{}", var.id().to_string(), prevent_numeric_str) + }); write!(f, "forall {}. {}", typevars.join(" "), typ) } Type::Function(args, ret, env) => { @@ -914,19 +951,24 @@ impl Type { }; let this = self.substitute(bindings).follow_bindings(); - match &this { Type::Constant(length) if *length == target_length => { bindings.insert(target_id, (var.clone(), this)); Ok(()) } Type::NotConstant => { + // let other = var.clone().substitute(bindings).follow_bindings(); + // println!("try_bind_to_maybe_constant: {:?} {:?} {:?}", target_id, (var.clone(), 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) => { + if *kind == TypeVariableKind::NotConstant { + return Err(UnificationError); + } let borrow = new_var.borrow(); match &*borrow { TypeBinding::Bound(typ) => { @@ -959,6 +1001,7 @@ impl Type { bindings.insert(*new_target_id, (new_var.clone(), Type::NotConstant)); Ok(()) } + TypeVariableKind::NotConstant => Err(UnificationError), TypeVariableKind::IntegerOrField => Err(UnificationError), TypeVariableKind::Integer => Err(UnificationError), }, @@ -1091,7 +1134,7 @@ impl Type { fn get_inner_type_variable(&self) -> Option> { match self { - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _) => Some(var.1.clone()), + Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => Some(var.1.clone()), _ => None, } } @@ -1127,6 +1170,7 @@ impl Type { use Type::*; use TypeVariableKind as Kind; + // println!("TODO try_unify: {:?} {:?} {:?}", self, other, bindings); match (self, other) { (Error, _) | (_, Error) => Ok(()), @@ -1160,6 +1204,8 @@ impl Type { (TypeVariable(var, Kind::Constant(length)), other) | (other, TypeVariable(var, Kind::Constant(length))) => other .try_unify_to_type_variable(var, bindings, |bindings| { + // let other_follow = other.substitute(bindings).follow_bindings(); + // println!("TODO remove: try_unify constant: {:?} {:?} {:?} {:?}", var, length, other, other_follow); other.try_bind_to_maybe_constant(var, *length, bindings) }), @@ -1200,22 +1246,31 @@ impl Type { } } - (NamedGeneric(binding, _), other) | (other, NamedGeneric(binding, _)) + (NamedGeneric(binding, _, prevent_numeric), other) + | (other, NamedGeneric(binding, _, prevent_numeric)) if !binding.borrow().is_unbound() => { if let TypeBinding::Bound(link) = &*binding.borrow() { + // if *prevent_numeric { + // println!("TODO try_unify NamedGeneric: {:?} {:?} {:?} {:?} {:?}", prevent_numeric, self, other, bindings, link); + // } + link.try_unify(other, bindings) } else { unreachable!("If guard ensures binding is bound") } } - (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { + ( + NamedGeneric(binding_a, name_a, prevent_numeric_a), + NamedGeneric(binding_b, name_b, prevent_numeric_b), + ) => { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); if name_a == name_b { + assert!(prevent_numeric_a == prevent_numeric_b); Ok(()) } else { Err(UnificationError) @@ -1322,6 +1377,11 @@ impl Type { // 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() { + println!( + "TODO remove: convert_array_expression_to_slice {:?} {:?} {:?} {:?}", + expression, this, target, bindings + ); + convert_array_expression_to_slice(expression, this, target, interner); Self::apply_type_bindings(bindings); return true; @@ -1401,7 +1461,7 @@ impl Type { ) -> (Type, TypeBindings) { match self { Type::Forall(typevars, typ) => { - for var in typevars { + for (var, _prevent_numeric) in typevars { bindings .entry(var.id()) .or_insert_with(|| (var.clone(), interner.next_type_variable())); @@ -1422,7 +1482,7 @@ impl Type { Type::Forall(typevars, typ) => { let replacements = typevars .iter() - .map(|var| { + .map(|(var, _prevent_numeric)| { let new = interner.next_type_variable(); (var.id(), (var.clone(), new)) }) @@ -1491,6 +1551,11 @@ impl Type { Type::Array(size, element) => { let size = size.substitute_helper(type_bindings, substitute_bound_typevars); let element = element.substitute_helper(type_bindings, substitute_bound_typevars); + + // let size_follow = size.follow_bindings(); + // let size_follow = size.substitute(type_bindings).follow_bindings(); + // println!("TODO substitute_helper: {:?} {:?} {:?}", self, size, element); + Type::Array(Box::new(size), Box::new(element)) } Type::String(size) => { @@ -1502,9 +1567,29 @@ impl Type { let fields = fields.substitute_helper(type_bindings, substitute_bound_typevars); Type::FmtString(Box::new(size), Box::new(fields)) } - Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { + + // TODO: cleanup + Type::NamedGeneric(binding, _, prevent_numeric) => { + // Type::NamedGeneric(binding, _, prevent_numeric) | Type::TypeVariable(binding, _) => { + // if *prevent_numeric { + // + // for (_type_variable, binding) in type_bindings.values() { + // println!("TODO: {:?}", binding.follow_bindings()); + // } + // + // println!("TODO substitute_binding {:?} {:?} {:?}", binding, prevent_numeric, type_bindings); + // self.clone() + // } else { + // + // // TODO: run in both cases + // substitute_binding(binding) + // } + substitute_binding(binding) } + Type::TypeVariable(binding, _) => substitute_binding(binding), + // TODO: cleanup above + // Do not substitute_helper fields, it can lead to infinite recursion // and we should not match fields when type checking anyway. Type::Struct(fields, args) => { @@ -1528,7 +1613,7 @@ impl Type { Type::Forall(typevars, typ) => { // Trying to substitute_helper a variable de, substitute_bound_typevarsfined within a nested Forall // is usually impossible and indicative of an error in the type checker somewhere. - for var in typevars { + for (var, _prevent_numeric) in typevars { assert!(!type_bindings.contains_key(&var.id())); } let typ = Box::new(typ.substitute_helper(type_bindings, substitute_bound_typevars)); @@ -1571,14 +1656,15 @@ impl Type { generic_args.iter().any(|arg| arg.occurs(target_id)) } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), - Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { + Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding, _) => { match &*binding.borrow() { TypeBinding::Bound(binding) => binding.occurs(target_id), TypeBinding::Unbound(id) => *id == target_id, } } Type::Forall(typevars, typ) => { - !typevars.iter().any(|var| var.id() == target_id) && typ.occurs(target_id) + !typevars.iter().any(|(var, _prevent_numeric)| var.id() == target_id) + && typ.occurs(target_id) } Type::Function(args, ret, env) => { args.iter().any(|arg| arg.occurs(target_id)) @@ -1626,7 +1712,7 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var, _) | NamedGeneric(var, _) => { + TypeVariable(var, _) | NamedGeneric(var, _, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -1656,7 +1742,13 @@ impl Type { } pub fn from_generics(generics: &Generics) -> Vec { - vecmap(generics, |var| Type::TypeVariable(var.clone(), TypeVariableKind::Normal)) + vecmap(generics, |(var, prevent_numeric)| { + if *prevent_numeric { + Type::TypeVariable(var.clone(), TypeVariableKind::NotConstant) + } else { + Type::TypeVariable(var.clone(), TypeVariableKind::Normal) + } + }) } } @@ -1710,6 +1802,7 @@ impl TypeVariableKind { TypeVariableKind::IntegerOrField | TypeVariableKind::Normal => Type::default_int_type(), TypeVariableKind::Integer => Type::default_range_loop_type(), TypeVariableKind::Constant(length) => Type::Constant(*length), + TypeVariableKind::NotConstant => Type::NotConstant, } } } @@ -1741,7 +1834,8 @@ impl From<&Type> for PrintableType { TypeBinding::Bound(typ) => typ.into(), TypeBinding::Unbound(_) => Type::default_range_loop_type().into(), }, - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { + Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) + | Type::TypeVariable(binding, TypeVariableKind::NotConstant) => { match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), TypeBinding::Unbound(_) => Type::default_int_type().into(), @@ -1806,6 +1900,9 @@ impl std::fmt::Debug for Type { Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { write!(f, "{}{:?}", n, binding) } + Type::TypeVariable(binding, TypeVariableKind::NotConstant) => { + write!(f, "{:?}?", binding) + } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); if args.is_empty() { @@ -1841,7 +1938,10 @@ impl std::fmt::Debug for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name) => write!(f, "{}{:?}", name, binding), + Type::NamedGeneric(binding, name, prevent_numeric) => { + let prevent_numeric_str = if *prevent_numeric { "?" } else { "" }; + write!(f, "{}{:?}{}", name, binding, prevent_numeric_str) + } Type::Constant(x) => x.fmt(f), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| format!("{:?}", var)); diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index cf66ece0c30..db381d74d87 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -109,6 +109,7 @@ impl<'a> Lexer<'a> { Some('.') => self.glue(Token::Dot), Some(':') => self.glue(Token::Colon), Some('!') => self.glue(Token::Bang), + Some('?') => self.single_char_token(Token::Question), Some('-') => self.glue(Token::Minus), Some('&') => self.ampersand(), Some('|') => self.single_char_token(Token::Pipe), @@ -584,10 +585,11 @@ mod tests { use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; #[test] fn test_single_double_char() { - let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>"; + let input = "! ? != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>"; let expected = vec![ Token::Bang, + Token::Question, Token::NotEqual, Token::Plus, Token::LeftParen, diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index fe12132e202..8e453bf25c4 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -84,6 +84,8 @@ pub enum Token { Semicolon, /// ! Bang, + /// ? + Question, /// = Assign, #[allow(clippy::upper_case_acronyms)] @@ -199,6 +201,7 @@ impl fmt::Display for Token { Token::Semicolon => write!(f, ";"), Token::Assign => write!(f, "="), Token::Bang => write!(f, "!"), + Token::Question => write!(f, "?"), Token::EOF => write!(f, "end of input"), Token::Invalid(c) => write!(f, "{c}"), Token::Whitespace(ref s) => write!(f, "{s}"), diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index ce880401d77..9092e61fc1d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -861,7 +861,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _) => { + HirType::NamedGeneric(binding, _, _prevent_numeric) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return self.convert_type(binding); } @@ -1669,12 +1669,20 @@ impl<'interner> Monomorphizer<'interner> { let (generics, impl_method_type) = self.interner.function_meta(&impl_method).typ.unwrap_forall(); - let replace_type_variable = |var: &TypeVariable| { - (var.id(), (var.clone(), Type::TypeVariable(var.clone(), TypeVariableKind::Normal))) + let replace_type_variable = |var: &TypeVariable, prevent_numeric: bool| { + let kind = if prevent_numeric { + TypeVariableKind::NotConstant + } else { + TypeVariableKind::Normal + }; + (var.id(), (var.clone(), Type::TypeVariable(var.clone(), kind))) }; // Replace each NamedGeneric with a TypeVariable containing the same internal type variable - let type_bindings = generics.iter().map(replace_type_variable).collect(); + let type_bindings = generics + .iter() + .map(|(var, prevent_numeric)| replace_type_variable(var, *prevent_numeric)) + .collect(); let impl_method_type = impl_method_type.force_substitute(&type_bindings); trait_method_type.try_unify(&impl_method_type, &mut bindings).unwrap_or_else(|_| { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5de43e59254..e66f89ea6de 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -544,12 +544,12 @@ impl NodeInterner { name: unresolved_trait.trait_def.name.clone(), crate_id: unresolved_trait.crate_id, location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), - generics: vecmap(&unresolved_trait.trait_def.generics, |_| { + generics: vecmap(&unresolved_trait.trait_def.generics, |generic_ident| { // Temporary type variable ids before the trait is resolved to its actual ids. // This lets us record how many arguments the type expects so that other types // can refer to it with generic arguments before the generic parameters themselves // are resolved. - TypeVariable::unbound(TypeVariableId(0)) + (TypeVariable::unbound(TypeVariableId(0)), generic_ident.prevent_numeric) }), self_type_typevar_id, self_type_typevar: TypeVariable::unbound(self_type_typevar_id), @@ -574,12 +574,12 @@ impl NodeInterner { // Fields will be filled in later let no_fields = Vec::new(); - let generics = vecmap(&typ.struct_def.generics, |_| { + let generics = vecmap(&typ.struct_def.generics, |generic_ident| { // Temporary type variable ids before the struct is resolved to its actual ids. // This lets us record how many arguments the type expects so that other types // can refer to it with generic arguments before the generic parameters themselves // are resolved. - TypeVariable::unbound(TypeVariableId(0)) + (TypeVariable::unbound(TypeVariableId(0)), generic_ident.prevent_numeric) }); let location = Location::new(typ.struct_def.span, file_id); @@ -597,7 +597,9 @@ impl NodeInterner { typ.type_alias_def.name.clone(), Location::new(typ.type_alias_def.span, typ.file_id), Type::Error, - vecmap(&typ.type_alias_def.generics, |_| TypeVariable::unbound(TypeVariableId(0))), + vecmap(&typ.type_alias_def.generics, |generic_ident| { + (TypeVariable::unbound(TypeVariableId(0)), generic_ident.prevent_numeric) + }), ))); type_id @@ -1306,7 +1308,7 @@ impl NodeInterner { // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() - .map(|typevar| (typevar.id(), (typevar, self.next_type_variable()))) + .map(|(typevar, _prevent_numeric)| (typevar.id(), (typevar, self.next_type_variable()))) .collect(); let instantiated_object_type = object_type.substitute(&substitutions); @@ -1705,7 +1707,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), - Type::NamedGeneric(_, _) => Some(Generic), + Type::NamedGeneric(_, _, _) => Some(Generic), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 1cb81e26a0a..d402e9d96c5 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -38,11 +38,11 @@ use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Attribute, Attributes, Keyword, SecondaryAttribute, Token, TokenKind}; use crate::{ BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, - ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, - IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, Recoverable, Statement, - TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, - UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, + ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, + GenericIdent, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, + NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, + Recoverable, Statement, TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, + UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; use chumsky::prelude::*; @@ -233,13 +233,20 @@ fn is_pub_crate() -> impl NoirParser { .map(|a| a.is_some()) } -/// non_empty_ident_list: ident ',' non_empty_ident_list -/// | ident +/// opt_ident: ident | ident '?' +/// +/// non_empty_ident_list: opt_ident ',' non_empty_ident_list +/// | opt_ident /// /// generics: '<' non_empty_ident_list '>' /// | %empty -fn generics() -> impl NoirParser> { +fn generics() -> impl NoirParser> { ident() + .then(just(Token::Question).or_not()) + .map(|(id, opt_question)| GenericIdent { + ident: id, + prevent_numeric: opt_question.is_some(), + }) .separated_by(just(Token::Comma)) .allow_trailing() .at_least(1) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c661cc92eef..d3b5e4a1cf4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -48,6 +48,7 @@ mod test { } pub(crate) fn get_program( + mock_stdlib: bool, src: &str, ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); @@ -56,7 +57,11 @@ mod test { let mut context = Context::new(fm, Default::default()); context.def_interner.populate_dummy_operator_traits(); let root_file_id = FileId::dummy(); - let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + let root_crate_id = if mock_stdlib { + context.crate_graph.add_stdlib(root_file_id) + } else { + context.crate_graph.add_crate_root(root_file_id) + }; let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); @@ -87,8 +92,12 @@ mod test { (program, context, errors) } + pub(crate) fn get_program_mock_stdlib_errors(src: &str) -> Vec<(CompilationError, FileId)> { + get_program(true, src).2 + } + pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { - get_program(src).2 + get_program(false, src).2 } #[test] @@ -749,7 +758,7 @@ mod test { } fn get_program_captures(src: &str) -> Vec> { - let (program, context, _errors) = get_program(src); + let (program, context, _errors) = get_program(false, src); let interner = context.def_interner; let mut all_captures: Vec> = Vec::new(); for func in program.into_sorted().functions { @@ -1128,7 +1137,7 @@ mod test { } fn check_rewrite(src: &str, expected: &str) { - let (_program, mut context, _errors) = get_program(src); + let (_program, mut context, _errors) = get_program(false, src); let main_func_id = context.def_interner.find_function("main").unwrap(); let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); assert!(format!("{}", program) == expected); @@ -1206,4 +1215,52 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { "#; assert_eq!(get_program_errors(src).len(), 1); } + + #[test] + fn ensure_map_slice_type_checks() { + let src = r#" + impl [T] { + #[builtin(slice_push_back)] + pub fn push_back(self, elem: T) -> Self { } + } + + impl [T; N] { + #[builtin(array_len)] + pub fn len(self) -> Field {} + + pub fn as_slice(self) -> [T] { + let mut slice = []; + for elem in self { + slice = slice.push_back(elem); + } + slice + } + + pub fn map(self, f: fn[Env](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 + } + } + + fn foo(x: [Field]) -> [Field] { + x + } + + fn main() { + let x0: [Field] = [3333333; 444444]; + let _ = foo(x0); + let x: [Field; 444444] = [3333333; 444444]; + let y: [Field] = x; + let z = foo(y); + let _ = z.map(|w| w); + } + "#; + assert_eq!(get_program_mock_stdlib_errors(src).len(), 0); + } } diff --git a/test_programs/compile_success_empty/array_slice_polymorphism/Nargo.toml b/test_programs/compile_success_empty/array_slice_polymorphism/Nargo.toml new file mode 100644 index 00000000000..0b35bbf9c02 --- /dev/null +++ b/test_programs/compile_success_empty/array_slice_polymorphism/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_slice_polymorphism" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/array_slice_polymorphism/src/main.nr b/test_programs/compile_success_empty/array_slice_polymorphism/src/main.nr new file mode 100644 index 00000000000..a930dc37d65 --- /dev/null +++ b/test_programs/compile_success_empty/array_slice_polymorphism/src/main.nr @@ -0,0 +1,10 @@ + +fn array_zeros(_x: [u64; N]) -> [u64; N] { + [0; N] +} + +fn main() { + let xs: [u64] = [11111; 1234567]; + assert(array_zeros(xs) == xs); +} + diff --git a/test_programs/compile_success_empty/map_slice/Nargo.toml b/test_programs/compile_success_empty/map_slice/Nargo.toml new file mode 100644 index 00000000000..6e5ea92fd16 --- /dev/null +++ b/test_programs/compile_success_empty/map_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "map_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/map_slice/src/main.nr b/test_programs/compile_success_empty/map_slice/src/main.nr new file mode 100644 index 00000000000..cbb327714bb --- /dev/null +++ b/test_programs/compile_success_empty/map_slice/src/main.nr @@ -0,0 +1,17 @@ +fn foo(x: [Field]) -> [Field] { + x +} + +fn main() { + let x0: [Field] = [3333333; 444444]; + let _ = foo(x0); + let x: [Field; 444444] = [3333333; 444444]; + let y: [Field] = x; + let z = foo(y); + let _ = z.map(|w| w); +} + +#[test] +fn test_main() { + main(); +} diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 94969d45e81..d5a6ee4e934 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -6,7 +6,7 @@ use crate::visitor::{FmtVisitor, Shape}; use noirc_frontend::hir::resolution::errors::Span; use noirc_frontend::lexer::Lexer; use noirc_frontend::token::Token; -use noirc_frontend::{Expression, Ident, Param, Visibility}; +use noirc_frontend::{Expression, GenericIdent, Ident, Param, Visibility}; pub(crate) fn changed_comment_content(original: &str, new: &str) -> bool { comments(original).ne(comments(new)) @@ -170,6 +170,16 @@ impl HasItem for Ident { } } +impl HasItem for GenericIdent { + fn span(&self) -> Span { + self.ident.span() + } + + fn format(self, visitor: &FmtVisitor, _shape: Shape) -> String { + visitor.slice(self.span()).into() + } +} + pub(crate) fn first_line_width(exprs: &str) -> usize { exprs.lines().next().map_or(0, |line: &str| line.chars().count()) } diff --git a/tooling/nargo_fmt/tests/expected/fn.nr b/tooling/nargo_fmt/tests/expected/fn.nr index 0088dba6a8f..fec6b2eb68e 100644 --- a/tooling/nargo_fmt/tests/expected/fn.nr +++ b/tooling/nargo_fmt/tests/expected/fn.nr @@ -4,6 +4,8 @@ fn main(x: pub u8, y: u8) -> pub Field {} fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + fn main() // hello {} @@ -19,6 +21,8 @@ fn main( fn main() where T: Eq {} +fn main() where T: Eq {} + fn main( tape: [Field; TAPE_LEN], initial_registers: [Field; REGISTER_COUNT], @@ -36,6 +40,14 @@ fn apply_binary_field_op( registers: &mut Registers ) -> bool {} +fn apply_binary_field_op( + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + op: u8, + registers: &mut Registers +) -> bool {} + fn main() -> distinct pub [Field; 2] {} fn ret_normal_lambda1() -> ((fn() -> Field)) {} @@ -52,6 +64,8 @@ fn make_counter() -> fn[(&mut Field,)]() -> Field {} fn get_some(generator: fn[Env]() -> Field) -> [Field; 5] {} +fn get_some(generator: fn[Env]() -> Field) -> [Field; 5] {} + fn main( message: [u8; 10], message_field: Field, diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index 26ff5933802..fd15a1c151d 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -4,6 +4,8 @@ fn main(x: pub u8, y: u8) -> pub Field {} fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + fn main() // hello {} @@ -19,10 +21,14 @@ fn main( fn main() where T: Eq {} +fn main() where T: Eq {} + fn main(tape: [Field; TAPE_LEN], initial_registers: [Field; REGISTER_COUNT], initial_memory: [Field; MEM_COUNT], initial_program_counter: Field, initial_call_stack: [Field; MAX_CALL_STACK], initial_call_stack_pointer: u64) -> pub ExecutionResult {} fn apply_binary_field_op(lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, op: u8, registers: &mut Registers) -> bool {} +fn apply_binary_field_op(lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, op: u8, registers: &mut Registers) -> bool {} + fn main() -> distinct pub [Field;2] {} fn ret_normal_lambda1() -> ((fn() -> Field)) {} @@ -39,6 +45,8 @@ fn make_counter() -> fn[(&mut Field,)]() -> Field {} fn get_some(generator: fn[Env]() -> Field) -> [Field;5] {} +fn get_some(generator: fn[Env]() -> Field) -> [Field;5] {} + fn main( message: [u8; 10], message_field: Field, pub_key_x: Field, pub_key_y: Field, signature: [u8; 64] ) {}