From 843cdecbfb83d7596967f78faa8eeda9f81ba551 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 18 Jan 2024 17:33:14 +0100 Subject: [PATCH] check --- Cargo.lock | 16 ++- Cargo.toml | 2 +- crates/concrete_ast/src/common.rs | 8 ++ crates/concrete_ast/src/imports.rs | 3 +- crates/concrete_ast/src/modules.rs | 3 +- crates/concrete_check/Cargo.toml | 13 +++ crates/concrete_check/src/ast_helper.rs | 127 +++++++++++++++++++++ crates/concrete_check/src/lib.rs | 101 ++++++++++++++++ crates/concrete_driver/Cargo.toml | 1 + crates/concrete_driver/src/lib.rs | 12 ++ crates/concrete_parser/src/grammar.lalrpop | 8 +- crates/concrete_type_checker/Cargo.toml | 8 -- crates/concrete_type_checker/src/lib.rs | 1 - examples/missing_import.con | 8 ++ 14 files changed, 292 insertions(+), 19 deletions(-) create mode 100644 crates/concrete_check/Cargo.toml create mode 100644 crates/concrete_check/src/ast_helper.rs create mode 100644 crates/concrete_check/src/lib.rs delete mode 100644 crates/concrete_type_checker/Cargo.toml delete mode 100644 crates/concrete_type_checker/src/lib.rs create mode 100644 examples/missing_import.con diff --git a/Cargo.lock b/Cargo.lock index 25c2bd6..60fadbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "concrete_check" +version = "0.1.0" +dependencies = [ + "ariadne", + "concrete_ast", + "concrete_session", + "itertools 0.12.0", + "thiserror", +] + [[package]] name = "concrete_codegen_mlir" version = "0.1.0" @@ -379,6 +390,7 @@ dependencies = [ "ariadne", "clap", "concrete_ast", + "concrete_check", "concrete_codegen_mlir", "concrete_parser", "concrete_session", @@ -409,10 +421,6 @@ dependencies = [ "ariadne", ] -[[package]] -name = "concrete_type_checker" -version = "0.1.0" - [[package]] name = "convert_case" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index d8e9029..27930ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" -members = [ "crates/concrete","crates/concrete_ast", "crates/concrete_codegen_mlir", "crates/concrete_driver", "crates/concrete_parser", "crates/concrete_session", "crates/concrete_type_checker"] +members = [ "crates/concrete","crates/concrete_ast", "crates/concrete_codegen_mlir", "crates/concrete_driver", "crates/concrete_parser", "crates/concrete_session", "crates/concrete_check"] [profile.release] lto = true diff --git a/crates/concrete_ast/src/common.rs b/crates/concrete_ast/src/common.rs index c94d349..ef7a45f 100644 --- a/crates/concrete_ast/src/common.rs +++ b/crates/concrete_ast/src/common.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Span { pub from: usize, @@ -10,6 +12,12 @@ impl Span { } } +impl From for Range { + fn from(val: Span) -> Self { + val.from..val.to + } +} + #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] pub struct DocString { contents: String, diff --git a/crates/concrete_ast/src/imports.rs b/crates/concrete_ast/src/imports.rs index f158b9b..615230c 100644 --- a/crates/concrete_ast/src/imports.rs +++ b/crates/concrete_ast/src/imports.rs @@ -1,7 +1,8 @@ -use crate::common::Ident; +use crate::common::{Ident, Span}; #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct ImportStmt { pub module: Vec, pub symbols: Vec, + pub span: Span, } diff --git a/crates/concrete_ast/src/modules.rs b/crates/concrete_ast/src/modules.rs index d67b590..5ed22cf 100644 --- a/crates/concrete_ast/src/modules.rs +++ b/crates/concrete_ast/src/modules.rs @@ -1,5 +1,5 @@ use crate::{ - common::{DocString, Ident}, + common::{DocString, Ident, Span}, constants::ConstantDef, functions::FunctionDef, imports::ImportStmt, @@ -13,6 +13,7 @@ pub struct Module { pub imports: Vec, pub name: Ident, pub contents: Vec, + pub span: Span, } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/crates/concrete_check/Cargo.toml b/crates/concrete_check/Cargo.toml new file mode 100644 index 0000000..b326b82 --- /dev/null +++ b/crates/concrete_check/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "concrete_check" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ariadne = { version = "0.4.0", features = ["auto-color"] } +concrete_ast = { version = "0.1.0", path = "../concrete_ast" } +concrete_session = { version = "0.1.0", path = "../concrete_session" } +itertools = "0.12.0" +thiserror = "1.0.56" diff --git a/crates/concrete_check/src/ast_helper.rs b/crates/concrete_check/src/ast_helper.rs new file mode 100644 index 0000000..0d8c6bd --- /dev/null +++ b/crates/concrete_check/src/ast_helper.rs @@ -0,0 +1,127 @@ +use std::collections::HashMap; + +use concrete_ast::{ + common::Ident, + constants::ConstantDef, + functions::FunctionDef, + modules::{Module, ModuleDefItem}, + structs::StructDecl, + types::TypeDecl, + Program, +}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleInfo<'p> { + pub name: String, + pub functions: HashMap, + pub constants: HashMap, + pub structs: HashMap, + pub types: HashMap, + pub modules: HashMap>, +} + +impl<'p> ModuleInfo<'p> { + pub fn get_module_from_import(&self, import: &[Ident]) -> Option<&ModuleInfo<'p>> { + let next = import.first()?; + let module = self.modules.get(&next.name)?; + + if import.len() > 1 { + module.get_module_from_import(&import[1..]) + } else { + Some(module) + } + } + + /// Returns the symbol name from a local name. + pub fn get_symbol_name(&self, local_name: &str) -> String { + if local_name == "main" { + return local_name.to_string(); + } + + let mut result = self.name.clone(); + + result.push_str("::"); + result.push_str(local_name); + + result + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AstHelper<'p> { + pub root: &'p Program, + pub modules: HashMap>, +} + +impl<'p> AstHelper<'p> { + pub fn new(root: &'p Program) -> Self { + let mut modules = HashMap::default(); + + for module in &root.modules { + modules.insert( + module.name.name.clone(), + Self::create_module_info(module, None), + ); + } + + Self { root, modules } + } + + pub fn get_module_from_import(&self, import: &[Ident]) -> Option<&ModuleInfo<'p>> { + let next = import.first()?; + let module = self.modules.get(&next.name)?; + + if import.len() > 1 { + module.get_module_from_import(&import[1..]) + } else { + Some(module) + } + } + + fn create_module_info(module: &Module, parent_name: Option) -> ModuleInfo<'_> { + let mut functions = HashMap::default(); + let mut constants = HashMap::default(); + let mut structs = HashMap::default(); + let mut types = HashMap::default(); + let mut child_modules = HashMap::default(); + let mut name = parent_name.clone().unwrap_or_default(); + + if name.is_empty() { + name = module.name.name.clone(); + } else { + name.push_str(&format!("::{}", module.name.name)); + } + + for stmt in &module.contents { + match stmt { + ModuleDefItem::Constant(info) => { + constants.insert(info.decl.name.name.clone(), info); + } + ModuleDefItem::Function(info) => { + functions.insert(info.decl.name.name.clone(), info); + } + ModuleDefItem::Struct(info) => { + structs.insert(info.name.name.clone(), info); + } + ModuleDefItem::Type(info) => { + types.insert(info.name.name.clone(), info); + } + ModuleDefItem::Module(info) => { + child_modules.insert( + info.name.name.clone(), + Self::create_module_info(info, Some(name.clone())), + ); + } + } + } + + ModuleInfo { + name, + functions, + structs, + constants, + types, + modules: child_modules, + } + } +} diff --git a/crates/concrete_check/src/lib.rs b/crates/concrete_check/src/lib.rs new file mode 100644 index 0000000..2dbeb84 --- /dev/null +++ b/crates/concrete_check/src/lib.rs @@ -0,0 +1,101 @@ +use std::{collections::HashMap, ops::Range}; + +use ariadne::{ColorGenerator, Label, Report, ReportKind}; +use ast_helper::AstHelper; +use concrete_ast::{imports::ImportStmt, modules::Module, Program}; +use concrete_session::Session; +use itertools::Itertools; +use thiserror::Error; + +mod ast_helper; + +#[derive(Error, Debug, Clone)] +pub enum CheckError<'p> { + #[error("import not found {:?} in module {}", import, module.name.name)] + ImportModuleMissing { + module: &'p Module, + import: &'p ImportStmt, + }, +} + +impl<'p> CheckError<'p> { + pub fn to_report<'s>(&self, session: &'s Session) -> Report<'s, (String, Range)> { + let path = session.file_path.display().to_string(); + let mut colors = ColorGenerator::new(); + colors.next(); + + match self { + CheckError::ImportModuleMissing { module, import } => { + let contex_module_span = module.span; + let span = { + let mut span = import + .module + .first() + .expect("module path can't be empty") + .span; + span.to = import + .module + .last() + .expect("module path can't be empty") + .span + .to; + span + }; + let offset = import.module[0].span.from; + Report::build(ReportKind::Error, path.clone(), offset) + .with_code("E1") + .with_label( + Label::new((path.clone(), contex_module_span.into())) + .with_message(format!("In module {:?}.", module.name.name)), + ) + .with_label( + Label::new((path, span.into())) + .with_message(format!( + "Module {:?} not found.", + import.module.iter().map(|x| &x.name).join(".") + )) + .with_color(colors.next()), + ) + .with_message("Failed to find import.") + .finish() + } + } + } +} + +pub fn check_program<'p>( + program: &'p Program, + _session: &Session, +) -> Result<(), Vec>> { + let helper = AstHelper::new(program); + + let mut errors = Vec::new(); + + for module in &program.modules { + // let info = helper.modules.get(&module.name.name).unwrap(); + + let mut imports = HashMap::new(); + + for import in &module.imports { + let target_module = helper.get_module_from_import(&import.module); + + if target_module.is_none() { + errors.push(CheckError::ImportModuleMissing { module, import }); + continue; + } + + let target_module = target_module.unwrap(); + + for symbol in &import.symbols { + // todo: check if symbol exists. + imports.insert(symbol.name.clone(), target_module); + } + } + } + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } +} diff --git a/crates/concrete_driver/Cargo.toml b/crates/concrete_driver/Cargo.toml index 35b9084..865d496 100644 --- a/crates/concrete_driver/Cargo.toml +++ b/crates/concrete_driver/Cargo.toml @@ -15,6 +15,7 @@ concrete_session = { path = "../concrete_session"} concrete_codegen_mlir = { path = "../concrete_codegen_mlir"} salsa = { git = "https://github.com/salsa-rs/salsa.git", package = "salsa-2022" } ariadne = { version = "0.4.0", features = ["auto-color"] } +concrete_check = { version = "0.1.0", path = "../concrete_check" } [dev-dependencies] tempfile = "3.9.0" diff --git a/crates/concrete_driver/src/lib.rs b/crates/concrete_driver/src/lib.rs index e293af0..cb29140 100644 --- a/crates/concrete_driver/src/lib.rs +++ b/crates/concrete_driver/src/lib.rs @@ -88,8 +88,20 @@ pub fn main() -> Result<(), Box> { target_dir, output_file, }; + tracing::debug!("Compiling with session: {:#?}", session); + if let Err(errors) = concrete_check::check_program(&program, &session) { + for error in &errors { + let path = session.file_path.display().to_string(); + error + .to_report(&session) + .eprint((path, session.source.clone()))?; + } + + std::process::exit(1); + } + let object_path = concrete_codegen_mlir::compile(&session, &program)?; if session.library { diff --git a/crates/concrete_parser/src/grammar.lalrpop b/crates/concrete_parser/src/grammar.lalrpop index 7bcda21..5bb7c78 100644 --- a/crates/concrete_parser/src/grammar.lalrpop +++ b/crates/concrete_parser/src/grammar.lalrpop @@ -188,12 +188,13 @@ pub Program: ast::Program = { // Modules pub(crate) Module: ast::modules::Module = { - "mod" "{" "}" => { + "mod" "{" "}" => { ast::modules::Module { doc_string: None, imports: imports.unwrap_or_else(Vec::new), name, - contents + contents, + span: Span::new(lo, hi), } } } @@ -208,10 +209,11 @@ pub(crate) ImportList: Vec = { pub(crate) ImportStmt: ast::imports::ImportStmt = { - "import" > "{" > "}" ";" => { + "import" > "{" > "}" ";" => { ast::imports::ImportStmt { module, symbols, + span: Span::new(lo, hi), } } } diff --git a/crates/concrete_type_checker/Cargo.toml b/crates/concrete_type_checker/Cargo.toml deleted file mode 100644 index 44762dd..0000000 --- a/crates/concrete_type_checker/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "concrete_type_checker" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/crates/concrete_type_checker/src/lib.rs b/crates/concrete_type_checker/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/concrete_type_checker/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/missing_import.con b/examples/missing_import.con new file mode 100644 index 0000000..f299ba0 --- /dev/null +++ b/examples/missing_import.con @@ -0,0 +1,8 @@ +mod Simple { + import Other.{hello1}; + import Other.Deep.{hello2}; + + fn main() -> i64 { + return hello1(2) + hello2(2); + } +}