leo_passes/ssa_const_propagation/
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::{Expression, Node, NodeID, interpreter_value::Value};
20use leo_errors::StaticAnalyzerError;
21use leo_span::{Span, Symbol};
22
23use indexmap::IndexMap;
24
25/// Visitor that propagates constant values through the program.
26pub struct SsaConstPropagationVisitor<'a> {
27    pub state: &'a mut CompilerState,
28    /// Maps variable names to their constant values.
29    /// Only variables assigned constant values are tracked here.
30    pub constants: IndexMap<Symbol, Value>,
31    /// Have we actually modified the program at all?
32    pub changed: bool,
33}
34
35impl SsaConstPropagationVisitor<'_> {
36    /// Emit a `StaticAnalyzerError`.
37    pub fn emit_err(&self, err: StaticAnalyzerError) {
38        self.state.handler.emit_err(err);
39    }
40
41    /// Convert a Value to an Expression.
42    /// Returns the new expression and its node ID.
43    /// If the original node doesn't have a type, this will return None.
44    pub fn value_to_expression(&mut self, value: &Value, span: Span, id: NodeID) -> Option<(Expression, NodeID)> {
45        let ty = self.state.type_table.get(&id)?.clone();
46        let symbol_table = &self.state.symbol_table;
47        let struct_lookup = |sym: &[Symbol]| {
48            symbol_table
49                .lookup_struct(sym)
50                .unwrap()
51                .members
52                .iter()
53                .map(|mem| (mem.identifier.name, mem.type_.clone()))
54                .collect()
55        };
56        let new_expr = value.to_expression(span, &self.state.node_builder, &ty, &struct_lookup)?;
57        let new_id = new_expr.id();
58
59        // Copy the type information to the new node ID.
60        self.copy_types_recursively(&new_expr, &ty);
61
62        Some((new_expr, new_id))
63    }
64
65    /// Copy types for nested expressions based on the type structure.
66    /// This ensures that ALL expressions in the tree have proper type information.
67    fn copy_types_recursively(&mut self, expr: &Expression, ty: &leo_ast::Type) {
68        use leo_ast::Type;
69
70        self.state.type_table.insert(expr.id(), ty.clone());
71
72        // Then recursively handle nested expressions
73        match (expr, ty) {
74            (Expression::Array(array_expr), Type::Array(array_ty)) => {
75                for element in &array_expr.elements {
76                    self.copy_types_recursively(element, array_ty.element_type());
77                }
78            }
79            (Expression::Tuple(tuple_expr), Type::Tuple(tuple_ty)) => {
80                for (element, elem_ty) in tuple_expr.elements.iter().zip(tuple_ty.elements()) {
81                    self.copy_types_recursively(element, elem_ty);
82                }
83            }
84            (Expression::Struct(struct_expr), Type::Composite(composite_ty)) => {
85                let symbols = composite_ty.path.as_symbols();
86                let member_types: Vec<leo_ast::Type> = self
87                    .state
88                    .symbol_table
89                    .lookup_struct(&symbols)
90                    .map(|struct_def| struct_def.members.iter().map(|m| m.type_.clone()).collect())
91                    .unwrap_or_default();
92                for (member, member_ty) in struct_expr.members.iter().zip(member_types.iter()) {
93                    if let Some(expr) = &member.expression {
94                        self.copy_types_recursively(expr, member_ty);
95                    }
96                }
97            }
98            _ => {
99                // For leaf expressions (literals, etc.), we've already set the type above
100            }
101        }
102    }
103}