This commit is contained in:
Folkert 2021-08-12 22:19:02 +02:00
parent 46f6e86716
commit 35874a6b1c

View file

@ -630,6 +630,14 @@ fn separate_union_tags(
(separate(it1, it2), new_ext1, new_ext2) (separate(it1, it2), new_ext1, new_ext2)
} }
#[derive(Debug, Copy, Clone)]
enum Rec {
None,
Left(Variable),
Right(Variable),
Both(Variable, Variable),
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn unify_tag_union_new( fn unify_tag_union_new(
subs: &mut Subs, subs: &mut Subs,
@ -639,17 +647,11 @@ fn unify_tag_union_new(
initial_ext1: Variable, initial_ext1: Variable,
tags2: UnionTags, tags2: UnionTags,
initial_ext2: Variable, initial_ext2: Variable,
recursion: (Option<Variable>, Option<Variable>), recursion_var: Rec,
) -> Outcome { ) -> Outcome {
let (separate, ext1, ext2) = let (separate, ext1, ext2) =
separate_union_tags(subs, tags1, initial_ext1, tags2, initial_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; let shared_tags = separate.in_both;
if separate.only_in_1.is_empty() { if separate.only_in_1.is_empty() {
@ -789,7 +791,7 @@ fn unify_shared_tags_new(
shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>, shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>,
other_tags: OtherTags2, other_tags: OtherTags2,
ext: Variable, ext: Variable,
recursion_var: Option<Variable>, recursion_var: Rec,
) -> Outcome { ) -> Outcome {
let mut matching_tags = Vec::default(); let mut matching_tags = Vec::default();
let num_shared_tags = shared_tags.len(); let num_shared_tags = shared_tags.len();
@ -826,11 +828,15 @@ fn unify_shared_tags_new(
// and so on until the whole non-recursive tag union can be unified with it. // and so on until the whole non-recursive tag union can be unified with it.
let mut problems = Vec::new(); let mut problems = Vec::new();
{ problems.extend(unify_pool(subs, pool, actual, expected));
problems.extend(unify_pool(subs, pool, actual, expected));
}
if problems.is_empty() { // clearly, this is very suspicious: these variables have just been unified. And yet,
// not doing this leads to stack overflows
if let Rec::Right(_) = recursion_var {
if problems.is_empty() {
matching_vars.push(expected);
}
} else if problems.is_empty() {
matching_vars.push(actual); matching_vars.push(actual);
} }
} }
@ -898,13 +904,14 @@ fn unify_shared_tags_merge_new(
ctx: &Context, ctx: &Context,
new_tags: UnionTags, new_tags: UnionTags,
new_ext_var: Variable, new_ext_var: Variable,
recursion_var: Option<Variable>, recursion_var: Rec,
) -> Outcome { ) -> Outcome {
let flat_type = if let Some(rec) = recursion_var { let flat_type = match recursion_var {
debug_assert!(is_recursion_var(subs, rec)); Rec::None => FlatType::TagUnion(new_tags, new_ext_var),
FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var) Rec::Left(rec) | Rec::Right(rec) | Rec::Both(rec, _) => {
} else { debug_assert!(is_recursion_var(subs, rec));
FlatType::TagUnion(new_tags, new_ext_var) FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var)
}
}; };
merge(subs, ctx, Structure(flat_type)) merge(subs, ctx, Structure(flat_type))
@ -1441,38 +1448,31 @@ fn unify_flat_type(
} }
(TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => { (TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => {
unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, (None, None)) unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, Rec::None)
} }
(RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => { (RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => {
debug_assert!(is_recursion_var(subs, *recursion_var)); debug_assert!(is_recursion_var(subs, *recursion_var));
// this never happens in type-correct programs, but may happen if there is a type error // this never happens in type-correct programs, but may happen if there is a type error
let union1 = gather_tags_new(subs, *tags1, *ext1);
let union2 = gather_tags_new(subs, *tags2, *ext2);
unify_tag_union( let rec = Rec::Left(*recursion_var);
subs,
pool, unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec)
ctx,
union1,
union2,
(Some(*recursion_var), None),
)
} }
(TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { (TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
debug_assert!(is_recursion_var(subs, *recursion_var)); debug_assert!(is_recursion_var(subs, *recursion_var));
let union1 = gather_tags_new(subs, *tags1, *ext1);
let union2 = gather_tags_new(subs, *tags2, *ext2);
unify_tag_union_not_recursive_recursive(subs, pool, ctx, union1, union2, *recursion_var) let rec = Rec::Right(*recursion_var);
unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec)
} }
(RecursiveTagUnion(rec1, tags1, ext1), RecursiveTagUnion(rec2, tags2, ext2)) => { (RecursiveTagUnion(rec1, tags1, ext1), RecursiveTagUnion(rec2, tags2, ext2)) => {
debug_assert!(is_recursion_var(subs, *rec1)); debug_assert!(is_recursion_var(subs, *rec1));
debug_assert!(is_recursion_var(subs, *rec2)); debug_assert!(is_recursion_var(subs, *rec2));
let rec = (Some(*rec1), Some(*rec2)); let rec = Rec::Both(*rec1, *rec2);
let mut problems = let mut problems =
unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec); unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec);
problems.extend(unify_pool(subs, pool, *rec1, *rec2)); problems.extend(unify_pool(subs, pool, *rec1, *rec2));
@ -1536,12 +1536,12 @@ fn unify_flat_type(
false, false,
) )
} }
(FunctionOrTagUnion(tag_name_1, _, ext_1), FunctionOrTagUnion(tag_name_2, _, ext_2)) => { (FunctionOrTagUnion(tag_name_1, _, ext1), FunctionOrTagUnion(tag_name_2, _, ext2)) => {
let tag_name_1 = subs[*tag_name_1].clone(); let tag_name_1_ref = &subs[*tag_name_1];
let tag_name_2 = subs[*tag_name_2].clone(); let tag_name_2_ref = &subs[*tag_name_2];
if tag_name_1 == tag_name_2 { if tag_name_1_ref == tag_name_2_ref {
let problems = unify_pool(subs, pool, *ext_1, *ext_2); let problems = unify_pool(subs, pool, *ext1, *ext2);
if problems.is_empty() { if problems.is_empty() {
let content = subs.get_content_without_compacting(ctx.second).clone(); let content = subs.get_content_without_compacting(ctx.second).clone();
merge(subs, ctx, content) merge(subs, ctx, content)
@ -1549,72 +1549,41 @@ fn unify_flat_type(
problems problems
} }
} else { } else {
let mut tags1 = MutMap::default(); let tags1 = UnionTags::from_tag_name_index(*tag_name_1);
tags1.insert(tag_name_1, vec![]); let tags2 = UnionTags::from_tag_name_index(*tag_name_2);
let union1 = gather_tags(subs, tags1, *ext_1);
let mut tags2 = MutMap::default(); unify_tag_union_new(subs, pool, ctx, tags1, *ext1, tags2, *ext2, Rec::None)
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)) => { (TagUnion(tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => {
let tag_name = subs[*tag_name].clone(); let tags2 = UnionTags::from_tag_name_index(*tag_name);
let union1 = gather_tags_new(subs, *tags1, *ext1);
let mut tags2 = MutMap::default(); unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, tags2, *ext2, Rec::None)
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)) => { (FunctionOrTagUnion(tag_name, _, ext1), TagUnion(tags2, ext2)) => {
let tag_name = subs[*tag_name].clone(); let tags1 = UnionTags::from_tag_name_index(*tag_name);
let mut tags1 = MutMap::default();
tags1.insert(tag_name, vec![]);
let union1 = gather_tags(subs, tags1, *ext1); unify_tag_union_new(subs, pool, ctx, tags1, *ext1, *tags2, *ext2, Rec::None)
let union2 = gather_tags_new(subs, *tags2, *ext2);
unify_tag_union(subs, pool, ctx, union1, union2, (None, None))
} }
(RecursiveTagUnion(recursion_var, tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => { (RecursiveTagUnion(recursion_var, tags1, ext1), FunctionOrTagUnion(tag_name, _, ext2)) => {
// this never happens in type-correct programs, but may happen if there is a type error // this never happens in type-correct programs, but may happen if there is a type error
debug_assert!(is_recursion_var(subs, *recursion_var)); debug_assert!(is_recursion_var(subs, *recursion_var));
let tag_name = subs[*tag_name].clone(); let tags2 = UnionTags::from_tag_name_index(*tag_name);
let rec = Rec::Left(*recursion_var);
let mut tags2 = MutMap::default(); unify_tag_union_new(subs, pool, ctx, *tags1, *ext1, tags2, *ext2, rec)
tags2.insert(tag_name, vec![]);
let union1 = gather_tags_new(subs, *tags1, *ext1);
let union2 = gather_tags(subs, tags2, *ext2);
unify_tag_union(
subs,
pool,
ctx,
union1,
union2,
(Some(*recursion_var), None),
)
} }
(FunctionOrTagUnion(tag_name, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => { (FunctionOrTagUnion(tag_name, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
debug_assert!(is_recursion_var(subs, *recursion_var)); debug_assert!(is_recursion_var(subs, *recursion_var));
let tag_name = subs[*tag_name].clone(); let tags1 = UnionTags::from_tag_name_index(*tag_name);
let rec = Rec::Right(*recursion_var);
let mut tags1 = MutMap::default(); // NOTE arguments are flipped by design
tags1.insert(tag_name, vec![]); unify_tag_union_new(subs, pool, ctx, tags1, *ext1, *tags2, *ext2, rec)
let union1 = gather_tags(subs, tags1, *ext1);
let union2 = gather_tags_new(subs, *tags2, *ext2);
unify_tag_union_not_recursive_recursive(subs, pool, ctx, union1, union2, *recursion_var)
} }
(other1, other2) => mismatch!( (other1, other2) => mismatch!(