diff --git a/Cargo.lock b/Cargo.lock index 60fadbe..0b34ba8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,6 +375,7 @@ dependencies = [ "bumpalo", "cc", "concrete_ast", + "concrete_check", "concrete_session", "itertools 0.12.0", "llvm-sys", diff --git a/crates/concrete_check/src/lib.rs b/crates/concrete_check/src/lib.rs index 2dbeb84..9ea6c0e 100644 --- a/crates/concrete_check/src/lib.rs +++ b/crates/concrete_check/src/lib.rs @@ -2,12 +2,12 @@ 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_ast::{common::Ident, imports::ImportStmt, modules::Module, Program}; use concrete_session::Session; use itertools::Itertools; use thiserror::Error; -mod ast_helper; +pub mod ast_helper; #[derive(Error, Debug, Clone)] pub enum CheckError<'p> { @@ -16,6 +16,12 @@ pub enum CheckError<'p> { module: &'p Module, import: &'p ImportStmt, }, + #[error("import symbol {:?} not found in module {}", symbol, module.name.name)] + ImportSymbolMissing { + module: &'p Module, + import: &'p ImportStmt, + symbol: &'p Ident, + }, } impl<'p> CheckError<'p> { @@ -59,6 +65,32 @@ impl<'p> CheckError<'p> { .with_message("Failed to find import.") .finish() } + CheckError::ImportSymbolMissing { + module, + import, + symbol, + } => { + let contex_module_span = module.span; + let import_span = import.span; + let offset = symbol.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.clone(), import_span.into())) + .with_message("In this import statement"), + ) + .with_label( + Label::new((path, symbol.span.into())) + .with_message(format!("Failed to find symbol {:?}", symbol.name)) + .with_color(colors.next()), + ) + .with_message("Failed to find import.") + .finish() + } } } } @@ -76,6 +108,7 @@ pub fn check_program<'p>( let mut imports = HashMap::new(); + // check modules for import in &module.imports { let target_module = helper.get_module_from_import(&import.module); @@ -86,8 +119,23 @@ pub fn check_program<'p>( let target_module = target_module.unwrap(); + // check if symbol exists for symbol in &import.symbols { - // todo: check if symbol exists. + let name = &symbol.name; + let exists = target_module.functions.get(name).is_some() + || target_module.constants.get(name).is_some() + || target_module.structs.get(name).is_some() + || target_module.types.get(name).is_some(); + + if !exists { + errors.push(CheckError::ImportSymbolMissing { + module, + import, + symbol, + }); + continue; + } + imports.insert(symbol.name.clone(), target_module); } } diff --git a/crates/concrete_codegen_mlir/Cargo.toml b/crates/concrete_codegen_mlir/Cargo.toml index 9df9c6f..6013882 100644 --- a/crates/concrete_codegen_mlir/Cargo.toml +++ b/crates/concrete_codegen_mlir/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bumpalo = { version = "3.14.0", features = ["std"] } concrete_ast = { path = "../concrete_ast"} +concrete_check = { version = "0.1.0", path = "../concrete_check" } concrete_session = { path = "../concrete_session"} itertools = "0.12.0" llvm-sys = "170.0.1" diff --git a/crates/concrete_codegen_mlir/src/ast_helper.rs b/crates/concrete_codegen_mlir/src/ast_helper.rs deleted file mode 100644 index 0d8c6bd..0000000 --- a/crates/concrete_codegen_mlir/src/ast_helper.rs +++ /dev/null @@ -1,127 +0,0 @@ -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_codegen_mlir/src/codegen.rs b/crates/concrete_codegen_mlir/src/codegen.rs index 0db203f..13e74ad 100644 --- a/crates/concrete_codegen_mlir/src/codegen.rs +++ b/crates/concrete_codegen_mlir/src/codegen.rs @@ -11,6 +11,7 @@ use concrete_ast::{ types::TypeSpec, Program, }; +use concrete_check::ast_helper::{AstHelper, ModuleInfo}; use concrete_session::Session; use melior::{ dialect::{ @@ -26,8 +27,6 @@ use melior::{ Context as MeliorContext, }; -use crate::ast_helper::{AstHelper, ModuleInfo}; - pub fn compile_program( session: &Session, ctx: &MeliorContext, diff --git a/crates/concrete_codegen_mlir/src/lib.rs b/crates/concrete_codegen_mlir/src/lib.rs index 372e73f..959807b 100644 --- a/crates/concrete_codegen_mlir/src/lib.rs +++ b/crates/concrete_codegen_mlir/src/lib.rs @@ -29,7 +29,6 @@ use llvm_sys::{ }; use module::MLIRModule; -mod ast_helper; mod codegen; mod context; mod error; diff --git a/crates/concrete_parser/src/grammar.lalrpop b/crates/concrete_parser/src/grammar.lalrpop index 5bb7c78..dc0b675 100644 --- a/crates/concrete_parser/src/grammar.lalrpop +++ b/crates/concrete_parser/src/grammar.lalrpop @@ -188,12 +188,12 @@ 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: contents.unwrap_or_else(Vec::new), span: Span::new(lo, hi), } } @@ -270,7 +270,7 @@ pub(crate) Param: ast::functions::Param = { pub(crate) FunctionDef: ast::functions::FunctionDef = { "fn" "(" > ")" "{" - + "}" => { ast::functions::FunctionDef { decl: ast::functions::FunctionDecl { @@ -280,7 +280,7 @@ pub(crate) FunctionDef: ast::functions::FunctionDef = { params, ret_type, }, - body: statements + body: statements.unwrap_or_else(Vec::new) } } } diff --git a/crates/concrete_parser/src/lib.rs b/crates/concrete_parser/src/lib.rs index d0b4e1b..f6fcb16 100644 --- a/crates/concrete_parser/src/lib.rs +++ b/crates/concrete_parser/src/lib.rs @@ -89,7 +89,7 @@ mod ModuleName { "##; let lexer = Lexer::new(source); let parser = grammar::ProgramParser::new(); - dbg!(parser.parse(lexer).unwrap()); + parser.parse(lexer).unwrap(); } #[test] @@ -104,7 +104,7 @@ mod ModuleName { }"##; let lexer = Lexer::new(source); let parser = grammar::ProgramParser::new(); - dbg!(parser.parse(lexer).unwrap()); + parser.parse(lexer).unwrap(); } #[test] @@ -116,7 +116,7 @@ mod ModuleName { }"##; let lexer = Lexer::new(source); let parser = grammar::ProgramParser::new(); - dbg!(parser.parse(lexer).unwrap()); + parser.parse(lexer).unwrap(); } #[test] @@ -128,6 +128,25 @@ mod ModuleName { }"##; let lexer = Lexer::new(source); let parser = grammar::ProgramParser::new(); - dbg!(parser.parse(lexer).unwrap()); + parser.parse(lexer).unwrap(); + } + + #[test] + fn parse_empty_mod() { + let source = r##"mod MyMod { +}"##; + let lexer = Lexer::new(source); + let parser = grammar::ProgramParser::new(); + parser.parse(lexer).unwrap(); + } + + #[test] + fn parse_empty_fn() { + let source = r##"mod MyMod { + fn hello() {} +}"##; + let lexer = Lexer::new(source); + let parser = grammar::ProgramParser::new(); + parser.parse(lexer).unwrap(); } } diff --git a/examples/missing_import.con b/examples/missing_import.con index f299ba0..3987d99 100644 --- a/examples/missing_import.con +++ b/examples/missing_import.con @@ -1,8 +1,19 @@ mod Simple { import Other.{hello1}; import Other.Deep.{hello2}; + import Hello.{world}; fn main() -> i64 { return hello1(2) + hello2(2); } } + +mod Hello { + fn a() { + return 1; + } +} + +mod A { + +}