mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-19 15:41:36 +03:00
Merge pull request #1786 from AleoHQ/feature/type-checking
[Impl] type-checking
This commit is contained in:
commit
be9fa96d9e
@ -14,6 +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 leo_errors::Result;
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::Node;
|
||||
|
@ -47,20 +47,6 @@ pub enum BinaryOperation {
|
||||
Le,
|
||||
/// Lesser-than relation, i.e. `<`.
|
||||
Lt,
|
||||
/// Bitwise-or inclusive, i.e., `|`.
|
||||
BitOr,
|
||||
/// Bitwise-and, i.e., `&`.
|
||||
BitAnd,
|
||||
/// Bitwise-or exclusive, i.e., `^`.
|
||||
BitXor,
|
||||
/// Shift-right, i.e `>>`.
|
||||
Shr,
|
||||
/// Unsigned shift-right, i.e `>>>`.
|
||||
ShrSigned,
|
||||
/// Shift-left, i.e `<<`.
|
||||
Shl,
|
||||
/// Modulus or remainder operation, i.e., `%`.
|
||||
Mod,
|
||||
}
|
||||
|
||||
/// The category a binary operation belongs to.
|
||||
@ -88,13 +74,6 @@ impl AsRef<str> for BinaryOperation {
|
||||
BinaryOperation::Gt => ">",
|
||||
BinaryOperation::Le => "<=",
|
||||
BinaryOperation::Lt => "<",
|
||||
BinaryOperation::BitOr => "|",
|
||||
BinaryOperation::BitAnd => "&",
|
||||
BinaryOperation::BitXor => "^",
|
||||
BinaryOperation::Shr => ">>",
|
||||
BinaryOperation::ShrSigned => ">>>",
|
||||
BinaryOperation::Shl => "<<",
|
||||
BinaryOperation::Mod => "%",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,13 +87,6 @@ impl BinaryOperation {
|
||||
| BinaryOperation::Sub
|
||||
| BinaryOperation::Mul
|
||||
| BinaryOperation::Div
|
||||
| BinaryOperation::BitOr
|
||||
| BinaryOperation::BitAnd
|
||||
| BinaryOperation::BitXor
|
||||
| BinaryOperation::Shr
|
||||
| BinaryOperation::ShrSigned
|
||||
| BinaryOperation::Shl
|
||||
| BinaryOperation::Mod
|
||||
| BinaryOperation::Pow => BinaryOperationClass::Numeric,
|
||||
BinaryOperation::Or
|
||||
| BinaryOperation::And
|
||||
|
@ -15,7 +15,6 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{GroupValue, Identifier, IntegerType, Node};
|
||||
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -24,9 +24,6 @@ pub enum UnaryOperation {
|
||||
Not,
|
||||
/// The arithmetic negation operator, i.e., `-`.
|
||||
Negate,
|
||||
/// The bitwise negation operator, i.e., `~`.
|
||||
/// For example, it transforms `1010` to `0101`.
|
||||
BitNot,
|
||||
}
|
||||
|
||||
impl AsRef<str> for UnaryOperation {
|
||||
@ -34,7 +31,6 @@ impl AsRef<str> for UnaryOperation {
|
||||
match self {
|
||||
UnaryOperation::Not => "!",
|
||||
UnaryOperation::Negate => "-",
|
||||
UnaryOperation::BitNot => "~",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ pub enum ValueExpression {
|
||||
/// A group literal, either product or affine.
|
||||
/// For example, `42group` or `(12, 52)group`.
|
||||
Group(Box<GroupValue>),
|
||||
/// A negated non-integer literal, e.g., `-4.2`.
|
||||
Implicit(String, #[serde(with = "leo_span::span_json")] Span),
|
||||
/// An integer literal, e.g., `42`.
|
||||
Integer(IntegerType, String, #[serde(with = "leo_span::span_json")] Span),
|
||||
/// A string literal, e.g., `"foobar"`.
|
||||
@ -49,7 +47,6 @@ impl fmt::Display for ValueExpression {
|
||||
Boolean(boolean, _) => write!(f, "{}", boolean),
|
||||
Char(character) => write!(f, "{}", character),
|
||||
Field(field, _) => write!(f, "{}", field),
|
||||
Implicit(implicit, _) => write!(f, "{}", implicit),
|
||||
Integer(type_, value, _) => write!(f, "{}{}", value, type_),
|
||||
Group(group) => write!(f, "{}", group),
|
||||
String(string, _) => {
|
||||
@ -66,12 +63,7 @@ impl Node for ValueExpression {
|
||||
fn span(&self) -> &Span {
|
||||
use ValueExpression::*;
|
||||
match &self {
|
||||
Address(_, span)
|
||||
| Boolean(_, span)
|
||||
| Field(_, span)
|
||||
| Implicit(_, span)
|
||||
| Integer(_, _, span)
|
||||
| String(_, span) => span,
|
||||
Address(_, span) | Boolean(_, span) | Field(_, span) | Integer(_, _, span) | String(_, span) => span,
|
||||
Char(character) => &character.span,
|
||||
Group(group) => match &**group {
|
||||
GroupValue::Single(_, span) => span,
|
||||
@ -83,12 +75,9 @@ impl Node for ValueExpression {
|
||||
fn set_span(&mut self, new_span: Span) {
|
||||
use ValueExpression::*;
|
||||
match self {
|
||||
Address(_, span)
|
||||
| Boolean(_, span)
|
||||
| Field(_, span)
|
||||
| Implicit(_, span)
|
||||
| Integer(_, _, span)
|
||||
| String(_, span) => *span = new_span,
|
||||
Address(_, span) | Boolean(_, span) | Field(_, span) | Integer(_, _, span) | String(_, span) => {
|
||||
*span = new_span
|
||||
}
|
||||
Char(character) => character.span = new_span,
|
||||
Group(group) => match &mut **group {
|
||||
GroupValue::Single(_, span) => *span = new_span,
|
||||
|
@ -47,7 +47,7 @@ pub struct FunctionInputVariable {
|
||||
/// The mode of the function parameter.
|
||||
mode: ParamMode,
|
||||
/// What's the parameter's type?
|
||||
type_: Type,
|
||||
pub type_: Type,
|
||||
/// The parameters span from any annotations to its type.
|
||||
pub span: Span,
|
||||
}
|
||||
@ -65,10 +65,6 @@ impl FunctionInputVariable {
|
||||
pub fn mode(&self) -> ParamMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn type_(&self) -> Type {
|
||||
self.type_.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionInputVariable {
|
||||
|
@ -42,11 +42,8 @@ impl TryFrom<(Type, Expression)> for InputValue {
|
||||
Self::Boolean(bool_value)
|
||||
}
|
||||
(Type::Char, ValueExpression::Char(value)) => Self::Char(value),
|
||||
(Type::Field, ValueExpression::Field(value, _) | ValueExpression::Implicit(value, _)) => {
|
||||
Self::Field(value)
|
||||
}
|
||||
(Type::Field, ValueExpression::Field(value, _)) => Self::Field(value),
|
||||
(Type::Group, ValueExpression::Group(value)) => Self::Group(*value),
|
||||
(Type::IntegerType(type_), ValueExpression::Implicit(value, _)) => Self::Integer(type_, value),
|
||||
(Type::IntegerType(expected), ValueExpression::Integer(actual, value, span)) => {
|
||||
if expected == actual {
|
||||
Self::Integer(expected, value)
|
||||
|
@ -275,7 +275,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
|
||||
variable: &FunctionInputVariable,
|
||||
) -> Result<FunctionInputVariable> {
|
||||
let identifier = self.reduce_identifier(&variable.identifier)?;
|
||||
let type_ = self.reduce_type(&variable.type_(), &variable.span)?;
|
||||
let type_ = self.reduce_type(&variable.type_, &variable.span)?;
|
||||
|
||||
self.reducer.reduce_function_input_variable(variable, identifier, type_)
|
||||
}
|
||||
|
@ -76,10 +76,7 @@ impl<'a, V: ExpressionVisitor<'a>> VisitorDirector<'a, V> {
|
||||
|
||||
pub fn visit_call(&mut self, input: &'a CallExpression) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
||||
self.visit_expression(&input.function);
|
||||
for expr in input.arguments.iter() {
|
||||
self.visit_expression(expr);
|
||||
}
|
||||
input.arguments.iter().for_each(|expr| self.visit_expression(expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,8 +135,11 @@ impl<'a, V: ExpressionVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V>
|
||||
|
||||
pub fn visit_console(&mut self, input: &'a ConsoleStatement) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_console(input) {
|
||||
if let ConsoleFunction::Assert(expr) = &input.function {
|
||||
self.visit_expression(expr);
|
||||
match &input.function {
|
||||
ConsoleFunction::Assert(expr) => self.visit_expression(expr),
|
||||
ConsoleFunction::Error(fmt) | ConsoleFunction::Log(fmt) => {
|
||||
fmt.parameters.iter().for_each(|expr| self.visit_expression(expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,9 +152,7 @@ impl<'a, V: ExpressionVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V>
|
||||
|
||||
pub fn visit_block(&mut self, input: &'a Block) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_block(input) {
|
||||
for stmt in input.statements.iter() {
|
||||
self.visit_statement(stmt);
|
||||
}
|
||||
input.statements.iter().for_each(|stmt| self.visit_statement(stmt));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,9 +160,10 @@ impl<'a, V: ExpressionVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V>
|
||||
impl<'a, V: ExpressionVisitor<'a> + ProgramVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V> {
|
||||
pub fn visit_program(&mut self, input: &'a Program) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_program(input) {
|
||||
for function in input.functions.values() {
|
||||
self.visit_function(function);
|
||||
}
|
||||
input
|
||||
.functions
|
||||
.values()
|
||||
.for_each(|function| self.visit_function(function));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Expression, Node};
|
||||
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -130,6 +130,8 @@ impl<'a> Compiler<'a> {
|
||||
fn compiler_stages(&self) -> Result<SymbolTable<'_>> {
|
||||
let symbol_table = CreateSymbolTable::do_pass((&self.ast, self.handler))?;
|
||||
|
||||
TypeChecker::do_pass((&self.ast, &mut symbol_table.clone(), self.handler))?;
|
||||
|
||||
Ok(symbol_table)
|
||||
}
|
||||
|
||||
|
@ -86,13 +86,13 @@ impl Namespace for CompileNamespace {
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
struct OutputItem {
|
||||
pub initial_input_ast: String,
|
||||
pub symbol_table: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
struct CompileOutput {
|
||||
pub output: Vec<OutputItem>,
|
||||
pub initial_ast: String,
|
||||
pub symbol_table: String,
|
||||
}
|
||||
|
||||
/// Get the path of the `input_file` given in `input` into `list`.
|
||||
@ -102,6 +102,12 @@ fn get_input_file_paths(list: &mut Vec<PathBuf>, test: &Test, input: &Value) {
|
||||
let mut input_file = input_file;
|
||||
input_file.push(input.as_str().expect("input_file was not a string or array"));
|
||||
list.push(input_file.clone());
|
||||
} else if let Some(seq) = input.as_sequence() {
|
||||
for name in seq {
|
||||
let mut input_file = input_file.clone();
|
||||
input_file.push(name.as_str().expect("input_file was not a string"));
|
||||
list.push(input_file.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,11 +122,7 @@ fn collect_all_inputs(test: &Test) -> Result<Vec<PathBuf>, String> {
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
fn compile_and_process<'a>(
|
||||
parsed: &'a mut Compiler<'a>,
|
||||
input_file_path: PathBuf,
|
||||
) -> Result<SymbolTable<'a>, LeoError> {
|
||||
parsed.parse_input(input_file_path)?;
|
||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<SymbolTable<'a>, LeoError> {
|
||||
parsed.compiler_stages()
|
||||
}
|
||||
|
||||
@ -176,24 +178,29 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
cwd.join(&val.as_str().unwrap())
|
||||
});
|
||||
|
||||
let parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||
|
||||
// (name, content)
|
||||
let inputs = buffer_if_err(err_buf, collect_all_inputs(&test))?;
|
||||
|
||||
let mut output_items = Vec::with_capacity(inputs.len());
|
||||
|
||||
for input in inputs {
|
||||
let mut parsed = parsed.clone();
|
||||
let symbol_table = handler.extend_if_error(compile_and_process(&mut parsed, input))?;
|
||||
let initial_input_ast = hash_file("/tmp/output/inital_input_ast.json");
|
||||
|
||||
if inputs.is_empty() {
|
||||
output_items.push(OutputItem {
|
||||
initial_input_ast,
|
||||
symbol_table: hash_content(&symbol_table.to_string()),
|
||||
initial_input_ast: "no input".to_string(),
|
||||
});
|
||||
} else {
|
||||
for input in inputs {
|
||||
let mut parsed = parsed.clone();
|
||||
handler.extend_if_error(parsed.parse_input(input))?;
|
||||
let initial_input_ast = hash_file("/tmp/output/inital_input_ast.json");
|
||||
|
||||
output_items.push(OutputItem { initial_input_ast });
|
||||
}
|
||||
}
|
||||
|
||||
let symbol_table = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
|
||||
let initial_ast = hash_file("/tmp/output/initial_ast.json");
|
||||
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
@ -203,6 +210,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
let final_output = CompileOutput {
|
||||
output: output_items,
|
||||
initial_ast,
|
||||
symbol_table: hash_content(&symbol_table.to_string()),
|
||||
};
|
||||
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ pub(crate) const TYPE_TOKENS: &[Token] = &[
|
||||
Token::Group,
|
||||
Token::Address,
|
||||
Token::Bool,
|
||||
Token::Char,
|
||||
];
|
||||
|
||||
impl ParserContext<'_> {
|
||||
|
@ -76,10 +76,6 @@ fn with_handler<T>(
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn implicit_value_expr() -> Expression {
|
||||
Expression::Value(ValueExpression::Implicit("".into(), Span::default()))
|
||||
}
|
||||
|
||||
fn tokenize(test: Test) -> Result<Vec<SpannedToken>, String> {
|
||||
tokenizer::tokenize("test", &test.content).map_err(|x| x.to_string())
|
||||
}
|
||||
@ -105,7 +101,7 @@ impl Namespace for ParseExpressionNamespace {
|
||||
create_session_if_not_set_then(|_| {
|
||||
let tokenizer = tokenize(test)?;
|
||||
if all_are_comments(&tokenizer) {
|
||||
return Ok(yaml_or_fail(implicit_value_expr()));
|
||||
return Ok(yaml_or_fail(""));
|
||||
}
|
||||
with_handler(tokenizer, |p| p.parse_expression()).map(yaml_or_fail)
|
||||
})
|
||||
@ -124,7 +120,7 @@ impl Namespace for ParseStatementNamespace {
|
||||
let tokenizer = tokenize(test)?;
|
||||
if all_are_comments(&tokenizer) {
|
||||
return Ok(yaml_or_fail(Statement::Expression(ExpressionStatement {
|
||||
expression: implicit_value_expr(),
|
||||
expression: Expression::Value(ValueExpression::String(Vec::new(), Default::default())),
|
||||
span: Span::default(),
|
||||
})));
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
// disable
|
||||
// Temporarily disable canonicalization.
|
||||
/* pub mod canonicalization;
|
||||
pub use canonicalization::*;
|
||||
*/
|
||||
@ -31,3 +31,6 @@ pub use self::pass::*;
|
||||
|
||||
pub mod symbol_table;
|
||||
pub use symbol_table::*;
|
||||
|
||||
pub mod type_checker;
|
||||
pub use type_checker::*;
|
||||
|
@ -20,6 +20,9 @@ pub use create::*;
|
||||
pub mod table;
|
||||
pub use table::*;
|
||||
|
||||
pub mod variable_scope;
|
||||
pub use variable_scope::*;
|
||||
|
||||
pub mod variable_symbol;
|
||||
pub use variable_symbol::*;
|
||||
|
||||
|
@ -22,7 +22,7 @@ use leo_span::Symbol;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::VariableSymbol;
|
||||
use crate::{VariableScope, VariableSymbol};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SymbolTable<'a> {
|
||||
@ -31,7 +31,7 @@ pub struct SymbolTable<'a> {
|
||||
functions: IndexMap<Symbol, &'a Function>,
|
||||
/// Variables represents functions variable definitions and input variables.
|
||||
/// This field is not populated till necessary.
|
||||
variables: VariableSymbol<'a>,
|
||||
pub(crate) variables: VariableScope<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolTable<'a> {
|
||||
@ -48,11 +48,39 @@ impl<'a> SymbolTable<'a> {
|
||||
self.variables.clear();
|
||||
}
|
||||
|
||||
pub fn insert_fn(&mut self, symbol: Symbol, function: &'a Function) -> Result<()> {
|
||||
pub fn insert_fn(&mut self, symbol: Symbol, insert: &'a Function) -> Result<()> {
|
||||
self.check_shadowing(&symbol)?;
|
||||
self.functions.insert(symbol, function);
|
||||
self.functions.insert(symbol, insert);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol<'a>) -> Result<()> {
|
||||
self.check_shadowing(&symbol)?;
|
||||
self.variables.variables.insert(symbol, insert);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lookup_fn(&self, symbol: &Symbol) -> Option<&&'a Function> {
|
||||
self.functions.get(symbol)
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
|
||||
self.variables.lookup_variable(symbol)
|
||||
}
|
||||
|
||||
pub fn push_variable_scope(&mut self) {
|
||||
let current_scope = self.variables.clone();
|
||||
self.variables = VariableScope {
|
||||
parent: Some(Box::new(current_scope)),
|
||||
variables: Default::default(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pop_variable_scope(&mut self) {
|
||||
let parent = self.variables.parent.clone().unwrap();
|
||||
|
||||
self.variables = *parent;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for SymbolTable<'a> {
|
||||
|
78
compiler/passes/src/symbol_table/variable_scope.rs
Normal file
78
compiler/passes/src/symbol_table/variable_scope.rs
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2019-2022 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 std::fmt::Display;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Symbol;
|
||||
|
||||
use crate::VariableSymbol;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct VariableScope<'a> {
|
||||
/// The parent scope of variables if it exists.
|
||||
/// For example if we are in a if block inside a function.
|
||||
/// The parent would be the functions variables and inputs.
|
||||
/// This field is populated as necessary.
|
||||
pub(crate) parent: Option<Box<VariableScope<'a>>>,
|
||||
/// The variables defined in a scope.
|
||||
/// This field is populated as necessary.
|
||||
pub(crate) variables: IndexMap<Symbol, VariableSymbol<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> VariableScope<'a> {
|
||||
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
|
||||
if let Some(var) = self.variables.get(symbol) {
|
||||
Err(AstError::shadowed_variable(symbol, var.span).into())
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.check_shadowing(symbol)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.parent = None;
|
||||
self.variables.clear();
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
|
||||
if let Some(var) = self.variables.get(symbol) {
|
||||
Some(var)
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.lookup_variable(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for VariableScope<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "VariableScope")?;
|
||||
self.parent
|
||||
.as_ref()
|
||||
.map(|parent| write!(f, "parent {parent}"))
|
||||
.transpose()?;
|
||||
|
||||
for (sym, var) in self.variables.iter() {
|
||||
write!(f, "{sym} {var}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -16,76 +16,38 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use leo_ast::{DefinitionStatement, FunctionInput, FunctionInputVariable};
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Symbol;
|
||||
use leo_ast::{ParamMode, Type};
|
||||
use leo_span::Span;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct VariableSymbol<'a> {
|
||||
/// The parent scope of variables if it exists.
|
||||
/// For example if we are in a if block inside a function.
|
||||
/// The parent would be the functions variables and inputs.
|
||||
/// This field is populated as necessary.
|
||||
parent: Option<Box<VariableSymbol<'a>>>,
|
||||
/// The input variables defined in a scope.
|
||||
/// This field is populated as necessary.
|
||||
inputs: IndexMap<Symbol, &'a FunctionInputVariable>,
|
||||
/// The variables defined in a scope.
|
||||
/// This field is populated as necessary.
|
||||
variables: IndexMap<Symbol, &'a DefinitionStatement>,
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Declaration {
|
||||
Const,
|
||||
Input(ParamMode),
|
||||
Mut,
|
||||
}
|
||||
|
||||
impl<'a> VariableSymbol<'a> {
|
||||
pub fn new(parent: Option<Box<VariableSymbol<'a>>>, inputs: Vec<&'a FunctionInput>) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
inputs: inputs
|
||||
.iter()
|
||||
.map(|input| {
|
||||
let inner = input.get_variable();
|
||||
(inner.identifier.name, inner)
|
||||
})
|
||||
.collect(),
|
||||
variables: IndexMap::new(),
|
||||
impl Display for Declaration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Declaration::*;
|
||||
|
||||
match self {
|
||||
Const => write!(f, "const var"),
|
||||
Input(m) => write!(f, "{m} input"),
|
||||
Mut => write!(f, "mut var"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
|
||||
if let Some(input) = self.inputs.get(symbol) {
|
||||
Err(AstError::shadowed_function_input(symbol, &input.span).into())
|
||||
} else if let Some(var) = self.variables.get(symbol) {
|
||||
Err(AstError::shadowed_variable(symbol, &var.span).into())
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.check_shadowing(symbol)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.parent = None;
|
||||
self.inputs.clear();
|
||||
self.variables.clear();
|
||||
}
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VariableSymbol<'a> {
|
||||
pub type_: &'a Type,
|
||||
pub span: &'a Span,
|
||||
pub declaration: Declaration,
|
||||
}
|
||||
|
||||
impl<'a> Display for VariableSymbol<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "VariableSymbol")?;
|
||||
self.parent
|
||||
.as_ref()
|
||||
.map(|parent| write!(f, "parent {parent}"))
|
||||
.transpose()?;
|
||||
|
||||
for input in self.inputs.values() {
|
||||
write!(f, "{input}")?;
|
||||
}
|
||||
|
||||
for var in self.variables.values() {
|
||||
write!(f, "{var}")?;
|
||||
}
|
||||
|
||||
write!(f, "{}: {}", self.declaration, self.type_)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
317
compiler/passes/src/type_checker/check_expressions.rs
Normal file
317
compiler/passes/src/type_checker/check_expressions.rs
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright (C) 2019-2022 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 leo_ast::*;
|
||||
use leo_errors::TypeCheckerError;
|
||||
use leo_span::Span;
|
||||
|
||||
use crate::TypeChecker;
|
||||
|
||||
fn return_incorrect_type(t1: Option<Type>, t2: Option<Type>, expected: Option<Type>) -> Option<Type> {
|
||||
match (t1, t2) {
|
||||
(Some(t1), Some(t2)) if t1 == t2 => Some(t1),
|
||||
(Some(t1), Some(t2)) => {
|
||||
if let Some(expected) = expected {
|
||||
if t1 != expected {
|
||||
Some(t1)
|
||||
} else {
|
||||
Some(t2)
|
||||
}
|
||||
} else {
|
||||
Some(t1)
|
||||
}
|
||||
}
|
||||
(None, Some(_)) | (Some(_), None) | (None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeChecker<'a> {
|
||||
pub(crate) fn compare_expr_type(&mut self, expr: &Expression, expected: Option<Type>, span: &Span) -> Option<Type> {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => {
|
||||
if let Some(var) = self.symbol_table.lookup_variable(&ident.name) {
|
||||
Some(self.assert_type(var.type_.clone(), expected, span))
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("variable", ident.name, span).into());
|
||||
None
|
||||
}
|
||||
}
|
||||
Expression::Value(value) => match value {
|
||||
ValueExpression::Address(_, _) => Some(self.assert_type(Type::Address, expected, value.span())),
|
||||
ValueExpression::Boolean(_, _) => Some(self.assert_type(Type::Boolean, expected, value.span())),
|
||||
ValueExpression::Char(_) => Some(self.assert_type(Type::Char, expected, value.span())),
|
||||
ValueExpression::Field(_, _) => Some(self.assert_type(Type::Field, expected, value.span())),
|
||||
ValueExpression::Integer(type_, str_content, _) => {
|
||||
match type_ {
|
||||
IntegerType::I8 => {
|
||||
let int = if self.negate {
|
||||
self.negate = false;
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i8>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i8", value.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I16 => {
|
||||
let int = if self.negate {
|
||||
self.negate = false;
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i16>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i16", value.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I32 => {
|
||||
let int = if self.negate {
|
||||
self.negate = false;
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i32>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i32", value.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I64 => {
|
||||
let int = if self.negate {
|
||||
self.negate = false;
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i64>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i64", value.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I128 => {
|
||||
let int = if self.negate {
|
||||
self.negate = false;
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i128>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i128", value.span()).into());
|
||||
}
|
||||
}
|
||||
|
||||
IntegerType::U8 if str_content.parse::<u8>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", value.span()).into()),
|
||||
IntegerType::U16 if str_content.parse::<u16>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", value.span()).into()),
|
||||
IntegerType::U32 if str_content.parse::<u32>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", value.span()).into()),
|
||||
IntegerType::U64 if str_content.parse::<u64>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", value.span()).into()),
|
||||
IntegerType::U128 if str_content.parse::<u128>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", value.span()).into()),
|
||||
_ => {}
|
||||
}
|
||||
Some(self.assert_type(Type::IntegerType(*type_), expected, value.span()))
|
||||
}
|
||||
ValueExpression::Group(_) => Some(self.assert_type(Type::Group, expected, value.span())),
|
||||
ValueExpression::String(_, _) => unreachable!("String types are not reachable"),
|
||||
},
|
||||
Expression::Binary(binary) => match binary.op {
|
||||
BinaryOperation::And | BinaryOperation::Or => {
|
||||
self.assert_type(Type::Boolean, expected.clone(), binary.span());
|
||||
let t1 = self.compare_expr_type(&binary.left, expected.clone(), binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, expected.clone(), binary.right.span());
|
||||
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
BinaryOperation::Add | BinaryOperation::Sub => {
|
||||
self.assert_arith_type(expected.clone(), binary.span());
|
||||
let t1 = self.compare_expr_type(&binary.left, expected.clone(), binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, expected.clone(), binary.right.span());
|
||||
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
BinaryOperation::Mul => {
|
||||
self.assert_arith_type(expected.clone(), binary.span());
|
||||
|
||||
let t1 = self.compare_expr_type(&binary.left, None, binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, None, binary.right.span());
|
||||
|
||||
match (t1.as_ref(), t2.as_ref()) {
|
||||
(Some(Type::Group), Some(other)) | (Some(other), Some(Type::Group)) => {
|
||||
self.assert_int_type(Some(other.clone()), binary.span());
|
||||
Some(Type::Group)
|
||||
}
|
||||
_ => return_incorrect_type(t1, t2, expected),
|
||||
}
|
||||
}
|
||||
BinaryOperation::Div => {
|
||||
self.assert_field_or_int_type(expected.clone(), binary.span());
|
||||
|
||||
let t1 = self.compare_expr_type(&binary.left, expected.clone(), binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, expected.clone(), binary.right.span());
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
BinaryOperation::Pow => {
|
||||
let t1 = self.compare_expr_type(&binary.left, None, binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, None, binary.right.span());
|
||||
|
||||
match (t1.as_ref(), t2.as_ref()) {
|
||||
// Type A must be an int.
|
||||
// Type B must be a unsigned int.
|
||||
(Some(Type::IntegerType(_)), Some(Type::IntegerType(itype))) if !itype.is_signed() => {
|
||||
self.assert_type(t1.clone().unwrap(), expected, binary.span());
|
||||
}
|
||||
// Type A was an int.
|
||||
// But Type B was not a unsigned int.
|
||||
(Some(Type::IntegerType(_)), Some(t)) => {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_pow_exponent_type("unsigned int", t, binary.right.span())
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
// Type A must be a field.
|
||||
// Type B must be an int.
|
||||
(Some(Type::Field), Some(Type::IntegerType(_))) => {
|
||||
self.assert_type(Type::Field, expected, binary.span());
|
||||
}
|
||||
// Type A was a field.
|
||||
// But Type B was not an int.
|
||||
(Some(Type::Field), Some(t)) => {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_pow_exponent_type("int", t, binary.right.span()).into(),
|
||||
);
|
||||
}
|
||||
// The base is some type thats not an int or field.
|
||||
(Some(t), _) => {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::incorrect_pow_base_type(t, binary.left.span()).into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
t1
|
||||
}
|
||||
BinaryOperation::Eq | BinaryOperation::Ne => {
|
||||
self.assert_type(Type::Boolean, expected.clone(), binary.span());
|
||||
|
||||
let t1 = self.compare_expr_type(&binary.left, None, binary.left.span());
|
||||
let t2 = self.compare_expr_type(&binary.right, None, binary.right.span());
|
||||
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Le | BinaryOperation::Ge => {
|
||||
self.assert_type(Type::Boolean, expected.clone(), binary.span());
|
||||
|
||||
let t1 = self.compare_expr_type(&binary.left, None, binary.left.span());
|
||||
self.assert_int_type(t1.clone(), binary.left.span());
|
||||
|
||||
let t2 = self.compare_expr_type(&binary.right, None, binary.right.span());
|
||||
self.assert_int_type(t2.clone(), binary.right.span());
|
||||
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
},
|
||||
Expression::Unary(unary) => match unary.op {
|
||||
UnaryOperation::Not => {
|
||||
self.assert_type(Type::Boolean, expected.clone(), unary.span());
|
||||
self.compare_expr_type(&unary.inner, expected, unary.inner.span())
|
||||
}
|
||||
UnaryOperation::Negate => {
|
||||
match expected.as_ref() {
|
||||
Some(
|
||||
Type::IntegerType(
|
||||
IntegerType::I8
|
||||
| IntegerType::I16
|
||||
| IntegerType::I32
|
||||
| IntegerType::I64
|
||||
| IntegerType::I128,
|
||||
)
|
||||
| Type::Field
|
||||
| Type::Group,
|
||||
) => self.negate = !self.negate,
|
||||
Some(t) => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::type_is_not_negatable(t, unary.inner.span()).into()),
|
||||
_ => {}
|
||||
};
|
||||
self.compare_expr_type(&unary.inner, expected, unary.inner.span())
|
||||
}
|
||||
},
|
||||
Expression::Ternary(ternary) => {
|
||||
self.compare_expr_type(&ternary.condition, Some(Type::Boolean), ternary.condition.span());
|
||||
let t1 = self.compare_expr_type(&ternary.if_true, expected.clone(), ternary.if_true.span());
|
||||
let t2 = self.compare_expr_type(&ternary.if_false, expected.clone(), ternary.if_false.span());
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
Expression::Call(call) => match &*call.function {
|
||||
Expression::Identifier(ident) => {
|
||||
if let Some(func) = self.symbol_table.lookup_fn(&ident.name) {
|
||||
let ret = self.assert_type(func.output.clone(), expected, ident.span());
|
||||
|
||||
if func.input.len() != call.arguments.len() {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_args_to_call(
|
||||
func.input.len(),
|
||||
call.arguments.len(),
|
||||
call.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
func.input
|
||||
.iter()
|
||||
.zip(call.arguments.iter())
|
||||
.for_each(|(expected, argument)| {
|
||||
self.compare_expr_type(
|
||||
argument,
|
||||
Some(expected.get_variable().type_.clone()),
|
||||
argument.span(),
|
||||
);
|
||||
});
|
||||
|
||||
Some(ret)
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()).into());
|
||||
None
|
||||
}
|
||||
}
|
||||
expr => self.compare_expr_type(expr, expected, call.span()),
|
||||
},
|
||||
Expression::Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {}
|
42
compiler/passes/src/type_checker/check_file.rs
Normal file
42
compiler/passes/src/type_checker/check_file.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2019-2022 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 leo_ast::*;
|
||||
|
||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||
|
||||
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
|
||||
self.symbol_table.clear_variables();
|
||||
self.parent = Some(input.name());
|
||||
input.input.iter().for_each(|i| {
|
||||
let input_var = i.get_variable();
|
||||
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
input_var.identifier.name,
|
||||
VariableSymbol {
|
||||
type_: &input_var.type_,
|
||||
span: input_var.identifier.span(),
|
||||
declaration: Declaration::Input(input_var.mode()),
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
});
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
}
|
150
compiler/passes/src/type_checker/check_statements.rs
Normal file
150
compiler/passes/src/type_checker/check_statements.rs
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright (C) 2019-2022 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 leo_ast::*;
|
||||
use leo_errors::TypeCheckerError;
|
||||
|
||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||
|
||||
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_return(&mut self, input: &'a ReturnStatement) -> VisitResult {
|
||||
// we can safely unwrap all self.parent instances because
|
||||
// statements should always have some parent block
|
||||
let parent = self.parent.unwrap();
|
||||
|
||||
// Would never be None.
|
||||
let func_output_type = self.symbol_table.lookup_fn(&parent).map(|f| f.output.clone());
|
||||
self.compare_expr_type(&input.expression, func_output_type, input.expression.span());
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_definition(&mut self, input: &'a DefinitionStatement) -> VisitResult {
|
||||
let declaration = if input.declaration_type == Declare::Const {
|
||||
Declaration::Const
|
||||
} else {
|
||||
Declaration::Mut
|
||||
};
|
||||
|
||||
input.variable_names.iter().for_each(|v| {
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
v.identifier.name,
|
||||
VariableSymbol {
|
||||
type_: &input.type_,
|
||||
span: input.span(),
|
||||
declaration: declaration.clone(),
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
self.compare_expr_type(&input.value, Some(input.type_.clone()), input.value.span());
|
||||
});
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, input: &'a AssignStatement) -> VisitResult {
|
||||
let var_name = &input.assignee.identifier.name;
|
||||
let var_type = if let Some(var) = self.symbol_table.lookup_variable(var_name) {
|
||||
match &var.declaration {
|
||||
Declaration::Const => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::cannont_assign_to_const_var(var_name, var.span).into()),
|
||||
Declaration::Input(ParamMode::Constant) => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::cannont_assign_to_const_input(var_name, var.span).into()),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(var.type_.clone())
|
||||
} else {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::unknown_sym("variable", &input.assignee.identifier.name, &input.assignee.span).into(),
|
||||
);
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
if var_type.is_some() {
|
||||
self.compare_expr_type(&input.value, var_type, input.value.span());
|
||||
}
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_conditional(&mut self, input: &'a ConditionalStatement) -> VisitResult {
|
||||
self.compare_expr_type(&input.condition, Some(Type::Boolean), input.condition.span());
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_iteration(&mut self, input: &'a IterationStatement) -> VisitResult {
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
input.variable.name,
|
||||
VariableSymbol {
|
||||
type_: &input.type_,
|
||||
span: input.span(),
|
||||
declaration: Declaration::Const,
|
||||
},
|
||||
) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
self.compare_expr_type(&input.start, Some(input.type_.clone()), input.start.span());
|
||||
self.compare_expr_type(&input.stop, Some(input.type_.clone()), input.stop.span());
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_console(&mut self, input: &'a ConsoleStatement) -> VisitResult {
|
||||
match &input.function {
|
||||
ConsoleFunction::Assert(expr) => {
|
||||
self.compare_expr_type(expr, Some(Type::Boolean), expr.span());
|
||||
}
|
||||
ConsoleFunction::Error(_) | ConsoleFunction::Log(_) => {
|
||||
// TODO: undetermined
|
||||
}
|
||||
}
|
||||
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) -> VisitResult {
|
||||
self.compare_expr_type(&input.expression, None, input.span());
|
||||
VisitResult::SkipChildren
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, input: &'a Block) -> VisitResult {
|
||||
self.symbol_table.push_variable_scope();
|
||||
// have to redo the logic here so we have scoping
|
||||
input.statements.iter().for_each(|stmt| {
|
||||
match stmt {
|
||||
Statement::Return(stmt) => self.visit_return(stmt),
|
||||
Statement::Definition(stmt) => self.visit_definition(stmt),
|
||||
Statement::Assign(stmt) => self.visit_assign(stmt),
|
||||
Statement::Conditional(stmt) => self.visit_conditional(stmt),
|
||||
Statement::Iteration(stmt) => self.visit_iteration(stmt),
|
||||
Statement::Console(stmt) => self.visit_console(stmt),
|
||||
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
|
||||
Statement::Block(stmt) => self.visit_block(stmt),
|
||||
};
|
||||
});
|
||||
self.symbol_table.pop_variable_scope();
|
||||
|
||||
VisitResult::SkipChildren
|
||||
}
|
||||
}
|
125
compiler/passes/src/type_checker/checker.rs
Normal file
125
compiler/passes/src/type_checker/checker.rs
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (C) 2019-2022 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 leo_ast::{IntegerType, Type};
|
||||
use leo_errors::{emitter::Handler, TypeCheckerError};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::SymbolTable;
|
||||
|
||||
pub struct TypeChecker<'a> {
|
||||
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
|
||||
pub(crate) handler: &'a Handler,
|
||||
pub(crate) parent: Option<Symbol>,
|
||||
pub(crate) negate: bool,
|
||||
}
|
||||
|
||||
const ARITHMETIC_TYPES: &[Type] = &[
|
||||
Type::Field,
|
||||
Type::Group,
|
||||
Type::IntegerType(IntegerType::I8),
|
||||
Type::IntegerType(IntegerType::I16),
|
||||
Type::IntegerType(IntegerType::I32),
|
||||
Type::IntegerType(IntegerType::I64),
|
||||
Type::IntegerType(IntegerType::I128),
|
||||
Type::IntegerType(IntegerType::U8),
|
||||
Type::IntegerType(IntegerType::U16),
|
||||
Type::IntegerType(IntegerType::U32),
|
||||
Type::IntegerType(IntegerType::U64),
|
||||
Type::IntegerType(IntegerType::U128),
|
||||
];
|
||||
|
||||
const FIELD_AND_INT_TYPES: &[Type] = &[
|
||||
Type::Field,
|
||||
Type::IntegerType(IntegerType::I8),
|
||||
Type::IntegerType(IntegerType::I16),
|
||||
Type::IntegerType(IntegerType::I32),
|
||||
Type::IntegerType(IntegerType::I64),
|
||||
Type::IntegerType(IntegerType::I128),
|
||||
Type::IntegerType(IntegerType::U8),
|
||||
Type::IntegerType(IntegerType::U16),
|
||||
Type::IntegerType(IntegerType::U32),
|
||||
Type::IntegerType(IntegerType::U64),
|
||||
Type::IntegerType(IntegerType::U128),
|
||||
];
|
||||
|
||||
const INT_TYPES: &[Type] = &[
|
||||
Type::IntegerType(IntegerType::I8),
|
||||
Type::IntegerType(IntegerType::I16),
|
||||
Type::IntegerType(IntegerType::I32),
|
||||
Type::IntegerType(IntegerType::I64),
|
||||
Type::IntegerType(IntegerType::I128),
|
||||
Type::IntegerType(IntegerType::U8),
|
||||
Type::IntegerType(IntegerType::U16),
|
||||
Type::IntegerType(IntegerType::U32),
|
||||
Type::IntegerType(IntegerType::U64),
|
||||
Type::IntegerType(IntegerType::U128),
|
||||
];
|
||||
|
||||
impl<'a> TypeChecker<'a> {
|
||||
pub fn new(symbol_table: &'a mut SymbolTable<'a>, handler: &'a Handler) -> Self {
|
||||
Self {
|
||||
symbol_table,
|
||||
handler,
|
||||
parent: None,
|
||||
negate: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn assert_type(&self, type_: Type, expected: Option<Type>, span: &Span) -> Type {
|
||||
if let Some(expected) = expected {
|
||||
if type_ != expected {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::type_should_be(type_.clone(), expected, span).into());
|
||||
}
|
||||
}
|
||||
|
||||
type_
|
||||
}
|
||||
|
||||
pub(crate) fn assert_one_of_types(&self, type_: Option<Type>, expected: &[Type], span: &Span) -> Option<Type> {
|
||||
if let Some(type_) = type_.clone() {
|
||||
for t in expected.iter() {
|
||||
if &type_ == t {
|
||||
return Some(type_);
|
||||
}
|
||||
}
|
||||
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::expected_one_type_of(
|
||||
expected.iter().map(|t| t.to_string() + ",").collect::<String>(),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
type_
|
||||
}
|
||||
|
||||
pub(crate) fn assert_arith_type(&self, type_: Option<Type>, span: &Span) -> Option<Type> {
|
||||
self.assert_one_of_types(type_, ARITHMETIC_TYPES, span)
|
||||
}
|
||||
|
||||
pub(crate) fn assert_field_or_int_type(&self, type_: Option<Type>, span: &Span) -> Option<Type> {
|
||||
self.assert_one_of_types(type_, FIELD_AND_INT_TYPES, span)
|
||||
}
|
||||
|
||||
pub(crate) fn assert_int_type(&self, type_: Option<Type>, span: &Span) -> Option<Type> {
|
||||
self.assert_one_of_types(type_, INT_TYPES, span)
|
||||
}
|
||||
}
|
47
compiler/passes/src/type_checker/mod.rs
Normal file
47
compiler/passes/src/type_checker/mod.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2019-2022 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/>.
|
||||
|
||||
pub mod check_expressions;
|
||||
pub use check_expressions::*;
|
||||
|
||||
pub mod check_file;
|
||||
pub use check_file::*;
|
||||
|
||||
pub mod check_statements;
|
||||
pub use check_statements::*;
|
||||
|
||||
pub mod checker;
|
||||
pub use checker::*;
|
||||
|
||||
use crate::{Pass, SymbolTable};
|
||||
|
||||
use leo_ast::{Ast, VisitorDirector};
|
||||
use leo_errors::{emitter::Handler, Result};
|
||||
|
||||
impl<'a> Pass<'a> for TypeChecker<'a> {
|
||||
type Input = (&'a Ast, &'a mut SymbolTable<'a>, &'a Handler);
|
||||
type Output = Result<()>;
|
||||
|
||||
fn do_pass((ast, symbol_table, handler): Self::Input) -> Self::Output {
|
||||
let mut visitor = VisitorDirector::new(TypeChecker::new(symbol_table, handler));
|
||||
visitor.visit_program(ast.as_repr());
|
||||
// todo @gluax: awkward cause last error double prints...
|
||||
// but can't os exit or it causes tests to stop.
|
||||
handler.last_err()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
function main(a: u32, a: u32) {
|
||||
console.log("{}", 1u8);
|
||||
}
|
@ -5,9 +5,6 @@ input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
function main() -> i8 {
|
||||
if true {
|
||||
return 1i8; //ignored
|
||||
}
|
||||
return 2i8; //ignored
|
||||
return 3i8; //returns
|
||||
return 2i8;
|
||||
return 3i8;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
const hi = 1u32;
|
||||
|
@ -1,10 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: ../input/dummy.in
|
||||
*/
|
||||
|
||||
function main(y: bool) -> bool {
|
||||
const a: i32 = 2147483647;
|
||||
return y == true;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: ../input/dummy.in
|
||||
*/
|
||||
|
||||
function main(y: bool) -> bool {
|
||||
const a: i32 = -2147483648;
|
||||
return y == true;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- i32.in: |
|
||||
[main]
|
||||
a: i32 = 2;
|
||||
b: i32 = 5;
|
||||
c: i32 = 10;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(a: i32, b: i32, c: i32) -> bool {
|
||||
return a * b == c;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- i32_ne.in: |
|
||||
[main]
|
||||
a: i32 = 2;
|
||||
b: i32 = 5;
|
||||
c: bool = true;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
- i32_e.in: |
|
||||
[main]
|
||||
a: i32 = 5;
|
||||
b: i32 = 5;
|
||||
c: bool = false;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(a: i32, b: i32, c: bool) -> bool{
|
||||
return (a != b) == c;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- i32.in: |
|
||||
[main]
|
||||
a: i32 = 5;
|
||||
b: i32 = -5;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
- i32.in: |
|
||||
[main]
|
||||
a: i32 = -5;
|
||||
b: i32 = 5;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(a: i32, b: i32) -> bool {
|
||||
return -a == b;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- dummy.in: |
|
||||
[main]
|
||||
y: bool = true;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(y: bool) -> bool {
|
||||
const a = 0i32;
|
||||
|
||||
return (-a == 0i32) == y;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: i32, b: i32, c: i32) -> bool {
|
||||
return a ** b == c;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- i32.in: |
|
||||
[main]
|
||||
a: i32 = 100;
|
||||
b: i32 = 50;
|
||||
c: i32 = 50;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(a: i32, b: i32, c: i32) -> bool {
|
||||
return a - b == c;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
inputs:
|
||||
- i32.in: |
|
||||
[main]
|
||||
s: bool = true;
|
||||
a: i32 = 10;
|
||||
b: i32 = 5;
|
||||
c: i32 = 10;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
- i32_rev.in: |
|
||||
[main]
|
||||
s: bool = false;
|
||||
a: i32 = 10;
|
||||
b: i32 = 5;
|
||||
c: i32 = 5;
|
||||
|
||||
[registers]
|
||||
r0: bool = false;
|
||||
*/
|
||||
|
||||
function main(s: bool, a: i32, b: i32, c: i32) -> bool {
|
||||
let r = s ? a : b;
|
||||
|
||||
return r == c;
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
expectation: Pass
|
||||
input_file: ../input/dummy.in
|
||||
*/
|
||||
|
||||
function main() {
|
||||
const a = -128i8;
|
||||
const b = -a;
|
||||
function main(y: bool) -> bool {
|
||||
const a: = -128i8;
|
||||
const b: = -a;
|
||||
|
||||
return (b == -a) == y;
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
function main(a: bool) -> bool {
|
||||
let x = 2u8;
|
||||
let y = x;
|
||||
let z = y / 2u8;
|
||||
return (z == 1) == a;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: inputs/dummy.in
|
||||
*/
|
||||
|
||||
function main(k: bool) -> bool {
|
||||
let reverse: u32 = 0;
|
||||
for i in 9..0 {
|
||||
reverse += i;
|
||||
}
|
||||
|
||||
let forward: u32 = 0;
|
||||
for x in 0..10 {
|
||||
forward += x;
|
||||
}
|
||||
|
||||
let reverse_inclusive: u32 = 0;
|
||||
for a in 10..=0 {
|
||||
reverse_inclusive += a;
|
||||
}
|
||||
|
||||
let forward_inclusive: u32 = 0;
|
||||
for b in 0..=10 {
|
||||
forward_inclusive += b;
|
||||
}
|
||||
|
||||
return (reverse == forward) && (reverse_inclusive == forward_inclusive) && k;
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: inputs/u32_3.in
|
||||
*/
|
||||
|
||||
function main(x: u32) -> bool {
|
||||
const COUNT: u32 = 2;
|
||||
let y = x;
|
||||
|
||||
for i in 0..COUNT {
|
||||
y -= 1;
|
||||
}
|
||||
|
||||
return y == 1;
|
||||
}
|
@ -147,14 +147,6 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user shadows a function input.
|
||||
@formatted
|
||||
shadowed_function_input {
|
||||
args: (fn_input: impl Display),
|
||||
msg: format!("function input `{fn_input}` shadowed"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user shadows a variable.
|
||||
@formatted
|
||||
shadowed_variable {
|
||||
|
@ -20,7 +20,7 @@ use std::fmt::{Debug, Display};
|
||||
create_messages!(
|
||||
/// InputError enum that represents all the errors for the inputs part of `leo-ast` crate.
|
||||
InputError,
|
||||
code_mask: 8000i32,
|
||||
code_mask: 1000i32,
|
||||
code_prefix: "INP",
|
||||
|
||||
/// For when declared variable type mismatches actual type.
|
||||
|
@ -41,6 +41,10 @@ pub use self::package::*;
|
||||
pub mod parser;
|
||||
pub use self::parser::*;
|
||||
|
||||
/// Contains the Type Checker error definitions.
|
||||
pub mod type_checker;
|
||||
pub use self::type_checker::*;
|
||||
|
||||
/// The LeoError type that contains all sub error types.
|
||||
/// This allows a unified error type throughout the Leo crates.
|
||||
#[derive(Clone, Debug, Error)]
|
||||
@ -63,6 +67,9 @@ pub enum LeoError {
|
||||
/// Represents an Parser Error in a Leo Error.
|
||||
#[error(transparent)]
|
||||
ParserError(#[from] ParserError),
|
||||
/// Represents an Type Checker Error in a Leo Error.
|
||||
#[error(transparent)]
|
||||
TypeCheckerError(#[from] TypeCheckerError),
|
||||
}
|
||||
|
||||
impl LeoError {
|
||||
@ -77,6 +84,7 @@ impl LeoError {
|
||||
InputError(error) => error.error_code(),
|
||||
ParserError(error) => error.error_code(),
|
||||
PackageError(error) => error.error_code(),
|
||||
TypeCheckerError(error) => error.error_code(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +99,7 @@ impl LeoError {
|
||||
InputError(error) => error.exit_code(),
|
||||
ParserError(error) => error.exit_code(),
|
||||
PackageError(error) => error.exit_code(),
|
||||
TypeCheckerError(error) => error.exit_code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,9 +291,9 @@ create_messages!(
|
||||
/// When the user tries to pass an implicit value.
|
||||
@formatted
|
||||
implicit_values_not_allowed {
|
||||
args: (input: impl Display),
|
||||
msg: format!("Could not parse the implicit value: {}.", input),
|
||||
help: None,
|
||||
args: (input: impl Display),
|
||||
msg: format!("Could not parse the implicit value: {}.", input),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// When a escaped unicode char was given but no following closing symbol.
|
||||
|
19
leo/errors/src/errors/type_checker/mod.rs
Normal file
19
leo/errors/src/errors/type_checker/mod.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2019-2022 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 contains the Input error definitions.
|
||||
pub mod type_checker_error;
|
||||
pub use self::type_checker_error::*;
|
135
leo/errors/src/errors/type_checker/type_checker_error.rs
Normal file
135
leo/errors/src/errors/type_checker/type_checker_error.rs
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2019-2022 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};
|
||||
|
||||
create_messages!(
|
||||
/// InputError enum that represents all the errors for the inputs part of `leo-ast` crate.
|
||||
TypeCheckerError,
|
||||
code_mask: 2000i32,
|
||||
code_prefix: "TYC",
|
||||
|
||||
/// For when the user tries to assign to a const input.
|
||||
@formatted
|
||||
cannont_assign_to_const_input {
|
||||
args: (input: impl Display),
|
||||
msg: format!(
|
||||
"Cannot assign to const input `{input}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to assign to a const input.
|
||||
@formatted
|
||||
cannont_assign_to_const_var {
|
||||
args: (var: impl Display),
|
||||
msg: format!(
|
||||
"Cannot assign to const variable `{var}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to assign to a const input.
|
||||
@formatted
|
||||
type_should_be {
|
||||
args: (type_: impl Display, expected: impl Display),
|
||||
msg: format!(
|
||||
"Found type `{type_}` but type `{expected}` was expected",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to return a unknown variable.
|
||||
@formatted
|
||||
unknown_sym {
|
||||
args: (kind: impl Display, sym: impl Display),
|
||||
msg: format!(
|
||||
"Unknown {kind} `{sym}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to expect a non integer type .
|
||||
@formatted
|
||||
type_should_be_integer {
|
||||
args: (op: impl Debug, type_: impl Display),
|
||||
msg: format!(
|
||||
"Binary statement has numeric operation `{op:?}` but has expected type `{type_}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries to negate a non negatable type.
|
||||
@formatted
|
||||
type_is_not_negatable {
|
||||
args: (type_: impl Display),
|
||||
msg: format!(
|
||||
"The type `{type_}` is not negatable",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries calls a function with the incorrect number of args.
|
||||
@formatted
|
||||
incorrect_num_args_to_call {
|
||||
args: (expected: impl Display, received: impl Display),
|
||||
msg: format!(
|
||||
"Call expected `{expected}` args, but got `{received}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when one of the following types was expected.
|
||||
@formatted
|
||||
expected_one_type_of {
|
||||
args: (expected: impl Display, received: impl Display),
|
||||
msg: format!(
|
||||
"Expected one type from `{expected}`, but got `{received}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the base of a power is not a valid type.
|
||||
@formatted
|
||||
incorrect_pow_base_type {
|
||||
args: (type_: impl Display),
|
||||
msg: format!(
|
||||
"The first operand must be an integer or field but got type `{type_}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the exponent of a power is not a valid type.
|
||||
@formatted
|
||||
incorrect_pow_exponent_type {
|
||||
args: (allowed: impl Display, type_: impl Display),
|
||||
msg: format!(
|
||||
"The second operand must be a {allowed} but got type `{type_}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when an integer is not in a valid range.
|
||||
@formatted
|
||||
invalid_int_value {
|
||||
args: (value: impl Display, type_: impl Display),
|
||||
msg: format!(
|
||||
"The value {value} is not a valid `{type_}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
);
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group, c: group) {
|
||||
console.assert(a + b == c);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group) {
|
||||
console.assert(a == b);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (+, +)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (_, _)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (-, -)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group, c: bool) {
|
||||
console.assert((a == b) == c);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group) {
|
||||
console.assert(a == b);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
[main]
|
||||
a: group = (0, -)group;
|
||||
b: group = (0, _)group;
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group) {
|
||||
console.assert(-a == b);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const g = (0,1) group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = 1group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const point = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: group) {
|
||||
const b = a;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
function main() {
|
||||
const pos_element = 1group;
|
||||
const neg_element = -1group;
|
||||
|
||||
const pair_x_pos = (1, _)group;
|
||||
const pair_x_neg = (-1, _)group;
|
||||
|
||||
const pair_y_pos = (_, 1)group;
|
||||
const pair_y_neg = (_, -1)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main(a: group, b: group, c: group) {
|
||||
console.assert(a - b == c);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
function main(s: bool, a: group, b: group, c: group) {
|
||||
const r = s ? a : b;
|
||||
|
||||
console.assert(r == c);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (0, +)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (0, _)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (0, -)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (+, 1)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (_, 1)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = (-, 1)group;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
const element = 0group;
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- input/false_false.in
|
||||
- input/false_true.in
|
||||
- input/true_false.in
|
||||
- input/true_true.in
|
||||
- inputs/false_false.in
|
||||
- inputs/false_true.in
|
||||
- inputs/true_false.in
|
||||
- inputs/true_true.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> bool {
|
||||
|
@ -2,10 +2,10 @@
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- input/false_false.in
|
||||
- input/false_true.in
|
||||
- input/true_false.in
|
||||
- input/true_true.in
|
||||
- inputs/false_false.in
|
||||
- inputs/false_true.in
|
||||
- inputs/true_false.in
|
||||
- inputs/true_true.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> bool {
|
||||
|
@ -2,10 +2,10 @@
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- input/false_false.in
|
||||
- input/false_true.in
|
||||
- input/true_false.in
|
||||
- input/true_true.in
|
||||
- inputs/false_false.in
|
||||
- inputs/false_true.in
|
||||
- inputs/true_false.in
|
||||
- inputs/true_true.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> bool {
|
||||
|
@ -2,10 +2,10 @@
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- input/false_false.in
|
||||
- input/false_true.in
|
||||
- input/true_false.in
|
||||
- input/true_true.in
|
||||
- inputs/false_false.in
|
||||
- inputs/false_true.in
|
||||
- inputs/true_false.in
|
||||
- inputs/true_true.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> bool {
|
||||
|
@ -2,10 +2,10 @@
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- input/false_false.in
|
||||
- input/false_true.in
|
||||
- input/true_false.in
|
||||
- input/true_true.in
|
||||
- inputs/false_false.in
|
||||
- inputs/false_true.in
|
||||
- inputs/true_false.in
|
||||
- inputs/true_true.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> bool {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user