diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index 6d5faa2bb1..1412da9573 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -717,6 +717,7 @@ pub fn constrain_expr( arguments, loc_body: boxed, captured_symbols, + name, .. } => { use roc_can::expr::Recursive; @@ -799,8 +800,7 @@ pub fn constrain_expr( )); } - let tag_name_string = format!("Closure_{}", closure_var.index()); - let tag_name = roc_module::ident::TagName::Global(tag_name_string.into()); + let tag_name = roc_module::ident::TagName::Closure(*name); let closure_type = Type::TagUnion( vec![(tag_name, tag_arguments)], Box::new(Type::Variable(closure_ext_var)), diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index c448094df2..3024a29fb7 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -1844,13 +1844,11 @@ pub fn build_proc_header<'a, 'ctx, 'env>( let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); - let mut arg_symbols = Vec::new_in(arena); - for (layout, arg_symbol) in args.iter() { + for (layout, _) in args.iter() { let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes); arg_basic_types.push(arg_type); - arg_symbols.push(arg_symbol); } let fn_type = get_fn_type(&ret_type, &arg_basic_types); @@ -1895,14 +1893,29 @@ pub fn build_proc<'a, 'ctx, 'env>( for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) { set_name(arg_val, arg_symbol.ident_string(&env.interns)); + // the closure argument (if any) comes in as an opaque sequence of bytes. + // we need to cast that to the specific closure data layout that the body expects + let value = if let Symbol::ARG_CLOSURE = *arg_symbol { + // blindly trust that there is a layout available for the closure data + let layout = proc.closure_data_layout.clone().unwrap(); + + // cast the input into the type that the body expects + let closure_data_type = + basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes); + + cast_basic_basic(env.builder, arg_val, closure_data_type) + } else { + arg_val + }; + let alloca = create_entry_block_alloca( env, fn_val, - arg_val.get_type(), + value.get_type(), arg_symbol.ident_string(&env.interns), ); - builder.build_store(alloca, arg_val); + builder.build_store(alloca, value); scope.insert(*arg_symbol, (layout.clone(), alloca)); } diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 900fb630a6..eccc8026f9 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -77,7 +77,7 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>( parent, layout_ids, field_ptr, - &closure_layout.as_layout(), + &closure_layout.as_block_of_memory_layout(), ) } } diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index e91d8ad0b6..19092d8b5c 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -936,8 +936,8 @@ mod gen_primitives { f main = - f = foo {} - f {} + g = foo {} + g {} "# ), 42, @@ -971,8 +971,9 @@ mod gen_primitives { } #[test] - #[ignore] fn specialize_closure() { + use roc_std::RocList; + assert_evals_to!( indoc!( r#" @@ -990,11 +991,12 @@ mod gen_primitives { main = items = foo {} - List.len items + # List.len items + List.map items (\f -> f {}) "# ), - 2, - i64 + RocList::from_slice(&[41, 42]), + RocList ); } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0d2cb82902..7609e4b4db 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -60,6 +60,7 @@ pub struct Proc<'a> { pub name: Symbol, pub args: &'a [(Layout<'a>, Symbol)], pub body: Stmt<'a>, + pub closure_data_layout: Option>, pub ret_layout: Layout<'a>, pub is_self_recursive: SelfRecursive, } @@ -542,7 +543,10 @@ impl<'a> Procs<'a> { .insert((symbol, layout.clone()), InProgress); match specialize(env, self, symbol, layout_cache, pending, partial_proc) { - Ok((proc, layout)) => { + Ok((proc, _ignore_layout)) => { + // the `layout` is a function pointer, while `_ignore_layout` can be a + // closure. We only specialize functions, storing this value with a closure + // layout will give trouble. self.specialized.insert((symbol, layout), Done(proc)); } Err(error) => { @@ -1444,7 +1448,7 @@ fn specialize_external<'a>( } } - let (proc_args, ret_layout) = + let (proc_args, opt_closure_layout, ret_layout) = build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?; // reset subs, so we don't get type errors when specializing for a different signature @@ -1457,10 +1461,16 @@ fn specialize_external<'a>( SelfRecursive::NotSelfRecursive }; + let closure_data_layout = match opt_closure_layout { + Some(closure_layout) => Some(closure_layout.as_named_layout(proc_name)), + None => None, + }; + let proc = Proc { name: proc_name, args: proc_args, body: specialized_body, + closure_data_layout, ret_layout, is_self_recursive: recursivity, }; @@ -1468,6 +1478,12 @@ fn specialize_external<'a>( Ok(proc) } +type SpecializedLayout<'a> = ( + &'a [(Layout<'a>, Symbol)], + Option>, + Layout<'a>, +); + #[allow(clippy::type_complexity)] fn build_specialized_proc_from_var<'a>( env: &mut Env<'a, '_>, @@ -1475,7 +1491,7 @@ fn build_specialized_proc_from_var<'a>( proc_name: Symbol, pattern_symbols: &[Symbol], fn_var: Variable, -) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> { +) -> Result, LayoutProblem> { match layout_cache.from_var(env.arena, fn_var, env.subs) { Ok(Layout::FunctionPointer(pattern_layouts, ret_layout)) => { let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena); @@ -1560,7 +1576,7 @@ fn build_specialized_proc_adapter<'a>( pattern_vars: &[Variable], opt_closure_layout: Option>, ret_var: Variable, -) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> { +) -> Result, LayoutProblem> { let mut arg_layouts = Vec::with_capacity_in(pattern_vars.len(), &env.arena); for arg_var in pattern_vars { @@ -1586,12 +1602,12 @@ fn build_specialized_proc_adapter<'a>( #[allow(clippy::type_complexity)] fn build_specialized_proc<'a>( arena: &'a Bump, - proc_name: Symbol, + _proc_name: Symbol, pattern_symbols: &[Symbol], pattern_layouts: Vec>, opt_closure_layout: Option>, ret_layout: Layout<'a>, -) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> { +) -> Result, LayoutProblem> { let mut proc_args = Vec::with_capacity_in(pattern_layouts.len(), arena); let pattern_layouts_len = pattern_layouts.len(); @@ -1621,13 +1637,17 @@ fn build_specialized_proc<'a>( Some(layout) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => { // here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`, // it stores the closure structure (just an integer in this case) - proc_args.push((layout.as_named_layout(proc_name), Symbol::ARG_CLOSURE)); + proc_args.push((layout.as_block_of_memory_layout(), Symbol::ARG_CLOSURE)); debug_assert_eq!( pattern_layouts_len + 1, pattern_symbols.len(), "Tried to zip two vecs with different lengths!" ); + + let proc_args = proc_args.into_bump_slice(); + + Ok((proc_args, Some(layout), ret_layout)) } Some(layout) => { // else if there is a closure layout, we're building the `f_closure` value @@ -1642,7 +1662,7 @@ fn build_specialized_proc<'a>( let closure_layout = Layout::Struct(arena.alloc([function_ptr_layout, closure_data_layout])); - return Ok((&[], closure_layout)); + return Ok((&[], None, closure_layout)); } None => { // else we're making a normal function, no closure problems to worry about @@ -1658,12 +1678,11 @@ fn build_specialized_proc<'a>( pattern_symbols.len(), "Tried to zip two vecs with different lengths!" ); + let proc_args = proc_args.into_bump_slice(); + + Ok((proc_args, None, ret_layout)) } } - - let proc_args = proc_args.into_bump_slice(); - - Ok((proc_args, ret_layout)) } fn specialize<'a>( @@ -1983,8 +2002,25 @@ pub fn with_hole<'a>( match hole { Stmt::Jump(id, _) => Stmt::Jump(*id, env.arena.alloc([symbol])), _ => { - // if you see this, there is variable aliasing going on - Stmt::Ret(symbol) + let result = Stmt::Ret(symbol); + let original = symbol; + + // we don't have a more accurate variable available, which means the variable + // from the partial_proc will be used. So far that has given the correct + // result, but I'm not sure this will continue to be the case in more complex + // examples. + let opt_fn_var = None; + + // if this is a function symbol, ensure that it's properly specialized! + reuse_function_symbol( + env, + procs, + layout_cache, + opt_fn_var, + symbol, + result, + original, + ) } } } @@ -2647,7 +2683,7 @@ pub fn with_hole<'a>( ) .unwrap(); - let closure_data_layout = closure_layout.as_layout(); + let closure_data_layout = closure_layout.as_block_of_memory_layout(); // define the function pointer let function_ptr_layout = { let mut temp = @@ -2807,7 +2843,8 @@ pub fn with_hole<'a>( let closure_symbol = function_symbol; // layout of the closure record - let closure_record_layout = closure_fields.as_layout(); + let closure_record_layout = + closure_fields.as_block_of_memory_layout(); let arg_symbols = { let mut temp = @@ -3106,183 +3143,28 @@ pub fn from_can<'a>( let function_layout = layout_cache.from_var(env.arena, function_type, env.subs); - if let Ok(Layout::Closure( - argument_layouts, - closure_fields, - ret_layout, - )) = &function_layout - { - let mut captured_symbols = - Vec::from_iter_in(captured_symbols, env.arena); - captured_symbols.sort(); - let captured_symbols = captured_symbols.into_bump_slice(); - - procs.insert_named( - env, - layout_cache, - *symbol, - function_type, - arguments, - loc_body, - CapturedSymbols::Captured(captured_symbols), - is_self_recursive, - return_type, - ); - - let closure_data_layout = closure_fields.as_layout(); - // define the function pointer - let function_ptr_layout = { - let mut temp = Vec::from_iter_in( - argument_layouts.iter().cloned(), - env.arena, - ); - temp.push(closure_data_layout.clone()); - Layout::FunctionPointer(temp.into_bump_slice(), ret_layout) + let captured_symbols = + if let Ok(Layout::Closure(_, _, _)) = &function_layout { + let mut temp = Vec::from_iter_in(captured_symbols, env.arena); + temp.sort(); + CapturedSymbols::Captured(temp.into_bump_slice()) + } else { + CapturedSymbols::None }; - let full_layout = function_ptr_layout.clone(); + procs.insert_named( + env, + layout_cache, + *symbol, + function_type, + arguments, + loc_body, + captured_symbols, + is_self_recursive, + return_type, + ); - let fn_var = function_type; - let proc_name = *symbol; - let pending = PendingSpecialization::from_var(env.subs, fn_var); - - // When requested (that is, when procs.pending_specializations is `Some`), - // store a pending specialization rather than specializing immediately. - // - // We do this so that we can do specialization in two passes: first, - // build the mono_expr with all the specialized calls in place (but - // no specializations performed yet), and then second, *after* - // de-duplicating requested specializations (since multiple modules - // which could be getting monomorphized in parallel might request - // the same specialization independently), we work through the - // queue of pending specializations to complete each specialization - // exactly once. - match &mut procs.pending_specializations { - Some(pending_specializations) => { - // register the pending specialization, so this gets code genned later - add_pending( - pending_specializations, - proc_name, - full_layout.clone(), - pending, - ); - } - None => { - let opt_partial_proc = procs.partial_procs.get(&proc_name); - - match opt_partial_proc { - None => panic!("invalid proc"), - Some(partial_proc) => { - // TODO should pending_procs hold a Rc to avoid this .clone()? - let partial_proc = partial_proc.clone(); - - // Mark this proc as in-progress, so if we're dealing with - // mutually recursive functions, we don't loop forever. - // (We had a bug around this before this system existed!) - procs.specialized.insert( - (proc_name, full_layout.clone()), - InProgress, - ); - - match specialize( - env, - procs, - proc_name, - layout_cache, - pending, - partial_proc, - ) { - Ok((proc, _layout)) => { - // TODO sometimes the full_layout is a - // function pointer, but the layout is a - // closure. Figure out how to handle this - // debug_assert_eq!(full_layout, layout); - // let function_layout = FunctionLayouts::from_layout(layout); - - procs.specialized.remove(&( - proc_name, - full_layout.clone(), - )); - - procs.specialized.insert( - ( - proc_name, - // function_layout.full.clone(), - full_layout.clone(), - ), - Done(proc), - ); - } - Err(error) => { - panic!("procedure is invalid {:?}", error); - } - } - } - } - } - } - - let mut stmt = from_can(env, cont.value, procs, layout_cache); - - let function_pointer = env.unique_symbol(); - let closure_data = env.unique_symbol(); - - // define the closure - let expr = - Expr::Struct(env.arena.alloc([function_pointer, closure_data])); - - stmt = Stmt::Let( - *symbol, - expr, - Layout::Struct(env.arena.alloc([ - function_ptr_layout.clone(), - closure_data_layout.clone(), - ])), - env.arena.alloc(stmt), - ); - - // define the closure data - - let symbols = Vec::from_iter_in( - captured_symbols.iter().map(|x| x.0), - env.arena, - ) - .into_bump_slice(); - let expr = Expr::Struct(symbols); - - stmt = Stmt::Let( - closure_data, - expr, - closure_data_layout.clone(), - env.arena.alloc(stmt), - ); - - let expr = - Expr::FunctionPointer(*symbol, function_ptr_layout.clone()); - - stmt = Stmt::Let( - function_pointer, - expr, - function_ptr_layout, - env.arena.alloc(stmt), - ); - - return stmt; - } else { - procs.insert_named( - env, - layout_cache, - *symbol, - function_type, - arguments, - loc_body, - CapturedSymbols::None, - is_self_recursive, - return_type, - ); - - return from_can(env, cont.value, procs, layout_cache); - } + return from_can(env, cont.value, procs, layout_cache); } _ => unreachable!(), } @@ -4225,6 +4107,133 @@ fn possible_reuse_symbol<'a>( } } +/// If the symbol is a function, make sure it is properly specialized +fn reuse_function_symbol<'a>( + env: &mut Env<'a, '_>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>, + arg_var: Option, + symbol: Symbol, + result: Stmt<'a>, + original: Symbol, +) -> Stmt<'a> { + match procs.partial_procs.get(&original) { + None => result, + Some(partial_proc) => { + let arg_var = arg_var.unwrap_or(partial_proc.annotation); + // this symbol is a function, that is used by-name (e.g. as an argument to another + // function). Register it with the current variable, then create a function pointer + // to it in the IR. + let layout = layout_cache + .from_var(env.arena, arg_var, env.subs) + .expect("creating layout does not fail"); + + // we have three kinds of functions really. Plain functions, closures by capture, + // and closures by unification. Here we record whether this function captures + // anything. + let captures = partial_proc.captured_symbols.captures(); + let captured = partial_proc.captured_symbols.clone(); + + match layout { + Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => { + // this is a closure by capture, meaning it itself captures local variables. + // we've defined the closure as a (function_ptr, closure_data) pair already + + let mut stmt = result; + + let function_pointer = env.unique_symbol(); + let closure_data = env.unique_symbol(); + + // let closure_data_layout = closure_layout.as_named_layout(original); + let closure_data_layout = closure_layout.as_block_of_memory_layout(); + + // define the function pointer + let function_ptr_layout = ClosureLayout::extend_function_layout( + env.arena, + argument_layouts, + closure_layout.clone(), + ret_layout, + ); + + procs.insert_passed_by_name( + env, + arg_var, + original, + function_ptr_layout.clone(), + layout_cache, + ); + + // define the closure + let expr = Expr::Struct(env.arena.alloc([function_pointer, closure_data])); + + stmt = Stmt::Let( + symbol, + expr, + Layout::Struct( + env.arena + .alloc([function_ptr_layout.clone(), closure_data_layout.clone()]), + ), + env.arena.alloc(stmt), + ); + + // define the closure data + + let symbols = match captured { + CapturedSymbols::Captured(captured_symbols) => { + Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena) + .into_bump_slice() + } + CapturedSymbols::None => unreachable!(), + }; + + // define the closure data, unless it's a basic unwrapped type already + match closure_layout.build_closure_data(original, symbols) { + Ok(expr) => { + stmt = Stmt::Let( + closure_data, + expr, + closure_data_layout.clone(), + env.arena.alloc(stmt), + ); + } + Err(current) => { + // there is only one symbol captured, use that immediately + substitute_in_exprs(env.arena, &mut stmt, closure_data, current); + } + } + + let expr = Expr::FunctionPointer(original, function_ptr_layout.clone()); + + stmt = Stmt::Let( + function_pointer, + expr, + function_ptr_layout, + env.arena.alloc(stmt), + ); + + stmt + } + _ => { + procs.insert_passed_by_name( + env, + arg_var, + original, + layout.clone(), + layout_cache, + ); + + Stmt::Let( + symbol, + Expr::FunctionPointer(original, layout.clone()), + layout, + env.arena.alloc(result), + ) + } + } + } + } +} + fn assign_to_symbol<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, @@ -4236,42 +4245,15 @@ fn assign_to_symbol<'a>( ) -> Stmt<'a> { // if this argument is already a symbol, we don't need to re-define it if let roc_can::expr::Expr::Var(original) = loc_arg.value { - match procs.partial_procs.get(&original) { - Some(partial_proc) => { - // this symbol is a function, that is used by-name (e.g. as an argument to another - // function). Register it with the current variable, then create a function pointer - // to it in the IR. - let layout = layout_cache - .from_var(env.arena, arg_var, env.subs) - .expect("creating layout does not fail"); - - // we have three kinds of functions really. Plain functions, closures by capture, - // and closures by unification. Here we record whether this function captures - // anything. - let captures = partial_proc.captured_symbols.captures(); - drop(partial_proc); - - procs.insert_passed_by_name(env, arg_var, original, layout.clone(), layout_cache); - - match layout { - Layout::Closure(_, _, _) if captures => { - // this is a closure by capture, meaning it itself captures local variables. - // we've defined the closure as a (function_ptr, closure_data) pair already - // replace `symbol` with `original` - let mut stmt = result; - substitute_in_exprs(env.arena, &mut stmt, symbol, original); - stmt - } - _ => Stmt::Let( - symbol, - Expr::FunctionPointer(original, layout.clone()), - layout, - env.arena.alloc(result), - ), - } - } - _ => result, - } + reuse_function_symbol( + env, + procs, + layout_cache, + Some(arg_var), + symbol, + result, + original, + ) } else { with_hole( env, diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 58087a7c00..19f3a80502 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -35,14 +35,13 @@ pub struct ClosureLayout<'a> { /// the layout that this specific closure captures captured: &'a [(TagName, &'a [Layout<'a>])], - /// the layout that represents the maximum size the closure layout can have - max_size: &'a [Layout<'a>], + max_size: &'a Layout<'a>, } impl<'a> ClosureLayout<'a> { fn from_bool(arena: &'a Bump) -> Self { let layout = Layout::Builtin(Builtin::Int1); - let layouts = arena.alloc([layout]); + let layouts = arena.alloc(layout); ClosureLayout { captured: &[], max_size: layouts, @@ -50,47 +49,55 @@ impl<'a> ClosureLayout<'a> { } fn from_byte(arena: &'a Bump) -> Self { let layout = Layout::Builtin(Builtin::Int8); - let layouts = arena.alloc([layout]); + let layouts = arena.alloc(layout); ClosureLayout { captured: &[], max_size: layouts, } } - fn from_unwrapped(layouts: &'a [Layout<'a>]) -> Self { + fn from_unwrapped(arena: &'a Bump, layouts: &'a [Layout<'a>]) -> Self { debug_assert!(!layouts.is_empty()); - ClosureLayout { - captured: &[], - max_size: layouts, + if layouts.len() == 1 { + ClosureLayout { + captured: &[], + max_size: &layouts[0], + } + } else { + ClosureLayout { + captured: &[], + max_size: arena.alloc(Layout::Struct(layouts)), + } } } - fn from_wrapped(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self { + fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self { debug_assert!(!tags.is_empty()); - // NOTE we fabricate a pointer size here. - // That's fine because we don't care about the exact size, just the biggest one - let pointer_size = 8; - - let mut largest_size = 0; - let mut max_size = &[] as &[_]; let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena); + let mut foobar = Vec::with_capacity_in(tags.len(), arena); for (name, tag_args_with_discr) in tags.iter() { - let tag_args = &tag_args_with_discr[1..]; - let size = tag_args.iter().map(|l| l.stack_size(pointer_size)).sum(); - - // >= because some of our layouts have 0 size, but are still valid layouts - if size >= largest_size { - largest_size = size; - max_size = tag_args; - } + // drop the discriminant, essentially creating an untagged union + let tag_args = &tag_args_with_discr[0..]; tag_arguments.push((name.clone(), tag_args)); + foobar.push(tag_args); } + let arguments = tag_arguments.into_bump_slice(); ClosureLayout { - captured: tag_arguments.into_bump_slice(), - max_size, + captured: arguments, + max_size: arena.alloc(Layout::Union(foobar.into_bump_slice())), + } + } + + pub fn get_wrapped(&self) -> crate::ir::Wrapped { + use crate::ir::Wrapped; + + match self.max_size { + Layout::Struct(_) => Wrapped::RecordOrSingleTagUnion, + Layout::Union(_) => Wrapped::MultiTagUnion, + _ => Wrapped::SingleElementRecord, } } @@ -123,14 +130,14 @@ impl<'a> ClosureLayout<'a> { } Unwrapped(layouts) => { let closure_layout = - ClosureLayout::from_unwrapped(layouts.into_bump_slice()); + ClosureLayout::from_unwrapped(arena, layouts.into_bump_slice()); Ok(Some(closure_layout)) } Wrapped(tags) => { // Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>), let closure_layout = - ClosureLayout::from_wrapped(arena, tags.into_bump_slice()); + ClosureLayout::from_tag_union(arena, tags.into_bump_slice()); Ok(Some(closure_layout)) } } @@ -150,7 +157,8 @@ impl<'a> ClosureLayout<'a> { closure_layout: Self, ret_layout: &'a Layout<'a>, ) -> Layout<'a> { - let closure_data_layout = closure_layout.as_layout(); + let closure_data_layout = closure_layout.max_size; + // define the function pointer let function_ptr_layout = { let mut temp = Vec::from_iter_in(argument_layouts.iter().cloned(), arena); @@ -162,35 +170,28 @@ impl<'a> ClosureLayout<'a> { } pub fn stack_size(&self, pointer_size: u32) -> u32 { - self.max_size - .iter() - .map(|l| l.stack_size(pointer_size)) - .sum() + self.max_size.stack_size(pointer_size) } pub fn contains_refcounted(&self) -> bool { - self.captured - .iter() - .map(|t| t.1) - .flatten() - .any(|l| l.contains_refcounted()) + self.max_size.contains_refcounted() } pub fn safe_to_memcpy(&self) -> bool { - self.captured - .iter() - .map(|t| t.1) - .flatten() - .all(|l| l.safe_to_memcpy()) + self.max_size.safe_to_memcpy() } pub fn as_named_layout(&self, symbol: Symbol) -> Layout<'a> { let layouts = if self.captured.is_empty() { - self.max_size + self.max_size.clone() } else if let Some((_, tag_args)) = self .captured .iter() .find(|(tn, _)| *tn == TagName::Closure(symbol)) { - tag_args + if tag_args.len() == 1 { + tag_args[0].clone() + } else { + Layout::Struct(tag_args) + } } else { unreachable!( "invariant broken, TagName::Closure({:?}) is not in {:?}", @@ -198,30 +199,38 @@ impl<'a> ClosureLayout<'a> { ); }; - if layouts.len() == 1 { - layouts[0].clone() - } else { - Layout::Struct(layouts) - } - } - - pub fn as_layout(&self) -> Layout<'a> { - if self.captured.is_empty() { - if self.max_size.len() == 1 { - self.max_size[0].clone() - } else { - Layout::Struct(self.max_size) - } - } else { - panic!(); - } + layouts } pub fn as_block_of_memory_layout(&self) -> Layout<'a> { - if self.max_size.len() == 1 { - self.max_size[0].clone() - } else { - Layout::Struct(self.max_size) + self.max_size.clone() + } + + pub fn build_closure_data( + &self, + original: Symbol, + symbols: &'a [Symbol], + ) -> Result, Symbol> { + use crate::ir::Expr; + + match self.max_size { + Layout::Struct(fields) if fields.len() > 1 => Ok(Expr::Struct(symbols)), + Layout::Union(tags) => { + let expr = Expr::Tag { + tag_layout: Layout::Union(tags), + tag_name: TagName::Closure(original), + tag_id: 0 as u8, + union_size: tags.len() as u8, + arguments: symbols, + }; + + Ok(expr) + } + _ => { + debug_assert_eq!(symbols.len(), 1); + + Err(symbols[0]) + } } } } @@ -503,13 +512,18 @@ impl<'a> LayoutCache<'a> { let result = Layout::from_var(&mut env, var); - let cached_layout = match &result { - Ok(layout) => Cached(layout.clone()), - Err(problem) => Problem(problem.clone()), - }; + // Don't actually cache. The layout cache is very hard to get right in the presence + // of specialization, it's turned of for now so an invalid cache is never the cause + // of a problem + if false { + let cached_layout = match &result { + Ok(layout) => Cached(layout.clone()), + Err(problem) => Problem(problem.clone()), + }; - self.layouts - .update_value(cached_var, |existing| existing.value = cached_layout); + self.layouts + .update_value(cached_var, |existing| existing.value = cached_layout); + } result }