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}