Skip to content

Commit

Permalink
Start refactoring to get rid of WorkingOnResolvers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
VonTum committed Oct 22, 2024
1 parent 0133780 commit 5826495
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 72 deletions.
147 changes: 80 additions & 67 deletions src/flattening/typechecking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ 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,
};

context.typecheck();
context.find_unused_variables();
apply_types(&mut context.type_checker, context.modules.working_on, context.errors, &context.types);
},
);

Expand All @@ -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<ConditionStackElem>,
Expand Down Expand Up @@ -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) =
Expand All @@ -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 ====
*/
Expand Down Expand Up @@ -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}"
);
}
}
7 changes: 2 additions & 5 deletions src/typing/abstract_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, TemplateIDMarker>,
errors: &'errs ErrorCollector<'linker>,
pub type_substitutor: TypeSubstitutor<AbstractType, TypeVariableIDMarker>,
Expand All @@ -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),
Expand Down Expand Up @@ -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}"));
}
}
Expand Down

0 comments on commit 5826495

Please sign in to comment.