diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index f75826c1a5..b1e54e5481 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -9,7 +9,7 @@ use roc_mono::ir::ProcLayout; use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant}; use roc_parse::ast::{AssignedField, Expr, StrLiteral}; use roc_region::all::{Located, Region}; -use roc_types::subs::{Content, FlatType, RecordFields, Subs, Variable}; +use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable}; struct Env<'a, 'env> { arena: &'a Bump, @@ -162,19 +162,27 @@ fn jit_to_ast_help<'a>( Content::Structure(FlatType::TagUnion(tags, _)) => { debug_assert_eq!(tags.len(), 1); - let (tag_name, payload_vars) = tags.iter().next().unwrap(); + let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags); Ok(single_tag_union_to_ast( env, ptr, field_layouts, - tag_name.clone(), + tag_name, payload_vars, )) } - Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => Ok( - single_tag_union_to_ast(env, ptr, field_layouts, *tag_name.clone(), &[]), - ), + Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => { + let tag_name = &env.subs[*tag_name]; + + Ok(single_tag_union_to_ast( + env, + ptr, + field_layouts, + tag_name, + &[], + )) + } Content::Structure(FlatType::Func(_, _, _)) => { // a function with a struct as the closure environment Err(ToAstProblem::FunctionLayout) @@ -206,8 +214,13 @@ fn jit_to_ast_help<'a>( Content::Structure(FlatType::TagUnion(tags, _)) => { debug_assert_eq!(union_layouts.len(), tags.len()); - let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = - tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect(); + let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = tags + .unsorted_iterator(env.subs, Variable::EMPTY_TAG_UNION) + .map(|(a, b)| (a.clone(), b.to_vec())) + .collect(); + + let tags_map: roc_collections::all::MutMap<_, _> = + tags_vec.iter().cloned().collect(); let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs); @@ -262,7 +275,7 @@ fn jit_to_ast_help<'a>( let loc_tag_expr = &*env.arena.alloc(Located::at_zero(tag_expr)); - let variables = &tags[tag_name]; + let variables = &tags_map[tag_name]; debug_assert_eq!(arg_layouts.len(), variables.len()); @@ -295,7 +308,7 @@ fn jit_to_ast_help<'a>( let loc_tag_expr = &*env.arena.alloc(Located::at_zero(tag_expr)); - let variables = &tags[tag_name]; + let variables = &tags_map[tag_name]; // because the arg_layouts include the tag ID, it is one longer debug_assert_eq!( @@ -436,11 +449,12 @@ fn ptr_to_ast<'a>( Content::Structure(FlatType::TagUnion(tags, _)) => { debug_assert_eq!(tags.len(), 1); - let (tag_name, payload_vars) = tags.iter().next().unwrap(); - single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars) + let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags); + single_tag_union_to_ast(env, ptr, field_layouts, tag_name, payload_vars) } Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => { - single_tag_union_to_ast(env, ptr, field_layouts, *tag_name.clone(), &[]) + let tag_name = &env.subs[*tag_name]; + single_tag_union_to_ast(env, ptr, field_layouts, tag_name, &[]) } Content::Structure(FlatType::EmptyRecord) => { struct_to_ast(env, ptr, &[], RecordFields::empty()) @@ -511,14 +525,14 @@ fn single_tag_union_to_ast<'a>( env: &Env<'a, '_>, ptr: *const u8, field_layouts: &'a [Layout<'a>], - tag_name: TagName, + tag_name: &TagName, payload_vars: &[Variable], ) -> Expr<'a> { debug_assert_eq!(field_layouts.len(), payload_vars.len()); let arena = env.arena; - let tag_expr = tag_name_to_expr(env, &tag_name); + let tag_expr = tag_name_to_expr(env, tag_name); let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr)); @@ -639,6 +653,36 @@ fn struct_to_ast<'a>( } } +fn unpack_single_element_tag_union(subs: &Subs, tags: UnionTags) -> (&TagName, &[Variable]) { + let (tag_name_index, payload_vars_index) = tags.iter_all().next().unwrap(); + + let tag_name = &subs[tag_name_index]; + let subs_slice = subs[payload_vars_index].as_subs_slice(); + let payload_vars = subs.get_subs_slice(*subs_slice); + + (tag_name, payload_vars) +} + +fn unpack_two_element_tag_union( + subs: &Subs, + tags: UnionTags, +) -> (&TagName, &[Variable], &TagName, &[Variable]) { + let mut it = tags.iter_all(); + let (tag_name_index, payload_vars_index) = it.next().unwrap(); + + let tag_name1 = &subs[tag_name_index]; + let subs_slice = subs[payload_vars_index].as_subs_slice(); + let payload_vars1 = subs.get_subs_slice(*subs_slice); + + let (tag_name_index, payload_vars_index) = it.next().unwrap(); + + let tag_name2 = &subs[tag_name_index]; + let subs_slice = subs[payload_vars_index].as_subs_slice(); + let payload_vars2 = subs.get_subs_slice(*subs_slice); + + (tag_name1, payload_vars1, tag_name2, payload_vars2) +} + fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a> { use Content::*; @@ -685,7 +729,7 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a } } FlatType::TagUnion(tags, _) if tags.len() == 1 => { - let (tag_name, payload_vars) = tags.iter().next().unwrap(); + let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags); let loc_tag_expr = { let tag_name = &tag_name.as_ident_str(env.interns, env.home); @@ -720,9 +764,8 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a Expr::Apply(loc_tag_expr, payload, CalledVia::Space) } FlatType::TagUnion(tags, _) if tags.len() == 2 => { - let mut tags_iter = tags.iter(); - let (tag_name_1, payload_vars_1) = tags_iter.next().unwrap(); - let (tag_name_2, payload_vars_2) = tags_iter.next().unwrap(); + let (tag_name_1, payload_vars_1, tag_name_2, payload_vars_2) = + unpack_two_element_tag_union(env.subs, *tags); debug_assert!(payload_vars_1.is_empty()); debug_assert!(payload_vars_2.is_empty()); @@ -801,7 +844,7 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> } } FlatType::TagUnion(tags, _) if tags.len() == 1 => { - let (tag_name, payload_vars) = tags.iter().next().unwrap(); + let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags); let loc_tag_expr = { let tag_name = &tag_name.as_ident_str(env.interns, env.home); @@ -839,8 +882,10 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> // anything with fewer tags is not a byte debug_assert!(tags.len() > 2); - let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = - tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect(); + let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = tags + .unsorted_iterator(env.subs, Variable::EMPTY_TAG_UNION) + .map(|(a, b)| (a.clone(), b.to_vec())) + .collect(); let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs); @@ -923,7 +968,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E // This was a single-tag union that got unwrapped at runtime. debug_assert_eq!(tags.len(), 1); - let (tag_name, payload_vars) = tags.iter().next().unwrap(); + let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags); // If this tag union represents a number, skip right to // returning tis as an Expr::Num diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 3536bf1cdb..cbb009956a 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -15,7 +15,7 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_problem::can::RuntimeError; use roc_region::all::{Located, Region}; use roc_types::solved_types::SolvedType; -use roc_types::subs::{Content, FlatType, Subs, SubsSlice, Variable}; +use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; @@ -4346,7 +4346,7 @@ fn convert_tag_union<'a>( #[allow(clippy::too_many_arguments)] fn tag_union_to_function<'a>( env: &mut Env<'a, '_>, - argument_variables: SubsSlice, + argument_variables: VariableSubsSlice, return_variable: Variable, tag_name: TagName, proc_symbol: Symbol, diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 3b308b6c2c..2b2519c36d 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -4,8 +4,8 @@ use bumpalo::Bump; use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; -use roc_types::subs::{Content, FlatType, Subs, Variable}; -use roc_types::types::RecordField; +use roc_types::subs::{Content, FlatType, RecordFields, Subs, Variable}; +use roc_types::types::{gather_fields_unsorted_iter, RecordField}; use std::collections::HashMap; use ven_pretty::{DocAllocator, DocBuilder}; @@ -1250,13 +1250,27 @@ fn layout_from_flat_type<'a>( TagUnion(tags, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); - Ok(layout_from_tag_union(arena, tags, subs)) + let mut new_tags = MutMap::default(); + + for (tag_index, index) in tags.iter_all() { + let tag = subs[tag_index].clone(); + let slice = subs[index]; + let mut new_vars = std::vec::Vec::new(); + for var_index in slice { + let var = subs[var_index]; + new_vars.push(var); + } + + new_tags.insert(tag, new_vars); + } + + Ok(layout_from_tag_union(arena, new_tags, subs)) } FunctionOrTagUnion(tag_name, _, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); let mut tags = MutMap::default(); - tags.insert(*tag_name, vec![]); + tags.insert(subs[tag_name].clone(), vec![]); Ok(layout_from_tag_union(arena, tags, subs)) } @@ -1358,26 +1372,27 @@ pub fn sort_record_fields<'a>( var: Variable, subs: &Subs, ) -> Vec<'a, (Lowercase, Variable, Result, Layout<'a>>)> { - let mut fields_map = MutMap::default(); - let mut env = Env { arena, subs, seen: MutSet::default(), }; - match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) { - Ok(()) | Err((_, Content::FlexVar(_))) => sort_record_fields_help(&mut env, fields_map), - Err(other) => panic!("invalid content in record variable: {:?}", other), - } + let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var); + + let it = it + .into_iter() + .map(|(field, field_type)| (field.clone(), field_type)); + + sort_record_fields_help(&mut env, it) } fn sort_record_fields_help<'a>( env: &mut Env<'a, '_>, - fields_map: MutMap>, + fields_map: impl Iterator)>, ) -> Vec<'a, (Lowercase, Variable, Result, Layout<'a>>)> { // Sort the fields by label - let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), env.arena); + let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena); for (label, field) in fields_map { let var = match field { @@ -1392,10 +1407,7 @@ fn sort_record_fields_help<'a>( let layout = Layout::from_var(env, var).expect("invalid layout from var"); - // Drop any zero-sized fields like {} - if !layout.is_dropped_because_empty() { - sorted_fields.push((label, var, Ok(layout))); - } + sorted_fields.push((label, var, Ok(layout))); } sorted_fields.sort_by( diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 856771a2d3..ec3250e745 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -5,10 +5,11 @@ use roc_module::symbol::Symbol; use roc_region::all::{Located, Region}; use roc_types::solved_types::Solved; use roc_types::subs::{ - Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, SubsSlice, Variable, + Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, SubsIndex, + Variable, VariableSubsSlice, }; use roc_types::types::Type::{self, *}; -use roc_types::types::{Alias, Category, ErrorType, PatternCategory}; +use roc_types::types::{gather_fields_unsorted_iter, Alias, Category, ErrorType, PatternCategory}; use roc_unify::unify::unify; use roc_unify::unify::Unified::*; @@ -666,11 +667,7 @@ fn type_to_variable( new_arg_vars.push(var); } - let start = subs.variables.len() as u32; - let length = arg_vars.len() as u16; - let arg_vars = SubsSlice::new(start, length); - - subs.variables.extend(new_arg_vars); + let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); let ret_var = type_to_variable(subs, rank, pools, cached, ret_type); let closure_var = type_to_variable(subs, rank, pools, cached, closure_type); @@ -679,29 +676,28 @@ fn type_to_variable( register(subs, rank, pools, content) } Record(fields, ext) => { - let mut field_vars = MutMap::with_capacity_and_hasher(fields.len(), default_hasher()); + let mut field_vars = Vec::with_capacity(fields.len()); for (field, field_type) in fields { let field_var = field_type.map(|typ| type_to_variable(subs, rank, pools, cached, typ)); - field_vars.insert(field.clone(), field_var); + field_vars.push((field.clone(), field_var)); } let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext); - let new_ext_var = match roc_types::pretty_print::chase_ext_record( - subs, - temp_ext_var, - &mut field_vars, - ) { - Ok(()) => Variable::EMPTY_RECORD, - Err((new, _)) => new, - }; - let mut all_fields: Vec<_> = field_vars.into_iter().collect(); - all_fields.sort_unstable_by(RecordFields::compare); + let (it, new_ext_var) = + gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var); - let record_fields = RecordFields::insert_into_subs(subs, all_fields); + let it = it + .into_iter() + .map(|(field, field_type)| (field.clone(), field_type)); + + field_vars.extend(it); + field_vars.sort_unstable_by(RecordFields::compare); + + let record_fields = RecordFields::insert_into_subs(subs, field_vars); let content = Content::Structure(FlatType::Record(record_fields, new_ext_var)); @@ -732,7 +728,8 @@ fn type_to_variable( }; tag_vars.extend(ext_tag_vec.into_iter()); - let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var)); + let content = + Content::Structure(roc_unify::unify::from_mutmap(subs, tag_vars, new_ext_var)); register(subs, rank, pools, content) } @@ -749,11 +746,12 @@ fn type_to_variable( }; debug_assert!(ext_tag_vec.is_empty()); - let content = Content::Structure(FlatType::FunctionOrTagUnion( - Box::new(tag_name.clone()), - *symbol, - new_ext_var, - )); + let start = subs.tag_names.len() as u32; + subs.tag_names.push(tag_name.clone()); + let slice = SubsIndex::new(start); + + let content = + Content::Structure(FlatType::FunctionOrTagUnion(slice, *symbol, new_ext_var)); register(subs, rank, pools, content) } @@ -865,7 +863,7 @@ fn check_for_infinite_type( ) { let var = loc_var.value; - while let Some((recursive, _chain)) = subs.occurs(var) { + while let Err((recursive, _chain)) = subs.occurs(var) { let description = subs.get(recursive); let content = description.content; @@ -884,13 +882,16 @@ fn check_for_infinite_type( let mut new_tags = MutMap::default(); - for (label, args) in &tags { - let new_args: Vec<_> = args - .iter() - .map(|var| subs.explicit_substitute(recursive, rec_var, *var)) - .collect(); + for (name_index, slice_index) in tags.iter_all() { + let slice = subs[slice_index]; - new_tags.insert(label.clone(), new_args); + let mut new_vars = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + new_vars.push(subs.explicit_substitute(recursive, rec_var, var)); + } + + new_tags.insert(subs[name_index].clone(), new_vars); } let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var); @@ -1104,9 +1105,13 @@ fn adjust_rank_content( TagUnion(tags, ext_var) => { let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var); - for var in tags.values().flatten() { - rank = - rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var)); + for (_, index) in tags.iter_all() { + let slice = subs[index]; + for var_index in slice { + let var = subs[var_index]; + rank = rank + .max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); + } } rank @@ -1252,8 +1257,10 @@ fn instantiate_rigids_help( } TagUnion(tags, ext_var) => { - for (_, vars) in tags { - for var in vars.into_iter() { + for (_, index) in tags.iter_all() { + let slice = subs[index]; + for var_index in slice { + let var = subs[var_index]; instantiate_rigids_help(subs, max_rank, pools, var); } } @@ -1380,11 +1387,7 @@ fn deep_copy_var_help( new_arg_vars.push(copy_var); } - let start = subs.variables.len() as u32; - let length = arg_vars.len() as u16; - let arg_vars = SubsSlice::new(start, length); - - subs.variables.extend(new_arg_vars); + let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); Func(arg_vars, new_closure_var, new_ret_var) } @@ -1435,15 +1438,21 @@ fn deep_copy_var_help( TagUnion(tags, ext_var) => { let mut new_tags = MutMap::default(); - for (tag, vars) in tags { - let new_vars: Vec = vars - .into_iter() - .map(|var| deep_copy_var_help(subs, max_rank, pools, var)) - .collect(); + for (tag_index, index) in tags.iter_all() { + let tag = subs[tag_index].clone(); + let slice = subs[index]; + let mut new_vars = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + let new_var = deep_copy_var_help(subs, max_rank, pools, var); + new_vars.push(new_var); + } + new_tags.insert(tag, new_vars); } - TagUnion(new_tags, deep_copy_var_help(subs, max_rank, pools, ext_var)) + let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var); + roc_unify::unify::from_mutmap(subs, new_tags, new_ext) } FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion( diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 40eaac4913..8291e788ba 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -1,4 +1,4 @@ -use crate::subs::{Content, FlatType, GetSubsSlice, Subs, Variable}; +use crate::subs::{Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable}; use crate::types::{name_type_var, RecordField}; use roc_collections::all::{MutMap, MutSet}; use roc_module::ident::{Lowercase, TagName}; @@ -76,7 +76,7 @@ fn find_names_needed( use crate::subs::Content::*; use crate::subs::FlatType::*; - while let Some((recursive, _chain)) = subs.occurs(variable) { + while let Err((recursive, _chain)) = subs.occurs(variable) { let rec_var = subs.fresh_unnamed_flex_var(); let content = subs.get_content_without_compacting(recursive); @@ -84,14 +84,16 @@ fn find_names_needed( Content::Structure(FlatType::TagUnion(tags, ext_var)) => { let mut new_tags = MutMap::default(); - for (label, args) in tags { - let new_args = args - .clone() - .into_iter() - .map(|var| if var == recursive { rec_var } else { var }) - .collect(); + for (name_index, slice_index) in tags.iter_all() { + let slice = subs[slice_index]; - new_tags.insert(label.clone(), new_args); + let mut new_vars = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + new_vars.push(if var == recursive { rec_var } else { var }); + } + + new_tags.insert(subs[name_index].clone(), new_vars); } let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, *ext_var); @@ -162,11 +164,12 @@ fn find_names_needed( find_names_needed(*ext_var, subs, roots, root_appearances, names_taken); } Structure(TagUnion(tags, ext_var)) => { - let mut sorted_tags: Vec<_> = tags.iter().collect(); - sorted_tags.sort(); - - for var in sorted_tags.into_iter().map(|(_, v)| v).flatten() { - find_names_needed(*var, subs, roots, root_appearances, names_taken); + for slice_index in tags.variables { + let slice = subs[slice_index]; + for var_index in slice { + let var = subs[var_index]; + find_names_needed(var, subs, roots, root_appearances, names_taken); + } } find_names_needed(*ext_var, subs, roots, root_appearances, names_taken); @@ -345,13 +348,94 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa } } +enum ExtContent<'a> { + Empty, + Content(Variable, &'a Content), +} + +impl<'a> ExtContent<'a> { + fn from_var(subs: &'a Subs, ext: Variable) -> Self { + let content = subs.get_content_without_compacting(ext); + match content { + Content::Structure(FlatType::EmptyTagUnion) => ExtContent::Empty, + Content::Structure(FlatType::EmptyRecord) => ExtContent::Empty, + + Content::FlexVar(_) | Content::RigidVar(_) => ExtContent::Content(ext, content), + + other => unreachable!("something weird ended up in an ext var: {:?}", other), + } + } +} + +fn write_ext_content<'a>( + env: &Env, + subs: &'a Subs, + buf: &mut String, + ext_content: ExtContent<'a>, + parens: Parens, +) { + if let ExtContent::Content(_, content) = ext_content { + // This is an open record or tag union, so print the variable + // right after the '}' or ']' + // + // e.g. the "*" at the end of `{ x: I64 }*` + // or the "r" at the end of `{ x: I64 }r` + write_content(env, content, subs, buf, parens) + } +} + +fn write_sorted_tags2<'a>( + env: &Env, + subs: &'a Subs, + buf: &mut String, + tags: &UnionTags, + ext_var: Variable, +) -> ExtContent<'a> { + // Sort the fields so they always end up in the same order. + let (it, new_ext_var) = tags.unsorted_iterator_and_ext(subs, ext_var); + let mut sorted_fields: Vec<_> = it.collect(); + + let interns = &env.interns; + let home = env.home; + + sorted_fields.sort_by(|(a, _), (b, _)| { + a.as_ident_str(interns, home) + .cmp(&b.as_ident_str(interns, home)) + }); + + let mut any_written_yet = false; + + for (label, vars) in sorted_fields { + if any_written_yet { + buf.push_str(", "); + } else { + any_written_yet = true; + } + + buf.push_str(label.as_ident_str(interns, home).as_str()); + + for var in vars { + buf.push(' '); + write_content( + env, + subs.get_content_without_compacting(*var), + subs, + buf, + Parens::InTypeParam, + ); + } + } + + ExtContent::from_var(subs, new_ext_var) +} + fn write_sorted_tags<'a>( env: &Env, subs: &'a Subs, buf: &mut String, tags: &MutMap>, ext_var: Variable, -) -> Result<(), (Variable, &'a Content)> { +) -> ExtContent<'a> { // Sort the fields so they always end up in the same order. let mut sorted_fields = Vec::with_capacity(tags.len()); @@ -362,7 +446,7 @@ fn write_sorted_tags<'a>( // If the `ext` contains tags, merge them into the list of tags. // this can occur when inferring mutually recursive tags let mut from_ext = Default::default(); - let ext_content = chase_ext_tag_union(subs, ext_var, &mut from_ext); + let _ext_content = chase_ext_tag_union(subs, ext_var, &mut from_ext); for (tag_name, arguments) in from_ext.iter() { sorted_fields.push((tag_name, arguments)); @@ -399,7 +483,7 @@ fn write_sorted_tags<'a>( } } - ext_content + ExtContent::from_var(subs, ext_var) } fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut String, parens: Parens) { @@ -409,9 +493,14 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin Apply(symbol, args) => write_apply(env, *symbol, args, subs, buf, parens), EmptyRecord => buf.push_str(EMPTY_RECORD), EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION), - Func(args, _closure, ret) => { - write_fn(env, subs.get_subs_slice(*args), *ret, subs, buf, parens) - } + Func(args, _closure, ret) => write_fn( + env, + subs.get_subs_slice(*args.as_subs_slice()), + *ret, + subs, + buf, + parens, + ), Record(fields, ext_var) => { use crate::types::{gather_fields, RecordStructure}; @@ -476,37 +565,23 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin TagUnion(tags, ext_var) => { buf.push_str("[ "); - let ext_content = write_sorted_tags(env, subs, buf, tags, *ext_var); + let ext_content = write_sorted_tags2(env, subs, buf, tags, *ext_var); buf.push_str(" ]"); - if let Err((_, content)) = ext_content { - // This is an open tag union, so print the variable - // right after the ']' - // - // e.g. the "*" at the end of `{ x: I64 }*` - // or the "r" at the end of `{ x: I64 }r` - write_content(env, content, subs, buf, parens) - } + write_ext_content(env, subs, buf, ext_content, parens) } FunctionOrTagUnion(tag_name, _, ext_var) => { buf.push_str("[ "); let mut tags: MutMap = MutMap::default(); - tags.insert(*tag_name.clone(), vec![]); + tags.insert(subs[*tag_name].clone(), vec![]); let ext_content = write_sorted_tags(env, subs, buf, &tags, *ext_var); buf.push_str(" ]"); - if let Err((_, content)) = ext_content { - // This is an open tag union, so print the variable - // right after the ']' - // - // e.g. the "*" at the end of `{ x: I64 }*` - // or the "r" at the end of `{ x: I64 }r` - write_content(env, content, subs, buf, parens) - } + write_ext_content(env, subs, buf, ext_content, parens) } RecursiveTagUnion(rec_var, tags, ext_var) => { @@ -516,14 +591,7 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin buf.push_str(" ]"); - if let Err((_, content)) = ext_content { - // This is an open tag union, so print the variable - // right after the ']' - // - // e.g. the "*" at the end of `{ x: I64 }*` - // or the "r" at the end of `{ x: I64 }r` - write_content(env, content, subs, buf, parens) - } + write_ext_content(env, subs, buf, ext_content, parens); buf.push_str(" as "); write_content( @@ -548,8 +616,19 @@ pub fn chase_ext_tag_union<'a>( use FlatType::*; match subs.get_content_without_compacting(var) { Content::Structure(EmptyTagUnion) => Ok(()), - Content::Structure(TagUnion(tags, ext_var)) - | Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => { + Content::Structure(TagUnion(tags, ext_var)) => { + for (name_index, slice_index) in tags.iter_all() { + let subs_slice = subs[slice_index]; + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + let tag_name = subs[name_index].clone(); + + fields.push((tag_name, slice.to_vec())); + } + + chase_ext_tag_union(subs, *ext_var, fields) + } + + Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => { for (label, vars) in tags { fields.push((label.clone(), vars.to_vec())); } @@ -557,7 +636,7 @@ pub fn chase_ext_tag_union<'a>( chase_ext_tag_union(subs, *ext_var, fields) } Content::Structure(FunctionOrTagUnion(tag_name, _, ext_var)) => { - fields.push((*tag_name.clone(), vec![])); + fields.push((subs[*tag_name].clone(), vec![])); chase_ext_tag_union(subs, *ext_var, fields) } @@ -567,35 +646,6 @@ pub fn chase_ext_tag_union<'a>( } } -pub fn chase_ext_record( - subs: &Subs, - var: Variable, - fields: &mut MutMap>, -) -> Result<(), (Variable, Content)> { - use crate::subs::Content::*; - use crate::subs::FlatType::*; - - match subs.get_content_without_compacting(var) { - Structure(Record(sub_fields, sub_ext)) => { - for (i1, i2, i3) in sub_fields.iter_all() { - let label = &subs[i1]; - let var = subs[i2]; - let record_field = subs[i3].map(|_| var); - - fields.insert(label.clone(), record_field); - } - - chase_ext_record(subs, *sub_ext, fields) - } - - Structure(EmptyRecord) => Ok(()), - - Alias(_, _, var) => chase_ext_record(subs, *var, fields), - - content => Err((var, content.clone())), - } -} - fn write_apply( env: &Env, symbol: Symbol, diff --git a/compiler/types/src/solved_types.rs b/compiler/types/src/solved_types.rs index b3b1a10d86..a36712d193 100644 --- a/compiler/types/src/solved_types.rs +++ b/compiler/types/src/solved_types.rs @@ -387,7 +387,7 @@ impl SolvedType { Func(args, closure, ret) => { let mut new_args = Vec::with_capacity(args.len()); - for var in subs.get_subs_slice(*args) { + for var in subs.get_subs_slice(*args.as_subs_slice()) { new_args.push(Self::from_var_help(subs, recursion_vars, *var)); } @@ -420,13 +420,16 @@ impl SolvedType { TagUnion(tags, ext_var) => { let mut new_tags = Vec::with_capacity(tags.len()); - for (tag_name, args) in tags { - let mut new_args = Vec::with_capacity(args.len()); + for (name_index, slice_index) in tags.iter_all() { + let slice = subs[slice_index]; - for var in args { - new_args.push(Self::from_var_help(subs, recursion_vars, *var)); + let mut new_args = Vec::with_capacity(slice.len()); + + for var_index in slice { + let var = subs[var_index]; + new_args.push(Self::from_var_help(subs, recursion_vars, var)); } - + let tag_name = subs[name_index].clone(); new_tags.push((tag_name.clone(), new_args)); } @@ -437,7 +440,7 @@ impl SolvedType { FunctionOrTagUnion(tag_name, symbol, ext_var) => { let ext = Self::from_var_help(subs, recursion_vars, *ext_var); - SolvedType::FunctionOrTagUnion(*tag_name.clone(), *symbol, Box::new(ext)) + SolvedType::FunctionOrTagUnion(subs[*tag_name].clone(), *symbol, Box::new(ext)) } RecursiveTagUnion(rec_var, tags, ext_var) => { recursion_vars.insert(subs, *rec_var); diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index 861d5a8f3e..48512ad3d4 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -56,6 +56,7 @@ pub struct Subs { pub tag_names: Vec, pub field_names: Vec, pub record_fields: Vec>, + pub variable_slices: Vec, } /// A slice into the Vec of subs @@ -99,6 +100,20 @@ impl std::ops::Index> for Subs { } } +impl std::ops::Index> for Subs { + type Output = TagName; + + fn index(&self, index: SubsIndex) -> &Self::Output { + &self.tag_names[index.start as usize] + } +} + +impl std::ops::IndexMut> for Subs { + fn index_mut(&mut self, index: SubsIndex) -> &mut Self::Output { + &mut self.tag_names[index.start as usize] + } +} + impl std::ops::IndexMut> for Subs { fn index_mut(&mut self, index: SubsIndex) -> &mut Self::Output { &mut self.field_names[index.start as usize] @@ -119,6 +134,20 @@ impl std::ops::IndexMut>> for Subs { } } +impl std::ops::Index> for Subs { + type Output = VariableSubsSlice; + + fn index(&self, index: SubsIndex) -> &Self::Output { + &self.variable_slices[index.start as usize] + } +} + +impl std::ops::IndexMut> for Subs { + fn index_mut(&mut self, index: SubsIndex) -> &mut Self::Output { + &mut self.variable_slices[index.start as usize] + } +} + // custom debug impl std::fmt::Debug for SubsIndex { @@ -483,12 +512,19 @@ impl Subs { Content::Structure(FlatType::EmptyTagUnion), ); - subs.set_content(Variable::BOOL_ENUM, { - let mut tags = MutMap::default(); - tags.insert(TagName::Global("False".into()), vec![]); - tags.insert(TagName::Global("True".into()), vec![]); + let bool_union_tags = UnionTags::insert_into_subs( + &mut subs, + [ + (TagName::Global("False".into()), []), + (TagName::Global("True".into()), []), + ], + ); - Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION)) + subs.set_content(Variable::BOOL_ENUM, { + Content::Structure(FlatType::TagUnion( + bool_union_tags, + Variable::EMPTY_TAG_UNION, + )) }); subs.set_content(Variable::BOOL, { @@ -615,7 +651,7 @@ impl Subs { self.utable.is_redirect(var) } - pub fn occurs(&self, var: Variable) -> Option<(Variable, Vec)> { + pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec)> { occurs(self, &ImSet::default(), var) } @@ -829,10 +865,10 @@ impl Content { #[derive(Clone, Debug)] pub enum FlatType { Apply(Symbol, Vec), - Func(SubsSlice, Variable, Variable), + Func(VariableSubsSlice, Variable, Variable), Record(RecordFields, Variable), - TagUnion(MutMap>, Variable), - FunctionOrTagUnion(Box, Symbol, Variable), + TagUnion(UnionTags, Variable), + FunctionOrTagUnion(SubsIndex, Symbol, Variable), RecursiveTagUnion(Variable, MutMap>, Variable), Erroneous(Box), EmptyRecord, @@ -847,6 +883,249 @@ pub enum Builtin { EmptyRecord, } +#[derive(Clone, Copy, Debug)] +pub struct VariableSubsSlice { + slice: SubsSlice, +} + +impl VariableSubsSlice { + pub fn len(&self) -> usize { + self.slice.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn as_subs_slice(&self) -> &SubsSlice { + &self.slice + } + + pub fn new(start: u32, length: u16) -> Self { + Self { + slice: SubsSlice::new(start, length), + } + } + + pub fn insert_into_subs(subs: &mut Subs, input: I) -> Self + where + I: IntoIterator, + { + let start = subs.variables.len() as u32; + + subs.variables.extend(input.into_iter()); + + let length = (subs.variables.len() as u32 - start) as u16; + + Self::new(start, length) + } +} + +impl IntoIterator for VariableSubsSlice { + type Item = SubsIndex; + + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.slice.into_iter() + } +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct UnionTags { + pub tag_names: SubsSlice, + pub variables: SubsSlice, +} + +impl UnionTags { + pub fn len(&self) -> usize { + self.tag_names.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn compare(x: &(TagName, T), y: &(TagName, T)) -> std::cmp::Ordering { + first(x, y) + } + pub fn insert_into_subs(subs: &mut Subs, input: I) -> Self + where + I: IntoIterator, + I2: IntoIterator, + { + let tag_names_start = subs.tag_names.len() as u32; + let variables_start = subs.variable_slices.len() as u32; + + let it = input.into_iter(); + let size_hint = it.size_hint().0; + + subs.tag_names.reserve(size_hint); + subs.variable_slices.reserve(size_hint); + + let mut length = 0; + for (k, v) in it { + let variables = VariableSubsSlice::insert_into_subs(subs, v.into_iter()); + + subs.tag_names.push(k); + subs.variable_slices.push(variables); + + length += 1; + } + + UnionTags { + variables: SubsSlice::new(variables_start, length), + tag_names: SubsSlice::new(tag_names_start, length), + } + } + + pub fn insert_slices_into_subs(subs: &mut Subs, input: I) -> Self + where + I: IntoIterator, + { + let tag_names_start = subs.tag_names.len() as u32; + let variables_start = subs.variable_slices.len() as u32; + + let it = input.into_iter(); + let size_hint = it.size_hint().0; + + subs.tag_names.reserve(size_hint); + subs.variable_slices.reserve(size_hint); + + let mut length = 0; + for (k, variables) in it { + subs.tag_names.push(k); + subs.variable_slices.push(variables); + + length += 1; + } + + UnionTags { + variables: SubsSlice::new(variables_start, length), + tag_names: SubsSlice::new(tag_names_start, length), + } + } + + pub fn iter_all( + &self, + ) -> impl Iterator, SubsIndex)> { + self.tag_names.into_iter().zip(self.variables.into_iter()) + } + + #[inline(always)] + pub fn unsorted_iterator<'a>( + &'a self, + subs: &'a Subs, + ext: Variable, + ) -> impl Iterator + 'a { + let (it, _) = crate::types::gather_tags_unsorted_iter(subs, *self, ext); + + it.map(move |(label, slice)| (label, subs.get_subs_slice(*slice.as_subs_slice()))) + } + + pub fn unsorted_iterator_and_ext<'a>( + &'a self, + subs: &'a Subs, + ext: Variable, + ) -> (impl Iterator + 'a, Variable) { + let (it, ext) = crate::types::gather_tags_unsorted_iter(subs, *self, ext); + + ( + it.map(move |(label, slice)| (label, subs.get_subs_slice(*slice.as_subs_slice()))), + ext, + ) + } + + #[inline(always)] + pub fn sorted_iterator_and_ext<'a>( + &'_ self, + subs: &'a Subs, + ext: Variable, + ) -> (SortedTagsIterator<'a>, Variable) { + if is_empty_tag_union(subs, ext) { + ( + Box::new(self.iter_all().map(move |(i1, i2)| { + let tag_name: &TagName = &subs[i1]; + let subs_slice = subs[i2]; + + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + + (tag_name.clone(), slice) + })), + ext, + ) + } else { + let union_structure = crate::types::gather_tags(subs, *self, ext); + + ( + Box::new(union_structure.fields.into_iter()), + union_structure.ext, + ) + } + } + + #[inline(always)] + pub fn sorted_slices_iterator_and_ext<'a>( + &'_ self, + subs: &'a Subs, + ext: Variable, + ) -> (SortedTagsSlicesIterator<'a>, Variable) { + if is_empty_tag_union(subs, ext) { + ( + Box::new(self.iter_all().map(move |(i1, i2)| { + let tag_name: &TagName = &subs[i1]; + let subs_slice = subs[i2]; + + (tag_name.clone(), subs_slice) + })), + ext, + ) + } else { + let (fields, ext) = crate::types::gather_tags_slices(subs, *self, ext); + + (Box::new(fields.into_iter()), ext) + } + } +} + +pub type SortedTagsIterator<'a> = Box + 'a>; +pub type SortedTagsSlicesIterator<'a> = Box + 'a>; + +pub fn is_empty_tag_union(subs: &Subs, mut var: Variable) -> bool { + use crate::subs::Content::*; + use crate::subs::FlatType::*; + + loop { + match subs.get_content_without_compacting(var) { + FlexVar(_) => return true, + Structure(EmptyTagUnion) => return true, + Structure(TagUnion(sub_fields, sub_ext)) => { + if !sub_fields.is_empty() { + return false; + } + + var = *sub_ext; + } + Structure(RecursiveTagUnion(_, sub_fields, sub_ext)) => { + if !sub_fields.is_empty() { + return false; + } + + var = *sub_ext; + } + + Alias(_, _, actual_var) => { + // TODO according to elm/compiler: "TODO may be dropping useful alias info here" + var = *actual_var; + } + + _other => { + return false; + } + } + } +} + #[derive(Clone, Copy, Debug)] pub struct RecordFields { pub length: u16, @@ -907,8 +1186,15 @@ impl RecordFields { let variables_start = subs.variables.len() as u32; let field_types_start = subs.record_fields.len() as u32; + let it = input.into_iter(); + let size_hint = it.size_hint().0; + + subs.variables.reserve(size_hint); + subs.field_names.reserve(size_hint); + subs.record_fields.reserve(size_hint); + let mut length = 0; - for (k, v) in input { + for (k, v) in it { let var = *v.as_inner(); let record_field = v.map(|_| ()); @@ -1027,17 +1313,17 @@ fn occurs( subs: &Subs, seen: &ImSet, input_var: Variable, -) -> Option<(Variable, Vec)> { +) -> Result<(), (Variable, Vec)> { use self::Content::*; use self::FlatType::*; let root_var = subs.get_root_key_without_compacting(input_var); if seen.contains(&root_var) { - Some((root_var, vec![])) + Err((root_var, vec![])) } else { match subs.get_content_without_compacting(root_var) { - FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => None, + FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => Ok(()), Structure(flat_type) => { let mut new_seen = seen.clone(); @@ -1049,7 +1335,7 @@ fn occurs( Func(arg_vars, closure_var, ret_var) => { let it = once(ret_var) .chain(once(closure_var)) - .chain(subs.get_subs_slice(*arg_vars).iter()); + .chain(subs.get_subs_slice(*arg_vars.as_subs_slice()).iter()); short_circuit(subs, root_var, &new_seen, it) } Record(vars_by_field, ext_var) => { @@ -1059,8 +1345,15 @@ fn occurs( short_circuit(subs, root_var, &new_seen, it) } TagUnion(tags, ext_var) => { - let it = once(ext_var).chain(tags.values().flatten()); - short_circuit(subs, root_var, &new_seen, it) + for slice_index in tags.variables { + let slice = subs[slice_index]; + for var_index in slice { + let var = subs[var_index]; + short_circuit_help(subs, root_var, &new_seen, var)?; + } + } + + short_circuit_help(subs, root_var, &new_seen, *ext_var) } FunctionOrTagUnion(_, _, ext_var) => { let it = once(ext_var); @@ -1071,7 +1364,7 @@ fn occurs( let it = once(ext_var).chain(tags.values().flatten()); short_circuit(subs, root_var, &new_seen, it) } - EmptyRecord | EmptyTagUnion | Erroneous(_) => None, + EmptyRecord | EmptyTagUnion | Erroneous(_) => Ok(()), } } Alias(_, args, _) => { @@ -1089,17 +1382,29 @@ fn short_circuit<'a, T>( root_key: Variable, seen: &ImSet, iter: T, -) -> Option<(Variable, Vec)> +) -> Result<(), (Variable, Vec)> where T: Iterator, { for var in iter { - if let Some((v, mut vec)) = occurs(subs, seen, *var) { - vec.push(root_key); - return Some((v, vec)); - } + short_circuit_help(subs, root_key, seen, *var)?; } - None + + Ok(()) +} + +fn short_circuit_help( + subs: &Subs, + root_key: Variable, + seen: &ImSet, + var: Variable, +) -> Result<(), (Variable, Vec)> { + if let Err((v, mut vec)) = occurs(subs, seen, var) { + vec.push(root_key); + return Err((v, vec)); + } + + Ok(()) } fn explicit_substitute( @@ -1149,14 +1454,38 @@ fn explicit_substitute( Structure(Func(arg_vars, new_closure_var, new_ret_var)), ); } - TagUnion(mut tags, ext_var) => { + TagUnion(tags, ext_var) => { let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen); - for (_, variables) in tags.iter_mut() { - for var in variables.iter_mut() { - *var = explicit_substitute(subs, from, to, *var, seen); + + let mut new_slices = Vec::new(); + for slice_index in tags.variables { + let slice = subs[slice_index]; + + let mut new_variables = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + let new_var = explicit_substitute(subs, from, to, var, seen); + new_variables.push(new_var); + // subs[var_index] = new_var; } + + let start = subs.variables.len() as u32; + let length = new_variables.len() as u16; + + subs.variables.extend(new_variables); + + new_slices.push(VariableSubsSlice::new(start, length)); } - subs.set_content(in_var, Structure(TagUnion(tags, new_ext_var))); + + let start = subs.variable_slices.len() as u32; + let length = new_slices.len() as u16; + + subs.variable_slices.extend(new_slices); + + let mut union_tags = tags; + union_tags.variables = SubsSlice::new(start, length); + + subs.set_content(in_var, Structure(TagUnion(union_tags, new_ext_var))); } FunctionOrTagUnion(tag_name, symbol, ext_var) => { let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen); @@ -1294,9 +1623,11 @@ fn get_var_names( FlatType::TagUnion(tags, ext_var) => { let mut taken_names = get_var_names(subs, ext_var, taken_names); - for vars in tags.values() { - for arg_var in vars { - taken_names = get_var_names(subs, *arg_var, taken_names) + for slice_index in tags.variables { + let slice = subs[slice_index]; + for var_index in slice { + let var = subs[var_index]; + taken_names = get_var_names(subs, var, taken_names) } } @@ -1536,13 +1867,16 @@ fn flat_type_to_err_type( TagUnion(tags, ext_var) => { let mut err_tags = SendMap::default(); - for (tag, vars) in tags.into_iter() { - let mut err_vars = Vec::with_capacity(vars.len()); + for (name_index, slice_index) in tags.iter_all() { + let mut err_vars = Vec::with_capacity(tags.len()); - for var in vars { + let slice = subs[slice_index]; + for var_index in slice { + let var = subs[var_index]; err_vars.push(var_to_err_type(subs, state, var)); } + let tag = subs[name_index].clone(); err_tags.insert(tag, err_vars); } @@ -1568,7 +1902,7 @@ fn flat_type_to_err_type( } FunctionOrTagUnion(tag_name, _, ext_var) => { - let tag_name = *tag_name; + let tag_name = subs[tag_name].clone(); let mut err_tags = SendMap::default(); @@ -1685,8 +2019,12 @@ fn restore_content(subs: &mut Subs, content: &Content) { subs.restore(*ext_var); } TagUnion(tags, ext_var) => { - for var in tags.values().flatten() { - subs.restore(*var); + for slice_index in tags.variables { + let slice = subs[slice_index]; + for var_index in slice { + let var = subs[var_index]; + subs.restore(var); + } } subs.restore(*ext_var); diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index 72c23ea183..1a52904f3a 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -1,5 +1,7 @@ use crate::pretty_print::Parens; -use crate::subs::{LambdaSet, RecordFields, Subs, VarStore, Variable}; +use crate::subs::{ + GetSubsSlice, LambdaSet, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice, +}; use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap}; use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName}; use roc_module::low_level::LowLevel; @@ -985,6 +987,13 @@ pub struct RecordStructure { pub ext: Variable, } +#[derive(Debug)] +pub struct TagUnionStructure<'a> { + /// Invariant: these should be sorted! + pub fields: Vec<(TagName, &'a [Variable])>, + pub ext: Variable, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum PReason { TypedArg { @@ -1547,7 +1556,13 @@ pub fn gather_fields_unsorted_iter( var = *actual_var; } - _ => break, + Structure(EmptyRecord) => break, + FlexVar(_) => break, + + // TODO investigate apparently this one pops up in the reporting tests! + RigidVar(_) => break, + + other => unreachable!("something weird ended up in a record type: {:?}", other), } } @@ -1580,3 +1595,104 @@ pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) -> ext, } } + +pub fn gather_tags_unsorted_iter( + subs: &Subs, + other_fields: UnionTags, + mut var: Variable, +) -> ( + impl Iterator + '_, + Variable, +) { + use crate::subs::Content::*; + use crate::subs::FlatType::*; + + let mut stack = vec![other_fields]; + + loop { + match subs.get_content_without_compacting(var) { + Structure(TagUnion(sub_fields, sub_ext)) => { + stack.push(*sub_fields); + + var = *sub_ext; + } + + Structure(FunctionOrTagUnion(_tag_name_index, _, _sub_ext)) => { + todo!("this variant does not use SOA yet, and therefore this case is unreachable right now") + // let sub_fields: UnionTags = (*tag_name_index).into(); + // stack.push(sub_fields); + // + // var = *sub_ext; + } + + Structure(RecursiveTagUnion(_, _sub_fields, _sub_ext)) => { + todo!("this variant does not use SOA yet, and therefore this case is unreachable right now") + // stack.push(*sub_fields); + // + // var = *sub_ext; + } + + Alias(_, _, actual_var) => { + // TODO according to elm/compiler: "TODO may be dropping useful alias info here" + var = *actual_var; + } + + Structure(EmptyTagUnion) => break, + FlexVar(_) => break, + + // TODO investigate this likely can happen when there is a type error + RigidVar(_) => break, + + other => unreachable!("something weird ended up in a tag union type: {:?}", other), + } + } + + let it = stack + .into_iter() + .map(|union_tags| union_tags.iter_all()) + .flatten() + .map(move |(i1, i2)| { + let tag_name: &TagName = &subs[i1]; + let subs_slice = subs[i2]; + + (tag_name, subs_slice) + }); + + (it, var) +} + +pub fn gather_tags_slices( + subs: &Subs, + other_fields: UnionTags, + var: Variable, +) -> (Vec<(TagName, VariableSubsSlice)>, Variable) { + let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var); + + let mut result: Vec<_> = it + .map(|(ref_label, field)| (ref_label.clone(), field)) + .collect(); + + result.sort_by(|(a, _), (b, _)| a.cmp(b)); + + (result, ext) +} + +pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUnionStructure { + let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var); + + let mut result: Vec<_> = it + .map(|(ref_label, field)| { + ( + ref_label.clone(), + subs.get_subs_slice(*field.as_subs_slice()), + ) + }) + .collect(); + + result.sort_by(|(a, _), (b, _)| a.cmp(b)); + + TagUnionStructure { + fields: result, + ext, + } +} diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index f57e524fdf..a96ee936ec 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -3,7 +3,8 @@ use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::Symbol; use roc_types::subs::Content::{self, *}; use roc_types::subs::{ - Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, RecordFields, Subs, SubsSlice, Variable, + Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, RecordFields, Subs, SubsIndex, + SubsSlice, UnionTags, Variable, VariableSubsSlice, }; use roc_types::types::{ErrorType, Mismatch, RecordField}; @@ -413,15 +414,8 @@ fn unify_shared_fields( if num_shared_fields == matching_fields.len() { // pull fields in from the ext_var - let mut ext_fields = MutMap::default(); - let new_ext_var = - match roc_types::pretty_print::chase_ext_record(subs, ext, &mut ext_fields) { - Ok(()) => Variable::EMPTY_RECORD, - Err((new, _)) => new, - }; - - let mut ext_fields: Vec<_> = ext_fields.into_iter().collect(); - ext_fields.sort_by(|(name1, _), (name2, _)| name1.cmp(name2)); + let (ext_fields, new_ext_var) = RecordFields::empty().sorted_iterator_and_ext(subs, ext); + let ext_fields: Vec<_> = ext_fields.into_iter().collect(); let fields: RecordFields = match other_fields { OtherFields::None => { @@ -620,6 +614,313 @@ where } } +fn separate_union_tags( + subs: &Subs, + fields1: UnionTags, + ext1: Variable, + fields2: UnionTags, + ext2: Variable, +) -> (Separate, Variable, Variable) { + let (it1, new_ext1) = fields1.sorted_slices_iterator_and_ext(subs, ext1); + let (it2, new_ext2) = fields2.sorted_slices_iterator_and_ext(subs, ext2); + + let it1 = it1.collect::>(); + let it2 = it2.collect::>(); + + (separate(it1, it2), new_ext1, new_ext2) +} + +#[allow(clippy::too_many_arguments)] +fn unify_tag_union_new( + subs: &mut Subs, + pool: &mut Pool, + ctx: &Context, + tags1: UnionTags, + initial_ext1: Variable, + tags2: UnionTags, + initial_ext2: Variable, + recursion: (Option, Option), +) -> Outcome { + let (separate, ext1, ext2) = + separate_union_tags(subs, tags1, initial_ext1, tags2, initial_ext2); + + let recursion_var = match recursion { + (None, None) => None, + (Some(v), None) | (None, Some(v)) => Some(v), + (Some(v1), Some(_v2)) => Some(v1), + }; + + let shared_tags = separate.in_both; + + if separate.only_in_1.is_empty() { + if separate.only_in_2.is_empty() { + let ext_problems = unify_pool(subs, pool, ext1, ext2); + + if !ext_problems.is_empty() { + return ext_problems; + } + + let mut tag_problems = unify_shared_tags_new( + subs, + pool, + ctx, + shared_tags, + OtherTags2::Empty, + ext1, + recursion_var, + ); + + tag_problems.extend(ext_problems); + + tag_problems + } else { + let unique_tags2 = UnionTags::insert_slices_into_subs(subs, separate.only_in_2); + let flat_type = FlatType::TagUnion(unique_tags2, ext2); + let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); + let ext_problems = unify_pool(subs, pool, ext1, sub_record); + + if !ext_problems.is_empty() { + return ext_problems; + } + + let mut tag_problems = unify_shared_tags_new( + subs, + pool, + ctx, + shared_tags, + OtherTags2::Empty, + sub_record, + recursion_var, + ); + + tag_problems.extend(ext_problems); + + tag_problems + } + } else if separate.only_in_2.is_empty() { + let unique_tags1 = UnionTags::insert_slices_into_subs(subs, separate.only_in_1); + let flat_type = FlatType::TagUnion(unique_tags1, ext1); + let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); + let ext_problems = unify_pool(subs, pool, sub_record, ext2); + + if !ext_problems.is_empty() { + return ext_problems; + } + + let mut tag_problems = unify_shared_tags_new( + subs, + pool, + ctx, + shared_tags, + OtherTags2::Empty, + sub_record, + recursion_var, + ); + + tag_problems.extend(ext_problems); + + tag_problems + } else { + let other_tags = OtherTags2::Union(separate.only_in_1.clone(), separate.only_in_2.clone()); + + let unique_tags1 = UnionTags::insert_slices_into_subs(subs, separate.only_in_1); + let unique_tags2 = UnionTags::insert_slices_into_subs(subs, separate.only_in_2); + + let ext = fresh(subs, pool, ctx, Content::FlexVar(None)); + let flat_type1 = FlatType::TagUnion(unique_tags1, ext); + let flat_type2 = FlatType::TagUnion(unique_tags2, ext); + + let sub1 = fresh(subs, pool, ctx, Structure(flat_type1)); + let sub2 = fresh(subs, pool, ctx, Structure(flat_type2)); + + // NOTE: for clearer error messages, we rollback unification of the ext vars when either fails + // + // This is inspired by + // + // + // f : [ Red, Green ] -> Bool + // f = \_ -> True + // + // f Blue + // + // In this case, we want the mismatch to be between `[ Blue ]a` and `[ Red, Green ]`, but + // without rolling back, the mismatch is between `[ Blue, Red, Green ]a` and `[ Red, Green ]`. + // TODO is this also required for the other cases? + + let snapshot = subs.snapshot(); + + let ext1_problems = unify_pool(subs, pool, ext1, sub2); + if !ext1_problems.is_empty() { + subs.rollback_to(snapshot); + return ext1_problems; + } + + let ext2_problems = unify_pool(subs, pool, sub1, ext2); + if !ext2_problems.is_empty() { + subs.rollback_to(snapshot); + return ext2_problems; + } + + subs.commit_snapshot(snapshot); + + let mut tag_problems = + unify_shared_tags_new(subs, pool, ctx, shared_tags, other_tags, ext, recursion_var); + + tag_problems.reserve(ext1_problems.len() + ext2_problems.len()); + tag_problems.extend(ext1_problems); + tag_problems.extend(ext2_problems); + + tag_problems + } +} + +enum OtherTags2 { + Empty, + Union( + Vec<(TagName, VariableSubsSlice)>, + Vec<(TagName, VariableSubsSlice)>, + ), +} + +fn unify_shared_tags_new( + subs: &mut Subs, + pool: &mut Pool, + ctx: &Context, + shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>, + other_tags: OtherTags2, + ext: Variable, + recursion_var: Option, +) -> Outcome { + let mut matching_tags = Vec::default(); + let num_shared_tags = shared_tags.len(); + + for (name, (actual_vars, expected_vars)) in shared_tags { + let mut matching_vars = Vec::with_capacity(actual_vars.len()); + + let actual_len = actual_vars.len(); + let expected_len = expected_vars.len(); + + for (actual_index, expected_index) in actual_vars.into_iter().zip(expected_vars.into_iter()) + { + let actual = subs[actual_index]; + let expected = subs[expected_index]; + // NOTE the arguments of a tag can be recursive. For instance in the expression + // + // Cons 1 (Cons "foo" Nil) + // + // We need to not just check the outer layer (inferring ConsList Int) + // but also the inner layer (finding a type error, as desired) + // + // This correction introduces the same issue as in https://github.com/elm/compiler/issues/1964 + // Polymorphic recursion is now a type error. + // + // The strategy is to expand the recursive tag union as deeply as the non-recursive one + // is. + // + // > RecursiveTagUnion(rvar, [ Cons a rvar, Nil ], ext) + // + // Conceptually becomes + // + // > RecursiveTagUnion(rvar, [ Cons a [ Cons a rvar, Nil ], Nil ], ext) + // + // and so on until the whole non-recursive tag union can be unified with it. + let mut problems = Vec::new(); + + { + problems.extend(unify_pool(subs, pool, actual, expected)); + } + + if problems.is_empty() { + matching_vars.push(actual); + } + } + + // only do this check after unification so the error message has more info + if actual_len == expected_len && actual_len == matching_vars.len() { + matching_tags.push((name, matching_vars)); + } + } + + if num_shared_tags == matching_tags.len() { + // pull fields in from the ext_var + + let (ext_fields, new_ext_var) = UnionTags::default().sorted_iterator_and_ext(subs, ext); + let ext_fields: Vec<_> = ext_fields + .into_iter() + .map(|(label, variables)| (label, variables.to_vec())) + .collect(); + + let new_tags: UnionTags = match other_tags { + OtherTags2::Empty => { + if ext_fields.is_empty() { + UnionTags::insert_into_subs(subs, matching_tags) + } else { + let all_fields = merge_sorted(matching_tags, ext_fields); + UnionTags::insert_into_subs(subs, all_fields) + } + } + OtherTags2::Union(other1, other2) => { + let mut all_fields = merge_sorted(matching_tags, ext_fields); + all_fields = merge_sorted( + all_fields, + other1.into_iter().map(|(field_name, subs_slice)| { + let vec = subs.get_subs_slice(*subs_slice.as_subs_slice()).to_vec(); + + (field_name, vec) + }), + ); + + all_fields = merge_sorted( + all_fields, + other2.into_iter().map(|(field_name, subs_slice)| { + let vec = subs.get_subs_slice(*subs_slice.as_subs_slice()).to_vec(); + + (field_name, vec) + }), + ); + + UnionTags::insert_into_subs(subs, all_fields) + } + }; + + unify_shared_tags_merge_new(subs, ctx, new_tags, new_ext_var, recursion_var) + } else { + mismatch!( + "Problem with Tag Union\nThere should be {:?} matching tags, but I only got \n{:?}", + num_shared_tags, + &matching_tags + ) + } +} + +fn unify_shared_tags_merge_new( + subs: &mut Subs, + ctx: &Context, + new_tags: UnionTags, + new_ext_var: Variable, + recursion_var: Option, +) -> Outcome { + let flat_type = if let Some(rec) = recursion_var { + debug_assert!(is_recursion_var(subs, rec)); + + let mut tags = MutMap::default(); + + for (name_index, slice_index) in new_tags.iter_all() { + let subs_slice = subs[slice_index]; + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + let tag = subs[name_index].clone(); + + tags.insert(tag, slice.to_vec()); + } + + FlatType::RecursiveTagUnion(rec, tags, new_ext_var) + } else { + FlatType::TagUnion(new_tags, new_ext_var) + }; + + merge(subs, ctx, Structure(flat_type)) +} + fn unify_tag_union( subs: &mut Subs, pool: &mut Pool, @@ -676,7 +977,7 @@ fn unify_tag_union( tag_problems } else { - let flat_type = FlatType::TagUnion(unique_tags2, rec2.ext); + let flat_type = from_mutmap(subs, unique_tags2, rec2.ext); let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); let ext_problems = unify_pool(subs, pool, rec1.ext, sub_record); @@ -699,7 +1000,7 @@ fn unify_tag_union( tag_problems } } else if unique_tags2.is_empty() { - let flat_type = FlatType::TagUnion(unique_tags1, rec1.ext); + let flat_type = from_mutmap(subs, unique_tags1, rec1.ext); let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); let ext_problems = unify_pool(subs, pool, sub_record, rec2.ext); @@ -727,8 +1028,8 @@ fn unify_tag_union( }; let ext = fresh(subs, pool, ctx, Content::FlexVar(None)); - let flat_type1 = FlatType::TagUnion(unique_tags1, ext); - let flat_type2 = FlatType::TagUnion(unique_tags2, ext); + let flat_type1 = from_mutmap(subs, unique_tags1, ext); + let flat_type2 = from_mutmap(subs, unique_tags2, ext); let sub1 = fresh(subs, pool, ctx, Structure(flat_type1)); let sub2 = fresh(subs, pool, ctx, Structure(flat_type2)); @@ -811,7 +1112,7 @@ fn unify_tag_union_not_recursive_recursive( tag_problems } else { - let flat_type = FlatType::TagUnion(unique_tags2, rec2.ext); + let flat_type = from_mutmap(subs, unique_tags2, rec2.ext); let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); let ext_problems = unify_pool(subs, pool, rec1.ext, sub_record); @@ -834,7 +1135,7 @@ fn unify_tag_union_not_recursive_recursive( tag_problems } } else if unique_tags2.is_empty() { - let flat_type = FlatType::TagUnion(unique_tags1, rec1.ext); + let flat_type = from_mutmap(subs, unique_tags1, rec1.ext); let sub_record = fresh(subs, pool, ctx, Structure(flat_type)); let ext_problems = unify_pool(subs, pool, sub_record, rec2.ext); @@ -859,8 +1160,8 @@ fn unify_tag_union_not_recursive_recursive( let other_tags = union(unique_tags1.clone(), &unique_tags2); let ext = fresh(subs, pool, ctx, Content::FlexVar(None)); - let flat_type1 = FlatType::TagUnion(unique_tags1, ext); - let flat_type2 = FlatType::TagUnion(unique_tags2, ext); + let flat_type1 = from_mutmap(subs, unique_tags1, ext); + let flat_type2 = from_mutmap(subs, unique_tags2, ext); let sub1 = fresh(subs, pool, ctx, Structure(flat_type1)); let sub2 = fresh(subs, pool, ctx, Structure(flat_type2)); @@ -1109,7 +1410,7 @@ fn unify_shared_tags_merge( debug_assert!(is_recursion_var(subs, rec)); FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var) } else { - FlatType::TagUnion(new_tags, new_ext_var) + from_mutmap(subs, new_tags, new_ext_var) }; merge(subs, ctx, Structure(flat_type)) @@ -1151,17 +1452,14 @@ fn unify_flat_type( } (TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => { - let union1 = gather_tags(subs, tags1.clone(), *ext1); - let union2 = gather_tags(subs, tags2.clone(), *ext2); - - unify_tag_union(subs, pool, ctx, union1, union2, (None, None)) + unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, (None, None)) } (RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => { debug_assert!(is_recursion_var(subs, *recursion_var)); // this never happens in type-correct programs, but may happen if there is a type error let union1 = gather_tags(subs, tags1.clone(), *ext1); - let union2 = gather_tags(subs, tags2.clone(), *ext2); + let union2 = gather_tags_new(subs, *tags2, *ext2); unify_tag_union( subs, @@ -1175,7 +1473,7 @@ fn unify_flat_type( (TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { debug_assert!(is_recursion_var(subs, *recursion_var)); - let union1 = gather_tags(subs, tags1.clone(), *ext1); + let union1 = gather_tags_new(subs, *tags1, *ext1); let union2 = gather_tags(subs, tags2.clone(), *ext2); unify_tag_union_not_recursive_recursive(subs, pool, ctx, union1, union2, *recursion_var) @@ -1206,7 +1504,8 @@ fn unify_flat_type( (Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret)) if l_args.len() == r_args.len() => { - let arg_problems = unify_zip_slices(subs, pool, *l_args, *r_args); + let arg_problems = + unify_zip_slices(subs, pool, *l_args.as_subs_slice(), *r_args.as_subs_slice()); let ret_problems = unify_pool(subs, pool, *l_ret, *r_ret); let closure_problems = unify_pool(subs, pool, *l_closure, *r_closure); @@ -1250,6 +1549,9 @@ fn unify_flat_type( ) } (FunctionOrTagUnion(tag_name_1, _, ext_1), FunctionOrTagUnion(tag_name_2, _, ext_2)) => { + let tag_name_1 = subs[*tag_name_1].clone(); + let tag_name_2 = subs[*tag_name_2].clone(); + if tag_name_1 == tag_name_2 { let problems = unify_pool(subs, pool, *ext_1, *ext_2); if problems.is_empty() { @@ -1260,31 +1562,33 @@ fn unify_flat_type( } } else { let mut tags1 = MutMap::default(); - tags1.insert(*tag_name_1.clone(), vec![]); + tags1.insert(tag_name_1, vec![]); let union1 = gather_tags(subs, tags1, *ext_1); let mut tags2 = MutMap::default(); - tags2.insert(*tag_name_2.clone(), vec![]); + tags2.insert(tag_name_2, vec![]); let union2 = gather_tags(subs, tags2, *ext_2); unify_tag_union(subs, pool, ctx, union1, union2, (None, None)) } } (TagUnion(tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => { - let union1 = gather_tags(subs, tags1.clone(), *ext1); + let tag_name = subs[*tag_name].clone(); + let union1 = gather_tags_new(subs, *tags1, *ext1); let mut tags2 = MutMap::default(); - tags2.insert(*tag_name.clone(), vec![]); + tags2.insert(tag_name, vec![]); let union2 = gather_tags(subs, tags2, *ext2); unify_tag_union(subs, pool, ctx, union1, union2, (None, None)) } (FunctionOrTagUnion(tag_name, _, ext1), TagUnion(tags2, ext2)) => { + let tag_name = subs[*tag_name].clone(); let mut tags1 = MutMap::default(); - tags1.insert(*tag_name.clone(), vec![]); - let union1 = gather_tags(subs, tags1, *ext1); + tags1.insert(tag_name, vec![]); - let union2 = gather_tags(subs, tags2.clone(), *ext2); + let union1 = gather_tags(subs, tags1, *ext1); + let union2 = gather_tags_new(subs, *tags2, *ext2); unify_tag_union(subs, pool, ctx, union1, union2, (None, None)) } @@ -1293,8 +1597,10 @@ fn unify_flat_type( // this never happens in type-correct programs, but may happen if there is a type error debug_assert!(is_recursion_var(subs, *recursion_var)); + let tag_name = subs[*tag_name].clone(); + let mut tags2 = MutMap::default(); - tags2.insert(*tag_name.clone(), vec![]); + tags2.insert(tag_name, vec![]); let union1 = gather_tags(subs, tags1.clone(), *ext1); let union2 = gather_tags(subs, tags2, *ext2); @@ -1312,8 +1618,10 @@ fn unify_flat_type( (FunctionOrTagUnion(tag_name, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { debug_assert!(is_recursion_var(subs, *recursion_var)); + let tag_name = subs[*tag_name].clone(); + let mut tags1 = MutMap::default(); - tags1.insert(*tag_name.clone(), vec![]); + tags1.insert(tag_name, vec![]); let union1 = gather_tags(subs, tags1, *ext1); let union2 = gather_tags(subs, tags2.clone(), *ext2); @@ -1504,8 +1812,50 @@ fn gather_tags( match subs.get_content_without_compacting(var) { Structure(TagUnion(sub_tags, sub_ext)) => { - for (k, v) in sub_tags { - tags.insert(k.clone(), v.clone()); + for (name_index, slice_index) in sub_tags.iter_all() { + let subs_slice = subs[slice_index]; + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + let tag = subs[name_index].clone(); + + tags.insert(tag, slice.to_vec()); + } + + let sub_ext = *sub_ext; + gather_tags(subs, tags, sub_ext) + } + + Alias(_, _, var) => { + // TODO according to elm/compiler: "TODO may be dropping useful alias info here" + let var = *var; + gather_tags(subs, tags, var) + } + + _ => TagUnionStructure { tags, ext: var }, + } +} + +fn gather_tags_new(subs: &mut Subs, input: UnionTags, var: Variable) -> TagUnionStructure { + use roc_types::subs::Content::*; + use roc_types::subs::FlatType::*; + + let mut tags = MutMap::default(); + + for (name_index, slice_index) in input.iter_all() { + let subs_slice = subs[slice_index]; + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + let tag = subs[name_index].clone(); + + tags.insert(tag, slice.to_vec()); + } + + match subs.get_content_without_compacting(var) { + Structure(TagUnion(sub_tags, sub_ext)) => { + for (k, v) in sub_tags.iter_all() { + let subs_slice = subs[v]; + let slice = subs.get_subs_slice(*subs_slice.as_subs_slice()); + let tag_name = subs[k].clone(); + + tags.insert(tag_name, slice.to_vec()); } let sub_ext = *sub_ext; @@ -1529,29 +1879,45 @@ fn is_recursion_var(subs: &Subs, var: Variable) -> bool { ) } +// TODO remove when all tags use SOA +pub fn from_mutmap( + subs: &mut Subs, + tags: MutMap>, + ext: Variable, +) -> FlatType { + let mut vec: Vec<_> = tags.into_iter().collect(); + + vec.sort(); + + let union_tags = UnionTags::insert_into_subs(subs, vec); + + FlatType::TagUnion(union_tags, ext) +} + #[allow(clippy::too_many_arguments)] fn unify_function_or_tag_union_and_func( subs: &mut Subs, pool: &mut Pool, ctx: &Context, - tag_name: &TagName, + tag_name_index: &SubsIndex, tag_symbol: Symbol, tag_ext: Variable, - function_arguments: SubsSlice, + function_arguments: VariableSubsSlice, function_return: Variable, function_lambda_set: Variable, left: bool, ) -> Outcome { - use FlatType::*; + let tag_name = subs[*tag_name_index].clone(); let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); new_tags.insert( - tag_name.clone(), - subs.get_subs_slice(function_arguments).to_owned(), + tag_name, + subs.get_subs_slice(*function_arguments.as_subs_slice()) + .to_owned(), ); - let content = Structure(TagUnion(new_tags, tag_ext)); + let content = Content::Structure(from_mutmap(subs, new_tags, tag_ext)); let new_tag_union_var = fresh(subs, pool, ctx, content); @@ -1567,7 +1933,7 @@ fn unify_function_or_tag_union_and_func( let mut closure_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); closure_tags.insert(TagName::Closure(tag_symbol), vec![]); - let lambda_set_content = Structure(TagUnion(closure_tags, lambda_set_ext)); + let lambda_set_content = Structure(from_mutmap(subs, closure_tags, lambda_set_ext)); let tag_lambda_set = register( subs, diff --git a/editor/src/lang/solve.rs b/editor/src/lang/solve.rs index 8a5a547eb7..60ff3f0098 100644 --- a/editor/src/lang/solve.rs +++ b/editor/src/lang/solve.rs @@ -10,9 +10,12 @@ use roc_module::symbol::Symbol; use roc_region::all::{Located, Region}; use roc_types::solved_types::Solved; use roc_types::subs::{ - Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, SubsSlice, Variable, + Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, Variable, + VariableSubsSlice, +}; +use roc_types::types::{ + gather_fields_unsorted_iter, Alias, Category, ErrorType, PatternCategory, RecordField, }; -use roc_types::types::{Alias, Category, ErrorType, PatternCategory, RecordField}; use roc_unify::unify::unify; use roc_unify::unify::Unified::*; @@ -711,7 +714,7 @@ fn type_to_variable<'a>( EmptyTagUnion => roc_types::subs::Variable::EMPTY_TAG_UNION, Record(fields, ext_id) => { - let mut field_vars = MutMap::default(); + let mut field_vars = Vec::new(); for node_id in fields.iter_node_ids() { use RecordField::*; @@ -748,24 +751,23 @@ fn type_to_variable<'a>( )), }; - field_vars.insert(field.as_str(mempool).into(), field_var); + field_vars.push((field.as_str(mempool).into(), field_var)); } let ext = mempool.get(*ext_id); let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext); - let new_ext_var = match roc_types::pretty_print::chase_ext_record( - subs, - temp_ext_var, - &mut field_vars, - ) { - Ok(()) => roc_types::subs::Variable::EMPTY_RECORD, - Err((new, _)) => new, - }; - let mut all_fields: Vec<_> = field_vars.into_iter().collect(); - all_fields.sort_unstable_by(RecordFields::compare); + let (it, new_ext_var) = + gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var); - let record_fields = RecordFields::insert_into_subs(subs, all_fields); + let it = it + .into_iter() + .map(|(field, field_type)| (field.clone(), field_type)); + + field_vars.extend(it); + field_vars.sort_unstable_by(RecordFields::compare); + + let record_fields = RecordFields::insert_into_subs(subs, field_vars); let content = Content::Structure(FlatType::Record(record_fields, new_ext_var)); @@ -854,7 +856,8 @@ fn type_to_variable<'a>( }; tag_vars.extend(ext_tag_vec.into_iter()); - let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var)); + let content = + Content::Structure(roc_unify::unify::from_mutmap(subs, tag_vars, new_ext_var)); register(subs, rank, pools, content) } @@ -871,11 +874,7 @@ fn type_to_variable<'a>( new_arg_vars.push(var) } - let start = subs.variables.len() as u32; - let length = arg_vars.len() as u16; - let arg_vars = SubsSlice::new(start, length); - - subs.variables.extend(new_arg_vars); + let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); let ret_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ret_type); let closure_var = @@ -981,7 +980,7 @@ fn check_for_infinite_type( ) { let var = loc_var.value; - while let Some((recursive, _chain)) = subs.occurs(var) { + while let Err((recursive, _chain)) = subs.occurs(var) { let description = subs.get(recursive); let content = description.content; @@ -1000,13 +999,16 @@ fn check_for_infinite_type( let mut new_tags = MutMap::default(); - for (label, args) in &tags { - let new_args: Vec<_> = args - .iter() - .map(|var| subs.explicit_substitute(recursive, rec_var, *var)) - .collect(); + for (name_index, slice_index) in tags.iter_all() { + let slice = subs[slice_index]; - new_tags.insert(label.clone(), new_args); + let mut new_vars = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + new_vars.push(subs.explicit_substitute(recursive, rec_var, var)); + } + + new_tags.insert(subs[name_index].clone(), new_vars); } let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var); @@ -1231,9 +1233,13 @@ fn adjust_rank_content( TagUnion(tags, ext_var) => { let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var); - for var in tags.values().flatten() { - rank = - rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var)); + for (_, index) in tags.iter_all() { + let slice = subs[index]; + for var_index in slice { + let var = subs[var_index]; + rank = rank + .max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); + } } rank @@ -1379,8 +1385,10 @@ fn instantiate_rigids_help( } TagUnion(tags, ext_var) => { - for (_, vars) in tags { - for var in vars.into_iter() { + for (_, index) in tags.iter_all() { + let slice = subs[index]; + for var_index in slice { + let var = subs[var_index]; instantiate_rigids_help(subs, max_rank, pools, var); } } @@ -1507,11 +1515,7 @@ fn deep_copy_var_help( new_arg_vars.push(copy_var); } - let start = subs.variables.len() as u32; - let length = arg_vars.len() as u16; - let arg_vars = SubsSlice::new(start, length); - - subs.variables.extend(new_arg_vars); + let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); Func(arg_vars, new_closure_var, new_ret_var) } @@ -1562,15 +1566,21 @@ fn deep_copy_var_help( TagUnion(tags, ext_var) => { let mut new_tags = MutMap::default(); - for (tag, vars) in tags { - let new_vars: Vec = vars - .into_iter() - .map(|var| deep_copy_var_help(subs, max_rank, pools, var)) - .collect(); + for (tag_index, index) in tags.iter_all() { + let tag = subs[tag_index].clone(); + let slice = subs[index]; + let mut new_vars = Vec::new(); + for var_index in slice { + let var = subs[var_index]; + let new_var = deep_copy_var_help(subs, max_rank, pools, var); + new_vars.push(new_var); + } + new_tags.insert(tag, new_vars); } - TagUnion(new_tags, deep_copy_var_help(subs, max_rank, pools, ext_var)) + let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var); + roc_unify::unify::from_mutmap(subs, new_tags, new_ext) } FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion(