all the work

This commit is contained in:
Folkert 2021-08-07 00:51:53 +02:00
parent 1cee8f77b9
commit 69c224843e
10 changed files with 582 additions and 366 deletions

View file

@ -5,7 +5,7 @@ use roc_types::subs::Content::{self, *};
use roc_types::subs::{
Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, RecordFields, Subs, SubsSlice, Variable,
};
use roc_types::types::{gather_fields_ref, ErrorType, Mismatch, RecordField, RecordStructure};
use roc_types::types::{gather_fields, ErrorType, Mismatch, RecordField, RecordStructure};
macro_rules! mismatch {
() => {{
@ -259,34 +259,35 @@ fn unify_record(
subs: &mut Subs,
pool: &mut Pool,
ctx: &Context,
rec1: RecordStructure,
rec2: RecordStructure,
fields1: RecordFields,
ext1: Variable,
fields2: RecordFields,
ext2: Variable,
) -> Outcome {
let fields1 = rec1.fields;
let fields2 = rec2.fields;
let separate = RecordFields::separate(fields1, fields2);
let (separate, ext1, ext2) = separate_record_fields(subs, fields1, ext1, fields2, ext2);
let shared_fields = separate.in_both;
if separate.only_in_1.is_empty() {
if separate.only_in_2.is_empty() {
let ext_problems = unify_pool(subs, pool, rec1.ext, rec2.ext);
// these variable will be the empty record, but we must still unify them
let ext_problems = unify_pool(subs, pool, ext1, ext2);
if !ext_problems.is_empty() {
return ext_problems;
}
let mut field_problems =
unify_shared_fields(subs, pool, ctx, shared_fields, OtherFields::None, rec1.ext);
unify_shared_fields(subs, pool, ctx, shared_fields, OtherFields::None, ext1);
field_problems.extend(ext_problems);
field_problems
} else {
let flat_type = FlatType::Record(separate.only_in_2, rec2.ext);
let only_in_2 = RecordFields::insert_into_subs(subs, separate.only_in_2);
let flat_type = FlatType::Record(only_in_2, ext2);
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
let ext_problems = unify_pool(subs, pool, rec1.ext, sub_record);
let ext_problems = unify_pool(subs, pool, ext1, sub_record);
if !ext_problems.is_empty() {
return ext_problems;
@ -306,9 +307,10 @@ fn unify_record(
field_problems
}
} else if separate.only_in_2.is_empty() {
let flat_type = FlatType::Record(separate.only_in_1, rec1.ext);
let only_in_1 = RecordFields::insert_into_subs(subs, separate.only_in_1);
let flat_type = FlatType::Record(only_in_1, ext1);
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
let ext_problems = unify_pool(subs, pool, sub_record, rec2.ext);
let ext_problems = unify_pool(subs, pool, sub_record, ext2);
if !ext_problems.is_empty() {
return ext_problems;
@ -327,26 +329,24 @@ fn unify_record(
field_problems
} else {
let it = (&separate.only_in_1)
.into_iter()
.chain((&separate.only_in_2).into_iter());
let other: RecordFields = it.collect();
let only_in_1 = RecordFields::insert_into_subs(subs, separate.only_in_1);
let only_in_2 = RecordFields::insert_into_subs(subs, separate.only_in_2);
let other_fields = OtherFields::Other(other);
let other_fields = OtherFields::Other(only_in_1, only_in_2);
let ext = fresh(subs, pool, ctx, Content::FlexVar(None));
let flat_type1 = FlatType::Record(separate.only_in_1, ext);
let flat_type2 = FlatType::Record(separate.only_in_2, ext);
let flat_type1 = FlatType::Record(only_in_1, ext);
let flat_type2 = FlatType::Record(only_in_2, ext);
let sub1 = fresh(subs, pool, ctx, Structure(flat_type1));
let sub2 = fresh(subs, pool, ctx, Structure(flat_type2));
let rec1_problems = unify_pool(subs, pool, rec1.ext, sub2);
let rec1_problems = unify_pool(subs, pool, ext1, sub2);
if !rec1_problems.is_empty() {
return rec1_problems;
}
let rec2_problems = unify_pool(subs, pool, sub1, rec2.ext);
let rec2_problems = unify_pool(subs, pool, sub1, ext2);
if !rec2_problems.is_empty() {
return rec2_problems;
}
@ -364,21 +364,21 @@ fn unify_record(
enum OtherFields {
None,
Other(RecordFields),
Other(RecordFields, RecordFields),
}
fn unify_shared_fields(
subs: &mut Subs,
pool: &mut Pool,
ctx: &Context,
shared_fields: Vec<(Lowercase, RecordField<Variable>, RecordField<Variable>)>,
shared_fields: Vec<(Lowercase, (RecordField<Variable>, RecordField<Variable>))>,
other_fields: OtherFields,
ext: Variable,
) -> Outcome {
let mut matching_fields = Vec::with_capacity(shared_fields.len());
let num_shared_fields = shared_fields.len();
for (name, actual, expected) in shared_fields {
for (name, (actual, expected)) in shared_fields {
let local_problems = unify_pool(subs, pool, actual.into_inner(), expected.into_inner());
if local_problems.is_empty() {
@ -418,22 +418,44 @@ fn unify_shared_fields(
Err((new, _)) => new,
};
let mut ext_fields: Vec<_> = ext_fields.into_iter().collect();
ext_fields.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));
let fields: RecordFields = match other_fields {
OtherFields::None => {
if ext_fields.is_empty() {
RecordFields::from_sorted_vec(matching_fields)
RecordFields::insert_into_subs(subs, matching_fields)
} else {
matching_fields
.into_iter()
.chain(ext_fields.into_iter())
.collect()
let all_fields = merge_sorted(matching_fields, ext_fields);
RecordFields::insert_into_subs(subs, all_fields)
}
}
OtherFields::Other(other_fields) => matching_fields
.into_iter()
.chain(other_fields.into_iter())
.chain(ext_fields.into_iter())
.collect(),
OtherFields::Other(other1, other2) => {
let mut all_fields = merge_sorted(matching_fields, ext_fields);
all_fields = merge_sorted(
all_fields,
other1.iter_all().map(|(i1, i2, i3)| {
let field_name: Lowercase = subs[i1].clone();
let variable = subs[i2];
let record_field: RecordField<Variable> = subs[i3].map(|_| variable);
(field_name, record_field)
}),
);
all_fields = merge_sorted(
all_fields,
other2.iter_all().map(|(i1, i2, i3)| {
let field_name: Lowercase = subs[i1].clone();
let variable = subs[i2];
let record_field: RecordField<Variable> = subs[i3].map(|_| variable);
(field_name, record_field)
}),
);
RecordFields::insert_into_subs(subs, all_fields)
}
};
let flat_type = FlatType::Record(fields, new_ext_var);
@ -444,13 +466,133 @@ fn unify_shared_fields(
}
}
fn separate_record_fields(
subs: &Subs,
fields1: RecordFields,
ext1: Variable,
fields2: RecordFields,
ext2: Variable,
) -> (
Separate<Lowercase, RecordField<Variable>>,
Variable,
Variable,
) {
let (it1, new_ext1) = fields1.sorted_iterator_help(subs, ext1);
let (it2, new_ext2) = fields2.sorted_iterator_help(subs, ext2);
use std::iter::FromIterator;
let it1 = Vec::from_iter(it1);
let it2 = Vec::from_iter(it2);
(separate(it1, it2), new_ext1, new_ext2)
}
#[derive(Debug)]
struct Separate<K, V> {
only_in_1: Vec<(K, V)>,
only_in_2: Vec<(K, V)>,
in_both: Vec<(K, (V, V))>,
}
fn merge_sorted<K, V, I1, I2>(input1: I1, input2: I2) -> Vec<(K, V)>
where
K: Ord,
I1: IntoIterator<Item = (K, V)>,
I2: IntoIterator<Item = (K, V)>,
{
use std::cmp::Ordering;
let mut it1 = input1.into_iter().peekable();
let mut it2 = input2.into_iter().peekable();
let input1_len = it1.size_hint().0;
let input2_len = it2.size_hint().0;
let mut result = Vec::with_capacity(input1_len + input2_len);
loop {
let which = match (it1.peek(), it2.peek()) {
(Some((l, _)), Some((r, _))) => Some(l.cmp(&r)),
(Some(_), None) => Some(Ordering::Less),
(None, Some(_)) => Some(Ordering::Greater),
(None, None) => None,
};
match which {
Some(Ordering::Less) => {
result.push(it1.next().unwrap());
}
Some(Ordering::Equal) => {
let (k, v) = it1.next().unwrap();
let (_, _) = it2.next().unwrap();
result.push((k, v));
}
Some(Ordering::Greater) => {
result.push(it2.next().unwrap());
}
None => break,
}
}
result
}
fn separate<K, V, I1, I2>(input1: I1, input2: I2) -> Separate<K, V>
where
K: Ord,
I1: IntoIterator<Item = (K, V)>,
I2: IntoIterator<Item = (K, V)>,
{
use std::cmp::Ordering;
let mut it1 = input1.into_iter().peekable();
let mut it2 = input2.into_iter().peekable();
let input1_len = it1.size_hint().0;
let input2_len = it2.size_hint().0;
let max_common = input1_len.min(input2_len);
let mut result = Separate {
only_in_1: Vec::with_capacity(input1_len),
only_in_2: Vec::with_capacity(input2_len),
in_both: Vec::with_capacity(max_common),
};
loop {
let which = match (it1.peek(), it2.peek()) {
(Some((l, _)), Some((r, _))) => Some(l.cmp(&r)),
(Some(_), None) => Some(Ordering::Less),
(None, Some(_)) => Some(Ordering::Greater),
(None, None) => None,
};
match which {
Some(Ordering::Less) => {
result.only_in_1.push(it1.next().unwrap());
}
Some(Ordering::Equal) => {
let (k, v1) = it1.next().unwrap();
let (_, v2) = it2.next().unwrap();
result.in_both.push((k, (v1, v2)));
}
Some(Ordering::Greater) => {
result.only_in_2.push(it2.next().unwrap());
}
None => break,
}
}
result
}
struct SeparateTags<K, V> {
only_in_1: MutMap<K, V>,
only_in_2: MutMap<K, V>,
in_both: MutMap<K, (V, V)>,
}
fn separate<K, V>(tags1: MutMap<K, V>, mut tags2: MutMap<K, V>) -> Separate<K, V>
fn separate_tags<K, V>(tags1: MutMap<K, V>, mut tags2: MutMap<K, V>) -> SeparateTags<K, V>
where
K: Ord + std::hash::Hash,
{
@ -470,7 +612,7 @@ where
}
}
Separate {
SeparateTags {
only_in_1,
only_in_2: tags2,
in_both,
@ -505,11 +647,11 @@ fn unify_tag_union(
return unify_shared_tags_merge(subs, ctx, tags1, rec1.ext, recursion_var);
}
let Separate {
let SeparateTags {
only_in_1: unique_tags1,
only_in_2: unique_tags2,
in_both: shared_tags,
} = separate(tags1, tags2);
} = separate_tags(tags1, tags2);
if unique_tags1.is_empty() {
if unique_tags2.is_empty() {
@ -985,19 +1127,16 @@ fn unify_flat_type(
match (left, right) {
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
(Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields() => {
(Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields(subs) => {
unify_pool(subs, pool, *ext, ctx.second)
}
(EmptyRecord, Record(fields, ext)) if fields.has_only_optional_fields() => {
(EmptyRecord, Record(fields, ext)) if fields.has_only_optional_fields(subs) => {
unify_pool(subs, pool, ctx.first, *ext)
}
(Record(fields1, ext1), Record(fields2, ext2)) => {
let rec1 = gather_fields_ref(subs, fields1, *ext1);
let rec2 = gather_fields_ref(subs, fields2, *ext2);
unify_record(subs, pool, ctx, rec1, rec2)
unify_record(subs, pool, ctx, *fields1, *ext1, *fields2, *ext2)
}
(EmptyTagUnion, EmptyTagUnion) => merge(subs, ctx, Structure(left.clone())),