mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 10:12:21 +03:00
Add typechecking for inline functions
This commit is contained in:
parent
6a3039277e
commit
b3ef6f79c3
@ -447,13 +447,16 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
if let Some(func) = func {
|
||||
// Check that the call is valid.
|
||||
match self.is_transition_function {
|
||||
// If the function is not a transition function, it cannot call any other functions.
|
||||
false => {
|
||||
self.emit_err(TypeCheckerError::cannot_invoke_call_from_standard_function(input.span));
|
||||
// Note that this unwrap is safe since we always set the variant before traversing the body of the function.
|
||||
match self.variant.unwrap() {
|
||||
// If the function is not a transition function, it can only call "inline" functions.
|
||||
Variant::Inline | Variant::Standard => {
|
||||
if !matches!(func.variant, Variant::Inline) {
|
||||
self.emit_err(TypeCheckerError::can_only_call_inline_function(input.span));
|
||||
}
|
||||
}
|
||||
// If the function is a transition function, then check that the call is not to another local transition function.
|
||||
true => {
|
||||
Variant::Transition => {
|
||||
if matches!(func.variant, Variant::Transition) && input.external.is_none() {
|
||||
self.emit_err(TypeCheckerError::cannot_invoke_call_to_local_transition_function(
|
||||
input.span,
|
||||
|
@ -181,7 +181,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
|
||||
}
|
||||
|
||||
self.is_transition_function = matches!(function.variant, Variant::Transition);
|
||||
self.variant = Some(function.variant);
|
||||
|
||||
// Lookup function metadata in the symbol table.
|
||||
// Note that this unwrap is safe since function metadata is stored in a prior pass.
|
||||
@ -216,13 +216,14 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input_var.span()))
|
||||
}
|
||||
|
||||
match self.is_transition_function {
|
||||
// Note that this unwrap is safe since we assign to `self.variant` above.
|
||||
match self.variant.unwrap() {
|
||||
// If the function is a transition function, then check that the parameter mode is not a constant.
|
||||
true if input_var.mode() == Mode::Const => self.emit_err(
|
||||
Variant::Transition if input_var.mode() == Mode::Const => self.emit_err(
|
||||
TypeCheckerError::transition_function_inputs_cannot_be_const(input_var.span()),
|
||||
),
|
||||
// If the function is not a transition function, then check that the parameters do not have an associated mode.
|
||||
false if input_var.mode() != Mode::None => self.emit_err(
|
||||
Variant::Standard | Variant::Inline if input_var.mode() != Mode::None => self.emit_err(
|
||||
TypeCheckerError::regular_function_inputs_cannot_have_modes(input_var.span()),
|
||||
),
|
||||
_ => {} // Do nothing.
|
||||
@ -248,7 +249,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
Output::External(external) => {
|
||||
// If the function is not a transition function, then it cannot output a record.
|
||||
// Note that an external output must always be a record.
|
||||
if !self.is_transition_function {
|
||||
if !matches!(function.variant, Variant::Transition) {
|
||||
self.emit_err(TypeCheckerError::function_cannot_output_record(external.span()));
|
||||
}
|
||||
// Otherwise, do not type check external record function outputs.
|
||||
@ -259,7 +260,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
self.assert_type_is_defined(&function_output.type_, function_output.span);
|
||||
// If the function is not a transition function, then it cannot output a record.
|
||||
if let Type::Identifier(identifier) = function_output.type_ {
|
||||
if !self.is_transition_function
|
||||
if !matches!(function.variant, Variant::Transition)
|
||||
&& self
|
||||
.symbol_table
|
||||
.borrow()
|
||||
@ -307,7 +308,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
self.has_finalize = false;
|
||||
|
||||
// Check that the function is a transition function.
|
||||
if !self.is_transition_function {
|
||||
if !matches!(function.variant, Variant::Transition) {
|
||||
self.emit_err(TypeCheckerError::only_transition_functions_can_have_finalize(
|
||||
finalize.span,
|
||||
));
|
||||
@ -393,7 +394,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
// Exit the function's scope.
|
||||
self.exit_scope(function_index);
|
||||
|
||||
// Unset `is_transition_function` flag.
|
||||
self.is_transition_function = false;
|
||||
// Unset the `variant`.
|
||||
self.variant = None;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use crate::{CallGraph, StructGraph, SymbolTable};
|
||||
|
||||
use leo_ast::{Identifier, IntegerType, Node, Type};
|
||||
use leo_ast::{Identifier, IntegerType, Node, Type, Variant};
|
||||
use leo_core::*;
|
||||
use leo_errors::{emitter::Handler, TypeCheckerError};
|
||||
use leo_span::{Span, Symbol};
|
||||
@ -35,12 +35,13 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) handler: &'a Handler,
|
||||
/// The name of the function that we are currently traversing.
|
||||
pub(crate) function: Option<Symbol>,
|
||||
/// The variant of the function that we are currently traversing.
|
||||
pub(crate) variant: Option<Variant>,
|
||||
/// Whether or not the function that we are currently traversing has a return statement.
|
||||
pub(crate) has_return: bool,
|
||||
/// Whether or not the function that we are currently traversing invokes the finalize block.
|
||||
pub(crate) has_finalize: bool,
|
||||
/// Whether or not we are currently traversing a transition function.
|
||||
pub(crate) is_transition_function: bool,
|
||||
|
||||
/// Whether or not we are currently traversing a finalize block.
|
||||
pub(crate) is_finalize: bool,
|
||||
/// Whether or not we are currently traversing an imported program.
|
||||
@ -105,9 +106,9 @@ impl<'a> TypeChecker<'a> {
|
||||
call_graph: CallGraph::new(function_names),
|
||||
handler,
|
||||
function: None,
|
||||
variant: None,
|
||||
has_return: false,
|
||||
has_finalize: false,
|
||||
is_transition_function: false,
|
||||
is_finalize: false,
|
||||
is_imported: false,
|
||||
is_return: false,
|
||||
|
@ -412,10 +412,11 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
// TODO: Is this error message clear?
|
||||
@formatted
|
||||
cannot_invoke_call_from_standard_function {
|
||||
can_only_call_inline_function {
|
||||
args: (),
|
||||
msg: format!("Cannot call another function from a standard function."),
|
||||
msg: format!("Only `inline` can be called from a `function` or `inline`."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user