clean up sorting code

This commit is contained in:
Folkert 2022-04-24 19:06:08 +02:00
parent 0aa7cb8479
commit 054907a4cb
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C

View file

@ -796,131 +796,93 @@ pub(crate) fn sort_can_defs(
output.aliases.insert(symbol, alias); output.aliases.insert(symbol, alias);
} }
macro_rules! take_def {
($index:expr) => {
match defs[$index].take() {
Some(def) => def,
None => {
// NOTE: a `_ = someDef` can mean we don't have a symbol here
let symbol = def_ordering.get_symbol($index);
roc_error_macros::internal_error!("def not available {:?}", symbol)
}
}
};
}
let nodes: Vec<_> = (0..defs.len() as u32).collect(); let nodes: Vec<_> = (0..defs.len() as u32).collect();
// We first perform SCC based on any reference, both variable usage and calls
// considering both value definitions and function bodies. This will spot any
// recursive relations between any 2 definitions.
let sccs = def_ordering let sccs = def_ordering
.references .references
.strongly_connected_components(&nodes); .strongly_connected_components(&nodes);
let mut declarations = Vec::new(); let mut declarations = Vec::new();
let mut problems = Vec::new();
for group in sccs.groups() { for group in sccs.groups() {
if group.count_ones() == 1 { if group.count_ones() == 1 {
// a group with a single Def, nice and simple // a group with a single Def, nice and simple
let index = group.iter_ones().next().unwrap(); let index = group.iter_ones().next().unwrap();
let def = match defs[index].take() { let def = take_def!(index);
Some(def) => def,
None => {
// NOTE: a `_ = someDef` can mean we don't have a symbol here
let symbol = def_ordering.get_symbol(index);
roc_error_macros::internal_error!("def not available {:?}", symbol)
}
};
let declaration = if def_ordering.direct_references.get_row_col(index, index) { let declaration = if def_ordering.direct_references.get_row_col(index, index) {
// This value references itself in an invalid way, e.g.: // a definition like `x = x + 1`, which is invalid in roc
//
// x = x
let symbol = def_ordering.get_symbol(index).unwrap(); let symbol = def_ordering.get_symbol(index).unwrap();
let entry = CycleEntry { let entries = vec![make_cycle_entry(symbol, &def)];
symbol,
symbol_region: def.loc_pattern.region,
expr_region: def.loc_expr.region,
};
let entries = vec![entry]; let problem = Problem::RuntimeError(RuntimeError::CircularDef(entries.clone()));
env.problem(problem);
problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
entries.clone(),
)));
Declaration::InvalidCycle(entries) Declaration::InvalidCycle(entries)
} else if def_ordering.references.get_row_col(index, index) { } else if def_ordering.references.get_row_col(index, index) {
// this function calls itself, and must be typechecked as a recursive def // this function calls itself, and must be typechecked as a recursive def
Declaration::DeclareRec(vec![mark_def_recursive(def)])
let mut def = def;
if let Closure(ClosureData {
recursive: recursive @ Recursive::NotRecursive,
..
}) = &mut def.loc_expr.value
{
*recursive = Recursive::Recursive
}
Declaration::DeclareRec(vec![def])
} else { } else {
Declaration::Declare(def) Declaration::Declare(def)
}; };
declarations.push(declaration); declarations.push(declaration);
} else { } else {
// There is something recursive going on between the Defs of this group.
// Now we use the direct_references to see if it is clearly invalid recursion, e.g.
//
// x = y
// y = x
//
// We allow indirect recursion (behind a lambda), e.g.
//
// boom = \{} -> boom {}
//
// In general we cannot spot faulty recursion (halting problem) so this is our best attempt
let nodes: Vec<_> = group.iter_ones().map(|v| v as u32).collect(); let nodes: Vec<_> = group.iter_ones().map(|v| v as u32).collect();
let direct_sccs = def_ordering let direct_sccs = def_ordering
.direct_references .direct_references
.strongly_connected_components(&nodes); .strongly_connected_components(&nodes);
let declaration = if direct_sccs.groups().count() == 1 { let declaration = if direct_sccs.groups().count() == 1 {
// all defs are part of the same direct cycle. That's invalid // all defs are part of the same direct cycle, that is invalid!
let mut entries = Vec::with_capacity(group.count_ones()); let mut entries = Vec::with_capacity(group.count_ones());
for index in group.iter_ones() { for index in group.iter_ones() {
let def = match defs[index].take() { let def = take_def!(index);
Some(def) => def,
None => {
// NOTE: a `_ = someDef` can mean we don't have a symbol here
let symbol = def_ordering.get_symbol(index);
roc_error_macros::internal_error!("def not available {:?}", symbol)
}
};
let symbol = def_ordering.get_symbol(index).unwrap(); let symbol = def_ordering.get_symbol(index).unwrap();
let entry = CycleEntry { entries.push(make_cycle_entry(symbol, &def))
symbol,
symbol_region: def.loc_pattern.region,
expr_region: def.loc_expr.region,
};
entries.push(entry)
} }
problems.push(Problem::RuntimeError(RuntimeError::CircularDef( let problem = Problem::RuntimeError(RuntimeError::CircularDef(entries.clone()));
entries.clone(), env.problem(problem);
)));
Declaration::InvalidCycle(entries) Declaration::InvalidCycle(entries)
} else { } else {
let mut rec_defs = Vec::with_capacity(group.count_ones()); let rec_defs = group
.iter_ones()
for index in group.iter_ones() { .map(|index| mark_def_recursive(take_def!(index)))
let def = match defs[index].take() { .collect();
Some(def) => def,
None => {
// NOTE: a `_ = someDef` can mean we don't have a symbol here
let symbol = def_ordering.get_symbol(index);
roc_error_macros::internal_error!("def not available {:?}", symbol)
}
};
let mut def = def;
if let Closure(ClosureData {
recursive: recursive @ Recursive::NotRecursive,
..
}) = &mut def.loc_expr.value
{
*recursive = Recursive::Recursive
}
rec_defs.push(def);
}
Declaration::DeclareRec(rec_defs) Declaration::DeclareRec(rec_defs)
}; };
@ -929,11 +891,27 @@ pub(crate) fn sort_can_defs(
} }
} }
for problem in problems { (Ok(declarations), output)
env.problem(problem); }
fn mark_def_recursive(mut def: Def) -> Def {
if let Closure(ClosureData {
recursive: recursive @ Recursive::NotRecursive,
..
}) = &mut def.loc_expr.value
{
*recursive = Recursive::Recursive
} }
(Ok(declarations), output) def
}
fn make_cycle_entry(symbol: Symbol, def: &Def) -> CycleEntry {
CycleEntry {
symbol,
symbol_region: def.loc_pattern.region,
expr_region: def.loc_expr.region,
}
} }
fn pattern_to_vars_by_symbol( fn pattern_to_vars_by_symbol(