do our sorting using just SSCs

This commit is contained in:
Folkert 2022-04-24 18:39:29 +02:00
parent fe76858e2d
commit c5f433ab94
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C

View file

@ -8,11 +8,10 @@ use crate::expr::{canonicalize_expr, Output, Recursive};
use crate::pattern::{bindings_from_patterns, canonicalize_def_header_pattern, Pattern}; use crate::pattern::{bindings_from_patterns, canonicalize_def_header_pattern, Pattern};
use crate::procedure::References; use crate::procedure::References;
use crate::reference_matrix::ReferenceMatrix; use crate::reference_matrix::ReferenceMatrix;
use crate::reference_matrix::TopologicalSort;
use crate::scope::create_alias; use crate::scope::create_alias;
use crate::scope::Scope; use crate::scope::Scope;
use roc_collections::VecMap; use roc_collections::VecMap;
use roc_collections::{ImSet, MutMap, MutSet, SendMap}; use roc_collections::{ImSet, MutMap, SendMap};
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::IdentId; use roc_module::symbol::IdentId;
use roc_module::symbol::ModuleId; use roc_module::symbol::ModuleId;
@ -822,150 +821,137 @@ pub(crate) fn sort_can_defs(
output.aliases.insert(symbol, alias); output.aliases.insert(symbol, alias);
} }
// TODO also do the same `addDirects` check elm/compiler does, so we can let nodes: Vec<_> = (0..defs.len() as u32).collect();
// report an error if a recursive definition can't possibly terminate! let sccs = def_ordering
match def_ordering.references.topological_sort_into_groups() { .references
TopologicalSort::Groups { groups } => { .strongly_connected_components(&nodes);
let mut declarations = Vec::new();
// groups are in reversed order
for group in groups.into_iter().rev() {
group_to_declaration(&def_ordering, &group, &mut defs, &mut declarations);
}
(Ok(declarations), output)
}
TopologicalSort::HasCycles {
mut groups,
nodes_in_cycle,
} => {
let mut declarations = Vec::new(); let mut declarations = Vec::new();
let mut problems = Vec::new(); let mut problems = Vec::new();
// nodes_in_cycle are symbols that form a syntactic cycle. That isn't always a problem, for group in sccs.groups() {
// and in general it's impossible to decide whether it is. So we use a crude heuristic: if group.count_ones() == 1 {
// // a group with a single Def, nice and simple
// Definitions where the cycle occurs behind a lambda are OK let index = group.iter_ones().next().unwrap();
//
// boom = \_ -> boom {}
//
// But otherwise we report an error, e.g.
//
// foo = if b then foo else bar
let sccs = def_ordering let def = match defs[index].take() {
.references Some(def) => def,
.strongly_connected_components(&nodes_in_cycle); None => {
// NOTE: a `_ = someDef` can mean we don't have a symbol here
let symbol = def_ordering.get_symbol(index);
for cycle in sccs.groups() { roc_error_macros::internal_error!("def not available {:?}", symbol)
// check whether the cycle is faulty, which is when it has }
// a direct successor in the current cycle. This catches things like:
//
// x = x
//
// or
//
// p = q
// q = p
let is_invalid_cycle = match cycle.iter_ones().next() {
Some(def_id) => def_ordering
.direct_references
.references_for(def_id)
.any(|key| cycle[key]),
None => false,
}; };
if is_invalid_cycle { let declaration = if def_ordering.direct_references.get_row_col(index, index) {
// We want to show the entire cycle in the error message, so expand it out. // This value references itself in an invalid way, e.g.:
let mut entries = Vec::new(); //
// x = x
for def_id in cycle.iter_ones() { let symbol = def_ordering.get_symbol(index).unwrap();
let symbol = def_ordering.get_symbol(def_id).unwrap();
let def = &defs[def_id];
let expr_region = defs[def_id].as_ref().unwrap().loc_expr.region;
let entry = CycleEntry { let entry = CycleEntry {
symbol, symbol,
symbol_region: def.as_ref().unwrap().loc_pattern.region, symbol_region: def.loc_pattern.region,
expr_region, expr_region: def.loc_expr.region,
}; };
entries.push(entry); let entries = vec![entry];
}
// Sort them by line number to make the report more helpful.
entries.sort_by_key(|entry| entry.symbol_region);
problems.push(Problem::RuntimeError(RuntimeError::CircularDef( problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
entries.clone(), entries.clone(),
))); )));
declarations.push(Declaration::InvalidCycle(entries)); Declaration::InvalidCycle(entries)
} else if def_ordering.references.get_row_col(index, index) {
// this function calls itself, and must be typechecked as a recursive def
let mut def = def;
if let Closure(ClosureData {
recursive: recursive @ Recursive::NotRecursive,
..
}) = &mut def.loc_expr.value
{
*recursive = Recursive::Recursive
} }
// if it's an invalid cycle, other groups may depend on the Declaration::DeclareRec(vec![def])
// symbols defined here, so also push this cycle onto the groups } else {
// Declaration::Declare(def)
// if it's not an invalid cycle, this is slightly inefficient,
// because we know this becomes exactly one DeclareRec already
let cycle = cycle.iter_ones().map(|v| v as u32).collect();
groups.push(cycle);
}
// now we have a collection of groups whose dependencies are not cyclic.
// They are however not yet topologically sorted. Here we have to get a bit
// creative to get all the definitions in the correct sorted order.
let mut group_ids = Vec::with_capacity(groups.len());
let mut symbol_to_group_index = MutMap::default();
for (i, group) in groups.iter().enumerate() {
for symbol in group {
symbol_to_group_index.insert(*symbol, i);
}
group_ids.push(i);
}
let successors_of_group = |group_id: &usize| {
let mut result = MutSet::default();
// for each symbol in this group
for symbol in &groups[*group_id] {
// find its successors
for succ in def_ordering.successors_without_self(*symbol) {
// and add its group to the result
match symbol_to_group_index.get(&succ) {
Some(index) => {
result.insert(*index);
}
None => unreachable!("no index for symbol {:?}", succ),
}
}
}
// don't introduce any cycles to self
result.remove(group_id);
result
}; };
match ven_graph::topological_sort_into_groups(&group_ids, successors_of_group) { declarations.push(declaration);
Ok(sorted_group_ids) => { } else {
for sorted_group in sorted_group_ids.iter().rev() { let nodes: Vec<_> = group.iter_ones().map(|v| v as u32).collect();
for group_id in sorted_group.iter().rev() { let direct_sccs = def_ordering
let group = &groups[*group_id]; .direct_references
.strongly_connected_components(&nodes);
group_to_declaration( let declaration = if direct_sccs.groups().count() == 1 {
&def_ordering, // all defs are part of the same direct cycle. That's invalid
group, let mut entries = Vec::with_capacity(group.count_ones());
&mut defs,
&mut declarations, for index in group.iter_ones() {
); let def = 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 symbol = def_ordering.get_symbol(index).unwrap();
let entry = CycleEntry {
symbol,
symbol_region: def.loc_pattern.region,
expr_region: def.loc_expr.region,
};
entries.push(entry)
} }
problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
entries.clone(),
)));
Declaration::InvalidCycle(entries)
} else {
let mut rec_defs = Vec::with_capacity(group.count_ones());
for index in group.iter_ones() {
let def = 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 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)
};
declarations.push(declaration);
} }
Err(_) => unreachable!("there should be no cycles now!"),
} }
for problem in problems { for problem in problems {
@ -974,8 +960,6 @@ pub(crate) fn sort_can_defs(
(Ok(declarations), output) (Ok(declarations), output)
} }
}
}
fn group_to_declaration( fn group_to_declaration(
def_ordering: &DefOrdering, def_ordering: &DefOrdering,