Merge pull request #3541 from rtfeldman/rocasync

Changes to get roc-async working
This commit is contained in:
Folkert de Vries 2022-07-18 19:22:07 +02:00 committed by GitHub
commit ca38ec4eb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 164 deletions

View file

@ -890,15 +890,7 @@ fn unify_structure<M: MetaCollector>(
RecursionVar { structure, .. } => match flat_type {
FlatType::TagUnion(_, _) => {
// unify the structure with this unrecursive tag union
let mut outcome = unify_pool(subs, pool, ctx.first, *structure, ctx.mode);
if outcome.mismatches.is_empty() {
outcome.union(fix_tag_union_recursion_variable(
subs, ctx, ctx.first, other,
));
}
outcome
unify_pool(subs, pool, ctx.first, *structure, ctx.mode)
}
FlatType::RecursiveTagUnion(rec, _, _) => {
debug_assert!(is_recursion_var(subs, *rec));
@ -907,15 +899,7 @@ fn unify_structure<M: MetaCollector>(
}
FlatType::FunctionOrTagUnion(_, _, _) => {
// unify the structure with this unrecursive tag union
let mut outcome = unify_pool(subs, pool, ctx.first, *structure, ctx.mode);
if outcome.mismatches.is_empty() {
outcome.union(fix_tag_union_recursion_variable(
subs, ctx, ctx.first, other,
));
}
outcome
unify_pool(subs, pool, ctx.first, *structure, ctx.mode)
}
// Only tag unions can be recursive; everything else is an error.
_ => mismatch!(
@ -1376,57 +1360,6 @@ fn unify_lambda_set_help<M: MetaCollector>(
whole_outcome
}
/// Ensures that a non-recursive tag union, when unified with a recursion var to become a recursive
/// tag union, properly contains a recursion variable that recurses on itself.
//
// When might this not be the case? For example, in the code
//
// Indirect : [Indirect ConsList]
//
// ConsList : [Nil, Cons Indirect]
//
// l : ConsList
// l = Cons (Indirect (Cons (Indirect Nil)))
// # ^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~~~^ region-a
// # ~~~~~~~~~~~~~~~~~~~~~ region-b
// l
//
// Suppose `ConsList` has the expanded type `[Nil, Cons [Indirect <rec>]] as <rec>`.
// After unifying the tag application annotated "region-b" with the recursion variable `<rec>`,
// the tentative total-type of the application annotated "region-a" would be
// `<v> = [Nil, Cons [Indirect <v>]] as <rec>`. That is, the type of the recursive tag union
// would be inlined at the site "v", rather than passing through the correct recursion variable
// "rec" first.
//
// This is not incorrect from a type perspective, but causes problems later on for e.g. layout
// determination, which expects recursion variables to be placed correctly. Attempting to detect
// this during layout generation does not work so well because it may be that there *are* recursive
// tag unions that should be inlined, and not pass through recursion variables. So instead, try to
// resolve these cases here.
//
// See tests labeled "issue_2810" for more examples.
fn fix_tag_union_recursion_variable<M: MetaCollector>(
subs: &mut Subs,
ctx: &Context,
tag_union_promoted_to_recursive: Variable,
recursion_var: &Content,
) -> Outcome<M> {
debug_assert!(matches!(
subs.get_content_without_compacting(tag_union_promoted_to_recursive),
Structure(FlatType::RecursiveTagUnion(..))
));
let has_recursing_recursive_variable = subs
.occurs_including_recursion_vars(tag_union_promoted_to_recursive)
.is_err();
if !has_recursing_recursive_variable {
merge(subs, ctx, *recursion_var)
} else {
Outcome::default()
}
}
fn unify_record<M: MetaCollector>(
subs: &mut Subs,
pool: &mut Pool,
@ -2097,14 +2030,47 @@ fn unify_shared_tags_new<M: MetaCollector>(
outcome.union(unify_pool(subs, pool, actual, expected, ctx.mode));
// 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 outcome.mismatches.is_empty() {
matching_vars.push(expected);
}
} else if outcome.mismatches.is_empty() {
matching_vars.push(actual);
if outcome.mismatches.is_empty() {
// If one of the variables is a recursion var, keep that one, so that we avoid inlining
// a recursive tag union type content where we should have a recursion var instead.
//
// When might this happen? For example, in the code
//
// Indirect : [Indirect ConsList]
//
// ConsList : [Nil, Cons Indirect]
//
// l : ConsList
// l = Cons (Indirect (Cons (Indirect Nil)))
// # ^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~~~^ region-a
// # ~~~~~~~~~~~~~~~~~~~~~ region-b
// l
//
// Suppose `ConsList` has the expanded type `[Nil, Cons [Indirect <rec>]] as <rec>`.
// After unifying the tag application annotated "region-b" with the recursion variable `<rec>`,
// we might have that e.g. `actual` is `<rec>` and `expected` is `[Cons (Indirect ...)]`.
//
// Now, we need to be careful to set the type we choose to represent the merged type
// here to be `<rec>`, not the tag union content of `expected`! Otherwise, we will
// have lost a recursion variable in the recursive tag union.
//
// This would not be incorrect from a type perspective, but causes problems later on for e.g.
// layout generation, which expects recursion variables to be placed correctly. Attempting to detect
// this during layout generation does not work so well because it may be that there *are* recursive
// tag unions that should be inlined, and not pass through recursion variables. So instead, resolve
// these cases here.
//
// See tests labeled "issue_2810" for more examples.
let merged_var = match (
(actual, subs.get_content_unchecked(actual)),
(expected, subs.get_content_unchecked(expected)),
) {
((var, Content::RecursionVar { .. }), _)
| (_, (var, Content::RecursionVar { .. })) => var,
_ => actual,
};
matching_vars.push(merged_var);
}
total_outcome.union(outcome);