diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index efa09d4dde..60d3906078 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1381,6 +1381,265 @@ fn instantiate_rigids_help( var } +fn deep_copy_var_to_help( + source: &Subs, + target: &mut Subs, + max_rank: Rank, + pools: &mut Pools, + var: Variable, +) -> Variable { + use roc_types::subs::Content::*; + use roc_types::subs::FlatType::*; + + let desc = source.get_without_compacting(var); + + if let Some(copy) = desc.copy.into_variable() { + return copy; + } else if desc.rank != Rank::NONE { + panic!( + "about to return a variable in source, but should only return variables from target" + ); + return var; + } + + let make_descriptor = |content| Descriptor { + content, + rank: max_rank, + mark: Mark::NONE, + copy: OptVariable::NONE, + }; + + let content = desc.content; + let copy = target.fresh(make_descriptor(content.clone())); + + pools.get_mut(max_rank).push(copy); + + // Link the original variable to the new variable. This lets us + // avoid making multiple copies of the variable we are instantiating. + // + // Need to do this before recursively copying to avoid looping. + source.set( + var, + Descriptor { + content: content.clone(), + rank: desc.rank, + mark: Mark::NONE, + copy: copy.into(), + }, + ); + + // Now we recursively copy the content of the variable. + // We have already marked the variable as copied, so we + // will not repeat this work or crawl this variable again. + match content { + Structure(flat_type) => { + let new_flat_type = match flat_type { + Apply(symbol, args) => { + let mut new_arg_vars = Vec::with_capacity(args.len()); + + for index in args.into_iter() { + let var = source[index]; + let copy_var = deep_copy_var_to_help(source, target, max_rank, pools, var); + new_arg_vars.push(copy_var); + } + + let arg_vars = VariableSubsSlice::insert_into_subs(target, new_arg_vars); + + Apply(symbol, arg_vars) + } + + Func(arg_vars, closure_var, ret_var) => { + let new_ret_var = + deep_copy_var_to_help(source, target, max_rank, pools, ret_var); + let new_closure_var = + deep_copy_var_to_help(source, target, max_rank, pools, closure_var); + + let mut new_arg_vars = Vec::with_capacity(arg_vars.len()); + + for index in arg_vars.into_iter() { + let var = source[index]; + let copy_var = deep_copy_var_to_help(source, target, max_rank, pools, var); + new_arg_vars.push(copy_var); + } + + let arg_vars = VariableSubsSlice::insert_into_subs(target, new_arg_vars); + + Func(arg_vars, new_closure_var, new_ret_var) + } + + same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same, + + Record(fields, ext_var) => { + let record_fields = { + let mut new_vars = Vec::with_capacity(fields.len()); + + for index in fields.iter_variables() { + let var = source[index]; + let copy_var = + deep_copy_var_to_help(source, target, max_rank, pools, var); + + new_vars.push(copy_var); + } + + let field_names_start = target.field_names.len() as u32; + let variables_start = target.variables.len() as u32; + let field_types_start = target.record_fields.len() as u32; + + let mut length = 0; + + for ((i1, _, i3), var) in fields.iter_all().zip(new_vars) { + let record_field = source[i3].map(|_| var); + + target.field_names.push(source[i1].clone()); + target.record_fields.push(record_field.map(|_| ())); + target.variables.push(*record_field.as_inner()); + + length += 1; + } + + RecordFields { + length, + field_names_start, + variables_start, + field_types_start, + } + }; + + Record( + record_fields, + deep_copy_var_to_help(source, target, max_rank, pools, ext_var), + ) + } + + TagUnion(tags, ext_var) => { + let mut new_variable_slices = Vec::with_capacity(tags.len()); + + let mut new_variables = Vec::new(); + for index in tags.variables() { + let slice = source[index]; + for var_index in slice { + let var = source[var_index]; + let new_var = + deep_copy_var_to_help(source, target, max_rank, pools, var); + new_variables.push(new_var); + } + + let new_slice = + VariableSubsSlice::insert_into_subs(target, new_variables.drain(..)); + + new_variable_slices.push(new_slice); + } + + let new_variables = { + let start = target.variable_slices.len() as u32; + let length = new_variable_slices.len() as u16; + target.variable_slices.extend(new_variable_slices); + + SubsSlice::new(start, length) + }; + + let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables); + + let new_ext = deep_copy_var_to_help(source, target, max_rank, pools, ext_var); + TagUnion(union_tags, new_ext) + } + + FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion( + tag_name, + symbol, + deep_copy_var_to_help(source, target, max_rank, pools, ext_var), + ), + + RecursiveTagUnion(rec_var, tags, ext_var) => { + let mut new_variable_slices = Vec::with_capacity(tags.len()); + + let mut new_variables = Vec::new(); + for index in tags.variables() { + let slice = source[index]; + for var_index in slice { + let var = source[var_index]; + let new_var = + deep_copy_var_to_help(source, target, max_rank, pools, var); + new_variables.push(new_var); + } + + let new_slice = + VariableSubsSlice::insert_into_subs(target, new_variables.drain(..)); + + new_variable_slices.push(new_slice); + } + + let new_variables = { + let start = target.variable_slices.len() as u32; + let length = new_variable_slices.len() as u16; + target.variable_slices.extend(new_variable_slices); + + SubsSlice::new(start, length) + }; + + let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables); + + let new_ext = deep_copy_var_to_help(source, target, max_rank, pools, ext_var); + let new_rec_var = + deep_copy_var_to_help(source, target, max_rank, pools, rec_var); + + RecursiveTagUnion(new_rec_var, union_tags, new_ext) + } + }; + + target.set(copy, make_descriptor(Structure(new_flat_type))); + + copy + } + + FlexVar(_) | Error => copy, + + RecursionVar { + opt_name, + structure, + } => { + let new_structure = deep_copy_var_to_help(source, target, max_rank, pools, structure); + + target.set( + copy, + make_descriptor(RecursionVar { + opt_name, + structure: new_structure, + }), + ); + + copy + } + + RigidVar(name) => { + target.set(copy, make_descriptor(FlexVar(Some(name)))); + + copy + } + + Alias(symbol, mut args, real_type_var) => { + let mut new_vars = Vec::with_capacity(args.variables().len()); + + for var_index in args.variables() { + let var = source[var_index]; + let new_var = deep_copy_var_to_help(source, target, max_rank, pools, var); + + new_vars.push(new_var); + } + + args.replace_variables(target, new_vars); + + let new_real_type_var = + deep_copy_var_to_help(source, target, max_rank, pools, real_type_var); + let new_content = Alias(symbol, args, new_real_type_var); + + target.set(copy, make_descriptor(new_content)); + + copy + } + } +} + fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable { let copy = deep_copy_var_help(subs, rank, pools, var); diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index 420ac0c322..55d1b51c05 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -9,10 +9,12 @@ use ven_ena::unify::{InPlace, Snapshot, UnificationTable, UnifyKey}; // if your changes cause this number to go down, great! // please change it to the lower number. // if it went up, maybe check that the change is really required -static_assertions::assert_eq_size!([u8; 48], Descriptor); -static_assertions::assert_eq_size!([u8; 32], Content); -static_assertions::assert_eq_size!([u8; 24], FlatType); -static_assertions::assert_eq_size!([u8; 48], Problem); +static_assertions::assert_eq_size!([u8; 6 * 8], Descriptor); +static_assertions::assert_eq_size!([u8; 4 * 8], Content); +static_assertions::assert_eq_size!([u8; 3 * 8], FlatType); +static_assertions::assert_eq_size!([u8; 6 * 8], Problem); +static_assertions::assert_eq_size!([u8; 12], UnionTags); +static_assertions::assert_eq_size!([u8; 2 * 8], RecordFields); #[derive(Clone, Copy, Hash, PartialEq, Eq)] pub struct Mark(i32); @@ -1326,6 +1328,12 @@ impl From for Descriptor { } } +static_assertions::assert_eq_size!([u8; 4 * 8], Content); +static_assertions::assert_eq_size!([u8; 4 * 8], (Variable, Option)); +static_assertions::assert_eq_size!([u8; 3 * 8], (Symbol, AliasVariables, Variable)); +static_assertions::assert_eq_size!([u8; 12], AliasVariables); +static_assertions::assert_eq_size!([u8; 3 * 8], FlatType); + #[derive(Clone, Debug)] pub enum Content { /// A type variable which the user did not name in an annotation, @@ -1466,6 +1474,8 @@ impl Content { } } +static_assertions::assert_eq_size!([u8; 3 * 8], FlatType); + #[derive(Clone, Debug)] pub enum FlatType { Apply(Symbol, VariableSubsSlice),