leo_passes/symbol_table_creation/
mod.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, Pass, VariableSymbol, VariableType};
18
19use leo_ast::{
20    Composite,
21    ExpressionVisitor,
22    Function,
23    FunctionStub,
24    Location,
25    Mapping,
26    MappingType,
27    Program,
28    ProgramScope,
29    ProgramVisitor,
30    StatementVisitor,
31    Stub,
32    Type,
33    TypeVisitor,
34    Variant,
35};
36use leo_errors::{AstError, LeoError, Result};
37use leo_span::Symbol;
38
39use indexmap::IndexSet;
40
41/// A pass to fill the SymbolTable.
42///
43/// Only creates the global data - local data will be constructed during type checking.
44pub struct SymbolTableCreation;
45
46impl Pass for SymbolTableCreation {
47    type Input = ();
48    type Output = ();
49
50    const NAME: &'static str = "SymbolTableCreation";
51
52    fn do_pass(_input: Self::Input, state: &mut CompilerState) -> Result<Self::Output> {
53        let ast = std::mem::take(&mut state.ast);
54        let mut visitor = SymbolTableCreationVisitor {
55            state,
56            structs: IndexSet::new(),
57            program_name: Symbol::intern(""),
58            is_stub: false,
59        };
60        visitor.visit_program(ast.as_repr());
61        visitor.state.handler.last_err().map_err(|e| *e)?;
62        visitor.state.ast = ast;
63        Ok(())
64    }
65}
66
67struct SymbolTableCreationVisitor<'a> {
68    /// The state of the compiler.
69    state: &'a mut CompilerState,
70    /// The current program name.
71    program_name: Symbol,
72    /// Whether or not traversing stub.
73    is_stub: bool,
74    /// The set of local structs that have been successfully visited.
75    structs: IndexSet<Symbol>,
76}
77
78impl ExpressionVisitor for SymbolTableCreationVisitor<'_> {
79    type AdditionalInput = ();
80    type Output = ();
81}
82
83impl TypeVisitor for SymbolTableCreationVisitor<'_> {}
84
85impl StatementVisitor for SymbolTableCreationVisitor<'_> {}
86
87impl ProgramVisitor for SymbolTableCreationVisitor<'_> {
88    fn visit_program_scope(&mut self, input: &ProgramScope) {
89        // Set current program name
90        self.program_name = input.program_id.name.name;
91        self.is_stub = false;
92
93        // Visit the program scope
94        input.structs.iter().for_each(|(_, c)| (self.visit_struct(c)));
95        input.mappings.iter().for_each(|(_, c)| (self.visit_mapping(c)));
96        input.functions.iter().for_each(|(_, c)| (self.visit_function(c)));
97        input.consts.iter().for_each(|(_, c)| (self.visit_const(c)));
98    }
99
100    fn visit_import(&mut self, input: &Program) {
101        self.visit_program(input)
102    }
103
104    fn visit_struct(&mut self, input: &Composite) {
105        // Allow up to one local redefinition for each external struct.
106        if !input.is_record && !self.structs.insert(input.name()) {
107            return self.state.handler.emit_err::<LeoError>(AstError::shadowed_struct(input.name(), input.span).into());
108        }
109        if input.is_record {
110            let program_name = input.external.unwrap_or(self.program_name);
111            if let Err(err) =
112                self.state.symbol_table.insert_record(Location::new(program_name, input.name()), input.clone())
113            {
114                self.state.handler.emit_err(err);
115            }
116        } else if let Err(err) = self.state.symbol_table.insert_struct(self.program_name, input.name(), input.clone()) {
117            self.state.handler.emit_err(err);
118        }
119    }
120
121    fn visit_mapping(&mut self, input: &Mapping) {
122        // Add the variable associated with the mapping to the symbol table.
123        if let Err(err) = self.state.symbol_table.insert_global(
124            Location::new(self.program_name, input.identifier.name),
125            VariableSymbol {
126                type_: Type::Mapping(MappingType {
127                    key: Box::new(input.key_type.clone()),
128                    value: Box::new(input.value_type.clone()),
129                    program: self.program_name,
130                }),
131                span: input.span,
132                declaration: VariableType::Mut,
133            },
134        ) {
135            self.state.handler.emit_err(err);
136        }
137    }
138
139    fn visit_function(&mut self, input: &Function) {
140        if let Err(err) =
141            self.state.symbol_table.insert_function(Location::new(self.program_name, input.name()), input.clone())
142        {
143            self.state.handler.emit_err(err);
144        }
145    }
146
147    fn visit_stub(&mut self, input: &Stub) {
148        self.is_stub = true;
149        self.program_name = input.stub_id.name.name;
150        input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c)));
151        input.structs.iter().for_each(|(_, c)| (self.visit_struct_stub(c)));
152        input.mappings.iter().for_each(|(_, c)| (self.visit_mapping(c)));
153    }
154
155    fn visit_function_stub(&mut self, input: &FunctionStub) {
156        // Construct the location for the function.
157        let location = Location::new(self.program_name, input.name());
158        // Initialize the function symbol.
159        if let Err(err) = self.state.symbol_table.insert_function(location, Function::from(input.clone())) {
160            self.state.handler.emit_err(err);
161        }
162
163        // If the `FunctionStub` is an async transition, attach the finalize logic to the function.
164        // NOTE - for an external function like this, we really only need to attach the finalizer
165        // for the use of `assert_simple_async_transition_call` in the static analyzer.
166        // In principle that could be handled differently.
167        if matches!(input.variant, Variant::AsyncTransition) {
168            // This matches the logic in the disassembler.
169            let name = Symbol::intern(&format!("finalize/{}", input.name()));
170            if let Err(err) = self.state.symbol_table.attach_finalizer(
171                location,
172                Location::new(self.program_name, name),
173                Vec::new(),
174                Vec::new(),
175            ) {
176                self.state.handler.emit_err(err);
177            }
178        }
179    }
180
181    fn visit_struct_stub(&mut self, input: &Composite) {
182        if let Some(program) = input.external {
183            assert_eq!(program, self.program_name);
184        }
185
186        if input.is_record {
187            let program_name = input.external.unwrap_or(self.program_name);
188            if let Err(err) =
189                self.state.symbol_table.insert_record(Location::new(program_name, input.name()), input.clone())
190            {
191                self.state.handler.emit_err(err);
192            }
193        } else if let Err(err) = self.state.symbol_table.insert_struct(self.program_name, input.name(), input.clone()) {
194            self.state.handler.emit_err(err);
195        }
196    }
197}