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;
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,
ident_ids,
arena,
@ -3024,8 +3024,16 @@ fn finish_specialization<'a>(
*layout,
);
glue_getters.extend(glue_procs.procs.iter().map(|t| t.0));
procedures.extend(glue_procs.procs);
glue_getters.extend(all_glue_procs.iter().flat_map(|(_, glue_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 struct GlueProcs<'a> {
pub procs: Vec<'a, ((Symbol, ProcLayout<'a>), Proc<'a>)>,
pub layouts: Vec<'a, Layout<'a>>,
pub struct GlueProc<'a> {
pub name: Symbol,
pub proc_layout: ProcLayout<'a>,
pub proc: Proc<'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,
layout_interner: &mut I,
layout: Layout<'a>,
) -> GlueProcs<'a> {
) -> Vec<'a, (Layout<'a>, Vec<'a, GlueProc<'a>>)> {
let mut stack = Vec::new_in(arena);
let mut procs = Vec::new_in(arena);
let mut layouts = Vec::new_in(arena);
let mut answer = Vec::new_in(arena);
stack.push(layout);
@ -10792,17 +10792,16 @@ pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
},
Layout::Struct { field_layouts, .. } => {
if layout.contains_function(arena) {
layouts.push(layout);
generate_glue_procs_for_fields(
let procs = generate_glue_procs_for_fields(
home,
ident_ids,
arena,
layout,
field_layouts,
&mut procs,
);
answer.push((layout, procs));
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>(
@ -10843,24 +10842,22 @@ fn generate_glue_procs_for_fields<'a>(
arena: &'a Bump,
unboxed_struct_layout: Layout<'a>,
field_layouts: &'a [Layout<'a>],
output: &mut Vec<'a, ((Symbol, ProcLayout<'a>), Proc<'a>)>,
) {
output.reserve(field_layouts.len());
) -> Vec<'a, GlueProc<'a>> {
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() {
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 {
arguments: arena.alloc([boxed_struct_layout]),
result: *field,
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 field_get_expr = Expr::StructAtIndex {
@ -10891,6 +10888,12 @@ fn generate_glue_procs_for_fields<'a>(
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::types::Types;
use bumpalo::Bump;
use roc_collections::MutMap;
use roc_intern::GlobalInterner;
use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading};
use roc_mono::ir::{generate_glue_procs, GlueProc};
use roc_mono::layout::LayoutCache;
use roc_reporting::report::RenderTarget;
use roc_target::{Architecture, TargetInfo};
@ -153,49 +155,53 @@ pub fn load_types(
operating_system,
};
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
let it = variables.clone().map(|var| {
use roc_mono::layout::Layout;
let mut glue_getters = bumpalo::collections::Vec::new_in(arena);
for var in variables.clone() {
let layout = layout_cache
.from_var(arena, var, subs)
.expect("Something weird ended up in the content");
// TODO right here, we want to check the entire layout for closures;
// if it has any, then we want to generate getters/setters for them!
match layout {
Layout::Builtin(_) => todo!(),
Layout::Struct {
field_order_hash,
field_layouts,
} => todo!(),
Layout::Boxed(_) => todo!(),
Layout::Union(_) => todo!(),
Layout::LambdaSet(_) => {
// TODO get function layout from LambdaSet
// let ret = &layout.result;
if layout.contains_function(arena) {
// Even though generate_glue_procs does more work than we need it to,
// it's important that we use it in order to make sure we get exactly
// the same names that mono::ir did for code gen!
for (layout, glue_procs) in
generate_glue_procs(home, ident_ids, arena, &mut layout_interner.fork(), layout)
{
let names =
bumpalo::collections::Vec::with_capacity_in(glue_procs.len(), arena);
// for layout in layout.arguments.iter().chain([ret]) {
// let glue_procs = roc_mono::ir::generate_glue_procs(
// home,
// ident_ids,
// arena,
// &mut layout_cache.interner,
// *layout,
// );
// Record all the getter/setter names associated with this layout
for GlueProc { name, .. } in glue_procs {
// Store them as strings, because symbols won't be useful to glue generators!
let name_string =
bumpalo::collections::String::from_str_in(name.as_str(&interns), arena);
// glue_getters.extend(glue_procs.procs.iter().map(|t| t.0));
// }
}
roc_mono::layout::Layout::RecursivePointer => todo!(),
// 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
// 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));
}

View file

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

View file

@ -6,17 +6,14 @@ use roc_builtins::bitcode::{
FloatWidth::*,
IntWidth::{self, *},
};
use roc_collections::VecMap;
use roc_collections::{MutMap, VecMap};
use roc_module::{
ident::TagName,
symbol::{Interns, Symbol},
symbol::{Interns, ModuleId, Symbol},
};
use roc_mono::{
ir::{Proc, ProcLayout},
layout::{
cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant,
Layout, LayoutCache, LayoutInterner, UnionLayout,
},
use roc_mono::layout::{
cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant, Layout,
LayoutCache, LayoutInterner, UnionLayout,
};
use roc_target::TargetInfo;
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,
subs: &'a Subs,
variables: I,
interns: &'a Interns,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
home: ModuleId,
layout_cache: LayoutCache<'a>,
target: TargetInfo,
) -> Self
where
// an iterator of (variable, getter glue procs for that variable)
I: Iterator<Item = (Variable, &'a [((Symbol, ProcLayout<'a>), Proc<'a>)])>,
{
) -> Self {
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);
// TODO incorporate glue_getter_procs!
}
env.resolve_pending_recursive_types(&mut types);
@ -269,11 +269,32 @@ impl Types {
(
NullableWrapped { tags: tags_a, .. },
NullableWrapped { tags: tags_b, .. },
) => {
if tags_a.len() != tags_b.len() {
false
} else {
tags_a.iter().zip(tags_b.iter()).all(
) => match (tags_a, tags_b) {
(
RocTags::HasClosure {
tag_getters: getters_a,
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 == name_b
&& match (opt_id_a, opt_id_b) {
@ -285,9 +306,9 @@ impl Types {
(None, Some(_)) | (Some(_), None) => false,
}
},
)
}
}
),
(_, _) => false,
},
(
NullableUnwrapped {
null_tag: null_tag_a,
@ -574,14 +595,19 @@ enum RocTypeOrPending<'a> {
Pending,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Accessors {
getter: String,
// TODO setter
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RocStructFields {
HasNoClosure {
fields: Vec<(String, TypeId)>,
},
HasClosure {
field_getters: Vec<(String, TypeId, RocFn)>,
// TODO field_setters
fields: Vec<(String, TypeId, Accessors)>,
},
}
@ -593,7 +619,7 @@ impl RocStructFields {
pub fn len(&self) -> usize {
match self {
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 {
name: String,
fields: Vec<(usize, TypeId)>,
fields: RocStructFields,
},
/// A recursive pointer, e.g. in StrConsList : [Nil, Cons Str StrConsList],
/// 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
/// application's implementation, so those sizes and order are not knowable at host build time.
HasClosure {
tag_getters: Vec<(String, Option<(TypeId, RocFn)>)>,
tag_getters: Vec<(String, Option<(TypeId, String)>)>,
discriminant_getter: RocFn,
},
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
/// application's implementation, so those sizes and order are not knowable at host build time.
HasClosure {
payload_getters: Vec<(TypeId, RocFn)>,
payload_getters: Vec<(TypeId, String)>,
},
HasNoClosures {
payload_fields: Vec<TypeId>,
@ -810,6 +836,7 @@ struct Env<'a> {
arena: &'a Bump,
subs: &'a Subs,
layout_cache: LayoutCache<'a>,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
interns: &'a Interns,
struct_names: Structs,
enum_names: Enums,
@ -819,11 +846,12 @@ struct Env<'a> {
}
impl<'a> Env<'a> {
pub fn new(
fn new(
arena: &'a Bump,
subs: &'a Subs,
interns: &'a Interns,
layout_cache: LayoutCache<'a>,
glue_procs_by_layout: &'a MutMap<Layout<'a>, &'a [&'a str]>,
target: TargetInfo,
) -> Self {
Env {
@ -834,6 +862,7 @@ impl<'a> Env<'a> {
enum_names: Default::default(),
pending_recursive_types: Default::default(),
known_recursive_types: Default::default(),
glue_procs_by_layout,
layout_cache,
target,
}
@ -1308,12 +1337,13 @@ fn add_struct<'a, I, L, F>(
where
I: IntoIterator<Item = (L, Variable)>,
L: Display + Ord,
F: FnOnce(String, Vec<(L, TypeId)>) -> RocType,
F: FnOnce(String, RocStructFields) -> RocType,
{
let subs = env.subs;
let arena = env.arena;
let fields_iter = &mut fields.into_iter();
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 {
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()
.map(|(label, field_var, field_layout)| {
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(
&env.layout_cache.interner,
name.clone(),
to_type(name, fields),
to_type(name, struct_fields),
layout,
)
}
@ -1378,19 +1436,15 @@ fn add_tag_union<'a>(
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.
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 {
name: name.clone(),
tag_name: tag_name.to_string(),
payload_fields: vec![payload_id],
payload,
}
}
Layout::Union(union_layout) => {
@ -1521,16 +1575,13 @@ fn add_tag_union<'a>(
add_int_enumeration(union_tags, subs, &name, int_width)
}
Layout::Struct { field_layouts, .. } => {
let (tag_name, payload_fields) =
single_tag_payload_fields(union_tags, subs, field_layouts, env, types);
let (tag_name, payload) =
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 {
name: name.clone(),
tag_name: tag_name.to_string(),
payload_fields,
payload,
}
}
Layout::Builtin(Builtin::Bool) => {
@ -1546,17 +1597,20 @@ fn add_tag_union<'a>(
RocTagUnion::SingleTagStruct {
name: name.clone(),
tag_name: tag_name.to_string(),
payload: RocSingleTagPayload::HasNoClosures {
// Builtins have no closures
payload_fields: vec![type_id],
},
}
}
Layout::Boxed(elem_layout) => {
let (tag_name, payload_fields) =
single_tag_payload_fields(union_tags, subs, &[*elem_layout], env, types);
let (tag_name, payload) =
single_tag_payload_fields(union_tags, subs, layout, &[*elem_layout], env, types);
RocTagUnion::SingleTagStruct {
name: name.clone(),
tag_name: tag_name.to_string(),
payload_fields,
payload,
}
}
Layout::LambdaSet(_) => {
@ -1647,20 +1701,44 @@ fn single_tag_payload<'a>(
fn single_tag_payload_fields<'a, 'b>(
union_tags: &'b UnionLabels<TagName>,
subs: &'b Subs,
layout: Layout<'a>,
field_layouts: &[Layout<'a>],
env: &mut Env<'a>,
types: &mut Types,
) -> (&'b str, Vec<TypeId>) {
todo!("TODO take closures into account and return one or the other");
) -> (&'b str, RocSingleTagPayload) {
let (tag_name, payload_vars) = single_tag_payload(union_tags, subs);
let payload_fields: Vec<TypeId> = payload_vars
let field_type_ids =
payload_vars
.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();
(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>(