diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index edd55534ad..e919d5eda1 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -356,19 +356,33 @@ pub fn listMap3( } } -pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList { +pub fn listKeepIf( + list: RocList, + caller: Caller1, + data: Opaque, + inc_n_data: IncN, + data_is_owned: bool, + alignment: usize, + element_width: usize, + inc: Inc, + dec: Dec, +) callconv(.C) RocList { if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; var output = RocList.allocate(std.heap.c_allocator, alignment, list.len(), list.len() * element_width); const target_ptr = output.bytes orelse unreachable; + if (data_is_owned) { + inc_n_data(data, size); + } + var kept: usize = 0; while (i < size) : (i += 1) { var keep = false; const element = source_ptr + (i * element_width); inc(element); - caller(transform, element, @ptrCast(?[*]u8, &keep)); + caller(data, element, @ptrCast(?[*]u8, &keep)); if (keep) { @memcpy(target_ptr + (kept * element_width), element, element_width); @@ -379,9 +393,6 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: } } - // consume the input list - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width); - if (kept == 0) { // if the output is empty, deallocate the space we made for the result utils.decref(std.heap.c_allocator, alignment, output.bytes, size * element_width); @@ -396,15 +407,73 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: } } -pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList { - return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result); +pub fn listKeepOks( + list: RocList, + caller: Caller1, + data: Opaque, + inc_n_data: IncN, + data_is_owned: bool, + alignment: usize, + before_width: usize, + result_width: usize, + after_width: usize, + dec_result: Dec, +) callconv(.C) RocList { + return listKeepResult( + list, + RocResult.isOk, + caller, + data, + inc_n_data, + data_is_owned, + alignment, + before_width, + result_width, + after_width, + dec_result, + ); } -pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList { - return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result); +pub fn listKeepErrs( + list: RocList, + caller: Caller1, + data: Opaque, + inc_n_data: IncN, + data_is_owned: bool, + alignment: usize, + before_width: usize, + result_width: usize, + after_width: usize, + dec_result: Dec, +) callconv(.C) RocList { + return listKeepResult( + list, + RocResult.isErr, + caller, + data, + inc_n_data, + data_is_owned, + alignment, + before_width, + result_width, + after_width, + dec_result, + ); } -pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) RocList { +pub fn listKeepResult( + list: RocList, + is_good_constructor: fn (RocResult) bool, + caller: Caller1, + data: Opaque, + inc_n_data: IncN, + data_is_owned: bool, + alignment: usize, + before_width: usize, + result_width: usize, + after_width: usize, + dec_result: Dec, +) RocList { if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; @@ -413,11 +482,14 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t var temporary = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, result_width) catch unreachable); + if (data_is_owned) { + inc_n_data(data, size); + } + var kept: usize = 0; while (i < size) : (i += 1) { const before_element = source_ptr + (i * before_width); - inc_closure(transform); - caller(transform, before_element, temporary); + caller(data, before_element, temporary); const result = utils.RocResult{ .bytes = temporary }; @@ -430,7 +502,6 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t } } - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * before_width); std.heap.c_allocator.free(temporary[0..result_width]); if (kept == 0) { @@ -746,13 +817,25 @@ fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_w } } -pub fn listSortWith(input: RocList, transform: Opaque, wrapper: CompareFn, alignment: usize, element_width: usize) callconv(.C) RocList { +pub fn listSortWith( + input: RocList, + caller: CompareFn, + data: Opaque, + inc_n_data: IncN, + data_is_owned: bool, + alignment: usize, + element_width: usize, +) callconv(.C) RocList { var list = input.makeUnique(std.heap.c_allocator, alignment, element_width); + if (data_is_owned) { + inc_n_data(data, list.len()); + } + if (list.bytes) |source_ptr| { const low = 0; const high: isize = @intCast(isize, list.len()) - 1; - quicksort(source_ptr, transform, wrapper, element_width, low, high); + quicksort(source_ptr, data, caller, element_width, low, high); } return list; diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index b2076ba262..3235f28585 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -2305,7 +2305,32 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( env, value.into_struct_value(), ); + + // because of how we insert DECREF for lists, we can't guarantee that + // the list is non-empty. When the list is empty, the pointer to the + // elements is NULL, and trying to get to the RC address will + // underflow, causing a segfault. Therefore, in this case we must + // manually check that the list is non-empty + let not_empty = env.context.append_basic_block(parent, "not_null"); + let done = env.context.append_basic_block(parent, "done"); + + let length = list_len(env.builder, value.into_struct_value()); + let is_empty = env.builder.build_int_compare( + IntPredicate::EQ, + length, + length.get_type().const_zero(), + "", + ); + + env.builder + .build_conditional_branch(is_empty, done, not_empty); + + env.builder.position_at_end(not_empty); + refcount_ptr.decrement(env, layout); + + env.builder.build_unconditional_branch(done); + env.builder.position_at_end(done); } _ if layout.is_refcounted() => { @@ -3680,16 +3705,24 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => default, Layout::Builtin(Builtin::List(_, element_layout)) => { + let argument_layouts = &[**element_layout, *default_layout]; + + let roc_function_call = roc_function_call( + env, + layout_ids, + function, + closure, + *closure_layout, + function_owns_closure_data, + argument_layouts, + ); + crate::llvm::build_list::list_walk_generic( env, layout_ids, - parent, + roc_function_call, list, element_layout, - function, - function_layout, - closure, - *closure_layout, default, default_layout, $variant, @@ -3862,15 +3895,21 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => list_keep_if( - env, - layout_ids, - function, - closure, - *closure_layout, - list, - element_layout, - ), + Layout::Builtin(Builtin::List(_, element_layout)) => { + let argument_layouts = &[**element_layout]; + + let roc_function_call = roc_function_call( + env, + layout_ids, + function, + closure, + *closure_layout, + function_owns_closure_data, + argument_layouts, + ); + + list_keep_if(env, layout_ids, roc_function_call, list, element_layout) + } _ => unreachable!("invalid list layout"), } } @@ -3890,17 +3929,29 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( ( Layout::Builtin(Builtin::List(_, before_layout)), Layout::Builtin(Builtin::List(_, after_layout)), - ) => list_keep_oks( - env, - layout_ids, - function, - function_layout, - closure, - *closure_layout, - list, - before_layout, - after_layout, - ), + ) => { + let argument_layouts = &[**before_layout]; + + let roc_function_call = roc_function_call( + env, + layout_ids, + function, + closure, + *closure_layout, + function_owns_closure_data, + argument_layouts, + ); + + list_keep_oks( + env, + layout_ids, + roc_function_call, + &function_layout, + list, + before_layout, + after_layout, + ) + } (other1, other2) => { unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) } @@ -3922,17 +3973,29 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( ( Layout::Builtin(Builtin::List(_, before_layout)), Layout::Builtin(Builtin::List(_, after_layout)), - ) => list_keep_errs( - env, - layout_ids, - function, - function_layout, - closure, - *closure_layout, - list, - before_layout, - after_layout, - ), + ) => { + let argument_layouts = &[**before_layout]; + + let roc_function_call = roc_function_call( + env, + layout_ids, + function, + closure, + *closure_layout, + function_owns_closure_data, + argument_layouts, + ); + + list_keep_errs( + env, + layout_ids, + roc_function_call, + &function_layout, + list, + before_layout, + after_layout, + ) + } (other1, other2) => { unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) } @@ -3959,14 +4022,34 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => list_sort_with( - env, - function, - closure, - *closure_layout, - list, - element_layout, - ), + Layout::Builtin(Builtin::List(_, element_layout)) => { + use crate::llvm::bitcode::build_compare_wrapper; + + let argument_layouts = &[**element_layout, **element_layout]; + + let compare_wrapper = + build_compare_wrapper(env, function, *closure_layout, element_layout) + .as_global_value() + .as_pointer_value(); + + let roc_function_call = roc_function_call( + env, + layout_ids, + function, + closure, + *closure_layout, + function_owns_closure_data, + argument_layouts, + ); + + list_sort_with( + env, + roc_function_call, + compare_wrapper, + list, + element_layout, + ) + } _ => unreachable!("invalid list layout"), } } diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index bc6cf6f71b..11ad45a8f7 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -1,7 +1,7 @@ #![allow(clippy::too_many_arguments)] use crate::llvm::bitcode::{ - build_compare_wrapper, build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, - build_inc_wrapper, build_transform_caller_new, call_bitcode_fn, call_void_bitcode_fn, + build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, build_inc_wrapper, + build_transform_caller_new, call_bitcode_fn, call_void_bitcode_fn, }; use crate::llvm::build::{ allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, InPlace, RocFunctionCall, @@ -444,56 +444,12 @@ pub enum ListWalk { WalkBackwardsUntil, } -pub fn list_walk_help<'a, 'ctx, 'env>( - env: &'ctx Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - scope: &crate::llvm::build::Scope<'a, 'ctx>, - parent: FunctionValue<'ctx>, - args: &[roc_module::symbol::Symbol], - function: FunctionValue<'a>, - function_layout: Layout<'a>, - variant: ListWalk, -) -> BasicValueEnum<'ctx> { - use crate::llvm::build::load_symbol_and_layout; - - debug_assert_eq!(args.len(), 4); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => default, - Layout::Builtin(Builtin::List(_, element_layout)) => list_walk_generic( - env, - layout_ids, - parent, - list, - element_layout, - function, - function_layout, - closure, - *closure_layout, - default, - default_layout, - variant, - ), - _ => unreachable!("invalid list layout"), - } -} - pub fn list_walk_generic<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - _parent: FunctionValue<'ctx>, + roc_function_call: RocFunctionCall<'ctx>, list: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, - transform: FunctionValue<'ctx>, - _transform_layout: Layout<'a>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, default: BasicValueEnum<'ctx>, default_layout: &Layout<'a>, variant: ListWalk, @@ -507,21 +463,9 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( ListWalk::WalkBackwardsUntil => todo!(), }; - let closure_data_ptr = builder.build_alloca(closure_data.get_type(), "closure_data_ptr"); - env.builder.build_store(closure_data_ptr, closure_data); - let default_ptr = builder.build_alloca(default.get_type(), "default_ptr"); env.builder.build_store(default_ptr, default); - let stepper_caller = build_transform_caller_new( - env, - transform, - closure_data_layout, - &[*element_layout, *default_layout], - ) - .as_global_value() - .as_pointer_value(); - let result_ptr = env.builder.build_alloca(default.get_type(), "result"); match variant { @@ -530,8 +474,10 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( env, &[ pass_list_as_i128(env, list), - pass_as_opaque(env, closure_data_ptr), - stepper_caller.into(), + roc_function_call.caller.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), pass_as_opaque(env, default_ptr), alignment_intvalue(env, &element_layout), layout_width(env, element_layout), @@ -547,8 +493,10 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( env, &[ pass_list_as_i128(env, list), - pass_as_opaque(env, closure_data_ptr), - stepper_caller.into(), + roc_function_call.caller.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), pass_as_opaque(env, default_ptr), alignment_intvalue(env, &element_layout), layout_width(env, element_layout), @@ -657,22 +605,10 @@ pub fn list_contains<'a, 'ctx, 'env>( pub fn list_keep_if<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - transform: FunctionValue<'ctx>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, + roc_function_call: RocFunctionCall<'ctx>, list: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - let builder = env.builder; - - let closure_data_ptr = builder.build_alloca(closure_data.get_type(), "closure_data_ptr"); - env.builder.build_store(closure_data_ptr, closure_data); - - let stepper_caller = - build_transform_caller_new(env, transform, closure_data_layout, &[*element_layout]) - .as_global_value() - .as_pointer_value(); - let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout); let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); @@ -680,8 +616,10 @@ pub fn list_keep_if<'a, 'ctx, 'env>( env, &[ pass_list_as_i128(env, list), - pass_as_opaque(env, closure_data_ptr), - stepper_caller.into(), + roc_function_call.caller.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), alignment_intvalue(env, &element_layout), layout_width(env, element_layout), inc_element_fn.as_global_value().as_pointer_value().into(), @@ -695,24 +633,35 @@ pub fn list_keep_if<'a, 'ctx, 'env>( pub fn list_keep_oks<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - transform: FunctionValue<'ctx>, - transform_layout: Layout<'a>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, + roc_function_call: RocFunctionCall<'ctx>, + function_layout: &Layout<'a>, list: BasicValueEnum<'ctx>, before_layout: &Layout<'a>, after_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - list_keep_result( + // Layout of the `Result after *` + let result_layout = match function_layout { + Layout::FunctionPointer(_, ret) => ret, + Layout::Closure(_, _, ret) => ret, + _ => unreachable!("not a callable layout"), + }; + + let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); + + call_bitcode_fn( env, - layout_ids, - transform, - transform_layout, - closure_data, - closure_data_layout, - list, - before_layout, - after_layout, + &[ + pass_list_as_i128(env, list), + roc_function_call.caller.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), + alignment_intvalue(env, &before_layout), + layout_width(env, before_layout), + layout_width(env, result_layout), + layout_width(env, after_layout), + dec_result_fn.as_global_value().as_pointer_value().into(), + ], bitcode::LIST_KEEP_OKS, ) } @@ -721,24 +670,35 @@ pub fn list_keep_oks<'a, 'ctx, 'env>( pub fn list_keep_errs<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - transform: FunctionValue<'ctx>, - transform_layout: Layout<'a>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, + roc_function_call: RocFunctionCall<'ctx>, + function_layout: &Layout<'a>, list: BasicValueEnum<'ctx>, before_layout: &Layout<'a>, after_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - list_keep_result( + // Layout of the `Result after *` + let result_layout = match function_layout { + Layout::FunctionPointer(_, ret) => ret, + Layout::Closure(_, _, ret) => ret, + _ => unreachable!("not a callable layout"), + }; + + let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); + + call_bitcode_fn( env, - layout_ids, - transform, - transform_layout, - closure_data, - closure_data_layout, - list, - before_layout, - after_layout, + &[ + pass_list_as_i128(env, list), + roc_function_call.caller.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), + alignment_intvalue(env, &before_layout), + layout_width(env, before_layout), + layout_width(env, result_layout), + layout_width(env, after_layout), + dec_result_fn.as_global_value().as_pointer_value().into(), + ], bitcode::LIST_KEEP_ERRS, ) } @@ -771,18 +731,6 @@ pub fn list_keep_result<'a, 'ctx, 'env>( .as_global_value() .as_pointer_value(); - let before_width = env - .ptr_int() - .const_int(before_layout.stack_size(env.ptr_bytes) as u64, false); - - let after_width = env - .ptr_int() - .const_int(after_layout.stack_size(env.ptr_bytes) as u64, false); - - let result_width = env - .ptr_int() - .const_int(result_layout.stack_size(env.ptr_bytes) as u64, false); - let inc_closure = build_inc_wrapper(env, layout_ids, &transform_layout); let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); @@ -793,9 +741,9 @@ pub fn list_keep_result<'a, 'ctx, 'env>( pass_as_opaque(env, closure_data_ptr), stepper_caller.into(), alignment_intvalue(env, &before_layout), - before_width.into(), - result_width.into(), - after_width.into(), + layout_width(env, before_layout), + layout_width(env, after_layout), + layout_width(env, result_layout), inc_closure.as_global_value().as_pointer_value().into(), dec_result_fn.as_global_value().as_pointer_value().into(), ], @@ -806,28 +754,19 @@ pub fn list_keep_result<'a, 'ctx, 'env>( /// List.sortWith : List a, (a, a -> Ordering) -> List a pub fn list_sort_with<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - transform: FunctionValue<'ctx>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, + roc_function_call: RocFunctionCall<'ctx>, + compare_wrapper: PointerValue<'ctx>, list: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - let compare_wrapper = - build_compare_wrapper(env, transform, closure_data_layout, element_layout) - .as_global_value() - .as_pointer_value(); - - let closure_data_ptr = env - .builder - .build_alloca(closure_data.get_type(), "closure_data_ptr"); - env.builder.build_store(closure_data_ptr, closure_data); - call_bitcode_fn_returns_list( env, &[ pass_list_as_i128(env, list), - pass_as_opaque(env, closure_data_ptr), compare_wrapper.into(), + pass_as_opaque(env, roc_function_call.data), + roc_function_call.inc_n_data.into(), + roc_function_call.data_is_owned.into(), alignment_intvalue(env, &element_layout), layout_width(env, element_layout), ], diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 9704fb6fd4..8871f3ff6f 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -425,7 +425,7 @@ impl<'a> BorrowInfState<'a> { debug_assert!(op.is_higher_order()); match op { - ListMap | ListSortWith => { + ListMap | ListKeepIf | ListKeepOks | ListKeepErrs => { match self.param_map.get_symbol(arguments[1], *closure_layout) { Some(function_ps) => { // own the list if the function wants to own the element @@ -481,7 +481,6 @@ impl<'a> BorrowInfState<'a> { if !function_ps[0].borrow { self.own_var(arguments[0]); } - if !function_ps[1].borrow { self.own_var(arguments[1]); } @@ -496,6 +495,41 @@ impl<'a> BorrowInfState<'a> { } None => unreachable!(), }, + ListSortWith => { + match self.param_map.get_symbol(arguments[1], *closure_layout) { + Some(function_ps) => { + // always own the input list + self.own_var(arguments[0]); + + // own the closure environment if the function needs to own it + if let Some(false) = function_ps.get(2).map(|p| p.borrow) { + self.own_var(arguments[2]); + } + } + None => unreachable!(), + } + } + ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => { + match self.param_map.get_symbol(arguments[2], *closure_layout) { + Some(function_ps) => { + // own the data structure if the function wants to own the element + if !function_ps[0].borrow { + self.own_var(arguments[0]); + } + + // own the default value if the function wants to own it + if !function_ps[1].borrow { + self.own_var(arguments[1]); + } + + // own the closure environment if the function needs to own it + if let Some(false) = function_ps.get(2).map(|p| p.borrow) { + self.own_var(arguments[3]); + } + } + None => unreachable!(), + } + } _ => { // very unsure what demand RunLowLevel should place upon its arguments self.own_var(z); diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index a2f8cc4b02..d55e69254c 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -490,7 +490,10 @@ impl<'a> Context<'a> { const CLOSURE_DATA: bool = BORROWED; match op { - roc_module::low_level::LowLevel::ListMap => { + roc_module::low_level::LowLevel::ListMap + | roc_module::low_level::LowLevel::ListKeepIf + | roc_module::low_level::LowLevel::ListKeepOks + | roc_module::low_level::LowLevel::ListKeepErrs => { match self.param_map.get_symbol(arguments[1], *closure_layout) { Some(function_ps) => { let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA]; @@ -589,6 +592,56 @@ impl<'a> Context<'a> { None => unreachable!(), } } + roc_module::low_level::LowLevel::ListSortWith => { + match self.param_map.get_symbol(arguments[1], *closure_layout) { + Some(function_ps) => { + let borrows = [OWNED, FUNCTION, CLOSURE_DATA]; + + let b = self.add_dec_after_lowlevel( + arguments, + &borrows, + b, + b_live_vars, + ); + + let v = create_call!(function_ps.get(2)); + + &*self.arena.alloc(Stmt::Let(z, v, l, b)) + } + None => unreachable!(), + } + } + roc_module::low_level::LowLevel::ListWalk + | roc_module::low_level::LowLevel::ListWalkUntil + | roc_module::low_level::LowLevel::ListWalkBackwards + | roc_module::low_level::LowLevel::DictWalk => { + match self.param_map.get_symbol(arguments[2], *closure_layout) { + Some(function_ps) => { + // borrow data structure based on first argument of the folded function + // borrow the default based on second argument of the folded function + let borrows = [ + function_ps[0].borrow, + function_ps[1].borrow, + FUNCTION, + CLOSURE_DATA, + ]; + + let b = self.add_dec_after_lowlevel( + arguments, + &borrows, + b, + b_live_vars, + ); + + let b = decref_if_owned!(function_ps[0].borrow, arguments[0], b); + + let v = create_call!(function_ps.get(2)); + + &*self.arena.alloc(Stmt::Let(z, v, l, b)) + } + None => unreachable!(), + } + } _ => { let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);