mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
sort aliases before adding them to scope
This commit is contained in:
parent
40e57142c4
commit
a5957d9982
2 changed files with 227 additions and 59 deletions
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue