Add typechecking for inline functions

This commit is contained in:
d0cd 2023-02-08 17:09:17 -08:00
parent 6a3039277e
commit b3ef6f79c3
4 changed files with 26 additions and 20 deletions

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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,
}