Skip to main content

leo_passes/processing_async/
mod.rs

1// Copyright (C) 2019-2026 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//! The `ProcessingAsync` pass rewrites `async { ... }` blocks into standalone
18//! `async function`s. Each block is lifted to a new top-level async function,
19//! and the block is replaced with a call to that function.
20//!
21//! This involves:
22//! - Capturing all variable and tuple field accesses used inside the block.
23//! - Filtering out globals and locals (which are handled differently).
24//! - Generating fresh function inputs for the captured values.
25//! - Rewriting the block with replacements for captured expressions.
26//! - Creating a `CallExpression` that invokes the synthesized async function.
27//!
28//! If any async blocks were rewritten, this pass will rebuild the symbol table
29//! and rerun path resolution and type checking to account for the new functions.
30//!
31//! # Example
32//! ```leo
33//! async transition foo(x: u32) -> Future {
34//!     return async {
35//!         assert(x == 1);  
36//!     };
37//! }
38//! ```
39//! becomes
40//! ```leo
41//! async function foo_(x: u32) {
42//!     assert(x == 1);
43//! }
44//!
45//! transition foo(x: u32) -> Future {
46//!     return foo_(x);
47//! }
48//! ```
49
50use crate::{
51    GlobalItemsCollection,
52    GlobalVarsCollection,
53    Pass,
54    PathResolution,
55    SymbolTable,
56    TypeChecking,
57    TypeCheckingInput,
58};
59
60use leo_ast::ProgramReconstructor as _;
61use leo_errors::Result;
62use leo_span::Symbol;
63
64mod ast;
65
66mod program;
67
68mod visitor;
69use visitor::*;
70
71pub struct ProcessingAsync;
72
73impl Pass for ProcessingAsync {
74    type Input = TypeCheckingInput;
75    type Output = ();
76
77    const NAME: &str = "ProcessingAsync";
78
79    fn do_pass(input: Self::Input, state: &mut crate::CompilerState) -> Result<Self::Output> {
80        let ast = std::mem::take(&mut state.ast);
81        let mut visitor = ProcessingAsyncVisitor {
82            state,
83            max_inputs: input.max_inputs,
84            current_program: Symbol::intern(""),
85            current_function: Symbol::intern(""),
86            new_async_functions: Vec::new(),
87            modified: false,
88        };
89
90        let ast = ast.map(
91            |program| visitor.reconstruct_program(program),
92            |library| library, // no-op for libraries
93        );
94
95        visitor.state.handler.last_err()?;
96        visitor.state.ast = ast;
97
98        if visitor.modified {
99            // If we actually changed anything in the program, then we need to recreate the symbol table and run type
100            // checking again. That's because this pass introduces new `async function`s to the program.
101            visitor.state.symbol_table = SymbolTable::default();
102            GlobalVarsCollection::do_pass((), state)?;
103            PathResolution::do_pass((), state)?;
104            GlobalItemsCollection::do_pass((), state)?;
105            TypeChecking::do_pass(input.clone(), state)?;
106        }
107
108        Ok(())
109    }
110}