diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 666bfc7e47..fbfcfbc069 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -300,282 +300,6 @@ impl<'a> LambdaSet<'a> { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ClosureLayout<'a> { - /// the layout that this specific closure captures - /// uses a Vec instead of a MutMap because it's Hash - /// the vec is likely to be small, so linear search is fine - pub captured: &'a [(TagName, &'a [Layout<'a>])], - - /// use with care; there is some stuff happening here re. unwrapping - /// one-element records that might cause issues - pub layout: &'a Layout<'a>, -} - -impl<'a> ClosureLayout<'a> { - fn from_unit(arena: &'a Bump) -> Self { - let layout = Layout::PhantomEmptyStruct; - let layouts = arena.alloc(layout); - ClosureLayout { - captured: &[], - layout: layouts, - } - } - fn from_bool(arena: &'a Bump) -> Self { - let layout = Layout::Builtin(Builtin::Int1); - let layouts = arena.alloc(layout); - ClosureLayout { - captured: &[], - layout: layouts, - } - } - fn from_byte(arena: &'a Bump) -> Self { - let layout = Layout::Builtin(Builtin::Int8); - let layouts = arena.alloc(layout); - ClosureLayout { - captured: &[], - layout: layouts, - } - } - fn from_unwrapped(arena: &'a Bump, tag_name: TagName, layouts: &'a [Layout<'a>]) -> Self { - debug_assert!(!layouts.is_empty()); - - // DO NOT unwrap 1-element records here! - let layout = arena.alloc(Layout::Struct(layouts)); - - ClosureLayout { - captured: &*arena.alloc([(tag_name, &*arena.alloc([*layout]) as &[_])]), - layout, - } - } - - fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self { - debug_assert!(tags.len() > 1); - - // if the closed-over value is actually a layout, it should be wrapped in a 1-element record - debug_assert!(matches!(tags[0].0, TagName::Closure(_))); - - let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena); - - for (_, tag_args) in tags.iter() { - tag_arguments.push(&tag_args[0..]); - } - - ClosureLayout { - captured: tags, - layout: arena.alloc(Layout::Union(UnionLayout::NonRecursive( - tag_arguments.into_bump_slice(), - ))), - } - } - - pub fn get_wrapped(&self) -> crate::ir::Wrapped { - use crate::ir::Wrapped; - - match self.layout { - Layout::Struct(fields) if fields.len() == 1 => Wrapped::SingleElementRecord, - Layout::Struct(_) => Wrapped::RecordOrSingleTagUnion, - Layout::Union(_) => Wrapped::MultiTagUnion, - _ => Wrapped::SingleElementRecord, - } - } - - pub fn from_var( - arena: &'a Bump, - subs: &Subs, - closure_var: Variable, - ) -> Result, LayoutProblem> { - let mut tags = std::vec::Vec::new(); - match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) { - Ok(()) | Err((_, Content::FlexVar(_))) if !tags.is_empty() => { - // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help(arena, tags, None, subs); - - use UnionVariant::*; - match variant { - Never | Unit | UnitWithArguments => { - // the closure layout is zero-sized, but there is something in it (e.g. `{}`) - - let closure_layout = ClosureLayout::from_unit(arena); - Ok(Some(closure_layout)) - } - BoolUnion { .. } => { - let closure_layout = ClosureLayout::from_bool(arena); - - Ok(Some(closure_layout)) - } - ByteUnion(_) => { - let closure_layout = ClosureLayout::from_byte(arena); - - Ok(Some(closure_layout)) - } - Unwrapped(tag_name, layouts) => { - let closure_layout = ClosureLayout::from_unwrapped( - arena, - tag_name, - layouts.into_bump_slice(), - ); - - Ok(Some(closure_layout)) - } - Wrapped(variant) => { - use WrappedVariant::*; - - match variant { - NonRecursive { - sorted_tag_layouts: tags, - } => { - let closure_layout = - ClosureLayout::from_tag_union(arena, tags.into_bump_slice()); - Ok(Some(closure_layout)) - } - - _ => panic!("handle recursive layouts"), - } - } - } - } - - Ok(()) | Err((_, Content::FlexVar(_))) => { - // a max closure size of 0 means this is a standart top-level function - Ok(None) - } - _ => panic!("called ClosureLayout.from_var on invalid input"), - } - } - - pub fn extend_function_layout( - arena: &'a Bump, - argument_layouts: &'a [Layout<'a>], - closure_layout: Self, - ret_layout: &'a Layout<'a>, - ) -> Layout<'a> { - let closure_data_layout = match closure_layout.layout { - Layout::Struct(fields) if fields.len() == 1 => &fields[0], - other => other, - }; - - // define the function pointer - let function_ptr_layout = { - let mut temp = Vec::from_iter_in(argument_layouts.iter().cloned(), arena); - temp.push(*closure_data_layout); - Layout::FunctionPointer(temp.into_bump_slice(), ret_layout) - }; - - function_ptr_layout - } - - pub fn stack_size(&self, pointer_size: u32) -> u32 { - self.layout.stack_size(pointer_size) - } - pub fn contains_refcounted(&self) -> bool { - self.layout.contains_refcounted() - } - pub fn safe_to_memcpy(&self) -> bool { - self.layout.safe_to_memcpy() - } - - pub fn as_named_layout(&self, symbol: Symbol) -> Layout<'a> { - let layouts = if self.captured.is_empty() { - match self.layout { - Layout::Struct(fields) if fields.len() == 1 => fields[0], - other => *other, - } - } else if let Some((_, tag_args)) = self - .captured - .iter() - .find(|(tn, _)| *tn == TagName::Closure(symbol)) - { - if tag_args.len() == 1 { - tag_args[0] - } else { - Layout::Struct(tag_args) - } - } else { - unreachable!( - "invariant broken, TagName::Closure({:?}) is not in {:?}", - symbol, &self.captured - ); - }; - - layouts - } - - pub fn as_block_of_memory_layout(&self) -> Layout<'a> { - match self.layout { - Layout::Struct(fields) if fields.len() == 1 => fields[0], - other => *other, - } - } - - pub fn internal_layout(&self) -> Layout<'a> { - *self.layout - } - - pub fn build_closure_data( - &self, - original: Symbol, - symbols: &'a [Symbol], - ) -> BuildClosureData<'a> { - use crate::ir::Expr; - - match self.layout { - Layout::Struct(fields) if fields.len() == 1 => BuildClosureData::Alias(symbols[0]), - Layout::Struct(fields) => { - debug_assert!(fields.len() > 1); - debug_assert_eq!(fields.len(), symbols.len()); - - BuildClosureData::Struct(Expr::Struct(symbols)) - } - Layout::Union(UnionLayout::NonRecursive(tags)) => { - // NOTE it's very important that this Union consists of Closure tags - // and is not an unpacked 1-element record - - let tag_id = self - .captured - .iter() - .position(|(tn, _)| *tn == TagName::Closure(original)) - .unwrap() as _; - - BuildClosureData::Union { - tag_layout: Layout::Union(UnionLayout::NonRecursive(tags)), - tag_name: TagName::Closure(original), - tag_id, - union_size: tags.len() as u8, - } - } - Layout::PhantomEmptyStruct => { - debug_assert_eq!(symbols.len(), 1); - - BuildClosureData::Struct(Expr::Struct(&[])) - } - - _ => { - debug_assert_eq!( - symbols.len(), - 1, - "symbols {:?} for layout {:?}", - &symbols, - &self.layout - ); - - BuildClosureData::Alias(symbols[0]) - } - } - } -} - -pub enum BuildClosureData<'a> { - Alias(Symbol), - Struct(crate::ir::Expr<'a>), - Union { - tag_layout: Layout<'a>, - tag_name: TagName, - tag_id: u8, - union_size: u8, - }, -} - #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] pub enum MemoryMode { Unique,