mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 07:17:50 +03:00
Merge pull request #1490 from rtfeldman/performance-tweaks
Performance tweaks
This commit is contained in:
commit
dc185f46ef
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 { .. }
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user