leo_passes/processing_async/
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//! 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::{Pass, PathResolution, SymbolTable, SymbolTableCreation, TypeChecking, TypeCheckingInput};
51
52use leo_ast::ProgramReconstructor as _;
53use leo_errors::Result;
54use leo_span::Symbol;
55
56mod ast;
57
58mod program;
59
60mod visitor;
61use visitor::*;
62
63pub struct ProcessingAsync;
64
65impl Pass for ProcessingAsync {
66    type Input = TypeCheckingInput;
67    type Output = ();
68
69    const NAME: &str = "ProcessingAsync";
70
71    fn do_pass(input: Self::Input, state: &mut crate::CompilerState) -> Result<Self::Output> {
72        let mut ast = std::mem::take(&mut state.ast);
73        let mut visitor = ProcessingAsyncVisitor {
74            state,
75            max_inputs: input.max_inputs,
76            current_program: Symbol::intern(""),
77            current_function: Symbol::intern(""),
78            new_async_functions: Vec::new(),
79            modified: false,
80        };
81        ast.ast = visitor.reconstruct_program(ast.ast);
82        visitor.state.handler.last_err()?;
83        visitor.state.ast = ast;
84
85        if visitor.modified {
86            // If we actually changed anything in the program, then we need to recreate the symbol table and run type
87            // checking again. That's because this pass introduces new `async function`s to the program.
88            visitor.state.symbol_table = SymbolTable::default();
89            PathResolution::do_pass((), state)?;
90            SymbolTableCreation::do_pass((), state)?;
91            TypeChecking::do_pass(input.clone(), state)?;
92        }
93
94        Ok(())
95    }
96}