Skip to content

Commit 76ea04d

Browse files
committed
Write to array and struct members.
This is supported with a new pass, `WriteTransforming`, which converts assignments to struct or array members into assignments to variables, and any time the struct or array is accessed, reconstructs it from the variables. Tests for this functionality are in `array_write.leo`. Also fixed the test `assign_fail.leo`, which seems to have been in a useless state for a long time. Fixes #28389, #28430
1 parent db7b2f8 commit 76ea04d

File tree

24 files changed

+1767
-512
lines changed

24 files changed

+1767
-512
lines changed

compiler/ast/src/expressions/literal.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ impl Literal {
101101
pub fn display_decimal(&self) -> impl '_ + fmt::Display {
102102
DisplayDecimal(self)
103103
}
104+
105+
pub fn as_u32(&self) -> Option<u32> {
106+
if let LiteralVariant::Integer(_, s) = &self.variant {
107+
u32::from_str_by_radix(&s.replace("_", "")).ok()
108+
} else {
109+
panic!("");
110+
}
111+
}
104112
}
105113

106114
impl fmt::Display for DisplayDecimal<'_> {

compiler/compiler/src/compiler.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ impl<N: Network> Compiler<N> {
187187

188188
self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
189189

190+
self.do_pass::<WriteTransforming>(())?;
191+
192+
self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
193+
190194
self.do_pass::<Flattening>(())?;
191195

192196
self.do_pass::<FunctionInlining>(())?;

compiler/passes/src/const_propagation/statement.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ impl StatementReconstructor for ConstPropagationVisitor<'_> {
5454
}
5555

5656
fn reconstruct_assign(&mut self, assign: AssignStatement) -> (Statement, Self::AdditionalOutput) {
57-
(AssignStatement { value: self.reconstruct_expression(assign.value).0, ..assign }.into(), None)
57+
let value = self.reconstruct_expression(assign.value).0;
58+
let place = self.reconstruct_expression(assign.place).0;
59+
(AssignStatement { value, place, ..assign }.into(), None)
5860
}
5961

6062
fn reconstruct_block(&mut self, mut block: Block) -> (Block, Self::AdditionalOutput) {

compiler/passes/src/destructuring/statement.rs

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -40,72 +40,83 @@ impl StatementReconstructor for DestructuringVisitor<'_> {
4040
fn reconstruct_assign(&mut self, mut assign: AssignStatement) -> (Statement, Self::AdditionalOutput) {
4141
let (value, mut statements) = self.reconstruct_expression(assign.value);
4242

43-
match assign.place {
44-
Expression::Identifier(identifier) => {
45-
if let Type::Tuple(tuple_type) =
46-
self.state.type_table.get(&value.id()).expect("Expressions should have types.")
47-
{
48-
// It's a variable of tuple type. Aleo VM doesn't know about tuples, so
49-
// we'll need to handle this.
50-
let new_symbol = self.state.assigner.unique_symbol(identifier, "##");
51-
let new_identifier = Identifier::new(new_symbol, self.state.node_builder.next_id());
52-
self.state.type_table.insert(new_identifier.id(), Type::Tuple(tuple_type.clone()));
53-
54-
let identifiers = self.tuples.get(&identifier.name).expect("Tuple should have been encountered.");
55-
56-
let Expression::Identifier(rhs) = value else {
57-
panic!("SSA should have ensured this is an identifier.");
58-
};
59-
60-
let rhs_identifiers = self.tuples.get(&rhs.name).expect("Tuple should have been encountered.");
43+
if let Expression::Identifier(identifier) = assign.place {
44+
if let Type::Tuple(tuple_type) =
45+
self.state.type_table.get(&value.id()).expect("Expressions should have types.")
46+
{
47+
// It's a variable of tuple type. Aleo VM doesn't know about tuples, so
48+
// we'll need to handle this.
49+
let new_symbol = self.state.assigner.unique_symbol(identifier, "##");
50+
let new_identifier = Identifier::new(new_symbol, self.state.node_builder.next_id());
51+
self.state.type_table.insert(new_identifier.id(), Type::Tuple(tuple_type.clone()));
52+
53+
let identifiers = self.tuples.get(&identifier.name).expect("Tuple should have been encountered.");
54+
55+
let Expression::Identifier(rhs) = value else {
56+
panic!("SSA should have ensured this is an identifier.");
57+
};
6158

62-
// Again, make an assignment for each identifier.
63-
for (identifier, rhs_identifier) in identifiers.iter().zip_eq(rhs_identifiers) {
64-
let stmt = AssignStatement {
65-
place: (*identifier).into(),
66-
value: (*rhs_identifier).into(),
67-
id: self.state.node_builder.next_id(),
68-
span: Default::default(),
69-
}
70-
.into();
59+
let rhs_identifiers = self.tuples.get(&rhs.name).expect("Tuple should have been encountered.");
7160

72-
statements.push(stmt);
61+
// Again, make an assignment for each identifier.
62+
for (identifier, rhs_identifier) in identifiers.iter().zip_eq(rhs_identifiers) {
63+
let stmt = AssignStatement {
64+
place: (*identifier).into(),
65+
value: (*rhs_identifier).into(),
66+
id: self.state.node_builder.next_id(),
67+
span: Default::default(),
7368
}
69+
.into();
7470

75-
(Statement::dummy(), statements)
76-
} else {
77-
assign.value = value;
78-
(assign.into(), statements)
71+
statements.push(stmt);
7972
}
73+
74+
return (Statement::dummy(), statements);
8075
}
76+
}
8177

82-
Expression::TupleAccess(access) => {
83-
// We're assigning to a tuple member. Again, Aleo VM doesn't know about tuples,
84-
// so we'll need to handle this.
85-
let Expression::Identifier(identifier) = &access.tuple else {
86-
panic!("SSA should have ensured this is an identifier.");
87-
};
78+
// We may be assigning to a tuple access followed by struct or array accesses, like
79+
// `tupl.1[2u8].mem = 5u8`
80+
// So we need to loop and see if we find a tuple.
8881

89-
let tuple_ids = self.tuples.get(&identifier.name).expect("Tuple should have been encountered.");
82+
assign.value = value;
83+
let mut place = &mut assign.place;
9084

91-
// This is the correspondig variable name of the member we're assigning to.
92-
let identifier = tuple_ids[access.index.value()];
85+
loop {
86+
match place {
87+
Expression::TupleAccess(access) => {
88+
// We're assigning to a tuple member. Again, Aleo VM doesn't know about tuples,
89+
// so we'll need to handle this.
90+
let Expression::Identifier(identifier) = &access.tuple else {
91+
panic!("SSA should have ensured this is an identifier.");
92+
};
9393

94-
// So just assign to the variable.
95-
let assign = AssignStatement {
96-
place: Expression::Identifier(identifier),
97-
value,
98-
span: Default::default(),
99-
id: self.state.node_builder.next_id(),
94+
let tuple_ids = self.tuples.get(&identifier.name).expect("Tuple should have been encountered.");
95+
96+
// This is the corresponding variable name of the member we're assigning to.
97+
let identifier = tuple_ids[access.index.value()];
98+
99+
*place = identifier.into();
100+
101+
return (assign.into(), statements);
100102
}
101-
.into();
102103

103-
(assign, statements)
104-
}
104+
Expression::ArrayAccess(access) => {
105+
// We need to investigate the array, as maybe it's inside a tuple access, like `tupl.0[1u8]`.
106+
place = &mut access.array;
107+
}
108+
109+
Expression::MemberAccess(access) => {
110+
// We need to investigate the struct, as maybe it's inside a tuple access, like `tupl.0.mem`.
111+
place = &mut access.inner;
112+
}
113+
114+
Expression::Identifier(..) => {
115+
// There was no tuple access, so there's nothing to do.
116+
return (assign.into(), statements);
117+
}
105118

106-
_ => {
107-
assign.value = value;
108-
(assign.into(), statements)
119+
_ => panic!("Type checking should have prevented this."),
109120
}
110121
}
111122
}

compiler/passes/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ pub use symbol_table_creation::*;
5555

5656
mod type_checking;
5757
pub use type_checking::*;
58+
59+
mod write_transforming;
60+
pub use write_transforming::*;

0 commit comments

Comments
 (0)