diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs index 8c46a4b463b26fda3a4ea794a9221b1aee169996..4e566cdc23d634459b11530513dee8b50d57f704 100644 --- a/src/protocol/ast_printer.rs +++ b/src/protocol/ast_printer.rs @@ -919,7 +919,9 @@ fn write_concrete_type(target: &mut String, heap: &Heap, def_id: DefinitionId, t idx = write_concrete_part(target, heap, def_id, t, idx + 1); } target.push('>'); - } + }, + CTP::Function(_, _) => todo!("AST printer for ConcreteTypePart::Function"), + CTP::Component(_, _) => todo!("AST printer for ConcreteTypePart::Component"), } idx + 1 diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index e300c088734e6e4231b82efa89b05ec0a25d4777..e23b69169d47007e1d58c8c94af6f6786a6a0822 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -211,21 +211,25 @@ impl Parser { for module_idx in 0..self.modules.len() { let mut ctx = visitor::Ctx{ heap: &mut self.heap, - module: &mut self.modules[module_idx], + modules: &mut self.modules, + module_idx, symbols: &mut self.symbol_table, types: &mut self.type_table, + arch: &self.arch, }; self.pass_validation.visit_module(&mut ctx)?; } // Perform typechecking on all modules let mut queue = ResolveQueue::new(); - for module in &mut self.modules { + for module_idx in 0..self.modules.len() { let mut ctx = visitor::Ctx{ heap: &mut self.heap, - module, + modules: &mut self.modules, + module_idx, symbols: &mut self.symbol_table, types: &mut self.type_table, + arch: &self.arch, }; PassTyping::queue_module_definitions(&mut ctx, &mut queue); }; @@ -233,9 +237,11 @@ impl Parser { let top = queue.pop().unwrap(); let mut ctx = visitor::Ctx{ heap: &mut self.heap, - module: &mut self.modules[top.root_id.index as usize], + modules: &mut self.modules, + module_idx: top.root_id.index as usize, symbols: &mut self.symbol_table, types: &mut self.type_table, + arch: &self.arch, }; self.pass_typing.handle_module_definition(&mut ctx, &mut queue, top)?; } diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index a561b8a79846a4b5a9262bb1e30d3a2fd1372b9c..ae7deef1ad37d8cf8df685096040dbdb22e4678c 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -559,14 +559,15 @@ impl InferenceType { /// Performs the conversion of the inference type into a concrete type. /// By calling this function you must make sure that no unspecified types - /// (e.g. Unknown or IntegerLike) exist in the type. + /// (e.g. Unknown or IntegerLike) exist in the type. Will not clear or check + /// if the supplied `ConcreteType` is empty, will simply append to the parts + /// vector. fn write_concrete_type(&self, concrete_type: &mut ConcreteType) { use InferenceTypePart as ITP; use ConcreteTypePart as CTP; // Make sure inference type is specified but concrete type is not yet specified debug_assert!(!self.parts.is_empty()); - debug_assert!(concrete_type.parts.is_empty()); concrete_type.parts.reserve(self.parts.len()); let mut idx = 0; @@ -807,22 +808,14 @@ impl DefinitionType { } pub(crate) struct ResolveQueueElement { + // Note that using the `definition_id` and the `monomorph_idx` one may + // query the type table for the full procedure type, thereby retrieving + // the polymorphic arguments to the procedure. pub(crate) root_id: RootId, pub(crate) definition_id: DefinitionId, - pub(crate) monomorph_types: Vec, pub(crate) reserved_monomorph_idx: i32, } -impl PartialEq for ResolveQueueElement { - fn eq(&self, other: &Self) -> bool { - return - self.root_id == other.root_id && - self.definition_id == other.definition_id && - self.monomorph_types == other.monomorph_types; - } -} -impl Eq for ResolveQueueElement {} - pub(crate) type ResolveQueue = Vec; #[derive(Clone)] @@ -927,24 +920,36 @@ impl PassTyping { // TODO: @cleanup Unsure about this, maybe a pattern will arise after // a while. pub(crate) fn queue_module_definitions(ctx: &mut Ctx, queue: &mut ResolveQueue) { - debug_assert_eq!(ctx.module.phase, ModuleCompilationPhase::ValidatedAndLinked); - let root_id = ctx.module.root_id; + debug_assert_eq!(ctx.module().phase, ModuleCompilationPhase::ValidatedAndLinked); + let root_id = ctx.module().root_id; let root = &ctx.heap.protocol_descriptions[root_id]; for definition_id in &root.definitions { let definition = &ctx.heap[*definition_id]; - let should_add_to_queue = match definition { - Definition::Function(definition) => definition.poly_vars.is_empty(), - Definition::Component(definition) => definition.poly_vars.is_empty(), - Definition::Enum(_) | Definition::Struct(_) | Definition::Union(_) => false, + let first_concrete_part = match definition { + Definition::Function(definition) => { + if definition.poly_vars.is_empty() { + Some(ConcreteTypePart::Function(*definition_id, 0)) + } else { + None + } + } + Definition::Component(definition) => { + if definition.poly_vars.is_empty() { + Some(ConcreteTypePart::Component(*definition_id, 0)) + } else { + None + } + }, + Definition::Enum(_) | Definition::Struct(_) | Definition::Union(_) => None, }; - if should_add_to_queue { - let reserved_idx = ctx.types.reserve_procedure_monomorph_index(definition_id, None); + if let Some(first_concrete_part) = first_concrete_part { + let concrete_type = ConcreteType{ parts: vec![first_concrete_part] }; + let reserved_idx = ctx.types.reserve_procedure_monomorph_index(definition_id, concrete_type); queue.push(ResolveQueueElement{ root_id, definition_id: *definition_id, - monomorph_types: Vec::new(), reserved_monomorph_idx: reserved_idx, }) } @@ -954,15 +959,26 @@ impl PassTyping { pub(crate) fn handle_module_definition( &mut self, ctx: &mut Ctx, queue: &mut ResolveQueue, element: ResolveQueueElement ) -> VisitorResult { - // Visit the definition - debug_assert_eq!(ctx.module.root_id, element.root_id); self.reset(); + debug_assert_eq!(ctx.module().root_id, element.root_id); debug_assert!(self.poly_vars.is_empty()); + + // Prepare for visiting the definition self.reserved_idx = element.reserved_monomorph_idx; - self.poly_vars = element.monomorph_types; - self.visit_definition(ctx, element.definition_id)?; - // Keep resolving types + let proc_base = ctx.types.get_base_definition(&element.definition_id).unwrap(); + if proc_base.is_polymorph { + let proc_monos = proc_base.definition.procedure_monomorphs(); + let proc_mono = &(*proc_monos)[element.reserved_monomorph_idx as usize]; + + for poly_arg in proc_mono.concrete_type.embedded_iter(0) { + self.poly_vars.push(ConcreteType{ parts: Vec::from(poly_arg) }); + } + } + + // Visit the definition, setting up the type resolving process, then + // (attempt to) resolve all types + self.visit_definition(ctx, element.definition_id)?; self.resolve_types(ctx, queue)?; Ok(()) } @@ -1394,17 +1410,17 @@ impl PassTyping { fn inference_type_to_concrete_type( ctx: &Ctx, expr_id: ExpressionId, inference: &Vec, first_concrete_part: ConcreteTypePart, - ) -> Result<(ConcreteType, Vec), ParseError> { + ) -> Result { // Prepare storage vector let mut num_inference_parts = 0; for inference_type in inference { num_inference_parts += inference_type.parts.len(); } - let mut concrete_full_parts = Vec::with_capacity(1 + num_inference_parts); - concrete_full_parts.push(first_concrete_part); - - let mut concrete_poly_args = Vec::with_capacity(inference.len()); + let mut concrete_type = ConcreteType{ + parts: Vec::with_capacity(1 + num_inference_parts), + }; + concrete_type.parts.push(first_concrete_part); // Go through all polymorphic arguments and add them to the concrete // types. @@ -1423,21 +1439,17 @@ impl PassTyping { }; let poly_vars = ctx.heap[definition].poly_vars(); return Err(ParseError::new_error_at_span( - &ctx.module.source, expr.operation_span(), format!( + &ctx.module().source, expr.operation_span(), format!( "could not fully infer the type of polymorphic variable '{}' of this expression (got '{}')", poly_vars[poly_idx].value.as_str(), poly_type.display_name(&ctx.heap) ) )); } - let mut concrete_type = ConcreteType::default(); poly_type.write_concrete_type(&mut concrete_type); - - concrete_full_parts.extend_from_slice(&concrete_type.parts); - concrete_poly_args.push(concrete_type); } - Ok((ConcreteType{ parts: concrete_full_parts }, concrete_poly_args)) + Ok(concrete_type) } // Inference is now done. But we may still have uninferred types. So we @@ -1452,7 +1464,7 @@ impl PassTyping { } else { let expr = &ctx.heap[infer_expr.expr_id]; return Err(ParseError::new_error_at_span( - &ctx.module.source, expr.full_span(), format!( + &ctx.module().source, expr.full_span(), format!( "could not fully infer the type of this expression (got '{}')", expr_type.display_name(&ctx.heap) ) @@ -1485,23 +1497,22 @@ impl PassTyping { }; let definition_id = expr.definition; - let (concrete_type, poly_types) = inference_type_to_concrete_type( + let concrete_type = inference_type_to_concrete_type( ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part )?; - match ctx.types.get_procedure_monomorph_index(&definition_id, &poly_types) { + match ctx.types.get_procedure_monomorph_index(&definition_id, &concrete_type) { Some(reserved_idx) => { // Already typechecked, or already put into the resolve queue infer_expr.field_or_monomorph_idx = reserved_idx; }, None => { // Not typechecked yet, so add an entry in the queue - let reserved_idx = ctx.types.reserve_procedure_monomorph_index(&definition_id, Some(poly_types.clone())); + let reserved_idx = ctx.types.reserve_procedure_monomorph_index(&definition_id, concrete_type); infer_expr.field_or_monomorph_idx = reserved_idx; queue.push(ResolveQueueElement{ root_id: ctx.heap[definition_id].defined_in(), definition_id, - monomorph_types: poly_types, reserved_monomorph_idx: reserved_idx, }); } @@ -1515,10 +1526,10 @@ impl PassTyping { _ => unreachable!(), }; let first_concrete_part = ConcreteTypePart::Instance(definition_id, extra_data.poly_vars.len() as u32); - let (concrete_type, poly_types) = inference_type_to_concrete_type( + let concrete_type = inference_type_to_concrete_type( ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part )?; - let mono_index = ctx.types.add_data_monomorph(modules, ctx, &definition_id, concrete_type)?; + let mono_index = ctx.types.add_data_monomorph(ctx.modules, ctx.heap, ctx.arch, definition_id, concrete_type)?; infer_expr.field_or_monomorph_idx = mono_index; }, Expression::Select(_) => { @@ -1545,7 +1556,6 @@ impl PassTyping { }; let target = ctx.types.get_procedure_expression_data_mut(&definition_id, self.reserved_idx); - debug_assert!(target.poly_args == self.poly_vars); debug_assert!(target.expr_data.is_empty()); // makes sure we never queue something twice target.expr_data.reserve(self.expr_types.len()); @@ -2018,7 +2028,7 @@ impl PassTyping { struct_def } else { return Err(ParseError::new_error_at_span( - &ctx.module.source, select_expr.field_name.span, format!( + &ctx.module().source, select_expr.field_name.span, format!( "Can only apply field access to structs, got a subject of type '{}'", subject_type.display_name(&ctx.heap) ) @@ -2040,7 +2050,7 @@ impl PassTyping { if struct_def_id.is_none() { let ast_struct_def = ctx.heap[type_def.ast_definition].as_struct(); return Err(ParseError::new_error_at_span( - &ctx.module.source, select_expr.field_name.span, format!( + &ctx.module().source, select_expr.field_name.span, format!( "this field does not exist on the struct '{}'", ast_struct_def.identifier.value.as_str() ) @@ -2058,7 +2068,7 @@ impl PassTyping { }, Err(()) => { return Err(ParseError::new_error_at_span( - &ctx.module.source, select_expr.field_name.span, format!( + &ctx.module().source, select_expr.field_name.span, format!( "Can only apply field access to structs, got a subject of type '{}'", subject_type.display_name(&ctx.heap) ) @@ -2484,9 +2494,9 @@ impl PassTyping { let cast_expr = &ctx.heap[id]; let subject_expr = &ctx.heap[cast_expr.subject]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, cast_expr.full_span, "invalid casting operation" + &ctx.module().source, cast_expr.full_span, "invalid casting operation" ).with_info_at_span( - &ctx.module.source, subject_expr.full_span(), format!( + &ctx.module().source, subject_expr.full_span(), format!( "cannot cast the argument type '{}' to the cast type '{}'", subject_type.display_name(&ctx.heap), expr_type.display_name(&ctx.heap) @@ -2634,12 +2644,12 @@ impl PassTyping { if infer_res == DualInferenceResult::Incompatible { let var_decl = &ctx.heap[var_id]; return Err(ParseError::new_error_at_span( - &ctx.module.source, var_decl.identifier.span, format!( + &ctx.module().source, var_decl.identifier.span, format!( "Conflicting types for this variable, previously assigned the type '{}'", var_data.var_type.display_name(&ctx.heap) ) ).with_info_at_span( - &ctx.module.source, var_expr.identifier.span, format!( + &ctx.module().source, var_expr.identifier.span, format!( "But inferred to have incompatible type '{}' here", expr_type.display_name(&ctx.heap) ) @@ -2689,12 +2699,12 @@ impl PassTyping { let link_decl = &ctx.heap[linked_id]; return Err(ParseError::new_error_at_span( - &ctx.module.source, var_decl.identifier.span, format!( + &ctx.module().source, var_decl.identifier.span, format!( "Conflicting types for this variable, assigned the type '{}'", var_data.var_type.display_name(&ctx.heap) ) ).with_info_at_span( - &ctx.module.source, link_decl.identifier.span, format!( + &ctx.module().source, link_decl.identifier.span, format!( "Because it is incompatible with this variable, assigned the type '{}'", link_data.var_type.display_name(&ctx.heap) ) @@ -2853,10 +2863,10 @@ impl PassTyping { ) }; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, outer_span, + &ctx.module().source, outer_span, "failed to fully resolve the types of this expression" ).with_info_at_span( - &ctx.module.source, span, format!( + &ctx.module().source, span, format!( "because the {} signature has been resolved to '{}', but the expression has been resolved to '{}'", span_name, signature_display_type, expression_display_type ) @@ -3567,12 +3577,12 @@ impl PassTyping { let arg_type = &self.expr_types[arg_expr_idx as usize].expr_type; return ParseError::new_error_at_span( - &ctx.module.source, expr.operation_span(), format!( + &ctx.module().source, expr.operation_span(), format!( "incompatible types: this expression expected a '{}'", expr_type.display_name(&ctx.heap) ) ).with_info_at_span( - &ctx.module.source, arg_expr.full_span(), format!( + &ctx.module().source, arg_expr.full_span(), format!( "but this expression yields a '{}'", arg_type.display_name(&ctx.heap) ) @@ -3593,15 +3603,15 @@ impl PassTyping { let arg2_type = &self.expr_types[arg2_idx as usize].expr_type; return ParseError::new_error_str_at_span( - &ctx.module.source, expr.operation_span(), + &ctx.module().source, expr.operation_span(), "incompatible types: cannot apply this expression" ).with_info_at_span( - &ctx.module.source, arg1.full_span(), format!( + &ctx.module().source, arg1.full_span(), format!( "Because this expression has type '{}'", arg1_type.display_name(&ctx.heap) ) ).with_info_at_span( - &ctx.module.source, arg2.full_span(), format!( + &ctx.module().source, arg2.full_span(), format!( "But this expression has type '{}'", arg2_type.display_name(&ctx.heap) ) @@ -3616,7 +3626,7 @@ impl PassTyping { let expr_type = &self.expr_types[expr_idx as usize].expr_type; return ParseError::new_error_at_span( - &ctx.module.source, expr.full_span(), format!( + &ctx.module().source, expr.full_span(), format!( "incompatible types: got a '{}' but expected a '{}'", expr_type.display_name(&ctx.heap), InferenceType::partial_display_name(&ctx.heap, template) @@ -3692,7 +3702,7 @@ impl PassTyping { Expression::Call(expr) => { let (poly_var, func_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id); return ParseError::new_error_at_span( - &ctx.module.source, expr.func_span, format!( + &ctx.module().source, expr.func_span, format!( "Conflicting type for polymorphic variable '{}' of '{}'", poly_var, func_name ) @@ -3701,7 +3711,7 @@ impl PassTyping { Expression::Literal(expr) => { let (poly_var, type_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id); return ParseError::new_error_at_span( - &ctx.module.source, expr.span, format!( + &ctx.module().source, expr.span, format!( "Conflicting type for polymorphic variable '{}' of instantiation of '{}'", poly_var, type_name ) @@ -3710,7 +3720,7 @@ impl PassTyping { Expression::Select(expr) => { let (poly_var, struct_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id); return ParseError::new_error_at_span( - &ctx.module.source, expr.full_span, format!( + &ctx.module().source, expr.full_span, format!( "Conflicting type for polymorphic variable '{}' while accessing field '{}' of '{}'", poly_var, expr.field_name.value.as_str(), struct_name ) @@ -3756,7 +3766,7 @@ impl PassTyping { ) { return construct_main_error(ctx, poly_data, poly_idx, expr) .with_info_at_span( - &ctx.module.source, expr.full_span(), format!( + &ctx.module().source, expr.full_span(), format!( "The {} inferred the conflicting types '{}' and '{}'", expr_return_name, InferenceType::partial_display_name(&ctx.heap, section_a), @@ -3778,7 +3788,7 @@ impl PassTyping { // Same argument let arg = &ctx.heap[expr_args[arg_a_idx]]; return error.with_info_at_span( - &ctx.module.source, arg.full_span(), format!( + &ctx.module().source, arg.full_span(), format!( "This argument inferred the conflicting types '{}' and '{}'", InferenceType::partial_display_name(&ctx.heap, section_a), InferenceType::partial_display_name(&ctx.heap, section_b) @@ -3788,12 +3798,12 @@ impl PassTyping { let arg_a = &ctx.heap[expr_args[arg_a_idx]]; let arg_b = &ctx.heap[expr_args[arg_b_idx]]; return error.with_info_at_span( - &ctx.module.source, arg_a.full_span(), format!( + &ctx.module().source, arg_a.full_span(), format!( "This argument inferred it to '{}'", InferenceType::partial_display_name(&ctx.heap, section_a) ) ).with_info_at_span( - &ctx.module.source, arg_b.full_span(), format!( + &ctx.module().source, arg_b.full_span(), format!( "While this argument inferred it to '{}'", InferenceType::partial_display_name(&ctx.heap, section_b) ) @@ -3807,13 +3817,13 @@ impl PassTyping { let arg = &ctx.heap[expr_args[arg_a_idx]]; return construct_main_error(ctx, poly_data, poly_idx, expr) .with_info_at_span( - &ctx.module.source, arg.full_span(), format!( + &ctx.module().source, arg.full_span(), format!( "This argument inferred it to '{}'", InferenceType::partial_display_name(&ctx.heap, section_arg) ) ) .with_info_at_span( - &ctx.module.source, expr.full_span(), format!( + &ctx.module().source, expr.full_span(), format!( "While the {} inferred it to '{}'", expr_return_name, InferenceType::partial_display_name(&ctx.heap, section_ret) @@ -3829,7 +3839,7 @@ impl PassTyping { let arg = &ctx.heap[expr_args[arg_idx]]; return construct_main_error(ctx, poly_data, poly_idx, expr) .with_info_at_span( - &ctx.module.source, arg.full_span(), format!( + &ctx.module().source, arg.full_span(), format!( "The polymorphic variable has type '{}' (which might have been partially inferred) while the argument inferred it to '{}'", InferenceType::partial_display_name(&ctx.heap, poly_section), InferenceType::partial_display_name(&ctx.heap, arg_section) @@ -3841,7 +3851,7 @@ impl PassTyping { if let Some((poly_idx, poly_section, ret_section)) = has_explicit_poly_mismatch(&poly_data.poly_vars, &poly_data.returned) { return construct_main_error(ctx, poly_data, poly_idx, expr) .with_info_at_span( - &ctx.module.source, expr.full_span(), format!( + &ctx.module().source, expr.full_span(), format!( "The polymorphic variable has type '{}' (which might have been partially inferred) while the {} inferred it to '{}'", InferenceType::partial_display_name(&ctx.heap, poly_section), expr_return_name, diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 57c1e2b94ccad88bb44718d92541dd09f09d89ae..dca88025f2fd36f261e77a8f9e67e650ac8d4023 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -132,9 +132,9 @@ macro_rules! assign_and_replace_next_stmt { impl Visitor for PassValidationLinking { fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { - debug_assert_eq!(ctx.module.phase, ModuleCompilationPhase::TypesAddedToTable); + debug_assert_eq!(ctx.module().phase, ModuleCompilationPhase::TypesAddedToTable); - let root = &ctx.heap[ctx.module.root_id]; + let root = &ctx.heap[ctx.module().root_id]; let section = self.definition_buffer.start_section_initialized(&root.definitions); for definition_idx in 0..section.len() { let definition_id = section[definition_idx]; @@ -142,7 +142,7 @@ impl Visitor for PassValidationLinking { } section.forget(); - ctx.module.phase = ModuleCompilationPhase::ValidatedAndLinked; + ctx.module_mut().phase = ModuleCompilationPhase::ValidatedAndLinked; Ok(()) } //-------------------------------------------------------------------------- @@ -343,15 +343,15 @@ impl Visitor for PassValidationLinking { // Nested synchronous statement let old_sync_span = ctx.heap[self.in_sync].span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, cur_sync_span, "Illegal nested synchronous statement" + &ctx.module().source, cur_sync_span, "Illegal nested synchronous statement" ).with_info_str_at_span( - &ctx.module.source, old_sync_span, "It is nested in this synchronous statement" + &ctx.module().source, old_sync_span, "It is nested in this synchronous statement" )); } if !self.def_type.is_primitive() { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, cur_sync_span, + &ctx.module().source, cur_sync_span, "synchronous statements may only be used in primitive components" )); } @@ -375,7 +375,7 @@ impl Visitor for PassValidationLinking { let stmt = &ctx.heap[id]; if !self.def_type.is_function() { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, stmt.span, + &ctx.module().source, stmt.span, "return statements may only appear in function bodies" )); } @@ -404,9 +404,9 @@ impl Visitor for PassValidationLinking { let goto_stmt = &ctx.heap[id]; let sync_stmt = &ctx.heap[self.in_sync]; return Err( - ParseError::new_error_str_at_span(&ctx.module.source, goto_stmt.span, "goto may not escape the surrounding synchronous block") - .with_info_str_at_span(&ctx.module.source, target.label.span, "this is the target of the goto statement") - .with_info_str_at_span(&ctx.module.source, sync_stmt.span, "which will jump past this statement") + ParseError::new_error_str_at_span(&ctx.module().source, goto_stmt.span, "goto may not escape the surrounding synchronous block") + .with_info_str_at_span(&ctx.module().source, target.label.span, "this is the target of the goto statement") + .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "which will jump past this statement") ); } @@ -420,7 +420,7 @@ impl Visitor for PassValidationLinking { if !self.def_type.is_composite() { let new_stmt = &ctx.heap[id]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, new_stmt.span, + &ctx.module().source, new_stmt.span, "instantiating components may only be done in composite components" )); } @@ -465,10 +465,13 @@ impl Visitor for PassValidationLinking { match self.expr_parent { // Look at us: lying through our teeth while providing error messages. ExpressionParent::ExpressionStmt(_) => {}, - _ => return Err(ParseError::new_error_str_at_span( - &ctx.module.source, assignment_expr.full_span, - "assignments are statements, and cannot be used in expressions" - )), + _ => { + let assignment_span = assignment_expr.full_span; + return Err(ParseError::new_error_str_at_span( + &ctx.module().source, assignment_span, + "assignments are statements, and cannot be used in expressions" + )) + }, } let left_expr_id = assignment_expr.left; @@ -494,14 +497,14 @@ impl Visitor for PassValidationLinking { // Check for valid context of binding expression if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a binding expression" + &ctx.module().source, span, "cannot assign to the result from a binding expression" )); } if self.in_test_expr.is_invalid() { let binding_expr = &ctx.heap[id]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, binding_expr.full_span, + &ctx.module().source, binding_expr.full_span, "binding expressions can only be used inside the testing expression of 'if' and 'while' statements" )); } @@ -510,10 +513,10 @@ impl Visitor for PassValidationLinking { let binding_expr = &ctx.heap[id]; let previous_expr = &ctx.heap[self.in_binding_expr]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, binding_expr.full_span, + &ctx.module().source, binding_expr.full_span, "nested binding expressions are not allowed" ).with_info_str_at_span( - &ctx.module.source, previous_expr.operator_span, + &ctx.module().source, previous_expr.operator_span, "the outer binding expression is found here" )); } @@ -551,10 +554,10 @@ impl Visitor for PassValidationLinking { let binding_expr = &ctx.heap[id]; let parent_expr = &ctx.heap[seeking_parent.as_expression()]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, binding_expr.full_span, + &ctx.module().source, binding_expr.full_span, "only the logical-and operator (&&) may be applied to binding expressions" ).with_info_str_at_span( - &ctx.module.source, parent_expr.operation_span(), + &ctx.module().source, parent_expr.operation_span(), "this was the disallowed operation applied to the result from a binding expression" )); } @@ -584,7 +587,7 @@ impl Visitor for PassValidationLinking { _ => { let binding_expr = &ctx.heap[id]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, binding_expr.operator_span, + &ctx.module().source, binding_expr.operator_span, "the left hand side of a binding expression may only be a variable or a literal expression" )); }, @@ -610,7 +613,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a conditional expression" + &ctx.module().source, span, "cannot assign to the result from a conditional expression" )) } @@ -640,7 +643,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a binary expression" + &ctx.module().source, span, "cannot assign to the result from a binary expression" )) } @@ -667,7 +670,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a unary expression" + &ctx.module().source, span, "cannot assign to the result from a unary expression" )) } @@ -715,7 +718,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { // TODO: @Slicing return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "assignment to slices should be valid in the final language, but is currently not implemented" + &ctx.module().source, span, "assignment to slices should be valid in the final language, but is currently not implemented" )); } @@ -768,7 +771,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to a literal expression" + &ctx.module().source, span, "cannot assign to a literal expression" )) } @@ -796,7 +799,7 @@ impl Visitor for PassValidationLinking { let literal = ctx.heap[id].value.as_struct(); let ast_definition = &ctx.heap[literal.definition]; return Err(ParseError::new_error_at_span( - &ctx.module.source, field_span, format!( + &ctx.module().source, field_span, format!( "This field does not exist on the struct '{}'", ast_definition.identifier().value.as_str() ) @@ -806,8 +809,9 @@ impl Visitor for PassValidationLinking { // Check if specified more than once if specified[field.field_idx] { + let field_span = field.identifier.span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, field.identifier.span, + &ctx.module().source, field_span, "This field is specified more than once" )); } @@ -835,8 +839,9 @@ impl Visitor for PassValidationLinking { format!("not all fields are specified, [{}] are missing", not_specified) }; + let literal_span = literal.parser_type.full_span; return Err(ParseError::new_error_at_span( - &ctx.module.source, literal.parser_type.full_span, msg + &ctx.module().source, literal_span, msg )); } @@ -868,7 +873,7 @@ impl Visitor for PassValidationLinking { let literal = ctx.heap[id].value.as_enum(); let ast_definition = ctx.heap[literal.definition].as_enum(); return Err(ParseError::new_error_at_span( - &ctx.module.source, literal.parser_type.full_span, format!( + &ctx.module().source, literal.parser_type.full_span, format!( "the variant '{}' does not exist on the enum '{}'", literal.variant.value.as_str(), ast_definition.identifier.value.as_str() ) @@ -889,7 +894,7 @@ impl Visitor for PassValidationLinking { let literal = ctx.heap[id].value.as_union(); let ast_definition = ctx.heap[literal.definition].as_union(); return Err(ParseError::new_error_at_span( - &ctx.module.source, literal.parser_type.full_span, format!( + &ctx.module().source, literal.parser_type.full_span, format!( "the variant '{}' does not exist on the union '{}'", literal.variant.value.as_str(), ast_definition.identifier.value.as_str() ) @@ -905,7 +910,7 @@ impl Visitor for PassValidationLinking { let literal = ctx.heap[id].value.as_union(); let ast_definition = ctx.heap[literal.definition].as_union(); return Err(ParseError::new_error_at_span( - &ctx.module.source, literal.parser_type.full_span, format!( + &ctx.module().source, literal.parser_type.full_span, format!( "The variant '{}' of union '{}' expects {} embedded values, but {} were specified", literal.variant.value.as_str(), ast_definition.identifier.value.as_str(), union_variant.embedded.len(), literal.values.len() @@ -953,7 +958,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a cast expression" + &ctx.module().source, span, "cannot assign to the result from a cast expression" )) } @@ -977,7 +982,7 @@ impl Visitor for PassValidationLinking { if let Some(span) = self.must_be_assignable { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "cannot assign to the result from a call expression" + &ctx.module().source, span, "cannot assign to the result from a call expression" )) } @@ -987,42 +992,48 @@ impl Visitor for PassValidationLinking { match &mut call_expr.method { Method::Get => { if !self.def_type.is_primitive() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'get' may only occur in primitive component definitions" )); } if self.in_sync.is_invalid() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'get' may only occur inside synchronous blocks" )); } }, Method::Put => { if !self.def_type.is_primitive() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'put' may only occur in primitive component definitions" )); } if self.in_sync.is_invalid() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'put' may only occur inside synchronous blocks" )); } }, Method::Fires => { if !self.def_type.is_primitive() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'fires' may only occur in primitive component definitions" )); } if self.in_sync.is_invalid() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "a call to 'fires' may only occur inside synchronous blocks" )); } @@ -1031,14 +1042,16 @@ impl Visitor for PassValidationLinking { Method::Length => {}, Method::Assert => { if self.def_type.is_function() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "assert statement may only occur in components" )); } if self.in_sync.is_invalid() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "assert statements may only occur inside synchronous blocks" )); } @@ -1051,15 +1064,17 @@ impl Visitor for PassValidationLinking { if expected_wrapping_new_stmt { if !self.expr_parent.is_new() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "cannot call a component, it can only be instantiated by using 'new'" )); } } else { if self.expr_parent.is_new() { + let call_span = call_expr.func_span; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, call_expr.func_span, + &ctx.module().source, call_span, "only components can be instantiated, this is a function" )); } @@ -1076,8 +1091,9 @@ impl Visitor for PassValidationLinking { let num_provided_args = call_expr.arguments.len(); if num_provided_args != num_expected_args { let argument_text = if num_expected_args == 1 { "argument" } else { "arguments" }; + let call_span = call_expr.full_span; return Err(ParseError::new_error_at_span( - &ctx.module.source, call_expr.full_span, format!( + &ctx.module().source, call_span, format!( "expected {} {}, but {} were provided", num_expected_args, argument_text, num_provided_args ) @@ -1118,7 +1134,7 @@ impl Visitor for PassValidationLinking { // then this may be the thing we're binding to. if self.in_binding_expr.is_invalid() || !self.in_binding_expr_lhs { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, var_expr.identifier.span, "unresolved variable" + &ctx.module().source, var_expr.identifier.span, "unresolved variable" )); } @@ -1158,10 +1174,10 @@ impl Visitor for PassValidationLinking { if !is_valid_binding { let binding_expr = &ctx.heap[self.in_binding_expr]; return Err(ParseError::new_error_str_at_span( - &ctx.module.source, var_expr.identifier.span, + &ctx.module().source, var_expr.identifier.span, "illegal location for binding variable: binding variables may only be nested under a binding expression, or a struct, union or array literal" ).with_info_at_span( - &ctx.module.source, binding_expr.operator_span, format!( + &ctx.module().source, binding_expr.operator_span, format!( "'{}' was interpreted as a binding variable because the variable is not declared and it is nested under this binding expression", var_expr.identifier.value.as_str() ) @@ -1416,9 +1432,9 @@ impl PassValidationLinking { if local.identifier == parameter.identifier { return Err( ParseError::new_error_str_at_span( - &ctx.module.source, local.identifier.span, "Local variable name conflicts with parameter" + &ctx.module().source, local.identifier.span, "Local variable name conflicts with parameter" ).with_info_str_at_span( - &ctx.module.source, parameter.identifier.span, "Parameter definition is found here" + &ctx.module().source, parameter.identifier.span, "Parameter definition is found here" ) ); } @@ -1442,9 +1458,9 @@ impl PassValidationLinking { // Collision within this scope return Err( ParseError::new_error_str_at_span( - &ctx.module.source, local.identifier.span, "Local variable name conflicts with another variable" + &ctx.module().source, local.identifier.span, "Local variable name conflicts with another variable" ).with_info_str_at_span( - &ctx.module.source, other_local.identifier.span, "Previous variable is found here" + &ctx.module().source, other_local.identifier.span, "Previous variable is found here" ) ); } @@ -1467,10 +1483,10 @@ impl PassValidationLinking { let ident = &ctx.heap[id].identifier; if let Some(symbol) = ctx.symbols.get_symbol_by_name(cur_scope, &ident.value.as_bytes()) { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, ident.span, + &ctx.module().source, ident.span, "local variable declaration conflicts with symbol" ).with_info_str_at_span( - &ctx.module.source, symbol.variant.span_of_introduction(&ctx.heap), "the conflicting symbol is introduced here" + &ctx.module().source, symbol.variant.span_of_introduction(&ctx.heap), "the conflicting symbol is introduced here" )); } } @@ -1488,9 +1504,9 @@ impl PassValidationLinking { // Collision return Err( ParseError::new_error_str_at_span( - &ctx.module.source, local.identifier.span, "Local variable name conflicts with another variable" + &ctx.module().source, local.identifier.span, "Local variable name conflicts with another variable" ).with_info_str_at_span( - &ctx.module.source, other_local.identifier.span, "Previous variable is found here" + &ctx.module().source, other_local.identifier.span, "Previous variable is found here" ) ); } @@ -1572,9 +1588,9 @@ impl PassValidationLinking { if other_label.label == label.label { // Collision return Err(ParseError::new_error_str_at_span( - &ctx.module.source, label.label.span, "label name is used more than once" + &ctx.module().source, label.label.span, "label name is used more than once" ).with_info_str_at_span( - &ctx.module.source, other_label.label.span, "the other label is found here" + &ctx.module().source, other_label.label.span, "the other label is found here" )); } } @@ -1615,9 +1631,9 @@ impl PassValidationLinking { let local = &ctx.heap[*local_id]; if local.relative_pos_in_block > relative_scope_pos && local.relative_pos_in_block < label.relative_pos_in_block { return Err( - ParseError::new_error_str_at_span(&ctx.module.source, identifier.span, "this target label skips over a variable declaration") - .with_info_str_at_span(&ctx.module.source, label.label.span, "because it jumps to this label") - .with_info_str_at_span(&ctx.module.source, local.identifier.span, "which skips over this variable") + ParseError::new_error_str_at_span(&ctx.module().source, identifier.span, "this target label skips over a variable declaration") + .with_info_str_at_span(&ctx.module().source, label.label.span, "because it jumps to this label") + .with_info_str_at_span(&ctx.module().source, local.identifier.span, "which skips over this variable") ); } } @@ -1628,7 +1644,7 @@ impl PassValidationLinking { scope = &block.scope_node.parent; if !scope.is_block() { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, identifier.span, "could not find this label" + &ctx.module().source, identifier.span, "could not find this label" )); } @@ -1673,18 +1689,18 @@ impl PassValidationLinking { // present underneath this particular labeled while statement if !self.has_parent_while_scope(ctx, target_stmt.this) { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, label.span, "break statement is not nested under the target label's while statement" + &ctx.module().source, label.span, "break statement is not nested under the target label's while statement" ).with_info_str_at_span( - &ctx.module.source, target.label.span, "the targeted label is found here" + &ctx.module().source, target.label.span, "the targeted label is found here" )); } target_stmt.this } else { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, label.span, "incorrect break target label, it must target a while loop" + &ctx.module().source, label.span, "incorrect break target label, it must target a while loop" ).with_info_str_at_span( - &ctx.module.source, target.label.span, "The targeted label is found here" + &ctx.module().source, target.label.span, "The targeted label is found here" )); } }, @@ -1693,7 +1709,7 @@ impl PassValidationLinking { // nested within that while statement if self.in_while.is_invalid() { return Err(ParseError::new_error_str_at_span( - &ctx.module.source, span, "Break statement is not nested under a while loop" + &ctx.module().source, span, "Break statement is not nested under a while loop" )); } @@ -1711,9 +1727,9 @@ impl PassValidationLinking { debug_assert!(!self.in_sync.is_invalid()); let sync_stmt = &ctx.heap[self.in_sync]; return Err( - ParseError::new_error_str_at_span(&ctx.module.source, span, "break may not escape the surrounding synchronous block") - .with_info_str_at_span(&ctx.module.source, target_while.span, "the break escapes out of this loop") - .with_info_str_at_span(&ctx.module.source, sync_stmt.span, "And would therefore escape this synchronous block") + ParseError::new_error_str_at_span(&ctx.module().source, span, "break may not escape the surrounding synchronous block") + .with_info_str_at_span(&ctx.module().source, target_while.span, "the break escapes out of this loop") + .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "And would therefore escape this synchronous block") ); } } diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 58daff1b21d1ff99f641095cbf3f4e03bd28e640..9c1ffedd4fa3fe53c89b1f5770836019e83b8696 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -67,6 +67,13 @@ impl TypeClass { TypeClass::Component => "component", } } + + pub(crate) fn is_data_type(&self) -> bool { + match self { + TypeClass::Enum | TypeClass::Union | TypeClass::Struct => true, + TypeClass::Function | TypeClass::Component => false, + } + } } impl std::fmt::Display for TypeClass { @@ -112,14 +119,14 @@ impl DefinedType { // Helper to compare two types, while disregarding the polymorphic // variables that are not in use. - let concrete_types_match = |type_a: &ConcreteType, type_b: &ConcreteType| -> bool { + let concrete_types_match = |type_a: &ConcreteType, type_b: &ConcreteType, check_if_poly_var_is_used: bool| -> bool { let mut a_iter = type_a.embedded_iter(0).enumerate(); let mut b_iter = type_b.embedded_iter(0); while let Some((section_idx, a_section)) = a_iter.next() { let b_section = b_iter.next().unwrap(); - if !self.poly_vars[section_idx].is_in_use { + if check_if_poly_var_is_used && !self.poly_vars[section_idx].is_in_use { continue; } @@ -153,20 +160,31 @@ impl DefinedType { }, DTV::Union(definition) => { for (monomorph_idx, monomorph) in definition.monomorphs.iter().enumerate() { - if concrete_types_match(&monomorph.concrete_type, concrete_type) { + if concrete_types_match(&monomorph.concrete_type, concrete_type, true) { return Some(monomorph_idx); } } }, DTV::Struct(definition) => { for (monomorph_idx, monomorph) in definition.monomorphs.iter().enumerate() { - if concrete_types_match(&monomorph.concrete_type, concrete_type) { + if concrete_types_match(&monomorph.concrete_type, concrete_type, true) { return Some(monomorph_idx); } } }, - DTV::Function(_) | DTV::Component(_) => { - unreachable!("called get_monomorph_index on a procedure type"); + DTV::Function(definition) => { + for (monomorph_idx, monomorph) in definition.monomorphs.iter().enumerate() { + if concrete_types_match(&monomorph.concrete_type, concrete_type, false) { + return Some(monomorph_idx) + } + } + } + DTV::Component(definition) => { + for (monomorph_idx, monomorph) in definition.monomorphs.iter().enumerate() { + if concrete_types_match(&monomorph.concrete_type, concrete_type, false) { + return Some(monomorph_idx) + } + } } } @@ -301,7 +319,7 @@ pub struct PolymorphicVariable { /// procedure. (in that case, there will only ever be one) pub struct ProcedureMonomorph { // Expression data for one particular monomorph - pub poly_args: Vec, + pub concrete_type: ConcreteType, pub expr_data: Vec, } @@ -446,6 +464,8 @@ pub struct MonomorphExpression { // Type table //------------------------------------------------------------------------------ +// Programmer note: keep this struct free of dynamically allocated memory +#[derive(Clone)] struct TypeLoopBreadcrumb { definition_id: DefinitionId, monomorph_idx: usize, @@ -453,16 +473,25 @@ struct TypeLoopBreadcrumb { next_embedded: usize, // for unions, the index into the variant's embedded types } +#[derive(Clone)] +struct MemoryBreadcrumb { + definition_id: DefinitionId, + monomorph_idx: usize, + next_member: usize, + next_embedded: usize, + first_size_alignment_idx: usize, +} + #[derive(Debug, PartialEq, Eq)] -enum BreadcrumbResult { +enum TypeLoopResult { TypeExists, - PushedBreadcrumb, + PushBreadcrumb(DefinitionId, ConcreteType), TypeLoop(usize), // index into vec of breadcrumbs at which the type matched } enum MemoryLayoutResult { TypeExists(usize, usize), // (size, alignment) - PushBreadcrumb(TypeLoopBreadcrumb), + PushBreadcrumb(MemoryBreadcrumb), } // TODO: @Optimize, initial memory-unoptimized implementation @@ -482,9 +511,14 @@ pub struct TypeTable { lookup: HashMap, /// Breadcrumbs left behind while trying to find type loops. Also used to /// determine sizes of types when all type loops are detected. - breadcrumbs: Vec, + type_loop_breadcrumbs: Vec, type_loops: Vec, + /// Stores all encountered types during type loop detection. Used afterwards + /// to iterate over all types in order to compute size/alignment. encountered_types: Vec, + /// Breadcrumbs and temporary storage during memory layout computation. + memory_layout_breadcrumbs: Vec, + size_alignment_stack: Vec<(usize, usize)>, } impl TypeTable { @@ -492,9 +526,11 @@ impl TypeTable { pub(crate) fn new() -> Self { Self{ lookup: HashMap::new(), - breadcrumbs: Vec::with_capacity(32), + type_loop_breadcrumbs: Vec::with_capacity(32), type_loops: Vec::with_capacity(8), encountered_types: Vec::with_capacity(32), + memory_layout_breadcrumbs: Vec::with_capacity(32), + size_alignment_stack: Vec::with_capacity(64), } } @@ -534,14 +570,13 @@ impl TypeTable { } debug_assert_eq!(self.lookup.len(), reserve_size, "mismatch in reserved size of type table"); // NOTE: Temp fix for builtin functions - for module in modules { + for module in modules.iter_mut() { module.phase = ModuleCompilationPhase::TypesAddedToTable; } // Go through all types again, lay out all types that are not // polymorphic. This might cause us to lay out types that are monomorphs // of polymorphic types. - let empty_concrete_type = ConcreteType{ parts: Vec::new() }; for definition_idx in 0..ctx.heap.definitions.len() { let definition_id = ctx.heap.definitions.get_id(definition_idx); let poly_type = self.lookup.get(&definition_id).unwrap(); @@ -550,14 +585,14 @@ impl TypeTable { // polymorphic arguments (even if it has phantom polymorphic // arguments) because otherwise the user will see very weird // error messages. - if poly_type.poly_vars.is_empty() && poly_type.num_monomorphs() == 0 { + if poly_type.definition.type_class().is_data_type() && poly_type.poly_vars.is_empty() && poly_type.num_monomorphs() == 0 { self.detect_and_resolve_type_loops_for( - modules, ctx, + modules, ctx.heap, ConcreteType{ parts: vec![ConcreteTypePart::Instance(definition_id, 0)] }, )?; - self.lay_out_memory_for_encountered_types(ctx); + self.lay_out_memory_for_encountered_types(ctx.arch); } } @@ -574,22 +609,12 @@ impl TypeTable { /// Returns the index into the monomorph type array if the procedure type /// already has a (reserved) monomorph. - pub(crate) fn get_procedure_monomorph_index(&self, definition_id: &DefinitionId, types: &Vec) -> Option { + pub(crate) fn get_procedure_monomorph_index(&self, definition_id: &DefinitionId, types: &ConcreteType) -> Option { let def = self.lookup.get(definition_id).unwrap(); - if def.is_polymorph { - let monos = def.definition.procedure_monomorphs(); - return monos.iter() - .position(|v| v.poly_args == *types) - .map(|v| v as i32); - } else { - // We don't actually care about the types - let monos = def.definition.procedure_monomorphs(); - if monos.is_empty() { - return None - } else { - return Some(0) - } - } + let monos = def.definition.procedure_monomorphs(); + return monos.iter() + .position(|v| v.concrete_type == *types) + .map(|v| v as i32); } /// Returns a mutable reference to a procedure's monomorph expression data. @@ -613,52 +638,42 @@ impl TypeTable { /// monomorph may NOT exist yet (because the reservation implies that we're /// going to be performing typechecking on it, and we don't want to /// check the same monomorph twice) - pub(crate) fn reserve_procedure_monomorph_index(&mut self, definition_id: &DefinitionId, types: Option>) -> i32 { + pub(crate) fn reserve_procedure_monomorph_index(&mut self, definition_id: &DefinitionId, concrete_type: ConcreteType) -> i32 { let def = self.lookup.get_mut(definition_id).unwrap(); - if let Some(types) = types { - // Expecting a polymorphic procedure - let monos = def.definition.procedure_monomorphs_mut(); - debug_assert!(def.is_polymorph); - debug_assert!(def.poly_vars.len() == types.len()); - debug_assert!(monos.iter().find(|v| v.poly_args == types).is_none()); - - let mono_idx = monos.len(); - monos.push(ProcedureMonomorph{ poly_args: types, expr_data: Vec::new() }); - - return mono_idx as i32; - } else { - // Expecting a non-polymorphic procedure - let monos = def.definition.procedure_monomorphs_mut(); - debug_assert!(!def.is_polymorph); - debug_assert!(def.poly_vars.is_empty()); - debug_assert!(monos.is_empty()); - - monos.push(ProcedureMonomorph{ poly_args: Vec::new(), expr_data: Vec::new() }); + let mono_types = def.definition.procedure_monomorphs_mut(); + debug_assert!(def.is_polymorph == (concrete_type.parts.len() != 1)); + debug_assert!(!mono_types.iter().any(|v| v.concrete_type == concrete_type)); + + let mono_idx = mono_types.len(); + mono_types.push(ProcedureMonomorph{ + concrete_type, + expr_data: Vec::new(), + }); - return 0; - } + return mono_idx as i32; } /// Adds a datatype polymorph to the type table. Will not add the /// monomorph if it is already present, or if the type's polymorphic /// variables are all unused. + /// TODO: Fix signature pub(crate) fn add_data_monomorph( - &mut self, modules: &[Module], ctx: &PassCtx, definition_id: &DefinitionId, concrete_type: ConcreteType + &mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, definition_id: DefinitionId, concrete_type: ConcreteType ) -> Result { - debug_assert_eq!(*definition_id, get_concrete_type_definition(&concrete_type)); + debug_assert_eq!(definition_id, get_concrete_type_definition(&concrete_type)); // Check if the monomorph already exists - let poly_type = self.lookup.get_mut(definition_id).unwrap(); + let poly_type = self.lookup.get_mut(&definition_id).unwrap(); if let Some(idx) = poly_type.get_monomorph_index(&concrete_type) { return Ok(idx as i32); } // Doesn't exist, so instantiate a monomorph and determine its memory // layout. - self.detect_and_resolve_type_loops_for(modules, ctx, concrete_type)?; + self.detect_and_resolve_type_loops_for(modules, heap, concrete_type)?; debug_assert_eq!(self.encountered_types[0].definition_id, definition_id); let mono_idx = self.encountered_types[0].monomorph_idx; - self.lay_out_memory_for_encountered_types(ctx); + self.lay_out_memory_for_encountered_types(arch); return Ok(mono_idx as i32); } @@ -756,7 +771,6 @@ impl TypeTable { )?; } - let has_embedded = !variant.value.is_empty(); variants.push(UnionVariant{ identifier: variant.identifier.clone(), embedded: variant.value.clone(), @@ -776,7 +790,7 @@ impl TypeTable { Self::check_identifier_collision( modules, root_id, &variants, |variant| &variant.identifier, "union variant" )?; - Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars); + Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?; // Construct internal representation of union let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars); @@ -1084,31 +1098,36 @@ impl TypeTable { fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], heap: &Heap, concrete_type: ConcreteType) -> Result<(), ParseError> { use DefinedTypeVariant as DTV; - debug_assert!(self.breadcrumbs.is_empty()); + debug_assert!(self.type_loop_breadcrumbs.is_empty()); debug_assert!(self.type_loops.is_empty()); debug_assert!(self.encountered_types.is_empty()); // Push the initial breadcrumb - let _initial_result = self.push_breadcrumb_for_type_loops(get_concrete_type_definition(&concrete_type), &concrete_type); - debug_assert_eq!(_initial_result, BreadcrumbResult::PushedBreadcrumb); + let initial_breadcrumb = self.check_member_for_type_loops(&concrete_type); + if let TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) = initial_breadcrumb { + self.handle_new_breadcrumb_for_type_loops(definition_id, concrete_type); + } else { + unreachable!(); + } // Enter into the main resolving loop - while !self.breadcrumbs.is_empty() { - let breadcrumb_idx = self.breadcrumbs.len() - 1; - let breadcrumb = self.breadcrumbs.last_mut().unwrap(); + while !self.type_loop_breadcrumbs.is_empty() { + // Because we might be modifying the breadcrumb array we need to + let breadcrumb_idx = self.type_loop_breadcrumbs.len() - 1; + let mut breadcrumb = self.type_loop_breadcrumbs[breadcrumb_idx].clone(); let poly_type = self.lookup.get(&breadcrumb.definition_id).unwrap(); + let poly_type_definition_id = poly_type.ast_definition; - // TODO: Misuse of BreadcrumbResult enum? let resolve_result = match &poly_type.definition { DTV::Enum(_) => { - BreadcrumbResult::TypeExists + TypeLoopResult::TypeExists }, DTV::Union(definition) => { let monomorph = &definition.monomorphs[breadcrumb.monomorph_idx]; let num_variants = monomorph.variants.len(); - let mut union_result = BreadcrumbResult::TypeExists; + let mut union_result = TypeLoopResult::TypeExists; 'member_loop: while breadcrumb.next_member < num_variants { let mono_variant = &monomorph.variants[breadcrumb.next_member]; @@ -1116,9 +1135,9 @@ impl TypeTable { while breadcrumb.next_embedded < num_embedded { let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded]; - union_result = self.push_breadcrumb_for_type_loops(poly_type.ast_definition, &mono_embedded.concrete_type); + union_result = self.check_member_for_type_loops(&mono_embedded.concrete_type); - if union_result != BreadcrumbResult::TypeExists { + if union_result != TypeLoopResult::TypeExists { // In type loop or new breadcrumb pushed, so // break out of the resolving loop break 'member_loop; @@ -1137,12 +1156,12 @@ impl TypeTable { let monomorph = &definition.monomorphs[breadcrumb.monomorph_idx]; let num_fields = monomorph.fields.len(); - let mut struct_result = BreadcrumbResult::TypeExists; + let mut struct_result = TypeLoopResult::TypeExists; while breadcrumb.next_member < num_fields { let mono_field = &monomorph.fields[breadcrumb.next_member]; - struct_result = self.push_breadcrumb_for_type_loops(poly_type.ast_definition, &mono_field.concrete_type); + struct_result = self.check_member_for_type_loops(&mono_field.concrete_type); - if struct_result != BreadcrumbResult::TypeExists { + if struct_result != TypeLoopResult::TypeExists { // Type loop or breadcrumb pushed, so break out of // the resolving loop break; @@ -1158,19 +1177,26 @@ impl TypeTable { // Handle the result of attempting to resolve the current breadcrumb match resolve_result { - BreadcrumbResult::TypeExists => { + TypeLoopResult::TypeExists => { // We finished parsing the type - self.breadcrumbs.pop(); + self.type_loop_breadcrumbs.pop(); }, - BreadcrumbResult::PushedBreadcrumb => { - // We recurse into the member type, since the breadcrumb is - // already pushed we don't take any action here. + TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) => { + // We recurse into the member type. + self.type_loop_breadcrumbs[breadcrumb_idx] = breadcrumb; + self.handle_new_breadcrumb_for_type_loops(definition_id, concrete_type); }, - BreadcrumbResult::TypeLoop(first_idx) => { + TypeLoopResult::TypeLoop(first_idx) => { + // Because we will be modifying breadcrumbs within the + // type-loop handling code, put back the modified breadcrumb + self.type_loop_breadcrumbs[breadcrumb_idx] = breadcrumb; + // We're in a type loop. Add the type loop - let mut loop_members = Vec::with_capacity(self.breadcrumbs.len() - first_idx); - for breadcrumb_idx in first_idx..self.breadcrumbs.len() { - let breadcrumb = &mut self.breadcrumbs[breadcrumb_idx]; + let mut loop_members = Vec::with_capacity(self.type_loop_breadcrumbs.len() - first_idx); + let mut contains_union = false; + + for breadcrumb_idx in first_idx..self.type_loop_breadcrumbs.len() { + let breadcrumb = &mut self.type_loop_breadcrumbs[breadcrumb_idx]; let mut is_union = false; let entry = self.lookup.get_mut(&breadcrumb.definition_id).unwrap(); @@ -1184,6 +1210,7 @@ impl TypeTable { variant.lives_on_heap = true; breadcrumb.next_embedded += 1; is_union = true; + contains_union = true; }, _ => {}, // else: we don't care for now } @@ -1195,7 +1222,17 @@ impl TypeTable { }); } - self.type_loops.push(TypeLoop{ members: loop_members }); + let new_type_loop = TypeLoop{ members: loop_members }; + if !contains_union { + // No way to (potentially) break the union. So return a + // type loop error. This is because otherwise our + // breadcrumb resolver ends up in an infinite loop. + return Err(construct_type_loop_error( + self, &new_type_loop, modules, heap + )); + } + + self.type_loops.push(new_type_loop); } } } @@ -1247,6 +1284,26 @@ impl TypeTable { ); } + fn construct_type_loop_error(table: &TypeTable, type_loop: &TypeLoop, modules: &[Module], heap: &Heap) -> ParseError { + let first_entry = &type_loop.members[0]; + let first_type = table.lookup.get(&first_entry.definition_id).unwrap(); + let (first_module, first_span, first_message) = type_loop_source_span_and_message( + modules, heap, first_type, first_entry.monomorph_idx, 0 + ); + let mut parse_error = ParseError::new_error_at_span(first_module, first_span, first_message); + + for member_idx in 1..type_loop.members.len() { + let entry = &type_loop.members[member_idx]; + let entry_type = table.lookup.get(&first_entry.definition_id).unwrap(); + let (module, span, message) = type_loop_source_span_and_message( + modules, heap, entry_type, entry.monomorph_idx, member_idx + ); + parse_error = parse_error.with_info_at_span(module, span, message); + } + + parse_error + } + for type_loop in &self.type_loops { let mut can_be_broken = false; debug_assert!(!type_loop.members.is_empty()); @@ -1266,23 +1323,7 @@ impl TypeTable { if !can_be_broken { // Construct a type loop error - let first_entry = &type_loop.members[0]; - let first_type = self.lookup.get(&first_entry.definition_id).unwrap(); - let (first_module, first_span, first_message) = type_loop_source_span_and_message( - modules, heap, first_type, first_entry.monomorph_idx, 0 - ); - let mut parse_error = ParseError::new_error_at_span(first_module, first_span, first_message); - - for member_idx in 1..type_loop.members.len() { - let entry = &type_loop.members[member_idx]; - let entry_type = self.lookup.get(&first_entry.definition_id).unwrap(); - let (module, span, message) = type_loop_source_span_and_message( - modules, heap, entry_type, entry.monomorph_idx, member_idx - ); - parse_error = parse_error.with_info_at_span(module, span, message); - } - - return Err(parse_error); + return Err(construct_type_loop_error(self, type_loop, modules, heap)); } } @@ -1293,31 +1334,60 @@ impl TypeTable { return Ok(()); } - // TODO: Pass in definition_type by value? - fn push_breadcrumb_for_type_loops(&mut self, definition_id: DefinitionId, definition_type: &ConcreteType) -> BreadcrumbResult { - use DefinedTypeVariant as DTV; + /// Checks if the specified type needs to be resolved (i.e. we need to push + /// a breadcrumb), is already resolved (i.e. we can continue with the next + /// member of the currently considered type) or is in the process of being + /// resolved (i.e. we're in a type loop). Because of borrowing rules we + /// don't do any modifications of internal types here. Hence: if we + /// return `PushBreadcrumb` then call `handle_new_breadcrumb_for_type_loops` + /// to take care of storing the appropriate types. + fn check_member_for_type_loops(&self, definition_type: &ConcreteType) -> TypeLoopResult { + use ConcreteTypePart as CTP; + + // We're only interested in user-defined types, so exit if it is a + // builtin of some sort. + debug_assert!(!definition_type.parts.is_empty()); + let definition_id = match &definition_type.parts[0] { + CTP::Instance(definition_id, _) | + CTP::Function(definition_id, _) | + CTP::Component(definition_id, _) => { + *definition_id + }, + _ => { + return TypeLoopResult::TypeExists + }, + }; - let mut base_type = self.lookup.get_mut(&definition_id).unwrap(); + let base_type = self.lookup.get(&definition_id).unwrap(); if let Some(mono_idx) = base_type.get_monomorph_index(&definition_type) { // Monomorph is already known. Check if it is present in the // breadcrumbs. If so, then we are in a type loop - for (breadcrumb_idx, breadcrumb) in self.breadcrumbs.iter().enumerate() { + for (breadcrumb_idx, breadcrumb) in self.type_loop_breadcrumbs.iter().enumerate() { if breadcrumb.definition_id == definition_id && breadcrumb.monomorph_idx == mono_idx { - return BreadcrumbResult::TypeLoop(breadcrumb_idx); + return TypeLoopResult::TypeLoop(breadcrumb_idx); } } - return BreadcrumbResult::TypeExists; + return TypeLoopResult::TypeExists; } // Type is not yet known, so we need to insert it into the lookup and // push a new breadcrumb. + return TypeLoopResult::PushBreadcrumb(definition_id, definition_type.clone()); + } + + /// Handles the `PushBreadcrumb` result for a `check_member_for_type_loops` + /// call. + fn handle_new_breadcrumb_for_type_loops(&mut self, definition_id: DefinitionId, definition_type: ConcreteType) { + use DefinedTypeVariant as DTV; + + let base_type = self.lookup.get_mut(&definition_id).unwrap(); let mut is_union = false; let monomorph_idx = match &mut base_type.definition { DTV::Enum(definition) => { debug_assert!(definition.monomorphs.is_empty()); definition.monomorphs.push(EnumMonomorph{ - concrete_type: definition_type.clone(), + concrete_type: definition_type, }); 0 }, @@ -1327,7 +1397,7 @@ impl TypeTable { for poly_variant in &definition.variants { let mut mono_embedded = Vec::with_capacity(poly_variant.embedded.len()); for poly_embedded in &poly_variant.embedded { - let mono_concrete = Self::construct_concrete_type(poly_embedded, definition_type); + let mono_concrete = Self::construct_concrete_type(poly_embedded, &definition_type); mono_embedded.push(UnionMonomorphEmbedded{ concrete_type: mono_concrete, size: 0, @@ -1344,7 +1414,7 @@ impl TypeTable { let mono_idx = definition.monomorphs.len(); definition.monomorphs.push(UnionMonomorph{ - concrete_type: definition_type.clone(), + concrete_type: definition_type, variants: mono_variants, stack_size: 0, stack_alignment: 0, @@ -1358,7 +1428,7 @@ impl TypeTable { DTV::Struct(definition) => { let mut mono_fields = Vec::with_capacity(definition.fields.len()); for poly_field in &definition.fields { - let mono_concrete = Self::construct_concrete_type(&poly_field.parser_type, definition_type); + let mono_concrete = Self::construct_concrete_type(&poly_field.parser_type, &definition_type); mono_fields.push(StructMonomorphField{ concrete_type: mono_concrete, size: 0, @@ -1369,7 +1439,7 @@ impl TypeTable { let mono_idx = definition.monomorphs.len(); definition.monomorphs.push(StructMonomorph{ - concrete_type: definition_type.clone(), + concrete_type: definition_type, fields: mono_fields, size: 0, alignment: 0 @@ -1382,22 +1452,18 @@ impl TypeTable { }, }; - self.breadcrumbs.push(TypeLoopBreadcrumb{ + self.encountered_types.push(TypeLoopEntry{ definition_id, monomorph_idx, - next_member: 0, - next_embedded: 0 + is_union, }); - // With the breadcrumb constructed we know this is a new type, so we - // also need to add an entry in the list of all encountered types - self.encountered_types.push(TypeLoopEntry{ + self.type_loop_breadcrumbs.push(TypeLoopBreadcrumb{ definition_id, monomorph_idx, - is_union, + next_member: 0, + next_embedded: 0, }); - - return BreadcrumbResult::PushedBreadcrumb; } /// Constructs a concrete type out of a parser type for a struct field or @@ -1467,32 +1533,34 @@ impl TypeTable { // Determining memory layout for types //-------------------------------------------------------------------------- - fn lay_out_memory_for_encountered_types(&mut self, ctx: &PassCtx) { + fn lay_out_memory_for_encountered_types(&mut self, arch: &TargetArch) { use DefinedTypeVariant as DTV; // Just finished type loop detection, so we're left with the encountered // types only - debug_assert!(self.breadcrumbs.is_empty()); debug_assert!(self.type_loops.is_empty()); debug_assert!(!self.encountered_types.is_empty()); + debug_assert!(self.memory_layout_breadcrumbs.is_empty()); + debug_assert!(self.size_alignment_stack.is_empty()); // Push the first entry (the type we originally started with when we // were detecting type loops) let first_entry = &self.encountered_types[0]; - self.breadcrumbs.push(TypeLoopBreadcrumb{ + self.memory_layout_breadcrumbs.push(MemoryBreadcrumb{ definition_id: first_entry.definition_id, monomorph_idx: first_entry.monomorph_idx, next_member: 0, next_embedded: 0, + first_size_alignment_idx: 0, }); // Enter the main resolving loop - 'breadcrumb_loop: while !self.breadcrumbs.is_empty() { - let breadcrumb_idx = self.breadcrumbs.len() - 1; - let breadcrumb = &mut self.breadcrumbs[breadcrumb_idx]; + 'breadcrumb_loop: while !self.memory_layout_breadcrumbs.is_empty() { + let cur_breadcrumb_idx = self.memory_layout_breadcrumbs.len() - 1; + let mut breadcrumb = self.memory_layout_breadcrumbs[cur_breadcrumb_idx].clone(); - let poly_type = self.lookup.get_mut(&breadcrumb.definition_id).unwrap(); - match &mut poly_type.definition { + let poly_type = self.lookup.get(&breadcrumb.definition_id).unwrap(); + match &poly_type.definition { DTV::Enum(definition) => { // Size should already be computed debug_assert!(definition.size != 0 && definition.alignment != 0); @@ -1500,10 +1568,10 @@ impl TypeTable { DTV::Union(definition) => { // Retrieve size/alignment of each embedded type. We do not // compute the offsets or total type sizes yet. - let mono_type = &mut definition.monomorphs[breadcrumb.monomorph_idx]; + let mono_type = &definition.monomorphs[breadcrumb.monomorph_idx]; let num_variants = mono_type.variants.len(); while breadcrumb.next_member < num_variants { - let mono_variant = &mut mono_type.variants[breadcrumb.next_member]; + let mono_variant = &mono_type.variants[breadcrumb.next_member]; if mono_variant.lives_on_heap { // To prevent type loops we made this a heap- @@ -1512,14 +1580,14 @@ impl TypeTable { } else { let num_embedded = mono_variant.embedded.len(); while breadcrumb.next_embedded < num_embedded { - let mono_embedded = &mut mono_variant.embedded[breadcrumb.next_embedded]; - match self.get_memory_layout_or_breadcrumb(ctx, &mono_embedded.concrete_type) { + let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded]; + match self.get_memory_layout_or_breadcrumb(arch, &mono_embedded.concrete_type) { MemoryLayoutResult::TypeExists(size, alignment) => { - mono_embedded.size = size; - mono_embedded.alignment = alignment; + self.size_alignment_stack.push((size, alignment)); }, MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { - self.breadcrumbs.push(new_breadcrumb); + self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; + self.memory_layout_breadcrumbs.push(new_breadcrumb); continue 'breadcrumb_loop; } } @@ -1539,6 +1607,11 @@ impl TypeTable { let mut max_size = definition.tag_size; let mut max_alignment = definition.tag_size; + let poly_type = self.lookup.get_mut(&breadcrumb.definition_id).unwrap(); + let definition = poly_type.definition.as_union_mut(); + let mono_type = &mut definition.monomorphs[breadcrumb.monomorph_idx]; + let mut size_alignment_idx = breadcrumb.first_size_alignment_idx; + for variant in &mut mono_type.variants { // We're doing stack computations, so always start with // the tag size/alignment. @@ -1547,7 +1620,7 @@ impl TypeTable { if variant.lives_on_heap { // Variant lives on heap, so just a pointer - let (ptr_size, ptr_align) = ctx.arch.pointer_size_alignment; + let (ptr_size, ptr_align) = arch.pointer_size_alignment; align_offset_to(&mut variant_offset, ptr_align); variant_offset += ptr_size; @@ -1556,11 +1629,16 @@ impl TypeTable { // Variant lives on stack, so walk all embedded // types. for embedded in &mut variant.embedded { - align_offset_to(&mut variant_offset, embedded.alignment); + let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; + embedded.size = size; + embedded.alignment = alignment; + size_alignment_idx += 1; + + align_offset_to(&mut variant_offset, alignment); embedded.offset = variant_offset; - variant_offset += embedded.size; - variant_alignment = variant_alignment.max(embedded.alignment); + variant_offset += size; + variant_alignment = variant_alignment.max(alignment); } }; @@ -1570,22 +1648,23 @@ impl TypeTable { mono_type.stack_size = max_size; mono_type.stack_alignment = max_alignment; + self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx); }, DTV::Struct(definition) => { // Retrieve size and alignment of each struct member. We'll // compute the offsets once all of those are known - let mono_type = &mut definition.monomorphs[breadcrumb.monomorph_idx]; + let mono_type = &definition.monomorphs[breadcrumb.monomorph_idx]; let num_fields = mono_type.fields.len(); while breadcrumb.next_member < num_fields { - let mono_field = &mut mono_type.fields[breadcrumb.next_member]; + let mono_field = &mono_type.fields[breadcrumb.next_member]; - match self.get_memory_layout_or_breadcrumb(ctx, &mono_field.concrete_type) { + match self.get_memory_layout_or_breadcrumb(arch, &mono_field.concrete_type) { MemoryLayoutResult::TypeExists(size, alignment) => { - mono_field.size = size; - mono_field.alignment = alignment; + self.size_alignment_stack.push((size, alignment)) }, MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { - self.breadcrumbs.push(new_breadcrumb); + self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; + self.memory_layout_breadcrumbs.push(new_breadcrumb); continue 'breadcrumb_loop; }, } @@ -1596,16 +1675,28 @@ impl TypeTable { // Compute offsets and size of total type let mut cur_offset = 0; let mut max_alignment = 1; + + let poly_type = self.lookup.get_mut(&breadcrumb.definition_id).unwrap(); + let definition = poly_type.definition.as_struct_mut(); + let mono_type = &mut definition.monomorphs[breadcrumb.monomorph_idx]; + let mut size_alignment_idx = breadcrumb.first_size_alignment_idx; + for field in &mut mono_type.fields { - align_offset_to(&mut cur_offset, field.alignment); + let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; + field.size = size; + field.alignment = alignment; + size_alignment_idx += 1; + + align_offset_to(&mut cur_offset, alignment); field.offset = cur_offset; - cur_offset += field.size; - max_alignment = max_alignment.max(field.alignment); + cur_offset += size; + max_alignment = max_alignment.max(alignment); } mono_type.size = cur_offset; mono_type.alignment = max_alignment; + self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx); }, DTV::Function(_) | DTV::Component(_) => { unreachable!(); @@ -1614,9 +1705,11 @@ impl TypeTable { // If here, then we completely layed out the current type. So move // to the next breadcrumb - self.breadcrumbs.pop(); + self.memory_layout_breadcrumbs.pop(); } + debug_assert!(self.size_alignment_stack.is_empty()); + // If here then all types have been layed out. What remains is to // compute the sizes/alignment/offsets of the heap variants of the // unions we have encountered. @@ -1625,50 +1718,67 @@ impl TypeTable { continue; } + // First pass, use buffer to store size/alignment to prevent + // borrowing issues. + let poly_type = self.lookup.get(&entry.definition_id).unwrap(); + let definition = poly_type.definition.as_union(); + let mono_type = &definition.monomorphs[entry.monomorph_idx]; + + for variant in &mono_type.variants { + if !variant.lives_on_heap { + continue; + } + + debug_assert!(!variant.embedded.is_empty()); + + for embedded in &variant.embedded { + match self.get_memory_layout_or_breadcrumb(arch, &embedded.concrete_type) { + MemoryLayoutResult::TypeExists(size, alignment) => { + self.size_alignment_stack.push((size, alignment)); + }, + _ => unreachable!(), + } + } + } + + // Second pass, apply the size/alignment values in our buffer let poly_type = self.lookup.get_mut(&entry.definition_id).unwrap(); - match &mut poly_type.definition { - DTV::Union(definition) => { - let mono_type = &mut definition.monomorphs[entry.monomorph_idx]; - let mut max_size = 0; - let mut max_alignment = 1; + let definition = poly_type.definition.as_union_mut(); + let mono_type = &mut definition.monomorphs[entry.monomorph_idx]; - for variant in &mut mono_type.variants { - if !variant.lives_on_heap { - continue; - } + let mut max_size = 0; + let mut max_alignment = 1; + let mut size_alignment_idx = 0; - let mut variant_offset = 0; - let mut variant_alignment = 1; - debug_assert!(!variant.embedded.is_empty()); + for variant in &mut mono_type.variants { + if !variant.lives_on_heap { + continue; + } - for embedded in &mut variant.embedded { - match self.get_memory_layout_or_breadcrumb(ctx, &embedded.concrete_type) { - MemoryLayoutResult::TypeExists(size, alignment) => { - embedded.size = size; - embedded.alignment = alignment; + let mut variant_offset = 0; + let mut variant_alignment = 1; - align_offset_to(&mut variant_offset, alignment); - embedded.alignment = variant_offset; + for embedded in &mut variant.embedded { + let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; + embedded.size = size; + embedded.alignment = alignment; + size_alignment_idx += 1; - variant_offset += size; - variant_alignment = variant_alignment.max(alignment); - }, - _ => unreachable!(), - } - } + align_offset_to(&mut variant_offset, alignment); + embedded.alignment = variant_offset; - // Update heap size/alignment - max_size = max_size.max(variant_offset); - max_alignment = max_alignment.max(variant_alignment); - } + variant_offset += size; + variant_alignment = variant_alignment.max(alignment); + } - if max_size != 0 { - // At least one entry lives on the heap - mono_type.heap_size = max_size; - mono_type.heap_alignment = max_alignment; - } - }, - _ => unreachable!(), + max_size = max_size.max(variant_offset); + max_alignment = max_alignment.max(variant_alignment); + } + + if max_size != 0 { + // At least one entry lives on the heap + mono_type.heap_size = max_size; + mono_type.heap_alignment = max_alignment; } } @@ -1676,7 +1786,7 @@ impl TypeTable { self.encountered_types.clear(); } - fn get_memory_layout_or_breadcrumb(&self, ctx: &PassCtx, concrete_type: &ConcreteType) -> MemoryLayoutResult { + fn get_memory_layout_or_breadcrumb(&self, arch: &TargetArch, concrete_type: &ConcreteType) -> MemoryLayoutResult { use ConcreteTypePart as CTP; // Before we do any fancy shenanigans, we need to check if the concrete @@ -1684,7 +1794,7 @@ impl TypeTable { debug_assert!(!concrete_type.parts.is_empty()); let (builtin_size, builtin_alignment) = match concrete_type.parts[0] { CTP::Void => (0, 1), - CTP::Message => ctx.arch.array_size_alignment, + CTP::Message => arch.array_size_alignment, CTP::Bool => (1, 1), CTP::UInt8 => (1, 1), CTP::UInt16 => (2, 2), @@ -1695,11 +1805,11 @@ impl TypeTable { CTP::SInt32 => (4, 4), CTP::SInt64 => (8, 8), CTP::Character => (4, 4), - CTP::String => ctx.arch.string_size_alignment, - CTP::Array => ctx.arch.array_size_alignment, - CTP::Slice => ctx.arch.array_size_alignment, - CTP::Input => ctx.arch.port_size_alignment, - CTP::Output => ctx.arch.port_size_alignment, + CTP::String => arch.string_size_alignment, + CTP::Array => arch.array_size_alignment, + CTP::Slice => arch.array_size_alignment, + CTP::Input => arch.port_size_alignment, + CTP::Output => arch.port_size_alignment, CTP::Instance(definition_id, _) => { // Special case where we explicitly return to simplify the // return case for the builtins. @@ -1710,11 +1820,12 @@ impl TypeTable { // Type has been layed out in memory return MemoryLayoutResult::TypeExists(size, alignment); } else { - return MemoryLayoutResult::PushBreadcrumb(TypeLoopBreadcrumb { + return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{ definition_id, monomorph_idx, next_member: 0, next_embedded: 0, + first_size_alignment_idx: self.size_alignment_stack.len(), }); } }, diff --git a/src/protocol/parser/visitor.rs b/src/protocol/parser/visitor.rs index 7fe2688704ed60a76c8c5e4ec21ac200cc3d2e37..c30b5129c9d77c333103e4eb3f96768645067890 100644 --- a/src/protocol/parser/visitor.rs +++ b/src/protocol/parser/visitor.rs @@ -14,11 +14,25 @@ pub(crate) const STMT_BUFFER_INIT_CAPACITY: usize = 256; pub(crate) const EXPR_BUFFER_INIT_CAPACITY: usize = 256; /// General context structure that is used while traversing the AST. +/// TODO: Revise, visitor abstraction is starting to get in the way of programming pub(crate) struct Ctx<'p> { pub heap: &'p mut Heap, - pub module: &'p mut Module, + pub modules: &'p mut [Module], + pub module_idx: usize, // currently considered module pub symbols: &'p mut SymbolTable, pub types: &'p mut TypeTable, + pub arch: &'p crate::protocol::TargetArch, +} + +impl<'p> Ctx<'p> { + /// Returns module `modules[module_idx]` + pub(crate) fn module(&self) -> &Module { + &self.modules[self.module_idx] + } + + pub(crate) fn module_mut(&mut self) -> &mut Module { + &mut self.modules[self.module_idx] + } } /// Visitor is a generic trait that will fully walk the AST. The default @@ -29,9 +43,10 @@ pub(crate) trait Visitor { // Entry point fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { let mut def_index = 0; + let module_root_id = ctx.modules[ctx.module_idx].root_id; loop { let definition_id = { - let root = &ctx.heap[ctx.module.root_id]; + let root = &ctx.heap[module_root_id]; if def_index >= root.definitions.len() { return Ok(()) } diff --git a/src/protocol/tests/parser_monomorphs.rs b/src/protocol/tests/parser_monomorphs.rs index c38ada857574258cfd6076e26734c3af70b5cec6..a09c038446f1a6a9d08be59b83663768d8265558 100644 --- a/src/protocol/tests/parser_monomorphs.rs +++ b/src/protocol/tests/parser_monomorphs.rs @@ -11,7 +11,8 @@ fn test_struct_monomorphs() { "no polymorph", "struct Integer{ s32 field }" ).for_struct("Integer", |s| { s - .assert_num_monomorphs(0); + .assert_num_monomorphs(1) + .assert_has_monomorph("Integer"); }); Tester::new_single_source_expect_ok( @@ -28,11 +29,11 @@ fn test_struct_monomorphs() { } " ).for_struct("Number", |s| { s - .assert_has_monomorph("s8") - .assert_has_monomorph("s16") - .assert_has_monomorph("s32") - .assert_has_monomorph("s64") + .assert_has_monomorph("Number") .assert_has_monomorph("Number") + .assert_has_monomorph("Number") + .assert_has_monomorph("Number") + .assert_has_monomorph("Number>") .assert_num_monomorphs(5); }).for_function("instantiator", |f| { f .for_variable("a", |v| {v.assert_concrete_type("Number");} ) @@ -49,11 +50,12 @@ fn test_enum_monomorphs() { func do_it() -> s32 { auto a = Answer::Yes; return 0; } " ).for_enum("Answer", |e| { e - .assert_num_monomorphs(0); + .assert_num_monomorphs(1) + .assert_has_monomorph("Answer"); }); // Note for reader: because the enum doesn't actually use the polymorphic - // variable, we expect to have 0 polymorphs: the type only has to be laid + // variable, we expect to have 1 monomorph: the type only has to be laid // out once. Tester::new_single_source_expect_ok( "single polymorph", @@ -68,7 +70,8 @@ fn test_enum_monomorphs() { } " ).for_enum("Answer", |e| { e - .assert_num_monomorphs(0); + .assert_num_monomorphs(1) + .assert_has_monomorph("Answer"); }); } @@ -81,7 +84,8 @@ fn test_union_monomorphs() { func do_it() -> s32 { auto a = Trinary::Value(true); return 0; } " ).for_union("Trinary", |e| { e - .assert_num_monomorphs(0); + .assert_num_monomorphs(1) + .assert_has_monomorph("Trinary"); }); // TODO: Does this do what we want? Or do we expect the embedded monomorph @@ -100,11 +104,12 @@ fn test_union_monomorphs() { } " ).for_union("Result", |e| { e - .assert_num_monomorphs(4) - .assert_has_monomorph("s8;bool") - .assert_has_monomorph("bool;s8") - .assert_has_monomorph("Result;Result") - .assert_has_monomorph("s16;s64"); + .assert_num_monomorphs(5) + .assert_has_monomorph("Result") + .assert_has_monomorph("Result") + .assert_has_monomorph("Result,Result>") + .assert_has_monomorph("Result") + .assert_has_monomorph("Result"); }).for_function("instantiator", |f| { f .for_variable("d", |v| { v .assert_parser_type("auto") diff --git a/src/protocol/tests/parser_types.rs b/src/protocol/tests/parser_types.rs index aa33064b04c56bca438d3c215c915388187a0cc0..0f3ebb06c522e83c8f351694c8b8337779d5f999 100644 --- a/src/protocol/tests/parser_types.rs +++ b/src/protocol/tests/parser_types.rs @@ -11,7 +11,7 @@ fn test_invalid_struct_type_loops() { "struct Foo { Foo foo }" ).error(|e| { e .assert_occurs_at(0, "Foo {") - .assert_msg_has(0, "cyclic type"); + .assert_msg_has(0, "infinitely large type"); }); } @@ -22,7 +22,7 @@ fn test_invalid_union_type_loops() { "union Foo { One(Foo) }" ).error(|e| { e .assert_occurs_at(0, "Foo {") - .assert_msg_has(0, "cyclic type"); + .assert_msg_has(0, "infinitely large type"); }); Tester::new_single_source_expect_err( @@ -33,6 +33,55 @@ fn test_invalid_union_type_loops() { " ).error(|e| { e .assert_occurs_at(0, "UOne") - .assert_msg_has(0, "cyclic type"); + .assert_msg_has(0, "infinitely large type"); + }); +} + +#[test] +fn test_valid_loops() { + // Linked-list like thing + Tester::new_single_source_expect_ok( + "linked list", + " + struct Entry { u32 number, Link next } + union Link { Next(Entry), None } + " + ).for_struct("Entry", |s| { + s.assert_size_alignment("Entry", 24, 8); // [4 'number', 4 'pad', x 'union tag', 8-x 'padding', 8 pointer] + }).for_union("Link", |u| { + u.assert_size_alignment("Link", 16, 8, 24, 8); + }); + + // Tree-like thing, version 1 + Tester::new_single_source_expect_ok( + "tree, optional children", + " + union Option { Some(T), None } + struct Node { + u32 value, + Option left, + Option right, + } + " + ).for_struct("Node", |s| { + s.assert_size_alignment("Node", 40, 8); + }).for_union("Option", |u| { + u.assert_size_alignment("Option", 16, 8, 40, 8); + }); + + // Tree-like thing, version 2 + Tester::new_single_source_expect_ok( + "tree, with left/right link", + " + union Option { Some(T), None } + struct Link { Node left, Node right } + struct Node { u32 value, Option link } + " + ).for_struct("Node", |s| { + s.assert_size_alignment("Node", 24, 8); + }).for_struct("Link", |s| { + s.assert_size_alignment("Link", 48, 8); + }).for_union("Option", |u| { + u.assert_size_alignment("Option", 16, 8, 48, 8); }); } \ No newline at end of file diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 753c1f10d5a66b3962e03d16b9b9eee460627c7f..6407afea6458747e10b2a7e6e534aee15e3aa619 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -5,7 +5,7 @@ use crate::protocol::{ input_source::*, parser::{ Parser, - type_table::{TypeTable, DefinedTypeVariant}, + type_table::*, symbol_table::SymbolTable, token_parsing::*, }, @@ -149,13 +149,17 @@ impl AstOkTester { pub(crate) fn for_struct(self, name: &str, f: F) -> Self { let mut found = false; for definition in self.heap.definitions.iter() { - if let Definition::Struct(definition) = definition { - if definition.identifier.value.as_str() != name { + if let Definition::Struct(ast_definition) = definition { + if ast_definition.identifier.value.as_str() != name { continue; } // Found struct with the same name - let tester = StructTester::new(self.ctx(), definition); + let definition_id = ast_definition.this.upcast(); + let type_entry = self.types.get_base_definition(&definition_id).unwrap(); + let type_definition = type_entry.definition.as_struct(); + + let tester = StructTester::new(self.ctx(), ast_definition, type_definition); f(tester); found = true; break @@ -201,7 +205,9 @@ impl AstOkTester { } // Found union with the same name - let tester = UnionTester::new(self.ctx(), definition); + let definition_id = definition.this.upcast(); + let base_type = self.types.get_base_definition(&definition_id).unwrap(); + let tester = UnionTester::new(self.ctx(), definition, &base_type.definition.as_union()); f(tester); found = true; break; @@ -257,25 +263,26 @@ impl AstOkTester { pub(crate) struct StructTester<'a> { ctx: TestCtx<'a>, - def: &'a StructDefinition, + ast_def: &'a StructDefinition, + type_def: &'a StructType, } impl<'a> StructTester<'a> { - fn new(ctx: TestCtx<'a>, def: &'a StructDefinition) -> Self { - Self{ ctx, def } + fn new(ctx: TestCtx<'a>, ast_def: &'a StructDefinition, type_def: &'a StructType) -> Self { + Self{ ctx, ast_def, type_def } } pub(crate) fn assert_num_fields(self, num: usize) -> Self { assert_eq!( - num, self.def.fields.len(), + num, self.ast_def.fields.len(), "[{}] Expected {} struct fields, but found {} for {}", - self.ctx.test_name, num, self.def.fields.len(), self.assert_postfix() + self.ctx.test_name, num, self.ast_def.fields.len(), self.assert_postfix() ); self } pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self { - let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.def.this.upcast()); + let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.ast_def.this.upcast()); assert!( is_equal, "[{}] Expected {} monomorphs, but got {} for {}", self.ctx.test_name, num, num_encountered, self.assert_postfix() @@ -283,20 +290,32 @@ impl<'a> StructTester<'a> { self } - /// Asserts that a monomorph exist, separate polymorphic variable types by - /// a semicolon. pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self { - let (has_monomorph, serialized) = has_monomorph(self.ctx, self.def.this.upcast(), serialized_monomorph); + let (has_monomorph, serialized) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph); assert!( - has_monomorph, "[{}] Expected to find monomorph {}, but got {} for {}", + has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}", self.ctx.test_name, serialized_monomorph, &serialized, self.assert_postfix() ); self } + pub(crate) fn assert_size_alignment(mut self, monomorph: &str, size: usize, alignment: usize) -> Self { + self = self.assert_has_monomorph(monomorph); + let (mono_idx, _) = has_monomorph(self.ctx, self.ast_def.this.upcast(), monomorph); + let mono_idx = mono_idx.unwrap(); + + let mono = &self.type_def.monomorphs[mono_idx]; + assert!( + mono.size == size && mono.alignment == alignment, + "[{}] Expected (size,alignment) of ({}, {}), but got ({}, {}) for {}", + self.ctx.test_name, size, alignment, mono.size, mono.alignment, self.assert_postfix() + ); + self + } + pub(crate) fn for_field(self, name: &str, f: F) -> Self { // Find field with specified name - for field in &self.def.fields { + for field in &self.ast_def.fields { if field.field.value.as_str() == name { let tester = StructFieldTester::new(self.ctx, field); f(tester); @@ -314,9 +333,9 @@ impl<'a> StructTester<'a> { fn assert_postfix(&self) -> String { let mut v = String::new(); v.push_str("Struct{ name: "); - v.push_str(self.def.identifier.value.as_str()); + v.push_str(self.ast_def.identifier.value.as_str()); v.push_str(", fields: ["); - for (field_idx, field) in self.def.fields.iter().enumerate() { + for (field_idx, field) in self.ast_def.fields.iter().enumerate() { if field_idx != 0 { v.push_str(", "); } v.push_str(field.field.value.as_str()); } @@ -384,7 +403,7 @@ impl<'a> EnumTester<'a> { pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self { let (has_monomorph, serialized) = has_monomorph(self.ctx, self.def.this.upcast(), serialized_monomorph); assert!( - has_monomorph, "[{}] Expected to find monomorph {}, but got {} for {}", + has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}", self.ctx.test_name, serialized_monomorph, serialized, self.assert_postfix() ); self @@ -406,25 +425,26 @@ impl<'a> EnumTester<'a> { pub(crate) struct UnionTester<'a> { ctx: TestCtx<'a>, - def: &'a UnionDefinition, + ast_def: &'a UnionDefinition, + type_def: &'a UnionType, } impl<'a> UnionTester<'a> { - fn new(ctx: TestCtx<'a>, def: &'a UnionDefinition) -> Self { - Self{ ctx, def } + fn new(ctx: TestCtx<'a>, ast_def: &'a UnionDefinition, type_def: &'a UnionType) -> Self { + Self{ ctx, ast_def, type_def } } pub(crate) fn assert_num_variants(self, num: usize) -> Self { assert_eq!( - num, self.def.variants.len(), + num, self.ast_def.variants.len(), "[{}] Expected {} union variants, but found {} for {}", - self.ctx.test_name, num, self.def.variants.len(), self.assert_postfix() + self.ctx.test_name, num, self.ast_def.variants.len(), self.assert_postfix() ); self } pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self { - let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.def.this.upcast()); + let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.ast_def.this.upcast()); assert!( is_equal, "[{}] Expected {} monomorphs, but got {} for {}", self.ctx.test_name, num, num_encountered, self.assert_postfix() @@ -433,20 +453,40 @@ impl<'a> UnionTester<'a> { } pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self { - let (has_monomorph, serialized) = has_monomorph(self.ctx, self.def.this.upcast(), serialized_monomorph); + let (has_monomorph, serialized) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph); assert!( - has_monomorph, "[{}] Expected to find monomorph {}, but got {} for {}", + has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}", self.ctx.test_name, serialized_monomorph, serialized, self.assert_postfix() ); self } + pub(crate) fn assert_size_alignment( + mut self, serialized_monomorph: &str, + stack_size: usize, stack_alignment: usize, heap_size: usize, heap_alignment: usize + ) -> Self { + self = self.assert_has_monomorph(serialized_monomorph); + let (mono_idx, _) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph); + let mono_idx = mono_idx.unwrap(); + let mono = &self.type_def.monomorphs[mono_idx]; + assert!( + stack_size == mono.stack_size && stack_alignment == mono.stack_alignment && + heap_size == mono.heap_size && heap_alignment == mono.heap_alignment, + "[{}] Expected (stack | heap) (size, alignment) of ({}, {} | {}, {}), but got ({}, {} | {}, {}) for {}", + self.ctx.test_name, + stack_size, stack_alignment, heap_size, heap_alignment, + mono.stack_size, mono.stack_alignment, mono.heap_size, mono.heap_alignment, + self.assert_postfix() + ); + self + } + fn assert_postfix(&self) -> String { let mut v = String::new(); v.push_str("Union{ name: "); - v.push_str(self.def.identifier.value.as_str()); + v.push_str(self.ast_def.identifier.value.as_str()); v.push_str(", variants: ["); - for (variant_idx, variant) in self.def.variants.iter().enumerate() { + for (variant_idx, variant) in self.ast_def.variants.iter().enumerate() { if variant_idx != 0 { v.push_str(", "); } v.push_str(variant.identifier.value.as_str()); } @@ -868,56 +908,51 @@ fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionI (num_on_type == num, num_on_type) } -fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (bool, String) { +fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (Option, String) { use DefinedTypeVariant::*; let type_def = ctx.types.get_base_definition(&definition_id).unwrap(); // Note: full_buffer is just for error reporting let mut full_buffer = String::new(); - let mut has_match = false; - - let serialize_monomorph = |monomorph: &Vec| -> String { - let mut buffer = String::new(); - for (element_idx, element) in monomorph.iter().enumerate() { - if element_idx != 0 { - buffer.push(';'); - } - serialize_concrete_type(&mut buffer, ctx.heap, definition_id, element); - } - - buffer - }; + let mut has_match = None; full_buffer.push('['); - let mut append_to_full_buffer = |buffer: String| { + let mut append_to_full_buffer = |concrete_type: &ConcreteType, mono_idx: usize| { if full_buffer.len() != 1 { full_buffer.push_str(", "); } full_buffer.push('"'); - full_buffer.push_str(&buffer); + + let first_idx = full_buffer.len(); + serialize_concrete_type(&mut full_buffer, ctx.heap, definition_id, concrete_type); + if &full_buffer[first_idx..] == serialized_monomorph { + has_match = Some(mono_idx); + } + full_buffer.push('"'); }; match &type_def.definition { - Enum(_) | Union(_) | Struct(_) => { - let monomorphs = type_def.definition.data_monomorphs(); - for monomorph in monomorphs.iter() { - let buffer = serialize_monomorph(&monomorph.poly_args); - if buffer == serialized_monomorph { - has_match = true; - } - append_to_full_buffer(buffer); + Enum(definition) => { + for (mono_idx, mono) in definition.monomorphs.iter().enumerate() { + append_to_full_buffer(&mono.concrete_type, mono_idx); + } + }, + Union(definition) => { + for (mono_idx, mono) in definition.monomorphs.iter().enumerate() { + append_to_full_buffer(&mono.concrete_type, mono_idx); + } + }, + Struct(definition) => { + for (mono_idx, mono) in definition.monomorphs.iter().enumerate() { + append_to_full_buffer(&mono.concrete_type, mono_idx); } }, Function(_) | Component(_) => { let monomorphs = type_def.definition.procedure_monomorphs(); - for monomorph in monomorphs.iter() { - let buffer = serialize_monomorph(&monomorph.poly_args); - if buffer == serialized_monomorph { - has_match = true; - } - append_to_full_buffer(buffer); + for (mono_idx, mono) in monomorphs.iter().enumerate() { + append_to_full_buffer(&mono.concrete_type, mono_idx); } } } @@ -1051,7 +1086,9 @@ fn serialize_concrete_type(buffer: &mut String, heap: &Heap, def: DefinitionId, idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); buffer.push('>'); }, - CTP::Instance(definition_id, num_sub) => { + CTP::Instance(definition_id, num_sub) | + CTP::Function(definition_id, num_sub) | + CTP::Component(definition_id, num_sub) => { let definition_name = heap[*definition_id].identifier(); buffer.push_str(definition_name.value.as_str()); if *num_sub != 0 { @@ -1062,7 +1099,7 @@ fn serialize_concrete_type(buffer: &mut String, heap: &Heap, def: DefinitionId, } buffer.push('>'); } - } + }, } idx