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            && let Type::Tuple(..) = self.state.type_table.get(&value.id()).expect("Expressions should have types.")
169        {
170            // This is the first case, assigning to a variable of tuple type.
171            let identifiers = 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 = self.tuples.get(&rhs.identifier().name).expect("Tuple should have been encountered.");
178
179            // Again, make an assignment for each identifier.
180            for (&identifier, &rhs_identifier) in identifiers.iter().zip_eq(rhs_identifiers) {
181                let stmt = AssignStatement {
182                    place: Path::from(identifier).into_absolute().into(),
183                    value: Path::from(rhs_identifier).into_absolute().into(),
184                    id: self.state.node_builder.next_id(),
185                    span: Default::default(),
186                }
187                .into();
188
189                statements.push(stmt);
190            }
191
192            // We don't need the original assignment, just the ones we've created.
193            return (Statement::dummy(), statements);
194        }
195
196        // We need to check for case 2, so we loop and see if we find a tuple access.
197
198        assign.value = value;
199        let mut place = &mut assign.place;
200
201        loop {
202            // Loop through the places in the assignment to the top-level expression until an identifier or tuple access is reached.
203            match place {
204                Expression::TupleAccess(access) => {
205                    // We're assigning to a tuple member, case 2 mentioned above.
206                    let Expression::Path(path) = &access.tuple else {
207                        panic!("SSA should have ensured this is an identifier.");
208                    };
209
210                    let tuple_ids =
211                        self.tuples.get(&path.identifier().name).expect("Tuple should have been encountered.");
212
213                    // This is the corresponding variable name of the member we're assigning to.
214                    let identifier = tuple_ids[access.index.value()];
215
216                    *place = Path::from(identifier).into_absolute().into();
217
218                    return (assign.into(), statements);
219                }
220
221                Expression::ArrayAccess(access) => {
222                    // We need to investigate the array, as maybe it's inside a tuple access, like `tupl.0[1u8]`.
223                    place = &mut access.array;
224                }
225
226                Expression::MemberAccess(access) => {
227                    // We need to investigate the struct, as maybe it's inside a tuple access, like `tupl.0.mem`.
228                    place = &mut access.inner;
229                }
230
231                Expression::Path(..) => {
232                    // There was no tuple access, so this is neither case 1 nor 2; there's nothing to do.
233                    return (assign.into(), statements);
234                }
235
236                _ => panic!("Type checking should have prevented this."),
237            }
238        }
239    }
240
241    fn reconstruct_block(&mut self, block: Block) -> (Block, Self::AdditionalOutput) {
242        let mut statements = Vec::with_capacity(block.statements.len());
243
244        // Reconstruct the statements in the block, accumulating any additional statements.
245        for statement in block.statements {
246            let (reconstructed_statement, additional_statements) = self.reconstruct_statement(statement);
247            statements.extend(additional_statements);
248            if !reconstructed_statement.is_empty() {
249                statements.push(reconstructed_statement);
250            }
251        }
252
253        (Block { statements, ..block }, Default::default())
254    }
255
256    fn reconstruct_conditional(&mut self, input: ConditionalStatement) -> (Statement, Self::AdditionalOutput) {
257        let (condition, mut statements) = self.reconstruct_expression(input.condition, &());
258        let (then, statements2) = self.reconstruct_block(input.then);
259        statements.extend(statements2);
260        let otherwise = input.otherwise.map(|oth| {
261            let (expr, statements3) = self.reconstruct_statement(*oth);
262            statements.extend(statements3);
263            Box::new(expr)
264        });
265        (ConditionalStatement { condition, then, otherwise, ..input }.into(), statements)
266    }
267
268    fn reconstruct_definition(&mut self, definition: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
269        use DefinitionPlace::*;
270
271        let make_identifiers = |slf: &mut Self, single: Symbol, count: usize| -> Vec<Identifier> {
272            (0..count)
273                .map(|i| {
274                    Identifier::new(
275                        slf.state.assigner.unique_symbol(format_args!("{single}#tuple{i}"), "$"),
276                        slf.state.node_builder.next_id(),
277                    )
278                })
279                .collect()
280        };
281
282        let (value, mut statements) = self.reconstruct_expression(definition.value, &());
283        let ty = self.state.type_table.get(&value.id()).expect("Expressions should have a type.");
284        match (definition.place, value, ty) {
285            (Single(identifier), Expression::Path(rhs), Type::Tuple(tuple_type)) => {
286                // We need to give the members new names, in case they are assigned to.
287                let identifiers = make_identifiers(self, identifier.name, tuple_type.length());
288
289                let rhs_identifiers = self.tuples.get(&rhs.identifier().name).unwrap();
290
291                for (identifier, rhs_identifier, ty) in izip!(&identifiers, rhs_identifiers, tuple_type.elements()) {
292                    // Make a definition for each.
293                    let stmt = DefinitionStatement {
294                        place: Single(*identifier),
295                        type_: Some(ty.clone()),
296                        value: Expression::Path(Path::from(*rhs_identifier).into_absolute()),
297                        span: Default::default(),
298                        id: self.state.node_builder.next_id(),
299                    }
300                    .into();
301                    statements.push(stmt);
302
303                    // Put each into the type table.
304                    self.state.type_table.insert(identifier.id(), ty.clone());
305                }
306
307                // Put the identifier in `self.tuples`. We don't need to keep our definition.
308                self.tuples.insert(identifier.name, identifiers);
309                (Statement::dummy(), statements)
310            }
311            (Single(identifier), Expression::Tuple(tuple), Type::Tuple(tuple_type)) => {
312                // Name each of the expressions on the right.
313                let identifiers = make_identifiers(self, identifier.name, tuple_type.length());
314
315                for (identifier, expr, ty) in izip!(&identifiers, tuple.elements, tuple_type.elements()) {
316                    // Make a definition for each.
317                    let stmt = DefinitionStatement {
318                        place: Single(*identifier),
319                        type_: Some(ty.clone()),
320                        value: expr,
321                        span: Default::default(),
322                        id: self.state.node_builder.next_id(),
323                    }
324                    .into();
325                    statements.push(stmt);
326
327                    // Put each into the type table.
328                    self.state.type_table.insert(identifier.id(), ty.clone());
329                }
330
331                // Put the identifier in `self.tuples`. We don't need to keep our definition.
332                self.tuples.insert(identifier.name, identifiers);
333                (Statement::dummy(), statements)
334            }
335            (Single(identifier), rhs @ Expression::Call(..), Type::Tuple(tuple_type)) => {
336                let definition_stmt = self.assign_tuple(rhs, identifier.name);
337
338                let Statement::Definition(DefinitionStatement {
339                    place: DefinitionPlace::Multiple(identifiers), ..
340                }) = &definition_stmt
341                else {
342                    panic!("assign_tuple creates `Multiple`.");
343                };
344
345                // Put it into `self.tuples`.
346                self.tuples.insert(identifier.name, identifiers.clone());
347
348                // Put each into the type table.
349                for (identifier, ty) in identifiers.iter().zip(tuple_type.elements()) {
350                    self.state.type_table.insert(identifier.id(), ty.clone());
351                }
352
353                (definition_stmt, statements)
354            }
355            (Multiple(identifiers), Expression::Tuple(tuple), Type::Tuple(..)) => {
356                // Just make a definition for each tuple element.
357                for (identifier, expr) in identifiers.into_iter().zip_eq(tuple.elements) {
358                    let stmt = DefinitionStatement {
359                        place: Single(identifier),
360                        type_: None,
361                        value: expr,
362                        span: Default::default(),
363                        id: self.state.node_builder.next_id(),
364                    }
365                    .into();
366                    statements.push(stmt);
367                }
368
369                // We don't need to keep the original definition.
370                (Statement::dummy(), statements)
371            }
372            (Multiple(identifiers), Expression::Path(rhs), Type::Tuple(..)) => {
373                // Again, make a definition for each tuple element.
374                let rhs_identifiers =
375                    self.tuples.get(&rhs.identifier().name).expect("We should have encountered this tuple by now");
376                for (identifier, rhs_identifier) in identifiers.into_iter().zip_eq(rhs_identifiers.iter()) {
377                    let stmt = DefinitionStatement {
378                        place: Single(identifier),
379                        type_: None,
380                        value: Expression::Path(Path::from(*rhs_identifier).into_absolute()),
381                        span: Default::default(),
382                        id: self.state.node_builder.next_id(),
383                    }
384                    .into();
385                    statements.push(stmt);
386                }
387
388                // We don't need to keep the original definition.
389                (Statement::dummy(), statements)
390            }
391            (m @ Multiple(..), value @ Expression::Call(..), Type::Tuple(..)) => {
392                // Just reconstruct the statement.
393                let stmt =
394                    DefinitionStatement { place: m, type_: None, value, span: definition.span, id: definition.id }
395                        .into();
396                (stmt, statements)
397            }
398            (_, _, Type::Tuple(..)) => {
399                panic!("Expressions of tuple type can only be tuple literals, identifiers, or calls.");
400            }
401            (s @ Single(..), rhs, _) => {
402                // This isn't a tuple. Just build the definition again.
403                let stmt = DefinitionStatement {
404                    place: s,
405                    type_: None,
406                    value: rhs,
407                    span: Default::default(),
408                    id: definition.id,
409                }
410                .into();
411                (stmt, statements)
412            }
413            (Multiple(_), _, _) => panic!("A definition with multiple identifiers must have tuple type"),
414        }
415    }
416
417    fn reconstruct_iteration(&mut self, _: IterationStatement) -> (Statement, Self::AdditionalOutput) {
418        panic!("`IterationStatement`s should not be in the AST at this phase of compilation.");
419    }
420
421    fn reconstruct_return(&mut self, input: ReturnStatement) -> (Statement, Self::AdditionalOutput) {
422        let (expression, statements) = self.reconstruct_expression_tuple(input.expression);
423        (ReturnStatement { expression, ..input }.into(), statements)
424    }
425}