mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
all the work
This commit is contained in:
parent
1cee8f77b9
commit
69c224843e
10 changed files with 582 additions and 366 deletions
|
@ -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())),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue