diff --git a/crates/nargo_cli/tests/test_data/references/src/main.nr b/crates/nargo_cli/tests/test_data/references/src/main.nr index b112875b9ff..f70293cb5a6 100644 --- a/crates/nargo_cli/tests/test_data/references/src/main.nr +++ b/crates/nargo_cli/tests/test_data/references/src/main.nr @@ -32,6 +32,7 @@ fn main(mut x: Field) { assert(*c.bar.array == [3, 4]); regression_1887(); + regression_2054(); } fn add1(x: &mut Field) { @@ -77,3 +78,12 @@ impl Bar { self.x = 32; } } + +// Ensure that mutating a variable does not also mutate its copy +fn regression_2054() { + let mut x = 2; + let z = x; + + x += 1; + assert(z == 2); +} diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index 710450eb1e6..d6169dfd218 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -89,8 +89,13 @@ impl<'a> FunctionContext<'a> { self.codegen_expression(expr).into_leaf().eval(self) } - /// Codegen for identifiers - fn codegen_ident(&mut self, ident: &ast::Ident) -> Values { + /// Codegen a reference to an ident. + /// The only difference between this and codegen_ident is that if the variable is mutable + /// as in `let mut var = ...;` the `Value::Mutable` will be returned directly instead of + /// being automatically loaded from. This is needed when taking the reference of a variable + /// to reassign to it. Note that mutable references `let x = &mut ...;` do not require this + /// since they are not automatically loaded from and must be explicitly dereferenced. + fn codegen_ident_reference(&mut self, ident: &ast::Ident) -> Values { match &ident.definition { ast::Definition::Local(id) => self.lookup(*id), ast::Definition::Function(id) => self.get_or_queue_function(*id), @@ -104,6 +109,11 @@ impl<'a> FunctionContext<'a> { } } + /// Codegen an identifier, automatically loading its value if it is mutable. + fn codegen_ident(&mut self, ident: &ast::Ident) -> Values { + self.codegen_ident_reference(ident).map(|value| value.eval(self).into()) + } + fn codegen_literal(&mut self, literal: &ast::Literal) -> Values { match literal { ast::Literal::Array(array) => { @@ -159,20 +169,21 @@ impl<'a> FunctionContext<'a> { } fn codegen_unary(&mut self, unary: &ast::Unary) -> Values { - let rhs = self.codegen_expression(&unary.rhs); match unary.operator { noirc_frontend::UnaryOp::Not => { + let rhs = self.codegen_expression(&unary.rhs); let rhs = rhs.into_leaf().eval(self); self.builder.insert_not(rhs).into() } noirc_frontend::UnaryOp::Minus => { + let rhs = self.codegen_expression(&unary.rhs); let rhs = rhs.into_leaf().eval(self); let typ = self.builder.type_of_value(rhs); let zero = self.builder.numeric_constant(0u128, typ); self.builder.insert_binary(zero, BinaryOp::Sub, rhs).into() } noirc_frontend::UnaryOp::MutableReference => { - rhs.map(|rhs| { + self.codegen_reference(&unary.rhs).map(|rhs| { match rhs { value::Value::Normal(value) => { let alloc = self.builder.insert_allocate(); @@ -186,11 +197,23 @@ impl<'a> FunctionContext<'a> { }) } noirc_frontend::UnaryOp::Dereference { .. } => { + let rhs = self.codegen_expression(&unary.rhs); self.dereference(&rhs, &unary.result_type) } } } + fn codegen_reference(&mut self, expr: &Expression) -> Values { + match expr { + Expression::Ident(ident) => self.codegen_ident_reference(ident), + Expression::ExtractTupleField(tuple, index) => { + let tuple = self.codegen_reference(tuple); + Self::get_field(tuple, *index) + } + other => self.codegen_expression(other), + } + } + fn codegen_binary(&mut self, binary: &ast::Binary) -> Values { let lhs = self.codegen_non_tuple_expression(&binary.lhs); let rhs = self.codegen_non_tuple_expression(&binary.rhs);