diff --git a/examples/triple_import_main.reid b/examples/triple_import_main.reid index a4e6613..8ac797d 100644 --- a/examples/triple_import_main.reid +++ b/examples/triple_import_main.reid @@ -1,5 +1,7 @@ -import triple_import_vec2::Vec2; import triple_import_ship::Ship; -fn main() -> i32 { return 0; } \ No newline at end of file +fn main() -> u32 { + let a = Ship::new(); + return a.position.x; +} \ No newline at end of file diff --git a/examples/triple_import_ship.reid b/examples/triple_import_ship.reid index 4f5b017..49ade80 100644 --- a/examples/triple_import_ship.reid +++ b/examples/triple_import_ship.reid @@ -1,3 +1,11 @@ import triple_import_vec2::Vec2; -struct Ship { position: Vec2 } \ No newline at end of file +struct Ship { position: Vec2 } + +impl Ship { + pub fn new() -> Ship { + Ship { + position: Vec2 {x: 15, y: 16} + } + } +} \ No newline at end of file diff --git a/examples/triple_import_vec2.reid b/examples/triple_import_vec2.reid index 5dd4a9d..8cc75b3 100644 --- a/examples/triple_import_vec2.reid +++ b/examples/triple_import_vec2.reid @@ -1,2 +1,2 @@ -struct Vec2 { x: f32, y: f32 } \ No newline at end of file +struct Vec2 { x: u32, y: u32 } \ No newline at end of file diff --git a/reid/src/mir/linker.rs b/reid/src/mir/linker.rs index bfbee92..bcf0db1 100644 --- a/reid/src/mir/linker.rs +++ b/reid/src/mir/linker.rs @@ -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 { 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>, + foreign_types: HashMap>, } type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, LinkerState, ErrorKind>; +#[derive(Clone, Debug)] +struct LinkerModule { + module: Rc>, + // Functions imported directly from a module + function_imports: HashMap, + // Types imported either directly by the user or indirectly via functions. + // May contain type-imports that are again recursively imported elsewhere. + type_imports: HashMap, +} + 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::>>::new(); + let mut modules = HashMap::::new(); let mut module_ids = HashMap::::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>> = modules.values().cloned().collect(); + let mut module_queue: Vec = modules.values().cloned().collect(); - let mut still_required_types = HashSet::::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::::new(); - let mut already_imported_types = HashSet::::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::>(); - - 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 = 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) -> 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) -> 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) -> 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, - importer_mod_id: SourceModuleId, - ) -> TypeKind { + fn update_imported(&self, foreign_types: &HashMap) -> 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 { 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, - typedefs: &Vec, -) -> Vec { - 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::>(); - - 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, +) -> Result { + 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, + seen: &mut HashSet, +) -> Result, 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) } diff --git a/reid/tests/e2e.rs b/reid/tests/e2e.rs index e69de29..7ba6c9f 100644 --- a/reid/tests/e2e.rs +++ b/reid/tests/e2e.rs @@ -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) { + 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"); +}