leo_passes/destructuring/
visitor.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 crate::CompilerState;
18
19use leo_ast::{
20    AstReconstructor,
21    BinaryExpression,
22    BinaryOperation,
23    DefinitionPlace,
24    DefinitionStatement,
25    Expression,
26    Identifier,
27    Node as _,
28    Path,
29    Statement,
30    TupleExpression,
31    Type,
32};
33use leo_span::Symbol;
34
35use indexmap::IndexMap;
36
37pub struct DestructuringVisitor<'a> {
38    pub state: &'a mut CompilerState,
39    /// A mapping between variables and tuple elements.
40    pub tuples: IndexMap<Symbol, Vec<Identifier>>,
41    /// Whether or not we are currently traversing an async function block.
42    pub is_async: bool,
43}
44
45impl DestructuringVisitor<'_> {
46    /// Similar to `reconstruct_expression`, except that if `expression` is of tuple type, returns it as a tuple
47    /// literal `(mem1, mem2, mem3, ...)`.
48    pub fn reconstruct_expression_tuple(&mut self, expression: Expression) -> (Expression, Vec<Statement>) {
49        let Type::Tuple(tuple_type) =
50            self.state.type_table.get(&expression.id()).expect("Expressions should have types.")
51        else {
52            // It's not a tuple, so there's no more to do.
53            return self.reconstruct_expression(expression, &());
54        };
55
56        let (new_expression, mut statements) = self.reconstruct_expression(expression, &());
57
58        match new_expression {
59            Expression::Path(path) => {
60                // It's a variable name, so just get the member identifiers we've already made.
61                let identifiers = self.tuples.get(&path.identifier().name).expect("Tuples should have been found");
62                let elements: Vec<Expression> =
63                    identifiers.iter().map(|identifier| Path::from(*identifier).into_absolute().into()).collect();
64
65                let tuple: Expression =
66                    TupleExpression { elements, span: Default::default(), id: self.state.node_builder.next_id() }
67                        .into();
68
69                self.state.type_table.insert(tuple.id(), Type::Tuple(tuple_type.clone()));
70
71                (tuple, statements)
72            }
73
74            tuple @ Expression::Tuple(..) => {
75                // It's already a tuple literal.
76                (tuple, statements)
77            }
78
79            expr @ Expression::Call(..) => {
80                // It's a call, so we'll need to make a new definition for the variables.
81                let definition_stmt = self.assign_tuple(expr, Symbol::intern("destructure"));
82                let Statement::Definition(DefinitionStatement {
83                    place: DefinitionPlace::Multiple(identifiers), ..
84                }) = &definition_stmt
85                else {
86                    panic!("`assign_tuple` always creates a definition with `Multiple`");
87                };
88
89                let elements =
90                    identifiers.iter().map(|identifier| Path::from(*identifier).into_absolute().into()).collect();
91
92                let expr = Expression::Tuple(TupleExpression {
93                    elements,
94                    span: Default::default(),
95                    id: self.state.node_builder.next_id(),
96                });
97
98                self.state.type_table.insert(expr.id(), Type::Tuple(tuple_type.clone()));
99
100                statements.push(definition_stmt);
101
102                (expr, statements)
103            }
104
105            _ => panic!("Tuples may only be identifiers, tuple literals, or calls."),
106        }
107    }
108
109    /// Folds an iterator of expressions into a left-associated chain using `op`.
110    ///
111    /// Given expressions `[e1, e2, e3]`, this produces `((e1 op e2) op e3)`.
112    /// Each intermediate node is assigned a fresh ID and recorded as `Boolean`
113    /// in the type table.
114    ///
115    /// Panics if the iterator is empty.
116    pub fn fold_with_op<I>(&mut self, op: BinaryOperation, pieces: I) -> Expression
117    where
118        I: Iterator<Item = Expression>,
119    {
120        pieces
121            .reduce(|left, right| {
122                let expr: Expression = BinaryExpression {
123                    op,
124                    left,
125                    right,
126                    span: Default::default(),
127                    id: self.state.node_builder.next_id(),
128                }
129                .into();
130
131                self.state.type_table.insert(expr.id(), Type::Boolean);
132                expr
133            })
134            .expect("fold_with_op called with empty iterator")
135    }
136
137    // Given the `expression` of tuple type, make a definition assigning variable to its members.
138    //
139    // That is, `let (mem1, mem2, mem3...) = expression;`
140    pub fn assign_tuple(&mut self, expression: Expression, name: Symbol) -> Statement {
141        let Type::Tuple(tuple_type) =
142            self.state.type_table.get(&expression.id()).expect("Expressions should have types.")
143        else {
144            panic!("assign_tuple should only be called for tuple types.");
145        };
146
147        let new_identifiers: Vec<Identifier> = (0..tuple_type.length())
148            .map(|i| {
149                let new_symbol = self.state.assigner.unique_symbol(name, format_args!("#{i}#"));
150                Identifier::new(new_symbol, self.state.node_builder.next_id())
151            })
152            .collect();
153
154        Statement::Definition(DefinitionStatement {
155            place: DefinitionPlace::Multiple(new_identifiers),
156            type_: Some(Type::Tuple(tuple_type.clone())),
157            value: expression,
158            span: Default::default(),
159            id: self.state.node_builder.next_id(),
160        })
161    }
162}