@@ -37,75 +37,88 @@ use leo_span::Symbol;
37
37
use itertools:: { Itertools as _, izip} ;
38
38
39
39
impl StatementReconstructor for DestructuringVisitor < ' _ > {
40
+ /// Modify assignments to tuples to become assignments to the corresponding variables.
41
+ ///
42
+ /// There are two cases we handle:
43
+ /// 1. An assignment to a tuple x, like `x = rhs;`.
44
+ /// This we need to transform into individual assignments
45
+ /// `x_i = rhs_i;`
46
+ /// of the variables corresponding to members of `x` and `rhs`.
47
+ /// 2. An assignment to a tuple member, like `x.2[i].member = rhs;`.
48
+ /// This we need to change into
49
+ /// `x_2[i].member = rhs;`
50
+ /// where `x_2` is the variable corresponding to `x.2`.
40
51
fn reconstruct_assign ( & mut self , mut assign : AssignStatement ) -> ( Statement , Self :: AdditionalOutput ) {
41
52
let ( value, mut statements) = self . reconstruct_expression ( assign. value ) ;
42
53
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 ( ) ) ) ;
54
+ if let Expression :: Identifier ( identifier) = assign. place {
55
+ if let Type :: Tuple ( ..) = self . state . type_table . get ( & value. id ( ) ) . expect ( "Expressions should have types." ) {
56
+ // This is the first case, assigning to a variable of tuple type.
57
+ let identifiers = self . tuples . get ( & identifier. name ) . expect ( "Tuple should have been encountered." ) ;
53
58
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." ) ;
59
+ let Expression :: Identifier ( rhs) = value else {
60
+ panic ! ( "SSA should have ensured this is an identifier." ) ;
61
+ } ;
61
62
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 ( ) ;
63
+ let rhs_identifiers = self . tuples . get ( & rhs. name ) . expect ( "Tuple should have been encountered." ) ;
71
64
72
- statements. push ( stmt) ;
65
+ // Again, make an assignment for each identifier.
66
+ for ( & identifier, & rhs_identifier) in identifiers. iter ( ) . zip_eq ( rhs_identifiers) {
67
+ let stmt = AssignStatement {
68
+ place : identifier. into ( ) ,
69
+ value : rhs_identifier. into ( ) ,
70
+ id : self . state . node_builder . next_id ( ) ,
71
+ span : Default :: default ( ) ,
73
72
}
73
+ . into ( ) ;
74
74
75
- ( Statement :: dummy ( ) , statements)
76
- } else {
77
- assign. value = value;
78
- ( assign. into ( ) , statements)
75
+ statements. push ( stmt) ;
79
76
}
77
+
78
+ // We don't need the original assignment, just the ones we've created.
79
+ return ( Statement :: dummy ( ) , statements) ;
80
80
}
81
+ }
81
82
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
- } ;
83
+ // We need to check for case 2, so we loop and see if we find a tuple access.
88
84
89
- let tuple_ids = self . tuples . get ( & identifier. name ) . expect ( "Tuple should have been encountered." ) ;
85
+ assign. value = value;
86
+ let mut place = & mut assign. place ;
90
87
91
- // This is the correspondig variable name of the member we're assigning to.
92
- let identifier = tuple_ids[ access. index . value ( ) ] ;
88
+ loop {
89
+ match place {
90
+ Expression :: TupleAccess ( access) => {
91
+ // We're assigning to a tuple member, case 2 mentioned above.
92
+ let Expression :: Identifier ( identifier) = & access. tuple else {
93
+ panic ! ( "SSA should have ensured this is an identifier." ) ;
94
+ } ;
93
95
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 ( ) ,
96
+ let tuple_ids = self . tuples . get ( & identifier. name ) . expect ( "Tuple should have been encountered." ) ;
97
+
98
+ // This is the corresponding variable name of the member we're assigning to.
99
+ let identifier = tuple_ids[ access. index . value ( ) ] ;
100
+
101
+ * place = identifier. into ( ) ;
102
+
103
+ return ( assign. into ( ) , statements) ;
100
104
}
101
- . into ( ) ;
102
105
103
- ( assign, statements)
104
- }
106
+ Expression :: ArrayAccess ( access) => {
107
+ // We need to investigate the array, as maybe it's inside a tuple access, like `tupl.0[1u8]`.
108
+ place = & mut access. array ;
109
+ }
110
+
111
+ Expression :: MemberAccess ( access) => {
112
+ // We need to investigate the struct, as maybe it's inside a tuple access, like `tupl.0.mem`.
113
+ place = & mut access. inner ;
114
+ }
105
115
106
- _ => {
107
- assign. value = value;
108
- ( assign. into ( ) , statements)
116
+ Expression :: Identifier ( ..) => {
117
+ // There was no tuple access, so this is neither case 1 nor 2; there's nothing to do.
118
+ return ( assign. into ( ) , statements) ;
119
+ }
120
+
121
+ _ => panic ! ( "Type checking should have prevented this." ) ,
109
122
}
110
123
}
111
124
}
@@ -117,7 +130,9 @@ impl StatementReconstructor for DestructuringVisitor<'_> {
117
130
for statement in block. statements {
118
131
let ( reconstructed_statement, additional_statements) = self . reconstruct_statement ( statement) ;
119
132
statements. extend ( additional_statements) ;
120
- statements. push ( reconstructed_statement) ;
133
+ if !reconstructed_statement. is_empty ( ) {
134
+ statements. push ( reconstructed_statement) ;
135
+ }
121
136
}
122
137
123
138
( Block { statements, ..block } , Default :: default ( ) )
0 commit comments