From 667392237f4e2a4d24febb5d629bd789228e2eae Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 11 Nov 2020 14:57:39 -0800 Subject: [PATCH] add symbol-table module --- Cargo.lock | 13 + Cargo.toml | 2 +- compiler/Cargo.toml | 6 +- compiler/src/compiler.rs | 20 +- compiler/src/errors/compiler.rs | 8 +- compiler/src/import/parser/import_parser.rs | 67 --- compiler/src/import/store/import.rs | 32 +- compiler/tests/core/mod.rs | 64 +-- compiler/tests/mod.rs | 6 +- symbol-table/Cargo.toml | 40 ++ symbol-table/src/attributes/attribute.rs | 24 + symbol-table/src/attributes/mod.rs | 18 + symbol-table/src/errors/mod.rs | 21 + symbol-table/src/errors/symbol_table.rs | 100 ++++ symbol-table/src/errors/type_.rs | 86 +++ symbol-table/src/imports/imported_symbols.rs | 57 ++ symbol-table/src/imports/mod.rs | 18 + symbol-table/src/lib.rs | 41 ++ symbol-table/src/symbol_table.rs | 502 ++++++++++++++++++ symbol-table/src/types/circuits/circuit.rs | 194 +++++++ .../src/types/circuits/circuit_function.rs | 27 + .../src/types/circuits/circuit_variable.rs | 40 ++ symbol-table/src/types/circuits/mod.rs | 24 + symbol-table/src/types/functions/function.rs | 136 +++++ .../src/types/functions/function_input.rs | 90 ++++ .../functions/function_input_variable.rs | 119 +++++ .../src/types/functions/function_output.rs | 69 +++ symbol-table/src/types/functions/mod.rs | 27 + symbol-table/src/types/mod.rs | 30 ++ symbol-table/src/types/type_.rs | 265 +++++++++ symbol-table/src/types/type_variable.rs | 46 ++ symbol-table/src/types/user_defined/mod.rs | 18 + .../types/user_defined/user_defined_type.rs | 86 +++ symbol-table/tests/mod.rs | 122 +++++ .../tests/symbol_table/duplicate_circuit.leo | 10 + .../tests/symbol_table/duplicate_function.leo | 10 + symbol-table/tests/symbol_table/mod.rs | 75 +++ .../tests/symbol_table/self_not_available.leo | 8 + .../tests/symbol_table/undefined_circuit.leo | 10 + 39 files changed, 2395 insertions(+), 136 deletions(-) delete mode 100644 compiler/src/import/parser/import_parser.rs create mode 100644 symbol-table/Cargo.toml create mode 100644 symbol-table/src/attributes/attribute.rs create mode 100644 symbol-table/src/attributes/mod.rs create mode 100644 symbol-table/src/errors/mod.rs create mode 100644 symbol-table/src/errors/symbol_table.rs create mode 100644 symbol-table/src/errors/type_.rs create mode 100644 symbol-table/src/imports/imported_symbols.rs create mode 100644 symbol-table/src/imports/mod.rs create mode 100644 symbol-table/src/lib.rs create mode 100644 symbol-table/src/symbol_table.rs create mode 100644 symbol-table/src/types/circuits/circuit.rs create mode 100644 symbol-table/src/types/circuits/circuit_function.rs create mode 100644 symbol-table/src/types/circuits/circuit_variable.rs create mode 100644 symbol-table/src/types/circuits/mod.rs create mode 100644 symbol-table/src/types/functions/function.rs create mode 100644 symbol-table/src/types/functions/function_input.rs create mode 100644 symbol-table/src/types/functions/function_input_variable.rs create mode 100644 symbol-table/src/types/functions/function_output.rs create mode 100644 symbol-table/src/types/functions/mod.rs create mode 100644 symbol-table/src/types/mod.rs create mode 100644 symbol-table/src/types/type_.rs create mode 100644 symbol-table/src/types/type_variable.rs create mode 100644 symbol-table/src/types/user_defined/mod.rs create mode 100644 symbol-table/src/types/user_defined/user_defined_type.rs create mode 100644 symbol-table/tests/mod.rs create mode 100644 symbol-table/tests/symbol_table/duplicate_circuit.leo create mode 100644 symbol-table/tests/symbol_table/duplicate_function.leo create mode 100644 symbol-table/tests/symbol_table/mod.rs create mode 100644 symbol-table/tests/symbol_table/self_not_available.leo create mode 100644 symbol-table/tests/symbol_table/undefined_circuit.leo diff --git a/Cargo.lock b/Cargo.lock index 7fb1f04a75..67ffa6943f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1312,6 +1312,7 @@ dependencies = [ "leo-input", "leo-package", "leo-state", + "leo-symbol-table", "num-bigint", "pest", "rand", @@ -1481,6 +1482,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "leo-symbol-table" +version = "1.0.4" +dependencies = [ + "leo-ast", + "leo-core", + "leo-grammar", + "leo-imports", + "serde", + "thiserror", +] + [[package]] name = "libc" version = "0.2.80" diff --git a/Cargo.toml b/Cargo.toml index f49322dc12..1b6e880f5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ members = [ "linter", "package", "state", -# "symbol-table", + "symbol-table", # "type-inference", ] diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index aa18805cce..e9e94ea3b5 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -49,9 +49,9 @@ version = "1.0.4" path = "../state" version = "1.0.4" -#[dependencies.leo-symbol-table] -#path = "../symbol-table" -#version = "1.0.4" +[dependencies.leo-symbol-table] +path = "../symbol-table" +version = "1.0.4" #[dependencies.leo-type-inference] #path = "../type-inference" diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index c17eb9c398..ea2cc9ef09 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -29,7 +29,7 @@ use leo_imports::ImportParser; use leo_input::LeoInputParser; use leo_package::inputs::InputPairs; use leo_state::verify_local_data_commitment; -// use leo_symbol_table::SymbolTable; +use leo_symbol_table::SymbolTable; // use leo_type_inference::TypeInference; use snarkos_dpc::{base_dpc::instantiated::Components, SystemParameters}; @@ -205,13 +205,13 @@ impl> Compiler { /// catching type mismatch errors. /// pub(crate) fn check_program(&self) -> Result<(), CompilerError> { - // // Create a new symbol table from the program, imported_programs, and program_input. - // let _symbol_table = - // SymbolTable::new(&self.program, &self.imported_programs, &self.program_input).map_err(|mut e| { - // e.set_path(&self.main_file_path); - // - // e - // })?; + // Create a new symbol table from the program, imported_programs, and program_input. + let _symbol_table = + SymbolTable::new(&self.program, &self.imported_programs, &self.program_input).map_err(|mut e| { + e.set_path(&self.main_file_path); + + e + })?; // // Run type inference check on program. // TypeInference::new(&self.program, symbol_table).map_err(|mut e| { @@ -252,8 +252,8 @@ impl> Compiler { // Parse and store all programs imported by the main program file. self.imported_programs = ImportParser::parse(&self.program)?; - // // Create a new symbol table from the program, imported programs, and program input. - // let _symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?; + // Create a new symbol table from the program, imported programs, and program input. + let _symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?; // // Run type inference check on program. // TypeInference::new(&self.program, symbol_table)?; diff --git a/compiler/src/errors/compiler.rs b/compiler/src/errors/compiler.rs index 4239c18e7f..a29d733999 100644 --- a/compiler/src/errors/compiler.rs +++ b/compiler/src/errors/compiler.rs @@ -19,7 +19,7 @@ use leo_grammar::ParserError; use leo_imports::ImportParserError; use leo_input::InputParserError; use leo_state::LocalDataVerificationError; -// use leo_symbol_table::SymbolTableError; +use leo_symbol_table::SymbolTableError; // use leo_type_inference::TypeInferenceError; use bincode::Error as SerdeError; @@ -68,9 +68,9 @@ pub enum CompilerError { #[error("{}", _0)] SerdeError(#[from] SerdeError), - // #[error("{}", _0)] - // SymbolTableError(#[from] SymbolTableError), + #[error("{}", _0)] + SymbolTableError(#[from] SymbolTableError), // #[error("{}", _0)] // TypeInferenceError(#[from] TypeInferenceError), } @@ -81,7 +81,7 @@ impl CompilerError { CompilerError::InputParserError(error) => error.set_path(path), CompilerError::FunctionError(error) => error.set_path(path), CompilerError::OutputStringError(error) => error.set_path(path), - // CompilerError::SymbolTableError(error) => error.set_path(path), + CompilerError::SymbolTableError(error) => error.set_path(path), // CompilerError::TypeInferenceError(error) => error.set_path(path), _ => {} } diff --git a/compiler/src/import/parser/import_parser.rs b/compiler/src/import/parser/import_parser.rs deleted file mode 100644 index 9e440436ad..0000000000 --- a/compiler/src/import/parser/import_parser.rs +++ /dev/null @@ -1,67 +0,0 @@ -// 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::errors::ImportError; -use leo_ast::{Package, Program}; - -use std::{collections::HashMap, env::current_dir}; - -/// Parses all relevant import files for a program. -/// Stores compiled program structs. -#[derive(Clone, Default)] -pub struct ImportParser { - imports: HashMap, - core_packages: Vec, -} - -impl ImportParser { - pub fn new() -> Self { - Self::default() - } - - pub(crate) fn insert_import(&mut self, file_name: String, program: Program) { - // todo: handle conflicting versions for duplicate imports here - let _res = self.imports.insert(file_name, program); - } - - pub(crate) fn insert_core_package(&mut self, package: &Package) { - let _res = self.core_packages.push(package.clone()); - } - - pub fn get_import(&self, file_name: &str) -> Option<&Program> { - self.imports.get(file_name) - } - - pub fn core_packages(&self) -> &Vec { - &self.core_packages - } - - pub fn parse(program: &Program) -> Result { - let mut imports = Self::new(); - - // Find all imports relative to current directory - let path = current_dir().map_err(ImportError::current_directory_error)?; - - // Parse each imported file - program - .imports - .iter() - .map(|import| imports.parse_package(path.clone(), &import.package)) - .collect::, ImportError>>()?; - - Ok(imports) - } -} diff --git a/compiler/src/import/store/import.rs b/compiler/src/import/store/import.rs index 8e34423557..9453d2ef02 100644 --- a/compiler/src/import/store/import.rs +++ b/compiler/src/import/store/import.rs @@ -17,7 +17,7 @@ use crate::{errors::ImportError, ConstrainedProgram, GroupType}; use leo_ast::ImportStatement; use leo_imports::ImportParser; -// use leo_symbol_table::imported_symbols::ImportedSymbols; +use leo_symbol_table::imported_symbols::ImportedSymbols; use snarkos_models::curves::{Field, PrimeField}; @@ -37,21 +37,21 @@ impl> ConstrainedProgram { return Ok(()); } - // // Fetch dependencies for the current import - // let imported_symbols = ImportedSymbols::new(import); - // - // for (name, symbol) in imported_symbols.symbols { - // // Find imported program - // let program = imported_programs - // .get_import(&name) - // .ok_or_else(|| ImportError::unknown_package(import.package.name.clone()))?; - // - // // Parse imported program - // self.store_definitions(program.clone(), imported_programs)?; - // - // // Store the imported symbol - // self.store_symbol(scope, &name, &symbol, program)?; - // } + // Fetch dependencies for the current import + let imported_symbols = ImportedSymbols::new(import); + + for (name, symbol) in imported_symbols.symbols { + // Find imported program + let program = imported_programs + .get_import(&name) + .ok_or_else(|| ImportError::unknown_package(import.package.name.clone()))?; + + // Parse imported program + self.store_definitions(program.clone(), imported_programs)?; + + // Store the imported symbol + self.store_symbol(scope, &name, &symbol, program)?; + } Ok(()) } diff --git a/compiler/tests/core/mod.rs b/compiler/tests/core/mod.rs index 9e22953c05..20943b7c36 100644 --- a/compiler/tests/core/mod.rs +++ b/compiler/tests/core/mod.rs @@ -16,39 +16,39 @@ pub mod packages; -use crate::{assert_satisfied, parse_program}; +use crate::{assert_satisfied, expect_symbol_table_error, parse_program}; -// #[test] -// fn test_core_circuit_invalid() { -// let program_bytes = include_bytes!("core_package_invalid.leo"); -// let program = parse_program(program_bytes).err().unwrap(); -// -// expect_symbol_table_error(program); -// } -// -// #[test] -// fn test_core_circuit_star_fail() { -// let program_bytes = include_bytes!("core_circuit_star_fail.leo"); -// let error = parse_program(program_bytes).err().unwrap(); -// -// expect_symbol_table_error(error); -// } -// -// #[test] -// fn test_core_package_invalid() { -// let program_bytes = include_bytes!("core_package_invalid.leo"); -// let error = parse_program(program_bytes).err().unwrap(); -// -// expect_symbol_table_error(error); -// } -// -// #[test] -// fn test_core_unstable_package_invalid() { -// let program_bytes = include_bytes!("core_unstable_package_invalid.leo"); -// let error = parse_program(program_bytes).err().unwrap(); -// -// expect_symbol_table_error(error); -// } +#[test] +fn test_core_circuit_invalid() { + let program_bytes = include_bytes!("core_package_invalid.leo"); + let program = parse_program(program_bytes).err().unwrap(); + + expect_symbol_table_error(program); +} + +#[test] +fn test_core_circuit_star_fail() { + let program_bytes = include_bytes!("core_circuit_star_fail.leo"); + let error = parse_program(program_bytes).err().unwrap(); + + expect_symbol_table_error(error); +} + +#[test] +fn test_core_package_invalid() { + let program_bytes = include_bytes!("core_package_invalid.leo"); + let error = parse_program(program_bytes).err().unwrap(); + + expect_symbol_table_error(error); +} + +#[test] +fn test_core_unstable_package_invalid() { + let program_bytes = include_bytes!("core_unstable_package_invalid.leo"); + let error = parse_program(program_bytes).err().unwrap(); + + expect_symbol_table_error(error); +} #[test] fn test_unstable_blake2s_sanity() { diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index 478ccc9ab0..57cb8f3d2c 100644 --- a/compiler/tests/mod.rs +++ b/compiler/tests/mod.rs @@ -185,9 +185,9 @@ pub(crate) fn expect_compiler_error(program: EdwardsTestCompiler) -> CompilerErr // assert!(matches!(error, CompilerError::TypeInferenceError(_))) // } -// pub(crate) fn expect_symbol_table_error(error: CompilerError) { -// assert!(matches!(error, CompilerError::SymbolTableError(_))) -// } +pub(crate) fn expect_symbol_table_error(error: CompilerError) { + assert!(matches!(error, CompilerError::SymbolTableError(_))) +} pub(crate) fn generate_main_input(input: Vec<(&str, Option)>) -> MainInput { let mut main_input = MainInput::new(); diff --git a/symbol-table/Cargo.toml b/symbol-table/Cargo.toml new file mode 100644 index 0000000000..0e821fd122 --- /dev/null +++ b/symbol-table/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "leo-symbol-table" +version = "1.0.4" +authors = [ "The Aleo Team " ] +description = "Stores user-defined variables during type resolution" +homepage = "https://aleo.org" +repository = "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.4" + +[dependencies.leo-core] +path = "../core" +version = "1.0.4" + +[dependencies.leo-grammar] +path = "../grammar" +version = "1.0.4" + +[dependencies.leo-imports] +path = "../imports" +version = "1.0.4" + +[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..81d1dca02e --- /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, Copy, 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..7ec93ee2b1 --- /dev/null +++ b/symbol-table/src/errors/symbol_table.rs @@ -0,0 +1,100 @@ +// 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, UserDefinedType}; +use leo_ast::{Error as FormattedError, ImportSymbol, Program, Span}; +use leo_core::{CorePackageListError, LeoCorePackageError}; + +use std::path::Path; + +/// Errors encountered when tracking variable, function, and circuit names in a program. +#[derive(Debug, Error)] +pub enum SymbolTableError { + #[error("{}", _0)] + CorePackageListError(#[from] CorePackageListError), + + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + LeoCorePackageError(#[from] LeoCorePackageError), + + #[error("{}", _0)] + TypeError(#[from] TypeError), +} + +impl SymbolTableError { + /// + /// Sets the filepath for the error stacktrace. + /// + pub fn set_path(&mut self, path: &Path) { + match self { + SymbolTableError::CorePackageListError(error) => error.set_path(path), + SymbolTableError::Error(error) => error.set_path(path), + SymbolTableError::LeoCorePackageError(error) => error.set_path(path), + SymbolTableError::TypeError(error) => error.set_path(path), + } + } + + /// + /// Returns 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(variable: UserDefinedType) -> Self { + let message = format!("Duplicate circuit definition found for `{}`", variable.identifier); + + Self::new_from_span(message, variable.identifier.span) + } + + /// + /// Two functions have been defined with the same name. + /// + pub fn duplicate_function(variable: UserDefinedType) -> Self { + let message = format!("Duplicate function definition found for `{}`", variable.identifier); + + Self::new_from_span(message, variable.identifier.span) + } + + /// + /// Attempted to access a package name that is not defined. + /// + pub fn unknown_package(name: &str, span: &Span) -> Self { + let message = format!( + "Cannot find imported package `{}` in source files or import directory", + name + ); + + Self::new_from_span(message, span.to_owned()) + } + + /// + /// Attempted to import a name that is not defined in the current file. + /// + pub fn unknown_symbol(symbol: &ImportSymbol, program: &Program) -> Self { + let message = format!( + "Cannot find imported symbol `{}` in imported file `{}`", + symbol, program.name + ); + + Self::new_from_span(message, symbol.span.to_owned()) + } +} diff --git a/symbol-table/src/errors/type_.rs b/symbol-table/src/errors/type_.rs new file mode 100644 index 0000000000..c040fcc10a --- /dev/null +++ b/symbol-table/src/errors/type_.rs @@ -0,0 +1,86 @@ +// 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 leo_ast::{Error as FormattedError, Identifier, Span}; + +use std::path::Path; + +/// 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: &Path) { + 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)) + } + + /// + /// The `Self` keyword was used outside of a circuit. + /// + pub fn self_not_available(span: Span) -> Self { + let message = "Type `Self` is only available in circuit definitions and circuit functions.".to_string(); + + 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/imports/imported_symbols.rs b/symbol-table/src/imports/imported_symbols.rs new file mode 100644 index 0000000000..c344b9457b --- /dev/null +++ b/symbol-table/src/imports/imported_symbols.rs @@ -0,0 +1,57 @@ +// 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 leo_ast::{ImportStatement, ImportSymbol, Package, PackageAccess}; + +/// Stores the the package file name and imported symbol from an import statement +#[derive(Debug)] +pub struct ImportedSymbols { + pub symbols: Vec<(String, ImportSymbol)>, +} + +impl ImportedSymbols { + pub fn new(import: &ImportStatement) -> Self { + let mut imported_symbols = Self::default(); + + imported_symbols.push_package(&import.package); + + imported_symbols + } + + fn push_package(&mut self, package: &Package) { + self.push_package_access(package.name.name.clone(), &package.access); + } + + fn push_package_access(&mut self, package: String, access: &PackageAccess) { + match access { + PackageAccess::SubPackage(package) => self.push_package(package), + PackageAccess::Star(span) => { + let star = ImportSymbol::star(span); + self.symbols.push((package, star)); + } + PackageAccess::Symbol(symbol) => self.symbols.push((package, symbol.clone())), + PackageAccess::Multiple(packages) => packages + .iter() + .for_each(|access| self.push_package_access(package.clone(), access)), + } + } +} + +impl Default for ImportedSymbols { + fn default() -> Self { + Self { symbols: Vec::new() } + } +} diff --git a/symbol-table/src/imports/mod.rs b/symbol-table/src/imports/mod.rs new file mode 100644 index 0000000000..000a0a4885 --- /dev/null +++ b/symbol-table/src/imports/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 imported_symbols; +pub use self::imported_symbols::*; diff --git a/symbol-table/src/lib.rs b/symbol-table/src/lib.rs new file mode 100644 index 0000000000..2156653e13 --- /dev/null +++ b/symbol-table/src/lib.rs @@ -0,0 +1,41 @@ +// 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 . + +//! The symbol table for a Leo program. +//! +//! This module contains the [`SymbolTable`] type, an abstract data type that tracks the current +//! bindings for functions and circuits in a Leo program. +//! +//! A new [`Symbol Table`] type can be created from a reference to a [`LeoAst`]. +//! A [`Symbol Table`] type can be used to create a new [`TypeInference`] type. + +#[macro_use] +extern crate thiserror; + +pub mod attributes; +pub use self::attributes::*; + +pub mod errors; +pub use self::errors::*; + +pub mod imports; +pub use self::imports::*; + +pub mod symbol_table; +pub use self::symbol_table::*; + +pub mod types; +pub use self::types::*; diff --git a/symbol-table/src/symbol_table.rs b/symbol-table/src/symbol_table.rs new file mode 100644 index 0000000000..0e5a56b121 --- /dev/null +++ b/symbol-table/src/symbol_table.rs @@ -0,0 +1,502 @@ +// 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, CircuitVariableType, FunctionType, ImportedSymbols, SymbolTableError, UserDefinedType}; +use leo_ast::{Circuit, Function, Identifier, ImportStatement, ImportSymbol, Input, Package, Program}; +use leo_core::CorePackageList; +use leo_imports::ImportParser; + +use std::collections::{HashMap, HashSet}; + +pub const INPUT_VARIABLE_NAME: &str = "input"; +pub const RECORD_VARIABLE_NAME: &str = "record"; +pub const REGISTERS_VARIABLE_NAME: &str = "registers"; +pub const STATE_VARIABLE_NAME: &str = "state"; +pub const STATE_LEAF_VARIABLE_NAME: &str = "state_leaf"; + +/// The symbol table for 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. +/// A child symbol table cannot access names in another sibling's symbol table. +#[derive(Clone, Default)] +pub struct SymbolTable { + /// Maps name -> parameter type. + names: 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 { + /// + /// Returns a new `SymbolTable` from a given, program, imported programs, and program input. + /// + /// Checks that each circuit or function name is unique. + /// Unique names are added to a table of name -> user defined type. + /// + /// Checks that each circuit or function definition contains valid types. + /// + pub fn new( + program: &Program, + import_parser: &ImportParser, + input: &Input, + ) -> Result { + // Create a new symbol table. + let mut table = Self::default(); + + // Insert input types into symbol table. + table.insert_input(input)?; + + // Check for duplicate program and import names. + table.check_names(program, import_parser)?; + + // Check for unknown or invalid types. + table.check_types(program)?; + + Ok(table) + } + + /// + /// Insert a function or circuit name 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_name(&mut self, name: String, variable_type: UserDefinedType) -> Option { + self.names.insert(name, variable_type) + } + + /// + /// Insert a circuit name into the symbol table from a given name and variable type. + /// + /// Returns an error if the circuit name is a duplicate. + /// + pub fn insert_circuit_name( + &mut self, + name: String, + variable_type: UserDefinedType, + ) -> Result<(), SymbolTableError> { + // Check that the circuit name is unique. + match self.insert_name(name, variable_type) { + Some(duplicate) => Err(SymbolTableError::duplicate_circuit(duplicate)), + None => Ok(()), + } + } + + /// + /// Insert a function name into the symbol table from a given name and variable type. + /// + /// Returns an error if the function name is a duplicate. + /// + pub fn insert_function_name( + &mut self, + name: String, + variable_type: UserDefinedType, + ) -> Result<(), SymbolTableError> { + // Check that the circuit name is unique. + match self.insert_name(name, variable_type) { + Some(duplicate) => Err(SymbolTableError::duplicate_function(duplicate)), + None => Ok(()), + } + } + + /// + /// 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_type(&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_type( + &mut self, + identifier: Identifier, + function_type: FunctionType, + ) -> Option { + self.functions.insert(identifier.name, function_type) + } + + /// + /// 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_type(&self, name: &str) -> 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_type(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_type(&self, name: &str) -> Option<&FunctionType> { + // Lookup name in symbol table. + match self.functions.get(name) { + Some(circuit) => Some(circuit), + None => { + // Lookup name in parent symbol table + match &self.parent { + Some(parent) => parent.get_function_type(name), + None => None, + } + } + } + } + + /// + /// Checks for duplicate import, circuit, and function names given a 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 check_names(&mut self, program: &Program, import_parser: &ImportParser) -> Result<(), SymbolTableError> { + // Check unresolved program import names. + self.check_import_names(&program.imports, import_parser)?; + + // Check unresolved program circuit names. + self.check_circuit_names(&program.circuits)?; + + // Check unresolved program function names. + self.check_function_names(&program.functions)?; + + Ok(()) + } + + /// + /// Checks for duplicate circuit names given a hashmap of circuits. + /// + /// If a circuit name has no duplicates, then it is inserted into the symbol table. + /// Types defined later in the program cannot have the same name. + /// + pub fn check_circuit_names(&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. + self.insert_circuit_name(identifier.to_string(), UserDefinedType::from(circuit.clone()))?; + } + + Ok(()) + } + + /// + /// Checks for duplicate function names given a hashmap of functions. + /// + /// If a function name has no duplicates, then it is inserted into the symbol table. + /// Types defined later in the program cannot have the same name. + /// + pub fn check_function_names(&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. + self.insert_function_name(identifier.to_string(), UserDefinedType::from(function.clone()))?; + } + + Ok(()) + } + + /// + /// Checks that all given imported names exist in the list of imported programs. + /// + /// Additionally checks for duplicate imported names in the given vector of imports. + /// Types defined later in the program cannot have the same name. + /// + pub fn check_import_names( + &mut self, + imports: &[ImportStatement], + import_parser: &ImportParser, + ) -> Result<(), SymbolTableError> { + // Iterate over imported names. + for import in imports { + self.check_import_statement(import, import_parser)?; + } + + Ok(()) + } + + /// + /// Checks that a given import statement imports an existing package. + /// + /// Additionally checks for duplicate imported names in the given vector of imports. + /// Types defined later in the program cannot have the same name. + /// + pub fn check_import_statement( + &mut self, + import: &ImportStatement, + import_parser: &ImportParser, + ) -> Result<(), SymbolTableError> { + // Check if the import name exists as core package. + let core_package = import_parser.get_core_package(&import.package); + + // If the core package exists, then attempt to insert the import into the symbol table. + if let Some(package) = core_package { + return self.check_core_package(package); + } + + // Attempt to insert the imported names into the symbol table. + self.check_package(import, import_parser) + } + + /// + /// Inserts imported core package circuit names and types into the symbol table. + /// + /// Checks that the core package and all circuit names exist. Checks that imported circuit types + /// only contain known types. + /// + pub fn check_core_package(&mut self, package: &Package) -> Result<(), SymbolTableError> { + // Create list of imported core packages. + let list = CorePackageList::from_package_access(package.access.to_owned())?; + + // Fetch core package symbols from `leo-core`. + let symbol_list = list.to_symbols()?; + + // Insert name and type information for each core package symbol. + for (name, circuit) in symbol_list.symbols() { + // Store name of symbol. + self.insert_circuit_name(name.to_string(), UserDefinedType::from(circuit.clone()))?; + + // Create new circuit type for symbol. + let circuit_type = CircuitType::new(&self, circuit.to_owned())?; + + // Insert circuit type of symbol. + self.insert_circuit_type(circuit_type.identifier.clone(), circuit_type); + } + + Ok(()) + } + + /// + /// Inserts one or more imported symbols for a given imported package. + /// + /// Checks that the package and all circuit and function names exist. Checks that imported circuit + /// and function types only contain known types. + /// + pub fn check_package( + &mut self, + import: &ImportStatement, + import_parser: &ImportParser, + ) -> Result<(), SymbolTableError> { + // Get imported symbols from statement. + let imported_symbols = ImportedSymbols::new(import); + + // Import all symbols from an imported file for now. + // Keep track of which import files have already been checked. + let mut checked = HashSet::new(); + + // Iterate over each imported symbol. + for (name, symbol) in imported_symbols.symbols { + // Find the imported program. + let program = import_parser + .get_import(&name) + .ok_or_else(|| SymbolTableError::unknown_package(&name, &symbol.span))?; + + // Push the imported file's name to checked import files. + if !checked.insert(name) { + // Skip the imported symbol if we have already checked the file. + continue; + }; + + // Check the imported program for duplicate types. + self.check_names(program, import_parser)?; + + // Check the imported program for undefined types. + self.check_types(program)?; + + // Store the imported symbol. + // self.insert_import_symbol(symbol, program)?; // TODO (collinc97) uncomment this line when public/private import scopes are implemented. + } + + Ok(()) + } + + /// + /// Inserts the imported symbol into the symbol table if it is present in the given program. + /// + pub fn insert_import_symbol(&mut self, symbol: ImportSymbol, program: &Program) -> Result<(), SymbolTableError> { + // Check for import *. + if symbol.is_star() { + // Insert all program circuits. + self.check_circuit_names(&program.circuits)?; + + // Insert all program functions. + self.check_function_names(&program.functions) + } else { + // Check for a symbol alias. + let identifier = symbol.alias.to_owned().unwrap_or_else(|| symbol.symbol.to_owned()); + + // Check if the imported symbol is a circuit + match program.circuits.get(&symbol.symbol) { + Some(circuit) => { + // Insert imported circuit. + self.insert_circuit_name(identifier.to_string(), UserDefinedType::from(circuit.to_owned())) + } + None => { + // Check if the imported symbol is a function. + match program.functions.get(&symbol.symbol) { + Some(function) => { + // Insert the imported function. + self.insert_function_name( + identifier.to_string(), + UserDefinedType::from(function.to_owned()), + ) + } + None => Err(SymbolTableError::unknown_symbol(&symbol, program)), + } + } + } + } + } + + /// + /// 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 check_types(&mut self, program: &Program) -> Result<(), SymbolTableError> { + // Check unresolved program circuit definitions. + self.check_types_circuits(&program.circuits)?; + + // Check unresolved program function definitions. + self.check_types_functions(&program.functions)?; + + Ok(()) + } + + /// + /// Checks for unknown types in a circuit given a hashmap of circuits. + /// + /// If a circuit definition only contains known types, then it is inserted into the + /// symbol table. Variables defined later in the program can lookup the definition + /// and refer to its expected types + /// + pub fn check_types_circuits(&mut self, circuits: &HashMap) -> Result<(), SymbolTableError> { + // Iterate over circuit names and definitions. + for circuit in circuits.values() { + // Get the identifier of the circuit. + let identifier = circuit.circuit_name.clone(); + + // Resolve unknown types in the circuit definition. + let circuit_type = CircuitType::new(self, circuit.clone())?; + + // Attempt to insert the circuit definition into the symbol table. + self.insert_circuit_type(identifier, circuit_type); + } + + Ok(()) + } + + /// + /// Checks for unknown types in a function given a hashmap of functions. + /// + /// If a function definition only contains known types, then it is inserted into the + /// symbol table. Variables defined later in the program can lookup the definition + /// and refer to its expected types + /// + pub fn check_types_functions(&mut self, functions: &HashMap) -> Result<(), SymbolTableError> { + // Iterate over function names and definitions. + for function in functions.values() { + // Get the identifier of the function. + let identifier = function.identifier.clone(); + + // Resolve unknown types in the function definition. + let function_type = FunctionType::new(&self, function.clone())?; + + // Attempt to insert the function definition into the symbol table. + self.insert_function_type(identifier, function_type); + } + + Ok(()) + } + + /// + /// Inserts function input types into the symbol table. + /// + /// Creates a new `CircuitType` to represent the input values. + /// The new type contains register, record, state, and state leaf circuit variables. + /// This allows easy access to input types using dot syntax: `input.register.r0`. + /// + pub fn insert_input(&mut self, input: &Input) -> Result<(), SymbolTableError> { + // Get values for each input section. + let registers_values = input.get_registers().values(); + let record_values = input.get_record().values(); + let state_values = input.get_state().values(); + let state_leaf_values = input.get_state_leaf().values(); + + // Create a new `CircuitType` for each input section. + let registers_type = + CircuitType::from_input_section(&self, REGISTERS_VARIABLE_NAME.to_string(), registers_values)?; + let record_type = CircuitType::from_input_section(&self, RECORD_VARIABLE_NAME.to_string(), record_values)?; + let state_type = CircuitType::from_input_section(&self, STATE_VARIABLE_NAME.to_string(), state_values)?; + let state_leaf_type = + CircuitType::from_input_section(&self, STATE_LEAF_VARIABLE_NAME.to_string(), state_leaf_values)?; + + // Create a new `CircuitVariableType` for each type. + let registers_variable = CircuitVariableType::from(®isters_type); + let record_variable = CircuitVariableType::from(&record_type); + let state_variable = CircuitVariableType::from(&state_type); + let state_leaf_variable = CircuitVariableType::from(&state_leaf_type); + + // Create new `CircuitType` for input keyword. + let input_type = CircuitType { + identifier: Identifier::new(INPUT_VARIABLE_NAME.to_string()), + variables: vec![registers_variable, record_variable, state_variable, state_leaf_variable], + functions: Vec::new(), + }; + + // Insert each circuit type into the symbol table. + self.insert_circuit_type(registers_type.identifier.clone(), registers_type); + self.insert_circuit_type(record_type.identifier.clone(), record_type); + self.insert_circuit_type(state_type.identifier.clone(), state_type); + self.insert_circuit_type(state_leaf_type.identifier.clone(), state_leaf_type); + self.insert_circuit_type(input_type.identifier.clone(), input_type); + + 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..2e6ea923f0 --- /dev/null +++ b/symbol-table/src/types/circuits/circuit.rs @@ -0,0 +1,194 @@ +// 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, + SymbolTable, + Type, + TypeError, +}; +use leo_ast::{Circuit, CircuitMember, Identifier, InputValue, Parameter, Span}; + +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, +}; + +/// 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, 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 CircuitType { + /// + /// 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. + /// + pub fn new(table: &SymbolTable, unresolved: Circuit) -> 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::new_from_circuit( + table, + type_, + circuit_identifier.clone(), + circuit_identifier.span.clone(), + )?; + + // Check if the circuit member variable is mutable. + let attribute = if is_mutable { Some(Attribute::Mutable) } else { None }; + + // Create a new circuit variable type. + let variable = CircuitVariableType { + identifier: variable_identifier, + type_, + attribute, + }; + + // 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 attribute = if is_static { Some(Attribute::Static) } else { None }; + + // Create a new circuit function type. + let function = CircuitFunctionType { + function: function_type, + attribute, + }; + + // Store the circuit function type. + functions.push(function); + } + } + } + + // Return a new circuit type. + Ok(CircuitType { + identifier: circuit_identifier, + variables, + functions, + }) + } + + /// + /// Returns the function type of a circuit member given an identifier. + /// + pub fn member_function_type(&self, identifier: &Identifier) -> Option<&CircuitFunctionType> { + self.functions + .iter() + .find(|function| function.function.identifier.eq(identifier)) + } + + /// + /// 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 { + // 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_.to_owned()), + None => { + // Check if the circuit member is a circuit function. + let matched_function = self.member_function_type(identifier); + + match matched_function { + Some(function) => Ok(Type::Function(function.function.identifier.to_owned())), + None => Err(TypeError::undefined_circuit_member(identifier.clone())), + } + } + } + } + + /// + /// Returns a new `CircuitType` from a given `Input` struct. + /// + pub fn from_input_section( + table: &SymbolTable, + name: String, + section: HashMap>, + ) -> Result { + // Create a new `CircuitVariableType` for each section pair. + let mut variables = Vec::new(); + + for (parameter, _option) in section.into_iter() { + let variable = CircuitVariableType { + identifier: parameter.variable, + type_: Type::new(table, parameter.type_, Span::default())?, + attribute: None, + }; + + variables.push(variable); + } + + // Create a new `Identifier` for the input section. + let identifier = Identifier::new(name); + + // Return a new `CircuitType` with the given name. + Ok(Self { + identifier, + variables, + functions: Vec::new(), + }) + } +} + +impl PartialEq for CircuitType { + fn eq(&self, other: &Self) -> bool { + self.identifier.eq(&other.identifier) + } +} + +impl Eq for CircuitType {} + +impl Hash for CircuitType { + fn hash(&self, state: &mut H) { + self.identifier.hash(state); + } +} 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..4c6d8686a9 --- /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 attribute: Option, +} 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..dfbb4d94dc --- /dev/null +++ b/symbol-table/src/types/circuits/circuit_variable.rs @@ -0,0 +1,40 @@ +// 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, CircuitType, Type}; +use leo_ast::Identifier; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CircuitVariableType { + /// The name of the circuit variable + pub identifier: Identifier, + /// The type of the circuit variable + pub type_: Type, + /// The attribute of the circuit variable + pub attribute: Option, +} + +impl From<&CircuitType> for CircuitVariableType { + fn from(type_: &CircuitType) -> Self { + Self { + identifier: type_.identifier.clone(), + type_: Type::Circuit(type_.identifier.clone()), + attribute: None, + } + } +} 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..0abdc87aa9 --- /dev/null +++ b/symbol-table/src/types/functions/function.rs @@ -0,0 +1,136 @@ +// 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}, + SymbolTable, + TypeError, +}; +use leo_ast::{Function, Identifier}; + +use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; + +/// 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, 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 FunctionType { + /// + /// 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. + /// + pub fn new(table: &SymbolTable, unresolved: Function) -> Result { + let mut inputs_resolved = Vec::with_capacity(unresolved.input.len()); + + // Type check function inputs + for input in unresolved.input { + let input = FunctionInputType::new(table, input)?; + inputs_resolved.push(input); + } + + // Type check function output + let output = FunctionOutputType::new(table, unresolved.output, unresolved.span)?; + + Ok(FunctionType { + identifier: unresolved.identifier, + inputs: inputs_resolved, + output, + }) + } + + /// + /// 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: &SymbolTable, + circuit_name: Identifier, + unresolved_function: Function, + ) -> Result { + let function_identifier = unresolved_function.identifier; + let mut inputs = Vec::with_capacity(unresolved_function.input.len()); + + // Type check function inputs. + for unresolved_input in unresolved_function.input { + let input = FunctionInputType::new_from_circuit(table, unresolved_input, circuit_name.clone())?; + inputs.push(input); + } + + // Type check function output. + let output = FunctionOutputType::new_from_circuit( + table, + circuit_name, + unresolved_function.output, + unresolved_function.span, + )?; + + Ok(FunctionType { + identifier: function_identifier, + inputs, + output, + }) + } + + /// + /// 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::new(table, unresolved_function)?; + + // Insert (function_identifier -> function_type) as a (key -> value) pair in the symbol table. + table.insert_function_type(function_identifier, function); + + Ok(()) + } +} + +impl PartialEq for FunctionType { + fn eq(&self, other: &Self) -> bool { + self.identifier.eq(&other.identifier) + } +} + +impl Eq for FunctionType {} + +impl Hash for FunctionType { + fn hash(&self, state: &mut H) { + self.identifier.hash(state); + } +} 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..42afe2a52a --- /dev/null +++ b/symbol-table/src/types/functions/function_input.rs @@ -0,0 +1,90 @@ +// 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, SymbolTable, Type, TypeError}; +use leo_ast::{FunctionInput, Identifier}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum FunctionInputType { + InputKeyword(Identifier), + Variable(FunctionInputVariableType), +} + +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(identifier) => Type::Circuit(identifier.to_owned()), + FunctionInputType::Variable(variable) => variable.type_.to_owned(), + } + } + + /// + /// Return a new `FunctionInputType` from a given `FunctionInput`. + /// + /// Performs a lookup in the given symbol table if the function input contains + /// user-defined types. + /// + pub fn new(table: &SymbolTable, unresolved: FunctionInput) -> Result { + Ok(match unresolved { + FunctionInput::InputKeyword(identifier) => FunctionInputType::InputKeyword(identifier), + FunctionInput::Variable(variable) => { + let variable_resolved = FunctionInputVariableType::new(table, variable)?; + + FunctionInputType::Variable(variable_resolved) + } + }) + } + + /// + /// 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 new_from_circuit( + table: &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::new_from_circuit(table, unresolved_function_input, circuit_name)?; + + FunctionInputType::Variable(function_input) + } + }) + } +} 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..2cfd8275ec --- /dev/null +++ b/symbol-table/src/types/functions/function_input_variable.rs @@ -0,0 +1,119 @@ +// 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, SymbolTable, Type, TypeError, UserDefinedType}; +use leo_ast::{FunctionInputVariable, Identifier, Span}; + +use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; + +#[derive(Clone, Debug, 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 attribute: Option, + + /// The span of the function input. + pub span: 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. + /// + pub fn new(table: &SymbolTable, unresolved: FunctionInputVariable) -> Result { + let type_ = Type::new(table, unresolved.type_, unresolved.span.clone())?; + let attribute = if unresolved.mutable { + Some(Attribute::Mutable) + } else { + None + }; + + Ok(FunctionInputVariableType { + identifier: unresolved.identifier, + type_, + attribute, + span: unresolved.span, + }) + } + + /// + /// 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 new_from_circuit( + table: &SymbolTable, + unresolved_function_input: FunctionInputVariable, + circuit_name: Identifier, + ) -> Result { + let type_ = Type::new_from_circuit( + table, + unresolved_function_input.type_, + circuit_name, + unresolved_function_input.span.clone(), + )?; + + let attribute = if unresolved_function_input.mutable { + Some(Attribute::Mutable) + } else { + None + }; + + Ok(FunctionInputVariableType { + identifier: unresolved_function_input.identifier, + type_, + attribute, + 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 = UserDefinedType::from(self.clone()); + + table.insert_name(key, value) + } +} + +impl PartialEq for FunctionInputVariableType { + fn eq(&self, other: &Self) -> bool { + self.identifier.eq(&other.identifier) + } +} + +impl Eq for FunctionInputVariableType {} + +impl Hash for FunctionInputVariableType { + fn hash(&self, state: &mut H) { + self.identifier.hash(state) + } +} 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..2da52025cf --- /dev/null +++ b/symbol-table/src/types/functions/function_output.rs @@ -0,0 +1,69 @@ +// 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::{SymbolTable, Type, TypeError}; + +use leo_ast::{Identifier, Span, Type as UnresolvedType}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct FunctionOutputType { + /// Type of function output. + pub type_: 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. + /// + pub(crate) fn new( + table: &SymbolTable, + function_output: Option, + span: Span, + ) -> Result { + let type_ = match function_output { + None => Type::Tuple(vec![]), // functions with no return value return an empty tuple + Some(type_) => Type::new(table, type_, span)?, + }; + + Ok(FunctionOutputType { type_ }) + } + + /// + /// 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 new_from_circuit( + table: &SymbolTable, + circuit_name: Identifier, + unresolved: Option, + span: Span, + ) -> Result { + let output_type = match unresolved { + None => Type::Tuple(vec![]), + Some(type_) => Type::new_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..8c8adaad13 --- /dev/null +++ b/symbol-table/src/types/mod.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 . + +pub mod circuits; +pub use self::circuits::*; + +pub mod functions; +pub use self::functions::*; + +pub mod type_; +pub use self::type_::*; + +pub mod type_variable; +pub use self::type_variable::*; + +pub mod user_defined; +pub use self::user_defined::*; diff --git a/symbol-table/src/types/type_.rs b/symbol-table/src/types/type_.rs new file mode 100644 index 0000000000..304ffa083d --- /dev/null +++ b/symbol-table/src/types/type_.rs @@ -0,0 +1,265 @@ +// 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::{SymbolTable, TypeError, TypeVariable}; +use leo_ast::{Identifier, IntegerType, Span, Type as UnresolvedType}; + +use serde::{Deserialize, Serialize}; +use std::{ + cmp::{Eq, PartialEq}, + fmt, +}; + +/// A type in a Leo program. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum Type { + // Data types + Address, + Boolean, + Field, + Group, + IntegerType(IntegerType), + + // Data type wrappers + Array(Box), + Tuple(Vec), + + // User defined types + Circuit(Identifier), + Function(Identifier), + + // Unknown type variables + TypeVariable(TypeVariable), +} + +impl Type { + /// + /// Return a new type from the given unresolved type. + /// + /// Performs a lookup in the given symbol table if the type is user-defined. + /// + pub fn new(table: &SymbolTable, type_: UnresolvedType, span: Span) -> Result { + 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_, _) => { + let array_type = Type::new(table, *type_, span)?; + + Type::Array(Box::new(array_type)) + } + UnresolvedType::Tuple(types) => { + let tuple_types = types + .into_iter() + .map(|type_| Type::new(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_type(&identifier.name) + .ok_or_else(|| 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)); + } + }) + } + + /// + /// Return a new type from the given unresolved type. + /// + /// If this type is SelfType, return the circuit's type. + /// + pub fn new_from_circuit( + table: &SymbolTable, + type_: UnresolvedType, + circuit_name: Identifier, + span: Span, + ) -> Result { + Ok(match type_ { + UnresolvedType::Array(type_, _) => { + let array_type = Type::new_from_circuit(table, *type_, circuit_name, span)?; + Type::Array(Box::new(array_type)) + } + UnresolvedType::Tuple(types) => { + let tuple_types = types + .into_iter() + .map(|type_| Type::new_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::new(table, unresolved, span)?, + }) + } + + /// Returns a list of signed integer types. + pub const fn signed_integer_types() -> [Type; 5] { + [ + Type::IntegerType(IntegerType::I8), + Type::IntegerType(IntegerType::I16), + Type::IntegerType(IntegerType::I32), + Type::IntegerType(IntegerType::I64), + Type::IntegerType(IntegerType::I128), + ] + } + + /// Returns a list of unsigned integer types. + pub const fn unsigned_integer_types() -> [Type; 5] { + [ + Type::IntegerType(IntegerType::U8), + Type::IntegerType(IntegerType::U16), + Type::IntegerType(IntegerType::U32), + Type::IntegerType(IntegerType::U64), + Type::IntegerType(IntegerType::U128), + ] + } + + /// Returns a list of positive integer types. + pub fn negative_integer_types() -> Vec { + let field_group = [Type::Field, Type::Group]; + + let mut types = Vec::new(); + + types.extend_from_slice(&field_group); + types.extend_from_slice(&Self::signed_integer_types()); + + types + } + + /// Returns a list of integer types. + pub fn integer_types() -> Vec { + let mut types = Vec::new(); + + types.extend_from_slice(&Self::unsigned_integer_types()); + types.extend_from_slice(&Self::negative_integer_types()); + + types + } + + /// Returns a list of possible index types (u8, u16, u32). + pub fn index_types() -> Vec { + let index_types = [ + Type::IntegerType(IntegerType::U8), + Type::IntegerType(IntegerType::U16), + Type::IntegerType(IntegerType::U32), + ]; + + let mut types = Vec::new(); + + types.extend_from_slice(&index_types); + + types + } + + /// + /// Replaces self with the given type if self is equal to the given `TypeVariable`. + /// + pub fn substitute(&mut self, variable: &TypeVariable, type_: &Type) { + match self { + Type::TypeVariable(self_variable) => { + if self_variable == variable { + *self = type_.to_owned() + } + } + Type::Array(self_type) => { + self_type.substitute(variable, type_); + } + Type::Tuple(types) => types + .iter_mut() + .for_each(|tuple_type| tuple_type.substitute(variable, type_)), + _ => {} + } + } +} + +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_) => write!(f, "[{}]", *type_), + Type::Tuple(tuple) => { + let tuple_string = tuple.iter().map(|x| x.to_string()).collect::>().join(", "); + + write!(f, "({})", tuple_string) + } + + Type::Circuit(identifier) => write!(f, "circuit {}", identifier), + Type::Function(identifier) => write!(f, "function {}", identifier), + Type::TypeVariable(type_variable) => write!(f, "{}", type_variable), + } + } +} + +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Type::Address, Type::Address) => true, + (Type::Boolean, Type::Boolean) => true, + (Type::Field, Type::Field) => true, + (Type::Group, Type::Group) => true, + (Type::IntegerType(integer_type1), Type::IntegerType(integer_type2)) => integer_type1.eq(integer_type2), + + (Type::Array(array1), Type::Array(array2)) => { + // Get both array element types before comparison. + let array1_element = get_array_element_type(array1); + let array2_element = get_array_element_type(array2); + + // Check that both arrays have the same element type. + array1_element.eq(array2_element) + } + + (Type::Tuple(types1), Type::Tuple(types2)) => types1.eq(types2), + (Type::Circuit(identifier1), Type::Circuit(identifier2)) => identifier1.eq(identifier2), + (Type::Function(identifier1), Type::Function(identifier2)) => identifier1.eq(identifier2), + (Type::TypeVariable(variable1), Type::TypeVariable(variable2)) => variable1.eq(variable2), + _ => false, + } + } +} + +impl Eq for Type {} + +/// +/// Returns the data type of the array element. +/// +/// If the given `type_` is an array, call `get_array_element_type()` on the array element type. +/// If the given `type_` is any other type, return the `type_`. +/// +pub fn get_array_element_type(type_: &Type) -> &Type { + if let Type::Array(element_type) = type_ { + get_array_element_type(element_type) + } else { + type_ + } +} diff --git a/symbol-table/src/types/type_variable.rs b/symbol-table/src/types/type_variable.rs new file mode 100644 index 0000000000..161c5f3953 --- /dev/null +++ b/symbol-table/src/types/type_variable.rs @@ -0,0 +1,46 @@ +// 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 leo_ast::Identifier; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// An unknown type in a Leo program. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TypeVariable { + identifier: Identifier, +} + +impl fmt::Display for TypeVariable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.identifier) + } +} + +impl From for TypeVariable { + fn from(identifier: Identifier) -> Self { + Self { identifier } + } +} + +/// Compare the type variable `Identifier` and `Span`. +impl PartialEq for TypeVariable { + fn eq(&self, other: &Self) -> bool { + self.identifier.name.eq(&other.identifier.name) || self.identifier.span.eq(&other.identifier.span) + } +} + +impl Eq for TypeVariable {} diff --git a/symbol-table/src/types/user_defined/mod.rs b/symbol-table/src/types/user_defined/mod.rs new file mode 100644 index 0000000000..c919d6a014 --- /dev/null +++ b/symbol-table/src/types/user_defined/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 user_defined_type; +pub use self::user_defined_type::*; diff --git a/symbol-table/src/types/user_defined/user_defined_type.rs b/symbol-table/src/types/user_defined/user_defined_type.rs new file mode 100644 index 0000000000..80e75eedf8 --- /dev/null +++ b/symbol-table/src/types/user_defined/user_defined_type.rs @@ -0,0 +1,86 @@ +// 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, FunctionInputVariableType, Type}; +use leo_ast::{Circuit, Function, Identifier}; + +use std::{ + fmt, + hash::{Hash, Hasher}, +}; + +/// Stores information for a user defined type. +/// +/// User defined types include circuits and functions in a Leo program. +#[derive(Clone, Debug)] +pub struct UserDefinedType { + pub identifier: Identifier, + pub type_: Type, + pub attribute: Option, +} + +impl From for UserDefinedType { + fn from(value: Circuit) -> Self { + let identifier = value.circuit_name; + + UserDefinedType { + identifier: identifier.clone(), + type_: Type::Circuit(identifier), + attribute: None, + } + } +} + +impl From for UserDefinedType { + fn from(value: Function) -> Self { + let identifier = value.identifier; + + UserDefinedType { + identifier: identifier.clone(), + type_: Type::Function(identifier), + attribute: None, + } + } +} + +impl From for UserDefinedType { + fn from(value: FunctionInputVariableType) -> Self { + UserDefinedType { + identifier: value.identifier, + type_: value.type_, + attribute: value.attribute, + } + } +} + +impl fmt::Display for UserDefinedType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.identifier) + } +} + +impl PartialEq for UserDefinedType { + fn eq(&self, other: &Self) -> bool { + self.identifier.eq(&other.identifier) + } +} + +impl Eq for UserDefinedType {} + +impl Hash for UserDefinedType { + fn hash(&self, state: &mut H) { + self.identifier.hash(state); + } +} diff --git a/symbol-table/tests/mod.rs b/symbol-table/tests/mod.rs new file mode 100644 index 0000000000..ccb84e1170 --- /dev/null +++ b/symbol-table/tests/mod.rs @@ -0,0 +1,122 @@ +// 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::{Input, LeoAst}; +use leo_grammar::Grammar; +use leo_symbol_table::{SymbolTable, SymbolTableError}; + +use leo_imports::ImportParser; +use std::path::PathBuf; + +const TEST_PROGRAM_PATH: &str = ""; + +/// A helper struct to test a `SymbolTable`. +pub struct TestSymbolTable { + ast: LeoAst, +} + +impl TestSymbolTable { + /// + /// Returns a Leo 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 grammar = Grammar::new(&file_path, &*file_string).unwrap(); + + // Get Leo syntax tree + let ast = LeoAst::new(TEST_PROGRAM_PATH, &grammar); + + Self { ast } + } + + /// + /// Parse the Leo syntax tree into a symbol table. + /// + /// Expect no errors during parsing. + /// + pub fn expect_success(self) { + // Get program. + let program = self.ast.into_repr(); + + // Create empty import parser. + let import_parser = ImportParser::default(); + + // Create empty input. + let input = Input::new(); + + // Create new symbol table. + let _symbol_table = SymbolTable::new(&program, &import_parser, &input).unwrap(); + } + + /// + /// Parse the Leo 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.ast.into_repr(); + + // Create new symbol table. + let static_check = &mut SymbolTable::default(); + + // Create empty import parser. + let import_parser = ImportParser::default(); + + // Run pass one and expect an error. + let error = static_check.check_names(&program, &import_parser).unwrap_err(); + + match error { + SymbolTableError::Error(_) => {} // Ok + error => panic!("Expected a symbol table error found `{}`", error), + } + } + + /// + /// Parse the Leo 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.ast.into_repr(); + + // Create a new symbol table. + let static_check = &mut SymbolTable::default(); + + // Create empty import parser. + let import_parser = ImportParser::default(); + + // Run the pass one and expect no errors. + static_check.check_names(&program, &import_parser).unwrap(); + + // Run the pass two and expect and error. + let error = static_check.check_types(&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..4d40891377 --- /dev/null +++ b/symbol-table/tests/symbol_table/mod.rs @@ -0,0 +1,75 @@ +// 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