Merge branch 'trunk' into debug-flag

This commit is contained in:
joshuaharry 2021-08-11 21:20:08 -05:00 committed by GitHub
commit f2ed3923a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1251 additions and 302 deletions

View File

@ -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<Variable>)> =
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> = 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<Variable>)> =
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> = 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

View File

@ -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<Variable>,
argument_variables: VariableSubsSlice,
return_variable: Variable,
tag_name: TagName,
proc_symbol: Symbol,

View File

@ -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>, 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<Lowercase, RecordField<Variable>>,
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, 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(

View File

@ -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<Variable> = 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(

View File

@ -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<TagName, Vec<Variable>>,
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<TagName, _> = 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<Lowercase, RecordField<Variable>>,
) -> 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,

View File

@ -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);

View File

@ -56,6 +56,7 @@ pub struct Subs {
pub tag_names: Vec<TagName>,
pub field_names: Vec<Lowercase>,
pub record_fields: Vec<RecordField<()>>,
pub variable_slices: Vec<VariableSubsSlice>,
}
/// A slice into the Vec<T> of subs
@ -99,6 +100,20 @@ impl std::ops::Index<SubsIndex<Lowercase>> for Subs {
}
}
impl std::ops::Index<SubsIndex<TagName>> for Subs {
type Output = TagName;
fn index(&self, index: SubsIndex<TagName>) -> &Self::Output {
&self.tag_names[index.start as usize]
}
}
impl std::ops::IndexMut<SubsIndex<TagName>> for Subs {
fn index_mut(&mut self, index: SubsIndex<TagName>) -> &mut Self::Output {
&mut self.tag_names[index.start as usize]
}
}
impl std::ops::IndexMut<SubsIndex<Lowercase>> for Subs {
fn index_mut(&mut self, index: SubsIndex<Lowercase>) -> &mut Self::Output {
&mut self.field_names[index.start as usize]
@ -119,6 +134,20 @@ impl std::ops::IndexMut<SubsIndex<RecordField<()>>> for Subs {
}
}
impl std::ops::Index<SubsIndex<VariableSubsSlice>> for Subs {
type Output = VariableSubsSlice;
fn index(&self, index: SubsIndex<VariableSubsSlice>) -> &Self::Output {
&self.variable_slices[index.start as usize]
}
}
impl std::ops::IndexMut<SubsIndex<VariableSubsSlice>> for Subs {
fn index_mut(&mut self, index: SubsIndex<VariableSubsSlice>) -> &mut Self::Output {
&mut self.variable_slices[index.start as usize]
}
}
// custom debug
impl<T> std::fmt::Debug for SubsIndex<T> {
@ -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<Variable>)> {
pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
occurs(self, &ImSet::default(), var)
}
@ -829,10 +865,10 @@ impl Content {
#[derive(Clone, Debug)]
pub enum FlatType {
Apply(Symbol, Vec<Variable>),
Func(SubsSlice<Variable>, Variable, Variable),
Func(VariableSubsSlice, Variable, Variable),
Record(RecordFields, Variable),
TagUnion(MutMap<TagName, Vec<Variable>>, Variable),
FunctionOrTagUnion(Box<TagName>, Symbol, Variable),
TagUnion(UnionTags, Variable),
FunctionOrTagUnion(SubsIndex<TagName>, Symbol, Variable),
RecursiveTagUnion(Variable, MutMap<TagName, Vec<Variable>>, Variable),
Erroneous(Box<Problem>),
EmptyRecord,
@ -847,6 +883,249 @@ pub enum Builtin {
EmptyRecord,
}
#[derive(Clone, Copy, Debug)]
pub struct VariableSubsSlice {
slice: SubsSlice<Variable>,
}
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<Variable> {
&self.slice
}
pub fn new(start: u32, length: u16) -> Self {
Self {
slice: SubsSlice::new(start, length),
}
}
pub fn insert_into_subs<I>(subs: &mut Subs, input: I) -> Self
where
I: IntoIterator<Item = Variable>,
{
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<Variable>;
type IntoIter = <SubsSlice<Variable> 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<TagName>,
pub variables: SubsSlice<VariableSubsSlice>,
}
impl UnionTags {
pub fn len(&self) -> usize {
self.tag_names.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn compare<T>(x: &(TagName, T), y: &(TagName, T)) -> std::cmp::Ordering {
first(x, y)
}
pub fn insert_into_subs<I, I2>(subs: &mut Subs, input: I) -> Self
where
I: IntoIterator<Item = (TagName, I2)>,
I2: IntoIterator<Item = Variable>,
{
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<I>(subs: &mut Subs, input: I) -> Self
where
I: IntoIterator<Item = (TagName, VariableSubsSlice)>,
{
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<Item = (SubsIndex<TagName>, SubsIndex<VariableSubsSlice>)> {
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<Item = (&TagName, &[Variable])> + '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<Item = (&TagName, &[Variable])> + '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<dyn Iterator<Item = (TagName, &'a [Variable])> + 'a>;
pub type SortedTagsSlicesIterator<'a> = Box<dyn Iterator<Item = (TagName, VariableSubsSlice)> + '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<Variable>,
input_var: Variable,
) -> Option<(Variable, Vec<Variable>)> {
) -> Result<(), (Variable, Vec<Variable>)> {
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<Variable>,
iter: T,
) -> Option<(Variable, Vec<Variable>)>
) -> Result<(), (Variable, Vec<Variable>)>
where
T: Iterator<Item = &'a Variable>,
{
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<Variable>,
var: Variable,
) -> Result<(), (Variable, Vec<Variable>)> {
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);

View File

@ -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<Item = (&TagName, VariableSubsSlice)> + '_,
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,
}
}

View File

@ -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<TagName, VariableSubsSlice>, 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::<Vec<_>>();
let it2 = it2.collect::<Vec<_>>();
(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<Variable>, Option<Variable>),
) -> 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<Variable>,
) -> 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<Variable>,
) -> 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<TagName, Vec<Variable>>,
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<TagName>,
tag_symbol: Symbol,
tag_ext: Variable,
function_arguments: SubsSlice<Variable>,
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,

View File

@ -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<Variable> = 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(