mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 15:15:47 +03:00
Implement check for cyclic call graph
This commit is contained in:
parent
b17702e019
commit
f06b83c7f4
@ -481,6 +481,13 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
self.visit_expression(argument, &Some(expected.type_()));
|
||||
});
|
||||
|
||||
// Add the call to the call graph.
|
||||
let caller_name = match self.function {
|
||||
None => unreachable!("`self.function` is set every time a function is visited."),
|
||||
Some(func) => func,
|
||||
};
|
||||
self.call_graph.add_edge(caller_name, ident.name);
|
||||
|
||||
Some(ret)
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::unknown_sym("function", ident.name, ident.span()));
|
||||
|
@ -75,6 +75,11 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the call graph does not have any cycles.
|
||||
if let Err(GraphError::CycleDetected(path)) = self.call_graph.post_order() {
|
||||
self.emit_err(TypeCheckerError::cyclic_function_dependency(path));
|
||||
}
|
||||
|
||||
// TODO: Use the snarkVM configurations to parameterize the check, need similar checks for structs (all in separate PR)
|
||||
// Check that the number of transitions does not exceed the maximum.
|
||||
if transition_count > 15 {
|
||||
|
@ -14,7 +14,7 @@
|
||||
// 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::{StructGraph, SymbolTable};
|
||||
use crate::{CallGraph, StructGraph, SymbolTable};
|
||||
|
||||
use leo_ast::{Identifier, IntegerType, Node, Type};
|
||||
use leo_core::*;
|
||||
@ -29,6 +29,8 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) symbol_table: RefCell<SymbolTable>,
|
||||
/// A dependency graph of the structs in program.
|
||||
pub(crate) struct_graph: StructGraph,
|
||||
/// The call graph for the program.
|
||||
pub(crate) call_graph: CallGraph,
|
||||
/// The error handler.
|
||||
pub(crate) handler: &'a Handler,
|
||||
/// The name of the function that we are currently traversing.
|
||||
@ -95,9 +97,12 @@ impl<'a> TypeChecker<'a> {
|
||||
pub fn new(symbol_table: SymbolTable, handler: &'a Handler) -> Self {
|
||||
let struct_names = symbol_table.structs.keys().cloned().collect();
|
||||
|
||||
let function_names = symbol_table.functions.keys().cloned().collect();
|
||||
|
||||
Self {
|
||||
symbol_table: RefCell::new(symbol_table),
|
||||
struct_graph: StructGraph::new(struct_names),
|
||||
call_graph: CallGraph::new(function_names),
|
||||
handler,
|
||||
function: None,
|
||||
has_return: false,
|
||||
|
@ -27,20 +27,20 @@ pub use check_statements::*;
|
||||
pub mod checker;
|
||||
pub use checker::*;
|
||||
|
||||
use crate::{Pass, StructGraph, SymbolTable};
|
||||
use crate::{CallGraph, Pass, StructGraph, SymbolTable};
|
||||
|
||||
use leo_ast::{Ast, ProgramVisitor};
|
||||
use leo_errors::{emitter::Handler, Result};
|
||||
|
||||
impl<'a> Pass for TypeChecker<'a> {
|
||||
type Input = (&'a Ast, &'a Handler, SymbolTable);
|
||||
type Output = Result<(SymbolTable, StructGraph)>;
|
||||
type Output = Result<(SymbolTable, StructGraph, CallGraph)>;
|
||||
|
||||
fn do_pass((ast, handler, st): Self::Input) -> Self::Output {
|
||||
let mut visitor = TypeChecker::new(st, handler);
|
||||
visitor.visit_program(ast.as_repr());
|
||||
handler.last_err().map_err(|e| *e)?;
|
||||
|
||||
Ok((visitor.symbol_table.take(), visitor.struct_graph))
|
||||
Ok((visitor.symbol_table.take(), visitor.struct_graph, visitor.call_graph))
|
||||
}
|
||||
}
|
||||
|
@ -549,4 +549,14 @@ create_messages!(
|
||||
},
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
cyclic_function_dependency {
|
||||
args: (path: Vec<impl Display>),
|
||||
msg: {
|
||||
let path_string = path.into_iter().map(|name| format!("`{name}`")).collect::<Vec<String>>().join(" --> ");
|
||||
format!("Cyclic dependency between functions: {path_string}")
|
||||
},
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user