Simplify how glue_procs are handled

This commit is contained in:
Richard Feldman 2022-11-13 21:04:27 -05:00
parent d35d268a6b
commit 8e2bbee377
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
5 changed files with 557 additions and 448 deletions

View file

@ -3016,7 +3016,7 @@ fn finish_specialization<'a>(
let ret = &layout.result; let ret = &layout.result;
for layout in layout.arguments.iter().chain([ret]) { for layout in layout.arguments.iter().chain([ret]) {
let glue_procs = roc_mono::ir::generate_glue_procs( let all_glue_procs = roc_mono::ir::generate_glue_procs(
module_id, module_id,
ident_ids, ident_ids,
arena, arena,
@ -3024,8 +3024,16 @@ fn finish_specialization<'a>(
*layout, *layout,
); );
glue_getters.extend(glue_procs.procs.iter().map(|t| t.0)); glue_getters.extend(all_glue_procs.iter().flat_map(|(_, glue_procs)| {
procedures.extend(glue_procs.procs); glue_procs
.iter()
.map(|glue_proc| (glue_proc.name, glue_proc.proc_layout))
}));
procedures.extend(all_glue_procs.into_iter().flat_map(|(_, glue_procs)| {
glue_procs
.into_iter()
.map(|glue_proc| ((glue_proc.name, glue_proc.proc_layout), glue_proc.proc))
}));
} }
} }

View file

@ -10762,9 +10762,10 @@ pub struct GlueLayouts<'a> {
pub getters: std::vec::Vec<(Symbol, ProcLayout<'a>)>, pub getters: std::vec::Vec<(Symbol, ProcLayout<'a>)>,
} }
pub struct GlueProcs<'a> { pub struct GlueProc<'a> {
pub procs: Vec<'a, ((Symbol, ProcLayout<'a>), Proc<'a>)>, pub name: Symbol,
pub layouts: Vec<'a, Layout<'a>>, pub proc_layout: ProcLayout<'a>,
pub proc: Proc<'a>,
} }
pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>( pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
@ -10773,10 +10774,9 @@ pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
arena: &'a Bump, arena: &'a Bump,
layout_interner: &mut I, layout_interner: &mut I,
layout: Layout<'a>, layout: Layout<'a>,
) -> GlueProcs<'a> { ) -> Vec<'a, (Layout<'a>, Vec<'a, GlueProc<'a>>)> {
let mut stack = Vec::new_in(arena); let mut stack = Vec::new_in(arena);
let mut procs = Vec::new_in(arena); let mut answer = Vec::new_in(arena);
let mut layouts = Vec::new_in(arena);
stack.push(layout); stack.push(layout);
@ -10792,17 +10792,16 @@ pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
}, },
Layout::Struct { field_layouts, .. } => { Layout::Struct { field_layouts, .. } => {
if layout.contains_function(arena) { if layout.contains_function(arena) {
layouts.push(layout); let procs = generate_glue_procs_for_fields(
generate_glue_procs_for_fields(
home, home,
ident_ids, ident_ids,
arena, arena,
layout, layout,
field_layouts, field_layouts,
&mut procs,
); );
answer.push((layout, procs));
stack.extend(field_layouts); stack.extend(field_layouts);
} }
} }
@ -10834,7 +10833,7 @@ pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
} }
} }
GlueProcs { procs, layouts } answer
} }
fn generate_glue_procs_for_fields<'a>( fn generate_glue_procs_for_fields<'a>(
@ -10843,24 +10842,22 @@ fn generate_glue_procs_for_fields<'a>(
arena: &'a Bump, arena: &'a Bump,
unboxed_struct_layout: Layout<'a>, unboxed_struct_layout: Layout<'a>,
field_layouts: &'a [Layout<'a>], field_layouts: &'a [Layout<'a>],
output: &mut Vec<'a, ((Symbol, ProcLayout<'a>), Proc<'a>)>, ) -> Vec<'a, GlueProc<'a>> {
) {
output.reserve(field_layouts.len());
let boxed_struct_layout = Layout::Boxed(arena.alloc(unboxed_struct_layout)); let boxed_struct_layout = Layout::Boxed(arena.alloc(unboxed_struct_layout));
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
for (index, field) in field_layouts.iter().enumerate() { for (index, field) in field_layouts.iter().enumerate() {
let symbol = Symbol::new(home, ident_ids.gen_unique());
let argument = Symbol::new(home, ident_ids.gen_unique());
let unboxed = Symbol::new(home, ident_ids.gen_unique());
let result = Symbol::new(home, ident_ids.gen_unique());
let proc_layout = ProcLayout { let proc_layout = ProcLayout {
arguments: arena.alloc([boxed_struct_layout]), arguments: arena.alloc([boxed_struct_layout]),
result: *field, result: *field,
captures_niche: CapturesNiche::no_niche(), captures_niche: CapturesNiche::no_niche(),
}; };
let symbol = Symbol::new(home, ident_ids.gen_unique());
let argument = Symbol::new(home, ident_ids.gen_unique());
let unboxed = Symbol::new(home, ident_ids.gen_unique());
let result = Symbol::new(home, ident_ids.gen_unique());
let ret_stmt = arena.alloc(Stmt::Ret(result)); let ret_stmt = arena.alloc(Stmt::Ret(result));
let field_get_expr = Expr::StructAtIndex { let field_get_expr = Expr::StructAtIndex {
@ -10891,6 +10888,12 @@ fn generate_glue_procs_for_fields<'a>(
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
}; };
output.push(((symbol, proc_layout), proc)); answer.push(GlueProc {
name: symbol,
proc_layout,
proc,
});
} }
answer
} }

View file

@ -1,8 +1,10 @@
use crate::rust_glue; use crate::rust_glue;
use crate::types::Types; use crate::types::Types;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::MutMap;
use roc_intern::GlobalInterner; use roc_intern::GlobalInterner;
use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading}; use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading};
use roc_mono::ir::{generate_glue_procs, GlueProc};
use roc_mono::layout::LayoutCache; use roc_mono::layout::LayoutCache;
use roc_reporting::report::RenderTarget; use roc_reporting::report::RenderTarget;
use roc_target::{Architecture, TargetInfo}; use roc_target::{Architecture, TargetInfo};
@ -153,49 +155,53 @@ pub fn load_types(
operating_system, operating_system,
}; };
let layout_cache = LayoutCache::new(layout_interner.fork(), target_info); let layout_cache = LayoutCache::new(layout_interner.fork(), target_info);
let mut proc_names_by_layout = MutMap::default();
// Populate glue getters/setters for all relevant variables // Populate glue getters/setters for all relevant variables
let it = variables.clone().map(|var| { for var in variables.clone() {
use roc_mono::layout::Layout;
let mut glue_getters = bumpalo::collections::Vec::new_in(arena);
let layout = layout_cache let layout = layout_cache
.from_var(arena, var, subs) .from_var(arena, var, subs)
.expect("Something weird ended up in the content"); .expect("Something weird ended up in the content");
// TODO right here, we want to check the entire layout for closures; if layout.contains_function(arena) {
// if it has any, then we want to generate getters/setters for them! // Even though generate_glue_procs does more work than we need it to,
match layout { // it's important that we use it in order to make sure we get exactly
Layout::Builtin(_) => todo!(), // the same names that mono::ir did for code gen!
Layout::Struct { for (layout, glue_procs) in
field_order_hash, generate_glue_procs(home, ident_ids, arena, &mut layout_interner.fork(), layout)
field_layouts, {
} => todo!(), let names =
Layout::Boxed(_) => todo!(), bumpalo::collections::Vec::with_capacity_in(glue_procs.len(), arena);
Layout::Union(_) => todo!(),
Layout::LambdaSet(_) => {
// TODO get function layout from LambdaSet
// let ret = &layout.result;
// for layout in layout.arguments.iter().chain([ret]) { // Record all the getter/setter names associated with this layout
// let glue_procs = roc_mono::ir::generate_glue_procs( for GlueProc { name, .. } in glue_procs {
// home, // Store them as strings, because symbols won't be useful to glue generators!
// ident_ids, let name_string =
// arena, bumpalo::collections::String::from_str_in(name.as_str(&interns), arena);
// &mut layout_cache.interner,
// *layout,
// );
// glue_getters.extend(glue_procs.procs.iter().map(|t| t.0)); // Given a struct layout (including lambda sets!) the offsets - and therefore
// } // getters/setters - are deterministic, so we can use layout as the hash key
} // for these getters/setters. We also only need to store the name because since
roc_mono::layout::Layout::RecursivePointer => todo!(), // they are getters and setters, we can know their types (from a TypeId perspective)
// deterministically based on knowing the types of the structs and fields.
names.push(name_string.into_bump_str());
} }
(var, glue_getters.into_bump_slice()) proc_names_by_layout.insert(layout, names.into_bump_slice());
}); }
}
}
let types = Types::new(arena, subs, it, &interns, layout_cache, target_info); let types = Types::new(
arena,
subs,
variables.clone(),
&interns,
&proc_names_by_layout,
home,
layout_cache,
target_info,
);
types_and_targets.push((types, target_info)); types_and_targets.push((types, target_info));
} }

View file

@ -1,6 +1,6 @@
use crate::types::{ use crate::types::{
RocFn, RocNum, RocSingleTagPayload, RocStructFields, RocTagUnion, RocTags, RocType, TypeId, Accessors, RocFn, RocNum, RocSingleTagPayload, RocStructFields, RocTagUnion, RocTags, RocType,
Types, TypeId, Types,
}; };
use indexmap::IndexMap; use indexmap::IndexMap;
use roc_target::{Architecture, TargetInfo}; use roc_target::{Architecture, TargetInfo};
@ -143,15 +143,8 @@ pub fn emit(types_and_targets: &[(Types, TargetInfo)]) -> String {
fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impls) { fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impls) {
match types.get_type(id) { match types.get_type(id) {
RocType::Struct { RocType::Struct { name, fields } => {
name, add_struct(name, target_info, fields, id, types, impls, false)
fields: RocStructFields::HasNoClosure { fields },
} => add_struct(name, target_info, fields, id, types, impls, false),
RocType::Struct {
name,
fields: RocStructFields::HasClosure { field_getters },
} => {
todo!();
} }
RocType::TagUnionPayload { name, fields } => { RocType::TagUnionPayload { name, fields } => {
add_struct(name, target_info, fields, id, types, impls, true) add_struct(name, target_info, fields, id, types, impls, true)
@ -382,16 +375,18 @@ fn add_single_tag_struct(
buf.push_str("}\n"); buf.push_str("}\n");
} }
let field_getters = payload_getters let fields = payload_getters
.iter() .iter()
.map(|(type_id, roc_fn)| (String::new(), *type_id, *roc_fn)) .map(|(type_id, getter)| {
(String::new(), *type_id, Accessors { getter: *getter })
})
.collect(); .collect();
RocType::Struct { RocType::Struct {
// Deriving doesn't depend on the struct's name, // Deriving doesn't depend on the struct's name,
// so no need to clone name here. // so no need to clone name here.
name: String::new(), name: String::new(),
fields: RocStructFields::HasClosure { field_getters }, fields: RocStructFields::HasClosure { fields },
} }
} }
} }
@ -437,7 +432,8 @@ fn add_single_tag_struct(
} }
// the impl for the single-tag union itself // the impl for the single-tag union itself
{ match payload {
RocSingleTagPayload::HasNoClosures { payload_fields } => {
let opt_impl = Some(format!("impl {name}")); let opt_impl = Some(format!("impl {name}"));
if payload_fields.is_empty() { if payload_fields.is_empty() {
@ -587,13 +583,15 @@ fn add_single_tag_struct(
} }
} }
} }
}
// The Debug impl for the single-tag union // The Debug impl for the single-tag union
{ match payload {
RocSingleTagPayload::HasNoClosures { payload_fields } => {
let opt_impl = Some(format!("impl core::fmt::Debug for {name}")); let opt_impl = Some(format!("impl core::fmt::Debug for {name}"));
let mut buf = let mut buf = "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {"
"fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {".to_string(); .to_string();
if payload_fields.is_empty() { if payload_fields.is_empty() {
// ignore returned result, write can not fail as it is used here // ignore returned result, write can not fail as it is used here
@ -619,6 +617,7 @@ fn add_single_tag_struct(
add_decl(impls, opt_impl, target_info, buf); add_decl(impls, opt_impl, target_info, buf);
} }
} }
}
fn add_discriminant( fn add_discriminant(
name: &str, name: &str,
@ -1044,8 +1043,7 @@ pub struct {name} {{
}; };
} }
RocType::Struct { fields, name } => { RocType::Struct { fields, name } => {
let answer = let answer = tag_union_struct_help(name, fields, *payload_id, types, false);
tag_union_struct_help(name, fields.iter(), *payload_id, types, false);
owned_ret = answer.owned_ret; owned_ret = answer.owned_ret;
borrowed_ret = answer.borrowed_ret; borrowed_ret = answer.borrowed_ret;
@ -1055,8 +1053,7 @@ pub struct {name} {{
args_to_payload = answer.args_to_payload; args_to_payload = answer.args_to_payload;
} }
RocType::TagUnionPayload { fields, name } => { RocType::TagUnionPayload { fields, name } => {
let answer = let answer = tag_union_struct_help(name, fields, *payload_id, types, true);
tag_union_struct_help(name, fields.iter(), *payload_id, types, true);
owned_ret = answer.owned_ret; owned_ret = answer.owned_ret;
borrowed_ret = answer.borrowed_ret; borrowed_ret = answer.borrowed_ret;
@ -1664,12 +1661,16 @@ pub struct {name} {{
RocType::TagUnionPayload { fields, .. } => { RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new(); let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields { for (label, _) in fields {
// Needs an "f" prefix // Needs an "f" prefix
buf.push(format!( buf.push(format!(
".field(&({deref_str}{actual_self}.{tag_name}).f{label})" ".field(&({deref_str}{actual_self}.{tag_name}).f{label})"
)); ));
} }
}
}
buf.join("\n") buf.join("\n")
} }
@ -1766,10 +1767,10 @@ fn add_enumeration<I: ExactSizeIterator<Item = S>, S: AsRef<str> + Display>(
add_decl(impls, None, target_info, buf); add_decl(impls, None, target_info, buf);
} }
fn add_struct<S: Display>( fn add_struct(
name: &str, name: &str,
target_info: TargetInfo, target_info: TargetInfo,
fields: &[(S, TypeId)], fields: &RocStructFields,
struct_id: TypeId, struct_id: TypeId,
types: &Types, types: &Types,
impls: &mut Impls, impls: &mut Impls,
@ -1778,12 +1779,17 @@ fn add_struct<S: Display>(
let name = escape_kw(name.to_string()); let name = escape_kw(name.to_string());
let derive = derive_str(types.get_type(struct_id), types, true); let derive = derive_str(types.get_type(struct_id), types, true);
let pub_str = if is_tag_union_payload { "" } else { "pub " }; let pub_str = if is_tag_union_payload { "" } else { "pub " };
let mut buf;
match fields {
RocStructFields::HasNoClosure { fields } => {
let repr = if fields.len() == 1 { let repr = if fields.len() == 1 {
"transparent" "transparent"
} else { } else {
"C" "C"
}; };
let mut buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
for (label, type_id) in fields { for (label, type_id) in fields {
let type_str = type_name(*type_id, types); let type_str = type_name(*type_id, types);
@ -1800,6 +1806,8 @@ fn add_struct<S: Display>(
} }
buf.push('}'); buf.push('}');
}
}
add_decl(impls, None, target_info, buf); add_decl(impls, None, target_info, buf);
} }
@ -1976,8 +1984,7 @@ pub struct {name} {{
borrowed_ret = format!("&{owned_ret}"); borrowed_ret = format!("&{owned_ret}");
} }
RocType::Struct { fields, name } => { RocType::Struct { fields, name } => {
let answer = let answer = tag_union_struct_help(name, fields, non_null_payload, types, false);
tag_union_struct_help(name, fields.iter(), non_null_payload, types, false);
payload_args = answer.payload_args; payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload; args_to_payload = answer.args_to_payload;
@ -1987,8 +1994,7 @@ pub struct {name} {{
borrowed_ret_type = answer.borrowed_ret_type; borrowed_ret_type = answer.borrowed_ret_type;
} }
RocType::TagUnionPayload { fields, name } => { RocType::TagUnionPayload { fields, name } => {
let answer = let answer = tag_union_struct_help(name, fields, non_null_payload, types, true);
tag_union_struct_help(name, fields.iter(), non_null_payload, types, true);
payload_args = answer.payload_args; payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload; args_to_payload = answer.args_to_payload;
@ -2224,19 +2230,27 @@ pub struct {name} {{
RocType::Struct { fields, .. } => { RocType::Struct { fields, .. } => {
let mut buf = Vec::new(); let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields { for (label, _) in fields {
buf.push(format!(".field(&(&*{extra_deref}self.pointer).{label})")); buf.push(format!(".field(&(&*{extra_deref}self.pointer).{label})"));
} }
}
}
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}")) buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
} }
RocType::TagUnionPayload { fields, .. } => { RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new(); let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields { for (label, _) in fields {
// Needs an "f" prefix // Needs an "f" prefix
buf.push(format!(".field(&(&*{extra_deref}self.pointer).f{label})")); buf.push(format!(".field(&(&*{extra_deref}self.pointer).f{label})"));
} }
}
}
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}")) buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
} }
@ -2307,17 +2321,15 @@ struct StructIngredients {
borrowed_ret_type: String, borrowed_ret_type: String,
} }
fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + PartialOrd + 'a>( fn tag_union_struct_help<'a>(
name: &str, name: &str,
fields: I, fields: &RocStructFields,
payload_id: TypeId, payload_id: TypeId,
types: &Types, types: &Types,
is_tag_union_payload: bool, is_tag_union_payload: bool,
) -> StructIngredients { ) -> StructIngredients {
let mut sorted_fields = fields.collect::<Vec<&(L, TypeId)>>(); match fields {
RocStructFields::HasNoClosure { fields } => {
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.partial_cmp(label2).unwrap());
let mut ret_types = if is_tag_union_payload { let mut ret_types = if is_tag_union_payload {
// This will be a tuple we create when iterating over the fields. // This will be a tuple we create when iterating over the fields.
Vec::new() Vec::new()
@ -2327,7 +2339,7 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
let mut ret_values = Vec::new(); let mut ret_values = Vec::new();
for (label, type_id) in sorted_fields.iter() { for (label, type_id) in fields.iter() {
let label = if is_tag_union_payload { let label = if is_tag_union_payload {
// Tag union payload fields need "f" prefix // Tag union payload fields need "f" prefix
// because they're numbers // because they're numbers
@ -2352,7 +2364,7 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", "); .join(", ");
let args_to_payload = if is_tag_union_payload { let args_to_payload = if is_tag_union_payload {
let prefixed_fields = sorted_fields let prefixed_fields = fields
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, (label, _))| { .map(|(index, (label, _))| {
@ -2439,6 +2451,8 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
borrowed_ret_type, borrowed_ret_type,
} }
} }
}
}
fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool { fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool {
match roc_type { match roc_type {

View file

@ -6,17 +6,14 @@ use roc_builtins::bitcode::{
FloatWidth::*, FloatWidth::*,
IntWidth::{self, *}, IntWidth::{self, *},
}; };
use roc_collections::VecMap; use roc_collections::{MutMap, VecMap};
use roc_module::{ use roc_module::{
ident::TagName, ident::TagName,
symbol::{Interns, Symbol}, symbol::{Interns, ModuleId, Symbol},
}; };
use roc_mono::{ use roc_mono::layout::{
ir::{Proc, ProcLayout}, cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant, Layout,
layout::{ LayoutCache, LayoutInterner, UnionLayout,
cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant,
Layout, LayoutCache, LayoutInterner, UnionLayout,
},
}; };
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_types::{ use roc_types::{
@ -70,25 +67,28 @@ impl Types {
} }
} }
pub(crate) fn new<'a, I>( pub(crate) fn new<'a, I: Iterator<Item = Variable>>(
arena: &'a Bump, arena: &'a Bump,
subs: &'a Subs, subs: &'a Subs,
variables: I, variables: I,
interns: &'a Interns, interns: &'a Interns,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
home: ModuleId,
layout_cache: LayoutCache<'a>, layout_cache: LayoutCache<'a>,
target: TargetInfo, target: TargetInfo,
) -> Self ) -> Self {
where
// an iterator of (variable, getter glue procs for that variable)
I: Iterator<Item = (Variable, &'a [((Symbol, ProcLayout<'a>), Proc<'a>)])>,
{
let mut types = Self::with_capacity(variables.size_hint().0, target); let mut types = Self::with_capacity(variables.size_hint().0, target);
let mut env = Env::new(arena, subs, interns, layout_cache, target); let mut env = Env::new(
arena,
subs,
interns,
layout_cache,
glue_procs_by_layout,
target,
);
for (var, glue_getter_procs) in variables { for var in variables {
env.add_type(var, &mut types); env.add_type(var, &mut types);
// TODO incorporate glue_getter_procs!
} }
env.resolve_pending_recursive_types(&mut types); env.resolve_pending_recursive_types(&mut types);
@ -269,11 +269,32 @@ impl Types {
( (
NullableWrapped { tags: tags_a, .. }, NullableWrapped { tags: tags_a, .. },
NullableWrapped { tags: tags_b, .. }, NullableWrapped { tags: tags_b, .. },
) => { ) => match (tags_a, tags_b) {
if tags_a.len() != tags_b.len() { (
false RocTags::HasClosure {
} else { tag_getters: getters_a,
tags_a.iter().zip(tags_b.iter()).all( discriminant_getter: _,
},
RocTags::HasClosure {
tag_getters: getters_b,
discriminant_getter: _,
},
) if getters_a.len() == getters_b.len() => getters_a
.iter()
.zip(getters_b.iter())
.all(|((name_a, opt_getter_a), (name_b, opt_getter_b))| {
name_a == name_b && opt_getter_a == opt_getter_b
}),
(
RocTags::HasNoClosures {
tags: tags_a,
discriminant_offset: _,
},
RocTags::HasNoClosures {
tags: tags_b,
discriminant_offset: _,
},
) if tags_a.len() == tags_b.len() => tags_a.iter().zip(tags_b.iter()).all(
|((name_a, opt_id_a), (name_b, opt_id_b))| { |((name_a, opt_id_a), (name_b, opt_id_b))| {
name_a == name_b name_a == name_b
&& match (opt_id_a, opt_id_b) { && match (opt_id_a, opt_id_b) {
@ -285,9 +306,9 @@ impl Types {
(None, Some(_)) | (Some(_), None) => false, (None, Some(_)) | (Some(_), None) => false,
} }
}, },
) ),
} (_, _) => false,
} },
( (
NullableUnwrapped { NullableUnwrapped {
null_tag: null_tag_a, null_tag: null_tag_a,
@ -574,14 +595,19 @@ enum RocTypeOrPending<'a> {
Pending, Pending,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Accessors {
getter: String,
// TODO setter
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RocStructFields { pub enum RocStructFields {
HasNoClosure { HasNoClosure {
fields: Vec<(String, TypeId)>, fields: Vec<(String, TypeId)>,
}, },
HasClosure { HasClosure {
field_getters: Vec<(String, TypeId, RocFn)>, fields: Vec<(String, TypeId, Accessors)>,
// TODO field_setters
}, },
} }
@ -593,7 +619,7 @@ impl RocStructFields {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match self { match self {
RocStructFields::HasNoClosure { fields } => fields.len(), RocStructFields::HasNoClosure { fields } => fields.len(),
RocStructFields::HasClosure { field_getters } => field_getters.len(), RocStructFields::HasClosure { fields } => fields.len(),
} }
} }
} }
@ -623,7 +649,7 @@ pub enum RocType {
}, },
TagUnionPayload { TagUnionPayload {
name: String, name: String,
fields: Vec<(usize, TypeId)>, fields: RocStructFields,
}, },
/// A recursive pointer, e.g. in StrConsList : [Nil, Cons Str StrConsList], /// A recursive pointer, e.g. in StrConsList : [Nil, Cons Str StrConsList],
/// this would be the field of Cons containing the (recursive) StrConsList type, /// this would be the field of Cons containing the (recursive) StrConsList type,
@ -701,7 +727,7 @@ pub enum RocTags {
/// field getters and setters because the size and order of those fields can vary based on the /// field getters and setters because the size and order of those fields can vary based on the
/// application's implementation, so those sizes and order are not knowable at host build time. /// application's implementation, so those sizes and order are not knowable at host build time.
HasClosure { HasClosure {
tag_getters: Vec<(String, Option<(TypeId, RocFn)>)>, tag_getters: Vec<(String, Option<(TypeId, String)>)>,
discriminant_getter: RocFn, discriminant_getter: RocFn,
}, },
HasNoClosures { HasNoClosures {
@ -729,7 +755,7 @@ pub enum RocSingleTagPayload {
/// field getters and setters because the size and order of those fields can vary based on the /// field getters and setters because the size and order of those fields can vary based on the
/// application's implementation, so those sizes and order are not knowable at host build time. /// application's implementation, so those sizes and order are not knowable at host build time.
HasClosure { HasClosure {
payload_getters: Vec<(TypeId, RocFn)>, payload_getters: Vec<(TypeId, String)>,
}, },
HasNoClosures { HasNoClosures {
payload_fields: Vec<TypeId>, payload_fields: Vec<TypeId>,
@ -810,6 +836,7 @@ struct Env<'a> {
arena: &'a Bump, arena: &'a Bump,
subs: &'a Subs, subs: &'a Subs,
layout_cache: LayoutCache<'a>, layout_cache: LayoutCache<'a>,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
interns: &'a Interns, interns: &'a Interns,
struct_names: Structs, struct_names: Structs,
enum_names: Enums, enum_names: Enums,
@ -819,11 +846,12 @@ struct Env<'a> {
} }
impl<'a> Env<'a> { impl<'a> Env<'a> {
pub fn new( fn new(
arena: &'a Bump, arena: &'a Bump,
subs: &'a Subs, subs: &'a Subs,
interns: &'a Interns, interns: &'a Interns,
layout_cache: LayoutCache<'a>, layout_cache: LayoutCache<'a>,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
target: TargetInfo, target: TargetInfo,
) -> Self { ) -> Self {
Env { Env {
@ -834,6 +862,7 @@ impl<'a> Env<'a> {
enum_names: Default::default(), enum_names: Default::default(),
pending_recursive_types: Default::default(), pending_recursive_types: Default::default(),
known_recursive_types: Default::default(), known_recursive_types: Default::default(),
glue_procs_by_layout,
layout_cache, layout_cache,
target, target,
} }
@ -1308,12 +1337,13 @@ fn add_struct<'a, I, L, F>(
where where
I: IntoIterator<Item = (L, Variable)>, I: IntoIterator<Item = (L, Variable)>,
L: Display + Ord, L: Display + Ord,
F: FnOnce(String, Vec<(L, TypeId)>) -> RocType, F: FnOnce(String, RocStructFields) -> RocType,
{ {
let subs = env.subs; let subs = env.subs;
let arena = env.arena;
let fields_iter = &mut fields.into_iter(); let fields_iter = &mut fields.into_iter();
let mut sortables = let mut sortables =
bumpalo::collections::Vec::with_capacity_in(fields_iter.size_hint().0, env.arena); bumpalo::collections::Vec::with_capacity_in(fields_iter.size_hint().0, arena);
for (label, field_var) in fields_iter { for (label, field_var) in fields_iter {
sortables.push(( sortables.push((
@ -1336,19 +1366,47 @@ where
) )
}); });
let fields = sortables // This layout should have an entry in glue_procs_by_layout iff it
// contains closures, but we'll double-check that with a debug_assert.
let struct_fields = match env.glue_procs_by_layout.get(&layout) {
Some(&glue_procs) => {
debug_assert!(layout.contains_function(arena));
let fields: Vec<(String, TypeId, Accessors)> = sortables
.into_iter()
.zip(glue_procs.iter())
.map(|((label, field_var, field_layout), getter)| {
let type_id = add_type_help(env, field_layout, field_var, None, types);
let accessors = Accessors {
getter: getter.to_string(),
};
(format!("{}", label), type_id, accessors)
})
.collect();
RocStructFields::HasClosure { fields }
}
None => {
debug_assert!(!layout.contains_function(arena));
let fields: Vec<(String, TypeId)> = sortables
.into_iter() .into_iter()
.map(|(label, field_var, field_layout)| { .map(|(label, field_var, field_layout)| {
let type_id = add_type_help(env, field_layout, field_var, None, types); let type_id = add_type_help(env, field_layout, field_var, None, types);
(label, type_id) (format!("{}", label), type_id)
}) })
.collect::<Vec<(L, TypeId)>>(); .collect();
RocStructFields::HasNoClosure { fields }
}
};
types.add_named( types.add_named(
&env.layout_cache.interner, &env.layout_cache.interner,
name.clone(), name.clone(),
to_type(name, fields), to_type(name, struct_fields),
layout, layout,
) )
} }
@ -1378,19 +1436,15 @@ fn add_tag_union<'a>(
Content::Structure(FlatType::TagUnion(_, _)) Content::Structure(FlatType::TagUnion(_, _))
) => ) =>
{ {
let (tag_name, payload_vars) = single_tag_payload(union_tags, subs);
// A newtype wrapper should always have exactly one payload.
debug_assert_eq!(payload_vars.len(), 1);
// A newtype wrapper should always have the same layout as its payload. // A newtype wrapper should always have the same layout as its payload.
let payload_layout = layout; let payload_layout = layout;
let payload_id = add_type_help(env, payload_layout, payload_vars[0], None, types); let (tag_name, payload) =
single_tag_payload_fields(union_tags, subs, layout, &[payload_layout], env, types);
RocTagUnion::SingleTagStruct { RocTagUnion::SingleTagStruct {
name: name.clone(), name: name.clone(),
tag_name: tag_name.to_string(), tag_name: tag_name.to_string(),
payload_fields: vec![payload_id], payload,
} }
} }
Layout::Union(union_layout) => { Layout::Union(union_layout) => {
@ -1521,16 +1575,13 @@ fn add_tag_union<'a>(
add_int_enumeration(union_tags, subs, &name, int_width) add_int_enumeration(union_tags, subs, &name, int_width)
} }
Layout::Struct { field_layouts, .. } => { Layout::Struct { field_layouts, .. } => {
let (tag_name, payload_fields) = let (tag_name, payload) =
single_tag_payload_fields(union_tags, subs, field_layouts, env, types); single_tag_payload_fields(union_tags, subs, layout, field_layouts, env, types);
// A recursive tag union with just one constructor
// Optimization: No need to store a tag ID (the payload is "unwrapped")
// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
RocTagUnion::SingleTagStruct { RocTagUnion::SingleTagStruct {
name: name.clone(), name: name.clone(),
tag_name: tag_name.to_string(), tag_name: tag_name.to_string(),
payload_fields, payload,
} }
} }
Layout::Builtin(Builtin::Bool) => { Layout::Builtin(Builtin::Bool) => {
@ -1546,17 +1597,20 @@ fn add_tag_union<'a>(
RocTagUnion::SingleTagStruct { RocTagUnion::SingleTagStruct {
name: name.clone(), name: name.clone(),
tag_name: tag_name.to_string(), tag_name: tag_name.to_string(),
payload: RocSingleTagPayload::HasNoClosures {
// Builtins have no closures
payload_fields: vec![type_id], payload_fields: vec![type_id],
},
} }
} }
Layout::Boxed(elem_layout) => { Layout::Boxed(elem_layout) => {
let (tag_name, payload_fields) = let (tag_name, payload) =
single_tag_payload_fields(union_tags, subs, &[*elem_layout], env, types); single_tag_payload_fields(union_tags, subs, layout, &[*elem_layout], env, types);
RocTagUnion::SingleTagStruct { RocTagUnion::SingleTagStruct {
name: name.clone(), name: name.clone(),
tag_name: tag_name.to_string(), tag_name: tag_name.to_string(),
payload_fields, payload,
} }
} }
Layout::LambdaSet(_) => { Layout::LambdaSet(_) => {
@ -1647,20 +1701,44 @@ fn single_tag_payload<'a>(
fn single_tag_payload_fields<'a, 'b>( fn single_tag_payload_fields<'a, 'b>(
union_tags: &'b UnionLabels<TagName>, union_tags: &'b UnionLabels<TagName>,
subs: &'b Subs, subs: &'b Subs,
layout: Layout<'a>,
field_layouts: &[Layout<'a>], field_layouts: &[Layout<'a>],
env: &mut Env<'a>, env: &mut Env<'a>,
types: &mut Types, types: &mut Types,
) -> (&'b str, Vec<TypeId>) { ) -> (&'b str, RocSingleTagPayload) {
todo!("TODO take closures into account and return one or the other");
let (tag_name, payload_vars) = single_tag_payload(union_tags, subs); let (tag_name, payload_vars) = single_tag_payload(union_tags, subs);
let field_type_ids =
let payload_fields: Vec<TypeId> = payload_vars payload_vars
.iter() .iter()
.zip(field_layouts.iter()) .zip(field_layouts.iter())
.map(|(field_var, field_layout)| add_type_help(env, *field_layout, *field_var, None, types)) .map(|(field_var, field_layout)| {
add_type_help(env, *field_layout, *field_var, None, types)
});
// There should be a glue_procs_by_layout entry iff this layout has a closure in it,
// so we shouldn't need to separately check that. Howeevr, we still do a debug_assert
// anyway just so we have some warning in case that relationship somehow didn't hold!
let payload = match env.glue_procs_by_layout.get(&layout) {
Some(glue_procs) => {
debug_assert!(layout.contains_function(env.arena));
let payload_getters = field_type_ids
.zip(glue_procs.iter())
.map(|(type_id, getter_name)| (type_id, getter_name.to_string()))
.collect(); .collect();
(tag_name, payload_fields) RocSingleTagPayload::HasClosure { payload_getters }
}
None => {
debug_assert!(!layout.contains_function(env.arena));
RocSingleTagPayload::HasNoClosures {
payload_fields: field_type_ids.collect(),
}
}
};
(tag_name, payload)
} }
fn tag_to_type<'a, D: Display>( fn tag_to_type<'a, D: Display>(