leo_passes/destructuring/
ast.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::DestructuringVisitor;
18use leo_ast::*;
19use leo_span::Symbol;
20
21use itertools::{Itertools, izip};
22
23impl AstReconstructor for DestructuringVisitor<'_> {
24    type AdditionalInput = ();
25    type AdditionalOutput = Vec<Statement>;
26
27    /// Replaces a tuple access expression with the appropriate expression.
28    fn reconstruct_tuple_access(
29        &mut self,
30        input: TupleAccess,
31        _additional: &(),
32    ) -> (Expression, Self::AdditionalOutput) {
33        let Expression::Path(path) = &input.tuple else {
34            panic!("SSA guarantees that subexpressions are identifiers or literals.");
35        };
36
37        // Look up the expression in the tuple map.
38        match self.tuples.get(&path.identifier().name).and_then(|tuple_names| tuple_names.get(input.index.value())) {
39            Some(id) => (Path::from(*id).into_absolute().into(), Default::default()),
40            None => {
41                if !matches!(self.state.type_table.get(&path.id), Some(Type::Future(_))) {
42                    panic!("Type checking guarantees that all tuple accesses are declared and indices are valid.");
43                }
44
45                let index = Literal::integer(
46                    IntegerType::U32,
47                    input.index.to_string(),
48                    input.span,
49                    self.state.node_builder.next_id(),
50                );
51                self.state.type_table.insert(index.id(), Type::Integer(IntegerType::U32));
52
53                let expr =
54                    ArrayAccess { array: path.clone().into(), index: index.into(), span: input.span, id: input.id }
55                        .into();
56
57                (expr, Default::default())
58            }
59        }
60    }
61
62    /// If this is a ternary expression on tuples of length `n`, we'll need to change it into
63    /// `n` ternary expressions on the members.
64    fn reconstruct_ternary(
65        &mut self,
66        mut input: TernaryExpression,
67        _additional: &(),
68    ) -> (Expression, Self::AdditionalOutput) {
69        let (if_true, mut statements) = self.reconstruct_expression_tuple(std::mem::take(&mut input.if_true));
70        let (if_false, statements2) = self.reconstruct_expression_tuple(std::mem::take(&mut input.if_false));
71        statements.extend(statements2);
72
73        match (if_true, if_false) {
74            (Expression::Tuple(tuple_true), Expression::Tuple(tuple_false)) => {
75                // Aleo's `ternary` opcode doesn't know about tuples, so we have to handle this.
76                let Some(Type::Tuple(tuple_type)) = self.state.type_table.get(&tuple_true.id()) else {
77                    panic!("Should have tuple type");
78                };
79
80                // We'll be reusing `input.condition`, so assign it to a variable.
81                let cond = if let Expression::Path(..) = input.condition {
82                    input.condition
83                } else {
84                    let place = Identifier::new(
85                        self.state.assigner.unique_symbol("cond", "$$"),
86                        self.state.node_builder.next_id(),
87                    );
88
89                    let definition = self.state.assigner.simple_definition(
90                        place,
91                        input.condition,
92                        self.state.node_builder.next_id(),
93                    );
94
95                    statements.push(definition);
96
97                    self.state.type_table.insert(place.id(), Type::Boolean);
98
99                    Expression::Path(Path::from(place).into_absolute())
100                };
101
102                // These will be the `elements` of our resulting tuple.
103                let mut elements = Vec::with_capacity(tuple_true.elements.len());
104
105                // Create an individual `ternary` for each tuple member and assign the
106                // result to a new variable.
107                for (i, (lhs, rhs, ty)) in
108                    izip!(tuple_true.elements, tuple_false.elements, tuple_type.elements()).enumerate()
109                {
110                    let identifier = Identifier::new(
111                        self.state.assigner.unique_symbol(format_args!("ternary_{i}"), "$$"),
112                        self.state.node_builder.next_id(),
113                    );
114
115                    let expression: Expression = TernaryExpression {
116                        condition: cond.clone(),
117                        if_true: lhs,
118                        if_false: rhs,
119                        span: Default::default(),
120                        id: self.state.node_builder.next_id(),
121                    }
122                    .into();
123
124                    self.state.type_table.insert(identifier.id(), ty.clone());
125                    self.state.type_table.insert(expression.id(), ty.clone());
126
127                    let definition = self.state.assigner.simple_definition(
128                        identifier,
129                        expression,
130                        self.state.node_builder.next_id(),
131                    );
132
133                    statements.push(definition);
134                    elements.push(Path::from(identifier).into_absolute().into());
135                }
136
137                let expr: Expression =
138                    TupleExpression { elements, span: Default::default(), id: self.state.node_builder.next_id() }
139                        .into();
140
141                self.state.type_table.insert(expr.id(), Type::Tuple(tuple_type.clone()));
142
143                (expr, statements)
144            }
145            (if_true, if_false) => {
146                // This isn't a tuple. Just rebuild it and otherwise leave it alone.
147                (TernaryExpression { if_true, if_false, ..input }.into(), statements)
148            }
149        }
150    }
151
152    /* Statements */
153    /// Modify assignments to tuples to become assignments to the corresponding variables.
154    ///
155    /// There are two cases we handle:
156    /// 1. An assignment to a tuple x, like `x = rhs;`.
157    ///    This we need to transform into individual assignments
158    ///    `x_i = rhs_i;`
159    ///    of the variables corresponding to members of `x` and `rhs`.
160    /// 2. An assignment to a tuple member, like `x.2[i].member = rhs;`.
161    ///    This we need to change into
162    ///    `x_2[i].member = rhs;`
163    ///    where `x_2` is the variable corresponding to `x.2`.
164    fn reconstruct_assign(&mut self, mut assign: AssignStatement) -> (Statement, Self::AdditionalOutput) {
165        let (value, mut statements) = self.reconstruct_expression(assign.value, &());
166
167        if let Expression::Path(path) = &assign.place {
168            if let Type::Tuple(..) = self.state.type_table.get(&value.id()).expect("Expressions should have types.") {
169                // This is the first case, assigning to a variable of tuple type.
170                let identifiers =
171                    self.tuples.get(&path.identifier().name).expect("Tuple should have been encountered.");
172
173                let Expression::Path(rhs) = value else {
174                    panic!("SSA should have ensured this is an identifier.");
175                };
176
177                let rhs_identifiers =
178                    self.tuples.get(&rhs.identifier().name).expect("Tuple should have been encountered.");
179
180                // Again, make an assignment for each identifier.
181                for (&identifier, &rhs_identifier) in identifiers.iter().zip_eq(rhs_identifiers) {
182                    let stmt = AssignStatement {
183                        place: Path::from(identifier).into_absolute().into(),
184                        value: Path::from(rhs_identifier).into_absolute().into(),
185                        id: self.state.node_builder.next_id(),
186                        span: Default::default(),
187                    }
188                    .into();
189
190                    statements.push(stmt);
191                }
192
193                // We don't need the original assignment, just the ones we've created.
194                return (Statement::dummy(), statements);
195            }
196        }
197
198        // We need to check for case 2, so we loop and see if we find a tuple access.
199
200        assign.value = value;
201        let mut place = &mut assign.place;
202
203        loop {
204            // Loop through the places in the assignment to the top-level expression until an identifier or tuple access is reached.
205            match place {
206                Expression::TupleAccess(access) => {
207                    // We're assigning to a tuple member, case 2 mentioned above.
208                    let Expression::Path(path) = &access.tuple else {
209                        panic!("SSA should have ensured this is an identifier.");
210                    };
211
212                    let tuple_ids =
213                        self.tuples.get(&path.identifier().name).expect("Tuple should have been encountered.");
214
215                    // This is the corresponding variable name of the member we're assigning to.
216                    let identifier = tuple_ids[access.index.value()];
217
218                    *place = Path::from(identifier).into_absolute().into();
219
220                    return (assign.into(), statements);
221                }
222
223                Expression::ArrayAccess(access) => {
224                    // We need to investigate the array, as maybe it's inside a tuple access, like `tupl.0[1u8]`.
225                    place = &mut access.array;
226                }
227
228                Expression::MemberAccess(access) => {
229                    // We need to investigate the struct, as maybe it's inside a tuple access, like `tupl.0.mem`.
230                    place = &mut access.inner;
231                }
232
233                Expression::Path(..) => {
234                    // There was no tuple access, so this is neither case 1 nor 2; there's nothing to do.
235                    return (assign.into(), statements);
236                }
237
238                _ => panic!("Type checking should have prevented this."),
239            }
240        }
241    }
242
243    fn reconstruct_block(&mut self, block: Block) -> (Block, Self::AdditionalOutput) {
244        let mut statements = Vec::with_capacity(block.statements.len());
245
246        // Reconstruct the statements in the block, accumulating any additional statements.
247        for statement in block.statements {
248            let (reconstructed_statement, additional_statements) = self.reconstruct_statement(statement);
249            statements.extend(additional_statements);
250            if !reconstructed_statement.is_empty() {
251                statements.push(reconstructed_statement);
252            }
253        }
254
255        (Block { statements, ..block }, Default::default())
256    }
257
258    fn reconstruct_conditional(&mut self, input: ConditionalStatement) -> (Statement, Self::AdditionalOutput) {
259        let (condition, mut statements) = self.reconstruct_expression(input.condition, &());
260        let (then, statements2) = self.reconstruct_block(input.then);
261        statements.extend(statements2);
262        let otherwise = input.otherwise.map(|oth| {
263            let (expr, statements3) = self.reconstruct_statement(*oth);
264            statements.extend(statements3);
265            Box::new(expr)
266        });
267        (ConditionalStatement { condition, then, otherwise, ..input }.into(), statements)
268    }
269
270    fn reconstruct_definition(&mut self, definition: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
271        use DefinitionPlace::*;
272
273        let make_identifiers = |slf: &mut Self, single: Symbol, count: usize| -> Vec<Identifier> {
274            (0..count)
275                .map(|i| {
276                    Identifier::new(
277                        slf.state.assigner.unique_symbol(format_args!("{single}#tuple{i}"), "$"),
278                        slf.state.node_builder.next_id(),
279                    )
280                })
281                .collect()
282        };
283
284        let (value, mut statements) = self.reconstruct_expression(definition.value, &());
285        let ty = self.state.type_table.get(&value.id()).expect("Expressions should have a type.");
286        match (definition.place, value, ty) {
287            (Single(identifier), Expression::Path(rhs), Type::Tuple(tuple_type)) => {
288                // We need to give the members new names, in case they are assigned to.
289                let identifiers = make_identifiers(self, identifier.name, tuple_type.length());
290
291                let rhs_identifiers = self.tuples.get(&rhs.identifier().name).unwrap();
292
293                for (identifier, rhs_identifier, ty) in izip!(&identifiers, rhs_identifiers, tuple_type.elements()) {
294                    // Make a definition for each.
295                    let stmt = DefinitionStatement {
296                        place: Single(*identifier),
297                        type_: Some(ty.clone()),
298                        value: Expression::Path(Path::from(*rhs_identifier).into_absolute()),
299                        span: Default::default(),
300                        id: self.state.node_builder.next_id(),
301                    }
302                    .into();
303                    statements.push(stmt);
304
305                    // Put each into the type table.
306                    self.state.type_table.insert(identifier.id(), ty.clone());
307                }
308
309                // Put the identifier in `self.tuples`. We don't need to keep our definition.
310                self.tuples.insert(identifier.name, identifiers);
311                (Statement::dummy(), statements)
312            }
313            (Single(identifier), Expression::Tuple(tuple), Type::Tuple(tuple_type)) => {
314                // Name each of the expressions on the right.
315                let identifiers = make_identifiers(self, identifier.name, tuple_type.length());
316
317                for (identifier, expr, ty) in izip!(&identifiers, tuple.elements, tuple_type.elements()) {
318                    // Make a definition for each.
319                    let stmt = DefinitionStatement {
320                        place: Single(*identifier),
321                        type_: Some(ty.clone()),
322                        value: expr,
323                        span: Default::default(),
324                        id: self.state.node_builder.next_id(),
325                    }
326                    .into();
327                    statements.push(stmt);
328
329                    // Put each into the type table.
330                    self.state.type_table.insert(identifier.id(), ty.clone());
331                }
332
333                // Put the identifier in `self.tuples`. We don't need to keep our definition.
334                self.tuples.insert(identifier.name, identifiers);
335                (Statement::dummy(), statements)
336            }
337            (Single(identifier), rhs @ Expression::Call(..), Type::Tuple(tuple_type)) => {
338                let definition_stmt = self.assign_tuple(rhs, identifier.name);
339
340                let Statement::Definition(DefinitionStatement {
341                    place: DefinitionPlace::Multiple(identifiers), ..
342                }) = &definition_stmt
343                else {
344                    panic!("assign_tuple creates `Multiple`.");
345                };
346
347                // Put it into `self.tuples`.
348                self.tuples.insert(identifier.name, identifiers.clone());
349
350                // Put each into the type table.
351                for (identifier, ty) in identifiers.iter().zip(tuple_type.elements()) {
352                    self.state.type_table.insert(identifier.id(), ty.clone());
353                }
354
355                (definition_stmt, statements)
356            }
357            (Multiple(identifiers), Expression::Tuple(tuple), Type::Tuple(..)) => {
358                // Just make a definition for each tuple element.
359                for (identifier, expr) in identifiers.into_iter().zip_eq(tuple.elements) {
360                    let stmt = DefinitionStatement {
361                        place: Single(identifier),
362                        type_: None,
363                        value: expr,
364                        span: Default::default(),
365                        id: self.state.node_builder.next_id(),
366                    }
367                    .into();
368                    statements.push(stmt);
369                }
370
371                // We don't need to keep the original definition.
372                (Statement::dummy(), statements)
373            }
374            (Multiple(identifiers), Expression::Path(rhs), Type::Tuple(..)) => {
375                // Again, make a definition for each tuple element.
376                let rhs_identifiers =
377                    self.tuples.get(&rhs.identifier().name).expect("We should have encountered this tuple by now");
378                for (identifier, rhs_identifier) in identifiers.into_iter().zip_eq(rhs_identifiers.iter()) {
379                    let stmt = DefinitionStatement {
380                        place: Single(identifier),
381                        type_: None,
382                        value: Expression::Path(Path::from(*rhs_identifier).into_absolute()),
383                        span: Default::default(),
384                        id: self.state.node_builder.next_id(),
385                    }
386                    .into();
387                    statements.push(stmt);
388                }
389
390                // We don't need to keep the original definition.
391                (Statement::dummy(), statements)
392            }
393            (m @ Multiple(..), value @ Expression::Call(..), Type::Tuple(..)) => {
394                // Just reconstruct the statement.
395                let stmt =
396                    DefinitionStatement { place: m, type_: None, value, span: definition.span, id: definition.id }
397                        .into();
398                (stmt, statements)
399            }
400            (_, _, Type::Tuple(..)) => {
401                panic!("Expressions of tuple type can only be tuple literals, identifiers, or calls.");
402            }
403            (s @ Single(..), rhs, _) => {
404                // This isn't a tuple. Just build the definition again.
405                let stmt = DefinitionStatement {
406                    place: s,
407                    type_: None,
408                    value: rhs,
409                    span: Default::default(),
410                    id: definition.id,
411                }
412                .into();
413                (stmt, statements)
414            }
415            (Multiple(_), _, _) => panic!("A definition with multiple identifiers must have tuple type"),
416        }
417    }
418
419    fn reconstruct_iteration(&mut self, _: IterationStatement) -> (Statement, Self::AdditionalOutput) {
420        panic!("`IterationStatement`s should not be in the AST at this phase of compilation.");
421    }
422
423    fn reconstruct_return(&mut self, input: ReturnStatement) -> (Statement, Self::AdditionalOutput) {
424        let (expression, statements) = self.reconstruct_expression_tuple(input.expression);
425        (ReturnStatement { expression, ..input }.into(), statements)
426    }
427}