sort aliases before adding them to scope

This commit is contained in:
Folkert 2021-12-24 21:14:29 +01:00
parent 40e57142c4
commit a5957d9982
2 changed files with 227 additions and 59 deletions

View file

@ -2,7 +2,7 @@ use crate::env::Env;
use crate::scope::Scope; use crate::scope::Scope;
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap}; use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation}; use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable}; use roc_types::subs::{VarStore, Variable};
@ -136,6 +136,100 @@ fn make_apply_symbol(
} }
} }
pub fn find_alias_symbols(
module_id: ModuleId,
ident_ids: &mut IdentIds,
initial_annotation: &roc_parse::ast::TypeAnnotation,
) -> Vec<Symbol> {
use roc_parse::ast::TypeAnnotation::*;
let mut result = Vec::new();
let mut stack = vec![initial_annotation];
while let Some(annotation) = stack.pop() {
match annotation {
Apply(_module_name, ident, arguments) => {
let ident: Ident = (*ident).into();
let ident_id = ident_ids.get_or_insert(&ident);
let symbol = Symbol::new(module_id, ident_id);
result.push(symbol);
for t in arguments.iter() {
stack.push(&t.value);
}
}
Function(arguments, result) => {
for t in arguments.iter() {
stack.push(&t.value);
}
stack.push(&result.value);
}
BoundVariable(_) => {}
As(actual, _, _) => {
stack.push(&actual.value);
}
Record { fields, ext } => {
let mut inner_stack = Vec::with_capacity(fields.items.len());
for field in fields.items.iter() {
inner_stack.push(&field.value)
}
while let Some(assigned_field) = inner_stack.pop() {
match assigned_field {
AssignedField::RequiredValue(_, _, t)
| AssignedField::OptionalValue(_, _, t) => {
stack.push(&t.value);
}
AssignedField::LabelOnly(_) => {}
AssignedField::SpaceBefore(inner, _)
| AssignedField::SpaceAfter(inner, _) => inner_stack.push(inner),
AssignedField::Malformed(_) => {}
}
}
for t in ext.iter() {
stack.push(&t.value);
}
}
TagUnion { ext, tags } => {
let mut inner_stack = Vec::with_capacity(tags.items.len());
for tag in tags.items.iter() {
inner_stack.push(&tag.value)
}
while let Some(tag) = inner_stack.pop() {
match tag {
Tag::Global { args, .. } | Tag::Private { args, .. } => {
for t in args.iter() {
stack.push(&t.value);
}
}
Tag::SpaceBefore(inner, _) | Tag::SpaceAfter(inner, _) => {
inner_stack.push(inner)
}
Tag::Malformed(_) => {}
}
}
for t in ext.iter() {
stack.push(&t.value);
}
}
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
stack.push(inner);
}
Inferred | Wildcard | Malformed(_) => {}
}
}
result
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn can_annotation_help( fn can_annotation_help(
env: &mut Env, env: &mut Env,

View file

@ -21,7 +21,7 @@ use roc_types::subs::{VarStore, Variable};
use roc_types::types::{Alias, Type}; use roc_types::types::{Alias, Type};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use ven_graph::{strongly_connected_components, topological_sort_into_groups}; use ven_graph::{strongly_connected_components, topological_sort, topological_sort_into_groups};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Def { pub struct Def {
@ -106,6 +106,69 @@ impl Declaration {
} }
} }
/// Returns a topologically sorted sequence of alias names
fn sort_aliases_before_introduction(mut alias_symbols: MutMap<Symbol, Vec<Symbol>>) -> Vec<Symbol> {
let defined_symbols: Vec<Symbol> = alias_symbols.keys().copied().collect();
// find the strongly connected components and their relations
let sccs = {
// only retain symbols from the current alias_defs
for v in alias_symbols.iter_mut() {
v.1.retain(|x| defined_symbols.iter().any(|s| s == x));
}
let all_successors_with_self = |symbol: &Symbol| alias_symbols[symbol].iter().copied();
strongly_connected_components(&defined_symbols, all_successors_with_self)
};
// then sort the strongly connected components
let groups: Vec<_> = (0..sccs.len()).collect();
let mut group_symbols: Vec<Vec<Symbol>> = vec![Vec::new(); groups.len()];
let mut symbol_to_group_index = MutMap::default();
let mut group_to_groups = vec![Vec::new(); groups.len()];
for (index, group) in sccs.iter().enumerate() {
for s in group {
symbol_to_group_index.insert(*s, index);
}
}
for (index, group) in sccs.iter().enumerate() {
for s in group {
let reachable = &alias_symbols[s];
for r in reachable {
let new_index = symbol_to_group_index[r];
if new_index != index {
group_to_groups[index].push(new_index);
}
}
}
}
for v in group_symbols.iter_mut() {
v.sort();
v.dedup();
}
let all_successors_with_self = |group: &usize| group_to_groups[*group].iter().copied();
// split into self-recursive and mutually recursive
match topological_sort(&groups, all_successors_with_self) {
Ok(result) => result
.iter()
.rev()
.map(|group_index| sccs[*group_index].iter())
.flatten()
.copied()
.collect(),
Err(_loop_detected) => unreachable!("the groups cannot recurse"),
}
}
#[inline(always)] #[inline(always)]
pub fn canonicalize_defs<'a>( pub fn canonicalize_defs<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
@ -179,10 +242,27 @@ pub fn canonicalize_defs<'a>(
let mut aliases = SendMap::default(); let mut aliases = SendMap::default();
let mut value_defs = Vec::new(); let mut value_defs = Vec::new();
println!("anew"); let mut alias_defs = MutMap::default();
let mut alias_symbols = MutMap::default();
for pending_def in pending.into_iter() { for pending_def in pending.into_iter() {
match pending_def { match pending_def {
PendingDef::Alias { name, vars, ann } => { PendingDef::Alias { name, vars, ann } => {
let symbols =
crate::annotation::find_alias_symbols(env.home, &mut env.ident_ids, &ann.value);
alias_symbols.insert(name.value, symbols);
alias_defs.insert(name.value, (name, vars, ann));
}
other => value_defs.push(other),
}
}
let sorted = sort_aliases_before_introduction(alias_symbols);
for alias_name in sorted {
let (name, vars, ann) = alias_defs.remove(&alias_name).unwrap();
let symbol = name.value; let symbol = name.value;
let mut can_ann = let mut can_ann =
canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store); canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
@ -234,14 +314,10 @@ pub fn canonicalize_defs<'a>(
); );
} }
dbg!(symbol);
scope.add_alias(symbol, ann.region, can_vars.clone(), can_ann.typ.clone()); scope.add_alias(symbol, ann.region, can_vars.clone(), can_ann.typ.clone());
let alias = scope.lookup_alias(symbol).expect("alias is added to scope"); let alias = scope.lookup_alias(symbol).expect("alias is added to scope");
aliases.insert(symbol, alias.clone()); aliases.insert(symbol, alias.clone());
} }
other => value_defs.push(other),
}
}
correct_mutual_recursive_type_alias(env, &mut aliases, var_store); correct_mutual_recursive_type_alias(env, &mut aliases, var_store);
@ -919,7 +995,6 @@ fn canonicalize_pending_def<'a>(
} }
} }
dbg!(symbol);
scope.add_alias(symbol, name.region, can_vars.clone(), can_ann.typ.clone()); scope.add_alias(symbol, name.region, can_vars.clone(), can_ann.typ.clone());
if can_ann.typ.contains_symbol(symbol) { if can_ann.typ.contains_symbol(symbol) {
@ -930,7 +1005,6 @@ fn canonicalize_pending_def<'a>(
let mut rec_type_union = Type::RecursiveTagUnion(rec_var, tags, ext); let mut rec_type_union = Type::RecursiveTagUnion(rec_var, tags, ext);
rec_type_union.substitute_alias(symbol, &Type::Variable(rec_var)); rec_type_union.substitute_alias(symbol, &Type::Variable(rec_var));
dbg!(symbol);
scope.add_alias(symbol, name.region, can_vars, rec_type_union); scope.add_alias(symbol, name.region, can_vars, rec_type_union);
} else { } else {
env.problems env.problems