diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 811920188df..09887170ee2 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1983,13 +1983,16 @@ impl Type { if lhs_result.is_ok() && rhs_result.is_ok() { *bindings = new_bindings; - Ok(()) + return Ok(()); } else { - lhs.try_unify_by_moving_constant_terms(&rhs, bindings) + let result = lhs.try_unify_by_moving_constant_terms(&rhs, bindings); + if result.is_ok() { + return Ok(()); + } } - } else { - Err(UnificationError) } + + lhs.try_unify_by_isolating_an_unbound_type_variable(&rhs, bindings) } (Constant(value, kind), other) | (other, Constant(value, kind)) => { diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 04142bf89df..fa71b498ada 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -374,6 +374,99 @@ impl Type { Err(UnificationError) } + + /// Try to unify the following equations: + /// - `A + rhs = other` -> `A = other - rhs` + /// - `A - rhs = other` -> `A = other + rhs` + /// - `lhs + B = other` -> `B = other - lhs` + /// - `lhs - B = other` -> `B = lhs - other` + /// - `other = A + rhs` -> `A = other - rhs` + /// - `other = A - rhs` -> `A = other + rhs` + /// - `other = lhs + B` -> `B = other - lhs` + /// - `other = lhs - B` -> `B = lhs - other` + pub(super) fn try_unify_by_isolating_an_unbound_type_variable( + &self, + other: &Type, + bindings: &mut TypeBindings, + ) -> Result<(), UnificationError> { + self.try_unify_by_isolating_an_unbound_type_variable_in_self(other, bindings).or_else( + |_| other.try_unify_by_isolating_an_unbound_type_variable_in_self(self, bindings), + ) + } + + /// Try to unify the following equations: + /// - `A + rhs = other` -> `A = other - rhs` + /// - `A - rhs = other` -> `A = other + rhs` + /// - `lhs + B = other` -> `B = other - lhs` + /// - `lhs - B = other` -> `B = lhs - other` + fn try_unify_by_isolating_an_unbound_type_variable_in_self( + &self, + other: &Type, + bindings: &mut TypeBindings, + ) -> Result<(), UnificationError> { + if let Type::InfixExpr(lhs_lhs, lhs_op, lhs_rhs, _) = self { + let lhs_op_inverse = lhs_op.inverse(); + + // Check if it's `A + rhs = other` or `A - rhs = other` + if let (Some(op_a_inverse), Type::TypeVariable(lhs_lhs_var)) = + (lhs_op_inverse, lhs_lhs.as_ref()) + { + if lhs_lhs_var.1.borrow().is_unbound() { + // We can say that `A = other - rhs` or `A = other + rhs` respectively + let new_rhs = + Type::infix_expr(Box::new(other.clone()), op_a_inverse, lhs_rhs.clone()); + + let mut tmp_bindings = bindings.clone(); + if lhs_lhs.try_unify(&new_rhs, &mut tmp_bindings).is_ok() { + *bindings = tmp_bindings; + return Ok(()); + } + } + } + + // Check if it's `lhs + B = other` + if let (BinaryTypeOperator::Addition, Type::TypeVariable(lhs_rhs_var)) = + (lhs_op, lhs_rhs.as_ref()) + { + if lhs_rhs_var.1.borrow().is_unbound() { + // We can say that `B = other - lhs` + let new_rhs = Type::infix_expr( + Box::new(other.clone()), + BinaryTypeOperator::Subtraction, + lhs_lhs.clone(), + ); + + let mut tmp_bindings = bindings.clone(); + if lhs_rhs.try_unify(&new_rhs, &mut tmp_bindings).is_ok() { + *bindings = tmp_bindings; + return Ok(()); + } + } + } + + // Check if it's `lhs - B = other` + if let (BinaryTypeOperator::Subtraction, Type::TypeVariable(lhs_rhs_var)) = + (lhs_op, lhs_rhs.as_ref()) + { + if lhs_rhs_var.1.borrow().is_unbound() { + // We can say that `B = lhs - other` + let new_rhs = Type::infix_expr( + lhs_lhs.clone(), + BinaryTypeOperator::Subtraction, + Box::new(other.clone()), + ); + + let mut tmp_bindings = bindings.clone(); + if lhs_rhs.try_unify(&new_rhs, &mut tmp_bindings).is_ok() { + *bindings = tmp_bindings; + return Ok(()); + } + } + } + } + + Err(UnificationError) + } } #[cfg(test)] diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 2d9ae8b7b2a..5296f0df56e 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1633,7 +1633,8 @@ fn accesses_associated_type_inside_trait_using_self() { #[test] fn serialize_test_with_a_previous_unrelated_definition() { let src = r#" - // If you remove this Trait definition, the code compiles fine. That's the bug. + // There used to be a bug where this unrelated definition would cause compilation to fail + // with a "No impl found" error. pub trait Trait {} trait Serialize { @@ -1651,8 +1652,6 @@ fn serialize_test_with_a_previous_unrelated_definition() { fn serialize(self: Self) { self.0.serialize(); - ^^^^^^^^^^^^^^^^ No matching impl found for `(Field, Field): Serialize` - ~~~~~~~~~~~~~~~~ No impl for `(Field, Field): Serialize` } } @@ -1667,8 +1666,5 @@ fn serialize_test_with_a_previous_unrelated_definition() { x.serialize(); } "#; - - // In reality there should be no error here, so any error squiggles should be removed. - // See https://github.com/noir-lang/noir/issues/8780 check_monomorphization_error!(&src); } diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt deleted file mode 100644 index 78fa59df456..00000000000 --- a/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt +++ /dev/null @@ -1 +0,0 @@ -6336765763415174448 \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/Nargo.toml similarity index 100% rename from test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/Nargo.toml rename to test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/Nargo.toml diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr similarity index 80% rename from test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr rename to test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr index 2e876ad1a86..961b1b13f19 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src/main.nr @@ -1,5 +1,6 @@ - // If you remove this Trait definition, the code compiles fine. That's the bug. + // There used to be a bug where this unrelated definition would cause compilation to fail + // with a "No impl found" error. pub trait Trait {} trait Serialize { diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt new file mode 100644 index 00000000000..bff186806d2 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_serialize_test_with_a_previous_unrelated_definition/src_hash.txt @@ -0,0 +1 @@ +2680920423078649375 \ No newline at end of file