leo_passes/dead_code_elimination/
statement.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::DeadCodeEliminatingVisitor;
18
19use leo_ast::{
20    AssignStatement,
21    Block,
22    DefinitionPlace,
23    DefinitionStatement,
24    ExpressionReconstructor,
25    ExpressionStatement,
26    IterationStatement,
27    Statement,
28    StatementReconstructor,
29};
30
31impl StatementReconstructor for DeadCodeEliminatingVisitor<'_> {
32    /// Reconstruct an assignment statement by eliminating any dead code.
33    fn reconstruct_assign(&mut self, _input: AssignStatement) -> (Statement, Self::AdditionalOutput) {
34        panic!("`AssignStatement`s should not exist in the AST at this phase of compilation.")
35    }
36
37    /// Reconstructs the statements inside a basic block, eliminating any dead code.
38    fn reconstruct_block(&mut self, block: Block) -> (Block, Self::AdditionalOutput) {
39        // Don't count empty blocks as statements, as that would be a bit misleading to the user as
40        // to how much the code is being transformed.
41        self.statements_before += block.statements.iter().filter(|stmt| !stmt.is_empty()).count() as u32;
42
43        // Reconstruct each of the statements in reverse.
44        let mut statements: Vec<Statement> =
45            block.statements.into_iter().rev().map(|statement| self.reconstruct_statement(statement).0).collect();
46
47        statements.retain(|stmt| !stmt.is_empty());
48
49        // Reverse the direction of `statements`.
50        statements.reverse();
51
52        self.statements_after += statements.len() as u32;
53
54        (Block { statements, span: block.span, id: block.id }, Default::default())
55    }
56
57    /// Static single assignment replaces definition statements with assignment statements.
58    fn reconstruct_definition(&mut self, mut input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
59        // Check the lhs of the definition to see any of variables are used.
60        let lhs_is_used = match &input.place {
61            DefinitionPlace::Single(identifier) => self.used_variables.contains(&identifier.name),
62            DefinitionPlace::Multiple(identifiers) => {
63                identifiers.iter().any(|identifier| self.used_variables.contains(&identifier.name))
64            }
65        };
66
67        if !lhs_is_used && self.side_effect_free(&input.value) {
68            // We can eliminate this statement.
69            (Statement::dummy(), Default::default())
70        } else {
71            // We still need it.
72            input.value = self.reconstruct_expression(input.value).0;
73            (input.into(), Default::default())
74        }
75    }
76
77    /// Loop unrolling unrolls and removes iteration statements from the program.
78    fn reconstruct_iteration(&mut self, _: IterationStatement) -> (Statement, Self::AdditionalOutput) {
79        panic!("`IterationStatement`s should not be in the AST at this phase of compilation.");
80    }
81
82    fn reconstruct_expression_statement(&mut self, input: ExpressionStatement) -> (Statement, Self::AdditionalOutput) {
83        if self.side_effect_free(&input.expression) {
84            (Statement::dummy(), Default::default())
85        } else {
86            (
87                ExpressionStatement { expression: self.reconstruct_expression(input.expression).0, ..input }.into(),
88                Default::default(),
89            )
90        }
91    }
92}