mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Unification of Any and Openness tag extensions
This commit is contained in:
parent
1c93727822
commit
281b71cf94
1 changed files with 53 additions and 7 deletions
|
@ -2405,6 +2405,41 @@ fn close_uninhabited_extended_union(subs: &mut Subs, mut var: Variable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn unify_tag_ext<M: MetaCollector>(
|
||||||
|
env: &mut Env,
|
||||||
|
pool: &mut Pool,
|
||||||
|
ext: TagExt,
|
||||||
|
var: Variable,
|
||||||
|
mode: Mode,
|
||||||
|
) -> Outcome<M> {
|
||||||
|
let legal_unification = match ext {
|
||||||
|
TagExt::Openness(_) => {
|
||||||
|
// Openness extensions can either unify with empty tag unions (marking them as closed),
|
||||||
|
// or flex/rigids. Anything else is a change in the monomorphic size of the tag and not
|
||||||
|
// a reflection of the polymorphism of the tag, an error.
|
||||||
|
match env.subs.get_content_without_compacting(var) {
|
||||||
|
FlexVar(..) | RigidVar(..) | FlexAbleVar(..) | RigidAbleVar(..) => true,
|
||||||
|
Structure(s) => match s {
|
||||||
|
FlatType::EmptyTagUnion => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Error => {
|
||||||
|
// errors propagate
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagExt::Any(_) => true,
|
||||||
|
};
|
||||||
|
if legal_unification {
|
||||||
|
unify_pool(env, pool, ext.var(), var, mode)
|
||||||
|
} else {
|
||||||
|
mismatch!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn unify_tag_unions<M: MetaCollector>(
|
fn unify_tag_unions<M: MetaCollector>(
|
||||||
|
@ -2454,7 +2489,10 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
if separate.only_in_1.is_empty() {
|
if separate.only_in_1.is_empty() {
|
||||||
if separate.only_in_2.is_empty() {
|
if separate.only_in_2.is_empty() {
|
||||||
let ext_outcome = if ctx.mode.is_eq() {
|
let ext_outcome = if ctx.mode.is_eq() {
|
||||||
// TODO(openness)
|
// Neither extension can grow in monomorphic tag sizes,
|
||||||
|
// so the extension must either be [] or a polymorphic extension variable like `a`.
|
||||||
|
// As such we can always unify directly even if the extensions are measuring
|
||||||
|
// openness.
|
||||||
unify_pool(env, pool, ext1.var(), ext2.var(), ctx.mode)
|
unify_pool(env, pool, ext1.var(), ext2.var(), ctx.mode)
|
||||||
} else {
|
} else {
|
||||||
// In a presence context, we don't care about ext2 being equal to ext1
|
// In a presence context, we don't care about ext2 being equal to ext1
|
||||||
|
@ -2500,19 +2538,21 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
ext1 = new_ext;
|
ext1 = new_ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext_outcome = unify_pool(env, pool, ext1.var(), extra_tags_in_2, ctx.mode);
|
let ext_outcome = unify_tag_ext(env, pool, ext1, extra_tags_in_2, ctx.mode);
|
||||||
|
|
||||||
if !ext_outcome.mismatches.is_empty() {
|
if !ext_outcome.mismatches.is_empty() {
|
||||||
return ext_outcome;
|
return ext_outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let combined_ext = ext1.map(|_| extra_tags_in_2);
|
||||||
|
|
||||||
let mut shared_tags_outcome = unify_shared_tags(
|
let mut shared_tags_outcome = unify_shared_tags(
|
||||||
env,
|
env,
|
||||||
pool,
|
pool,
|
||||||
ctx,
|
ctx,
|
||||||
shared_tags,
|
shared_tags,
|
||||||
OtherTags2::Empty,
|
OtherTags2::Empty,
|
||||||
ext1.map(|_| extra_tags_in_2),
|
combined_ext,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2550,7 +2590,7 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
ext2 = new_ext;
|
ext2 = new_ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext_outcome = unify_pool(env, pool, extra_tags_in_1, ext2.var(), ctx.mode);
|
let ext_outcome = unify_tag_ext(env, pool, ext2, extra_tags_in_1, ctx.mode);
|
||||||
|
|
||||||
if !ext_outcome.mismatches.is_empty() {
|
if !ext_outcome.mismatches.is_empty() {
|
||||||
return ext_outcome;
|
return ext_outcome;
|
||||||
|
@ -2562,13 +2602,15 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let combined_ext = ext2.map(|_| extra_tags_in_1);
|
||||||
|
|
||||||
let shared_tags_outcome = unify_shared_tags(
|
let shared_tags_outcome = unify_shared_tags(
|
||||||
env,
|
env,
|
||||||
pool,
|
pool,
|
||||||
ctx,
|
ctx,
|
||||||
shared_tags,
|
shared_tags,
|
||||||
OtherTags2::Empty,
|
OtherTags2::Empty,
|
||||||
ext2.map(|_| extra_tags_in_1),
|
combined_ext,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
);
|
);
|
||||||
total_outcome.union(shared_tags_outcome);
|
total_outcome.union(shared_tags_outcome);
|
||||||
|
@ -2589,7 +2631,11 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
} else {
|
} else {
|
||||||
Content::FlexVar(None)
|
Content::FlexVar(None)
|
||||||
};
|
};
|
||||||
|
// If ext1 or ext2 are `Openness`, we will necessarily get an error below when unifying
|
||||||
|
// the separate tags since `Openness` cannot grow in terms of width of tags. As such, the
|
||||||
|
// unified type, when it exists, must always be include an `Any` extension.
|
||||||
let ext = TagExt::Any(fresh(env, pool, ctx, ext_content));
|
let ext = TagExt::Any(fresh(env, pool, ctx, ext_content));
|
||||||
|
|
||||||
let flat_type1 = FlatType::TagUnion(unique_tags1, ext);
|
let flat_type1 = FlatType::TagUnion(unique_tags1, ext);
|
||||||
let flat_type2 = FlatType::TagUnion(unique_tags2, ext);
|
let flat_type2 = FlatType::TagUnion(unique_tags2, ext);
|
||||||
|
|
||||||
|
@ -2613,7 +2659,7 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
let mut total_outcome = Outcome::default();
|
let mut total_outcome = Outcome::default();
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
|
|
||||||
let ext1_outcome = unify_pool(env, pool, ext1.var(), sub2, ctx.mode);
|
let ext1_outcome = unify_tag_ext(env, pool, ext1, sub2, ctx.mode);
|
||||||
if !ext1_outcome.mismatches.is_empty() {
|
if !ext1_outcome.mismatches.is_empty() {
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
return ext1_outcome;
|
return ext1_outcome;
|
||||||
|
@ -2621,7 +2667,7 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
total_outcome.union(ext1_outcome);
|
total_outcome.union(ext1_outcome);
|
||||||
|
|
||||||
if ctx.mode.is_eq() {
|
if ctx.mode.is_eq() {
|
||||||
let ext2_outcome = unify_pool(env, pool, sub1, ext2.var(), ctx.mode);
|
let ext2_outcome = unify_tag_ext(env, pool, ext2, sub1, ctx.mode);
|
||||||
if !ext2_outcome.mismatches.is_empty() {
|
if !ext2_outcome.mismatches.is_empty() {
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
return ext2_outcome;
|
return ext2_outcome;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue