From 73c2e9416c2c77cc384b9cfe76b594cd1764c586 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 17 Jul 2023 17:55:03 -0500 Subject: [PATCH] fix: method resolution when calling an `&mut` method with an `&mut` object type (#1947) Fix method resolution bug Co-authored-by: TomAFrench --- .../references/src/main.nr | 5 +++++ .../noirc_frontend/src/hir/type_check/expr.rs | 18 +++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/references/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/references/src/main.nr index 60b3c372fbd..d2c0b7f1244 100644 --- a/crates/nargo_cli/tests/test_data_ssa_refactor/references/src/main.nr +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/references/src/main.nr @@ -6,6 +6,11 @@ fn main(mut x: Field) { s.add2(); assert(s.y == 5); + // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T + let s_ref = &mut s; + s_ref.add2(); + assert(s.y == 7); + // Test that normal mutable variables are still copied let mut a = 0; mutate_copy(a); diff --git a/crates/noirc_frontend/src/hir/type_check/expr.rs b/crates/noirc_frontend/src/hir/type_check/expr.rs index 5b89f72841b..2ea9a33d191 100644 --- a/crates/noirc_frontend/src/hir/type_check/expr.rs +++ b/crates/noirc_frontend/src/hir/type_check/expr.rs @@ -116,7 +116,7 @@ impl<'interner> TypeChecker<'interner> { HirExpression::MethodCall(mut method_call) => { let object_type = self.check_expression(&method_call.object).follow_bindings(); let method_name = method_call.method.0.contents.as_str(); - match self.lookup_method(object_type.clone(), method_name, expr_id) { + match self.lookup_method(&object_type, method_name, expr_id) { Some(method_id) => { let mut args = vec![(object_type, self.interner.expr_span(&method_call.object))]; @@ -287,11 +287,12 @@ impl<'interner> TypeChecker<'interner> { if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) { let actual_type = argument_types[0].0.follow_bindings(); - if let Err(error) = verify_mutable_reference(self.interner, method_call.object) { - self.errors.push(TypeCheckError::ResolverError(error)); - } - if !matches!(actual_type, Type::MutableReference(_)) { + if let Err(error) = verify_mutable_reference(self.interner, method_call.object) + { + self.errors.push(TypeCheckError::ResolverError(error)); + } + let new_type = Type::MutableReference(Box::new(actual_type)); argument_types[0].0 = new_type.clone(); @@ -719,11 +720,11 @@ impl<'interner> TypeChecker<'interner> { fn lookup_method( &mut self, - object_type: Type, + object_type: &Type, method_name: &str, expr_id: &ExprId, ) -> Option { - match &object_type { + match object_type { Type::Struct(typ, _args) => { match self.interner.lookup_method(typ.borrow().id, method_name) { Some(method_id) => Some(method_id), @@ -738,6 +739,9 @@ impl<'interner> TypeChecker<'interner> { } } } + // Mutable references to another type should resolve to methods of their element type. + // This may be a struct or a primitive type. + Type::MutableReference(element) => self.lookup_method(element, method_name, expr_id), // If we fail to resolve the object to a struct type, we have no way of type // checking its arguments as we can't even resolve the name of the function Type::Error => None,