Merge pull request #1490 from rtfeldman/performance-tweaks

Performance tweaks
This commit is contained in:
Folkert de Vries 2021-07-22 23:31:40 +02:00 committed by GitHub
commit dc185f46ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 266 additions and 183 deletions

View File

@ -594,13 +594,16 @@ macro_rules! define_builtins {
$ident_name.into(),
)+
];
let mut by_ident = MutMap::default();
let mut by_ident = MutMap::with_capacity_and_hasher(by_id.len(), default_hasher());
$(
debug_assert!(!by_ident.contains_key($ident_name.clone().into()), "Error setting up Builtins: when inserting {} …: {:?} into module {} …: {:?} - the Ident name {:?} is already present in the map. Check the map for duplicate ident names within the {:?} module!", $ident_id, $ident_name, $module_id, $module_name, $ident_name, $module_name);
debug_assert!(by_ident.len() == $ident_id, "Error setting up Builtins: when inserting {} …: {:?} into module {} …: {:?} - this entry was assigned an ID of {}, but based on insertion order, it should have had an ID of {} instead! To fix this, change it from {} …: {:?} to {} …: {:?} instead.", $ident_id, $ident_name, $module_id, $module_name, $ident_id, by_ident.len(), $ident_id, $ident_name, by_ident.len(), $ident_name);
by_ident.insert($ident_name.into(), IdentId($ident_id));
let exists = by_ident.insert($ident_name.into(), IdentId($ident_id));
if let Some(_) = exists {
debug_assert!(false, "Error setting up Builtins: when inserting {} …: {:?} into module {} …: {:?} - the Ident name {:?} is already present in the map. Check the map for duplicate ident names within the {:?} module!", $ident_id, $ident_name, $module_id, $module_name, $ident_name, $module_name);
}
)+
IdentIds {

View File

@ -6362,13 +6362,14 @@ fn call_by_name_help<'a>(
let top_level_layout = ProcLayout::new(env.arena, argument_layouts, *ret_layout);
// the arguments given to the function, stored in symbols
let field_symbols = Vec::from_iter_in(
let mut field_symbols = Vec::with_capacity_in(loc_args.len(), arena);
field_symbols.extend(
loc_args
.iter()
.map(|(_, arg_expr)| possible_reuse_symbol(env, procs, &arg_expr.value)),
arena,
)
.into_bump_slice();
);
let field_symbols = field_symbols.into_bump_slice();
// the variables of the given arguments
let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena);

View File

@ -511,8 +511,8 @@ impl<'a> Layout<'a> {
match content {
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
RecursionVar { structure, .. } => {
let structure_content = env.subs.get_without_compacting(structure).content;
Self::new_help(env, structure, structure_content)
let structure_content = env.subs.get_content_without_compacting(structure);
Self::new_help(env, structure, structure_content.clone())
}
Structure(flat_type) => layout_from_flat_type(env, flat_type),
@ -582,8 +582,8 @@ impl<'a> Layout<'a> {
if env.is_seen(var) {
Ok(Layout::RecursivePointer)
} else {
let content = env.subs.get_without_compacting(var).content;
Self::new_help(env, var, content)
let content = env.subs.get_content_without_compacting(var);
Self::new_help(env, var, content.clone())
}
}
@ -1167,7 +1167,7 @@ fn layout_from_flat_type<'a>(
debug_assert_eq!(args.len(), 1);
let var = args.first().unwrap();
let content = subs.get_without_compacting(*var).content;
let content = subs.get_content_without_compacting(*var);
layout_from_num_content(content)
}
@ -1206,25 +1206,8 @@ fn layout_from_flat_type<'a>(
Err(_) => unreachable!("this would have been a type error"),
}
let sorted_fields = sort_record_fields_help(env, fields_map);
// Determine the layouts of the fields, maintaining sort order
let mut layouts = Vec::with_capacity_in(sorted_fields.len(), arena);
for (_, _, res_layout) in sorted_fields {
match res_layout {
Ok(layout) => {
// Drop any zero-sized fields like {}.
if !layout.is_dropped_because_empty() {
layouts.push(layout);
}
}
Err(_) => {
// optional field, ignore
continue;
}
}
}
// discard optional fields
let mut layouts = sort_stored_record_fields(env, fields_map);
if layouts.len() == 1 {
// If the record has only one field that isn't zero-sized,
@ -1403,6 +1386,43 @@ fn sort_record_fields_help<'a>(
sorted_fields
}
// drops optional fields
fn sort_stored_record_fields<'a>(
env: &mut Env<'a, '_>,
fields_map: MutMap<Lowercase, RecordField<Variable>>,
) -> Vec<'a, Layout<'a>> {
// Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), env.arena);
for (label, field) in fields_map {
let var = match field {
RecordField::Demanded(v) => v,
RecordField::Required(v) => v,
RecordField::Optional(_) => {
continue;
}
};
let layout = Layout::from_var(env, var).expect("invalid layout from var");
sorted_fields.push((label, layout));
}
sorted_fields.sort_by(|(label1, layout1), (label2, layout2)| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1).then(label1.cmp(label2))
});
let mut result = Vec::with_capacity_in(sorted_fields.len(), env.arena);
result.extend(sorted_fields.into_iter().map(|t| t.1));
result
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum UnionVariant<'a> {
Never,
@ -1528,8 +1548,8 @@ pub fn union_sorted_tags<'a>(
subs: &Subs,
) -> Result<UnionVariant<'a>, LayoutProblem> {
let var =
if let Content::RecursionVar { structure, .. } = subs.get_without_compacting(var).content {
structure
if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) {
*structure
} else {
var
};
@ -1548,9 +1568,9 @@ pub fn union_sorted_tags<'a>(
}
fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> {
match subs.get_without_compacting(var).content {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, _, _)) => Some(rec_var),
Content::Alias(_, _, actual) => get_recursion_var(subs, actual),
match subs.get_content_without_compacting(var) {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, _, _)) => Some(*rec_var),
Content::Alias(_, _, actual) => get_recursion_var(subs, *actual),
_ => None,
}
}
@ -1877,7 +1897,7 @@ fn ext_var_is_empty_tag_union(_: &Subs, _: Variable) -> bool {
unreachable!();
}
fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutProblem> {
fn layout_from_num_content<'a>(content: &Content) -> Result<Layout<'a>, LayoutProblem> {
use roc_types::subs::Content::*;
use roc_types::subs::FlatType::*;
@ -1890,7 +1910,7 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
// (e.g. for (5 + 5) assume both 5s are 64-bit integers.)
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN))
}
Structure(Apply(symbol, args)) => match symbol {
Structure(Apply(symbol, args)) => match *symbol {
// Ints
Symbol::NUM_NAT => Ok(Layout::Builtin(Builtin::Usize)),
@ -1915,8 +1935,8 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
_ => {
panic!(
"Invalid Num.Num type application: {:?}",
Apply(symbol, args)
"Invalid Num.Num type application: Apply({:?}, {:?})",
symbol, args
);
}
},
@ -1931,19 +1951,19 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
}
fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutProblem> {
match subs.get_without_compacting(var).content {
match subs.get_content_without_compacting(var) {
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.len() == 1);
let (_, precision_var) = args[0];
let precision = subs.get_without_compacting(precision_var).content;
let precision = subs.get_content_without_compacting(precision_var);
match precision {
Content::Alias(symbol, args, _) => {
debug_assert!(args.is_empty());
let builtin = match symbol {
let builtin = match *symbol {
Symbol::NUM_SIGNED128 => Builtin::Int128,
Symbol::NUM_SIGNED64 => Builtin::Int64,
Symbol::NUM_SIGNED32 => Builtin::Int32,
@ -1972,7 +1992,7 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
let (_, precision_var) = args[0];
let precision = subs.get_without_compacting(precision_var).content;
let precision = subs.get_content_without_compacting(precision_var);
match precision {
Content::Alias(Symbol::NUM_BINARY32, args, _) => {
@ -2012,15 +2032,15 @@ fn dict_layout_from_key_value<'a>(
key_var: Variable,
value_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> {
match env.subs.get_without_compacting(key_var).content {
match env.subs.get_content_without_compacting(key_var) {
Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (Dict * *) then it must have been an empty dict
Ok(Layout::Builtin(Builtin::EmptyDict))
}
key_content => {
let value_content = env.subs.get_without_compacting(value_var).content;
let key_layout = Layout::new_help(env, key_var, key_content)?;
let value_layout = Layout::new_help(env, value_var, value_content)?;
let value_content = env.subs.get_content_without_compacting(value_var);
let key_layout = Layout::new_help(env, key_var, key_content.clone())?;
let value_layout = Layout::new_help(env, value_var, value_content.clone())?;
// This is a normal list.
Ok(Layout::Builtin(Builtin::Dict(
@ -2035,7 +2055,7 @@ pub fn list_layout_from_elem<'a>(
env: &mut Env<'a, '_>,
elem_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> {
match env.subs.get_without_compacting(elem_var).content {
match env.subs.get_content_without_compacting(elem_var) {
Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (List *) then it must have been an empty list
Ok(Layout::Builtin(Builtin::EmptyList))

View File

@ -1,6 +1,6 @@
use roc_can::constraint::Constraint::{self, *};
use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{ImMap, MutMap};
use roc_collections::all::{default_hasher, MutMap};
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_types::solved_types::Solved;
@ -413,24 +413,25 @@ fn solve(
);
// Add a variable for each def to new_vars_by_env.
let mut local_def_vars = ImMap::default();
let mut local_def_vars = Vec::with_capacity(let_con.def_types.len());
for (symbol, loc_type) in let_con.def_types.iter() {
let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value);
local_def_vars.insert(
local_def_vars.push((
*symbol,
Located {
value: var,
region: loc_type.region,
},
);
));
}
let mut new_env = env.clone();
for (symbol, loc_var) in local_def_vars.iter() {
if !new_env.vars_by_symbol.contains_key(&symbol) {
new_env.vars_by_symbol.insert(*symbol, loc_var.value);
// better to ask for forgiveness than for permission
if let Some(old) = new_env.vars_by_symbol.insert(*symbol, loc_var.value) {
new_env.vars_by_symbol.insert(*symbol, old);
}
}
@ -485,7 +486,7 @@ fn solve(
// run solver in next pool
// Add a variable for each def to local_def_vars.
let mut local_def_vars = ImMap::default();
let mut local_def_vars = Vec::with_capacity(let_con.def_types.len());
for (symbol, loc_type) in let_con.def_types.iter() {
let def_type = &loc_type.value;
@ -493,13 +494,13 @@ fn solve(
let var =
type_to_var(subs, next_rank, next_pools, cached_aliases, def_type);
local_def_vars.insert(
local_def_vars.push((
*symbol,
Located {
value: var,
region: loc_type.region,
},
);
));
}
// Solve the assignments' constraints first.
@ -671,7 +672,7 @@ fn type_to_variable(
register(subs, rank, pools, content)
}
Record(fields, ext) => {
let mut field_vars = MutMap::default();
let mut field_vars = MutMap::with_capacity_and_hasher(fields.len(), default_hasher());
for (field, field_type) in fields {
use RecordField::*;
@ -700,7 +701,7 @@ fn type_to_variable(
register(subs, rank, pools, content)
}
TagUnion(tags, ext) => {
let mut tag_vars = MutMap::default();
let mut tag_vars = MutMap::with_capacity_and_hasher(tags.len(), default_hasher());
for (tag, tag_argument_types) in tags {
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
@ -750,7 +751,7 @@ fn type_to_variable(
register(subs, rank, pools, content)
}
RecursiveTagUnion(rec_var, tags, ext) => {
let mut tag_vars = MutMap::default();
let mut tag_vars = MutMap::with_capacity_and_hasher(tags.len(), default_hasher());
for (tag, tag_argument_types) in tags {
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
@ -791,51 +792,18 @@ fn type_to_variable(
}
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
Alias(symbol, args, alias_type) => {
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// different variables (once for each occurrence). The recursion restriction is required
// for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
// attribute in the body, when
//
// Peano : [ S Peano, Z ]
//
// becomes
//
// Peano : [ S (Attr u Peano), Z ]
//
// This `u` variable can be different between lists, so giving just one variable to
// this type is incorrect.
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
let is_recursive = alias_type.is_recursive();
let no_args = args.is_empty();
/*
if no_args && !is_recursive {
if let Some(var) = cached.get(symbol) {
return *var;
}
}
*/
let mut arg_vars = Vec::with_capacity(args.len());
let mut new_aliases = ImMap::default();
for (arg, arg_type) in args {
let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
arg_vars.push((arg.clone(), arg_var));
new_aliases.insert(arg.clone(), arg_var);
}
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
let content = Content::Alias(*symbol, arg_vars, alias_var);
let result = register(subs, rank, pools, content);
if no_args && !is_recursive {
// cached.insert(*symbol, result);
}
result
register(subs, rank, pools, content)
}
HostExposedAlias {
name: symbol,
@ -845,13 +813,11 @@ fn type_to_variable(
..
} => {
let mut arg_vars = Vec::with_capacity(args.len());
let mut new_aliases = ImMap::default();
for (arg, arg_type) in args {
let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
arg_vars.push((arg.clone(), arg_var));
new_aliases.insert(arg.clone(), arg_var);
}
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
@ -1023,45 +989,34 @@ fn adjust_rank(
group_rank: Rank,
var: Variable,
) -> Rank {
let desc = subs.get(var);
if desc.mark == young_mark {
let Descriptor {
content,
rank: _,
mark: _,
copy,
} = desc;
let (desc_rank, desc_mark) = subs.get_rank_mark(var);
if desc_mark == young_mark {
// Mark the variable as visited before adjusting content, as it may be cyclic.
subs.set_mark(var, visit_mark);
// SAFETY: in this function (and functions it calls, we ONLY modify rank and mark, never content!
// hence, we can have an immutable reference to it even though we also have a mutable
// reference to the Subs as a whole. This prevents a clone of the content, which turns out
// to be quite expensive.
let content = {
let ptr = &subs.get_ref(var).content as *const _;
unsafe { &*ptr }
};
let max_rank = adjust_rank_content(subs, young_mark, visit_mark, group_rank, &content);
subs.set(
var,
Descriptor {
content,
rank: max_rank,
mark: visit_mark,
copy,
},
);
subs.set_rank_mark(var, max_rank, visit_mark);
max_rank
} else if desc.mark == visit_mark {
} else if desc_mark == visit_mark {
// nothing changes
desc.rank
desc_rank
} else {
let mut desc = desc;
let min_rank = group_rank.min(desc.rank);
let min_rank = group_rank.min(desc_rank);
// TODO from elm-compiler: how can min_rank ever be group_rank?
desc.rank = min_rank;
desc.mark = visit_mark;
subs.set(var, desc);
subs.set_rank_mark(var, min_rank, visit_mark);
min_rank
}

View File

@ -585,7 +585,7 @@ pub fn chase_ext_tag_union(
fields: &mut Vec<(TagName, Vec<Variable>)>,
) -> Result<(), (Variable, Content)> {
use FlatType::*;
match subs.get_without_compacting(var).content {
match subs.get_content_without_compacting(var) {
Content::Structure(EmptyTagUnion) => Ok(()),
Content::Structure(TagUnion(tags, ext_var))
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
@ -593,16 +593,16 @@ pub fn chase_ext_tag_union(
fields.push((label.clone(), vars.to_vec()));
}
chase_ext_tag_union(subs, ext_var, fields)
chase_ext_tag_union(subs, *ext_var, fields)
}
Content::Structure(FunctionOrTagUnion(tag_name, _, ext_var)) => {
fields.push((tag_name, vec![]));
fields.push((tag_name.clone(), vec![]));
chase_ext_tag_union(subs, ext_var, fields)
chase_ext_tag_union(subs, *ext_var, fields)
}
Content::Alias(_, _, var) => chase_ext_tag_union(subs, var, fields),
Content::Alias(_, _, var) => chase_ext_tag_union(subs, *var, fields),
content => Err((var, content)),
content => Err((var, content.clone())),
}
}

View File

@ -341,24 +341,27 @@ impl SolvedType {
return SolvedType::Flex(VarId::from_var(var, subs));
}
match subs.get_without_compacting(var).content {
match subs.get_content_without_compacting(var) {
FlexVar(_) => SolvedType::Flex(VarId::from_var(var, subs)),
RecursionVar { structure, .. } => {
// TODO should there be a SolvedType RecursionVar variant?
Self::from_var_help(subs, recursion_vars, structure)
Self::from_var_help(subs, recursion_vars, *structure)
}
RigidVar(name) => SolvedType::Rigid(name),
RigidVar(name) => SolvedType::Rigid(name.clone()),
Structure(flat_type) => Self::from_flat_type(subs, recursion_vars, flat_type),
Alias(symbol, args, actual_var) => {
let mut new_args = Vec::with_capacity(args.len());
for (arg_name, arg_var) in args {
new_args.push((arg_name, Self::from_var_help(subs, recursion_vars, arg_var)));
new_args.push((
arg_name.clone(),
Self::from_var_help(subs, recursion_vars, *arg_var),
));
}
let aliased_to = Self::from_var_help(subs, recursion_vars, actual_var);
let aliased_to = Self::from_var_help(subs, recursion_vars, *actual_var);
SolvedType::Alias(symbol, new_args, Box::new(aliased_to))
SolvedType::Alias(*symbol, new_args, Box::new(aliased_to))
}
Error => SolvedType::Error,
}
@ -367,7 +370,7 @@ impl SolvedType {
fn from_flat_type(
subs: &Subs,
recursion_vars: &mut RecursionVars,
flat_type: FlatType,
flat_type: &FlatType,
) -> Self {
use crate::subs::FlatType::*;
@ -379,17 +382,17 @@ impl SolvedType {
new_args.push(Self::from_var_help(subs, recursion_vars, var));
}
SolvedType::Apply(symbol, new_args)
SolvedType::Apply(*symbol, new_args)
}
Func(args, closure, ret) => {
let mut new_args = Vec::with_capacity(args.len());
for var in args {
new_args.push(Self::from_var_help(subs, recursion_vars, var));
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
}
let ret = Self::from_var_help(subs, recursion_vars, ret);
let closure = Self::from_var_help(subs, recursion_vars, closure);
let ret = Self::from_var_help(subs, recursion_vars, *ret);
let closure = Self::from_var_help(subs, recursion_vars, *closure);
SolvedType::Func(new_args, Box::new(closure), Box::new(ret))
}
@ -400,15 +403,15 @@ impl SolvedType {
use RecordField::*;
let solved_type = match field {
Optional(var) => Optional(Self::from_var_help(subs, recursion_vars, var)),
Required(var) => Required(Self::from_var_help(subs, recursion_vars, var)),
Demanded(var) => Demanded(Self::from_var_help(subs, recursion_vars, var)),
Optional(var) => Optional(Self::from_var_help(subs, recursion_vars, *var)),
Required(var) => Required(Self::from_var_help(subs, recursion_vars, *var)),
Demanded(var) => Demanded(Self::from_var_help(subs, recursion_vars, *var)),
};
new_fields.push((label, solved_type));
new_fields.push((label.clone(), solved_type));
}
let ext = Self::from_var_help(subs, recursion_vars, ext_var);
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
SolvedType::Record {
fields: new_fields,
@ -422,23 +425,23 @@ impl SolvedType {
let mut new_args = Vec::with_capacity(args.len());
for var in args {
new_args.push(Self::from_var_help(subs, recursion_vars, var));
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
}
new_tags.push((tag_name, new_args));
new_tags.push((tag_name.clone(), new_args));
}
let ext = Self::from_var_help(subs, recursion_vars, ext_var);
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
SolvedType::TagUnion(new_tags, Box::new(ext))
}
FunctionOrTagUnion(tag_name, symbol, ext_var) => {
let ext = Self::from_var_help(subs, recursion_vars, ext_var);
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
SolvedType::FunctionOrTagUnion(tag_name, symbol, Box::new(ext))
SolvedType::FunctionOrTagUnion(tag_name.clone(), *symbol, Box::new(ext))
}
RecursiveTagUnion(rec_var, tags, ext_var) => {
recursion_vars.insert(subs, rec_var);
recursion_vars.insert(subs, *rec_var);
let mut new_tags = Vec::with_capacity(tags.len());
@ -446,23 +449,23 @@ impl SolvedType {
let mut new_args = Vec::with_capacity(args.len());
for var in args {
new_args.push(Self::from_var_help(subs, recursion_vars, var));
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
}
new_tags.push((tag_name, new_args));
new_tags.push((tag_name.clone(), new_args));
}
let ext = Self::from_var_help(subs, recursion_vars, ext_var);
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
SolvedType::RecursiveTagUnion(
VarId::from_var(rec_var, subs),
VarId::from_var(*rec_var, subs),
new_tags,
Box::new(ext),
)
}
EmptyRecord => SolvedType::EmptyRecord,
EmptyTagUnion => SolvedType::EmptyTagUnion,
Erroneous(problem) => SolvedType::Erroneous(problem),
Erroneous(problem) => SolvedType::Erroneous(problem.clone()),
}
}
}

View File

@ -339,10 +339,20 @@ impl Subs {
self.utable.probe_value_ref(key).value.mark
}
pub fn get_rank_mark(&mut self, key: Variable) -> (Rank, Mark) {
let desc = &self.utable.probe_value_ref(key).value;
(desc.rank, desc.mark)
}
pub fn get_without_compacting(&self, key: Variable) -> Descriptor {
self.utable.probe_value_without_compacting(key)
}
pub fn get_content_without_compacting(&self, key: Variable) -> &Content {
&self.utable.probe_value_ref(key).value.content
}
pub fn get_root_key(&mut self, key: Variable) -> Variable {
self.utable.get_root_key(key)
}
@ -373,6 +383,15 @@ impl Subs {
});
}
pub fn set_rank_mark(&mut self, key: Variable, rank: Rank, mark: Mark) {
let l_key = self.utable.get_root_key(key);
self.utable.update_value(l_key, |node| {
node.value.rank = rank;
node.value.mark = mark;
});
}
pub fn set_content(&mut self, key: Variable, content: Content) {
let l_key = self.utable.get_root_key(key);

View File

@ -405,6 +405,39 @@ fn unify_shared_fields(
}
}
struct Separate<K, V> {
only_in_1: MutMap<K, V>,
only_in_2: MutMap<K, V>,
in_both: MutMap<K, (V, V)>,
}
fn separate<K, V>(tags1: MutMap<K, V>, mut tags2: MutMap<K, V>) -> Separate<K, V>
where
K: Ord + std::hash::Hash,
{
let mut only_in_1 = MutMap::with_capacity_and_hasher(tags1.len(), default_hasher());
let max_common = tags1.len().min(tags2.len());
let mut in_both = MutMap::with_capacity_and_hasher(max_common, default_hasher());
for (k, v1) in tags1.into_iter() {
match tags2.remove(&k) {
Some(v2) => {
in_both.insert(k, (v1, v2));
}
None => {
only_in_1.insert(k, v1);
}
}
}
Separate {
only_in_1,
only_in_2: tags2,
in_both,
}
}
fn unify_tag_union(
subs: &mut Subs,
pool: &mut Pool,
@ -415,10 +448,6 @@ fn unify_tag_union(
) -> Outcome {
let tags1 = rec1.tags;
let tags2 = rec2.tags;
let shared_tags = get_shared(&tags1, &tags2);
// NOTE: don't use `difference` here. In contrast to Haskell, im's `difference` is symmetric
let unique_tags1 = relative_complement(&tags1, &tags2);
let unique_tags2 = relative_complement(&tags2, &tags1);
let recursion_var = match recursion {
(None, None) => None,
@ -426,6 +455,23 @@ fn unify_tag_union(
(Some(v1), Some(_v2)) => Some(v1),
};
// heuristic: our closure defunctionalization scheme generates a bunch of one-tag unions
// also our number types fall in this category too.
if tags1.len() == 1
&& tags2.len() == 1
&& tags1 == tags2
&& subs.get_content_without_compacting(rec1.ext)
== subs.get_content_without_compacting(rec2.ext)
{
return unify_shared_tags_merge(subs, ctx, tags1, rec1.ext, recursion_var);
}
let Separate {
only_in_1: unique_tags1,
only_in_2: unique_tags2,
in_both: shared_tags,
} = separate(tags1, tags2);
if unique_tags1.is_empty() {
if unique_tags2.is_empty() {
let ext_problems = unify_pool(subs, pool, rec1.ext, rec2.ext);
@ -439,7 +485,7 @@ fn unify_tag_union(
pool,
ctx,
shared_tags,
MutMap::default(),
OtherTags::Empty,
rec1.ext,
recursion_var,
);
@ -461,7 +507,7 @@ fn unify_tag_union(
pool,
ctx,
shared_tags,
MutMap::default(),
OtherTags::Empty,
sub_record,
recursion_var,
);
@ -484,7 +530,7 @@ fn unify_tag_union(
pool,
ctx,
shared_tags,
MutMap::default(),
OtherTags::Empty,
sub_record,
recursion_var,
);
@ -493,7 +539,10 @@ fn unify_tag_union(
tag_problems
} else {
let other_tags = union(unique_tags1.clone(), &unique_tags2);
let other_tags = OtherTags::Union {
tags1: unique_tags1.clone(),
tags2: unique_tags2.clone(),
};
let ext = fresh(subs, pool, ctx, Content::FlexVar(None));
let flat_type1 = FlatType::TagUnion(unique_tags1, ext);
@ -686,8 +735,8 @@ fn unify_tag_union_not_recursive_recursive(
/// into it.
#[allow(dead_code)]
fn is_structure(var: Variable, subs: &mut Subs) -> bool {
match subs.get(var).content {
Content::Alias(_, _, actual) => is_structure(actual, subs),
match subs.get_content_without_compacting(var) {
Content::Alias(_, _, actual) => is_structure(*actual, subs),
Content::Structure(_) => true,
_ => false,
}
@ -772,12 +821,20 @@ fn unify_shared_tags_recursive_not_recursive(
}
}
enum OtherTags {
Empty,
Union {
tags1: MutMap<TagName, Vec<Variable>>,
tags2: MutMap<TagName, Vec<Variable>>,
},
}
fn unify_shared_tags(
subs: &mut Subs,
pool: &mut Pool,
ctx: &Context,
shared_tags: MutMap<TagName, (Vec<Variable>, Vec<Variable>)>,
other_tags: MutMap<TagName, Vec<Variable>>,
other_tags: OtherTags,
ext: Variable,
recursion_var: Option<Variable>,
) -> Outcome {
@ -829,6 +886,8 @@ fn unify_shared_tags(
}
if num_shared_tags == matching_tags.len() {
let mut new_tags = matching_tags;
// merge fields from the ext_var into this tag union
let mut fields = Vec::new();
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(subs, ext, &mut fields)
@ -836,18 +895,18 @@ fn unify_shared_tags(
Ok(()) => Variable::EMPTY_TAG_UNION,
Err((new, _)) => new,
};
let mut new_tags = union(matching_tags, &other_tags);
new_tags.extend(fields.into_iter());
let flat_type = if let Some(rec) = recursion_var {
debug_assert!(is_recursion_var(subs, rec));
FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var)
} else {
FlatType::TagUnion(new_tags, new_ext_var)
};
match other_tags {
OtherTags::Empty => {}
OtherTags::Union { tags1, tags2 } => {
new_tags.reserve(tags1.len() + tags2.len());
new_tags.extend(tags1);
new_tags.extend(tags2);
}
}
merge(subs, ctx, Structure(flat_type))
unify_shared_tags_merge(subs, ctx, new_tags, new_ext_var, recursion_var)
} else {
mismatch!(
"Problem with Tag Union\nThere should be {:?} matching tags, but I only got \n{:?}",
@ -857,6 +916,23 @@ fn unify_shared_tags(
}
}
fn unify_shared_tags_merge(
subs: &mut Subs,
ctx: &Context,
new_tags: MutMap<TagName, Vec<Variable>>,
new_ext_var: Variable,
recursion_var: Option<Variable>,
) -> Outcome {
let flat_type = if let Some(rec) = recursion_var {
debug_assert!(is_recursion_var(subs, rec));
FlatType::RecursiveTagUnion(rec, new_tags, new_ext_var)
} else {
FlatType::TagUnion(new_tags, new_ext_var)
};
merge(subs, ctx, Structure(flat_type))
}
fn has_only_optional_fields<'a, I, T>(fields: &mut I) -> bool
where
I: Iterator<Item = &'a RecordField<T>>,
@ -1014,8 +1090,8 @@ fn unify_flat_type(
if tag_name_1 == tag_name_2 {
let problems = unify_pool(subs, pool, *ext_1, *ext_2);
if problems.is_empty() {
let desc = subs.get(ctx.second);
merge(subs, ctx, desc.content)
let content = subs.get_content_without_compacting(ctx.second).clone();
merge(subs, ctx, content)
} else {
problems
}
@ -1237,19 +1313,25 @@ fn fresh(subs: &mut Subs, pool: &mut Pool, ctx: &Context, content: Content) -> V
fn gather_tags(
subs: &mut Subs,
tags: MutMap<TagName, Vec<Variable>>,
mut tags: MutMap<TagName, Vec<Variable>>,
var: Variable,
) -> TagUnionStructure {
use roc_types::subs::Content::*;
use roc_types::subs::FlatType::*;
match subs.get(var).content {
match subs.get_content_without_compacting(var) {
Structure(TagUnion(sub_tags, sub_ext)) => {
gather_tags(subs, union(tags, &sub_tags), sub_ext)
for (k, v) in sub_tags {
tags.insert(k.clone(), v.clone());
}
let sub_ext = *sub_ext;
gather_tags(subs, tags, sub_ext)
}
Alias(_, _, var) => {
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
let var = *var;
gather_tags(subs, tags, var)
}
@ -1259,7 +1341,7 @@ fn gather_tags(
fn is_recursion_var(subs: &Subs, var: Variable) -> bool {
matches!(
subs.get_without_compacting(var).content,
subs.get_content_without_compacting(var),
Content::RecursionVar { .. }
)
}