Illegal self recursion on toplevel defs

Closes #4153
This commit is contained in:
Ayaz Hafiz 2022-10-03 13:14:56 -05:00
parent fd4b20de58
commit 92b754f292
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 51 additions and 8 deletions

View file

@ -1516,8 +1516,10 @@ pub(crate) fn sort_can_defs_new(
let def = defs.pop().unwrap();
let index = group.first_one().unwrap();
if def_ordering.direct_references.get_row_col(index, index) {
// a definition like `x = x + 1`, which is invalid in roc
let bad_recursion_body = if def_ordering.direct_references.get_row_col(index, index)
{
// a definition like `x = x + 1`, which is invalid in roc.
// We need to convert the body of the def to a runtime error.
let symbol = def_ordering.get_symbol(index).unwrap();
let entries = vec![make_cycle_entry(symbol, &def)];
@ -1525,9 +1527,19 @@ pub(crate) fn sort_can_defs_new(
let problem = Problem::RuntimeError(RuntimeError::CircularDef(entries.clone()));
env.problem(problem);
// Declaration::InvalidCycle(entries)
todo!("InvalidCycle: {:?}", entries)
} else if def_ordering.references.get_row_col(index, index) {
Some(Expr::RuntimeError(RuntimeError::CircularDef(entries)))
} else {
None
};
let is_illegally_self_recursive = bad_recursion_body.is_some();
let set_opt_invalid_recursion_body = |e: &mut Expr| match bad_recursion_body {
Some(err) => *e = err,
None => {}
};
if def_ordering.references.get_row_col(index, index) && !is_illegally_self_recursive
{
// this function calls itself, and must be typechecked as a recursive def
match def.loc_pattern.value {
Pattern::Identifier(symbol) => match def.loc_expr.value {
@ -1540,7 +1552,7 @@ pub(crate) fn sort_can_defs_new(
None,
);
}
_ => todo!(),
e => todo!("{:?}", e),
},
Pattern::AbilityMemberSpecialization {
ident: symbol,
@ -1562,7 +1574,9 @@ pub(crate) fn sort_can_defs_new(
} else {
match def.loc_pattern.value {
Pattern::Identifier(symbol) => match def.loc_expr.value {
Closure(closure_data) => {
Closure(mut closure_data) => {
set_opt_invalid_recursion_body(&mut closure_data.loc_body.value);
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
@ -1572,6 +1586,9 @@ pub(crate) fn sort_can_defs_new(
);
}
_ => {
let mut def = def;
set_opt_invalid_recursion_body(&mut def.loc_expr.value);
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
@ -1585,7 +1602,9 @@ pub(crate) fn sort_can_defs_new(
ident: symbol,
specializes,
} => match def.loc_expr.value {
Closure(closure_data) => {
Closure(mut closure_data) => {
set_opt_invalid_recursion_body(&mut closure_data.loc_body.value);
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
@ -1595,6 +1614,9 @@ pub(crate) fn sort_can_defs_new(
);
}
_ => {
let mut def = def;
set_opt_invalid_recursion_body(&mut def.loc_expr.value);
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
@ -1605,6 +1627,9 @@ pub(crate) fn sort_can_defs_new(
}
},
_ => {
let mut def = def;
set_opt_invalid_recursion_body(&mut def.loc_expr.value);
declarations.push_destructure_def(
def.loc_pattern,
def.loc_expr,

View file

@ -10767,4 +10767,22 @@ All branches in an `if` must have the same type!
@r###"
"###
);
test_report!(
invalid_toplevel_cycle,
indoc!(
r#"
app "test" imports [] provides [main] to "./platform"
main =
if Bool.true then \{} -> {} else main
"#
),
@r###"
CIRCULAR DEFINITION /code/proj/Main.roc
The `main` value is defined directly in terms of itself, causing an
infinite loop.
"###
);
}