neo_solidity/ir/statements/assignments/
compound.rs1fn lower_compound_assignment(
2 lhs: &Expression,
3 rhs: &Expression,
4 ctx: &mut LoweringContext,
5 instructions: &mut Vec<Instruction>,
6 op: BinaryOperator,
7) -> bool {
8 if let Some(mapping) = resolve_mapping_access(lhs, ctx) {
15 if !ctx.ensure_state_writable(mapping.state_index) {
16 return false;
17 }
18
19 let tmp_id = ctx.next_label();
22 let mut key_locals: Vec<usize> = Vec::new();
23 for (index, key_expr) in mapping.key_expressions.iter().enumerate() {
24 let local = ctx.allocate_local(
25 format!("__compound_key_{tmp_id}_{index}"),
26 mapping.key_types.get(index).cloned(),
27 );
28 if !lower_expression(key_expr, ctx, instructions) {
29 ctx.record_error_with_suggestion(
30 "failed to lower mapping key in compound assignment",
31 "ensure the mapping key expression is a supported type (integer, string, bytes, or address)",
32 );
33 return false;
34 }
35 instructions.push(Instruction::StoreLocal(local));
36 key_locals.push(local);
37 }
38
39 for local in key_locals.iter().rev() {
41 instructions.push(Instruction::LoadLocal(*local));
42 }
43 instructions.push(Instruction::LoadMappingElement {
44 state_index: mapping.state_index,
45 key_types: mapping.key_types.clone(),
46 });
47
48 if !lower_expression(rhs, ctx, instructions) {
50 return false;
51 }
52
53 instructions.push(Instruction::BinaryOp(op));
54
55 let result_local = ctx.allocate_local(format!("__compound_value_{tmp_id}"), None);
57 instructions.push(Instruction::StoreLocal(result_local));
58
59 instructions.push(Instruction::LoadLocal(result_local));
60 for local in key_locals.iter().rev() {
61 instructions.push(Instruction::LoadLocal(*local));
62 }
63 instructions.push(Instruction::StoreMappingElement {
64 state_index: mapping.state_index,
65 key_types: mapping.key_types.clone(),
66 });
67
68 instructions.push(Instruction::LoadLocal(result_local));
69 return true;
70 }
71
72 if let Some(reference) = resolve_storage_reference(lhs, ctx) {
73 if !ctx.ensure_state_writable(reference.state_index) {
74 return false;
75 }
76
77 if let Some(field) = reference.field_path.last() {
78 let field_keys: Vec<[u8; 32]> = reference.field_path.iter().map(|field| field.key).collect();
79 let tmp_id = ctx.next_label();
80 let mut key_locals: Vec<usize> = Vec::new();
81
82 for (index, key_expr) in reference.key_expressions.iter().enumerate() {
83 let local = ctx.allocate_local(
84 format!("__compound_key_{tmp_id}_{index}"),
85 reference.key_types.get(index).cloned(),
86 );
87 if !lower_expression(key_expr, ctx, instructions) {
88 ctx.record_error("failed to lower storage key in compound assignment");
89 return false;
90 }
91 instructions.push(Instruction::StoreLocal(local));
92 key_locals.push(local);
93 }
94
95 let push_keys_for_slot = |instructions: &mut Vec<Instruction>| {
96 for local in key_locals.iter().rev() {
97 instructions.push(Instruction::LoadLocal(*local));
98 }
99 };
100
101 push_keys_for_slot(instructions);
103 instructions.push(Instruction::LoadStructField {
104 state_index: reference.state_index,
105 key_types: reference.key_types.clone(),
106 field_keys: field_keys.clone(),
107 field_type: field.ty.clone(),
108 });
109
110 if !lower_expression(rhs, ctx, instructions) {
112 return false;
113 }
114
115 instructions.push(Instruction::BinaryOp(op));
116
117 let result_local = ctx.allocate_local(format!("__compound_value_{tmp_id}"), None);
119 instructions.push(Instruction::StoreLocal(result_local));
120
121 instructions.push(Instruction::LoadLocal(result_local));
122 push_keys_for_slot(instructions);
123 instructions.push(Instruction::StoreStructField {
124 state_index: reference.state_index,
125 key_types: reference.key_types.clone(),
126 field_keys,
127 field_type: field.ty.clone(),
128 });
129
130 instructions.push(Instruction::LoadLocal(result_local));
131 return true;
132 }
133 }
134
135 if let Expression::ArraySubscript(_, array, Some(index)) = lhs {
136 let tmp_id = ctx.next_label();
139 let array_local = ctx.allocate_local(format!("__compound_arr_{tmp_id}"), None);
140 let index_local = ctx.allocate_local(format!("__compound_idx_{tmp_id}"), None);
141
142 if !lower_expression(array, ctx, instructions) {
143 return false;
144 }
145 instructions.push(Instruction::StoreLocal(array_local));
146
147 if !lower_expression(index, ctx, instructions) {
148 return false;
149 }
150 instructions.push(Instruction::StoreLocal(index_local));
151
152 instructions.push(Instruction::LoadLocal(array_local));
154 instructions.push(Instruction::LoadLocal(index_local));
155 instructions.push(Instruction::ArrayGet);
156
157 if !lower_expression(rhs, ctx, instructions) {
158 return false;
159 }
160
161 instructions.push(Instruction::BinaryOp(op));
162
163 let result_local = ctx.allocate_local(format!("__compound_value_{tmp_id}"), None);
165 instructions.push(Instruction::StoreLocal(result_local));
166
167 instructions.push(Instruction::LoadLocal(array_local));
168 instructions.push(Instruction::LoadLocal(index_local));
169 instructions.push(Instruction::LoadLocal(result_local));
170 instructions.push(Instruction::ArraySet);
171
172 instructions.push(Instruction::LoadLocal(result_local));
173 return true;
174 }
175
176 if let Expression::Variable(identifier) = lhs {
177 let store_instr = if let Some(local) = ctx.resolve_local(&identifier.name) {
178 Instruction::StoreLocal(local)
179 } else if let Some(state_index) = ctx.state_index_map.get(&identifier.name).copied() {
180 if !ctx.ensure_state_writable(state_index) {
181 return false;
182 }
183 Instruction::StoreState(state_index)
184 } else {
185 let local = ctx.ensure_local(&identifier.name);
186 Instruction::StoreLocal(local)
187 };
188
189 let tmp_id = ctx.next_label();
190 let result_local = ctx.allocate_local(format!("__compound_value_{tmp_id}"), None);
191
192 if !lower_expression(lhs, ctx, instructions) {
194 return false;
195 }
196 if !lower_expression(rhs, ctx, instructions) {
197 return false;
198 }
199
200 instructions.push(Instruction::BinaryOp(op));
201 instructions.push(Instruction::StoreLocal(result_local));
202
203 instructions.push(Instruction::LoadLocal(result_local));
204 instructions.push(store_instr);
205
206 instructions.push(Instruction::LoadLocal(result_local));
207 return true;
208 }
209
210 ctx.record_error_with_suggestion(
211 "unsupported compound assignment target",
212 "compound assignment (+=, -=, etc.) is only supported for local variables, state variables, and mapping/array elements",
213 );
214 false
215}