Merge pull request #1514 from rtfeldman/records-soa

Records struct of arrays
This commit is contained in:
Richard Feldman 2021-08-01 22:24:35 -04:00 committed by GitHub
commit 44d5551259
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 412 additions and 235 deletions

View file

@ -1,17 +1,15 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use libloading::Library;
use roc_collections::all::MutMap;
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
use roc_module::ident::{Lowercase, TagName};
use roc_module::ident::TagName;
use roc_module::operator::CalledVia;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::ProcLayout;
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
use roc_region::all::{Located, Region};
use roc_types::subs::{Content, FlatType, Subs, Variable};
use roc_types::types::RecordField;
use roc_types::subs::{Content, FlatType, RecordFields, Subs, Variable};
struct Env<'a, 'env> {
arena: &'a Bump,
@ -155,9 +153,12 @@ fn jit_to_ast_help<'a>(
Content::Structure(FlatType::Record(fields, _)) => {
Ok(struct_to_ast(env, ptr, field_layouts, fields))
}
Content::Structure(FlatType::EmptyRecord) => {
Ok(struct_to_ast(env, ptr, field_layouts, &MutMap::default()))
}
Content::Structure(FlatType::EmptyRecord) => Ok(struct_to_ast(
env,
ptr,
field_layouts,
&RecordFields::with_capacity(0),
)),
Content::Structure(FlatType::TagUnion(tags, _)) => {
debug_assert_eq!(tags.len(), 1);
@ -437,7 +438,7 @@ fn ptr_to_ast<'a>(
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), &[])
}
Content::Structure(FlatType::EmptyRecord) => {
struct_to_ast(env, ptr, &[], &MutMap::default())
struct_to_ast(env, ptr, &[], &RecordFields::with_capacity(0))
}
other => {
unreachable!(
@ -556,26 +557,15 @@ fn struct_to_ast<'a>(
env: &Env<'a, '_>,
ptr: *const u8,
field_layouts: &'a [Layout<'a>],
fields: &MutMap<Lowercase, RecordField<Variable>>,
sorted_fields: &RecordFields,
) -> Expr<'a> {
let arena = env.arena;
let subs = env.subs;
let mut output = Vec::with_capacity_in(field_layouts.len(), arena);
// The fields, sorted alphabetically
let mut sorted_fields = {
let mut vec = fields
.iter()
.collect::<std::vec::Vec<(&Lowercase, &RecordField<Variable>)>>();
vec.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
vec
};
if sorted_fields.len() == 1 {
// this is a 1-field wrapper record around another record or 1-tag tag union
let (label, field) = sorted_fields.pop().unwrap();
let (label, field) = sorted_fields.into_iter().next().unwrap();
let inner_content = env.subs.get_content_without_compacting(field.into_inner());

View file

@ -2156,8 +2156,8 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Num c
, foobar : Num a
, bar : Num e
, foobar : Num d
, bar : Num a
, baz : Num b
, ...
}

View file

@ -6,7 +6,7 @@ use roc_region::all::{Located, Region};
use roc_types::solved_types::Solved;
use roc_types::subs::{Content, Descriptor, FlatType, Mark, OptVariable, Rank, Subs, Variable};
use roc_types::types::Type::{self, *};
use roc_types::types::{Alias, Category, ErrorType, PatternCategory, RecordField};
use roc_types::types::{Alias, Category, ErrorType, PatternCategory};
use roc_unify::unify::unify;
use roc_unify::unify::Unified::*;
@ -673,13 +673,8 @@ fn type_to_variable(
let mut field_vars = MutMap::with_capacity_and_hasher(fields.len(), default_hasher());
for (field, field_type) in fields {
use RecordField::*;
let field_var = match field_type {
Required(typ) => Required(type_to_variable(subs, rank, pools, cached, typ)),
Optional(typ) => Optional(type_to_variable(subs, rank, pools, cached, typ)),
Demanded(typ) => Demanded(type_to_variable(subs, rank, pools, cached, typ)),
};
let field_var =
field_type.map(|typ| type_to_variable(subs, rank, pools, cached, typ));
field_vars.insert(field.clone(), field_var);
}
@ -694,7 +689,8 @@ fn type_to_variable(
Err((new, _)) => new,
};
let content = Content::Structure(FlatType::Record(field_vars, new_ext_var));
let record_fields = field_vars.into_iter().collect();
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
register(subs, rank, pools, content)
}
@ -1084,14 +1080,9 @@ fn adjust_rank_content(
Record(fields, ext_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
for var in fields.values() {
rank = rank.max(adjust_rank(
subs,
young_mark,
visit_mark,
group_rank,
var.into_inner(),
));
for var in fields.iter_variables() {
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
@ -1238,14 +1229,8 @@ fn instantiate_rigids_help(
EmptyRecord | EmptyTagUnion | Erroneous(_) => {}
Record(fields, ext_var) => {
for (_, field) in fields {
use RecordField::*;
match field {
Demanded(var) => instantiate_rigids_help(subs, max_rank, pools, var),
Required(var) => instantiate_rigids_help(subs, max_rank, pools, var),
Optional(var) => instantiate_rigids_help(subs, max_rank, pools, var),
};
for var in fields.iter_variables() {
instantiate_rigids_help(subs, max_rank, pools, *var);
}
instantiate_rigids_help(subs, max_rank, pools, ext_var);
@ -1381,31 +1366,12 @@ fn deep_copy_var_help(
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
Record(fields, ext_var) => {
let mut new_fields = MutMap::default();
for (label, field) in fields {
use RecordField::*;
let new_field = match field {
Demanded(var) => {
Demanded(deep_copy_var_help(subs, max_rank, pools, var))
}
Required(var) => {
Required(deep_copy_var_help(subs, max_rank, pools, var))
}
Optional(var) => {
Optional(deep_copy_var_help(subs, max_rank, pools, var))
}
};
new_fields.insert(label, new_field);
Record(mut fields, ext_var) => {
for var in fields.iter_variables_mut() {
*var = deep_copy_var_help(subs, max_rank, pools, *var);
}
Record(
new_fields,
deep_copy_var_help(subs, max_rank, pools, ext_var),
)
Record(fields, deep_copy_var_help(subs, max_rank, pools, ext_var))
}
TagUnion(tags, ext_var) => {

View file

@ -152,19 +152,9 @@ fn find_names_needed(
find_names_needed(*ret_var, subs, roots, root_appearances, names_taken);
}
Structure(Record(fields, ext_var)) => {
let mut sorted_fields: Vec<_> = fields.iter().collect();
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
for (_, field) in sorted_fields {
find_names_needed(
field.into_inner(),
subs,
roots,
root_appearances,
names_taken,
);
Structure(Record(sorted_fields, ext_var)) => {
for var in sorted_fields.iter_variables() {
find_names_needed(*var, subs, roots, root_appearances, names_taken);
}
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
@ -420,7 +410,10 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin
use crate::types::{gather_fields, RecordStructure};
// If the `ext` has concrete fields (e.g. { foo : I64}{ bar : Bool }), merge them
let RecordStructure { fields, ext } = gather_fields(subs, fields, *ext_var);
let RecordStructure {
fields: sorted_fields,
ext,
} = gather_fields(subs, fields.clone(), *ext_var);
let ext_var = ext;
if fields.is_empty() {
@ -428,12 +421,6 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin
} else {
buf.push_str("{ ");
// Sort the fields so they always end up in the same order.
let mut sorted_fields = Vec::with_capacity(fields.len());
sorted_fields.extend(fields);
sorted_fields.sort_by(|(a, _), (b, _)| a.cmp(b));
let mut any_written_yet = false;
for (label, field_var) in sorted_fields {
@ -592,7 +579,7 @@ pub fn chase_ext_record(
match subs.get_content_without_compacting(var) {
Structure(Record(sub_fields, sub_ext)) => {
for (field_name, record_field) in sub_fields {
fields.insert(field_name.clone(), *record_field);
fields.insert(field_name.clone(), record_field);
}
chase_ext_record(subs, *sub_ext, fields)

View file

@ -400,13 +400,8 @@ impl SolvedType {
let mut new_fields = Vec::with_capacity(fields.len());
for (label, field) in fields {
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)),
};
let solved_type =
field.map(|var| Self::from_var_help(subs, recursion_vars, *var));
new_fields.push((label.clone(), solved_type));
}

View file

@ -2,8 +2,9 @@ use crate::types::{name_type_var, ErrorType, Problem, RecordField, TypeExt};
use roc_collections::all::{ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use std::cmp::Ordering;
use std::fmt;
use std::iter::{once, Iterator};
use std::iter::{once, Extend, FromIterator, Iterator, Map, Zip};
use ven_ena::unify::{InPlace, Snapshot, UnificationTable, UnifyKey};
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
@ -535,7 +536,7 @@ impl From<usize> for Rank {
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub struct Descriptor {
pub content: Content,
pub rank: Rank,
@ -573,7 +574,7 @@ impl From<Content> for Descriptor {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug)]
pub enum Content {
/// A type variable which the user did not name in an annotation,
///
@ -619,11 +620,11 @@ impl Content {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug)]
pub enum FlatType {
Apply(Symbol, Vec<Variable>),
Func(Vec<Variable>, Variable, Variable),
Record(MutMap<Lowercase, RecordField<Variable>>, Variable),
Record(RecordFields, Variable),
TagUnion(MutMap<TagName, Vec<Variable>>, Variable),
FunctionOrTagUnion(TagName, Symbol, Variable),
RecursiveTagUnion(Variable, MutMap<TagName, Vec<Variable>>, Variable),
@ -640,6 +641,232 @@ pub enum Builtin {
EmptyRecord,
}
#[derive(Clone, Debug)]
pub struct RecordFields {
field_names: Vec<Lowercase>,
variables: Vec<Variable>,
field_type: Vec<RecordField<()>>,
}
impl RecordFields {
pub fn with_capacity(capacity: usize) -> Self {
Self {
field_names: Vec::with_capacity(capacity),
variables: Vec::with_capacity(capacity),
field_type: Vec::with_capacity(capacity),
}
}
pub fn len(&self) -> usize {
let answer = self.field_names.len();
debug_assert_eq!(answer, self.variables.len());
debug_assert_eq!(answer, self.field_type.len());
answer
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter_variables(&self) -> impl Iterator<Item = &Variable> {
self.variables.iter()
}
pub fn iter_variables_mut(&mut self) -> impl Iterator<Item = &mut Variable> {
self.variables.iter_mut()
}
pub fn iter(&self) -> impl Iterator<Item = (&Lowercase, RecordField<Variable>)> {
self.into_iter()
}
pub fn has_only_optional_fields(&self) -> bool {
self.field_type
.iter()
.all(|field| matches!(field, RecordField::Optional(_)))
}
pub fn from_vec(mut vec: Vec<(Lowercase, RecordField<Variable>)>) -> Self {
// we assume there are no duplicate field names in there
vec.sort_unstable_by(|(name1, _), (name2, _)| name1.cmp(name2));
Self::from_sorted_vec(vec)
}
pub fn from_sorted_vec(vec: Vec<(Lowercase, RecordField<Variable>)>) -> Self {
let mut result = RecordFields::with_capacity(vec.len());
result.extend(vec);
result
}
pub fn merge(self, other: Self) -> Self {
if other.is_empty() {
return self;
}
// maximum final size (if there is no overlap at all)
let final_size = self.len() + other.len();
let mut result = Self::with_capacity(final_size);
let mut it1 = self.into_iter().peekable();
let mut it2 = other.into_iter().peekable();
loop {
let which = match (it1.peek(), it2.peek()) {
(Some((l, _)), Some((r, _))) => Some(l.cmp(r)),
(Some(_), None) => Some(Ordering::Less),
(None, Some(_)) => Some(Ordering::Greater),
(None, None) => None,
};
let next_element = match which {
Some(Ordering::Less) => it1.next(),
Some(Ordering::Equal) => {
let _ = it2.next();
it1.next()
}
Some(Ordering::Greater) => it2.next(),
None => break,
};
result.extend([next_element.unwrap()]);
}
result
}
pub fn separate(self, other: Self) -> SeparateRecordFields {
let max_common = self.len().min(other.len());
let mut result = SeparateRecordFields {
only_in_1: RecordFields::with_capacity(self.len()),
only_in_2: RecordFields::with_capacity(other.len()),
in_both: Vec::with_capacity(max_common),
};
let mut it1 = self.into_iter().peekable();
let mut it2 = other.into_iter().peekable();
loop {
let which = match (it1.peek(), it2.peek()) {
(Some((l, _)), Some((r, _))) => Some(l.cmp(r)),
(Some(_), None) => Some(Ordering::Less),
(None, Some(_)) => Some(Ordering::Greater),
(None, None) => None,
};
match which {
Some(Ordering::Less) => result.only_in_1.extend(it1.next()),
Some(Ordering::Equal) => {
let (label, field1) = it1.next().unwrap();
let (_, field2) = it2.next().unwrap();
result.in_both.push((label, field1, field2));
}
Some(Ordering::Greater) => result.only_in_2.extend(it2.next()),
None => break,
};
}
result
}
}
pub struct SeparateRecordFields {
pub only_in_1: RecordFields,
pub only_in_2: RecordFields,
pub in_both: Vec<(Lowercase, RecordField<Variable>, RecordField<Variable>)>,
}
impl Extend<(Lowercase, RecordField<Variable>)> for RecordFields {
fn extend<T: IntoIterator<Item = (Lowercase, RecordField<Variable>)>>(&mut self, iter: T) {
for (name, record_field) in iter.into_iter() {
self.field_names.push(name);
self.field_type.push(record_field.map(|_| ()));
self.variables.push(record_field.into_inner());
}
}
}
impl FromIterator<(Lowercase, RecordField<Variable>)> for RecordFields {
fn from_iter<T: IntoIterator<Item = (Lowercase, RecordField<Variable>)>>(iter: T) -> Self {
let vec: Vec<_> = iter.into_iter().collect();
Self::from_vec(vec)
}
}
impl<'a> FromIterator<(&'a Lowercase, RecordField<Variable>)> for RecordFields {
fn from_iter<T: IntoIterator<Item = (&'a Lowercase, RecordField<Variable>)>>(iter: T) -> Self {
let vec: Vec<_> = iter.into_iter().map(|(a, b)| (a.clone(), b)).collect();
Self::from_vec(vec)
}
}
impl IntoIterator for RecordFields {
type Item = (Lowercase, RecordField<Variable>);
#[allow(clippy::type_complexity)]
type IntoIter = Map<
Zip<
Zip<std::vec::IntoIter<Lowercase>, std::vec::IntoIter<Variable>>,
std::vec::IntoIter<RecordField<()>>,
>,
fn(((Lowercase, Variable), RecordField<()>)) -> (Lowercase, RecordField<Variable>),
>;
fn into_iter(self) -> Self::IntoIter {
self.field_names
.into_iter()
.zip(self.variables.into_iter())
.zip(self.field_type.into_iter())
.map(record_fields_into_iterator_help)
}
}
fn record_fields_into_iterator_help(
arg: ((Lowercase, Variable), RecordField<()>),
) -> (Lowercase, RecordField<Variable>) {
let ((name, var), field_type) = arg;
(name, field_type.map(|_| var))
}
impl<'a> IntoIterator for &'a RecordFields {
type Item = (&'a Lowercase, RecordField<Variable>);
#[allow(clippy::type_complexity)]
type IntoIter = Map<
Zip<
Zip<std::slice::Iter<'a, Lowercase>, std::slice::Iter<'a, Variable>>,
std::slice::Iter<'a, RecordField<()>>,
>,
fn(
((&'a Lowercase, &Variable), &RecordField<()>),
) -> (&'a Lowercase, RecordField<Variable>),
>;
fn into_iter(self) -> Self::IntoIter {
self.field_names
.iter()
.zip(self.variables.iter())
.zip(self.field_type.iter())
.map(ref_record_fields_into_iterator_help)
}
}
fn ref_record_fields_into_iterator_help<'a>(
arg: ((&'a Lowercase, &Variable), &RecordField<()>),
) -> (&'a Lowercase, RecordField<Variable>) {
let ((name, var), field_type) = arg;
(name, field_type.map(|_| *var))
}
fn occurs(
subs: &Subs,
seen: &ImSet<Variable>,
@ -670,12 +897,7 @@ fn occurs(
short_circuit(subs, root_var, &new_seen, it)
}
Record(vars_by_field, ext_var) => {
let it =
once(ext_var).chain(vars_by_field.values().map(|field| match field {
RecordField::Optional(var) => var,
RecordField::Required(var) => var,
RecordField::Demanded(var) => var,
}));
let it = once(ext_var).chain(vars_by_field.iter_variables());
short_circuit(subs, root_var, &new_seen, it)
}
TagUnion(tags, ext_var) => {
@ -799,21 +1021,10 @@ fn explicit_substitute(
Record(mut vars_by_field, ext_var) => {
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
for (_, field) in vars_by_field.iter_mut() {
use RecordField::*;
for var in vars_by_field.variables.iter_mut() {
*var = explicit_substitute(subs, from, to, *var, seen);
}
*field = match field {
Optional(var) => {
Optional(explicit_substitute(subs, from, to, *var, seen))
}
Required(var) => {
Required(explicit_substitute(subs, from, to, *var, seen))
}
Demanded(var) => {
Demanded(explicit_substitute(subs, from, to, *var, seen))
}
};
}
subs.set_content(in_var, Structure(Record(vars_by_field, new_ext_var)));
}
@ -1284,8 +1495,8 @@ fn restore_content(subs: &mut Subs, content: &Content) {
EmptyTagUnion => (),
Record(fields, ext_var) => {
for field in fields.values() {
subs.restore(field.into_inner());
for var in fields.iter_variables() {
subs.restore(*var);
}
subs.restore(*ext_var);

View file

@ -1,7 +1,7 @@
use crate::pretty_print::Parens;
use crate::subs::{LambdaSet, Subs, VarStore, Variable};
use crate::subs::{LambdaSet, RecordFields, Subs, VarStore, Variable};
use inlinable_string::InlinableString;
use roc_collections::all::{ImMap, ImSet, Index, MutMap, MutSet, SendMap};
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol};
@ -980,7 +980,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
}
pub struct RecordStructure {
pub fields: MutMap<Lowercase, RecordField<Variable>>,
pub fields: RecordFields,
pub ext: Variable,
}
@ -1522,20 +1522,18 @@ pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lower
pub fn gather_fields(
subs: &Subs,
other_fields: &MutMap<Lowercase, RecordField<Variable>>,
other_fields: RecordFields,
mut var: Variable,
) -> RecordStructure {
use crate::subs::Content::*;
use crate::subs::FlatType::*;
let mut result = other_fields.clone();
let mut result = other_fields;
loop {
match subs.get_content_without_compacting(var) {
Structure(Record(sub_fields, sub_ext)) => {
for (lowercase, record_field) in sub_fields {
result.insert(lowercase.clone(), *record_field);
}
result = RecordFields::merge(result, sub_fields.clone());
var = *sub_ext;
}
@ -1554,3 +1552,46 @@ pub fn gather_fields(
ext: var,
}
}
pub fn gather_fields_ref(
subs: &Subs,
other_fields: &RecordFields,
mut var: Variable,
) -> RecordStructure {
use crate::subs::Content::*;
use crate::subs::FlatType::*;
let mut from_ext = Vec::new();
loop {
match subs.get_content_without_compacting(var) {
Structure(Record(sub_fields, sub_ext)) => {
from_ext.extend(sub_fields.into_iter());
var = *sub_ext;
}
Alias(_, _, actual_var) => {
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
var = *actual_var;
}
_ => break,
}
}
if from_ext.is_empty() {
RecordStructure {
fields: other_fields.clone(),
ext: var,
}
} else {
RecordStructure {
fields: other_fields
.into_iter()
.chain(from_ext.into_iter())
.collect(),
ext: var,
}
}
}

View file

@ -2,8 +2,8 @@ use roc_collections::all::{default_hasher, get_shared, relative_complement, unio
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_types::subs::Content::{self, *};
use roc_types::subs::{Descriptor, FlatType, Mark, OptVariable, Subs, Variable};
use roc_types::types::{gather_fields, ErrorType, Mismatch, RecordField, RecordStructure};
use roc_types::subs::{Descriptor, FlatType, Mark, OptVariable, RecordFields, Subs, Variable};
use roc_types::types::{gather_fields_ref, ErrorType, Mismatch, RecordField, RecordStructure};
macro_rules! mismatch {
() => {{
@ -262,28 +262,27 @@ fn unify_record(
) -> Outcome {
let fields1 = rec1.fields;
let fields2 = rec2.fields;
let shared_fields = get_shared(&fields1, &fields2);
// NOTE: don't use `difference` here. In contrast to Haskell, im's `difference` is symmetric
let unique_fields1 = relative_complement(&fields1, &fields2);
let unique_fields2 = relative_complement(&fields2, &fields1);
if unique_fields1.is_empty() {
if unique_fields2.is_empty() {
let separate = RecordFields::separate(fields1, fields2);
let shared_fields = separate.in_both;
if separate.only_in_1.is_empty() {
if separate.only_in_2.is_empty() {
let ext_problems = unify_pool(subs, pool, rec1.ext, rec2.ext);
if !ext_problems.is_empty() {
return ext_problems;
}
let other_fields = MutMap::default();
let mut field_problems =
unify_shared_fields(subs, pool, ctx, shared_fields, other_fields, rec1.ext);
unify_shared_fields(subs, pool, ctx, shared_fields, OtherFields::None, rec1.ext);
field_problems.extend(ext_problems);
field_problems
} else {
let flat_type = FlatType::Record(unique_fields2, rec2.ext);
let flat_type = FlatType::Record(separate.only_in_2, rec2.ext);
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
let ext_problems = unify_pool(subs, pool, rec1.ext, sub_record);
@ -291,16 +290,21 @@ fn unify_record(
return ext_problems;
}
let other_fields = MutMap::default();
let mut field_problems =
unify_shared_fields(subs, pool, ctx, shared_fields, other_fields, sub_record);
let mut field_problems = unify_shared_fields(
subs,
pool,
ctx,
shared_fields,
OtherFields::None,
sub_record,
);
field_problems.extend(ext_problems);
field_problems
}
} else if unique_fields2.is_empty() {
let flat_type = FlatType::Record(unique_fields1, rec1.ext);
} else if separate.only_in_2.is_empty() {
let flat_type = FlatType::Record(separate.only_in_1, rec1.ext);
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
let ext_problems = unify_pool(subs, pool, sub_record, rec2.ext);
@ -308,19 +312,29 @@ fn unify_record(
return ext_problems;
}
let other_fields = MutMap::default();
let mut field_problems =
unify_shared_fields(subs, pool, ctx, shared_fields, other_fields, sub_record);
let mut field_problems = unify_shared_fields(
subs,
pool,
ctx,
shared_fields,
OtherFields::None,
sub_record,
);
field_problems.extend(ext_problems);
field_problems
} else {
let other_fields = union(unique_fields1.clone(), &unique_fields2);
let it = (&separate.only_in_1)
.into_iter()
.chain((&separate.only_in_2).into_iter());
let other: RecordFields = it.collect();
let other_fields = OtherFields::Other(other);
let ext = fresh(subs, pool, ctx, Content::FlexVar(None));
let flat_type1 = FlatType::Record(unique_fields1, ext);
let flat_type2 = FlatType::Record(unique_fields2, ext);
let flat_type1 = FlatType::Record(separate.only_in_1, ext);
let flat_type2 = FlatType::Record(separate.only_in_2, ext);
let sub1 = fresh(subs, pool, ctx, Structure(flat_type1));
let sub2 = fresh(subs, pool, ctx, Structure(flat_type2));
@ -346,18 +360,23 @@ fn unify_record(
}
}
enum OtherFields {
None,
Other(RecordFields),
}
fn unify_shared_fields(
subs: &mut Subs,
pool: &mut Pool,
ctx: &Context,
shared_fields: MutMap<Lowercase, (RecordField<Variable>, RecordField<Variable>)>,
other_fields: MutMap<Lowercase, RecordField<Variable>>,
shared_fields: Vec<(Lowercase, RecordField<Variable>, RecordField<Variable>)>,
other_fields: OtherFields,
ext: Variable,
) -> Outcome {
let mut matching_fields = MutMap::default();
let mut matching_fields = Vec::with_capacity(shared_fields.len());
let num_shared_fields = shared_fields.len();
for (name, (actual, expected)) in shared_fields {
for (name, actual, expected) in shared_fields {
let local_problems = unify_pool(subs, pool, actual.into_inner(), expected.into_inner());
if local_problems.is_empty() {
@ -383,20 +402,38 @@ fn unify_shared_fields(
(Optional(val), Optional(_)) => Optional(val),
};
let existing = matching_fields.insert(name, actual);
debug_assert_eq!(existing, None);
matching_fields.push((name, actual));
}
}
if num_shared_fields == matching_fields.len() {
// pull fields in from the ext_var
let mut fields = union(matching_fields, &other_fields);
let new_ext_var = match roc_types::pretty_print::chase_ext_record(subs, ext, &mut fields) {
let mut ext_fields = MutMap::default();
let new_ext_var =
match roc_types::pretty_print::chase_ext_record(subs, ext, &mut ext_fields) {
Ok(()) => Variable::EMPTY_RECORD,
Err((new, _)) => new,
};
let fields: RecordFields = match other_fields {
OtherFields::None => {
if ext_fields.is_empty() {
RecordFields::from_sorted_vec(matching_fields)
} else {
matching_fields
.into_iter()
.chain(ext_fields.into_iter())
.collect()
}
}
OtherFields::Other(other_fields) => matching_fields
.into_iter()
.chain(other_fields.into_iter())
.chain(ext_fields.into_iter())
.collect(),
};
let flat_type = FlatType::Record(fields, new_ext_var);
merge(subs, ctx, Structure(flat_type))
@ -460,8 +497,8 @@ fn unify_tag_union(
if tags1.len() == 1
&& tags2.len() == 1
&& tags1 == tags2
&& subs.get_content_without_compacting(rec1.ext)
== subs.get_content_without_compacting(rec2.ext)
&& subs.get_root_key_without_compacting(rec1.ext)
== subs.get_root_key_without_compacting(rec2.ext)
{
return unify_shared_tags_merge(subs, ctx, tags1, rec1.ext, recursion_var);
}
@ -933,18 +970,6 @@ fn unify_shared_tags_merge(
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>>,
T: 'a,
{
fields.all(|field| match field {
RecordField::Required(_) => false,
RecordField::Demanded(_) => false,
RecordField::Optional(_) => true,
})
}
#[inline(always)]
fn unify_flat_type(
subs: &mut Subs,
@ -958,17 +983,17 @@ fn unify_flat_type(
match (left, right) {
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
(Record(fields, ext), EmptyRecord) if has_only_optional_fields(&mut fields.values()) => {
(Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields() => {
unify_pool(subs, pool, *ext, ctx.second)
}
(EmptyRecord, Record(fields, ext)) if has_only_optional_fields(&mut fields.values()) => {
(EmptyRecord, Record(fields, ext)) if fields.has_only_optional_fields() => {
unify_pool(subs, pool, ctx.first, *ext)
}
(Record(fields1, ext1), Record(fields2, ext2)) => {
let rec1 = gather_fields(subs, fields1, *ext1);
let rec2 = gather_fields(subs, fields2, *ext2);
let rec1 = gather_fields_ref(subs, fields1, *ext1);
let rec2 = gather_fields_ref(subs, fields2, *ext2);
unify_record(subs, pool, ctx, rec1, rec2)
}

View file

@ -760,7 +760,9 @@ fn type_to_variable<'a>(
Err((new, _)) => new,
};
let content = Content::Structure(FlatType::Record(field_vars, new_ext_var));
let record_fields = field_vars.into_iter().collect();
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
register(subs, rank, pools, content)
}
@ -1212,14 +1214,9 @@ fn adjust_rank_content(
Record(fields, ext_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
for var in fields.values() {
rank = rank.max(adjust_rank(
subs,
young_mark,
visit_mark,
group_rank,
var.into_inner(),
));
for var in fields.iter_variables() {
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
@ -1372,28 +1369,12 @@ fn instantiate_rigids_help(
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
Record(fields, ext_var) => {
let mut new_fields = MutMap::default();
for (label, field) in fields {
use RecordField::*;
let new_field = match field {
Demanded(var) => {
Demanded(instantiate_rigids_help(subs, max_rank, pools, var))
}
Required(var) => {
Required(instantiate_rigids_help(subs, max_rank, pools, var))
}
Optional(var) => {
Optional(instantiate_rigids_help(subs, max_rank, pools, var))
}
};
new_fields.insert(label, new_field);
for var in fields.iter_variables() {
instantiate_rigids_help(subs, max_rank, pools, *var);
}
Record(
new_fields,
fields,
instantiate_rigids_help(subs, max_rank, pools, ext_var),
)
}
@ -1566,31 +1547,12 @@ fn deep_copy_var_help(
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
Record(fields, ext_var) => {
let mut new_fields = MutMap::default();
for (label, field) in fields {
use RecordField::*;
let new_field = match field {
Demanded(var) => {
Demanded(deep_copy_var_help(subs, max_rank, pools, var))
}
Required(var) => {
Required(deep_copy_var_help(subs, max_rank, pools, var))
}
Optional(var) => {
Optional(deep_copy_var_help(subs, max_rank, pools, var))
}
};
new_fields.insert(label, new_field);
Record(mut fields, ext_var) => {
for var in fields.iter_variables_mut() {
*var = deep_copy_var_help(subs, max_rank, pools, *var);
}
Record(
new_fields,
deep_copy_var_help(subs, max_rank, pools, ext_var),
)
Record(fields, deep_copy_var_help(subs, max_rank, pools, ext_var))
}
TagUnion(tags, ext_var) => {