diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 18312dbf6db..a13c7771909 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -1004,8 +1004,47 @@ impl Elaborator<'_> { let (typ, bindings) = self.instantiate(t, bindings, generics, function_generic_count, location); + if let ImplKind::TraitItem(mut method) = ident.impl_kind { + method.constraint.apply_bindings(&bindings); + if method.assumed { + let trait_generics = method.constraint.trait_bound.trait_generics.clone(); + let object_type = method.constraint.typ; + let trait_impl = TraitImplKind::Assumed { object_type, trait_generics }; + self.interner.select_impl_for_expression(expr_id, trait_impl); + } else { + self.push_trait_constraint( + method.constraint, + expr_id, + true, // this constraint should lead to choosing a trait impl method + ); + } + } + // Push any trait constraints required by this definition to the context // to be checked later when the type of this variable is further constrained. + // + // This must be done before the above trait constraint in case the above one further + // restricts types. + // + // For example, in this code: + // + // ```noir + // trait One {} + // + // trait Two { + // fn new() -> Self; + // } + // + // fn foo>() { + // let _: T = Two::new(); + // } + // ``` + // + // when type-checking `Two::new` we'll have a return type `'2` which is constrained by `'2: Two<'1>`. + // Then the definition for `new` has a constraint on it, `O: One`, which translates to `'1: One`. + // + // If we try to find a trait implementation for `'1` before finding one for `'2` we'll never find it: + // once we find one for `'2`, `'1` will become bound and a trait implementation will be found. if let Some(definition) = self.interner.try_definition(ident.id) { if let DefinitionKind::Function(function) = definition.kind { let function = self.interner.function_meta(&function); @@ -1021,25 +1060,6 @@ impl Elaborator<'_> { } } - if let ImplKind::TraitItem(mut method) = ident.impl_kind { - method.constraint.apply_bindings(&bindings); - if method.assumed { - let trait_generics = method.constraint.trait_bound.trait_generics.clone(); - let object_type = method.constraint.typ; - let trait_impl = TraitImplKind::Assumed { object_type, trait_generics }; - self.interner.select_impl_for_expression(expr_id, trait_impl); - } else { - // Currently only one impl can be selected per expr_id, so this - // constraint needs to be pushed after any other constraints so - // that monomorphization can resolve this trait method to the correct impl. - self.push_trait_constraint( - method.constraint, - expr_id, - true, // this constraint should lead to choosing a trait impl method - ); - } - } - if push_required_type_variables { for (type_variable, _kind, typ) in bindings.values() { self.push_required_type_variable( diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 9acdc156ca2..46b9d1bba79 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -2021,3 +2021,25 @@ fn associated_constant_mul_of_other_constants() { "#; assert_no_errors!(src); } + +#[named] +#[test] +fn trait_method_call_when_it_has_bounds_on_generic() { + let src = r#" + trait BigNum {} + + trait BigCurve + where + B: BigNum, + { + fn new() -> Self; + } + + pub fn foo>() { + let _: Curve = BigCurve::new(); + } + + fn main() {} + "#; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/Nargo.toml new file mode 100644 index 00000000000..a3fe9f536b0 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src/main.nr new file mode 100644 index 00000000000..50648383526 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src/main.nr @@ -0,0 +1,16 @@ + + trait BigNum {} + + trait BigCurve + where + B: BigNum, + { + fn new() -> Self; + } + + pub fn foo>() { + let _: Curve = BigCurve::new(); + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src_hash.txt new file mode 100644 index 00000000000..da31016778d --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/src_hash.txt @@ -0,0 +1 @@ +13386000313017476697 \ No newline at end of file diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/execute__tests__expanded.snap new file mode 100644 index 00000000000..9178b023862 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_trait_method_call_when_it_has_bounds_on_generic/execute__tests__expanded.snap @@ -0,0 +1,22 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +trait BigNum {} + +trait BigCurve +where + B: BigNum, +{ + fn new() -> Self; +} + +pub fn foo() +where + B: BigNum, + Curve: BigCurve, +{ + let _: Curve = Curve::new(); +} + +fn main() {}