leo_passes/storage_lowering/
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
17//! Performs lowering of `storage` variables and storage vectors into explicit `Mapping` operations.
18//!
19//! This pass rewrites high-level storage constructs—such as `storage` declarations and `Vector` methods concrete
20//! calls to the underlying `Mapping` API used in Aleo programs. Each `storage` variable is de-sugared into one or more
21//! `Mapping` instances, and all read/write operations are rewritten as `Mapping::get`, `Mapping::set`, or
22//! `Mapping::get_or_use` calls.
23//!
24//! ### Overview
25//!
26//! - **Storage Variables:** Each top-level `storage` variable (e.g., `storage counter: u32;`) is represented by a
27//!   `Mapping` that persists across transitions. The pass introduces ternary expressions that check whether the
28//!   underlying mapping contains a value before reading it.
29//!
30//! - **Storage Vectors:** A `storage` vector (e.g., `storage vec: [u32];`) is lowered into two mappings:
31//!   - `<vec_name>__` stores the vector elements keyed by a `u32` index.
32//!   - `<vec_name>__len__` stores the current vector length under key `false`.
33//!
34//!   Vector operations such as `.push()`, `.pop()`, `.len()`, `.get()`, `.set()`, `.swap_remove()`, and `.clear()`
35//!   are translated into combinations of `Mapping` calls and helper expressions that manipulate the length mapping and
36//!   element mappings directly.
37//!
38//! ### Example: Storage Vector
39//!
40//! ```leo
41//! storage vec: [u32];
42//!
43//! async transition test_vector_ops() -> Future {
44//!     return async {
45//!         vec.push(10u32);
46//!         let x = vec.get(0u32).unwrap();
47//!         let y = vec.pop();
48//!     };
49//! }
50//! ```
51//!
52//! is lowered to:
53//!
54//! ```leo
55//! mapping vec__: u32 => u32;       // vector values
56//! mapping vec__len__: bool => u32; // length
57//!
58//! // vec.push(10u32);
59//! let $len_var = Mapping::get_or_use(vec__len__, false, 0u32);
60//! Mapping::set(vec__, $len_var, 10u32);
61//! Mapping::set(vec__len__, false, $len_var + 1u32);
62//!
63//! // let x = vec.get(3u32);
64//! let $len_var = Mapping::get_or_use(vec__len__, false, 0u32);
65//! let x = 3u32 < $len_var ? Mapping::get_or_use(vec__, 3u32, 0u32) : None;
66//!
67//! // let y = vec.pop();
68//! let $len_var = Mapping::get_or_use(vec__len__, false, 0u32);
69//! if ($len_var > 0u32) { Mapping::set(vec__len__, false, $len_var - 1u32); }
70//! let y = $len_var > 0u32 ? Mapping::get_or_use(vec__, $len_var - 1u32, 0u32) : None;
71//! ```
72//!
73//! ### Example: Singleton Storage
74//!
75//! ```leo
76//! storage counter: u32;
77//!
78//! async transition increment() -> Future {
79//!     return async {
80//!         let old = counter.unwrap_or(0u32);
81//!         counter = old + 1u32;
82//!     };
83//! }
84//! ```
85//!
86//! is lowered to:
87//!
88//! ```leo
89//! mapping counter__: bool => u32;
90//!
91//! // let old = counter.unwrap_or(0u32)
92//! let old = counter__.contains(false) ? counter__.get_or_use(false, 0u32) : 0u32
93//!
94//! // counter = old + 1u32
95//! Mapping::set(counter__, false, old + 1u32)
96//! ```
97
98use crate::{Pass, PathResolution, SymbolTable, SymbolTableCreation, TypeChecking, TypeCheckingInput};
99
100use leo_ast::ProgramReconstructor as _;
101use leo_errors::Result;
102use leo_span::Symbol;
103
104use indexmap::IndexMap;
105
106mod ast;
107
108mod program;
109
110mod visitor;
111use visitor::*;
112
113pub struct StorageLowering;
114
115impl Pass for StorageLowering {
116    type Input = TypeCheckingInput;
117    type Output = ();
118
119    const NAME: &str = "StorageLowering";
120
121    fn do_pass(input: TypeCheckingInput, state: &mut crate::CompilerState) -> Result<Self::Output> {
122        let mut ast = std::mem::take(&mut state.ast);
123        let mut visitor = StorageLoweringVisitor { state, program: Symbol::intern(""), new_mappings: IndexMap::new() };
124        ast.ast = visitor.reconstruct_program(ast.ast);
125        visitor.state.handler.last_err()?;
126        visitor.state.ast = ast;
127
128        // We need to recreate the symbol table and run type checking again because this pass may introduce new mappings
129        // and new statements and expressions.
130        visitor.state.symbol_table = SymbolTable::default();
131        PathResolution::do_pass((), state)?;
132        SymbolTableCreation::do_pass((), state)?;
133        TypeChecking::do_pass(input.clone(), state)?;
134
135        Ok(())
136    }
137}