mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 16:51:53 +03:00
Merge branch 'trunk' into debug-flag
This commit is contained in:
commit
f2ed3923a4
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user