mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-17 01:05:02 +00:00
Clone delayed type alias when there are equivalent type variables
This commit is contained in:
parent
7456be1771
commit
71b078d2dc
2 changed files with 73 additions and 36 deletions
|
@ -323,6 +323,43 @@ impl Aliases {
|
|||
|
||||
let mut substitutions: MutMap<_, _> = Default::default();
|
||||
|
||||
let old_type_variables = delayed_variables.type_variables(&mut self.variables);
|
||||
let new_type_variables = &subs.variables[alias_variables.type_variables().indices()];
|
||||
|
||||
let some_new_vars_are_equivalent = {
|
||||
// In practice the number of type variables is tiny, so just do a quadratic check
|
||||
// without allocating.
|
||||
let mut some_equivalent = false;
|
||||
for (i, var) in new_type_variables.iter().enumerate() {
|
||||
for j in (i + 1)..new_type_variables.len() {
|
||||
let other_var = new_type_variables[j];
|
||||
some_equivalent = some_equivalent || *var == other_var;
|
||||
}
|
||||
}
|
||||
some_equivalent
|
||||
};
|
||||
|
||||
// If some type variables are equivalent, we have to work over a cloned type variable,
|
||||
// otherwise we will leave in place an alias without preserving the property of unique
|
||||
// type variables.
|
||||
//
|
||||
// For example, if a delayed alias `Foo a b` is instantiated with args `t1 t1` without cloning,
|
||||
// then the delayed alias would be updated to `Foo t1 t1`, and now the distinction between the
|
||||
// two type variables is lost.
|
||||
let can_reuse_old_definition = !some_new_vars_are_equivalent;
|
||||
|
||||
for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) {
|
||||
// if constraint gen duplicated a type these variables could be the same
|
||||
// (happens very often in practice)
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
|
||||
if can_reuse_old_definition {
|
||||
old.var = *new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for OptAbleVar {
|
||||
var: rec_var,
|
||||
opt_ability,
|
||||
|
@ -333,19 +370,9 @@ impl Aliases {
|
|||
debug_assert!(opt_ability.is_none());
|
||||
let new_var = subs.fresh_unnamed_flex_var();
|
||||
substitutions.insert(*rec_var, new_var);
|
||||
*rec_var = new_var;
|
||||
}
|
||||
|
||||
let old_type_variables = delayed_variables.type_variables(&mut self.variables);
|
||||
let new_type_variables = &subs.variables[alias_variables.type_variables().indices()];
|
||||
|
||||
for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) {
|
||||
// if constraint gen duplicated a type these variables could be the same
|
||||
// (happens very often in practice)
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
|
||||
old.var = *new;
|
||||
if can_reuse_old_definition {
|
||||
*rec_var = new_var;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,34 +387,44 @@ impl Aliases {
|
|||
debug_assert!(old.opt_ability.is_none());
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
old.var = *new;
|
||||
}
|
||||
}
|
||||
|
||||
if !substitutions.is_empty() {
|
||||
typ.substitute_variables(&substitutions);
|
||||
}
|
||||
|
||||
let mut t = Type::EmptyRec;
|
||||
|
||||
std::mem::swap(typ, &mut t);
|
||||
|
||||
// assumption: an alias does not (transitively) syntactically contain itself
|
||||
// (if it did it would have to be a recursive tag union, which we should have fixed up
|
||||
// during canonicalization)
|
||||
let alias_variable = type_to_variable(subs, rank, pools, arena, self, &t, false);
|
||||
|
||||
{
|
||||
match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) {
|
||||
None => unreachable!(),
|
||||
Some((_, typ, _, _)) => {
|
||||
// swap typ back
|
||||
std::mem::swap(typ, &mut t);
|
||||
if can_reuse_old_definition {
|
||||
old.var = *new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(alias_variable, kind)
|
||||
if !can_reuse_old_definition {
|
||||
let mut typ = typ.clone();
|
||||
typ.substitute_variables(&substitutions);
|
||||
let alias_variable = type_to_variable(subs, rank, pools, arena, self, &typ, false);
|
||||
(alias_variable, kind)
|
||||
} else {
|
||||
if !substitutions.is_empty() {
|
||||
typ.substitute_variables(&substitutions);
|
||||
}
|
||||
|
||||
let mut t = Type::EmptyRec;
|
||||
|
||||
std::mem::swap(typ, &mut t);
|
||||
|
||||
// assumption: an alias does not (transitively) syntactically contain itself
|
||||
// (if it did it would have to be a recursive tag union, which we should have fixed up
|
||||
// during canonicalization)
|
||||
let alias_variable = type_to_variable(subs, rank, pools, arena, self, &t, false);
|
||||
|
||||
{
|
||||
match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) {
|
||||
None => unreachable!(),
|
||||
Some((_, typ, _, _)) => {
|
||||
// swap typ back
|
||||
std::mem::swap(typ, &mut t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(alias_variable, kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue