can store, but not load recursive fields

This commit is contained in:
Folkert 2020-08-22 16:01:04 +02:00
parent f4e1aa046d
commit ba186bfe09
8 changed files with 400 additions and 100 deletions

View file

@ -1,6 +1,6 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::MutMap;
use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_types::subs::{Content, FlatType, Subs, Variable};
@ -57,13 +57,43 @@ pub enum Builtin<'a> {
EmptySet,
}
pub struct Env<'a, 'b> {
arena: &'a Bump,
seen: MutSet<Variable>,
subs: &'b Subs,
}
impl<'a, 'b> Env<'a, 'b> {
fn is_seen(&self, var: Variable) -> bool {
let var = self.subs.get_root_key_without_compacting(var);
self.seen.contains(&var)
}
fn insert_seen(&mut self, var: Variable) -> bool {
let var = self.subs.get_root_key_without_compacting(var);
self.seen.insert(var)
}
}
impl<'a> Layout<'a> {
pub fn new(arena: &'a Bump, content: Content, subs: &Subs) -> Result<Self, LayoutProblem> {
let mut env = Env {
arena,
subs,
seen: MutSet::default(),
};
Self::new_help(&mut env, content)
}
fn new_help<'b>(env: &mut Env<'a, 'b>, content: Content) -> Result<Self, LayoutProblem> {
use roc_types::subs::Content::*;
match content {
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar),
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs),
Structure(flat_type) => layout_from_flat_type(env, flat_type),
Alias(Symbol::NUM_INT, args, _) => {
debug_assert!(args.is_empty());
@ -73,7 +103,7 @@ impl<'a> Layout<'a> {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float64))
}
Alias(_, _, var) => Self::new(arena, subs.get_without_compacting(var).content, subs),
Alias(_, _, var) => Self::from_var(env, var),
Error => Err(LayoutProblem::Erroneous),
}
}
@ -81,10 +111,14 @@ impl<'a> Layout<'a> {
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
/// Panics if given a FlexVar or RigidVar, since those should have been
/// monomorphized away already!
fn from_var(arena: &'a Bump, var: Variable, subs: &Subs) -> Result<Self, LayoutProblem> {
let content = subs.get_without_compacting(var).content;
Self::new(arena, content, subs)
fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result<Self, LayoutProblem> {
if env.is_seen(var) {
Ok(Layout::RecursivePointer)
} else {
let content = env.subs.get_without_compacting(var).content;
println!("{:?} {:?}", var, &content);
Self::new_help(env, content)
}
}
pub fn safe_to_memcpy(&self) -> bool {
@ -211,13 +245,15 @@ impl<'a> LayoutCache<'a> {
// Store things according to the root Variable, to avoid duplicate work.
let var = subs.get_root_key_without_compacting(var);
let mut env = Env {
arena,
subs,
seen: MutSet::default(),
};
self.layouts
.entry(var)
.or_insert_with(|| {
let content = subs.get_without_compacting(var).content;
Layout::new(arena, content, subs)
})
.or_insert_with(|| Layout::from_var(&mut env, var))
.clone()
}
}
@ -296,12 +332,14 @@ impl<'a> Builtin<'a> {
}
fn layout_from_flat_type<'a>(
arena: &'a Bump,
env: &mut Env<'a, '_>,
flat_type: FlatType,
subs: &Subs,
) -> Result<Layout<'a>, LayoutProblem> {
use roc_types::subs::FlatType::*;
let arena = env.arena;
let subs = env.subs;
match flat_type {
Apply(symbol, args) => {
match symbol {
@ -323,7 +361,7 @@ fn layout_from_flat_type<'a>(
layout_from_num_content(content)
}
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
Symbol::LIST_LIST => list_layout_from_elem(arena, subs, args[0]),
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
Symbol::ATTR_ATTR => {
debug_assert_eq!(args.len(), 2);
@ -332,7 +370,7 @@ fn layout_from_flat_type<'a>(
let wrapped_var = args[1];
// correct the memory mode of unique lists
match Layout::from_var(arena, wrapped_var, subs)? {
match Layout::from_var(env, wrapped_var)? {
Layout::Builtin(Builtin::List(_, elem_layout)) => {
let uniqueness_var = args[0];
let uniqueness_content =
@ -358,13 +396,10 @@ fn layout_from_flat_type<'a>(
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
for arg_var in args {
let arg_content = subs.get_without_compacting(arg_var).content;
fn_args.push(Layout::new(arena, arg_content, subs)?);
fn_args.push(Layout::from_var(env, arg_var)?);
}
let ret_content = subs.get_without_compacting(ret_var).content;
let ret = Layout::new(arena, ret_content, subs)?;
let ret = Layout::from_var(env, ret_var)?;
Ok(Layout::FunctionPointer(
fn_args.into_bump_slice(),
@ -400,9 +435,8 @@ fn layout_from_flat_type<'a>(
Demanded(var) => var,
}
};
let field_content = subs.get_without_compacting(field_var).content;
match Layout::new(arena, field_content, subs) {
match Layout::from_var(env, field_var) {
Ok(layout) => {
// Drop any zero-sized fields like {}.
if !layout.is_zero_sized() {
@ -441,6 +475,7 @@ fn layout_from_flat_type<'a>(
// That means none of the optimizations for enums or single tag tag unions apply
let rec_var = subs.get_root_key_without_compacting(rec_var);
env.insert_seen(rec_var);
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
// tags: MutMap<TagName, std::vec::Vec<Variable>>,
@ -457,15 +492,12 @@ fn layout_from_flat_type<'a>(
continue;
}
let var_content = subs.get_without_compacting(var).content;
tag_layout.push(Layout::new(arena, var_content, subs)?);
tag_layout.push(Layout::from_var(env, var)?);
}
tag_layouts.push(tag_layout.into_bump_slice());
}
dbg!(&tag_layouts);
Ok(Layout::RecursiveUnion(tag_layouts.into_bump_slice()))
}
EmptyTagUnion => {
@ -486,6 +518,12 @@ pub fn sort_record_fields<'a>(
) -> Vec<'a, (Lowercase, Result<Layout<'a>, Layout<'a>>)> {
let mut fields_map = MutMap::default();
let mut env = Env {
arena,
subs,
seen: MutSet::default(),
};
match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) {
Ok(()) | Err((_, Content::FlexVar(_))) => {
// Sort the fields by label
@ -498,13 +536,13 @@ pub fn sort_record_fields<'a>(
RecordField::Required(v) => v,
RecordField::Optional(v) => {
let layout =
Layout::from_var(arena, v, subs).expect("invalid layout from var");
Layout::from_var(&mut env, v).expect("invalid layout from var");
sorted_fields.push((label, Err(layout)));
continue;
}
};
let layout = Layout::from_var(arena, var, subs).expect("invalid layout from var");
let layout = Layout::from_var(&mut env, var).expect("invalid layout from var");
// Drop any zero-sized fields like {}
if !layout.is_zero_sized() {
@ -532,20 +570,44 @@ pub enum UnionVariant<'a> {
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> {
let mut tags_vec = std::vec::Vec::new();
match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
Ok(()) | Err((_, Content::FlexVar(_))) => union_sorted_tags_help(arena, tags_vec, subs),
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
Ok(()) | Err((_, Content::FlexVar(_))) => {
let opt_rec_var = get_recursion_var(subs, var);
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs)
}
Err(other) => panic!("invalid content in tag union variable: {:?}", other),
};
result
}
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),
_ => None,
}
}
fn union_sorted_tags_help<'a>(
arena: &'a Bump,
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
subs: &Subs,
) -> UnionVariant<'a> {
// sort up front; make sure the ordering stays intact!
tags_vec.sort();
let mut env = Env {
arena,
subs,
seen: MutSet::default(),
};
if let Some(rec_var) = opt_rec_var {
env.insert_seen(rec_var);
}
match tags_vec.len() {
0 => {
// trying to instantiate a type with no values
@ -564,7 +626,7 @@ fn union_sorted_tags_help<'a>(
}
_ => {
for var in arguments {
match Layout::from_var(arena, var, subs) {
match Layout::from_var(&mut env, var) {
Ok(layout) => {
// Drop any zero-sized arguments like {}
if !layout.is_zero_sized() {
@ -603,10 +665,8 @@ fn union_sorted_tags_help<'a>(
arg_layouts.push(Layout::Builtin(Builtin::Int64));
for var in arguments {
dbg!(&var);
match dbg!(Layout::from_var(arena, var, subs)) {
match Layout::from_var(&mut env, var) {
Ok(layout) => {
dbg!(&layout);
// Drop any zero-sized arguments like {}
if !layout.is_zero_sized() {
has_any_arguments = true;
@ -665,7 +725,8 @@ pub fn layout_from_tag_union<'a>(
let tags_vec: std::vec::Vec<_> = tags.into_iter().collect();
if tags_vec[0].0 != TagName::Private(Symbol::NUM_AT_NUM) {
let variant = union_sorted_tags_help(arena, tags_vec, subs);
let opt_rec_var = None;
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
match variant {
Never => panic!("TODO gracefully handle trying to instantiate Never"),
@ -796,29 +857,28 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
}
pub fn list_layout_from_elem<'a>(
arena: &'a Bump,
subs: &Subs,
env: &mut Env<'a, '_>,
elem_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> {
match subs.get_without_compacting(elem_var).content {
match env.subs.get_without_compacting(elem_var).content {
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
debug_assert_eq!(args.len(), 2);
let var = *args.get(1).unwrap();
list_layout_from_elem(arena, subs, var)
list_layout_from_elem(env, var)
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (List *) then it must have been an empty list
Ok(Layout::Builtin(Builtin::EmptyList))
}
content => {
let elem_layout = Layout::new(arena, content, subs)?;
let elem_layout = Layout::new_help(env, content)?;
// This is a normal list.
Ok(Layout::Builtin(Builtin::List(
MemoryMode::Refcounted,
arena.alloc(elem_layout),
env.arena.alloc(elem_layout),
)))
}
}