Fix linker working with recursive imports
This commit is contained in:
parent
1c3386bc9a
commit
1ba0de442a
@ -1,5 +1,7 @@
|
||||
|
||||
import triple_import_vec2::Vec2;
|
||||
import triple_import_ship::Ship;
|
||||
|
||||
fn main() -> i32 { return 0; }
|
||||
fn main() -> u32 {
|
||||
let a = Ship::new();
|
||||
return a.position.x;
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
|
||||
import triple_import_vec2::Vec2;
|
||||
struct Ship { position: Vec2 }
|
||||
struct Ship { position: Vec2 }
|
||||
|
||||
impl Ship {
|
||||
pub fn new() -> Ship {
|
||||
Ship {
|
||||
position: Vec2 {x: 15, y: 16}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
|
||||
struct Vec2 { x: f32, y: f32 }
|
||||
struct Vec2 { x: u32, y: u32 }
|
@ -1,8 +1,9 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
cell::{RefCell, RefMut},
|
||||
collections::{HashMap, HashSet},
|
||||
convert::Infallible,
|
||||
fs::{self},
|
||||
hash::Hash,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
};
|
||||
@ -46,6 +47,12 @@ pub enum ErrorKind {
|
||||
NoMainDefined,
|
||||
#[error("Main module has no main-function!")]
|
||||
NoMainFunction,
|
||||
#[error("Type {0} has cyclical fields!")]
|
||||
CyclicalType(String),
|
||||
#[error("Type {0} is imported cyclically!")]
|
||||
RecursiveTypeImport(String),
|
||||
#[error("Type {} does not exist in module {}", 0.0, 0.1)]
|
||||
NoSuchTypeInModule(CustomTypeKey),
|
||||
#[error("Function {1} in module {0} is private!")]
|
||||
FunctionIsPrivate(String, String),
|
||||
}
|
||||
@ -53,11 +60,13 @@ pub enum ErrorKind {
|
||||
pub fn compile_std(module_map: &mut ErrorModules) -> Result<Module, ReidError> {
|
||||
let (id, tokens) = parse_module(STD_SOURCE, STD_NAME, None, module_map, None)?;
|
||||
let module = compile_module(id, tokens, module_map, None, false)?.map_err(|(_, e)| e)?;
|
||||
dbg!(id, module.module_id);
|
||||
|
||||
let module_id = module.module_id;
|
||||
let mut mir_context = super::Context::from(vec![module], Default::default());
|
||||
|
||||
let std_compiled = mir_context.modules.remove(&module_id).unwrap();
|
||||
dbg!(std_compiled.module_id);
|
||||
Ok(std_compiled)
|
||||
}
|
||||
|
||||
@ -70,11 +79,21 @@ pub struct LinkerPass<'map> {
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct LinkerState {
|
||||
extern_imported_types: HashMap<SourceModuleId, HashMap<String, SourceModuleId>>,
|
||||
foreign_types: HashMap<SourceModuleId, HashMap<String, SourceModuleId>>,
|
||||
}
|
||||
|
||||
type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, LinkerState, ErrorKind>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct LinkerModule {
|
||||
module: Rc<RefCell<Module>>,
|
||||
// Functions imported directly from a module
|
||||
function_imports: HashMap<String, (SourceModuleId, Metadata)>,
|
||||
// Types imported either directly by the user or indirectly via functions.
|
||||
// May contain type-imports that are again recursively imported elsewhere.
|
||||
type_imports: HashMap<String, (SourceModuleId, Metadata)>,
|
||||
}
|
||||
|
||||
impl<'map> Pass for LinkerPass<'map> {
|
||||
type Data = LinkerState;
|
||||
type TError = ErrorKind;
|
||||
@ -102,40 +121,56 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut modules = HashMap::<SourceModuleId, Rc<RefCell<_>>>::new();
|
||||
let mut modules = HashMap::<SourceModuleId, LinkerModule>::new();
|
||||
let mut module_ids = HashMap::<String, SourceModuleId>::new();
|
||||
|
||||
for (mod_id, module) in context.modules.drain() {
|
||||
modules.insert(mod_id, Rc::new(RefCell::new(module)));
|
||||
modules.insert(
|
||||
mod_id,
|
||||
LinkerModule {
|
||||
module: Rc::new(RefCell::new(module)),
|
||||
function_imports: HashMap::new(),
|
||||
type_imports: HashMap::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let mut modules_to_process: Vec<Rc<RefCell<_>>> = modules.values().cloned().collect();
|
||||
let mut module_queue: Vec<LinkerModule> = modules.values().cloned().collect();
|
||||
|
||||
let mut still_required_types = HashSet::<CustomTypeKey>::new();
|
||||
while let Some(mut importer) = module_queue.pop() {
|
||||
let importer_mod = importer.module.borrow_mut();
|
||||
|
||||
while let Some(module) = modules_to_process.pop() {
|
||||
let mut extern_types = HashMap::new();
|
||||
let mut already_imported_binops = HashSet::<BinopKey>::new();
|
||||
let mut already_imported_types = HashSet::<CustomTypeKey>::new();
|
||||
let mut importer_module = module.borrow_mut();
|
||||
|
||||
for import in importer_module.imports.clone() {
|
||||
// Gp go through all imports in this specific modulee
|
||||
for import in importer_mod.imports.clone() {
|
||||
let Import(path, _) = &import;
|
||||
if path.len() != 2 {
|
||||
state.ok::<_, Infallible>(Err(ErrorKind::InnerModulesNotYetSupported(import.clone())), import.1);
|
||||
}
|
||||
|
||||
// Cut the import statement into parts
|
||||
let Some((module_name, _)) = path.get(0) else {
|
||||
continue;
|
||||
};
|
||||
let Some((import_name, _)) = path.get(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut imported = if let Some(mod_id) = module_ids.get(module_name) {
|
||||
// Actually compile or fetch the imported module
|
||||
let imported = if let Some(mod_id) = module_ids.get(module_name) {
|
||||
modules.get(mod_id).unwrap()
|
||||
} else if module_name == STD_NAME {
|
||||
let std = compile_std(&mut self.module_map)?;
|
||||
modules.insert(std.module_id, Rc::new(RefCell::new(compile_std(&mut self.module_map)?)));
|
||||
module_ids.insert(std.name, std.module_id);
|
||||
modules.get(&std.module_id).unwrap()
|
||||
let module_id = std.module_id;
|
||||
modules.insert(
|
||||
std.module_id,
|
||||
LinkerModule {
|
||||
module: Rc::new(RefCell::new(std)),
|
||||
function_imports: HashMap::new(),
|
||||
type_imports: HashMap::new(),
|
||||
},
|
||||
);
|
||||
module_ids.insert(module_name.clone(), module_id);
|
||||
modules.get(&module_id).unwrap()
|
||||
} else {
|
||||
let file_path = PathBuf::from(&context.base.clone()).join(module_name.to_owned() + ".reid");
|
||||
|
||||
@ -176,9 +211,16 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
}
|
||||
let module_id = imported_module.module_id;
|
||||
module_ids.insert(imported_module.name.clone(), imported_module.module_id);
|
||||
modules.insert(module_id, Rc::new(RefCell::new(imported_module)));
|
||||
modules.insert(
|
||||
module_id,
|
||||
LinkerModule {
|
||||
module: Rc::new(RefCell::new(imported_module)),
|
||||
function_imports: HashMap::new(),
|
||||
type_imports: HashMap::new(),
|
||||
},
|
||||
);
|
||||
let imported = modules.get_mut(&module_id).unwrap();
|
||||
modules_to_process.push(imported.clone());
|
||||
module_queue.push(imported.clone());
|
||||
imported
|
||||
}
|
||||
Err((_, err)) => {
|
||||
@ -203,70 +245,126 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
.borrow_mut();
|
||||
|
||||
let Some((import_name, _)) = path.get(1) else {
|
||||
continue;
|
||||
};
|
||||
let imported_id = imported.module_id;
|
||||
|
||||
let mut imported_types = Vec::new();
|
||||
let imported_module = imported.module.borrow();
|
||||
|
||||
if let Some(func) = imported.functions.iter_mut().find(|f| f.name == *import_name) {
|
||||
let func_name = func.name.clone();
|
||||
let func_signature = func.signature();
|
||||
if let Some(func) = imported_module.functions.iter().find(|f| f.name == *import_name) {
|
||||
// If the imported item is a function, add it to the list of imported functions
|
||||
importer
|
||||
.function_imports
|
||||
.insert(func.name.clone(), (imported_module.module_id, import.1));
|
||||
} else if let Some(ty) = imported_module.typedefs.iter().find(|t| t.name == *import_name) {
|
||||
// If the imported item is a type, add it to the list of imported types
|
||||
// imported_types.insert((CustomTypeKey(ty.name.clone(), ty.source_module), true));
|
||||
importer
|
||||
.type_imports
|
||||
.insert(ty.name.clone(), (imported_module.module_id, import.1));
|
||||
}
|
||||
}
|
||||
|
||||
if !func.is_pub {
|
||||
let module_id = importer_mod.module_id;
|
||||
drop(importer_mod);
|
||||
modules.insert(module_id, importer);
|
||||
}
|
||||
|
||||
for (_, linker_module) in &modules {
|
||||
let mut importer_module = linker_module.module.borrow_mut();
|
||||
|
||||
let mut unresolved_types = HashMap::new();
|
||||
|
||||
// 1. Import functions and find all types that are dependencies of
|
||||
// functions
|
||||
for (name, (function_source, import_meta)) in &linker_module.function_imports {
|
||||
dbg!(&name, &function_source, &modules.keys());
|
||||
let mut function_module = modules.get(&function_source).unwrap().module.borrow_mut();
|
||||
let func_module_name = function_module.name.clone();
|
||||
let func_module_id = function_module.module_id;
|
||||
|
||||
let function = function_module.functions.iter_mut().find(|f| f.name == *name).unwrap();
|
||||
|
||||
// If function is not pub, error
|
||||
if !function.is_pub {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::FunctionIsPrivate(func_module_name, function.name.clone())),
|
||||
import_meta.clone(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If function already exists, error
|
||||
if let Some(existing) = importer_module.functions.iter().find(|f| f.name == *name) {
|
||||
if let Err(e) = existing.equals_as_imported(&function) {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
|
||||
import.1,
|
||||
Err(ErrorKind::FunctionImportIssue(func_module_name, name.clone(), e)),
|
||||
import_meta.clone(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
func.is_imported = true;
|
||||
function.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,
|
||||
);
|
||||
for ty in import_type(&function.return_type) {
|
||||
unresolved_types.insert(ty, (import_meta.clone(), true));
|
||||
}
|
||||
for param in &function.parameters {
|
||||
for ty in import_type(¶m.ty) {
|
||||
unresolved_types.insert(ty, (import_meta.clone(), true));
|
||||
}
|
||||
}
|
||||
|
||||
importer_module.functions.push(FunctionDefinition {
|
||||
name: function.name.clone(),
|
||||
linkage_name: None,
|
||||
is_pub: false,
|
||||
is_imported: false,
|
||||
return_type: function.return_type.clone(),
|
||||
parameters: function.parameters.clone(),
|
||||
kind: super::FunctionDefinitionKind::Extern(true),
|
||||
source: Some(func_module_id),
|
||||
signature_meta: function.signature(),
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Add all manually imported types to the list of types that need
|
||||
// to be resolved and recursed
|
||||
for (name, (source_module, meta)) in &linker_module.type_imports {
|
||||
unresolved_types.insert(
|
||||
CustomTypeKey(name.clone(), source_module.clone()),
|
||||
(meta.clone(), false),
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Recurse these types to find their true sources, find their
|
||||
// dependencies, and list them all. Store manually imported types
|
||||
// in a separate mapping for later.
|
||||
let mut imported_types = HashSet::new();
|
||||
let mut foreign_keys = HashSet::new();
|
||||
|
||||
let mut already_imported_binops = HashSet::new();
|
||||
|
||||
for (ty, (meta, is_dependency)) in unresolved_types {
|
||||
// First deal with manually imported types
|
||||
if !is_dependency {
|
||||
// Add them to the list of foreign types (types that are
|
||||
// later replaced in-source by name)
|
||||
let imported_ty_key = match resolve_type(&ty, &modules) {
|
||||
Ok(ty) => {
|
||||
foreign_keys.insert(ty.clone());
|
||||
ty
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
state.note_errors(&vec![e], meta);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let types = import_type(&func.return_type, false);
|
||||
let return_type = func.return_type.clone();
|
||||
imported_types.extend(types);
|
||||
|
||||
let mut param_tys = Vec::new();
|
||||
for param in &func.parameters {
|
||||
let types = import_type(¶m.ty, false);
|
||||
imported_types.extend(types);
|
||||
param_tys.push(param.clone());
|
||||
}
|
||||
|
||||
importer_module.functions.push(FunctionDefinition {
|
||||
name: func_name.clone(),
|
||||
linkage_name: None,
|
||||
is_pub: false,
|
||||
is_imported: false,
|
||||
return_type,
|
||||
parameters: param_tys,
|
||||
kind: super::FunctionDefinitionKind::Extern(true),
|
||||
source: Some(imported.module_id),
|
||||
signature_meta: func_signature,
|
||||
});
|
||||
} else if let Some(ty) = imported.typedefs.iter_mut().find(|f| f.name == *import_name) {
|
||||
let external_key = CustomTypeKey(ty.name.clone(), ty.source_module);
|
||||
let imported_ty = TypeKind::CustomType(external_key.clone());
|
||||
imported_types.push((external_key, true));
|
||||
let mut imported = modules.get(&imported_ty_key.1).unwrap().module.borrow_mut();
|
||||
let imported_module_name = imported.name.clone();
|
||||
let imported_module_id = imported.module_id.clone();
|
||||
let imported_ty = TypeKind::CustomType(imported_ty_key);
|
||||
|
||||
// Add all binary operators that are defined for this type
|
||||
for binop in &mut imported.binop_defs {
|
||||
if binop.lhs.ty != imported_ty && binop.rhs.ty != imported_ty {
|
||||
continue;
|
||||
@ -297,6 +395,7 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
}
|
||||
}
|
||||
|
||||
// Import all functions that are associated with this type
|
||||
for (ty, func) in &mut imported.associated_functions {
|
||||
if *ty != imported_ty {
|
||||
continue;
|
||||
@ -306,8 +405,11 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
|
||||
if !func.is_pub {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
|
||||
import.1,
|
||||
Err(ErrorKind::FunctionIsPrivate(
|
||||
imported_module_name.clone(),
|
||||
func_name.clone(),
|
||||
)),
|
||||
meta.clone(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@ -322,26 +424,42 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
if let Err(e) = existing.equals_as_imported(func) {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::FunctionImportIssue(
|
||||
module_name.clone(),
|
||||
imported_module_name.clone(),
|
||||
func_name.clone(),
|
||||
e,
|
||||
)),
|
||||
import.1,
|
||||
meta.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let types = import_type(&func.return_type, false);
|
||||
let mut assoc_function_types = HashSet::new();
|
||||
let types = import_type(&func.return_type);
|
||||
let return_type = func.return_type.clone();
|
||||
imported_types.extend(types);
|
||||
assoc_function_types.extend(types);
|
||||
|
||||
let mut param_tys = Vec::new();
|
||||
for param in &func.parameters {
|
||||
let types = import_type(¶m.ty, false);
|
||||
imported_types.extend(types);
|
||||
let types = import_type(¶m.ty);
|
||||
assoc_function_types.extend(types);
|
||||
param_tys.push(param.clone());
|
||||
}
|
||||
|
||||
for inner_ty in assoc_function_types {
|
||||
dbg!(&inner_ty, &imported_module_id);
|
||||
if inner_ty.1 != imported_module_id {
|
||||
let resolved = match resolve_types_recursively(&inner_ty, &modules, &mut HashSet::new())
|
||||
{
|
||||
Ok(ty) => ty,
|
||||
Err(e) => {
|
||||
state.note_errors(&vec![e], meta);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
imported_types.extend(resolved);
|
||||
}
|
||||
}
|
||||
|
||||
importer_module.associated_functions.push((
|
||||
ty.clone(),
|
||||
FunctionDefinition {
|
||||
@ -352,81 +470,32 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
return_type,
|
||||
parameters: param_tys,
|
||||
kind: super::FunctionDefinitionKind::Extern(true),
|
||||
source: Some(imported_id),
|
||||
source: Some(imported_module_id),
|
||||
signature_meta: func.signature_meta,
|
||||
},
|
||||
));
|
||||
}
|
||||
} else {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::ImportDoesNotExist(module_name.clone(), import_name.clone())),
|
||||
import.1,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
let mut current_extern_types = HashSet::new();
|
||||
seen.extend(imported_types.clone().iter().map(|t| t.0.clone()));
|
||||
|
||||
for ty in still_required_types.clone() {
|
||||
if ty.1 == imported_id && !seen.contains(&ty) {
|
||||
imported_types.push((ty, false));
|
||||
let resolved = match resolve_types_recursively(&ty, &modules, &mut HashSet::new()) {
|
||||
Ok(ty) => ty,
|
||||
Err(e) => {
|
||||
state.note_errors(&vec![e], meta);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
imported_types.extend(resolved);
|
||||
}
|
||||
|
||||
current_extern_types.extend(imported_types.clone().iter().filter(|t| t.1).map(|t| t.0.clone()));
|
||||
for extern_type in ¤t_extern_types {
|
||||
extern_types.insert(extern_type.0.clone(), extern_type.1);
|
||||
}
|
||||
|
||||
let imported_mod_id = imported.module_id;
|
||||
let imported_mod_typedefs = &mut imported.typedefs;
|
||||
|
||||
for typekey in imported_types.clone() {
|
||||
let typedef = imported_mod_typedefs
|
||||
.iter()
|
||||
.find(|ty| CustomTypeKey(ty.name.clone(), ty.source_module) == typekey.0)
|
||||
.unwrap();
|
||||
let inner = find_inner_types(typedef, seen.clone(), imported_mod_typedefs);
|
||||
for ty in inner {
|
||||
if ty.1 == imported_id && imported_mod_typedefs.iter().find(|t| t.name == ty.0).is_some() {
|
||||
seen.insert(ty);
|
||||
} else {
|
||||
still_required_types.insert(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unable to import same-named type from multiple places..
|
||||
let seen = seen
|
||||
.difference(&already_imported_types)
|
||||
// 4. Import all listed types.
|
||||
for typekey in &imported_types {
|
||||
let imported_ty_module = modules.get(&typekey.1).unwrap().module.borrow();
|
||||
if let Some(mut typedef) = imported_ty_module
|
||||
.typedefs
|
||||
.iter()
|
||||
.find(|ty| CustomTypeKey(ty.name.clone(), ty.source_module) == *typekey)
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
already_imported_types.extend(seen.clone());
|
||||
|
||||
for typekey in &already_imported_types {
|
||||
if current_extern_types.contains(typekey) {
|
||||
let module_id = importer_module.module_id;
|
||||
let typedef = importer_module
|
||||
.typedefs
|
||||
.iter_mut()
|
||||
.find(|t| t.name == typekey.0 && t.source_module == typekey.1);
|
||||
if let Some(typedef) = typedef {
|
||||
typedef.importer = Some(module_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for typekey in seen.into_iter() {
|
||||
let mut typedef = imported_mod_typedefs
|
||||
.iter()
|
||||
.find(|ty| CustomTypeKey(ty.name.clone(), imported_mod_id) == typekey)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
if current_extern_types.contains(&typekey) {
|
||||
{
|
||||
if foreign_keys.contains(&typekey) {
|
||||
typedef = TypeDefinition {
|
||||
importer: Some(importer_module.module_id),
|
||||
..typedef
|
||||
@ -436,16 +505,23 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
importer_module.typedefs.push(typedef);
|
||||
}
|
||||
}
|
||||
|
||||
// Set foreign types
|
||||
let mut foreign_types = HashMap::new();
|
||||
for key in imported_types {
|
||||
foreign_types.insert(key.0.clone(), key.1);
|
||||
}
|
||||
|
||||
state
|
||||
.scope
|
||||
.data
|
||||
.extern_imported_types
|
||||
.insert(importer_module.module_id, extern_types);
|
||||
.foreign_types
|
||||
.insert(importer_module.module_id, foreign_types);
|
||||
}
|
||||
|
||||
let mut modules: Vec<Module> = modules
|
||||
.into_values()
|
||||
.map(|v| Rc::into_inner(v).unwrap().into_inner())
|
||||
.map(|v| Rc::into_inner(v.module).unwrap().into_inner())
|
||||
.collect();
|
||||
|
||||
for module in modules.drain(..) {
|
||||
@ -456,13 +532,13 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
}
|
||||
|
||||
fn module(&mut self, module: &mut Module, state: PassState<Self::Data, Self::TError>) -> PassResult {
|
||||
let extern_types = &state.scope.data.extern_imported_types.get(&module.module_id);
|
||||
if let Some(extern_types) = extern_types {
|
||||
let foreign_types = &state.scope.data.foreign_types.get(&module.module_id);
|
||||
if let Some(foreign_types) = foreign_types {
|
||||
for ty in &mut module.typedefs {
|
||||
match &mut ty.kind {
|
||||
TypeDefinitionKind::Struct(StructType(fields)) => {
|
||||
for field in fields {
|
||||
field.1 = field.1.update_imported(extern_types, module.module_id);
|
||||
field.1 = field.1.update_imported(foreign_types);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -478,11 +554,11 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
) -> PassResult {
|
||||
if matches!(function.kind, FunctionDefinitionKind::Local(_, _)) {
|
||||
let mod_id = state.scope.module_id.unwrap();
|
||||
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
|
||||
if let Some(extern_types) = extern_types {
|
||||
function.return_type = function.return_type.update_imported(*extern_types, mod_id);
|
||||
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
|
||||
if let Some(foreign_types) = foreign_types {
|
||||
function.return_type = function.return_type.update_imported(*foreign_types);
|
||||
for param in function.parameters.iter_mut() {
|
||||
param.ty = param.ty.update_imported(extern_types, mod_id);
|
||||
param.ty = param.ty.update_imported(foreign_types);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -491,11 +567,11 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
|
||||
fn stmt(&mut self, stmt: &mut super::Statement, state: PassState<Self::Data, Self::TError>) -> PassResult {
|
||||
let mod_id = state.scope.module_id.unwrap();
|
||||
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
|
||||
if let Some(extern_types) = extern_types {
|
||||
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
|
||||
if let Some(foreign_types) = foreign_types {
|
||||
match &mut stmt.0 {
|
||||
super::StmtKind::Let(var_ref, _, _) => {
|
||||
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
|
||||
var_ref.0 = var_ref.0.update_imported(foreign_types);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -505,28 +581,24 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
|
||||
fn expr(&mut self, expr: &mut super::Expression, state: PassState<Self::Data, Self::TError>) -> PassResult {
|
||||
let mod_id = state.scope.module_id.unwrap();
|
||||
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
|
||||
if let Some(extern_types) = extern_types {
|
||||
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
|
||||
if let Some(foreign_types) = foreign_types {
|
||||
match &mut expr.0 {
|
||||
super::ExprKind::Variable(var_ref) => {
|
||||
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
|
||||
var_ref.0 = var_ref.0.update_imported(foreign_types);
|
||||
}
|
||||
super::ExprKind::Indexed(.., type_kind, _) => {
|
||||
*type_kind = type_kind.update_imported(extern_types, mod_id)
|
||||
}
|
||||
super::ExprKind::Accessed(.., type_kind, _, _) => {
|
||||
*type_kind = type_kind.update_imported(extern_types, mod_id)
|
||||
}
|
||||
super::ExprKind::BinOp(.., type_kind) => *type_kind = type_kind.update_imported(extern_types, mod_id),
|
||||
super::ExprKind::Indexed(.., type_kind, _) => *type_kind = type_kind.update_imported(foreign_types),
|
||||
super::ExprKind::Accessed(.., type_kind, _, _) => *type_kind = type_kind.update_imported(foreign_types),
|
||||
super::ExprKind::BinOp(.., type_kind) => *type_kind = type_kind.update_imported(foreign_types),
|
||||
|
||||
super::ExprKind::Borrow(..) => {}
|
||||
super::ExprKind::Deref(..) => {}
|
||||
super::ExprKind::CastTo(_, type_kind) => *type_kind = type_kind.update_imported(extern_types, mod_id),
|
||||
super::ExprKind::CastTo(_, type_kind) => *type_kind = type_kind.update_imported(foreign_types),
|
||||
super::ExprKind::AssociatedFunctionCall(type_kind, _) => {
|
||||
*type_kind = type_kind.update_imported(extern_types, mod_id)
|
||||
*type_kind = type_kind.update_imported(foreign_types)
|
||||
}
|
||||
super::ExprKind::Struct(key, _) => {
|
||||
*key = if let Some(mod_id) = extern_types.get(&key.0) {
|
||||
*key = if let Some(mod_id) = foreign_types.get(&key.0) {
|
||||
CustomTypeKey(key.0.clone(), *mod_id)
|
||||
} else {
|
||||
key.clone()
|
||||
@ -540,81 +612,101 @@ impl<'map> Pass for LinkerPass<'map> {
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
fn update_imported(
|
||||
&self,
|
||||
extern_types: &HashMap<String, SourceModuleId>,
|
||||
importer_mod_id: SourceModuleId,
|
||||
) -> TypeKind {
|
||||
fn update_imported(&self, foreign_types: &HashMap<String, SourceModuleId>) -> TypeKind {
|
||||
match &self {
|
||||
TypeKind::Array(type_kind, len) => {
|
||||
TypeKind::Array(Box::new(type_kind.update_imported(extern_types, importer_mod_id)), *len)
|
||||
TypeKind::Array(Box::new(type_kind.update_imported(foreign_types)), *len)
|
||||
}
|
||||
TypeKind::CustomType(custom_type_key) => {
|
||||
if let Some(mod_id) = extern_types.get(&custom_type_key.0) {
|
||||
dbg!(foreign_types, &custom_type_key);
|
||||
if let Some(mod_id) = foreign_types.get(&custom_type_key.0) {
|
||||
TypeKind::CustomType(CustomTypeKey(custom_type_key.0.clone(), *mod_id))
|
||||
} else {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
TypeKind::Borrow(type_kind, mutable) => TypeKind::Borrow(
|
||||
Box::new(type_kind.update_imported(extern_types, importer_mod_id)),
|
||||
*mutable,
|
||||
),
|
||||
TypeKind::UserPtr(type_kind) => {
|
||||
TypeKind::UserPtr(Box::new(type_kind.update_imported(extern_types, importer_mod_id)))
|
||||
}
|
||||
TypeKind::CodegenPtr(type_kind) => {
|
||||
TypeKind::CodegenPtr(Box::new(type_kind.update_imported(extern_types, importer_mod_id)))
|
||||
TypeKind::Borrow(type_kind, mutable) => {
|
||||
TypeKind::Borrow(Box::new(type_kind.update_imported(foreign_types)), *mutable)
|
||||
}
|
||||
TypeKind::UserPtr(type_kind) => TypeKind::UserPtr(Box::new(type_kind.update_imported(foreign_types))),
|
||||
TypeKind::CodegenPtr(type_kind) => TypeKind::CodegenPtr(Box::new(type_kind.update_imported(foreign_types))),
|
||||
_ => self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn import_type(ty: &TypeKind, usable_import: bool) -> Vec<(CustomTypeKey, bool)> {
|
||||
fn import_type(ty: &TypeKind) -> Vec<CustomTypeKey> {
|
||||
let mut imported_types = Vec::new();
|
||||
match &ty {
|
||||
TypeKind::CustomType(key) => imported_types.push((key.clone(), usable_import)),
|
||||
TypeKind::Borrow(ty, _) => imported_types.extend(import_type(ty, usable_import)),
|
||||
TypeKind::Array(ty, _) => imported_types.extend(import_type(ty, usable_import)),
|
||||
TypeKind::UserPtr(ty) => imported_types.extend(import_type(ty, usable_import)),
|
||||
TypeKind::CodegenPtr(ty) => imported_types.extend(import_type(ty, usable_import)),
|
||||
TypeKind::CustomType(key) => imported_types.push(key.clone()),
|
||||
TypeKind::Borrow(ty, _) => imported_types.extend(import_type(ty)),
|
||||
TypeKind::Array(ty, _) => imported_types.extend(import_type(ty)),
|
||||
TypeKind::UserPtr(ty) => imported_types.extend(import_type(ty)),
|
||||
TypeKind::CodegenPtr(ty) => imported_types.extend(import_type(ty)),
|
||||
_ => {}
|
||||
};
|
||||
imported_types
|
||||
}
|
||||
|
||||
fn find_inner_types(
|
||||
typedef: &TypeDefinition,
|
||||
mut seen: HashSet<CustomTypeKey>,
|
||||
typedefs: &Vec<TypeDefinition>,
|
||||
) -> Vec<CustomTypeKey> {
|
||||
match &typedef.kind {
|
||||
crate::mir::TypeDefinitionKind::Struct(struct_type) => {
|
||||
let typekeys = struct_type
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|t| match &t.1 {
|
||||
TypeKind::CustomType(key) => Some(key),
|
||||
_ => None,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for typekey in typekeys {
|
||||
if seen.contains(&typekey) {
|
||||
continue;
|
||||
}
|
||||
seen.insert(typekey.clone());
|
||||
if typekey.1 == typedef.source_module {
|
||||
if let Some(inner) = typedefs.iter().find(|t| t.name == typekey.0) {
|
||||
let ret = find_inner_types(inner, seen.clone(), typedefs);
|
||||
seen.extend(ret);
|
||||
}
|
||||
}
|
||||
fn resolve_type(
|
||||
ty: &CustomTypeKey,
|
||||
modules: &HashMap<SourceModuleId, LinkerModule>,
|
||||
) -> Result<CustomTypeKey, ErrorKind> {
|
||||
let mut source_module_id = ty.1;
|
||||
let mut seen = HashSet::new();
|
||||
loop {
|
||||
seen.insert(source_module_id);
|
||||
let source_module = modules.get(&source_module_id).unwrap();
|
||||
if let Some((new_module_id, _)) = source_module.type_imports.get(&ty.0) {
|
||||
if seen.contains(new_module_id) {
|
||||
return Err(ErrorKind::RecursiveTypeImport(ty.0.clone()));
|
||||
}
|
||||
|
||||
seen.into_iter().collect()
|
||||
source_module_id = *new_module_id;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CustomTypeKey(ty.0.clone(), source_module_id))
|
||||
}
|
||||
|
||||
fn resolve_types_recursively(
|
||||
ty: &CustomTypeKey,
|
||||
modules: &HashMap<SourceModuleId, LinkerModule>,
|
||||
seen: &mut HashSet<CustomTypeKey>,
|
||||
) -> Result<Vec<CustomTypeKey>, ErrorKind> {
|
||||
let resolved_ty = resolve_type(ty, modules)?;
|
||||
|
||||
if seen.contains(&resolved_ty) {
|
||||
return Err(ErrorKind::CyclicalType(ty.0.clone()));
|
||||
}
|
||||
let mut types = Vec::new();
|
||||
|
||||
types.push(resolved_ty.clone());
|
||||
seen.insert(resolved_ty.clone());
|
||||
|
||||
let resolved = modules
|
||||
.get(&resolved_ty.1)
|
||||
.unwrap()
|
||||
.module
|
||||
.borrow()
|
||||
.typedefs
|
||||
.iter()
|
||||
.find(|t| t.name == resolved_ty.0)
|
||||
.ok_or(ErrorKind::NoSuchTypeInModule(ty.clone()))
|
||||
.cloned()?;
|
||||
match resolved.kind {
|
||||
TypeDefinitionKind::Struct(StructType(fields)) => {
|
||||
for field in fields {
|
||||
match &field.1 {
|
||||
TypeKind::CustomType(ty_key) => {
|
||||
types.extend(resolve_types_recursively(ty_key, modules, seen)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(types)
|
||||
}
|
||||
|
@ -0,0 +1,171 @@
|
||||
use std::{path::PathBuf, process::Command, time::SystemTime};
|
||||
|
||||
use reid::{
|
||||
compile_module,
|
||||
ld::LDRunner,
|
||||
mir::{self},
|
||||
parse_module, perform_all_passes,
|
||||
};
|
||||
use reid_lib::{compile::CompileOutput, Context};
|
||||
use util::assert_err;
|
||||
|
||||
mod util;
|
||||
|
||||
fn test_compile(source: &str, name: &str) -> CompileOutput {
|
||||
assert_err(assert_err(std::panic::catch_unwind(|| {
|
||||
let mut map = Default::default();
|
||||
let (id, tokens) = assert_err(parse_module(source, name, None, &mut map, None));
|
||||
|
||||
let module = assert_err(assert_err(compile_module(id, tokens, &mut map, None, true)).map_err(|(_, e)| e));
|
||||
let mut mir_context = mir::Context::from(vec![module], Default::default());
|
||||
assert_err(perform_all_passes(&mut mir_context, &mut map));
|
||||
|
||||
let context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
|
||||
|
||||
let codegen = assert_err(mir_context.codegen(&context));
|
||||
|
||||
Ok::<_, ()>(codegen.compile(None, Vec::new()).output())
|
||||
})))
|
||||
}
|
||||
|
||||
fn test(source: &str, name: &str, expected_exit_code: Option<i32>) {
|
||||
assert_err(assert_err(std::panic::catch_unwind(|| {
|
||||
let output = test_compile(source, name);
|
||||
|
||||
let time = SystemTime::now();
|
||||
let in_path = PathBuf::from(format!(
|
||||
"/tmp/temp-{}.o",
|
||||
time.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos()
|
||||
));
|
||||
|
||||
std::fs::write(&in_path, &output.obj_buffer).expect("Could not write OBJ-file!");
|
||||
|
||||
let out_path = in_path.with_extension("out");
|
||||
LDRunner::from_command("ld")
|
||||
.with_library("c")
|
||||
.invoke(&in_path, &out_path);
|
||||
std::fs::remove_file(in_path).unwrap();
|
||||
|
||||
let executed = Command::new(&out_path).output();
|
||||
std::fs::remove_file(out_path).unwrap();
|
||||
|
||||
if let Some(expected_exit_code) = expected_exit_code {
|
||||
assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap());
|
||||
}
|
||||
|
||||
Ok::<(), ()>(())
|
||||
})))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arithmetic_compiles_well() {
|
||||
test(include_str!("../../examples/arithmetic.reid"), "test", Some(48));
|
||||
}
|
||||
#[test]
|
||||
fn array_structs_compiles_well() {
|
||||
test(include_str!("../../examples/array_structs.reid"), "test", Some(5));
|
||||
}
|
||||
#[test]
|
||||
fn array_compiles_well() {
|
||||
test(include_str!("../../examples/array.reid"), "test", Some(3));
|
||||
}
|
||||
#[test]
|
||||
fn borrow_compiles_well() {
|
||||
test(include_str!("../../examples/borrow.reid"), "test", Some(17));
|
||||
}
|
||||
#[test]
|
||||
fn borrow_hard_compiles_well() {
|
||||
test(include_str!("../../examples/borrow_hard.reid"), "test", Some(17));
|
||||
}
|
||||
#[test]
|
||||
fn cast_compiles_well() {
|
||||
test(include_str!("../../examples/cast.reid"), "test", Some(6));
|
||||
}
|
||||
#[test]
|
||||
fn char_compiles_well() {
|
||||
test(include_str!("../../examples/char.reid"), "test", Some(98));
|
||||
}
|
||||
#[test]
|
||||
fn div_mod_compiles_well() {
|
||||
test(include_str!("../../examples/div_mod.reid"), "test", Some(12));
|
||||
}
|
||||
#[test]
|
||||
fn fibonacci_compiles_well() {
|
||||
test(include_str!("../../examples/fibonacci.reid"), "test", Some(1));
|
||||
}
|
||||
#[test]
|
||||
fn float_compiles_well() {
|
||||
test(include_str!("../../examples/float.reid"), "test", Some(1));
|
||||
}
|
||||
#[test]
|
||||
fn hello_world_compiles_well() {
|
||||
test(include_str!("../../examples/hello_world.reid"), "test", None);
|
||||
}
|
||||
#[test]
|
||||
fn hello_world_harder_compiles_well() {
|
||||
test(include_str!("../../examples/hello_world_harder.reid"), "test", None);
|
||||
}
|
||||
#[test]
|
||||
fn mutable_compiles_well() {
|
||||
test(include_str!("../../examples/mutable.reid"), "test", Some(21));
|
||||
}
|
||||
#[test]
|
||||
fn ptr_compiles_well() {
|
||||
test(include_str!("../../examples/ptr.reid"), "test", Some(5));
|
||||
}
|
||||
#[test]
|
||||
fn std_test_compiles_well() {
|
||||
test(include_str!("../../examples/std_test.reid"), "test", Some(3));
|
||||
}
|
||||
#[test]
|
||||
fn strings_compiles_well() {
|
||||
test(include_str!("../../examples/strings.reid"), "test", Some(5));
|
||||
}
|
||||
#[test]
|
||||
fn struct_compiles_well() {
|
||||
test(include_str!("../../examples/struct.reid"), "test", Some(17));
|
||||
}
|
||||
#[test]
|
||||
fn loops_compiles_well() {
|
||||
test(include_str!("../../examples/loops.reid"), "test", Some(10));
|
||||
}
|
||||
#[test]
|
||||
fn ptr_hard_compiles_well() {
|
||||
test(include_str!("../../examples/ptr_hard.reid"), "test", Some(0));
|
||||
}
|
||||
#[test]
|
||||
fn loop_hard_compiles_well() {
|
||||
test(include_str!("../../examples/loop_hard.reid"), "test", Some(0));
|
||||
}
|
||||
#[test]
|
||||
fn custom_binop_compiles_well() {
|
||||
test(include_str!("../../examples/custom_binop.reid"), "test", Some(21));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_short_compiles_well() {
|
||||
test(include_str!("../../examples/array_short.reid"), "test", Some(5));
|
||||
}
|
||||
#[test]
|
||||
fn imported_type_compiles_well() {
|
||||
test(include_str!("../../examples/imported_type.reid"), "test", Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_functions() {
|
||||
test(
|
||||
include_str!("../../examples/associated_functions.reid"),
|
||||
"test",
|
||||
Some(4),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutable_inner_functions() {
|
||||
test(include_str!("../../examples/mutable_inner.reid"), "test", Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cpu_raytracer_compiles() {
|
||||
test_compile(include_str!("../../examples/cpu_raytracer.reid"), "test");
|
||||
}
|
Loading…
Reference in New Issue
Block a user