diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index ba444901bd..75aa1ec721 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -640,16 +640,16 @@ fn from_can<'a>( } => { let arena = env.arena; - let btree = crate::layout::record_fields_btree( + let sorted_fields = crate::layout::sort_record_fields( env.arena, record_var, env.subs, env.pointer_size, ); - let mut field_tuples = Vec::with_capacity_in(btree.len(), arena); + let mut field_tuples = Vec::with_capacity_in(sorted_fields.len(), arena); - for (label, layout) in btree { + for (label, layout) in sorted_fields { let field = fields.remove(&label).unwrap(); let expr = from_can(env, field.loc_expr.value, procs, layout_cache); @@ -749,7 +749,7 @@ fn from_can<'a>( } => { let arena = env.arena; - let btree = crate::layout::record_fields_btree( + let sorted_fields = crate::layout::sort_record_fields( env.arena, record_var, env.subs, @@ -757,9 +757,9 @@ fn from_can<'a>( ); let mut index = None; - let mut field_layouts = Vec::with_capacity_in(btree.len(), env.arena); + let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); - for (current, (label, field_layout)) in btree.into_iter().enumerate() { + for (current, (label, field_layout)) in sorted_fields.into_iter().enumerate() { field_layouts.push(field_layout); if label == field { @@ -1675,16 +1675,16 @@ fn from_can_pattern<'a>( let mut it = destructs.iter(); let mut opt_destruct = it.next(); - let btree = crate::layout::record_fields_btree( + let sorted_fields = crate::layout::sort_record_fields( env.arena, *whole_var, env.subs, env.pointer_size, ); - let mut field_layouts = Vec::with_capacity_in(btree.len(), env.arena); + let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); - for (label, field_layout) in btree.into_iter() { + for (label, field_layout) in sorted_fields.into_iter() { if let Some(destruct) = opt_destruct { if destruct.value.label == label { opt_destruct = it.next(); diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 87edce0f39..dac22c1503 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -4,7 +4,6 @@ use roc_collections::all::MutMap; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::Symbol; use roc_types::subs::{Content, FlatType, Subs, Variable}; -use std::collections::BTreeMap; pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::() * 8) as usize; @@ -304,13 +303,19 @@ fn layout_from_flat_type<'a>( Record(fields, ext_var) => { debug_assert!(ext_var_is_empty_record(subs, ext_var)); - let btree = fields - .into_iter() - .collect::>(); + // Sort the fields by label + let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena); - let mut layouts = Vec::with_capacity_in(btree.len(), arena); + for tuple in fields { + sorted_fields.push(tuple); + } - for (_, field_var) in btree { + sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2)); + + // Determine the layouts of the fields, maintaining sort order + let mut layouts = Vec::with_capacity_in(sorted_fields.len(), arena); + + for (_, field_var) in sorted_fields { let field_content = subs.get_without_compacting(field_var).content; match Layout::new(arena, field_content, subs, pointer_size) { @@ -327,7 +332,13 @@ fn layout_from_flat_type<'a>( } } - Ok(Layout::Struct(layouts.into_bump_slice())) + if layouts.len() == 1 { + // If the record has only one field that isn't zero-sized, + // unwrap it. + Ok(layouts.pop().unwrap()) + } else { + Ok(Layout::Struct(layouts.into_bump_slice())) + } } TagUnion(tags, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); @@ -348,26 +359,32 @@ fn layout_from_flat_type<'a>( } } -pub fn record_fields_btree<'a>( +pub fn sort_record_fields<'a>( arena: &'a Bump, var: Variable, subs: &Subs, pointer_size: u32, -) -> BTreeMap> { +) -> Vec<'a, (Lowercase, Layout<'a>)> { let mut fields_map = MutMap::default(); + match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) { Ok(()) | Err((_, Content::FlexVar(_))) => { - // collect into btreemap to sort - fields_map - .into_iter() - .map(|(label, var)| { - ( - label, - Layout::from_var(arena, var, subs, pointer_size) - .expect("invalid layout from var"), - ) - }) - .collect::>>() + // Sort the fields by label + let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), arena); + + for (label, var) in fields_map { + let layout = Layout::from_var(arena, var, subs, pointer_size) + .expect("invalid layout from var"); + + // Drop any zero-sized fields like {} + if layout.stack_size(pointer_size) != 0 { + sorted_fields.push((label, layout)); + } + } + + sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2)); + + sorted_fields } Err(other) => panic!("invalid content in record variable: {:?}", other), }