Skip to content

Commit

Permalink
fix: Assigning to tuple fields (#1318)
Browse files Browse the repository at this point in the history
* Fix assigning to tuple fields

* Cargo fmt

* Formatting

* Add regression test
  • Loading branch information
jfecher authored May 8, 2023
1 parent d872890 commit 460568e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 41 deletions.
20 changes: 15 additions & 5 deletions crates/nargo_cli/tests/test_data/tuples/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@ fn main(x: Field, y: Field) {
assert(a == 0);
assert(b == 1);

let (u,v) = if x as u32 <1 {
(x,x+1)
let (u,v) = if x as u32 < 1 {
(x, x + 1)
} else {
(x+1,x)
(x + 1, x)
};
assert(u==x+1);
assert(v==x);
assert(u == x+1);
assert(v == x);

// Test mutating tuples
let mut mutable = ((0, 0), 1, 2, 3);
mutable.0 = pair;
mutable.2 = 7;
assert(mutable.0.0 == 1);
assert(mutable.0.1 == 0);
assert(mutable.1 == 1);
assert(mutable.2 == 7);
assert(mutable.3 == 3);
}
2 changes: 1 addition & 1 deletion crates/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::program::{deserialize_circuit, serialize_circuit};
use acvm::acir::circuit::Circuit;
use noirc_abi::Abi;
use serde::{Deserialize, Serialize};
use crate::program::{serialize_circuit, deserialize_circuit};

/// Describes the types of smart contract functions that are allowed.
/// Unlike the similar enum in noirc_frontend, 'open' and 'unconstrained'
Expand Down
55 changes: 40 additions & 15 deletions crates/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,36 +455,61 @@ impl<'interner> TypeChecker<'interner> {

fn check_member_access(&mut self, access: expr::HirMemberAccess, expr_id: ExprId) -> Type {
let lhs_type = self.check_expression(&access.lhs).follow_bindings();
let span = self.interner.expr_span(&expr_id);

match self.check_field_access(&lhs_type, &access.rhs.0.contents, span) {
Some((element_type, index)) => {
self.interner.set_field_index(expr_id, index);
element_type
}
None => Type::Error,
}
}

/// This will verify that an expression in the form `lhs.rhs_name` has the given field and will push
/// a type error if it does not. If there is no error, the type of the struct/tuple field is returned
/// along with the index of the field in question.
///
/// This function is abstracted from check_member_access so that it can be shared between
/// there and the HirLValue::MemberAccess case of check_lvalue.
pub(super) fn check_field_access(
&mut self,
lhs_type: &Type,
field_name: &str,
span: Span,
) -> Option<(Type, usize)> {
let lhs_type = lhs_type.follow_bindings();

if let Type::Struct(s, args) = &lhs_type {
let s = s.borrow();
if let Some((field, index)) = s.get_field(&access.rhs.0.contents, args) {
self.interner.set_field_index(expr_id, index);
return field;
if let Some((field, index)) = s.get_field(field_name, args) {
return Some((field, index));
}
} else if let Type::Tuple(elements) = &lhs_type {
if let Ok(index) = access.rhs.0.contents.parse::<usize>() {
if index < elements.len() {
self.interner.set_field_index(expr_id, index);
return elements[index].clone();
if let Ok(index) = field_name.parse::<usize>() {
let length = elements.len();
if index < length {
return Some((elements[index].clone(), index));
} else {
self.errors.push(TypeCheckError::Unstructured {
msg: format!("Index {index} is out of bounds for this tuple {lhs_type} of length {length}"),
span,
});
return None;
}
}
}

// If we get here the type has no field named 'access.rhs'.
// Now we specialize the error message based on whether we know the object type in question yet.
if let Type::TypeVariable(..) = &lhs_type {
self.errors.push(TypeCheckError::TypeAnnotationsNeeded {
span: self.interner.expr_span(&access.lhs),
});
self.errors.push(TypeCheckError::TypeAnnotationsNeeded { span });
} else if lhs_type != Type::Error {
self.errors.push(TypeCheckError::Unstructured {
msg: format!("Type {lhs_type} has no member named {}", access.rhs),
span: self.interner.expr_span(&access.lhs),
});
let msg = format!("Type {lhs_type} has no member named {field_name}");
self.errors.push(TypeCheckError::Unstructured { msg, span });
}

Type::Error
None
}

fn comparator_operand_type_rules(
Expand Down
25 changes: 6 additions & 19 deletions crates/noirc_frontend/src/hir/type_check/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,28 +142,15 @@ impl<'interner> TypeChecker<'interner> {
(typ.clone(), HirLValue::Ident(ident, typ))
}
HirLValue::MemberAccess { object, field_name, .. } => {
let (result, object) = self.check_lvalue(*object, assign_span);
let (lhs_type, object) = self.check_lvalue(*object, assign_span);
let object = Box::new(object);

let mut error = |typ| {
self.errors.push(TypeCheckError::Unstructured {
msg: format!("Type {typ} has no member named {field_name}"),
span: field_name.span(),
});
(Type::Error, None)
};

let (typ, field_index) = match result.follow_bindings() {
Type::Struct(def, args) => {
match def.borrow().get_field(&field_name.0.contents, &args) {
Some((field, index)) => (field, Some(index)),
None => error(Type::Struct(def.clone(), args)),
}
}
Type::Error => (Type::Error, None),
other => error(other),
};
let span = field_name.span();
let (typ, field_index) = self
.check_field_access(&lhs_type, &field_name.0.contents, span)
.unwrap_or((Type::Error, 0));

let field_index = Some(field_index);
(typ.clone(), HirLValue::MemberAccess { object, field_name, field_index, typ })
}
HirLValue::Index { array, index, .. } => {
Expand Down
2 changes: 1 addition & 1 deletion crates/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ where
{
let l_ident = ident().map(LValue::Ident);

let l_member_rhs = just(Token::Dot).ignore_then(ident()).map(LValueRhs::MemberAccess);
let l_member_rhs = just(Token::Dot).ignore_then(field_name()).map(LValueRhs::MemberAccess);

let l_index = expr_parser
.delimited_by(just(Token::LeftBracket), just(Token::RightBracket))
Expand Down

0 comments on commit 460568e

Please sign in to comment.