mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 23:23:50 +03:00
impl method call type checking
This commit is contained in:
parent
f0a1573058
commit
5dba9cf07f
@ -28,7 +28,7 @@ pub struct Function {
|
||||
pub identifier: Identifier,
|
||||
/// The function's parameters.
|
||||
pub input: Vec<FunctionInput>,
|
||||
/// The function return type, if explicitly specified, or `()` if not.
|
||||
/// The function's required return type.
|
||||
pub output: Type,
|
||||
/// Any mapping to the core library.
|
||||
/// Always `None` when initially parsed.
|
||||
|
@ -100,7 +100,11 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
fn visit_method(&mut self, input: &'a MethodCallExpression, additional: &Self::AdditionalInput) -> Option<Self::Output> {
|
||||
fn visit_method(
|
||||
&mut self,
|
||||
input: &'a MethodCallExpression,
|
||||
additional: &Self::AdditionalInput,
|
||||
) -> Option<Self::Output> {
|
||||
self.visit_expression(&input.receiver, additional);
|
||||
if let VisitResult::VisitChildren = self.visitor_ref().visit_method(input) {
|
||||
input.arguments.iter().for_each(|expr| {
|
||||
|
@ -25,6 +25,9 @@ pub use account::*;
|
||||
mod algorithms;
|
||||
pub use algorithms::*;
|
||||
|
||||
mod methods;
|
||||
pub use methods::*;
|
||||
|
||||
pub trait Types {
|
||||
fn types() -> IndexSet<Symbol>;
|
||||
}
|
||||
|
83
compiler/core/src/methods/mod.rs
Normal file
83
compiler/core/src/methods/mod.rs
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2019-2021 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/>.
|
||||
|
||||
//! This module defines AVM opcodes as associated method calls on Leo types.
|
||||
use indexmap::IndexSet;
|
||||
use leo_span::Symbol;
|
||||
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Method {
|
||||
// todo @collinc97: this struct contains type checking info and does not consider opcodes.
|
||||
/// The name of the method as an interned string.
|
||||
pub name: Symbol,
|
||||
/// This count excludes the receiver e.g, `1u8.add(2u8)` contains 1 argument.
|
||||
pub num_arguments: usize,
|
||||
/// `true` if the receiver type == arguments type == return type
|
||||
pub types_are_equal: bool,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
pub(crate) fn new(name: &str, num_arguments: usize, types_are_equal: bool) -> Self {
|
||||
Self {
|
||||
name: Symbol::intern(name),
|
||||
num_arguments,
|
||||
types_are_equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use indexmap::{indexmap, indexset, IndexMap};
|
||||
use leo_ast::Type;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct MethodTable {
|
||||
/// Maps method name => method definition.
|
||||
method_definitions: IndexMap<Symbol, Method>,
|
||||
/// Supported methods for the field type.
|
||||
field_methods: IndexSet<Symbol>,
|
||||
}
|
||||
|
||||
impl MethodTable {
|
||||
pub fn load() -> Self {
|
||||
// Initialize method definitions.
|
||||
let add = Method::new("add", 1, true);
|
||||
|
||||
// Initialize associated methods for each type.
|
||||
Self {
|
||||
field_methods: indexset! {
|
||||
add.name
|
||||
},
|
||||
method_definitions: indexmap! {
|
||||
add.name => add
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the method corresponding to the given symbol.
|
||||
/// Used during type checking.
|
||||
pub fn lookup_method(&self, symbol: &Symbol) -> Option<&Method> {
|
||||
self.method_definitions.get(symbol)
|
||||
}
|
||||
|
||||
/// Returns `true` if the associated method exists for the given type.
|
||||
/// Sometimes used during type checking if the receiver type is known.
|
||||
pub fn type_method_is_supported(&self, type_: &Type, method: &Symbol) -> bool {
|
||||
match type_ {
|
||||
Type::Field => self.field_methods.contains(method),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
@ -239,13 +239,11 @@ impl ParserContext<'_> {
|
||||
method,
|
||||
arguments,
|
||||
});
|
||||
println!("expr {}", expr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let curr = &self.token;
|
||||
return Err(ParserError::unexpected_str(&curr.token, "int or ident", curr.span).into());
|
||||
|
||||
}
|
||||
if !self.check(&Token::LeftParen) {
|
||||
break;
|
||||
|
@ -405,37 +405,54 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_method(&mut self, input: &'a MethodCallExpression, expected: &Self::AdditionalInput) -> Option<Self::Output> {
|
||||
None
|
||||
// input.receiver.
|
||||
//
|
||||
// if let Some(func) = self.visitor.symbol_table.clone().lookup_fn(&input.method.name) {
|
||||
// let ret = self.visitor.assert_type(func.output, expected, func.span());
|
||||
//
|
||||
// if func.input.len() != input.arguments.len() {
|
||||
// self.visitor.handler.emit_err(
|
||||
// TypeCheckerError::incorrect_num_args_to_call(
|
||||
// func.input.len(),
|
||||
// input.arguments.len(),
|
||||
// input.span(),
|
||||
// )
|
||||
// .into(),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// func.input
|
||||
// .iter()
|
||||
// .zip(input.arguments.iter())
|
||||
// .for_each(|(expected, argument)| {
|
||||
// self.visit_expression(argument, &Some(expected.get_variable().type_));
|
||||
// });
|
||||
//
|
||||
// Some(ret)
|
||||
// } else {
|
||||
// self.visitor
|
||||
// .handler
|
||||
// .emit_err(TypeCheckerError::unknown_sym("method", &ident.name, ident.span()).into());
|
||||
// None
|
||||
// }
|
||||
fn visit_method(
|
||||
&mut self,
|
||||
input: &'a MethodCallExpression,
|
||||
expected: &Self::AdditionalInput,
|
||||
) -> Option<Self::Output> {
|
||||
// Check if the method exists.
|
||||
if let Some(method) = self.visitor.method_table.clone().lookup_method(&input.method.name) {
|
||||
// Check number of arguments is correct.
|
||||
if method.num_arguments != input.arguments.len() {
|
||||
self.visitor.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_args_to_call(
|
||||
method.num_arguments,
|
||||
input.arguments.len(),
|
||||
input.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
// Handle case where receiver type == argument type == return type.
|
||||
if method.types_are_equal {
|
||||
// Check if the method is supported for the type.
|
||||
if let Some(type_) = expected {
|
||||
if !self.visitor.method_table.type_method_is_supported(type_, &method.name) {
|
||||
self.visitor.handler.emit_err(
|
||||
TypeCheckerError::type_method_not_supported(type_, &input.method.name, input.span()).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if receiver is correct type for method.
|
||||
let ret = self.visit_expression(&input.receiver, expected);
|
||||
|
||||
// Check if arguments are correct types for method.
|
||||
input.arguments.iter().for_each(|arg| {
|
||||
self.visit_expression(arg, expected);
|
||||
});
|
||||
|
||||
ret
|
||||
} else {
|
||||
// Handle case where the method types are not known yet.
|
||||
*expected
|
||||
}
|
||||
} else {
|
||||
self.visitor
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("method", &input.method.name, input.span()).into());
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) negate: bool,
|
||||
pub(crate) account_types: IndexSet<Symbol>,
|
||||
pub(crate) algorithms_types: IndexSet<Symbol>,
|
||||
pub(crate) method_table: MethodTable,
|
||||
}
|
||||
|
||||
const INT_TYPES: [Type; 10] = [
|
||||
@ -82,6 +83,7 @@ impl<'a> TypeChecker<'a> {
|
||||
negate: false,
|
||||
account_types: Account::types(),
|
||||
algorithms_types: Algorithms::types(),
|
||||
method_table: MethodTable::load(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,16 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// The method name is known but not supported for the given type.
|
||||
@formatted
|
||||
type_method_not_supported {
|
||||
args: (type_: impl Display, method: impl Display),
|
||||
msg: format!(
|
||||
"Type `{type_}` does not support associated method `{method}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to return a unknown variable.
|
||||
@formatted
|
||||
unknown_sym {
|
||||
|
Loading…
Reference in New Issue
Block a user