mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-22 05:00:59 +03:00
Introduce StaticAnalyzer pass; move future await checking to this pass
This commit is contained in:
parent
1e31a9045a
commit
b87a79d991
@ -20,6 +20,28 @@
|
||||
|
||||
use crate::*;
|
||||
|
||||
// TODO: The Visitor and Reconstructor patterns need a redesign so that the default implementation can easily be invoked though its implemented in an overriding trait.
|
||||
// Here is a pattern that seems to work
|
||||
// trait ProgramVisitor {
|
||||
// // The trait method that can be overridden
|
||||
// fn visit_program_scope(&mut self);
|
||||
//
|
||||
// // Private helper function containing the default implementation
|
||||
// fn default_visit_program_scope(&mut self) {
|
||||
// println!("Do default stuff");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// struct YourStruct;
|
||||
//
|
||||
// impl ProgramVisitor for YourStruct {
|
||||
// fn visit_program_scope(&mut self) {
|
||||
// println!("Do custom stuff.");
|
||||
// // Call the default implementation
|
||||
// self.default_visit_program_scope();
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A Visitor trait for expressions in the AST.
|
||||
pub trait ExpressionVisitor<'a> {
|
||||
type AdditionalInput: Default;
|
||||
|
@ -155,7 +155,17 @@ impl<'a, N: Network> Compiler<'a, N> {
|
||||
|
||||
/// Runs the type checker pass.
|
||||
pub fn type_checker_pass(&'a self, symbol_table: SymbolTable) -> Result<(SymbolTable, StructGraph, CallGraph)> {
|
||||
let (symbol_table, struct_graph, call_graph) = TypeChecker::<N>::do_pass((
|
||||
let (symbol_table, struct_graph, call_graph) =
|
||||
TypeChecker::<N>::do_pass((&self.ast, self.handler, symbol_table, &self.type_table))?;
|
||||
if self.compiler_options.output.type_checked_symbol_table {
|
||||
self.write_symbol_table_to_json("type_checked_symbol_table.json", &symbol_table)?;
|
||||
}
|
||||
Ok((symbol_table, struct_graph, call_graph))
|
||||
}
|
||||
|
||||
/// Runs the static analysis pass.
|
||||
pub fn static_analysis_pass(&mut self, symbol_table: SymbolTable) -> Result<SymbolTable> {
|
||||
let symbol_table = StaticAnalyzer::<N>::do_pass((
|
||||
&self.ast,
|
||||
self.handler,
|
||||
symbol_table,
|
||||
@ -163,10 +173,7 @@ impl<'a, N: Network> Compiler<'a, N> {
|
||||
self.compiler_options.build.conditional_block_max_depth,
|
||||
self.compiler_options.build.disable_conditional_branch_type_checking,
|
||||
))?;
|
||||
if self.compiler_options.output.type_checked_symbol_table {
|
||||
self.write_symbol_table_to_json("type_checked_symbol_table.json", &symbol_table)?;
|
||||
}
|
||||
Ok((symbol_table, struct_graph, call_graph))
|
||||
Ok(symbol_table)
|
||||
}
|
||||
|
||||
/// Runs the loop unrolling pass.
|
||||
@ -285,8 +292,11 @@ impl<'a, N: Network> Compiler<'a, N> {
|
||||
/// Runs the compiler stages.
|
||||
pub fn compiler_stages(&mut self) -> Result<(SymbolTable, StructGraph, CallGraph)> {
|
||||
let st = self.symbol_table_pass()?;
|
||||
|
||||
let (st, struct_graph, call_graph) = self.type_checker_pass(st)?;
|
||||
|
||||
let st = self.static_analysis_pass(st)?;
|
||||
|
||||
// TODO: Make this pass optional.
|
||||
let st = self.loop_unrolling_pass(st)?;
|
||||
|
||||
|
@ -265,6 +265,8 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a, CurrentNetwork>) ->
|
||||
|
||||
let (st, struct_graph, call_graph) = parsed.type_checker_pass(st)?;
|
||||
|
||||
let st = parsed.static_analysis_pass(st)?;
|
||||
|
||||
CheckUniqueNodeIds::new().visit_program(&parsed.ast.ast);
|
||||
|
||||
let st = parsed.loop_unrolling_pass(st)?;
|
||||
|
@ -17,6 +17,9 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod static_analysis;
|
||||
pub use static_analysis::*;
|
||||
|
||||
pub mod code_generation;
|
||||
pub use code_generation::*;
|
||||
|
||||
|
@ -99,6 +99,7 @@ impl<'a> Unroller<'a> {
|
||||
.swap(previous_constant_propagation_table.borrow().lookup_scope_by_index(index).unwrap());
|
||||
self.constant_propagation_table.borrow_mut().parent =
|
||||
Some(Box::new(previous_constant_propagation_table.into_inner()));
|
||||
|
||||
core::mem::replace(&mut self.scope_index, 0)
|
||||
}
|
||||
|
||||
|
55
compiler/passes/src/static_analysis/analyze_expression.rs
Normal file
55
compiler/passes/src/static_analysis/analyze_expression.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{StaticAnalyzer, VariableSymbol};
|
||||
|
||||
use leo_ast::*;
|
||||
use leo_errors::{StaticAnalyzerError, emitter::Handler};
|
||||
use leo_span::{Span, Symbol, sym};
|
||||
|
||||
use snarkvm::console::network::Network;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
impl<'a, N: Network> ExpressionVisitor<'a> for StaticAnalyzer<'a, N> {
|
||||
type AdditionalInput = ();
|
||||
type Output = ();
|
||||
|
||||
fn visit_access(&mut self, input: &'a AccessExpression, _: &Self::AdditionalInput) -> Self::Output {
|
||||
if let AccessExpression::AssociatedFunction(access) = input {
|
||||
// Get the core function.
|
||||
let core_function = match CoreFunction::from_symbols(access.variant.name, access.name.name) {
|
||||
Some(core_function) => core_function,
|
||||
None => unreachable!("Typechecking guarantees that this function exists."),
|
||||
};
|
||||
|
||||
// Check that the future was awaited correctly.
|
||||
if core_function == CoreFunction::FutureAwait {
|
||||
self.assert_future_await(&access.arguments.first(), input.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, input: &'a CallExpression, _: &Self::AdditionalInput) -> Self::Output {
|
||||
match &*input.function {
|
||||
// Note that the parser guarantees that `input.function` is always an identifier.
|
||||
Expression::Identifier(ident) => {
|
||||
todo!()
|
||||
}
|
||||
_ => unreachable!("Parsing guarantees that a function name is always an identifier."),
|
||||
}
|
||||
}
|
||||
}
|
128
compiler/passes/src/static_analysis/analyze_program.rs
Normal file
128
compiler/passes/src/static_analysis/analyze_program.rs
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{DiGraphError, StaticAnalyzer, TypeChecker};
|
||||
|
||||
use leo_ast::{Type, *};
|
||||
use leo_errors::{StaticAnalyzerError, StaticAnalyzerWarning};
|
||||
use leo_span::sym;
|
||||
|
||||
use snarkvm::console::network::Network;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl<'a, N: Network> ProgramVisitor<'a> for StaticAnalyzer<'a, N> {
|
||||
fn visit_program_scope(&mut self, input: &'a ProgramScope) {
|
||||
// Set the current program name.
|
||||
self.current_program = Some(input.program_id.name.name);
|
||||
// Do the default implementation for visiting the program scope.
|
||||
input.structs.iter().for_each(|(_, c)| (self.visit_struct(c)));
|
||||
input.mappings.iter().for_each(|(_, c)| (self.visit_mapping(c)));
|
||||
input.functions.iter().for_each(|(_, c)| (self.visit_function(c)));
|
||||
input.consts.iter().for_each(|(_, c)| (self.visit_const(c)));
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, function: &'a Function) {
|
||||
// Lookup function metadata in the symbol table.
|
||||
// Note that this unwrap is safe since function metadata is stored in a prior pass.
|
||||
let function_index = self
|
||||
.symbol_table
|
||||
.borrow()
|
||||
.lookup_fn_symbol(Location::new(self.current_program, function.identifier.name))
|
||||
.unwrap()
|
||||
.id;
|
||||
|
||||
// Enter the function's scope.
|
||||
let previous_function_index = self.enter_scope(function_index);
|
||||
let previous_scope_index = self.enter_scope(self.scope_index);
|
||||
|
||||
// Set the function name and variant.
|
||||
self.variant = Some(function.variant);
|
||||
|
||||
// If the function is an async function, initialize the await checker.
|
||||
if self.variant == Some(Variant::AsyncFunction) {
|
||||
// Initialize the list of input futures. Each one must be awaited before the end of the function.
|
||||
self.await_checker.set_futures(
|
||||
function
|
||||
.input
|
||||
.iter()
|
||||
.filter_map(|input| {
|
||||
if let Type::Future(_) = input.type_.clone() { Some(input.identifier.name) } else { None }
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
self.visit_block(&function.block);
|
||||
|
||||
// Exit the function's scope.
|
||||
self.exit_scope(previous_scope_index);
|
||||
self.exit_scope(previous_function_index);
|
||||
|
||||
// Check that all futures were awaited exactly once.
|
||||
if self.variant == Some(Variant::AsyncFunction) {
|
||||
// Throw error if not all futures awaits even appear once.
|
||||
if !self.await_checker.static_to_await.is_empty() {
|
||||
self.emit_err(StaticAnalyzerError::future_awaits_missing(
|
||||
self.await_checker
|
||||
.static_to_await
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|f| f.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
function.span(),
|
||||
));
|
||||
} else if self.await_checker.enabled && !self.await_checker.to_await.is_empty() {
|
||||
// Tally up number of paths that are unawaited and number of paths that are awaited more than once.
|
||||
let (num_paths_unawaited, num_paths_duplicate_awaited, num_perfect) =
|
||||
self.await_checker.to_await.iter().fold((0, 0, 0), |(unawaited, duplicate, perfect), path| {
|
||||
(
|
||||
unawaited + if !path.elements.is_empty() { 1 } else { 0 },
|
||||
duplicate + if path.counter > 0 { 1 } else { 0 },
|
||||
perfect + if path.counter > 0 || !path.elements.is_empty() { 0 } else { 1 },
|
||||
)
|
||||
});
|
||||
|
||||
// Throw error if there does not exist a path in which all futures are awaited exactly once.
|
||||
if num_perfect == 0 {
|
||||
self.emit_err(StaticAnalyzerError::no_path_awaits_all_futures_exactly_once(
|
||||
self.await_checker.to_await.len(),
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
|
||||
// Throw warning if some futures are awaited more than once in some paths.
|
||||
if num_paths_unawaited > 0 {
|
||||
self.emit_warning(StaticAnalyzerWarning::some_paths_do_not_await_all_futures(
|
||||
self.await_checker.to_await.len(),
|
||||
num_paths_unawaited,
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
|
||||
// Throw warning if not all futures are awaited in some paths.
|
||||
if num_paths_duplicate_awaited > 0 {
|
||||
self.emit_warning(StaticAnalyzerWarning::some_paths_contain_duplicate_future_awaits(
|
||||
self.await_checker.to_await.len(),
|
||||
num_paths_duplicate_awaited,
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
compiler/passes/src/static_analysis/analyze_statement.rs
Normal file
71
compiler/passes/src/static_analysis/analyze_statement.rs
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use crate::{ConditionalTreeNode, TypeChecker, VariableSymbol, VariableType};
|
||||
|
||||
use leo_ast::{
|
||||
Type::{Future, Tuple},
|
||||
*,
|
||||
};
|
||||
use leo_errors::StaticAnalyzerError;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
impl<'a, N: Network> StatementVisitor<'a> for StaticAnalyzer<'a, N> {
|
||||
fn visit_block(&mut self, input: &'a Block) {
|
||||
// Enter the block scope.
|
||||
let scope_index = self.current_scope_index();
|
||||
let previous_scope_index = self.enter_scope(scope_index);
|
||||
|
||||
input.statements.iter().for_each(|stmt| self.visit_statement(stmt));
|
||||
|
||||
// Exit the block scope.
|
||||
self.exit_scope(previous_scope_index);
|
||||
}
|
||||
|
||||
fn visit_conditional(&mut self, input: &'a ConditionalStatement) {
|
||||
self.visit_expression(&input.condition, &Default::default());
|
||||
|
||||
// Create scope for checking awaits in `then` branch of conditional.
|
||||
let current_bst_nodes: Vec<ConditionalTreeNode> =
|
||||
match self.await_checker.create_then_scope(self.variant == Some(Variant::AsyncFunction), input.span) {
|
||||
Ok(nodes) => nodes,
|
||||
Err(warn) => return self.emit_warning(warn),
|
||||
};
|
||||
|
||||
// Visit block.
|
||||
self.visit_block(&input.then);
|
||||
|
||||
// Exit scope for checking awaits in `then` branch of conditional.
|
||||
let saved_paths =
|
||||
self.await_checker.exit_then_scope(self.variant == Some(Variant::AsyncFunction), current_bst_nodes);
|
||||
|
||||
if let Some(otherwise) = &input.otherwise {
|
||||
match &**otherwise {
|
||||
Statement::Block(stmt) => {
|
||||
// Visit the otherwise-block.
|
||||
self.visit_block(stmt);
|
||||
}
|
||||
Statement::Conditional(stmt) => self.visit_conditional(stmt),
|
||||
_ => unreachable!("Else-case can only be a block or conditional statement."),
|
||||
}
|
||||
}
|
||||
|
||||
// Update the set of all possible BST paths.
|
||||
self.await_checker.exit_statement_scope(self.variant == Some(Variant::AsyncFunction), saved_paths);
|
||||
}
|
||||
}
|
139
compiler/passes/src/static_analysis/analyzer.rs
Normal file
139
compiler/passes/src/static_analysis/analyzer.rs
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
CallGraph,
|
||||
StructGraph,
|
||||
SymbolTable,
|
||||
TypeTable,
|
||||
VariableSymbol,
|
||||
VariableType,
|
||||
static_analysis::await_checker::AwaitChecker,
|
||||
};
|
||||
|
||||
use leo_ast::*;
|
||||
use leo_errors::{StaticAnalyzerError, StaticAnalyzerWarning, emitter::Handler};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use snarkvm::console::network::Network;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use itertools::Itertools;
|
||||
use std::{cell::RefCell, marker::PhantomData};
|
||||
|
||||
pub struct StaticAnalyzer<'a, N: Network> {
|
||||
/// The symbol table for the program.
|
||||
// Note that this pass does not use the symbol table in a meaningful way.
|
||||
// However, this may be useful in future static analysis passes.
|
||||
pub(crate) symbol_table: RefCell<SymbolTable>,
|
||||
/// The type table for the program.
|
||||
// Note that this pass does not use the type table in a meaningful way.
|
||||
// However, this may be useful in future static analysis passes.
|
||||
pub(crate) type_table: &'a TypeTable,
|
||||
/// The error handler.
|
||||
pub(crate) handler: &'a Handler,
|
||||
/// Struct to store the state relevant to checking all futures are awaited.
|
||||
pub(crate) await_checker: AwaitChecker,
|
||||
/// The index of the current scope.
|
||||
pub(crate) scope_index: usize,
|
||||
/// The current program name.
|
||||
pub(crate) current_program: Option<Symbol>,
|
||||
/// The variant of the function that we are currently traversing.
|
||||
pub(crate) variant: Option<Variant>,
|
||||
// Allows the type checker to be generic over the network.
|
||||
phantom: PhantomData<N>,
|
||||
}
|
||||
|
||||
impl<'a, N: Network> StaticAnalyzer<'a, N> {
|
||||
/// Returns a new static analyzer given a symbol table and error handler.
|
||||
pub fn new(
|
||||
symbol_table: SymbolTable,
|
||||
type_table: &'a TypeTable,
|
||||
handler: &'a Handler,
|
||||
max_depth: usize,
|
||||
disabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
symbol_table: RefCell::new(symbol_table),
|
||||
type_table,
|
||||
handler,
|
||||
await_checker: AwaitChecker::new(max_depth, !disabled),
|
||||
scope_index: 0,
|
||||
current_program: None,
|
||||
variant: None,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of the current scope.
|
||||
/// Note that if we are in the midst of unrolling an IterationStatement, a new scope is created.
|
||||
pub(crate) fn current_scope_index(&mut self) -> usize {
|
||||
self.scope_index
|
||||
}
|
||||
|
||||
/// Enters a child scope.
|
||||
pub(crate) fn enter_scope(&mut self, index: usize) -> usize {
|
||||
let previous_symbol_table = std::mem::take(&mut self.symbol_table);
|
||||
self.symbol_table.swap(previous_symbol_table.borrow().lookup_scope_by_index(index).unwrap());
|
||||
self.symbol_table.borrow_mut().parent = Some(Box::new(previous_symbol_table.into_inner()));
|
||||
|
||||
core::mem::replace(&mut self.scope_index, 0)
|
||||
}
|
||||
|
||||
/// Exits the current block scope.
|
||||
pub(crate) fn exit_scope(&mut self, index: usize) {
|
||||
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
|
||||
self.symbol_table.swap(prev_st.lookup_scope_by_index(index).unwrap());
|
||||
self.symbol_table = RefCell::new(prev_st);
|
||||
|
||||
self.scope_index = index + 1;
|
||||
}
|
||||
|
||||
/// Emits a type checker error.
|
||||
pub(crate) fn emit_err(&self, err: StaticAnalyzerError) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
/// Emits a type checker warning
|
||||
pub fn emit_warning(&self, warning: StaticAnalyzerWarning) {
|
||||
self.handler.emit_warning(warning.into());
|
||||
}
|
||||
|
||||
/// Type checks the awaiting of a future.
|
||||
pub(crate) fn assert_future_await(&mut self, future: &Option<&Expression>, span: Span) {
|
||||
// Make sure that it is an identifier expression.
|
||||
let future_variable = match future {
|
||||
Some(Expression::Identifier(name)) => name,
|
||||
_ => {
|
||||
return self.emit_err(StaticAnalyzerError::invalid_await_call(span));
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure that the future is defined.
|
||||
match self.symbol_table.borrow().lookup_variable(Location::new(None, future_variable.name)) {
|
||||
Some(var) => {
|
||||
if !matches!(&var.type_, &Type::Future(_)) {
|
||||
self.emit_err(StaticAnalyzerError::expected_future(future_variable.name, future_variable.span()));
|
||||
}
|
||||
// Mark the future as consumed.
|
||||
self.await_checker.remove(future_variable);
|
||||
}
|
||||
None => {
|
||||
self.emit_err(StaticAnalyzerError::expected_future(future_variable.name, future_variable.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
use crate::ConditionalTreeNode;
|
||||
use indexmap::IndexSet;
|
||||
use leo_ast::Identifier;
|
||||
use leo_errors::TypeCheckerWarning;
|
||||
use leo_errors::StaticAnalyzerWarning;
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
// TODO: Could optimize by removing duplicate paths (if set of futures is the same).
|
||||
@ -65,14 +65,14 @@ impl AwaitChecker {
|
||||
&mut self,
|
||||
is_finalize: bool,
|
||||
input: Span,
|
||||
) -> Result<Vec<ConditionalTreeNode>, TypeCheckerWarning> {
|
||||
) -> Result<Vec<ConditionalTreeNode>, StaticAnalyzerWarning> {
|
||||
if is_finalize && self.enabled {
|
||||
let mut current_nodes = Vec::new();
|
||||
// Extend all paths by one node to represent the upcoming `then` branch.
|
||||
for node in self.to_await.iter() {
|
||||
// Error if exceed maximum depth.
|
||||
if node.depth > self.max_depth {
|
||||
return Err(TypeCheckerWarning::max_conditional_block_depth_exceeded(self.max_depth, input));
|
||||
return Err(StaticAnalyzerWarning::max_conditional_block_depth_exceeded(self.max_depth, input));
|
||||
}
|
||||
// Extend current path.
|
||||
current_nodes.push(node.clone().create_child());
|
46
compiler/passes/src/static_analysis/mod.rs
Normal file
46
compiler/passes/src/static_analysis/mod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod await_checker;
|
||||
|
||||
pub mod analyze_expression;
|
||||
|
||||
pub mod analyze_program;
|
||||
|
||||
pub mod analyze_statement;
|
||||
|
||||
pub mod analyzer;
|
||||
pub use analyzer::*;
|
||||
|
||||
use crate::{CallGraph, Pass, StructGraph, SymbolTable, TypeTable};
|
||||
|
||||
use leo_ast::{Ast, ProgramVisitor};
|
||||
use leo_errors::{Result, emitter::Handler};
|
||||
|
||||
use snarkvm::prelude::Network;
|
||||
|
||||
impl<'a, N: Network> Pass for StaticAnalyzer<'a, N> {
|
||||
type Input = (&'a Ast, &'a Handler, SymbolTable, &'a TypeTable, usize, bool);
|
||||
type Output = Result<SymbolTable>;
|
||||
|
||||
fn do_pass((ast, handler, st, tt, max_depth, await_checking): Self::Input) -> Self::Output {
|
||||
let mut visitor = StaticAnalyzer::<N>::new(st, tt, handler, max_depth, await_checking);
|
||||
visitor.visit_program(ast.as_repr());
|
||||
// TODO: Print the warnings.
|
||||
handler.last_err().map_err(|e| *e)?;
|
||||
Ok(visitor.symbol_table.take())
|
||||
}
|
||||
}
|
@ -124,9 +124,7 @@ impl<'a, N: Network> ExpressionVisitor<'a> for TypeChecker<'a, N> {
|
||||
self.emit_err(TypeCheckerError::can_only_await_one_future_at_a_time(access.span));
|
||||
return Some(Type::Unit);
|
||||
}
|
||||
self.assert_future_await(&access.arguments.first(), input.span());
|
||||
}
|
||||
|
||||
return return_type;
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::invalid_core_function_call(access, access.span()));
|
||||
|
@ -17,7 +17,7 @@
|
||||
use crate::{DiGraphError, TypeChecker};
|
||||
|
||||
use leo_ast::{Type, *};
|
||||
use leo_errors::{TypeCheckerError, TypeCheckerWarning};
|
||||
use leo_errors::TypeCheckerError;
|
||||
use leo_span::sym;
|
||||
|
||||
use snarkvm::console::network::Network;
|
||||
@ -262,19 +262,6 @@ impl<'a, N: Network> ProgramVisitor<'a> for TypeChecker<'a, N> {
|
||||
// Query helper function to type check function parameters and outputs.
|
||||
self.check_function_signature(function);
|
||||
|
||||
if self.scope_state.variant == Some(Variant::AsyncFunction) {
|
||||
// Initialize the list of input futures. Each one must be awaited before the end of the function.
|
||||
self.await_checker.set_futures(
|
||||
function
|
||||
.input
|
||||
.iter()
|
||||
.filter_map(|input| {
|
||||
if let Type::Future(_) = input.type_.clone() { Some(input.identifier.name) } else { None }
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
if function.variant == Variant::Function && function.input.is_empty() {
|
||||
self.emit_err(TypeCheckerError::empty_function_arglist(function.span));
|
||||
}
|
||||
@ -296,59 +283,6 @@ impl<'a, N: Network> ProgramVisitor<'a> for TypeChecker<'a, N> {
|
||||
if self.scope_state.variant == Some(Variant::AsyncTransition) && !self.scope_state.has_called_finalize {
|
||||
self.emit_err(TypeCheckerError::async_transition_must_call_async_function(function.span));
|
||||
}
|
||||
|
||||
// Check that all futures were awaited exactly once.
|
||||
if self.scope_state.variant == Some(Variant::AsyncFunction) {
|
||||
// Throw error if not all futures awaits even appear once.
|
||||
if !self.await_checker.static_to_await.is_empty() {
|
||||
self.emit_err(TypeCheckerError::future_awaits_missing(
|
||||
self.await_checker
|
||||
.static_to_await
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|f| f.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
function.span(),
|
||||
));
|
||||
} else if self.await_checker.enabled && !self.await_checker.to_await.is_empty() {
|
||||
// Tally up number of paths that are unawaited and number of paths that are awaited more than once.
|
||||
let (num_paths_unawaited, num_paths_duplicate_awaited, num_perfect) =
|
||||
self.await_checker.to_await.iter().fold((0, 0, 0), |(unawaited, duplicate, perfect), path| {
|
||||
(
|
||||
unawaited + if !path.elements.is_empty() { 1 } else { 0 },
|
||||
duplicate + if path.counter > 0 { 1 } else { 0 },
|
||||
perfect + if path.counter > 0 || !path.elements.is_empty() { 0 } else { 1 },
|
||||
)
|
||||
});
|
||||
|
||||
// Throw error if there does not exist a path in which all futures are awaited exactly once.
|
||||
if num_perfect == 0 {
|
||||
self.emit_err(TypeCheckerError::no_path_awaits_all_futures_exactly_once(
|
||||
self.await_checker.to_await.len(),
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
|
||||
// Throw warning if some futures are awaited more than once in some paths.
|
||||
if num_paths_unawaited > 0 {
|
||||
self.emit_warning(TypeCheckerWarning::some_paths_do_not_await_all_futures(
|
||||
self.await_checker.to_await.len(),
|
||||
num_paths_unawaited,
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
|
||||
// Throw warning if not all futures are awaited in some paths.
|
||||
if num_paths_duplicate_awaited > 0 {
|
||||
self.emit_warning(TypeCheckerWarning::some_paths_contain_duplicate_future_awaits(
|
||||
self.await_checker.to_await.len(),
|
||||
num_paths_duplicate_awaited,
|
||||
function.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use crate::{ConditionalTreeNode, TypeChecker, VariableSymbol, VariableType};
|
||||
use crate::{TypeChecker, VariableSymbol, VariableType};
|
||||
|
||||
use leo_ast::{
|
||||
Type::{Future, Tuple},
|
||||
@ -133,26 +133,12 @@ impl<'a, N: Network> StatementVisitor<'a> for TypeChecker<'a, N> {
|
||||
// Set the `is_conditional` flag.
|
||||
let previous_is_conditional = core::mem::replace(&mut self.scope_state.is_conditional, true);
|
||||
|
||||
// Create scope for checking awaits in `then` branch of conditional.
|
||||
let current_bst_nodes: Vec<ConditionalTreeNode> = match self
|
||||
.await_checker
|
||||
.create_then_scope(self.scope_state.variant == Some(Variant::AsyncFunction), input.span)
|
||||
{
|
||||
Ok(nodes) => nodes,
|
||||
Err(warn) => return self.emit_warning(warn),
|
||||
};
|
||||
|
||||
// Visit block.
|
||||
self.visit_block(&input.then);
|
||||
|
||||
// Store the `has_return` flag for the then-block.
|
||||
then_block_has_return = self.scope_state.has_return;
|
||||
|
||||
// Exit scope for checking awaits in `then` branch of conditional.
|
||||
let saved_paths = self
|
||||
.await_checker
|
||||
.exit_then_scope(self.scope_state.variant == Some(Variant::AsyncFunction), current_bst_nodes);
|
||||
|
||||
if let Some(otherwise) = &input.otherwise {
|
||||
// Set the `has_return` flag for the otherwise-block.
|
||||
self.scope_state.has_return = otherwise_block_has_return;
|
||||
@ -170,9 +156,6 @@ impl<'a, N: Network> StatementVisitor<'a> for TypeChecker<'a, N> {
|
||||
otherwise_block_has_return = self.scope_state.has_return;
|
||||
}
|
||||
|
||||
// Update the set of all possible BST paths.
|
||||
self.await_checker.exit_statement_scope(self.scope_state.variant == Some(Variant::AsyncFunction), saved_paths);
|
||||
|
||||
// Restore the previous `has_return` flag.
|
||||
self.scope_state.has_return = previous_has_return || (then_block_has_return && otherwise_block_has_return);
|
||||
// Restore the previous `is_conditional` flag.
|
||||
|
@ -21,7 +21,7 @@ use crate::{
|
||||
TypeTable,
|
||||
VariableSymbol,
|
||||
VariableType,
|
||||
type_checking::{await_checker::AwaitChecker, scope_state::ScopeState},
|
||||
type_checking::scope_state::ScopeState,
|
||||
};
|
||||
|
||||
use leo_ast::*;
|
||||
@ -47,8 +47,6 @@ pub struct TypeChecker<'a, N: Network> {
|
||||
pub(crate) handler: &'a Handler,
|
||||
/// The state of the current scope being traversed.
|
||||
pub(crate) scope_state: ScopeState,
|
||||
/// Struct to store the state relevant to checking all futures are awaited.
|
||||
pub(crate) await_checker: AwaitChecker,
|
||||
/// Mapping from async function name to the inferred input types.
|
||||
pub(crate) async_function_input_types: IndexMap<Location, Vec<Type>>,
|
||||
/// The set of used composites.
|
||||
@ -103,13 +101,7 @@ const MAGNITUDE_TYPES: [Type; 3] =
|
||||
|
||||
impl<'a, N: Network> TypeChecker<'a, N> {
|
||||
/// Returns a new type checker given a symbol table and error handler.
|
||||
pub fn new(
|
||||
symbol_table: SymbolTable,
|
||||
type_table: &'a TypeTable,
|
||||
handler: &'a Handler,
|
||||
max_depth: usize,
|
||||
disabled: bool,
|
||||
) -> Self {
|
||||
pub fn new(symbol_table: SymbolTable, type_table: &'a TypeTable, handler: &'a Handler) -> Self {
|
||||
let struct_names = symbol_table.structs.keys().map(|loc| loc.name).collect();
|
||||
let function_names = symbol_table.functions.keys().map(|loc| loc.name).collect();
|
||||
|
||||
@ -121,7 +113,6 @@ impl<'a, N: Network> TypeChecker<'a, N> {
|
||||
call_graph: CallGraph::new(function_names),
|
||||
handler,
|
||||
scope_state: ScopeState::new(),
|
||||
await_checker: AwaitChecker::new(max_depth, !disabled),
|
||||
async_function_input_types: IndexMap::new(),
|
||||
used_structs: IndexSet::new(),
|
||||
phantom: Default::default(),
|
||||
@ -1308,31 +1299,6 @@ impl<'a, N: Network> TypeChecker<'a, N> {
|
||||
struct_
|
||||
}
|
||||
|
||||
/// Type checks the awaiting of a future.
|
||||
pub(crate) fn assert_future_await(&mut self, future: &Option<&Expression>, span: Span) {
|
||||
// Make sure that it is an identifier expression.
|
||||
let future_variable = match future {
|
||||
Some(Expression::Identifier(name)) => name,
|
||||
_ => {
|
||||
return self.emit_err(TypeCheckerError::invalid_await_call(span));
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure that the future is defined.
|
||||
match self.symbol_table.borrow().lookup_variable(Location::new(None, future_variable.name)) {
|
||||
Some(var) => {
|
||||
if !matches!(&var.type_, &Type::Future(_)) {
|
||||
self.emit_err(TypeCheckerError::expected_future(future_variable.name, future_variable.span()));
|
||||
}
|
||||
// Mark the future as consumed.
|
||||
self.await_checker.remove(future_variable);
|
||||
}
|
||||
None => {
|
||||
self.emit_err(TypeCheckerError::expected_future(future_variable.name, future_variable.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts variable to symbol table.
|
||||
pub(crate) fn insert_variable(
|
||||
&mut self,
|
||||
|
@ -14,8 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod await_checker;
|
||||
|
||||
pub mod check_expressions;
|
||||
|
||||
pub mod check_program;
|
||||
@ -36,11 +34,11 @@ use leo_errors::{Result, emitter::Handler};
|
||||
use snarkvm::prelude::Network;
|
||||
|
||||
impl<'a, N: Network> Pass for TypeChecker<'a, N> {
|
||||
type Input = (&'a Ast, &'a Handler, SymbolTable, &'a TypeTable, usize, bool);
|
||||
type Input = (&'a Ast, &'a Handler, SymbolTable, &'a TypeTable);
|
||||
type Output = Result<(SymbolTable, StructGraph, CallGraph)>;
|
||||
|
||||
fn do_pass((ast, handler, st, tt, max_depth, await_checking): Self::Input) -> Self::Output {
|
||||
let mut visitor = TypeChecker::<N>::new(st, tt, handler, max_depth, await_checking);
|
||||
fn do_pass((ast, handler, st, tt): Self::Input) -> Self::Output {
|
||||
let mut visitor = TypeChecker::<N>::new(st, tt, handler);
|
||||
visitor.visit_program(ast.as_repr());
|
||||
handler.last_err().map_err(|e| *e)?;
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::create_errors;
|
||||
|
||||
use std::{
|
||||
error::Error as ErrorArg,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
create_errors!(
|
||||
/// ImportError enum that represents all the errors for the `leo-import` crate.
|
||||
ImportError,
|
||||
exit_code_mask: 4000i32,
|
||||
error_code_prefix: "IMP",
|
||||
);
|
@ -45,6 +45,10 @@ pub use self::package::*;
|
||||
pub mod parser;
|
||||
pub use self::parser::*;
|
||||
|
||||
/// Contains the Static Analyzer error definitions.
|
||||
pub mod static_analyzer;
|
||||
pub use self::static_analyzer::*;
|
||||
|
||||
/// Contains the Type Checker error definitions.
|
||||
pub mod type_checker;
|
||||
pub use self::type_checker::*;
|
||||
@ -72,6 +76,9 @@ pub enum LeoError {
|
||||
/// Represents a Parser Error in a Leo Error.
|
||||
#[error(transparent)]
|
||||
ParserError(#[from] ParserError),
|
||||
/// Represents a Static Analyzer Error in a Leo Error.
|
||||
#[error(transparent)]
|
||||
StaticAnalyzerError(#[from] StaticAnalyzerError),
|
||||
/// Represents a Type Checker Error in a Leo Error.
|
||||
#[error(transparent)]
|
||||
TypeCheckerError(#[from] TypeCheckerError),
|
||||
@ -104,6 +111,7 @@ impl LeoError {
|
||||
CliError(error) => error.error_code(),
|
||||
ParserError(error) => error.error_code(),
|
||||
PackageError(error) => error.error_code(),
|
||||
StaticAnalyzerError(error) => error.error_code(),
|
||||
TypeCheckerError(error) => error.error_code(),
|
||||
LoopUnrollerError(error) => error.error_code(),
|
||||
FlattenError(error) => error.error_code(),
|
||||
@ -123,6 +131,7 @@ impl LeoError {
|
||||
CliError(error) => error.exit_code(),
|
||||
ParserError(error) => error.exit_code(),
|
||||
PackageError(error) => error.exit_code(),
|
||||
StaticAnalyzerError(error) => error.exit_code(),
|
||||
TypeCheckerError(error) => error.exit_code(),
|
||||
LoopUnrollerError(error) => error.exit_code(),
|
||||
FlattenError(error) => error.exit_code(),
|
||||
@ -140,6 +149,9 @@ pub enum LeoWarning {
|
||||
/// Represents an Parser Warning in a Leo Warning.
|
||||
#[error(transparent)]
|
||||
ParserWarning(#[from] ParserWarning),
|
||||
/// Represents a Static Analyzer Warning in a Leo Warning.
|
||||
#[error(transparent)]
|
||||
StaticAnalyzerWarning(#[from] StaticAnalyzerWarning),
|
||||
/// Represents a Type Checker Warning in a Leo Warning.
|
||||
#[error(transparent)]
|
||||
TypeCheckerWarning(#[from] TypeCheckerWarning),
|
||||
@ -153,6 +165,7 @@ impl LeoWarning {
|
||||
match self {
|
||||
ParserWarning(warning) => warning.warning_code(),
|
||||
TypeCheckerWarning(warning) => warning.warning_code(),
|
||||
StaticAnalyzerWarning(warning) => warning.warning_code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/// This module contains the Import error definitions.
|
||||
pub mod import_errors;
|
||||
pub use self::import_errors::*;
|
||||
/// This module contains the static analysis error definitions.
|
||||
pub mod static_analyzer_error;
|
||||
pub use self::static_analyzer_error::*;
|
||||
|
||||
pub mod static_analyzer_warning;
|
||||
pub use self::static_analyzer_warning::*;
|
55
errors/src/errors/static_analyzer/static_analyzer_error.rs
Normal file
55
errors/src/errors/static_analyzer/static_analyzer_error.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::create_messages;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
// TODO: Consolidate errors.
|
||||
|
||||
create_messages!(
|
||||
/// StaticAnalyzer enum that represents all the errors for static analysis.
|
||||
StaticAnalyzerError,
|
||||
code_mask: 4000i32,
|
||||
code_prefix: "SAZ",
|
||||
|
||||
@formatted
|
||||
no_path_awaits_all_futures_exactly_once {
|
||||
args: (num_total_paths: impl Display),
|
||||
msg: format!("Futures must be awaited exactly once. Out of `{num_total_paths}`, there does not exist a single path in which all futures are awaited exactly once."),
|
||||
help: Some("Ex: for `f: Future` call `f.await()` to await a future. Remove duplicate future await redundancies, and add future awaits for un-awaited futures.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
future_awaits_missing {
|
||||
args: (unawaited: impl Display),
|
||||
msg: format!("The following futures were never awaited: {unawaited}"),
|
||||
help: Some("Ex: for `f: Future` call `f.await()` to await a future.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
invalid_await_call {
|
||||
args: (),
|
||||
msg: "Not a valid await call.".to_string(),
|
||||
help: Some("Ex: for `f: Future` call `f.await()` or `Future::await(f)` to await a future.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
expected_future {
|
||||
args: (type_: impl Display),
|
||||
msg: format!("Expected a future, but found `{type_}`"),
|
||||
help: Some("Only futures can be awaited.".to_string()),
|
||||
}
|
||||
);
|
47
errors/src/errors/static_analyzer/static_analyzer_warning.rs
Normal file
47
errors/src/errors/static_analyzer/static_analyzer_warning.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2019-2024 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::create_messages;
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
create_messages!(
|
||||
/// ParserWarning enum that represents all the warnings for static analysis
|
||||
StaticAnalyzerWarning,
|
||||
code_mask: 4000i32,
|
||||
code_prefix: "SAZ",
|
||||
|
||||
@formatted
|
||||
some_paths_do_not_await_all_futures {
|
||||
args: (num_total_paths: impl Display, num_unawaited_paths: impl Display),
|
||||
msg: format!("Not all paths through the function await all futures. {num_unawaited_paths}/{num_total_paths} paths contain at least one future that is never awaited."),
|
||||
help: Some("Ex: `f.await()` to await a future. Remove this warning by including the `--disable-conditional-branch-type-checking` flag.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
some_paths_contain_duplicate_future_awaits {
|
||||
args: (num_total_paths: impl Display, num_duplicate_await_paths: impl Display),
|
||||
msg: format!("Some paths through the function contain duplicate future awaits. {num_duplicate_await_paths}/{num_total_paths} paths contain at least one future that is awaited more than once."),
|
||||
help: Some("Look at the times `.await()` is called, and try to reduce redundancies. Remove this warning by including the `--disable-conditional-branch-type-checking` flag.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
max_conditional_block_depth_exceeded {
|
||||
args: (max: impl Display),
|
||||
msg: format!("The type checker has exceeded the max depth of nested conditional blocks: {max}."),
|
||||
help: Some("Re-run with a larger maximum depth using the `--conditional_block_max_depth` build option. Ex: `leo run main --conditional_block_max_depth 25`.".to_string()),
|
||||
}
|
||||
);
|
@ -742,6 +742,7 @@ create_messages!(
|
||||
help: Some(" Future arguments must be addressed by their index. Ex: `f.1.3`.".to_string()),
|
||||
}
|
||||
|
||||
// TODO: This error is deprecated. Remove.
|
||||
@formatted
|
||||
no_path_awaits_all_futures_exactly_once {
|
||||
args: (num_total_paths: impl Display),
|
||||
@ -749,6 +750,7 @@ create_messages!(
|
||||
help: Some("Ex: for `f: Future` call `f.await()` to await a future. Remove duplicate future await redundancies, and add future awaits for un-awaited futures.".to_string()),
|
||||
}
|
||||
|
||||
// TODO: This error is deprecated. Remove.
|
||||
@formatted
|
||||
future_awaits_missing {
|
||||
args: (unawaited: impl Display),
|
||||
@ -763,6 +765,7 @@ create_messages!(
|
||||
help: Some("Futures can only be defined as the result of async calls.".to_string()),
|
||||
}
|
||||
|
||||
// TODO: This error is deprecated. Remove.
|
||||
@formatted
|
||||
invalid_await_call {
|
||||
args: (),
|
||||
@ -777,6 +780,7 @@ create_messages!(
|
||||
help: Some("Ex: for `f: Future` call `f.await()` or `Future::await(f)` to await a future.".to_string()),
|
||||
}
|
||||
|
||||
// TODO: This error is deprecated. Remove.
|
||||
@formatted
|
||||
expected_future {
|
||||
args: (type_: impl Display),
|
||||
|
@ -38,6 +38,7 @@ create_messages!(
|
||||
help: Some("Look at the times `.await()` is called, and try to reduce redundancies. Remove this warning by including the `--disable-conditional-branch-type-checking` flag.".to_string()),
|
||||
}
|
||||
|
||||
// TODO: This warning is deprecated, remove it in the future.
|
||||
@formatted
|
||||
async_function_is_never_called_by_transition_function {
|
||||
args: (name: impl Display),
|
||||
|
Loading…
Reference in New Issue
Block a user