diff --git a/reid-llvm-lib/examples/libtest.rs b/reid-llvm-lib/examples/libtest.rs index f33f871..9389586 100644 --- a/reid-llvm-lib/examples/libtest.rs +++ b/reid-llvm-lib/examples/libtest.rs @@ -6,7 +6,7 @@ fn main() { let context = Context::new(); - let mut module = context.module("test"); + let mut module = context.module("test", true); let main = module.function("main", Type::I32, Vec::new(), FunctionFlags::default()); let mut m_entry = main.block("entry"); diff --git a/reid-llvm-lib/src/builder.rs b/reid-llvm-lib/src/builder.rs index 0b2d542..3e78fde 100644 --- a/reid-llvm-lib/src/builder.rs +++ b/reid-llvm-lib/src/builder.rs @@ -272,7 +272,6 @@ impl Builder { } Store(ptr, _) => { if let Ok(ty) = ptr.get_type(&self) { - dbg!(&ty); if let Type::Ptr(_) = ty { Ok(()) } else { diff --git a/reid-llvm-lib/src/compile.rs b/reid-llvm-lib/src/compile.rs index 16cbc48..51786b1 100644 --- a/reid-llvm-lib/src/compile.rs +++ b/reid-llvm-lib/src/compile.rs @@ -2,7 +2,7 @@ //! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces //! with the LLVM API. -use std::{collections::HashMap, ffi::CString, marker::PhantomData, ptr::null_mut}; +use std::{collections::HashMap, ffi::CString, ptr::null_mut}; use llvm_sys::{ LLVMIntPredicate, LLVMLinkage, @@ -134,13 +134,11 @@ impl Context { .clone(); let main_module_ref = main_module.compile(&context, &self.builder); - dbg!("main"); for holder in module_holders.borrow().iter() { if holder.value == main_module.value { continue; } - dbg!(holder.value); let module_ref = holder.compile(&context, &self.builder); LLVMLinkModules2(main_module_ref, module_ref); } @@ -205,7 +203,7 @@ impl ModuleHolder { }; for function in &self.functions { - function.compile(&mut module); + function.compile(&mut module, self.data.is_main); } module_ref @@ -242,21 +240,26 @@ impl FunctionHolder { } } - unsafe fn compile(&self, module: &mut LLVMModule) { + unsafe fn compile(&self, module: &mut LLVMModule, in_main_module: bool) { unsafe { let own_function = *module.functions.get(&self.value).unwrap(); if self.data.flags.is_extern { LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); + // Use "available internally" if the other kind of extern return; } - if self.data.flags.is_pub || self.data.name == "main" { + if self.data.flags.is_imported && !in_main_module { LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); } else { LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMPrivateLinkage); } + if in_main_module && self.data.flags.is_main { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); + } + for block in &self.blocks { if block.data.deleted { continue; diff --git a/reid-llvm-lib/src/lib.rs b/reid-llvm-lib/src/lib.rs index 00f878f..75f703f 100644 --- a/reid-llvm-lib/src/lib.rs +++ b/reid-llvm-lib/src/lib.rs @@ -4,7 +4,7 @@ use std::{fmt::Debug, marker::PhantomData}; -use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleHolder, ModuleValue}; +use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue}; use debug::PrintableModule; pub mod builder; @@ -26,9 +26,10 @@ impl Context { } } - pub fn module<'ctx>(&'ctx self, name: &str) -> Module<'ctx> { + pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> { let value = self.builder.add_module(ModuleData { name: name.to_owned(), + is_main: main, }); Module { phantom: PhantomData, @@ -41,6 +42,7 @@ impl Context { #[derive(Debug, Clone, Hash)] pub struct ModuleData { name: String, + is_main: bool, } pub struct Module<'ctx> { @@ -97,14 +99,18 @@ pub struct FunctionData { #[derive(Debug, Clone, Copy, Hash)] pub struct FunctionFlags { pub is_extern: bool, + pub is_main: bool, pub is_pub: bool, + pub is_imported: bool, } impl Default for FunctionFlags { fn default() -> FunctionFlags { FunctionFlags { is_extern: false, + is_main: false, is_pub: false, + is_imported: false, } } } diff --git a/reid/examples/testcodegen.rs b/reid/examples/testcodegen.rs index 6aa34a5..8c6b2d2 100644 --- a/reid/examples/testcodegen.rs +++ b/reid/examples/testcodegen.rs @@ -10,6 +10,7 @@ fn main() { let fibonacci = FunctionDefinition { name: fibonacci_name.clone(), is_pub: false, + is_imported: false, return_type: TypeKind::I32, parameters: vec![(fibonacci_n.clone(), TypeKind::I32)], kind: FunctionDefinitionKind::Local( @@ -131,6 +132,7 @@ fn main() { let main = FunctionDefinition { name: "main".to_owned(), is_pub: false, + is_imported: false, return_type: TypeKind::I32, parameters: vec![], kind: FunctionDefinitionKind::Local( @@ -162,6 +164,7 @@ fn main() { imports: vec![], functions: vec![fibonacci, main], path: None, + is_main: true, }], base: PathBuf::new(), }; diff --git a/reid/src/ast/mod.rs b/reid/src/ast/mod.rs index d768c39..c40a272 100644 --- a/reid/src/ast/mod.rs +++ b/reid/src/ast/mod.rs @@ -169,4 +169,5 @@ pub struct Module { pub name: String, pub top_level_statements: Vec, pub path: Option, + pub is_main: bool, } diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index a7a51e5..6f9a96c 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -401,9 +401,7 @@ impl Parse for VariableReference { stream.get_range().unwrap(), ); - dbg!(&var_ref); while let Ok(ValueIndex(idx)) = stream.parse() { - dbg!(idx); var_ref = VariableReference( VariableReferenceKind::Index(Box::new(var_ref), idx), stream.get_range().unwrap(), diff --git a/reid/src/ast/process.rs b/reid/src/ast/process.rs index f8f27ee..3e58dd2 100644 --- a/reid/src/ast/process.rs +++ b/reid/src/ast/process.rs @@ -26,6 +26,7 @@ impl ast::Module { let def = mir::FunctionDefinition { name: signature.name.clone(), is_pub: *is_pub, + is_imported: false, return_type: signature .return_type .clone() @@ -45,6 +46,7 @@ impl ast::Module { let def = mir::FunctionDefinition { name: signature.name.clone(), is_pub: false, + is_imported: false, return_type: signature .return_type .clone() @@ -68,6 +70,7 @@ impl ast::Module { imports, functions, path: self.path.clone(), + is_main: self.is_main, } } } diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index a226de7..dc2ecd2 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -45,7 +45,7 @@ impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> { impl mir::Module { fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> { - let mut module = context.module(&self.name); + let mut module = context.module(&self.name, self.is_main); let mut functions = HashMap::new(); @@ -56,13 +56,16 @@ impl mir::Module { .map(|(_, p)| p.get_type()) .collect(); + let is_main = self.is_main && function.name == "main"; let func = match &function.kind { mir::FunctionDefinitionKind::Local(_, _) => module.function( &function.name, function.return_type.get_type(), param_types, FunctionFlags { - is_pub: function.is_pub, + is_pub: function.is_pub || is_main, + is_main, + is_imported: function.is_imported, ..FunctionFlags::default() }, ), diff --git a/reid/src/lib.rs b/reid/src/lib.rs index 939cb32..cb344b9 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -43,7 +43,7 @@ use std::path::PathBuf; use mir::{ - imports::ImportsPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs, + linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs, }; use reid_lib::Context; @@ -65,12 +65,17 @@ pub enum ReidError { ParserError(#[from] token_stream::Error), #[error("Errors during typecheck: {0:?}")] TypeCheckErrors(Vec>), + #[error("Errors during type inference: {0:?}")] + TypeInferenceErrors(Vec>), + #[error("Errors during linking: {0:?}")] + LinkerErrors(Vec>), } pub fn compile_module( source: &str, name: String, path: Option, + is_main: bool, ) -> Result { let tokens = lexer::tokenize(source)?; @@ -89,6 +94,7 @@ pub fn compile_module( name, top_level_statements: statements, path, + is_main, }; Ok(ast_module.process()) @@ -103,18 +109,23 @@ pub fn compile(source: &str, path: PathBuf) -> Result { let mut mir_context = mir::Context::from( vec![compile_module( source, - "main".to_owned(), + path.file_name().unwrap().to_str().unwrap().to_owned(), Some(path.clone()), + true, )?], path.parent().unwrap().to_owned(), ); println!("{}", &mir_context); - let state = mir_context.pass(&mut ImportsPass); + let state = mir_context.pass(&mut LinkerPass); dbg!(&state); println!("{}", &mir_context); + if !state.errors.is_empty() { + return Err(ReidError::LinkerErrors(state.errors)); + } + let refs = TypeRefs::default(); let state = mir_context.pass(&mut TypeInference { refs: &refs }); @@ -122,6 +133,10 @@ pub fn compile(source: &str, path: PathBuf) -> Result { dbg!(&mir_context); println!("{}", &mir_context); + // if !state.errors.is_empty() { + // return Err(ReidError::TypeInferenceErrors(state.errors)); + // } + let state = mir_context.pass(&mut TypeCheck { refs: &refs }); dbg!(&state); println!("{}", &mir_context); diff --git a/reid/src/mir/imports.rs b/reid/src/mir/imports.rs deleted file mode 100644 index 2c99de6..0000000 --- a/reid/src/mir/imports.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::{collections::HashMap, convert::Infallible, fs, path::PathBuf}; - -use crate::{compile_module, ReidError}; - -use super::{ - pass::{Pass, PassState}, - types::EqualsIssue, - Context, FunctionDefinition, Import, Module, -}; - -#[derive(thiserror::Error, Debug, Clone)] -pub enum ErrorKind { - #[error("Unable to import inner modules, not yet supported: {0}")] - InnerModulesNotYetSupported(Import), - #[error("No such module: {0}")] - ModuleNotFound(String), - #[error("Error while compiling module {0}: {1}")] - ModuleCompilationError(String, ReidError), - #[error("No such function {0} found in module {1}")] - NoSuchFunctionInModule(String, String), - #[error("Importing function {0}::{1} not possible: {2}")] - FunctionImportIssue(String, String, EqualsIssue), -} - -/// Struct used to implement a type-checking pass that can be performed on the -/// MIR. -pub struct ImportsPass; - -impl Pass for ImportsPass { - type TError = ErrorKind; - fn context(&mut self, context: &mut Context, mut state: PassState) { - let mut modules = HashMap::::new(); - - for module in context.modules.clone() { - modules.insert(module.name.clone(), module); - } - - let mut modules_to_process: Vec = modules.values().cloned().collect(); - - while let Some(mut module) = modules_to_process.pop() { - for import in &module.imports { - let Import(path, _) = import; - if path.len() != 2 { - state.ok::<_, Infallible>( - Err(ErrorKind::InnerModulesNotYetSupported(import.clone())), - import.1, - ); - } - - let module_name = unsafe { path.get_unchecked(0) }; - - let imported = if let Some(module) = modules.get(module_name) { - module - } else { - let file_path = - PathBuf::from(&context.base.clone()).join(module_name.to_owned() + ".reid"); - - dbg!(&file_path); - let Ok(source) = fs::read_to_string(&file_path) else { - state.ok::<_, Infallible>( - Err(ErrorKind::ModuleNotFound(module_name.clone())), - import.1, - ); - continue; - }; - - match compile_module(&source, module_name.clone(), Some(file_path)) { - Ok(m) => { - let module_name = module.name.clone(); - modules.insert(module_name.clone(), m); - modules_to_process.push(modules.get_mut(&module_name).unwrap().clone()); - modules.get(&module_name).unwrap() - } - Err(err) => { - state.ok::<_, Infallible>( - Err(ErrorKind::ModuleCompilationError(module_name.clone(), err)), - import.1, - ); - continue; - } - } - }; - - let func_name = unsafe { path.get_unchecked(1) }; - - let Some(func) = imported.functions.iter().find(|f| f.name == *func_name) else { - state.ok::<_, Infallible>( - Err(ErrorKind::NoSuchFunctionInModule( - module_name.clone(), - func_name.clone(), - )), - import.1, - ); - continue; - }; - - if let Some(existing) = module.functions.iter().find(|f| f.name == *func_name) { - if let Err(e) = existing.equals_as_imported(func) { - state.ok::<_, Infallible>( - Err(ErrorKind::FunctionImportIssue( - module_name.clone(), - func_name.clone(), - e, - )), - import.1, - ); - } - } - - module.functions.push(FunctionDefinition { - name: func.name.clone(), - is_pub: false, - return_type: func.return_type.clone(), - parameters: func.parameters.clone(), - kind: super::FunctionDefinitionKind::Extern, - }); - } - - modules.insert(module.name.clone(), module); - } - - context.modules = modules.into_values().collect(); - } -} diff --git a/reid/src/mir/linker.rs b/reid/src/mir/linker.rs new file mode 100644 index 0000000..a955c46 --- /dev/null +++ b/reid/src/mir/linker.rs @@ -0,0 +1,193 @@ +use std::{ + cell::RefCell, + collections::HashMap, + convert::Infallible, + fs::{self}, + path::PathBuf, + rc::Rc, +}; + +use crate::{compile_module, ReidError}; + +use super::{ + pass::{Pass, PassState}, + types::EqualsIssue, + Context, FunctionDefinition, Import, Metadata, Module, +}; + +#[derive(thiserror::Error, Debug, Clone)] +pub enum ErrorKind { + #[error("Unable to import inner modules, not yet supported: {0}")] + InnerModulesNotYetSupported(Import), + #[error("No such module: {0}")] + ModuleNotFound(String), + #[error("Error while compiling module {0}: {1}")] + ModuleCompilationError(String, ReidError), + #[error("No such function {0} found in module {1}")] + NoSuchFunctionInModule(String, String), + #[error("Importing function {0}::{1} not possible: {2}")] + FunctionImportIssue(String, String, EqualsIssue), + #[error("Tried linking another main module: {0}")] + TriedLinkingMain(String), + #[error("Multiple Mains at the start!")] + MultipleMainsAtStart, + #[error("No Main-module found!")] + NoMainDefined, + #[error("Main module has no main-function!")] + NoMainFunction, + #[error("Function {1} in module {0} is private!")] + FunctionIsPrivate(String, String), +} + +/// Struct used to implement a type-checking pass that can be performed on the +/// MIR. +pub struct LinkerPass; + +impl Pass for LinkerPass { + type TError = ErrorKind; + fn context(&mut self, context: &mut Context, mut state: PassState) { + let mains = context + .modules + .iter() + .filter(|m| m.is_main) + .collect::>(); + if mains.len() > 1 { + state.note_errors(&vec![ErrorKind::MultipleMainsAtStart], Metadata::default()); + return; + } + let Some(main) = mains.first() else { + state.note_errors(&vec![ErrorKind::NoMainDefined], Metadata::default()); + return; + }; + + let Some(_) = main.functions.iter().find(|f| f.name == "main") else { + state.note_errors(&vec![ErrorKind::NoMainFunction], Metadata::default()); + return; + }; + + let mut modules = HashMap::>>::new(); + + for module in context.modules.drain(..) { + modules.insert(module.name.clone(), Rc::new(RefCell::new(module))); + } + + let mut modules_to_process: Vec>> = modules.values().cloned().collect(); + + while let Some(module) = modules_to_process.pop() { + let mut importer_module = module.borrow_mut(); + + for import in importer_module.imports.clone() { + let Import(path, _) = &import; + if path.len() != 2 { + state.ok::<_, Infallible>( + Err(ErrorKind::InnerModulesNotYetSupported(import.clone())), + import.1, + ); + } + + let module_name = unsafe { path.get_unchecked(0) }; + + let mut imported = if let Some(module) = modules.get_mut(module_name) { + module + } else { + let file_path = + PathBuf::from(&context.base.clone()).join(module_name.to_owned() + ".reid"); + + let Ok(source) = fs::read_to_string(&file_path) else { + state.ok::<_, Infallible>( + Err(ErrorKind::ModuleNotFound(module_name.clone())), + import.1, + ); + continue; + }; + + match compile_module(&source, module_name.clone(), Some(file_path), false) { + Ok(imported_module) => { + if imported_module.is_main { + state.ok::<_, Infallible>( + Err(ErrorKind::TriedLinkingMain(module_name.clone())), + import.1, + ); + continue; + } + let module_name = imported_module.name.clone(); + modules.insert( + module_name.clone(), + Rc::new(RefCell::new(imported_module)), + ); + let imported = modules.get_mut(&module_name).unwrap(); + modules_to_process.push(imported.clone()); + imported + } + Err(err) => { + state.ok::<_, Infallible>( + Err(ErrorKind::ModuleCompilationError(module_name.clone(), err)), + import.1, + ); + continue; + } + } + } + .borrow_mut(); + + let func_name = unsafe { path.get_unchecked(1) }; + + let Some(func) = imported.functions.iter_mut().find(|f| f.name == *func_name) + else { + state.ok::<_, Infallible>( + Err(ErrorKind::NoSuchFunctionInModule( + module_name.clone(), + func_name.clone(), + )), + import.1, + ); + continue; + }; + + if !func.is_pub { + state.ok::<_, Infallible>( + Err(ErrorKind::FunctionIsPrivate( + module_name.clone(), + func_name.clone(), + )), + import.1, + ); + continue; + } + + func.is_imported = true; + + if let Some(existing) = importer_module + .functions + .iter() + .find(|f| f.name == *func_name) + { + if let Err(e) = existing.equals_as_imported(func) { + state.ok::<_, Infallible>( + Err(ErrorKind::FunctionImportIssue( + module_name.clone(), + func_name.clone(), + e, + )), + import.1, + ); + } + } + + importer_module.functions.push(FunctionDefinition { + name: func.name.clone(), + is_pub: false, + is_imported: false, + return_type: func.return_type.clone(), + parameters: func.parameters.clone(), + kind: super::FunctionDefinitionKind::Extern, + }); + } + } + + context.modules = modules + .into_values() + .map(|v| Rc::into_inner(v).unwrap().into_inner()) + .collect(); + } +} diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 18fb9d9..29b650e 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use crate::token_stream::TokenRange; mod display; -pub mod imports; +pub mod linker; pub mod pass; pub mod typecheck; pub mod typeinference; @@ -208,13 +208,13 @@ pub enum ReturnKind { Soft, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata); #[derive(Debug, Clone)] pub struct Import(pub Vec, pub Metadata); -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum ExprKind { Variable(NamedVariableRef), Index(Box, TypeKind, u64), @@ -226,30 +226,31 @@ pub enum ExprKind { Block(Block), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Expression(pub ExprKind, pub Metadata); /// Condition, Then, Else -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct IfExpression(pub Box, pub Block, pub Option); -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FunctionCall { pub name: String, pub return_type: TypeKind, pub parameters: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FunctionDefinition { pub name: String, pub is_pub: bool, + pub is_imported: bool, pub return_type: TypeKind, pub parameters: Vec<(String, TypeKind)>, pub kind: FunctionDefinitionKind, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum FunctionDefinitionKind { /// Actual definition block and surrounding signature range Local(Block, Metadata), @@ -272,7 +273,7 @@ impl FunctionDefinition { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Block { /// List of non-returning statements pub statements: Vec, @@ -280,22 +281,22 @@ pub struct Block { pub meta: Metadata, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Statement(pub StmtKind, pub Metadata); -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct IndexedVariableReference { pub kind: IndexedVariableReferenceKind, pub meta: Metadata, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum IndexedVariableReferenceKind { Named(NamedVariableRef), Index(Box, u64), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum StmtKind { /// Variable name++mutability+type, evaluation Let(NamedVariableRef, bool, Expression), @@ -304,12 +305,13 @@ pub enum StmtKind { Expression(Expression), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Module { pub name: String, pub imports: Vec, pub functions: Vec, pub path: Option, + pub is_main: bool, } #[derive(Debug)] diff --git a/reid/src/mir/typeinference.rs b/reid/src/mir/typeinference.rs index 685c8ae..31c0d5d 100644 --- a/reid/src/mir/typeinference.rs +++ b/reid/src/mir/typeinference.rs @@ -31,7 +31,7 @@ impl<'t> Pass for TypeInference<'t> { fn module(&mut self, module: &mut Module, mut state: PassState) { for function in &mut module.functions { - let res = function.infer_types(&self.refs, &mut state); + let res = function.infer_types(&self.refs, &mut state.inner()); state.ok(res, function.block_meta()); } } diff --git a/reid/src/mir/typerefs.rs b/reid/src/mir/typerefs.rs index 3cf63f0..13a09e5 100644 --- a/reid/src/mir/typerefs.rs +++ b/reid/src/mir/typerefs.rs @@ -28,7 +28,6 @@ impl<'scope> TypeRef<'scope> { match resolved { TypeKind::Array(elem_ty, len) => { let resolved_elem_ty = self.1.from_type(&elem_ty).unwrap().resolve_type(); - dbg!(&elem_ty, &resolved_elem_ty); TypeKind::Array(Box::new(resolved_elem_ty), len) } _ => resolved, diff --git a/reid_src/std.reid b/reid_src/std.reid index 1d8be3c..1b69685 100644 --- a/reid_src/std.reid +++ b/reid_src/std.reid @@ -5,3 +5,6 @@ pub fn print(message: string) { puts(message); } +fn main() -> u16 { + return 0; +}