Skip to content

Commit

Permalink
fix: Implement auto-dereferencing when calling methods (#2581)
Browse files Browse the repository at this point in the history
Co-authored-by: Tom French <[email protected]>
  • Loading branch information
jfecher and TomAFrench authored Sep 7, 2023
1 parent 70e35b9 commit 3c731b1
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 2 deletions.
13 changes: 13 additions & 0 deletions crates/nargo_cli/tests/execution_success/references/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ fn main(mut x: Field) {
regression_2218_if_inner_else(20, x);
regression_2218_else(x, 3);
regression_2218_loop(x, 10);

regression_2560(s_ref);
}

fn add1(x: &mut Field) {
Expand All @@ -64,6 +66,10 @@ impl S {
fn add2(&mut self) {
self.y += 2;
}

fn get_y(self) -> Field {
self.y
}
}

fn mutate_copy(mut a: Field) {
Expand Down Expand Up @@ -230,3 +236,10 @@ fn regression_2218_loop(x: Field, y: Field) {
}
assert(*q1 == 2);
}

// This is more a feature test than a proper regression.
// Before, we never automatically dereferenced objects in method calls to their value types.
// Now, we insert as many `*` as necessary to get to `S`.
fn regression_2560(s_ref: &mut S) {
assert(s_ref.get_y() == 7);
}
31 changes: 29 additions & 2 deletions crates/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ impl<'interner> TypeChecker<'interner> {
};

if let Some(expected_object_type) = expected_object_type {
if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) {
let actual_type = argument_types[0].0.follow_bindings();
let actual_type = argument_types[0].0.follow_bindings();

if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) {
if !matches!(actual_type, Type::MutableReference(_)) {
if let Err(error) = verify_mutable_reference(self.interner, method_call.object)
{
Expand Down Expand Up @@ -350,10 +350,37 @@ impl<'interner> TypeChecker<'interner> {
);
}
}
// Otherwise if the object type is a mutable reference and the method is not, insert as
// many dereferences as needed.
} else if matches!(actual_type, Type::MutableReference(_)) {
let (object, new_type) =
self.insert_auto_dereferences(method_call.object, actual_type);
method_call.object = object;
argument_types[0].0 = new_type;
}
}
}

/// Insert as many dereference operations as necessary to automatically dereference a method
/// call object to its base value type T.
fn insert_auto_dereferences(&mut self, object: ExprId, typ: Type) -> (ExprId, Type) {
if let Type::MutableReference(element) = typ {
let location = self.interner.id_location(object);

let object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression {
operator: UnaryOp::Dereference { implicitly_added: true },
rhs: object,
}));
self.interner.push_expr_type(&object, element.as_ref().clone());
self.interner.push_expr_location(object, location.span, location.file);

// Recursively dereference to allow for converting &mut &mut T to T
self.insert_auto_dereferences(object, *element)
} else {
(object, typ)
}
}

/// Given a method object: `(*foo).bar` of a method call `(*foo).bar.baz()`, remove the
/// implicitly added dereference operator if one is found.
///
Expand Down

0 comments on commit 3c731b1

Please sign in to comment.