Fix linking so that non-imported functions don't bother main-module
This commit is contained in:
parent
2b47c4efc7
commit
fa4df50a04
@ -6,7 +6,7 @@ fn main() {
|
|||||||
|
|
||||||
let context = Context::new();
|
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 main = module.function("main", Type::I32, Vec::new(), FunctionFlags::default());
|
||||||
let mut m_entry = main.block("entry");
|
let mut m_entry = main.block("entry");
|
||||||
|
@ -272,7 +272,6 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
Store(ptr, _) => {
|
Store(ptr, _) => {
|
||||||
if let Ok(ty) = ptr.get_type(&self) {
|
if let Ok(ty) = ptr.get_type(&self) {
|
||||||
dbg!(&ty);
|
|
||||||
if let Type::Ptr(_) = ty {
|
if let Type::Ptr(_) = ty {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces
|
//! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces
|
||||||
//! with the LLVM API.
|
//! 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::{
|
use llvm_sys::{
|
||||||
LLVMIntPredicate, LLVMLinkage,
|
LLVMIntPredicate, LLVMLinkage,
|
||||||
@ -134,13 +134,11 @@ impl Context {
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let main_module_ref = main_module.compile(&context, &self.builder);
|
let main_module_ref = main_module.compile(&context, &self.builder);
|
||||||
dbg!("main");
|
|
||||||
|
|
||||||
for holder in module_holders.borrow().iter() {
|
for holder in module_holders.borrow().iter() {
|
||||||
if holder.value == main_module.value {
|
if holder.value == main_module.value {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dbg!(holder.value);
|
|
||||||
let module_ref = holder.compile(&context, &self.builder);
|
let module_ref = holder.compile(&context, &self.builder);
|
||||||
LLVMLinkModules2(main_module_ref, module_ref);
|
LLVMLinkModules2(main_module_ref, module_ref);
|
||||||
}
|
}
|
||||||
@ -205,7 +203,7 @@ impl ModuleHolder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for function in &self.functions {
|
for function in &self.functions {
|
||||||
function.compile(&mut module);
|
function.compile(&mut module, self.data.is_main);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_ref
|
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 {
|
unsafe {
|
||||||
let own_function = *module.functions.get(&self.value).unwrap();
|
let own_function = *module.functions.get(&self.value).unwrap();
|
||||||
|
|
||||||
if self.data.flags.is_extern {
|
if self.data.flags.is_extern {
|
||||||
LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage);
|
LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage);
|
||||||
|
// Use "available internally" if the other kind of extern
|
||||||
return;
|
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);
|
LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage);
|
||||||
} else {
|
} else {
|
||||||
LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMPrivateLinkage);
|
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 {
|
for block in &self.blocks {
|
||||||
if block.data.deleted {
|
if block.data.deleted {
|
||||||
continue;
|
continue;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use std::{fmt::Debug, marker::PhantomData};
|
use std::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleHolder, ModuleValue};
|
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue};
|
||||||
use debug::PrintableModule;
|
use debug::PrintableModule;
|
||||||
|
|
||||||
pub mod builder;
|
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 {
|
let value = self.builder.add_module(ModuleData {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
|
is_main: main,
|
||||||
});
|
});
|
||||||
Module {
|
Module {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
@ -41,6 +42,7 @@ impl Context {
|
|||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct ModuleData {
|
pub struct ModuleData {
|
||||||
name: String,
|
name: String,
|
||||||
|
is_main: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Module<'ctx> {
|
pub struct Module<'ctx> {
|
||||||
@ -97,14 +99,18 @@ pub struct FunctionData {
|
|||||||
#[derive(Debug, Clone, Copy, Hash)]
|
#[derive(Debug, Clone, Copy, Hash)]
|
||||||
pub struct FunctionFlags {
|
pub struct FunctionFlags {
|
||||||
pub is_extern: bool,
|
pub is_extern: bool,
|
||||||
|
pub is_main: bool,
|
||||||
pub is_pub: bool,
|
pub is_pub: bool,
|
||||||
|
pub is_imported: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FunctionFlags {
|
impl Default for FunctionFlags {
|
||||||
fn default() -> FunctionFlags {
|
fn default() -> FunctionFlags {
|
||||||
FunctionFlags {
|
FunctionFlags {
|
||||||
is_extern: false,
|
is_extern: false,
|
||||||
|
is_main: false,
|
||||||
is_pub: false,
|
is_pub: false,
|
||||||
|
is_imported: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ fn main() {
|
|||||||
let fibonacci = FunctionDefinition {
|
let fibonacci = FunctionDefinition {
|
||||||
name: fibonacci_name.clone(),
|
name: fibonacci_name.clone(),
|
||||||
is_pub: false,
|
is_pub: false,
|
||||||
|
is_imported: false,
|
||||||
return_type: TypeKind::I32,
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
@ -131,6 +132,7 @@ fn main() {
|
|||||||
let main = FunctionDefinition {
|
let main = FunctionDefinition {
|
||||||
name: "main".to_owned(),
|
name: "main".to_owned(),
|
||||||
is_pub: false,
|
is_pub: false,
|
||||||
|
is_imported: false,
|
||||||
return_type: TypeKind::I32,
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
@ -162,6 +164,7 @@ fn main() {
|
|||||||
imports: vec![],
|
imports: vec![],
|
||||||
functions: vec![fibonacci, main],
|
functions: vec![fibonacci, main],
|
||||||
path: None,
|
path: None,
|
||||||
|
is_main: true,
|
||||||
}],
|
}],
|
||||||
base: PathBuf::new(),
|
base: PathBuf::new(),
|
||||||
};
|
};
|
||||||
|
@ -169,4 +169,5 @@ pub struct Module {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub top_level_statements: Vec<TopLevelStatement>,
|
pub top_level_statements: Vec<TopLevelStatement>,
|
||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
|
pub is_main: bool,
|
||||||
}
|
}
|
||||||
|
@ -401,9 +401,7 @@ impl Parse for VariableReference {
|
|||||||
stream.get_range().unwrap(),
|
stream.get_range().unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
dbg!(&var_ref);
|
|
||||||
while let Ok(ValueIndex(idx)) = stream.parse() {
|
while let Ok(ValueIndex(idx)) = stream.parse() {
|
||||||
dbg!(idx);
|
|
||||||
var_ref = VariableReference(
|
var_ref = VariableReference(
|
||||||
VariableReferenceKind::Index(Box::new(var_ref), idx),
|
VariableReferenceKind::Index(Box::new(var_ref), idx),
|
||||||
stream.get_range().unwrap(),
|
stream.get_range().unwrap(),
|
||||||
|
@ -26,6 +26,7 @@ impl ast::Module {
|
|||||||
let def = mir::FunctionDefinition {
|
let def = mir::FunctionDefinition {
|
||||||
name: signature.name.clone(),
|
name: signature.name.clone(),
|
||||||
is_pub: *is_pub,
|
is_pub: *is_pub,
|
||||||
|
is_imported: false,
|
||||||
return_type: signature
|
return_type: signature
|
||||||
.return_type
|
.return_type
|
||||||
.clone()
|
.clone()
|
||||||
@ -45,6 +46,7 @@ impl ast::Module {
|
|||||||
let def = mir::FunctionDefinition {
|
let def = mir::FunctionDefinition {
|
||||||
name: signature.name.clone(),
|
name: signature.name.clone(),
|
||||||
is_pub: false,
|
is_pub: false,
|
||||||
|
is_imported: false,
|
||||||
return_type: signature
|
return_type: signature
|
||||||
.return_type
|
.return_type
|
||||||
.clone()
|
.clone()
|
||||||
@ -68,6 +70,7 @@ impl ast::Module {
|
|||||||
imports,
|
imports,
|
||||||
functions,
|
functions,
|
||||||
path: self.path.clone(),
|
path: self.path.clone(),
|
||||||
|
is_main: self.is_main,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> {
|
|||||||
|
|
||||||
impl mir::Module {
|
impl mir::Module {
|
||||||
fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
|
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();
|
let mut functions = HashMap::new();
|
||||||
|
|
||||||
@ -56,13 +56,16 @@ impl mir::Module {
|
|||||||
.map(|(_, p)| p.get_type())
|
.map(|(_, p)| p.get_type())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let is_main = self.is_main && function.name == "main";
|
||||||
let func = match &function.kind {
|
let func = match &function.kind {
|
||||||
mir::FunctionDefinitionKind::Local(_, _) => module.function(
|
mir::FunctionDefinitionKind::Local(_, _) => module.function(
|
||||||
&function.name,
|
&function.name,
|
||||||
function.return_type.get_type(),
|
function.return_type.get_type(),
|
||||||
param_types,
|
param_types,
|
||||||
FunctionFlags {
|
FunctionFlags {
|
||||||
is_pub: function.is_pub,
|
is_pub: function.is_pub || is_main,
|
||||||
|
is_main,
|
||||||
|
is_imported: function.is_imported,
|
||||||
..FunctionFlags::default()
|
..FunctionFlags::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use mir::{
|
use mir::{
|
||||||
imports::ImportsPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
|
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
|
||||||
};
|
};
|
||||||
use reid_lib::Context;
|
use reid_lib::Context;
|
||||||
|
|
||||||
@ -65,12 +65,17 @@ pub enum ReidError {
|
|||||||
ParserError(#[from] token_stream::Error),
|
ParserError(#[from] token_stream::Error),
|
||||||
#[error("Errors during typecheck: {0:?}")]
|
#[error("Errors during typecheck: {0:?}")]
|
||||||
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
||||||
|
#[error("Errors during type inference: {0:?}")]
|
||||||
|
TypeInferenceErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
||||||
|
#[error("Errors during linking: {0:?}")]
|
||||||
|
LinkerErrors(Vec<mir::pass::Error<mir::linker::ErrorKind>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_module(
|
pub fn compile_module(
|
||||||
source: &str,
|
source: &str,
|
||||||
name: String,
|
name: String,
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
|
is_main: bool,
|
||||||
) -> Result<mir::Module, ReidError> {
|
) -> Result<mir::Module, ReidError> {
|
||||||
let tokens = lexer::tokenize(source)?;
|
let tokens = lexer::tokenize(source)?;
|
||||||
|
|
||||||
@ -89,6 +94,7 @@ pub fn compile_module(
|
|||||||
name,
|
name,
|
||||||
top_level_statements: statements,
|
top_level_statements: statements,
|
||||||
path,
|
path,
|
||||||
|
is_main,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ast_module.process())
|
Ok(ast_module.process())
|
||||||
@ -103,18 +109,23 @@ pub fn compile(source: &str, path: PathBuf) -> Result<String, ReidError> {
|
|||||||
let mut mir_context = mir::Context::from(
|
let mut mir_context = mir::Context::from(
|
||||||
vec![compile_module(
|
vec![compile_module(
|
||||||
source,
|
source,
|
||||||
"main".to_owned(),
|
path.file_name().unwrap().to_str().unwrap().to_owned(),
|
||||||
Some(path.clone()),
|
Some(path.clone()),
|
||||||
|
true,
|
||||||
)?],
|
)?],
|
||||||
path.parent().unwrap().to_owned(),
|
path.parent().unwrap().to_owned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("{}", &mir_context);
|
println!("{}", &mir_context);
|
||||||
|
|
||||||
let state = mir_context.pass(&mut ImportsPass);
|
let state = mir_context.pass(&mut LinkerPass);
|
||||||
dbg!(&state);
|
dbg!(&state);
|
||||||
println!("{}", &mir_context);
|
println!("{}", &mir_context);
|
||||||
|
|
||||||
|
if !state.errors.is_empty() {
|
||||||
|
return Err(ReidError::LinkerErrors(state.errors));
|
||||||
|
}
|
||||||
|
|
||||||
let refs = TypeRefs::default();
|
let refs = TypeRefs::default();
|
||||||
|
|
||||||
let state = mir_context.pass(&mut TypeInference { refs: &refs });
|
let state = mir_context.pass(&mut TypeInference { refs: &refs });
|
||||||
@ -122,6 +133,10 @@ pub fn compile(source: &str, path: PathBuf) -> Result<String, ReidError> {
|
|||||||
dbg!(&mir_context);
|
dbg!(&mir_context);
|
||||||
println!("{}", &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 });
|
let state = mir_context.pass(&mut TypeCheck { refs: &refs });
|
||||||
dbg!(&state);
|
dbg!(&state);
|
||||||
println!("{}", &mir_context);
|
println!("{}", &mir_context);
|
||||||
|
@ -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<Self::TError>) {
|
|
||||||
let mut modules = HashMap::<String, Module>::new();
|
|
||||||
|
|
||||||
for module in context.modules.clone() {
|
|
||||||
modules.insert(module.name.clone(), module);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut modules_to_process: Vec<Module> = 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();
|
|
||||||
}
|
|
||||||
}
|
|
193
reid/src/mir/linker.rs
Normal file
193
reid/src/mir/linker.rs
Normal file
@ -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<Self::TError>) {
|
||||||
|
let mains = context
|
||||||
|
.modules
|
||||||
|
.iter()
|
||||||
|
.filter(|m| m.is_main)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
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::<String, Rc<RefCell<Module>>>::new();
|
||||||
|
|
||||||
|
for module in context.modules.drain(..) {
|
||||||
|
modules.insert(module.name.clone(), Rc::new(RefCell::new(module)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut modules_to_process: Vec<Rc<RefCell<Module>>> = 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();
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ use std::path::PathBuf;
|
|||||||
use crate::token_stream::TokenRange;
|
use crate::token_stream::TokenRange;
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
pub mod imports;
|
pub mod linker;
|
||||||
pub mod pass;
|
pub mod pass;
|
||||||
pub mod typecheck;
|
pub mod typecheck;
|
||||||
pub mod typeinference;
|
pub mod typeinference;
|
||||||
@ -208,13 +208,13 @@ pub enum ReturnKind {
|
|||||||
Soft,
|
Soft,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
|
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Import(pub Vec<String>, pub Metadata);
|
pub struct Import(pub Vec<String>, pub Metadata);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum ExprKind {
|
pub enum ExprKind {
|
||||||
Variable(NamedVariableRef),
|
Variable(NamedVariableRef),
|
||||||
Index(Box<Expression>, TypeKind, u64),
|
Index(Box<Expression>, TypeKind, u64),
|
||||||
@ -226,30 +226,31 @@ pub enum ExprKind {
|
|||||||
Block(Block),
|
Block(Block),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct Expression(pub ExprKind, pub Metadata);
|
pub struct Expression(pub ExprKind, pub Metadata);
|
||||||
|
|
||||||
/// Condition, Then, Else
|
/// Condition, Then, Else
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct IfExpression(pub Box<Expression>, pub Block, pub Option<Block>);
|
pub struct IfExpression(pub Box<Expression>, pub Block, pub Option<Block>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionCall {
|
pub struct FunctionCall {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub return_type: TypeKind,
|
pub return_type: TypeKind,
|
||||||
pub parameters: Vec<Expression>,
|
pub parameters: Vec<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionDefinition {
|
pub struct FunctionDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_pub: bool,
|
pub is_pub: bool,
|
||||||
|
pub is_imported: bool,
|
||||||
pub return_type: TypeKind,
|
pub return_type: TypeKind,
|
||||||
pub parameters: Vec<(String, TypeKind)>,
|
pub parameters: Vec<(String, TypeKind)>,
|
||||||
pub kind: FunctionDefinitionKind,
|
pub kind: FunctionDefinitionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum FunctionDefinitionKind {
|
pub enum FunctionDefinitionKind {
|
||||||
/// Actual definition block and surrounding signature range
|
/// Actual definition block and surrounding signature range
|
||||||
Local(Block, Metadata),
|
Local(Block, Metadata),
|
||||||
@ -272,7 +273,7 @@ impl FunctionDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
/// List of non-returning statements
|
/// List of non-returning statements
|
||||||
pub statements: Vec<Statement>,
|
pub statements: Vec<Statement>,
|
||||||
@ -280,22 +281,22 @@ pub struct Block {
|
|||||||
pub meta: Metadata,
|
pub meta: Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct Statement(pub StmtKind, pub Metadata);
|
pub struct Statement(pub StmtKind, pub Metadata);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct IndexedVariableReference {
|
pub struct IndexedVariableReference {
|
||||||
pub kind: IndexedVariableReferenceKind,
|
pub kind: IndexedVariableReferenceKind,
|
||||||
pub meta: Metadata,
|
pub meta: Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum IndexedVariableReferenceKind {
|
pub enum IndexedVariableReferenceKind {
|
||||||
Named(NamedVariableRef),
|
Named(NamedVariableRef),
|
||||||
Index(Box<IndexedVariableReference>, u64),
|
Index(Box<IndexedVariableReference>, u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum StmtKind {
|
pub enum StmtKind {
|
||||||
/// Variable name++mutability+type, evaluation
|
/// Variable name++mutability+type, evaluation
|
||||||
Let(NamedVariableRef, bool, Expression),
|
Let(NamedVariableRef, bool, Expression),
|
||||||
@ -304,12 +305,13 @@ pub enum StmtKind {
|
|||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub imports: Vec<Import>,
|
pub imports: Vec<Import>,
|
||||||
pub functions: Vec<FunctionDefinition>,
|
pub functions: Vec<FunctionDefinition>,
|
||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
|
pub is_main: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -31,7 +31,7 @@ impl<'t> Pass for TypeInference<'t> {
|
|||||||
|
|
||||||
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
|
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
|
||||||
for function in &mut module.functions {
|
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());
|
state.ok(res, function.block_meta());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ impl<'scope> TypeRef<'scope> {
|
|||||||
match resolved {
|
match resolved {
|
||||||
TypeKind::Array(elem_ty, len) => {
|
TypeKind::Array(elem_ty, len) => {
|
||||||
let resolved_elem_ty = self.1.from_type(&elem_ty).unwrap().resolve_type();
|
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)
|
TypeKind::Array(Box::new(resolved_elem_ty), len)
|
||||||
}
|
}
|
||||||
_ => resolved,
|
_ => resolved,
|
||||||
|
@ -5,3 +5,6 @@ pub fn print(message: string) {
|
|||||||
puts(message);
|
puts(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() -> u16 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user