mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Improve alias<->opaque unification logic
This commit is contained in:
parent
a53ba3498b
commit
8b291854d3
2 changed files with 148 additions and 57 deletions
|
@ -358,8 +358,11 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
Structure(flat_type) => {
|
||||
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
|
||||
}
|
||||
Alias(symbol, args, real_var, kind) => {
|
||||
unify_alias(subs, pool, &ctx, *symbol, *args, *real_var, *kind)
|
||||
Alias(symbol, args, real_var, AliasKind::Structural) => {
|
||||
unify_alias(subs, pool, &ctx, *symbol, *args, *real_var)
|
||||
}
|
||||
Alias(symbol, args, real_var, AliasKind::Opaque) => {
|
||||
unify_opaque(subs, pool, &ctx, *symbol, *args, *real_var)
|
||||
}
|
||||
&RangedNumber(typ, range_vars) => unify_ranged_number(subs, pool, &ctx, typ, range_vars),
|
||||
Error => {
|
||||
|
@ -448,6 +451,56 @@ fn check_valid_range(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_two_aliases(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
symbol: Symbol,
|
||||
args: AliasVariables,
|
||||
real_var: Variable,
|
||||
other_args: AliasVariables,
|
||||
other_real_var: Variable,
|
||||
other_content: &Content,
|
||||
) -> Outcome {
|
||||
if args.len() == other_args.len() {
|
||||
let mut outcome = Outcome::default();
|
||||
let it = args
|
||||
.all_variables()
|
||||
.into_iter()
|
||||
.zip(other_args.all_variables().into_iter());
|
||||
|
||||
let args_unification_snapshot = subs.snapshot();
|
||||
|
||||
for (l, r) in it {
|
||||
let l_var = subs[l];
|
||||
let r_var = subs[r];
|
||||
outcome.union(unify_pool(subs, pool, l_var, r_var, ctx.mode));
|
||||
}
|
||||
|
||||
if outcome.mismatches.is_empty() {
|
||||
outcome.union(merge(subs, ctx, *other_content));
|
||||
}
|
||||
|
||||
let args_unification_had_changes = !subs
|
||||
.vars_since_snapshot(&args_unification_snapshot)
|
||||
.is_empty();
|
||||
subs.commit_snapshot(args_unification_snapshot);
|
||||
|
||||
if !args.is_empty() && args_unification_had_changes && outcome.mismatches.is_empty() {
|
||||
// We need to unify the real vars because unification of type variables
|
||||
// may have made them larger, which then needs to be reflected in the `real_var`.
|
||||
outcome.union(unify_pool(subs, pool, real_var, other_real_var, ctx.mode));
|
||||
}
|
||||
|
||||
outcome
|
||||
} else {
|
||||
dbg!(args.len(), other_args.len());
|
||||
mismatch!("{:?}", symbol)
|
||||
}
|
||||
}
|
||||
|
||||
// Unifies a structural alias
|
||||
#[inline(always)]
|
||||
fn unify_alias(
|
||||
subs: &mut Subs,
|
||||
|
@ -456,72 +509,40 @@ fn unify_alias(
|
|||
symbol: Symbol,
|
||||
args: AliasVariables,
|
||||
real_var: Variable,
|
||||
kind: AliasKind,
|
||||
) -> Outcome {
|
||||
let other_content = &ctx.second_desc.content;
|
||||
|
||||
let either_is_opaque =
|
||||
kind == AliasKind::Opaque || matches!(other_content, Alias(_, _, _, AliasKind::Opaque));
|
||||
let kind = AliasKind::Structural;
|
||||
|
||||
match other_content {
|
||||
FlexVar(_) => {
|
||||
// Alias wins
|
||||
merge(subs, ctx, Alias(symbol, args, real_var, kind))
|
||||
}
|
||||
RecursionVar { structure, .. } if !either_is_opaque => {
|
||||
unify_pool(subs, pool, real_var, *structure, ctx.mode)
|
||||
RecursionVar { structure, .. } => unify_pool(subs, pool, real_var, *structure, ctx.mode),
|
||||
RigidVar(_) | RigidAbleVar(..) | FlexAbleVar(..) => {
|
||||
unify_pool(subs, pool, real_var, ctx.second, ctx.mode)
|
||||
}
|
||||
RigidVar(_) | RigidAbleVar(..) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||
FlexAbleVar(_, ability) if kind == AliasKind::Opaque && args.is_empty() => {
|
||||
// Opaque type wins
|
||||
let mut outcome = merge(subs, ctx, Alias(symbol, args, real_var, kind));
|
||||
outcome.must_implement_ability.push(MustImplementAbility { typ: symbol, ability: *ability });
|
||||
outcome
|
||||
}
|
||||
Alias(other_symbol, other_args, other_real_var, _)
|
||||
// Opaques types are only equal if the opaque symbols are equal!
|
||||
if !either_is_opaque || symbol == *other_symbol =>
|
||||
{
|
||||
Alias(_, _, _, AliasKind::Opaque) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||
Alias(other_symbol, other_args, other_real_var, AliasKind::Structural) => {
|
||||
if symbol == *other_symbol {
|
||||
if args.len() == other_args.len() {
|
||||
let mut outcome = Outcome::default();
|
||||
let it = args
|
||||
.all_variables()
|
||||
.into_iter()
|
||||
.zip(other_args.all_variables().into_iter());
|
||||
|
||||
let args_unification_snapshot = subs.snapshot();
|
||||
|
||||
for (l, r) in it {
|
||||
let l_var = subs[l];
|
||||
let r_var = subs[r];
|
||||
outcome.union(unify_pool(subs, pool, l_var, r_var, ctx.mode));
|
||||
}
|
||||
|
||||
if outcome.mismatches.is_empty() {
|
||||
outcome.union(merge(subs, ctx, *other_content));
|
||||
}
|
||||
|
||||
let args_unification_had_changes = !subs.vars_since_snapshot(&args_unification_snapshot).is_empty();
|
||||
subs.commit_snapshot(args_unification_snapshot);
|
||||
|
||||
if !args.is_empty() && args_unification_had_changes && outcome.mismatches.is_empty() {
|
||||
// We need to unify the real vars because unification of type variables
|
||||
// may have made them larger, which then needs to be reflected in the `real_var`.
|
||||
outcome.union(unify_pool(subs, pool, real_var, *other_real_var, ctx.mode));
|
||||
}
|
||||
|
||||
outcome
|
||||
} else {
|
||||
dbg!(args.len(), other_args.len());
|
||||
mismatch!("{:?}", symbol)
|
||||
}
|
||||
unify_two_aliases(
|
||||
subs,
|
||||
pool,
|
||||
ctx,
|
||||
symbol,
|
||||
args,
|
||||
real_var,
|
||||
*other_args,
|
||||
*other_real_var,
|
||||
other_content,
|
||||
)
|
||||
} else {
|
||||
unify_pool(subs, pool, real_var, *other_real_var, ctx.mode)
|
||||
}
|
||||
}
|
||||
Structure(_) if !either_is_opaque => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||
RangedNumber(other_real_var, other_range_vars) if !either_is_opaque => {
|
||||
Structure(_) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||
RangedNumber(other_real_var, other_range_vars) => {
|
||||
let outcome = unify_pool(subs, pool, real_var, *other_real_var, ctx.mode);
|
||||
if outcome.mismatches.is_empty() {
|
||||
check_valid_range(subs, pool, real_var, *other_range_vars, ctx.mode)
|
||||
|
@ -530,9 +551,60 @@ fn unify_alias(
|
|||
}
|
||||
}
|
||||
Error => merge(subs, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_opaque(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
symbol: Symbol,
|
||||
args: AliasVariables,
|
||||
real_var: Variable,
|
||||
) -> Outcome {
|
||||
let other_content = &ctx.second_desc.content;
|
||||
|
||||
let kind = AliasKind::Opaque;
|
||||
|
||||
match other_content {
|
||||
FlexVar(_) => {
|
||||
// Alias wins
|
||||
merge(subs, ctx, Alias(symbol, args, real_var, kind))
|
||||
}
|
||||
RigidVar(_) | RigidAbleVar(..) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||
FlexAbleVar(_, ability) if args.is_empty() => {
|
||||
// Opaque type wins
|
||||
let mut outcome = merge(subs, ctx, Alias(symbol, args, real_var, kind));
|
||||
outcome.must_implement_ability.push(MustImplementAbility {
|
||||
typ: symbol,
|
||||
ability: *ability,
|
||||
});
|
||||
outcome
|
||||
}
|
||||
Alias(_, _, other_real_var, AliasKind::Structural) => {
|
||||
unify_pool(subs, pool, ctx.first, *other_real_var, ctx.mode)
|
||||
}
|
||||
Alias(other_symbol, other_args, other_real_var, AliasKind::Opaque) => {
|
||||
// Opaques types are only equal if the opaque symbols are equal!
|
||||
if symbol == *other_symbol {
|
||||
unify_two_aliases(
|
||||
subs,
|
||||
pool,
|
||||
ctx,
|
||||
symbol,
|
||||
args,
|
||||
real_var,
|
||||
*other_args,
|
||||
*other_real_var,
|
||||
other_content,
|
||||
)
|
||||
} else {
|
||||
mismatch!("{:?}", symbol)
|
||||
}
|
||||
}
|
||||
other => {
|
||||
// The type on the left is an alias, but the one on the right is not!
|
||||
debug_assert!(either_is_opaque);
|
||||
// The type on the left is an opaque, but the one on the right is not!
|
||||
mismatch!("Cannot unify opaque {:?} with {:?}", symbol, other)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue