leo_passes/option_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 `optional` types (`T?`) and `optional` expressions within a `ProgramScope`.
18//!
19//! This pass rewrites all `optional` types into explicit struct representations with two fields:
20//! - `is_some: bool` — indicating whether the value is present,
21//! - `val: T` — holding the underlying value (or the "zero" value of `T` when `is_some` is `false`).
22//!
23//! All literals, variables, function parameters, and return values involving `optional` types are
24//! transformed into this struct representation. Nested structures (e.g., arrays, tuples, or user-defined
25//! structs containing `optional` types) are lowered recursively.
26//!
27//! ### Example
28//!
29//! ```leo
30//! let x: u8? = 42u8;
31//! ```
32//!
33//! is lowered to:
34//!
35//! ```leo
36//! let x: "u8?" = "u8?" { is_some: true, val: 42u8 };
37//! ```
38//!
39//! When a value is `none`, the `is_some` field is set to `false` and `val` is initialized
40//! with the zero value of the underlying type (`0u8`, `false`, `0field`, etc.).
41//!
42//! ### Recursive Lowering Example
43//!
44//! ```leo
45//! let arr: [u64?; 2] = [1u64, none];
46//! ```
47//!
48//! is lowered to:
49//!
50//! ```leo
51//! let arr: ["u64?"; 2] = [
52//!     "u64?" { is_some: true, val: 1u64 },
53//!     "u64?" { is_some: false, val: 0u64 },
54//! ];
55//! ```
56//!
57//! After this pass, no `T?` types remain in the program: all optional values are represented explicitly
58//! as structs with `is_some` and `val` fields.
59
60use crate::{Pass, PathResolution, SymbolTable, SymbolTableCreation, TypeChecking, TypeCheckingInput};
61
62use leo_ast::{ArrayType, CompositeType, ProgramReconstructor as _, Type};
63use leo_errors::Result;
64use leo_span::Symbol;
65
66use indexmap::IndexMap;
67use itertools::Itertools;
68
69mod ast;
70
71mod program;
72
73mod visitor;
74use visitor::*;
75
76pub struct OptionLowering;
77
78impl Pass for OptionLowering {
79    type Input = TypeCheckingInput;
80    type Output = ();
81
82    const NAME: &str = "OptionLowering";
83
84    fn do_pass(input: TypeCheckingInput, state: &mut crate::CompilerState) -> Result<Self::Output> {
85        let mut ast = std::mem::take(&mut state.ast);
86        let mut visitor = OptionLoweringVisitor {
87            state,
88            program: Symbol::intern(""),
89            module: vec![],
90            function: None,
91            new_structs: IndexMap::new(),
92            reconstructed_structs: IndexMap::new(),
93        };
94        ast.ast = visitor.reconstruct_program(ast.ast);
95        visitor.state.handler.last_err()?;
96        visitor.state.ast = ast;
97
98        // We need to recreate the symbol table and run type checking again because this pass may introduce new structs
99        // and modify existing ones.
100        visitor.state.symbol_table = SymbolTable::default();
101        PathResolution::do_pass((), state)?;
102        SymbolTableCreation::do_pass((), state)?;
103        TypeChecking::do_pass(input.clone(), state)?;
104
105        Ok(())
106    }
107}
108
109pub fn make_optional_struct_symbol(ty: &Type) -> Symbol {
110    // Step 1: Extract a usable type name
111    fn display_type(ty: &Type) -> String {
112        match ty {
113            Type::Address
114            | Type::Field
115            | Type::Group
116            | Type::Scalar
117            | Type::Signature
118            | Type::Boolean
119            | Type::Integer(..) => format!("{ty}"),
120            Type::Array(ArrayType { element_type, length }) => {
121                format!("[{}; {length}]", display_type(element_type))
122            }
123            Type::Composite(CompositeType { path, .. }) => {
124                format!("::{}", path.absolute_path().iter().format("::"))
125            }
126
127            Type::Tuple(_)
128            | Type::Optional(_)
129            | Type::Mapping(_)
130            | Type::Numeric
131            | Type::Identifier(_)
132            | Type::Future(_)
133            | Type::String
134            | Type::Err
135            | Type::Unit => {
136                panic!("unexpected inner type in optional struct name")
137            }
138        }
139    }
140
141    // Step 3: Build symbol that ends with `?`.
142    Symbol::intern(&format!("\"{}?\"", display_type(ty)))
143}