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 0d1dd1b4337..c7aee5acce2 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -501,7 +501,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); } @@ -609,7 +609,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/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index cb8fdb2a327..e60c27a47ee 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -684,12 +684,12 @@ 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. - let prevent_numeric = true; Type::NamedGeneric(typevar, Rc::new("".into()), prevent_numeric) } Some(length) => self.convert_expression_type(length), @@ -820,7 +820,7 @@ impl<'a> Resolver<'a> { self.generics.push((name, typevar.clone(), span, prevent_numeric)); } - typevar + (typevar, prevent_numeric) }) } @@ -830,7 +830,8 @@ 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) { + 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(), @@ -913,7 +914,7 @@ 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![]; diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 02160cad6a9..a750601db88 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -41,8 +41,8 @@ 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 @@ -142,7 +142,7 @@ 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 @@ -455,7 +455,7 @@ 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 88da5be8c8b..9e2574301aa 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -325,7 +325,7 @@ 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())); } } 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 479180d4cf3..2654c0f410c 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -222,8 +222,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) { @@ -272,7 +273,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) @@ -288,7 +289,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)| { @@ -305,7 +306,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)) } @@ -374,7 +375,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) @@ -384,7 +385,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) } } @@ -465,6 +466,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 @@ -728,7 +732,11 @@ 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::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(), @@ -742,7 +750,14 @@ 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))| { + // TODO: does this make sense? + 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)) } @@ -809,6 +824,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() { @@ -856,7 +878,10 @@ impl std::fmt::Display for Type { } 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) => { @@ -922,6 +947,8 @@ impl Type { }; let this = self.substitute(bindings).follow_bindings(); + // println!("TODO remove try_bind_to_maybe_constant: {:?} {:?} {:?} {:?} ||| {:?}", self, var, target_length, bindings, this); + match &this { Type::Constant(length) if *length == target_length => { @@ -967,6 +994,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), }, @@ -1131,6 +1159,7 @@ impl Type { use Type::*; use TypeVariableKind as Kind; + // println!("TODO try_unify: {:?} {:?} {:?}", self, other, bindings); match (self, other) { (Error, _) | (_, Error) => Ok(()), @@ -1164,6 +1193,11 @@ 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.follow_bindings(); + let other_follow = other.substitute(bindings).follow_bindings(); + + // println!("TODO remove: try_unify: {:?} {:?} {:?} {:?}", var, length, other, other_follow); + other.try_bind_to_maybe_constant(var, *length, bindings) }), @@ -1204,22 +1238,28 @@ 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) @@ -1326,6 +1366,8 @@ 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, interner); + convert_array_expression_to_slice(expression, this, target, interner); Self::apply_type_bindings(bindings); return true; @@ -1405,7 +1447,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())); @@ -1426,7 +1468,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)) }) @@ -1495,6 +1537,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) => { @@ -1506,9 +1553,34 @@ 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) => { @@ -1532,7 +1604,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)); @@ -1582,7 +1654,7 @@ impl Type { } } 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)) @@ -1660,7 +1732,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) + } + }) } } @@ -1714,6 +1792,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, } } } @@ -1745,7 +1824,7 @@ 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(), @@ -1810,6 +1889,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() { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index c9fef6f6da3..11dc50be6ee 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -785,7 +785,11 @@ 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 *prevent_numeric { + panic!("TODO: convert_type NamedGeneric: {:?} {:?} {:?}", typ, binding, prevent_numeric); + } + if let TypeBinding::Bound(binding) = &*binding.borrow() { return self.convert_type(binding); } @@ -1580,12 +1584,17 @@ 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 be10523d932..6d26590b1ed 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); 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 index df704cf31bb..a930dc37d65 100644 --- a/test_programs/compile_success_empty/array_slice_polymorphism/src/main.nr +++ b/test_programs/compile_success_empty/array_slice_polymorphism/src/main.nr @@ -3,16 +3,8 @@ fn array_zeros(_x: [u64; N]) -> [u64; N] { [0; N] } -fn main(x: Field, y: pub Field) { - let xs: [u64] = [0; 0]; +fn main() { + let xs: [u64] = [11111; 1234567]; assert(array_zeros(xs) == xs); - assert(x != y); } -#[test] -fn test_main() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); -}