diff --git a/Cargo.lock b/Cargo.lock index b0fede3470..6bef767a1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1334,6 +1334,7 @@ dependencies = [ "leo-input", "leo-package", "leo-state", + "leo-symbol-table", "notify", "num-bigint", "rand", @@ -1393,6 +1394,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "leo-symbol-table" +version = "1.0.3" +dependencies = [ + "leo-ast", + "leo-imports", + "leo-typed", + "serde", + "thiserror", +] + [[package]] name = "leo-typed" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index 9bf294db3d..e014240d55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,8 @@ members = [ "linter", "package", "typed", - "state" + "state", + "symbol-table", ] [dependencies.leo-compiler] @@ -66,6 +67,10 @@ version = "1.0.3" path = "./state" version = "1.0.3" +[dependencies.leo-symbol-table] +path = "./symbol-table" +version = "1.0.3" + [dependencies.snarkos-algorithms] version = "1.1.3" default-features = false diff --git a/compiler/src/function/function.rs b/compiler/src/function/function.rs index 80b58f9bbb..bca4d761e6 100644 --- a/compiler/src/function/function.rs +++ b/compiler/src/function/function.rs @@ -23,7 +23,7 @@ use crate::{ GroupType, }; -use leo_typed::{Expression, Function, InputVariable, Span, Type}; +use leo_typed::{Expression, Function, FunctionInput, Span, Type}; use snarkos_models::{ curves::{Field, PrimeField}, @@ -57,7 +57,7 @@ impl> ConstrainedProgram { // Store input values as new variables in resolved program for (input_model, input_expression) in function.input.clone().iter().zip(input.into_iter()) { let (name, value) = match input_model { - InputVariable::InputKeyword(identifier) => { + FunctionInput::InputKeyword(identifier) => { let input_value = self.enforce_function_input( cs, scope.clone(), @@ -69,7 +69,7 @@ impl> ConstrainedProgram { (identifier.name.clone(), input_value) } - InputVariable::FunctionInput(input_model) => { + FunctionInput::Variable(input_model) => { // First evaluate input expression let mut input_value = self.enforce_function_input( cs, diff --git a/compiler/src/function/main_function.rs b/compiler/src/function/main_function.rs index 950d1cdc7d..74bd917e04 100644 --- a/compiler/src/function/main_function.rs +++ b/compiler/src/function/main_function.rs @@ -23,7 +23,7 @@ use crate::{ OutputBytes, }; -use leo_typed::{Expression, Function, Input, InputVariable}; +use leo_typed::{Expression, Function, FunctionInput, Input}; use snarkos_models::{ curves::{Field, PrimeField}, @@ -45,12 +45,12 @@ impl> ConstrainedProgram { let mut input_variables = vec![]; for input_model in function.input.clone().into_iter() { let (identifier, value) = match input_model { - InputVariable::InputKeyword(identifier) => { + FunctionInput::InputKeyword(identifier) => { let value = self.allocate_input_keyword(cs, identifier.clone(), &input)?; (identifier, value) } - InputVariable::FunctionInput(input_model) => { + FunctionInput::Variable(input_model) => { let name = input_model.identifier.name.clone(); let input_option = input .get(&name) diff --git a/core/src/packages/unstable/blake2s.rs b/core/src/packages/unstable/blake2s.rs index 4c2a6f7426..2dfa759c88 100644 --- a/core/src/packages/unstable/blake2s.rs +++ b/core/src/packages/unstable/blake2s.rs @@ -22,8 +22,8 @@ use leo_typed::{ Expression, Function, FunctionInput, + FunctionInputVariable, Identifier, - InputVariable, IntegerType, Span, Statement, @@ -67,7 +67,7 @@ impl CoreCircuit for Blake2sCircuit { span: span.clone(), }, input: vec![ - InputVariable::FunctionInput(FunctionInput { + FunctionInput::Variable(FunctionInputVariable { identifier: Identifier { name: "seed".to_owned(), span: span.clone(), @@ -76,7 +76,7 @@ impl CoreCircuit for Blake2sCircuit { type_: Type::Array(Box::new(Type::IntegerType(IntegerType::U8)), vec![32usize]), span: span.clone(), }), - InputVariable::FunctionInput(FunctionInput { + FunctionInput::Variable(FunctionInputVariable { identifier: Identifier { name: "message".to_owned(), span: span.clone(), diff --git a/symbol-table/Cargo.toml b/symbol-table/Cargo.toml new file mode 100644 index 0000000000..3e03c9ebe2 --- /dev/null +++ b/symbol-table/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "leo-symbol-table" +version = "1.0.3" +authors = [ "The Aleo Team " ] +description = "Stores user-defined variables during type resolution" +homepage = "https://aleo.org" +respository = "https://github.com/AleoHQ/leo" +keywords = [ + "aleo", + "cryptography", + "leo", + "programming-language", + "zero-knowledge" +] +categories = [ "cryptography::croptocurrencies", "web-programming" ] +include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ] +license = "GPL-3.0" +edition = "2018" + +[dependencies.leo-ast] +path = "../ast" +version = "1.0.3" + +[dependencies.leo-imports] +path = "../imports" +version = "1.0.3" + +[dependencies.leo-typed] +path = "../typed" +version = "1.0.3" + +[dependencies.serde] +version = "1.0" + +[dependencies.thiserror] +version = "1.0" \ No newline at end of file diff --git a/symbol-table/src/attributes/attribute.rs b/symbol-table/src/attributes/attribute.rs new file mode 100644 index 0000000000..0fa7153b69 --- /dev/null +++ b/symbol-table/src/attributes/attribute.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2019-2020 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 . + +use serde::{Deserialize, Serialize}; + +/// Indicates that a program variable has additional functionality. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Attribute { + Mutable, + Static, +} diff --git a/symbol-table/src/attributes/mod.rs b/symbol-table/src/attributes/mod.rs new file mode 100644 index 0000000000..f8d19f5a62 --- /dev/null +++ b/symbol-table/src/attributes/mod.rs @@ -0,0 +1,18 @@ +// Copyright (C) 2019-2020 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 . + +pub mod attribute; +pub use self::attribute::*; diff --git a/symbol-table/src/errors/mod.rs b/symbol-table/src/errors/mod.rs new file mode 100644 index 0000000000..107ac193f8 --- /dev/null +++ b/symbol-table/src/errors/mod.rs @@ -0,0 +1,21 @@ +// Copyright (C) 2019-2020 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 . + +pub mod symbol_table; +pub use self::symbol_table::*; + +pub mod type_; +pub use self::type_::*; diff --git a/symbol-table/src/errors/symbol_table.rs b/symbol-table/src/errors/symbol_table.rs new file mode 100644 index 0000000000..deca39c4a1 --- /dev/null +++ b/symbol-table/src/errors/symbol_table.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2019-2020 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 . + +use crate::TypeError; +use leo_typed::{Error as FormattedError, Identifier, Span}; + +use std::path::PathBuf; + +/// Errors encountered when tracking variable, function, and circuit names in a program +#[derive(Debug, Error)] +pub enum SymbolTableError { + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + TypeError(#[from] TypeError), +} + +impl SymbolTableError { + /// + /// Set the filepath for the error stacktrace + /// + pub fn set_path(&mut self, path: PathBuf) { + match self { + SymbolTableError::Error(error) => error.set_path(path), + SymbolTableError::TypeError(error) => error.set_path(path), + } + } + + /// + /// Return a new formatted error with a given message and span information + /// + fn new_from_span(message: String, span: Span) -> Self { + SymbolTableError::Error(FormattedError::new_from_span(message, span)) + } + + /// + /// Two circuits have been defined with the same name + /// + pub fn duplicate_circuit(identifier: Identifier, span: Span) -> Self { + let message = format!("Duplicate circuit definition found for `{}`", identifier); + + Self::new_from_span(message, span) + } + + /// + /// Two functions have been defined with the same name + /// + pub fn duplicate_function(identifier: Identifier, span: Span) -> Self { + let message = format!("Duplicate function definition found for `{}`", identifier); + + Self::new_from_span(message, span) + } +} diff --git a/symbol-table/src/errors/type_.rs b/symbol-table/src/errors/type_.rs new file mode 100644 index 0000000000..5ae5f8a9be --- /dev/null +++ b/symbol-table/src/errors/type_.rs @@ -0,0 +1,141 @@ +// Copyright (C) 2019-2020 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 . + +use crate::Type; +use leo_typed::{Error as FormattedError, Identifier, Span}; + +use std::path::PathBuf; + +/// Errors encountered when resolving types. +#[derive(Debug, Error)] +pub enum TypeError { + #[error("{}", _0)] + Error(#[from] FormattedError), +} + +impl TypeError { + /// + /// Set the filepath for the error stacktrace. + /// + pub fn set_path(&mut self, path: PathBuf) { + match self { + TypeError::Error(error) => error.set_path(path), + } + } + + /// + /// Return a new formatted error with a given message and span information. + /// + fn new_from_span(message: String, span: Span) -> Self { + TypeError::Error(FormattedError::new_from_span(message, span)) + } + + /// + /// Expected an array type from the given expression. + /// + pub fn invalid_array(actual: &Type, span: Span) -> Self { + let message = format!("Expected array type, found type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Expected a circuit type from the given expression. + /// + pub fn invalid_circuit(actual: &Type, span: Span) -> Self { + let message = format!("Expected circuit type, found type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Expected a function type from the given expression. + /// + pub fn invalid_function(actual: &Type, span: Span) -> Self { + let message = format!("Expected function type, found type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Expected an integer type from the given expression. + /// + pub fn invalid_integer(actual: &Type, span: Span) -> Self { + let message = format!("Expected integer type, found type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// Expected a tuple type from the given expression. + /// + pub fn invalid_tuple(actual: &Type, span: Span) -> Self { + let message = format!("Expected tuple type, found type `{}`.", actual); + + Self::new_from_span(message, span) + } + + /// + /// The value of the expression does not match the given explicit type. + /// + pub fn mismatched_types(expected: &Type, actual: &Type, span: Span) -> Self { + let message = format!("Expected type `{}`, found type `{}`.", expected, actual); + + Self::new_from_span(message, span) + } + + /// + /// The `Self` keyword was used outside of a circuit. + /// + pub fn self_not_available(span: Span) -> Self { + let message = format!("Type `Self` is only available in circuit definitions and circuit functions."); + + Self::new_from_span(message, span) + } + + /// + /// Found an unknown circuit name. + /// + pub fn undefined_circuit(identifier: Identifier) -> Self { + let message = format!( + "Type circuit `{}` must be defined before it is used in an expression.", + identifier.name + ); + + Self::new_from_span(message, identifier.span) + } + + /// + /// Found an unknown circuit member name. + /// + pub fn undefined_circuit_member(identifier: Identifier) -> Self { + let message = format!("Circuit has no member `{}`.", identifier.name); + + Self::new_from_span(message, identifier.span) + } + + /// + /// Found an unknown function name. + /// + pub fn undefined_function(identifier: Identifier) -> Self { + let message = format!( + "Type function `{}` must be defined before it is used in an expression.", + identifier.name + ); + + Self::new_from_span(message, identifier.span) + } +} diff --git a/symbol-table/src/lib.rs b/symbol-table/src/lib.rs new file mode 100644 index 0000000000..f4a5aaa97a --- /dev/null +++ b/symbol-table/src/lib.rs @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2020 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 . + +#[macro_use] +extern crate thiserror; + +pub mod attributes; +pub use self::attributes::*; + +pub mod errors; +pub use self::errors::*; + +pub mod symbol_table; +pub use self::symbol_table::*; + +pub mod types; +pub use self::types::*; + +/// A resolved node in an abstract syntax tree (AST). +/// +/// Resolved nodes can be any function, statement, expression, type, etc. in an AST. +/// Resolved nodes should not contain any illegal types. +/// Resolved nodes should not contain any implicit types. +pub trait ResolvedNode { + /// The expected error type if the type resolution fails. + type Error; + + /// The unresolved AST node that is being resolved. + type UnresolvedNode; + + /// + /// Returns a resolved AST representation given an unresolved AST representation. + /// + /// User-defined types are looked up using the given symbol table. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result + where + Self: std::marker::Sized; +} diff --git a/symbol-table/src/symbol_table.rs b/symbol-table/src/symbol_table.rs new file mode 100644 index 0000000000..452269e8fb --- /dev/null +++ b/symbol-table/src/symbol_table.rs @@ -0,0 +1,291 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{CircuitType, FunctionType, ResolvedNode, SymbolTableError, VariableType}; +use leo_typed::{Circuit, Function, Identifier, Program as UnresolvedProgram}; + +use leo_imports::ImportParser; +use std::collections::HashMap; + +/// A abstract data type that tracks the current bindings of identifier +/// names to types in a Leo program. +/// +/// A symbol table has access to all function and circuit names in its +/// parent's symbol table. +/// A symbol table cannot access names in its child's symbol table. +/// Children cannot access names in another sibling's symbol table. +#[derive(Clone)] +pub struct SymbolTable { + /// Maps variable name -> variable type. + variables: HashMap, + + /// Maps circuit name -> circuit type. + circuits: HashMap, + + ///Maps function name -> function type. + functions: HashMap, + + /// The parent of this symbol table. + parent: Option>, +} + +impl SymbolTable { + /// + /// Creates a new symbol table with a given parent symbol table. + /// + pub fn new(parent: Option>) -> Self { + SymbolTable { + variables: HashMap::new(), + circuits: HashMap::new(), + functions: HashMap::new(), + parent, + } + } + + /// + /// Insert a variable into the symbol table from a given name and variable type. + /// + /// If the symbol table did not have this name present, `None` is returned. + /// If the symbol table did have this name present, the variable type is updated, and the old + /// variable type is returned. + /// + pub fn insert_variable(&mut self, name: String, variable_type: VariableType) -> Option { + self.variables.insert(name, variable_type) + } + + /// + /// Insert a circuit definition into the symbol table from a given circuit identifier and + /// circuit type. + /// + /// If the symbol table did not have this name present, `None` is returned. + /// If the symbol table did have this name present, the circuit type is updated, and the old + /// circuit type is returned. + /// + pub fn insert_circuit(&mut self, identifier: Identifier, circuit_type: CircuitType) -> Option { + self.circuits.insert(identifier.name, circuit_type) + } + + /// + /// Insert a function definition into the symbol table from a given identifier and + /// function type. + /// + /// If the symbol table did not have this name present, `None` is returned. + /// If the symbol table did have this name present, the function type is updated, and the old + /// function type is returned. + /// + pub fn insert_function(&mut self, identifier: Identifier, function_type: FunctionType) -> Option { + self.functions.insert(identifier.name, function_type) + } + + /// + /// Returns a reference to the variable type corresponding to the name. + /// + /// If the symbol table did not have this name present, then `None` is returned. + /// + pub fn get_variable(&self, name: &String) -> Option<&VariableType> { + // Lookup variable name in symbol table. + match self.variables.get(name) { + Some(variable) => Some(variable), + None => None, + } + } + + /// + /// Returns a reference to the circuit type corresponding to the name. + /// + /// If the symbol table did not have this name present, then the parent symbol table is checked. + /// If there is no parent symbol table, then `None` is returned. + /// + pub fn get_circuit(&self, name: &String) -> Option<&CircuitType> { + // Lookup name in symbol table. + match self.circuits.get(name) { + Some(circuit) => Some(circuit), + None => { + // Lookup name in parent symbol table. + match &self.parent { + Some(parent) => parent.get_circuit(name), + None => None, + } + } + } + } + + /// + /// Returns a reference to the function type corresponding to the name. + /// + /// If the symbol table did not have this name present, then the parent symbol table is checked. + /// If there is no parent symbol table, then `None` is returned. + /// + pub fn get_function(&self, key: &String) -> Option<&FunctionType> { + // Lookup name in symbol table. + match self.functions.get(key) { + Some(circuit) => Some(circuit), + None => { + // Lookup name in parent symbol table + match &self.parent { + Some(parent) => parent.get_function(key), + None => None, + } + } + } + } + + /// + /// Inserts all imported identifiers for a given list of imported programs. + /// + /// No type resolution performed at this step. + /// + pub fn insert_imports(&mut self, _imports: ImportParser) {} + + /// + /// Checks for duplicate circuit names given a hashmap of unresolved circuits. + /// + /// If a circuit name has no duplicates, then it is inserted into the symbol table. + /// Variables defined later in the unresolved program cannot have the same name. + /// + pub fn check_duplicate_circuits( + &mut self, + circuits: &HashMap, + ) -> Result<(), SymbolTableError> { + // Iterate over circuit names and definitions. + for (identifier, circuit) in circuits.iter() { + // Attempt to insert the circuit name into the symbol table. + let duplicate = self.insert_variable(identifier.to_string(), VariableType::from(circuit.clone())); + + // Check that the circuit name is unique. + if duplicate.is_some() { + return Err(SymbolTableError::duplicate_circuit( + identifier.clone(), + circuit.circuit_name.span.clone(), + )); + } + } + + Ok(()) + } + + /// + /// Checks for duplicate function names given a hashmap of unresolved functions. + /// + /// If a function name has no duplicates, then it is inserted into the symbol table. + /// Variables defined later in the unresolved program cannot have the same name. + /// + pub fn check_duplicate_functions( + &mut self, + functions: &HashMap, + ) -> Result<(), SymbolTableError> { + // Iterate over function names and definitions. + for (identifier, function) in functions.iter() { + // Attempt to insert the function name into the symbol table. + let duplicate = self.insert_variable(identifier.to_string(), VariableType::from(function.clone())); + + // Check that the function name is unique. + if duplicate.is_some() { + return Err(SymbolTableError::duplicate_function( + identifier.clone(), + function.identifier.span.clone(), + )); + } + } + + Ok(()) + } + + /// + /// Checks for unknown types in a circuit given a hashmap of unresolved circuits. + /// + /// If a circuit definition only contains known types, then it is inserted into the + /// symbol table. Variables defined later in the unresolved program can lookup the definition + /// and refer to its expected types + /// + pub fn check_unknown_types_circuits( + &mut self, + circuits: &HashMap, + ) -> Result<(), SymbolTableError> { + // Iterate over circuit names and definitions. + for (_, circuit) in circuits.iter() { + // Get the identifier of the unresolved circuit. + let identifier = circuit.circuit_name.clone(); + + // Resolve unknown types in the unresolved circuit definition. + let circuit_type = CircuitType::resolve(self, circuit.clone())?; + + // Attempt to insert the circuit definition into the symbol table. + self.insert_circuit(identifier, circuit_type); + } + + Ok(()) + } + + /// + /// Checks for unknown types in a function given a hashmap of unresolved functions. + /// + /// If a function definition only contains known types, then it is inserted into the + /// symbol table. Variables defined later in the unresolved program can lookup the definition + /// and refer to its expected types + /// + pub fn check_unknown_types_functions( + &mut self, + functions: &HashMap, + ) -> Result<(), SymbolTableError> { + // Iterate over function names and definitions. + for (_, function) in functions.iter() { + // Get the identifier of the unresolved function. + let identifier = function.identifier.clone(); + + // Resolve unknown types in the unresolved function definition. + let function_type = FunctionType::resolve(self, function.clone())?; + + // Attempt to insert the function definition into the symbol table. + self.insert_function(identifier, function_type); + } + + Ok(()) + } + + /// + /// Checks for duplicate circuit and function names given an unresolved program. + /// + /// If a circuit or function name has no duplicates, then it is inserted into the symbol table. + /// Variables defined later in the unresolved program cannot have the same name. + /// + pub fn pass_one(&mut self, program: &UnresolvedProgram) -> Result<(), SymbolTableError> { + // Check unresolved program circuit names. + self.check_duplicate_circuits(&program.circuits)?; + + // Check unresolved program function names. + self.check_duplicate_functions(&program.functions)?; + + Ok(()) + } + + /// + /// Checks for unknown types in circuit and function definitions given an unresolved program. + /// + /// If a circuit or function definition only contains known types, then it is inserted into the + /// symbol table. Variables defined later in the unresolved program can lookup the definition and + /// refer to its expected types. + /// + pub fn pass_two(&mut self, program: &UnresolvedProgram) -> Result<(), SymbolTableError> { + // Check unresolved program circuit definitions. + self.check_unknown_types_circuits(&program.circuits)?; + + // Check unresolved program function definitions. + self.check_unknown_types_functions(&program.functions)?; + + Ok(()) + } +} diff --git a/symbol-table/src/types/circuits/circuit.rs b/symbol-table/src/types/circuits/circuit.rs new file mode 100644 index 0000000000..8d1df94a91 --- /dev/null +++ b/symbol-table/src/types/circuits/circuit.rs @@ -0,0 +1,144 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{ + types::circuits::{CircuitFunctionType, CircuitVariableType}, + Attribute, + FunctionType, + ResolvedNode, + SymbolTable, + Type, + TypeError, +}; +use leo_typed::{Circuit, CircuitMember, Identifier}; + +use serde::{Deserialize, Serialize}; + +/// Stores circuit definition details. +/// +/// This type should be added to the circuit symbol table for a resolved syntax tree. +/// This is a user-defined type. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct CircuitType { + /// The name of the circuit definition. + pub identifier: Identifier, + + /// The circuit variables. + pub variables: Vec, + + /// The circuit functions. + pub functions: Vec, +} + +impl ResolvedNode for CircuitType { + type Error = TypeError; + type UnresolvedNode = Circuit; + + /// + /// Return a new `CircuitType` from a given `Circuit` definition. + /// + /// Performs a lookup in the given symbol table if the circuit definition contains + /// user-defined types. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + let circuit_identifier = unresolved.circuit_name; + let mut variables = vec![]; + let mut functions = vec![]; + + // Resolve the type of every circuit member. + for member in unresolved.members { + match member { + CircuitMember::CircuitVariable(is_mutable, variable_identifier, type_) => { + // Resolve the type of the circuit member variable. + let type_ = Type::from_circuit( + table, + type_, + circuit_identifier.clone(), + circuit_identifier.span.clone(), + )?; + + // Check if the circuit member variable is mutable. + let attributes = if is_mutable { vec![Attribute::Mutable] } else { vec![] }; + + // Create a new circuit variable type. + let variable = CircuitVariableType { + identifier: variable_identifier, + type_, + attributes, + }; + + // Store the circuit variable type. + variables.push(variable); + } + CircuitMember::CircuitFunction(is_static, function) => { + // Resolve the type of the circuit member function. + let function_type = FunctionType::from_circuit(table, circuit_identifier.clone(), function)?; + + // Check if the circuit member function is static. + let attributes = if is_static { vec![Attribute::Static] } else { vec![] }; + + // Create a new circuit function type. + let function = CircuitFunctionType { + function: function_type, + attributes, + }; + + // Store the circuit function type. + functions.push(function); + } + } + } + + // Return a new circuit type. + Ok(CircuitType { + identifier: circuit_identifier.clone(), + variables, + functions, + }) + } +} + +impl CircuitType { + /// + /// Returns the type of a circuit member. + /// + /// If the member is a circuit variable, then the type of the variable is returned. + /// If the member is a circuit function, then the return type of the function is returned. + /// + pub fn member_type(&self, identifier: &Identifier) -> Result<&Type, TypeError> { + // Check if the circuit member is a circuit variable. + let matched_variable = self + .variables + .iter() + .find(|variable| variable.identifier.eq(identifier)); + + match matched_variable { + Some(variable) => Ok(&variable.type_), + None => { + // Check if the circuit member is a circuit function. + let matched_function = self + .functions + .iter() + .find(|function| function.function.identifier.eq(identifier)); + + match matched_function { + Some(function) => Ok(&function.function.output.type_), + None => Err(TypeError::undefined_circuit_member(identifier.clone())), + } + } + } + } +} diff --git a/symbol-table/src/types/circuits/circuit_function.rs b/symbol-table/src/types/circuits/circuit_function.rs new file mode 100644 index 0000000000..0eff740236 --- /dev/null +++ b/symbol-table/src/types/circuits/circuit_function.rs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{types::FunctionType, Attribute}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct CircuitFunctionType { + /// The function signature of the circuit function + pub function: FunctionType, + /// The attributes of the circuit function + pub attributes: Vec, +} diff --git a/symbol-table/src/types/circuits/circuit_variable.rs b/symbol-table/src/types/circuits/circuit_variable.rs new file mode 100644 index 0000000000..55b8326b8a --- /dev/null +++ b/symbol-table/src/types/circuits/circuit_variable.rs @@ -0,0 +1,30 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{Attribute, Type}; +use leo_typed::Identifier; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct CircuitVariableType { + /// The name of the circuit variable + pub identifier: Identifier, + /// The type of the circuit variable + pub type_: Type, + /// The attributes of the circuit variable + pub attributes: Vec, +} diff --git a/symbol-table/src/types/circuits/mod.rs b/symbol-table/src/types/circuits/mod.rs new file mode 100644 index 0000000000..8205016c50 --- /dev/null +++ b/symbol-table/src/types/circuits/mod.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2019-2020 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 . + +pub mod circuit; +pub use self::circuit::*; + +pub mod circuit_function; +pub use self::circuit_function::*; + +pub mod circuit_variable; +pub use self::circuit_variable::*; diff --git a/symbol-table/src/types/functions/function.rs b/symbol-table/src/types/functions/function.rs new file mode 100644 index 0000000000..f446ec95ec --- /dev/null +++ b/symbol-table/src/types/functions/function.rs @@ -0,0 +1,127 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{ + types::functions::{FunctionInputType, FunctionOutputType}, + ResolvedNode, + SymbolTable, + TypeError, +}; +use leo_typed::{Function, Identifier}; + +use serde::{Deserialize, Serialize}; + +/// Stores function definition details. +/// +/// This type should be added to the function symbol table for a resolved syntax tree. +/// This is a user-defined type. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FunctionType { + /// The name of the function definition. + pub identifier: Identifier, + + /// The function inputs. + pub inputs: Vec, + + /// The function output. + pub output: FunctionOutputType, +} + +impl ResolvedNode for FunctionType { + type Error = TypeError; + type UnresolvedNode = Function; + + /// + /// Return a new `FunctionType` from a given `Function` definition. + /// + /// Performs a lookup in the given symbol table if the function definition contains + /// user-defined types. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + let mut inputs_resolved = vec![]; + + // Type check function inputs + for input in unresolved.input { + let input = FunctionInputType::resolve(table, input)?; + inputs_resolved.push(input); + } + + // Type check function output + let output = FunctionOutputType::resolve(table, (unresolved.returns, unresolved.span))?; + + Ok(FunctionType { + identifier: unresolved.identifier, + inputs: inputs_resolved, + output, + }) + } +} + +impl FunctionType { + /// + /// Resolve a function definition and insert it into the given symbol table. + /// + pub fn insert_definition(table: &mut SymbolTable, unresolved_function: Function) -> Result<(), TypeError> { + // Get the identifier of the function. + let function_identifier = unresolved_function.identifier.clone(); + + // Resolve the function definition into a function type. + let function = Self::resolve(table, unresolved_function)?; + + // Insert (function_identifier -> function_type) as a (key -> value) pair in the symbol table. + table.insert_function(function_identifier, function); + + Ok(()) + } + + /// + /// Return a new `FunctionType` from a given `Function` definition. + /// + /// Performs a lookup in the given symbol table if the function definition contains + /// user-defined types. + /// + /// If the function definition contains the `Self` keyword, then the given circuit identifier + /// is used as the type. + /// + pub fn from_circuit( + table: &mut SymbolTable, + circuit_name: Identifier, + unresolved_function: Function, + ) -> Result { + let function_identifier = unresolved_function.identifier; + let mut inputs = vec![]; + + // Type check function inputs. + for unresolved_input in unresolved_function.input { + let input = FunctionInputType::from_circuit(table, unresolved_input, circuit_name.clone())?; + inputs.push(input); + } + + // Type check function output. + let output = FunctionOutputType::from_circuit( + table, + circuit_name.clone(), + unresolved_function.returns, + unresolved_function.span, + )?; + + Ok(FunctionType { + identifier: function_identifier.clone(), + inputs, + output, + }) + } +} diff --git a/symbol-table/src/types/functions/function_input.rs b/symbol-table/src/types/functions/function_input.rs new file mode 100644 index 0000000000..74df8ede4c --- /dev/null +++ b/symbol-table/src/types/functions/function_input.rs @@ -0,0 +1,109 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{FunctionInputVariableType, ResolvedNode, SymbolTable, Type, TypeError, VariableType}; +use leo_typed::{FunctionInput, Identifier}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum FunctionInputType { + InputKeyword(Identifier), + Variable(FunctionInputVariableType), +} + +impl ResolvedNode for FunctionInputType { + type Error = TypeError; + type UnresolvedNode = FunctionInput; + + /// + /// Return a new `FunctionInputType` from a given `FunctionInput`. + /// + /// Performs a lookup in the given symbol table if the function input contains + /// user-defined types. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + Ok(match unresolved { + FunctionInput::InputKeyword(identifier) => FunctionInputType::InputKeyword(identifier), + FunctionInput::Variable(variable) => { + let variable_resolved = FunctionInputVariableType::resolve(table, variable)?; + + FunctionInputType::Variable(variable_resolved) + } + }) + } +} + +impl FunctionInputType { + /// + /// Return the `Identifier` containing name and span information about the current function input. + /// + pub fn identifier(&self) -> &Identifier { + match self { + FunctionInputType::InputKeyword(identifier) => identifier, + FunctionInputType::Variable(variable) => &variable.identifier, + } + } + + /// + /// Return the `Type` of the current function input. + /// + pub fn type_(&self) -> &Type { + match self { + FunctionInputType::InputKeyword(_) => unimplemented!("ERROR: input type not implemented"), + FunctionInputType::Variable(variable) => &variable.type_, + } + } + + /// + /// Return a new `FunctionInputType` from a given `FunctionInput`. + /// + /// Performs a lookup in the given symbol table if the function input contains + /// user-defined types. + /// + /// If the type of the function input is the `Self` keyword, then the given circuit identifier + /// is used as the type. + /// + pub fn from_circuit( + table: &mut SymbolTable, + unresolved: FunctionInput, + circuit_name: Identifier, + ) -> Result { + Ok(match unresolved { + FunctionInput::InputKeyword(identifier) => FunctionInputType::InputKeyword(identifier), + FunctionInput::Variable(unresolved_function_input) => { + let function_input = + FunctionInputVariableType::from_circuit(table, unresolved_function_input, circuit_name)?; + + FunctionInputType::Variable(function_input) + } + }) + } + + /// + /// Insert the current function input type into the given symbol table. + /// + /// If the symbol table did not have this name present, `None` is returned. + /// + pub fn insert(&self, table: &mut SymbolTable) -> Option { + match self { + FunctionInputType::Variable(variable) => variable.insert(table), + FunctionInputType::InputKeyword(_identifier) => { + unimplemented!("uncomment when support for input types is added") + } + } + } +} diff --git a/symbol-table/src/types/functions/function_input_variable.rs b/symbol-table/src/types/functions/function_input_variable.rs new file mode 100644 index 0000000000..5987d5e78f --- /dev/null +++ b/symbol-table/src/types/functions/function_input_variable.rs @@ -0,0 +1,108 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{Attribute, ResolvedNode, SymbolTable, Type, TypeError, VariableType}; +use leo_typed::{FunctionInputVariable, Identifier, Span}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FunctionInputVariableType { + /// Name of function input. + pub identifier: Identifier, + + /// Type of function input. + pub type_: Type, + + /// The attributes of the function input. + pub attributes: Vec, + + /// The span of the function input. + pub span: Span, +} + +impl ResolvedNode for FunctionInputVariableType { + type Error = TypeError; + type UnresolvedNode = FunctionInputVariable; + + /// + /// Return a new `FunctionInputVariableType` from a given `FunctionInputVariable`. + /// + /// Performs a lookup in the given symbol table if the type is user-defined. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + let type_ = Type::resolve(table, (unresolved.type_, unresolved.span.clone()))?; + let attributes = if unresolved.mutable { + vec![Attribute::Mutable] + } else { + vec![] + }; + + Ok(FunctionInputVariableType { + identifier: unresolved.identifier, + type_, + attributes, + span: unresolved.span, + }) + } +} + +impl FunctionInputVariableType { + /// + /// Return a new `FunctionInputVariableType` from a given `FunctionInputVariable`. + /// + /// Performs a lookup in the given symbol table if the type is user-defined. + /// + /// If the type of the function return type is the `Self` keyword, then the given circuit + /// identifier is used as the type. + /// + pub fn from_circuit( + table: &mut SymbolTable, + unresolved_function_input: FunctionInputVariable, + circuit_name: Identifier, + ) -> Result { + let type_ = Type::from_circuit( + table, + unresolved_function_input.type_, + circuit_name, + unresolved_function_input.span.clone(), + )?; + let attributes = if unresolved_function_input.mutable { + vec![Attribute::Mutable] + } else { + vec![] + }; + + Ok(FunctionInputVariableType { + identifier: unresolved_function_input.identifier, + type_, + attributes, + span: unresolved_function_input.span, + }) + } + + /// + /// Insert the current function input variable type into the given symbol table. + /// + /// If the symbol table did not have this name present, `None` is returned. + /// + pub fn insert(&self, table: &mut SymbolTable) -> Option { + let key = self.identifier.name.clone(); + let value = VariableType::from(self.clone()); + + table.insert_variable(key, value) + } +} diff --git a/symbol-table/src/types/functions/function_output.rs b/symbol-table/src/types/functions/function_output.rs new file mode 100644 index 0000000000..0f403b6ae4 --- /dev/null +++ b/symbol-table/src/types/functions/function_output.rs @@ -0,0 +1,74 @@ +// Copyright (C) 2019-2020 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 . + +use crate::{ResolvedNode, SymbolTable, Type, TypeError}; + +use leo_typed::{Identifier, Span, Type as UnresolvedType}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FunctionOutputType { + /// Type of function output. + pub type_: Type, +} + +impl ResolvedNode for FunctionOutputType { + type Error = TypeError; + /// (optional function output, span) + type UnresolvedNode = (Option, Span); + + /// + /// Return a new `FunctionOutputType` from a given optional function return type and span. + /// + /// Performs a lookup in the given symbol table if the return type is user-defined. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + let function_output = unresolved.0; + let span = unresolved.1; + + let type_ = match function_output { + None => Type::Tuple(vec![]), // functions with no return value return an empty tuple + Some(type_) => Type::resolve(table, (type_, span))?, + }; + + Ok(FunctionOutputType { type_ }) + } +} + +impl FunctionOutputType { + /// + /// Return a new `FunctionOutputType` from a given optional function return type and span. + /// + /// Performs a lookup in the given symbol table if the return type is user-defined. + /// + /// If the type of the function return type is the `Self` keyword, then the given circuit + /// identifier is used as the type. + /// + pub fn from_circuit( + table: &mut SymbolTable, + circuit_name: Identifier, + unresolved: Option, + span: Span, + ) -> Result { + let output_type = match unresolved { + None => Type::Tuple(vec![]), + Some(type_) => Type::from_circuit(table, type_, circuit_name, span)?, + }; + + Ok(FunctionOutputType { type_: output_type }) + } +} diff --git a/symbol-table/src/types/functions/mod.rs b/symbol-table/src/types/functions/mod.rs new file mode 100644 index 0000000000..fe22157837 --- /dev/null +++ b/symbol-table/src/types/functions/mod.rs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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 . + +pub mod function; +pub use self::function::*; + +pub mod function_input; +pub use self::function_input::*; + +pub mod function_input_variable; +pub use self::function_input_variable::*; + +pub mod function_output; +pub use self::function_output::*; diff --git a/symbol-table/src/types/mod.rs b/symbol-table/src/types/mod.rs new file mode 100644 index 0000000000..0d27661ad9 --- /dev/null +++ b/symbol-table/src/types/mod.rs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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 . + +pub mod circuits; +pub use self::circuits::*; + +pub mod functions; +pub use self::functions::*; + +pub mod type_; +pub use self::type_::*; + +pub mod variables; +pub use self::variables::*; diff --git a/symbol-table/src/types/type_.rs b/symbol-table/src/types/type_.rs new file mode 100644 index 0000000000..9a13e979a1 --- /dev/null +++ b/symbol-table/src/types/type_.rs @@ -0,0 +1,221 @@ +// Copyright (C) 2019-2020 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 . +use crate::{ResolvedNode, SymbolTable, TypeError}; +use leo_typed::{Identifier, IntegerType, Span, Type as UnresolvedType}; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// A resolved type in a Leo program. +/// +/// This type cannot be an implicit or `Self` type. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Type { + // Data types + Address, + Boolean, + Field, + Group, + IntegerType(IntegerType), + + // Data type wrappers + Array(Box, Vec), + Tuple(Vec), + + // User defined types + Circuit(Identifier), + Function(Identifier), +} + +impl ResolvedNode for Type { + type Error = TypeError; + type UnresolvedNode = (UnresolvedType, Span); + + /// + /// Resolves the given type. + /// + /// Cannot be an implicit or `Self` type. + /// + fn resolve(table: &mut SymbolTable, unresolved: Self::UnresolvedNode) -> Result { + let type_ = unresolved.0; + let span = unresolved.1; + + Ok(match type_ { + UnresolvedType::Address => Type::Address, + UnresolvedType::Boolean => Type::Boolean, + UnresolvedType::Field => Type::Field, + UnresolvedType::Group => Type::Group, + UnresolvedType::IntegerType(integer) => Type::IntegerType(integer), + + UnresolvedType::Array(type_, dimensions) => { + let array_type = Type::resolve(table, (*type_, span))?; + + Type::Array(Box::new(array_type), dimensions) + } + UnresolvedType::Tuple(types) => { + let tuple_types = types + .into_iter() + .map(|type_| Type::resolve(table, (type_, span.clone()))) + .collect::, _>>()?; + + Type::Tuple(tuple_types) + } + + UnresolvedType::Circuit(identifier) => { + // Lookup the circuit type in the symbol table + let circuit_type = table + .get_circuit(&identifier.name) + .ok_or(TypeError::undefined_circuit(identifier))?; + + Type::Circuit(circuit_type.identifier.clone()) + } + + UnresolvedType::SelfType => { + // Throw an error for using `Self` outside of a circuit + return Err(TypeError::self_not_available(span)); + } + }) + } +} + +impl Type { + /// + /// Resolve a type inside of a circuit definition. + /// + /// If this type is SelfType, return the circuit's type. + /// + pub fn from_circuit( + table: &mut SymbolTable, + type_: UnresolvedType, + circuit_name: Identifier, + span: Span, + ) -> Result { + Ok(match type_ { + UnresolvedType::Array(type_, dimensions) => { + let array_type = Type::from_circuit(table, *type_, circuit_name, span)?; + Type::Array(Box::new(array_type), dimensions) + } + UnresolvedType::Tuple(types) => { + let tuple_types = types + .into_iter() + .map(|type_| Type::from_circuit(table, type_, circuit_name.clone(), span.clone())) + .collect::, _>>()?; + + Type::Tuple(tuple_types) + } + UnresolvedType::SelfType => Type::Circuit(circuit_name), + // The unresolved type does not depend on the current circuit definition + unresolved => Type::resolve(table, (unresolved, span))?, + }) + } + + /// + /// Returns `Ok` if the given expected type is `Some` and expected type == actual type. + /// + pub fn check_type(expected_option: &Option, actual: &Type, span: Span) -> Result<(), TypeError> { + if let Some(expected) = expected_option { + if expected.ne(actual) { + return Err(TypeError::mismatched_types(expected, actual, span)); + } + } + Ok(()) + } + + /// + /// Returns `Ok` if self is an expected integer type `Type::IntegerType`. + /// + pub fn check_type_integer(&self, span: Span) -> Result<(), TypeError> { + match self { + Type::IntegerType(_) => Ok(()), + // Throw mismatched type error + type_ => Err(TypeError::invalid_integer(type_, span)), + } + } + + /// + /// Returns array element type and dimensions if self is an expected array type `Type::Array`. + /// + pub fn get_type_array(&self, span: Span) -> Result<(&Type, &Vec), TypeError> { + match self { + Type::Array(element_type, dimensions) => Ok((element_type, dimensions)), + // Throw mismatched type error + type_ => Err(TypeError::invalid_array(type_, span)), + } + } + + /// + /// Returns tuple element types if self is an expected tuple type `Type::Tuple`. + /// + pub fn get_type_tuple(&self, span: Span) -> Result<&Vec, TypeError> { + match self { + Type::Tuple(types) => Ok(types), + // Throw mismatched type error + type_ => Err(TypeError::invalid_tuple(type_, span)), + } + } + + /// + /// Returns circuit identifier if self is an expected circuit type `Type::Circuit`. + /// + pub fn get_type_circuit(&self, span: Span) -> Result<&Identifier, TypeError> { + match self { + Type::Circuit(identifier) => Ok(identifier), + // Throw mismatched type error + type_ => Err(TypeError::invalid_circuit(type_, span)), + } + } + + /// + /// Returns function identifier if self is an expected function type `Type::Function`. + /// + pub fn get_type_function(&self, span: Span) -> Result<&Identifier, TypeError> { + match self { + Type::Function(identifier) => Ok(identifier), + // Throw mismatched type error + type_ => Err(TypeError::invalid_function(type_, span)), + } + } +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self { + Type::Address => write!(f, "address"), + Type::Boolean => write!(f, "bool"), + Type::Field => write!(f, "field"), + Type::Group => write!(f, "group"), + Type::IntegerType(integer_type) => write!(f, "{}", integer_type), + + Type::Array(type_, dimensions) => { + let dimensions_string = dimensions + .iter() + .map(|dimension| format!("{}", dimension)) + .collect::>() + .join(", "); + + write!(f, "[{}; ({})]", *type_, dimensions_string) + } + Type::Tuple(tuple) => { + let tuple_string = tuple.iter().map(|x| format!("{}", x)).collect::>().join(", "); + + write!(f, "({})", tuple_string) + } + + Type::Circuit(identifier) => write!(f, "circuit {}", identifier), + Type::Function(identifier) => write!(f, "function {}", identifier), + } + } +} diff --git a/symbol-table/src/types/variables/mod.rs b/symbol-table/src/types/variables/mod.rs new file mode 100644 index 0000000000..80950ed0c3 --- /dev/null +++ b/symbol-table/src/types/variables/mod.rs @@ -0,0 +1,18 @@ +// Copyright (C) 2019-2020 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 . + +pub mod variable; +pub use self::variable::*; diff --git a/symbol-table/src/types/variables/variable.rs b/symbol-table/src/types/variables/variable.rs new file mode 100644 index 0000000000..4d30977fb7 --- /dev/null +++ b/symbol-table/src/types/variables/variable.rs @@ -0,0 +1,79 @@ +// Copyright (C) 2019-2020 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 . +use crate::{Attribute, Type}; +use leo_typed::{Circuit, Function, Identifier}; + +use crate::FunctionInputVariableType; +use std::fmt; + +/// Stores variable definition details. +/// +/// This type should be added to the variable symbol table for a resolved syntax tree. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct VariableType { + pub identifier: Identifier, + pub type_: Type, + pub attributes: Vec, +} + +impl VariableType { + /// + /// Returns `true` if this variable's value can be modified. + /// + pub fn is_mutable(&self) -> bool { + self.attributes.contains(&Attribute::Mutable) + } +} + +impl From for VariableType { + fn from(value: Circuit) -> Self { + let identifier = value.circuit_name; + + VariableType { + identifier: identifier.clone(), + type_: Type::Circuit(identifier), + attributes: vec![], + } + } +} + +impl From for VariableType { + fn from(value: Function) -> Self { + let identifier = value.identifier; + + VariableType { + identifier: identifier.clone(), + type_: Type::Function(identifier.clone()), + attributes: vec![], + } + } +} + +impl From for VariableType { + fn from(value: FunctionInputVariableType) -> Self { + VariableType { + identifier: value.identifier, + type_: value.type_, + attributes: value.attributes, + } + } +} + +impl fmt::Display for VariableType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.identifier) + } +} diff --git a/symbol-table/tests/mod.rs b/symbol-table/tests/mod.rs new file mode 100644 index 0000000000..1ee70d31bb --- /dev/null +++ b/symbol-table/tests/mod.rs @@ -0,0 +1,115 @@ +// Copyright (C) 2019-2020 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 . + +pub mod symbol_table; + +use leo_ast::LeoAst; +use leo_symbol_table::{SymbolTable, SymbolTableError}; +use leo_typed::LeoTypedAst; + +use std::path::PathBuf; + +const TEST_PROGRAM_PATH: &str = ""; + +/// A helper struct to test a `SymbolTable`. +pub struct TestSymbolTable { + typed: LeoTypedAst, +} + +impl TestSymbolTable { + /// + /// Returns a typed syntax tree given a Leo program. + /// + pub fn new(bytes: &[u8]) -> Self { + // Get file string from bytes. + let file_string = String::from_utf8_lossy(bytes); + + // Get test file path. + let file_path = PathBuf::from(TEST_PROGRAM_PATH); + + // Get parser syntax tree + let ast = LeoAst::new(&file_path, &*file_string).unwrap(); + + // Get typed syntax tree + let typed = LeoTypedAst::new(TEST_PROGRAM_PATH, &ast); + + Self { typed } + } + + /// + /// Parse the typed syntax tree into a symbol table. + /// + /// Expect no errors during parsing. + /// + pub fn expect_success(self) { + // Get program. + let program = self.typed.into_repr(); + + // Create new symbol table. + let symbol_table = &mut SymbolTable::new(None); + + // Run the first pass to check for duplicate names. + symbol_table.pass_one(&program).unwrap(); + + // Run the second pass to check for invalid definitions. + symbol_table.pass_two(&program).unwrap(); + } + + /// + /// Parse the typed syntax tree into a symbol table. + /// + /// Expect an error involving entries in the symbol table. + /// + pub fn expect_pass_one_error(self) { + // Get program. + let program = self.typed.into_repr(); + + // Create new symbol table. + let symbol_table = &mut SymbolTable::new(None); + + // Run pass one and expect an error. + let error = symbol_table.pass_one(&program).unwrap_err(); + + match error { + SymbolTableError::Error(_) => {} // Ok + error => panic!("Expected a symbol table error found `{}`", error), + } + } + + /// + /// Parse the typed syntax tree into a symbol table. + /// + /// Expect an error involving types in the symbol table. + /// + pub fn expect_pass_two_error(self) { + // Get program. + let program = self.typed.into_repr(); + + // Create a new symbol table. + let symbol_table = &mut SymbolTable::new(None); + + // Run the pass one and expect no errors. + symbol_table.pass_one(&program).unwrap(); + + // Run the pass two and expect and error. + let error = symbol_table.pass_two(&program).unwrap_err(); + + match error { + SymbolTableError::TypeError(_) => {} //Ok + error => panic!("Expected a type error found `{}`", error), + } + } +} diff --git a/symbol-table/tests/symbol_table/duplicate_circuit.leo b/symbol-table/tests/symbol_table/duplicate_circuit.leo new file mode 100644 index 0000000000..de08c207ad --- /dev/null +++ b/symbol-table/tests/symbol_table/duplicate_circuit.leo @@ -0,0 +1,10 @@ +/// +/// Defines a circuit `Foo {}`. +/// Attempts to define a second circuit `Foo {}`. +/// +/// Expected output: SymbolTableError +/// Message: "Duplicate circuit definition found for `Foo`." +/// + +circuit Foo {} +circuit Foo {} \ No newline at end of file diff --git a/symbol-table/tests/symbol_table/duplicate_function.leo b/symbol-table/tests/symbol_table/duplicate_function.leo new file mode 100644 index 0000000000..fffbbeff5a --- /dev/null +++ b/symbol-table/tests/symbol_table/duplicate_function.leo @@ -0,0 +1,10 @@ +/// +/// Defines a function `main() {}`. +/// Attempts to define a second function `main() {}`. +/// +/// Expected output: SymbolTableError +/// Message: "Duplicate function definition found for `main`." +/// + +function main() {} +function main() {} \ No newline at end of file diff --git a/symbol-table/tests/symbol_table/mod.rs b/symbol-table/tests/symbol_table/mod.rs new file mode 100644 index 0000000000..b6d511c89c --- /dev/null +++ b/symbol-table/tests/symbol_table/mod.rs @@ -0,0 +1,72 @@ +// Copyright (C) 2019-2020 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 . +use crate::TestSymbolTable; + +/// +/// Defines a circuit `Foo {}`. +/// Attempts to define a second circuit `Foo {}`. +/// +/// Expected output: SymbolTableError +/// Message: "Duplicate circuit definition found for `Foo`." +/// +#[test] +fn test_duplicate_circuit() { + let program_bytes = include_bytes!("duplicate_circuit.leo"); + let resolver = TestSymbolTable::new(program_bytes); + + resolver.expect_pass_one_error(); +} + +/// +/// Defines a function `main() {}`. +/// Attempts to define a second function `main() {}`. +/// +/// Expected output: SymbolTableError +/// Message: "Duplicate function definition found for `main`." +/// +#[test] +fn test_duplicate_function() { + let program_bytes = include_bytes!("duplicate_function.leo"); + let resolver = TestSymbolTable::new(program_bytes); + + resolver.expect_pass_one_error(); +} + +/// +/// Defines a function that returns `Self`. +/// +/// Expected output: TypeError +/// Message: "Type `Self` is only available in circuit definitions and circuit functions." +/// +#[test] +fn test_self_not_available() { + let program_bytes = include_bytes!("self_not_available.leo"); + let resolver = TestSymbolTable::new(program_bytes); + + resolver.expect_pass_two_error(); +} + +/// +/// Defines a circuit with variable whose type is `Bar`, an undefined circuit. +/// +/// Expected output: TypeError +/// Message: "Type circuit `Bar` must be defined before it is used in an expression." +/// +#[test] +fn test_undefined_circuit() { + let program_bytes = include_bytes!("undefined_circuit.leo"); + let resolver = TestSymbolTable::new(program_bytes); + + resolver.expect_pass_two_error(); +} diff --git a/symbol-table/tests/symbol_table/self_not_available.leo b/symbol-table/tests/symbol_table/self_not_available.leo new file mode 100644 index 0000000000..e34a4c5670 --- /dev/null +++ b/symbol-table/tests/symbol_table/self_not_available.leo @@ -0,0 +1,8 @@ +/// +/// Defines a function that returns `Self`. +/// +/// Expected output: TypeError +/// Message: "Type `Self` is only available in circuit definitions and circuit functions." +/// + +function main() -> Self {} \ No newline at end of file diff --git a/symbol-table/tests/symbol_table/undefined_circuit.leo b/symbol-table/tests/symbol_table/undefined_circuit.leo new file mode 100644 index 0000000000..396dcfd177 --- /dev/null +++ b/symbol-table/tests/symbol_table/undefined_circuit.leo @@ -0,0 +1,10 @@ +/// +/// Defines a circuit with variable whose type is `Bar`, an undefined circuit. +/// +/// Expected output: TypeError +/// Message: "Type circuit `Bar` must be defined before it is used in an expression." +/// + +circuit Foo { + b: Bar +} \ No newline at end of file diff --git a/typed/src/annotation.rs b/typed/src/annotation.rs index 3c1b66ee39..6ffa0fad8c 100644 --- a/typed/src/annotation.rs +++ b/typed/src/annotation.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{Circuit, Function, Identifier, Import, InputVariable, TestFunction}; +use crate::{Circuit, Function, FunctionInput, Identifier, Import, TestFunction}; use leo_ast::{ annotations::{Annotation, AnnotationArguments, AnnotationName}, definitions::{AnnotatedDefinition, Definition}, @@ -28,7 +28,7 @@ pub fn load_annotation( _circuits: &mut HashMap, _functions: &mut HashMap, tests: &mut HashMap, - _expected: &mut Vec, + _expected: &mut Vec, ) { let ast_annotation = annotated_definition.annotation; let ast_definition = *annotated_definition.definition; diff --git a/typed/src/functions/function.rs b/typed/src/functions/function.rs index 4f52da175d..9d1e1d718b 100644 --- a/typed/src/functions/function.rs +++ b/typed/src/functions/function.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{Identifier, InputVariable, Span, Statement, Type}; +use crate::{FunctionInput, Identifier, Span, Statement, Type}; use leo_ast::functions::Function as AstFunction; use serde::{Deserialize, Serialize}; @@ -23,7 +23,7 @@ use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Function { pub identifier: Identifier, - pub input: Vec, + pub input: Vec, pub returns: Option, pub statements: Vec, pub span: Span, @@ -35,7 +35,7 @@ impl<'ast> From> for Function { let parameters = function .parameters .into_iter() - .map(|parameter| InputVariable::from(parameter)) + .map(|parameter| FunctionInput::from(parameter)) .collect(); let returns = function.returns.map(|type_| Type::from(type_)); let statements = function diff --git a/typed/src/functions/input/function_input.rs b/typed/src/functions/input/function_input.rs index ea91b1b988..5de9468f94 100644 --- a/typed/src/functions/input/function_input.rs +++ b/typed/src/functions/input/function_input.rs @@ -21,16 +21,16 @@ use serde::{Deserialize, Serialize}; use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FunctionInput { +pub struct FunctionInputVariable { pub identifier: Identifier, pub mutable: bool, pub type_: Type, pub span: Span, } -impl<'ast> From> for FunctionInput { +impl<'ast> From> for FunctionInputVariable { fn from(parameter: AstFunctionInput<'ast>) -> Self { - FunctionInput { + FunctionInputVariable { identifier: Identifier::from(parameter.identifier), mutable: parameter.mutable.is_some(), type_: Type::from(parameter.type_), @@ -39,7 +39,7 @@ impl<'ast> From> for FunctionInput { } } -impl FunctionInput { +impl FunctionInputVariable { fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { // mut var: bool if self.mutable { @@ -50,13 +50,13 @@ impl FunctionInput { } } -impl fmt::Display for FunctionInput { +impl fmt::Display for FunctionInputVariable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } } -impl fmt::Debug for FunctionInput { +impl fmt::Debug for FunctionInputVariable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } diff --git a/typed/src/functions/input/input_variable.rs b/typed/src/functions/input/input_variable.rs index b3032d8b1b..01974dda8a 100644 --- a/typed/src/functions/input/input_variable.rs +++ b/typed/src/functions/input/input_variable.rs @@ -14,19 +14,19 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{FunctionInput, Identifier, Span}; +use crate::{FunctionInputVariable, Identifier, Span}; use leo_ast::functions::input::Input as AstInput; use serde::{Deserialize, Serialize}; use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum InputVariable { +pub enum FunctionInput { InputKeyword(Identifier), - FunctionInput(FunctionInput), + Variable(FunctionInputVariable), } -impl<'ast> From> for InputVariable { +impl<'ast> From> for FunctionInput { fn from(input: AstInput<'ast>) -> Self { match input { AstInput::InputKeyword(input_keyword) => { @@ -35,31 +35,31 @@ impl<'ast> From> for InputVariable { span: Span::from(input_keyword.span), }; - InputVariable::InputKeyword(id) + FunctionInput::InputKeyword(id) } AstInput::FunctionInput(function_input) => { - InputVariable::FunctionInput(FunctionInput::from(function_input)) + FunctionInput::Variable(FunctionInputVariable::from(function_input)) } } } } -impl InputVariable { +impl FunctionInput { fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - InputVariable::InputKeyword(id) => write!(f, "{}", id), - InputVariable::FunctionInput(function_input) => write!(f, "{}", function_input), + FunctionInput::InputKeyword(id) => write!(f, "{}", id), + FunctionInput::Variable(function_input) => write!(f, "{}", function_input), } } } -impl fmt::Display for InputVariable { +impl fmt::Display for FunctionInput { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } } -impl fmt::Debug for InputVariable { +impl fmt::Debug for FunctionInput { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } diff --git a/typed/src/program.rs b/typed/src/program.rs index 8cc8b4f3ad..90cfb41fc4 100644 --- a/typed/src/program.rs +++ b/typed/src/program.rs @@ -17,7 +17,7 @@ //! A typed Leo program consists of import, circuit, and function definitions. //! Each defined type consists of typed statements and expressions. -use crate::{load_annotation, Circuit, Function, Identifier, Import, InputVariable, TestFunction}; +use crate::{load_annotation, Circuit, Function, FunctionInput, Identifier, Import, TestFunction}; use leo_ast::{definitions::Definition, files::File}; use serde::{Deserialize, Serialize}; @@ -27,7 +27,7 @@ use std::collections::HashMap; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Program { pub name: String, - pub expected_input: Vec, + pub expected_input: Vec, pub imports: Vec, pub circuits: HashMap, pub functions: HashMap,