Correctly check mutual functional recursion between opaque types

The mutual-recursion checks does not admit types that are not function
types; because Roc is strict, only functional values can be involved in
mutual recursion. However, this check was exercised by checking the head
constructor of a type, which is not the correct way to do it. Aliases
and opaque types may in fact be function types as well, so we must chase
their actual contents.

Closes #4246
This commit is contained in:
Ayaz Hafiz 2022-10-08 10:09:55 -05:00
parent 25d60d20cd
commit d9863cbbaa
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 32 additions and 2 deletions

View file

@ -1522,8 +1522,9 @@ fn solve(
symbols.iter().any(|(s, _)| {
let var = env.get_var_by_symbol(s).expect("Symbol not solved!");
let content = subs.get_content_without_compacting(var);
!matches!(content, Error | Structure(FlatType::Func(..)))
let (_, underlying_content) = chase_alias_content(subs, var);
!matches!(underlying_content, Error | Structure(FlatType::Func(..)))
})
};
@ -1555,6 +1556,17 @@ fn solve(
state
}
fn chase_alias_content(subs: &Subs, mut var: Variable) -> (Variable, &Content) {
loop {
match subs.get_content_without_compacting(var) {
Content::Alias(_, _, real_var, _) => {
var = *real_var;
}
content => return (var, content),
}
}
}
#[allow(clippy::too_many_arguments)]
fn compact_lambdas_and_check_obligations(
arena: &Bump,

View file

@ -7940,4 +7940,22 @@ mod solve_expr {
"U32 -> U32",
);
}
#[test]
fn issue_4246_admit_recursion_between_opaque_functions() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [b] to "./platform"
O := {} -> {}
a = @O \{} -> ((\@O f -> f {}) b)
b = a
"#
),
"O",
);
}
}