From 5826495b16093a2991e3131d967397a75f30b1d2 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Tue, 22 Oct 2024 18:28:02 +0200 Subject: [PATCH] Start refactoring to get rid of WorkingOnResolvers Right now the typechecker needs a mutable reference to the list of modules, as it was changing the typ values within. With the change to HM we don't actually need this, as we can apply all newly formed types in one fell swoop. Factoring out the bit that actually applies the new types is the start of this. Do more tomorrow --- src/flattening/typechecking.rs | 147 ++++++++++++++++++--------------- src/typing/abstract_type.rs | 7 +- 2 files changed, 82 insertions(+), 72 deletions(-) diff --git a/src/flattening/typechecking.rs b/src/flattening/typechecking.rs index a18f88a..bcdce08 100644 --- a/src/flattening/typechecking.rs +++ b/src/flattening/typechecking.rs @@ -32,11 +32,11 @@ pub fn typecheck_all_modules(linker: &mut Linker) { let mut context = TypeCheckingContext { errors: name_resolver.errors, type_checker: TypeUnifier::new( - types, &modules.working_on.link_info.template_arguments, name_resolver.errors, &modules.working_on.link_info.type_variable_alloc ), + types, constants, runtime_condition_stack: Vec::new(), modules, @@ -44,6 +44,7 @@ pub fn typecheck_all_modules(linker: &mut Linker) { context.typecheck(); context.find_unused_variables(); + apply_types(&mut context.type_checker, context.modules.working_on, context.errors, &context.types); }, ); @@ -60,6 +61,7 @@ struct ConditionStackElem { struct TypeCheckingContext<'l, 'errs> { modules: WorkingOnResolver<'l, 'errs, ModuleUUIDMarker, Module>, type_checker: TypeUnifier<'l, 'errs>, + types: Resolver<'l, 'errs, TypeUUIDMarker, StructType>, constants: Resolver<'l, 'errs, ConstantUUIDMarker, NamedConstant>, errors: &'errs ErrorCollector<'l>, runtime_condition_stack: Vec, @@ -495,6 +497,7 @@ impl<'l, 'errs> TypeCheckingContext<'l, 'errs> { Some((last.domain, last.span)) } + /// Should be followed up by a [apply_types] call to actually apply all the checked types. fn typecheck(&mut self) { for (_id, port) in &self.modules.working_on.ports { let Instruction::Declaration(decl) = @@ -509,73 +512,8 @@ impl<'l, 'errs> TypeCheckingContext<'l, 'errs> { self.control_flow_visit_instruction(elem_id); self.typecheck_visit_instruction(elem_id); } - - // Set the remaining domain variables that aren't associated with a module port. - // We just find domain IDs that haven't been - let mut leftover_domain_alloc = UUIDAllocator::new_start_from(self.modules.working_on.domain_names.get_next_alloc_id()); - for d in self.type_checker.domain_substitutor.iter() { - if d.get().is_none() { - assert!(d.set(DomainType::Physical(leftover_domain_alloc.alloc())).is_ok()); - } - } - - // Assign names to all of the domains in this module - self.modules.working_on.domains = leftover_domain_alloc.into_range().map(|id| DomainInfo { - name: { - if let Some(name) = self.modules.working_on.domain_names.get(id) { - name.clone() - } else { - format!("domain_{}", id.get_hidden_value()) - } - } - }); - - // Post type application. Solidify types and flag any remaining AbstractType::Unknown - for (_id, inst) in self.modules.working_on.instructions.iter_mut() { - match inst { - Instruction::Wire(w) => self.type_checker.finalize_type(&mut w.typ, w.span), - Instruction::Declaration(decl) => self.type_checker.finalize_type(&mut decl.typ, decl.name_span), - Instruction::Write(Write { to_type, to_span, .. }) => self.type_checker.finalize_type(to_type, *to_span), - // IDK TODO re-add with new submodule domain system - Instruction::SubModule(sm) => { - for (_domain_id_in_submodule, domain_assigned_to_it_here) in &mut sm.local_interface_domains { - use self::HindleyMilner; - domain_assigned_to_it_here.fully_substitute(&self.type_checker.domain_substitutor).unwrap(); - } - } - _other => {} - } - } - - for FailedUnification{mut found, mut expected, span, context} in self.type_checker.type_substitutor.extract_errors() { - // Not being able to fully substitute is not an issue. We just display partial types - let _ = found.fully_substitute(&self.type_checker.type_substitutor); - let _ = expected.fully_substitute(&self.type_checker.type_substitutor); - - let expected_name = expected.to_string(&self.type_checker.linker_types, &self.type_checker.template_type_names); - let found_name = found.to_string(&self.type_checker.linker_types, &self.type_checker.template_type_names); - self.errors.error(span, format!("Typing Error: {context} expects a {expected_name} but was given a {found_name}")); - - assert!( - expected_name != found_name, - "{expected_name} != {found_name}" - ); - } - for FailedUnification{mut found, mut expected, span, context} in self.type_checker.domain_substitutor.extract_errors() { - found.fully_substitute(&self.type_checker.domain_substitutor).unwrap(); - expected.fully_substitute(&self.type_checker.domain_substitutor).unwrap(); - - let expected_name = format!("{expected:?}"); - let found_name = format!("{found:?}"); - self.errors.error(span, format!("Domain error: Attempting to combine domains {found_name} and {expected_name} in {context}")); - - assert!( - expected_name != found_name, - "{expected_name} != {found_name}" - ); - } } - + /* ==== Additional Warnings ==== */ @@ -664,3 +602,78 @@ impl<'l, 'errs> TypeCheckingContext<'l, 'errs> { instruction_fanins } } + + +// ====== Free functions for actually applying the result of type checking ====== + +pub fn apply_types<'linker, 'errs>( + type_checker: &mut TypeUnifier, + working_on: &mut Module, + errors: &ErrorCollector, + types: &Resolver<'linker, 'errs, TypeUUIDMarker, StructType> +) { + // Set the remaining domain variables that aren't associated with a module port. + // We just find domain IDs that haven't been + let mut leftover_domain_alloc = UUIDAllocator::new_start_from(working_on.domain_names.get_next_alloc_id()); + for d in type_checker.domain_substitutor.iter() { + if d.get().is_none() { + assert!(d.set(DomainType::Physical(leftover_domain_alloc.alloc())).is_ok()); + } + } + + // Assign names to all of the domains in this module + working_on.domains = leftover_domain_alloc.into_range().map(|id| DomainInfo { + name: { + if let Some(name) = working_on.domain_names.get(id) { + name.clone() + } else { + format!("domain_{}", id.get_hidden_value()) + } + } + }); + + // Post type application. Solidify types and flag any remaining AbstractType::Unknown + for (_id, inst) in working_on.instructions.iter_mut() { + match inst { + Instruction::Wire(w) => type_checker.finalize_type(types, &mut w.typ, w.span), + Instruction::Declaration(decl) => type_checker.finalize_type(types, &mut decl.typ, decl.name_span), + Instruction::Write(Write { to_type, to_span, .. }) => type_checker.finalize_type(types, to_type, *to_span), + // IDK TODO re-add with new submodule domain system + Instruction::SubModule(sm) => { + for (_domain_id_in_submodule, domain_assigned_to_it_here) in &mut sm.local_interface_domains { + use self::HindleyMilner; + domain_assigned_to_it_here.fully_substitute(&type_checker.domain_substitutor).unwrap(); + } + } + _other => {} + } + } + + for FailedUnification{mut found, mut expected, span, context} in type_checker.type_substitutor.extract_errors() { + // Not being able to fully substitute is not an issue. We just display partial types + let _ = found.fully_substitute(&type_checker.type_substitutor); + let _ = expected.fully_substitute(&type_checker.type_substitutor); + + let expected_name = expected.to_string(types, &type_checker.template_type_names); + let found_name = found.to_string(types, &type_checker.template_type_names); + errors.error(span, format!("Typing Error: {context} expects a {expected_name} but was given a {found_name}")); + + assert!( + expected_name != found_name, + "{expected_name} != {found_name}" + ); + } + for FailedUnification{mut found, mut expected, span, context} in type_checker.domain_substitutor.extract_errors() { + found.fully_substitute(&type_checker.domain_substitutor).unwrap(); + expected.fully_substitute(&type_checker.domain_substitutor).unwrap(); + + let expected_name = format!("{expected:?}"); + let found_name = format!("{found:?}"); + errors.error(span, format!("Domain error: Attempting to combine domains {found_name} and {expected_name} in {context}")); + + assert!( + expected_name != found_name, + "{expected_name} != {found_name}" + ); + } +} diff --git a/src/typing/abstract_type.rs b/src/typing/abstract_type.rs index fc93057..537a36d 100644 --- a/src/typing/abstract_type.rs +++ b/src/typing/abstract_type.rs @@ -83,7 +83,6 @@ pub struct FullType { /// /// 'x U 'y -> 'x = 'y pub struct TypeUnifier<'linker, 'errs> { - pub linker_types: Resolver<'linker, 'errs, TypeUUIDMarker, StructType>, pub template_type_names: FlatAlloc, errors: &'errs ErrorCollector<'linker>, pub type_substitutor: TypeSubstitutor, @@ -92,13 +91,11 @@ pub struct TypeUnifier<'linker, 'errs> { impl<'linker, 'errs> TypeUnifier<'linker, 'errs> { pub fn new( - linker_types: Resolver<'linker, 'errs, TypeUUIDMarker, StructType>, template_inputs: &TemplateInputs, errors: &'errs ErrorCollector<'linker>, typing_alloc: &TypingAllocator ) -> Self { Self { - linker_types, template_type_names: map_to_type_names(template_inputs), errors, type_substitutor: TypeSubstitutor::init(&typing_alloc.type_variable_alloc), @@ -327,12 +324,12 @@ impl<'linker, 'errs> TypeUnifier<'linker, 'errs> { self.typecheck_domain_from_to(&found.domain, &expected.domain, span, context); } - pub fn finalize_type(&mut self, typ: &mut FullType, span: Span) { + pub fn finalize_type(&mut self, types: &Resolver<'_, '_, TypeUUIDMarker, StructType>, typ: &mut FullType, span: Span) { use super::type_inference::HindleyMilner; typ.domain.fully_substitute(&self.domain_substitutor).unwrap(); if typ.typ.fully_substitute(&self.type_substitutor).is_err() { - let typ_as_string = typ.typ.to_string(&self.linker_types, &self.template_type_names); + let typ_as_string = typ.typ.to_string(types, &self.template_type_names); self.errors.error(span, format!("Could not fully figure out the type of this object. {typ_as_string}")); } }