diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9a20d0dd537..54a6af97744 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -55,9 +55,12 @@ struct LambdaContext { struct Monomorphizer<'interner> { /// Functions are keyed by their unique ID and expected type so that we can monomorphize /// a new version of the function for each type. + /// We also key by any turbofish generics that are specified. + /// This is necessary for a case where we may have a trait generic that can be instantiated + /// outside of a function parameter or return value. /// /// Using nested HashMaps here lets us avoid cloning HirTypes when calling .get() - functions: HashMap>, + functions: HashMap), FuncId>>, /// Unlike functions, locals are only keyed by their unique ID because they are never /// duplicated during monomorphization. Doing so would allow them to be used polymorphically @@ -198,10 +201,15 @@ impl<'interner> Monomorphizer<'interner> { id: node_interner::FuncId, expr_id: node_interner::ExprId, typ: &HirType, + turbofish_generics: Vec, trait_method: Option, ) -> Definition { let typ = typ.follow_bindings(); - match self.functions.get(&id).and_then(|inner_map| inner_map.get(&typ)) { + match self + .functions + .get(&id) + .and_then(|inner_map| inner_map.get(&(typ.clone(), turbofish_generics.clone()))) + { Some(id) => Definition::Function(*id), None => { // Function has not been monomorphized yet @@ -222,7 +230,8 @@ impl<'interner> Monomorphizer<'interner> { Definition::Builtin(opcode) } FunctionKind::Normal => { - let id = self.queue_function(id, expr_id, typ, trait_method); + let id = + self.queue_function(id, expr_id, typ, turbofish_generics, trait_method); Definition::Function(id) } FunctionKind::Oracle => { @@ -249,8 +258,14 @@ impl<'interner> Monomorphizer<'interner> { } /// Prerequisite: typ = typ.follow_bindings() - fn define_function(&mut self, id: node_interner::FuncId, typ: HirType, new_id: FuncId) { - self.functions.entry(id).or_default().insert(typ, new_id); + fn define_function( + &mut self, + id: node_interner::FuncId, + typ: HirType, + turbofish_generics: Vec, + new_id: FuncId, + ) { + self.functions.entry(id).or_default().insert((typ, turbofish_generics), new_id); } fn compile_main( @@ -393,7 +408,7 @@ impl<'interner> Monomorphizer<'interner> { use ast::Literal::*; let expr = match self.interner.expression(&expr) { - HirExpression::Ident(ident, _) => self.ident(ident, expr)?, + HirExpression::Ident(ident, generics) => self.ident(ident, expr, generics)?, HirExpression::Literal(HirLiteral::Str(contents)) => Literal(Str(contents)), HirExpression::Literal(HirLiteral::FmtStr(contents, idents)) => { let fields = try_vecmap(idents, |ident| self.expr(ident))?; @@ -825,6 +840,7 @@ impl<'interner> Monomorphizer<'interner> { &mut self, ident: HirIdent, expr_id: node_interner::ExprId, + generics: Option>, ) -> Result { let typ = self.interner.id_type(expr_id); @@ -838,7 +854,13 @@ impl<'interner> Monomorphizer<'interner> { let mutable = definition.mutable; let location = Some(ident.location); let name = definition.name.clone(); - let definition = self.lookup_function(*func_id, expr_id, &typ, None); + let definition = self.lookup_function( + *func_id, + expr_id, + &typ, + generics.unwrap_or_default(), + None, + ); let typ = Self::convert_type(&typ, ident.location)?; let ident = ast::Ident { location, mutable, definition, name, typ: typ.clone() }; let ident_expression = ast::Expression::Ident(ident); @@ -1063,10 +1085,11 @@ impl<'interner> Monomorphizer<'interner> { } }; - let func_id = match self.lookup_function(func_id, expr_id, &function_type, Some(method)) { - Definition::Function(func_id) => func_id, - _ => unreachable!(), - }; + let func_id = + match self.lookup_function(func_id, expr_id, &function_type, vec![], Some(method)) { + Definition::Function(func_id) => func_id, + _ => unreachable!(), + }; let the_trait = self.interner.get_trait(method.trait_id); let location = self.interner.expr_location(&expr_id); @@ -1292,10 +1315,11 @@ impl<'interner> Monomorphizer<'interner> { id: node_interner::FuncId, expr_id: node_interner::ExprId, function_type: HirType, + turbofish_generics: Vec, trait_method: Option, ) -> FuncId { let new_id = self.next_function_id(); - self.define_function(id, function_type.clone(), new_id); + self.define_function(id, function_type.clone(), turbofish_generics, new_id); let bindings = self.interner.get_instantiation_bindings(expr_id); let bindings = self.follow_bindings(bindings); diff --git a/test_programs/execution_success/trait_method_mut_self/src/main.nr b/test_programs/execution_success/trait_method_mut_self/src/main.nr index fa47fd5d881..0e736c2f098 100644 --- a/test_programs/execution_success/trait_method_mut_self/src/main.nr +++ b/test_programs/execution_success/trait_method_mut_self/src/main.nr @@ -12,14 +12,6 @@ fn main(x: Field, y: pub Field) { pass_trait_by_mut_ref(&mut a_mut_ref, y); assert(a_mut_ref.x == y); - - let mut hasher = Poseidon2Hasher::default(); - hasher.write(x); - hasher.write(y); - let expected_hash = hasher.finish(); - // Check that we get the same result when using the hasher in a - // method that purely uses trait methods without a supplied implementation. - assert(hash_simple_array::([x, y]) == expected_hash); } trait SomeTrait { @@ -58,17 +50,3 @@ fn pass_trait_by_mut_ref(a_mut_ref: &mut T, value: Field) where T: SomeTrait // We auto add a mutable reference to the object type if the method call expects a mutable self a_mut_ref.set_value(value); } - -fn hash_simple_array(input: [Field; 2]) -> Field where H: Hasher + Default { - // Check that we can call a trait method instead of a trait implementation - // TODO: Need to remove the need for this type annotation - // TODO: Curently, without the annotation we will get `Expression type is ambiguous` when trying to use the `hasher` - let mut hasher: H = H::default(); - // Regression that the object is converted to a mutable reference type `&mut _`. - // Otherwise will see `Expected type &mut _, found type H`. - // Then we need to make sure to also auto dereference later in the type checking process - // when searching for a matching impl or else we will get `No matching impl found for `&mut H: Hasher` - hasher.write(input[0]); - hasher.write(input[1]); - hasher.finish() -} diff --git a/test_programs/execution_success/turbofish_call_func_diff_types/Nargo.toml b/test_programs/execution_success/turbofish_call_func_diff_types/Nargo.toml new file mode 100644 index 00000000000..8624cda646b --- /dev/null +++ b/test_programs/execution_success/turbofish_call_func_diff_types/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "turbofish_call_func_diff_types" +type = "bin" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/turbofish_call_func_diff_types/Prover.toml b/test_programs/execution_success/turbofish_call_func_diff_types/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/execution_success/turbofish_call_func_diff_types/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr b/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr new file mode 100644 index 00000000000..709a694e77b --- /dev/null +++ b/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr @@ -0,0 +1,36 @@ +use dep::std::hash::Hasher; +use dep::std::hash::poseidon2::Poseidon2Hasher; +use dep::std::hash::poseidon::PoseidonHasher; + +fn main(x: Field, y: pub Field) { + let mut hasher = PoseidonHasher::default(); + hasher.write(x); + hasher.write(y); + let poseidon_expected_hash = hasher.finish(); + // Check that we get the same result when using the hasher in a + // method that purely uses trait methods without a supplied implementation. + assert(hash_simple_array::([x, y]) == poseidon_expected_hash); + + // Now let's do the same logic but with a different `Hasher` supplied to the turbofish operator + // We want to make sure that we have correctly monomorphized a function with a trait generic + // where the generic is not used on any function parameters or the return value. + let mut hasher = Poseidon2Hasher::default(); + hasher.write(x); + hasher.write(y); + let poseidon2_expected_hash = hasher.finish(); + assert(hash_simple_array::([x, y]) == poseidon2_expected_hash); +} + +fn hash_simple_array(input: [Field; 2]) -> Field where H: Hasher + Default { + // Check that we can call a trait method instead of a trait implementation + // TODO(https://github.com/noir-lang/noir/issues/5063): Need to remove the need for this type annotation + // Curently, without the annotation we will get `Expression type is ambiguous` when trying to use the `hasher` + let mut hasher: H = H::default(); + // Regression that the object is converted to a mutable reference type `&mut _`. + // Otherwise will see `Expected type &mut _, found type H`. + // Then we need to make sure to also auto dereference later in the type checking process + // when searching for a matching impl or else we will get `No matching impl found for `&mut H: Hasher` + hasher.write(input[0]); + hasher.write(input[1]); + hasher.finish() +}