diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 7dd9a2d1cb..1bede26857 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -881,17 +881,23 @@ pub fn build_exp_call<'a, 'ctx, 'env>( .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) } - CallType::LowLevel { + CallType::LowLevel { op } => { + run_low_level(env, layout_ids, scope, parent, layout, *op, arguments) + } + + CallType::HigherOrderLowLevel { op, - opt_closure_layout, - } => run_low_level( + closure_layout, + function_owns_closure_data, + } => run_higher_order_low_level( env, layout_ids, scope, parent, layout, *op, - *opt_closure_layout, + *closure_layout, + *function_owns_closure_data, arguments, ), @@ -2157,6 +2163,10 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( CallType::LowLevel { .. } => { unreachable!("lowlevel itself never throws exceptions") } + + CallType::HigherOrderLowLevel { .. } => { + unreachable!("lowlevel itself never throws exceptions") + } }, Rethrow => { @@ -3577,22 +3587,24 @@ pub static FAST_CALL_CONV: u32 = 8; pub static COLD_CALL_CONV: u32 = 9; #[allow(clippy::too_many_arguments)] -fn run_low_level<'a, 'ctx, 'env>( +fn run_higher_order_low_level<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, scope: &Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, layout: &Layout<'a>, op: LowLevel, - opt_closure_layout: Option>, + function_layout: Layout<'a>, + function_owns_closure_data: bool, args: &[Symbol], ) -> BasicValueEnum<'ctx> { use LowLevel::*; + debug_assert!(op.is_higher_order()); + // macros because functions cause lifetime issues related to the `env` or `layout_ids` macro_rules! passed_function_at_index { ($index:expr) => {{ - let function_layout = opt_closure_layout.unwrap(); let function_symbol = args[$index]; let fn_name = layout_ids @@ -3616,7 +3628,6 @@ fn run_low_level<'a, 'ctx, 'env>( let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); - let function_layout = opt_closure_layout.unwrap(); let function = passed_function_at_index!(2); let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); @@ -3643,6 +3654,285 @@ fn run_low_level<'a, 'ctx, 'env>( } }}; } + match op { + ListMap => { + // List.map : List before, (before -> after) -> List after + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + match list_layout { + Layout::Builtin(Builtin::EmptyList) => empty_list(env), + Layout::Builtin(Builtin::List(_, element_layout)) => list_map( + env, + function, + function_layout, + closure, + *closure_layout, + list, + element_layout, + ), + _ => unreachable!("invalid list layout"), + } + } + ListMap2 => { + debug_assert_eq!(args.len(), 4); + + let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]); + let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); + + let function = passed_function_at_index!(2); + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); + + match (list1_layout, list2_layout) { + ( + Layout::Builtin(Builtin::List(_, element1_layout)), + Layout::Builtin(Builtin::List(_, element2_layout)), + ) => list_map2( + env, + layout_ids, + function, + function_layout, + closure, + *closure_layout, + list1, + list2, + element1_layout, + element2_layout, + ), + (Layout::Builtin(Builtin::EmptyList), _) + | (_, Layout::Builtin(Builtin::EmptyList)) => empty_list(env), + _ => unreachable!("invalid list layout"), + } + } + ListMap3 => { + debug_assert_eq!(args.len(), 5); + + let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]); + let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); + let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]); + + let function = passed_function_at_index!(3); + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[4]); + + match (list1_layout, list2_layout, list3_layout) { + ( + Layout::Builtin(Builtin::List(_, element1_layout)), + Layout::Builtin(Builtin::List(_, element2_layout)), + Layout::Builtin(Builtin::List(_, element3_layout)), + ) => list_map3( + env, + layout_ids, + function, + function_layout, + closure, + *closure_layout, + list1, + list2, + list3, + element1_layout, + element2_layout, + element3_layout, + ), + (Layout::Builtin(Builtin::EmptyList), _, _) + | (_, Layout::Builtin(Builtin::EmptyList), _) + | (_, _, Layout::Builtin(Builtin::EmptyList)) => empty_list(env), + _ => unreachable!("invalid list layout"), + } + } + ListMapWithIndex => { + // List.mapWithIndex : List before, (Nat, before -> after) -> List after + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + match list_layout { + Layout::Builtin(Builtin::EmptyList) => empty_list(env), + Layout::Builtin(Builtin::List(_, element_layout)) => list_map_with_index( + env, + function, + function_layout, + closure, + *closure_layout, + list, + element_layout, + ), + _ => unreachable!("invalid list layout"), + } + } + ListKeepIf => { + // List.keepIf : List elem, (elem -> Bool) -> List elem + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + 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, + ), + _ => unreachable!("invalid list layout"), + } + } + ListKeepOks => { + // List.keepOks : List before, (before -> Result after *) -> List after + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + match (list_layout, layout) { + (_, Layout::Builtin(Builtin::EmptyList)) + | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(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, + ), + (other1, other2) => { + unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) + } + } + } + ListKeepErrs => { + // List.keepErrs : List before, (before -> Result * after) -> List after + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + match (list_layout, layout) { + (_, Layout::Builtin(Builtin::EmptyList)) + | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(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, + ), + (other1, other2) => { + unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) + } + } + } + ListWalk => { + list_walk!(crate::llvm::build_list::ListWalk::Walk) + } + ListWalkUntil => { + list_walk!(crate::llvm::build_list::ListWalk::WalkUntil) + } + ListWalkBackwards => { + list_walk!(crate::llvm::build_list::ListWalk::WalkBackwards) + } + ListSortWith => { + // List.sortWith : List a, (a, a -> Ordering) -> List a + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let function = passed_function_at_index!(1); + + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); + + 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, + ), + _ => unreachable!("invalid list layout"), + } + } + DictWalk => { + debug_assert_eq!(args.len(), 4); + + let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]); + let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); + let function = passed_function_at_index!(2); + let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); + + match dict_layout { + Layout::Builtin(Builtin::EmptyDict) => { + // no elements, so `key` is not in here + panic!("key type unknown") + } + Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => dict_walk( + env, + layout_ids, + dict, + function, + closure, + *closure_layout, + default, + key_layout, + value_layout, + default_layout, + ), + _ => unreachable!("invalid dict layout"), + } + } + _ => unreachable!(), + } +} + +#[allow(clippy::too_many_arguments)] +fn run_low_level<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + scope: &Scope<'a, 'ctx>, + parent: FunctionValue<'ctx>, + layout: &Layout<'a>, + op: LowLevel, + args: &[Symbol], +) -> BasicValueEnum<'ctx> { + use LowLevel::*; + + debug_assert!(!op.is_higher_order()); match op { StrConcat => { @@ -3784,213 +4074,6 @@ fn run_low_level<'a, 'ctx, 'env>( list_concat(env, inplace, parent, first_list, second_list, list_layout) } - ListMap => { - // List.map : List before, (before -> after) -> List after - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => list_map( - env, - function, - function_layout, - closure, - *closure_layout, - list, - element_layout, - ), - _ => unreachable!("invalid list layout"), - } - } - ListMap2 => { - debug_assert_eq!(args.len(), 4); - - let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]); - let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(2); - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); - - match (list1_layout, list2_layout) { - ( - Layout::Builtin(Builtin::List(_, element1_layout)), - Layout::Builtin(Builtin::List(_, element2_layout)), - ) => list_map2( - env, - layout_ids, - function, - function_layout, - closure, - *closure_layout, - list1, - list2, - element1_layout, - element2_layout, - ), - (Layout::Builtin(Builtin::EmptyList), _) - | (_, Layout::Builtin(Builtin::EmptyList)) => empty_list(env), - _ => unreachable!("invalid list layout"), - } - } - ListMap3 => { - debug_assert_eq!(args.len(), 5); - - let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]); - let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); - let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(3); - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[4]); - - match (list1_layout, list2_layout, list3_layout) { - ( - Layout::Builtin(Builtin::List(_, element1_layout)), - Layout::Builtin(Builtin::List(_, element2_layout)), - Layout::Builtin(Builtin::List(_, element3_layout)), - ) => list_map3( - env, - layout_ids, - function, - function_layout, - closure, - *closure_layout, - list1, - list2, - list3, - element1_layout, - element2_layout, - element3_layout, - ), - (Layout::Builtin(Builtin::EmptyList), _, _) - | (_, Layout::Builtin(Builtin::EmptyList), _) - | (_, _, Layout::Builtin(Builtin::EmptyList)) => empty_list(env), - _ => unreachable!("invalid list layout"), - } - } - ListMapWithIndex => { - // List.mapWithIndex : List before, (Nat, before -> after) -> List after - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => list_map_with_index( - env, - function, - function_layout, - closure, - *closure_layout, - list, - element_layout, - ), - _ => unreachable!("invalid list layout"), - } - } - ListKeepIf => { - // List.keepIf : List elem, (elem -> Bool) -> List elem - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - 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, - ), - _ => unreachable!("invalid list layout"), - } - } - ListKeepOks => { - // List.keepOks : List before, (before -> Result after *) -> List after - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - match (list_layout, layout) { - (_, Layout::Builtin(Builtin::EmptyList)) - | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(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, - ), - (other1, other2) => { - unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) - } - } - } - ListKeepErrs => { - // List.keepErrs : List before, (before -> Result * after) -> List after - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function_layout = opt_closure_layout.unwrap(); - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - match (list_layout, layout) { - (_, Layout::Builtin(Builtin::EmptyList)) - | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(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, - ), - (other1, other2) => { - unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2) - } - } - } ListContains => { // List.contains : List elem, elem -> Bool debug_assert_eq!(args.len(), 2); @@ -4015,15 +4098,6 @@ fn run_low_level<'a, 'ctx, 'env>( list_range(env, *builtin, low.into_int_value(), high.into_int_value()) } - ListWalk => { - list_walk!(crate::llvm::build_list::ListWalk::Walk) - } - ListWalkUntil => { - list_walk!(crate::llvm::build_list::ListWalk::WalkUntil) - } - ListWalkBackwards => { - list_walk!(crate::llvm::build_list::ListWalk::WalkBackwards) - } ListAppend => { // List.append : List elem, elem -> List elem debug_assert_eq!(args.len(), 2); @@ -4056,29 +4130,6 @@ fn run_low_level<'a, 'ctx, 'env>( list_join(env, inplace, parent, list, outer_list_layout) } - ListSortWith => { - // List.sortWith : List a, (a, a -> Ordering) -> List a - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let function = passed_function_at_index!(1); - - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); - - 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, - ), - _ => unreachable!("invalid list layout"), - } - } NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos | NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => { debug_assert_eq!(args.len(), 1); @@ -4512,34 +4563,6 @@ fn run_low_level<'a, 'ctx, 'env>( _ => unreachable!("invalid dict layout"), } } - DictWalk => { - debug_assert_eq!(args.len(), 4); - - let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]); - let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); - let function = passed_function_at_index!(2); - let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); - - match dict_layout { - Layout::Builtin(Builtin::EmptyDict) => { - // no elements, so `key` is not in here - panic!("key type unknown") - } - Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => dict_walk( - env, - layout_ids, - dict, - function, - closure, - *closure_layout, - default, - key_layout, - value_layout, - default_layout, - ), - _ => unreachable!("invalid dict layout"), - } - } SetFromList => { debug_assert_eq!(args.len(), 1); @@ -4581,6 +4604,10 @@ fn run_low_level<'a, 'ctx, 'env>( cond } + + ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk + | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith + | DictWalk => unreachable!("these are higher order, and are handled elsewhere"), } } diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index 7b2cd79057..36255ffc2a 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -211,10 +211,9 @@ where } } - CallType::LowLevel { - op: lowlevel, - opt_closure_layout: _, - } => self.build_run_low_level(sym, lowlevel, arguments, layout), + CallType::LowLevel { op: lowlevel } => { + self.build_run_low_level(sym, lowlevel, arguments, layout) + } x => Err(format!("the call type, {:?}, is not yet implemented", x)), } } @@ -524,6 +523,7 @@ where self.set_last_seen(*sym, stmt); } CallType::LowLevel { .. } => {} + CallType::HigherOrderLowLevel { .. } => {} CallType::Foreign { .. } => {} } } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index bd31c5031d..517c8bd01b 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -102,7 +102,9 @@ pub enum LowLevel { } impl LowLevel { - pub fn is_higher_order_function(&self) -> bool { + /// is one of the arguments always a function? + /// An example is List.map. + pub fn is_higher_order(&self) -> bool { use LowLevel::*; match self { diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 11faa80aa7..ca1596ab57 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -407,29 +407,36 @@ impl<'a> BorrowInfState<'a> { self.own_args(arguments); } - LowLevel { - op, - opt_closure_layout, + LowLevel { op } => { + debug_assert!(!op.is_higher_order()); + + self.own_var(z); + + let ps = lowlevel_borrow_signature(self.arena, *op); + + self.own_args_using_bools(arguments, ps); + } + + HigherOrderLowLevel { + op, closure_layout, .. } => { use roc_module::low_level::LowLevel::*; - match op { - ListMap => { - match self - .param_map - .get_symbol(arguments[1], opt_closure_layout.unwrap()) - { - Some(ps) => { - if !ps[0].borrow { - self.own_var(arguments[0]); - } - if ps.len() > 1 && !ps[1].borrow { - self.own_var(arguments[2]); - } + debug_assert!(op.is_higher_order()); + + match op { + ListMap => match self.param_map.get_symbol(arguments[1], *closure_layout) { + Some(ps) => { + if !ps[0].borrow { + self.own_var(arguments[0]); + } + + if ps.len() > 1 && !ps[1].borrow { + self.own_var(arguments[2]); } - None => unreachable!(), } - } + None => unreachable!(), + }, _ => { // very unsure what demand RunLowLevel should place upon its arguments self.own_var(z); diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index baa0ed17f5..d3bc2753d8 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -1405,10 +1405,7 @@ fn compile_test_help<'a>( }; let test = Expr::Call(crate::ir::Call { - call_type: crate::ir::CallType::LowLevel { - op: LowLevel::Eq, - opt_closure_layout: None, - }, + call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq }, arguments: arena.alloc([lhs, rhs]), }); diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index fe70d8e26b..4c5fa80d5b 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -441,15 +441,23 @@ impl<'a> Context<'a> { use crate::ir::CallType::*; match &call_type { - LowLevel { - op, - opt_closure_layout, + LowLevel { op } => { + let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); + let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars); + + let v = Expr::Call(crate::ir::Call { + call_type, + arguments, + }); + + &*self.arena.alloc(Stmt::Let(z, v, l, b)) + } + + HigherOrderLowLevel { + op, closure_layout, .. } => match op { roc_module::low_level::LowLevel::ListMap => { - match self - .param_map - .get_symbol(arguments[1], opt_closure_layout.unwrap()) - { + match self.param_map.get_symbol(arguments[1], *closure_layout) { Some(ps) => { let b = if ps[0].borrow { let ps = [BORROWED, BORROWED, BORROWED]; @@ -463,6 +471,18 @@ impl<'a> Context<'a> { )) }; + let call_type = { + if ps[1].borrow { + call_type + } else { + HigherOrderLowLevel { + op: *op, + closure_layout: *closure_layout, + function_owns_closure_data: true, + } + } + }; + let v = Expr::Call(crate::ir::Call { call_type, arguments, @@ -790,14 +810,15 @@ impl<'a> Context<'a> { use crate::ir::CallType; let stmt = match &call.call_type { - CallType::LowLevel { - op, - opt_closure_layout: _, - } => { + CallType::LowLevel { op } => { let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars) } + CallType::HigherOrderLowLevel { .. } => { + todo!("copy the code for normal calls over to here"); + } + CallType::Foreign { .. } => { let ps = crate::borrow::foreign_borrow_signature( self.arena, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 5cd3c6925a..5b88903ff1 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1047,10 +1047,14 @@ impl<'a> Call<'a> { .text("CallByPointer ") .append(alloc.intersperse(it, " ")) } - LowLevel { - op: lowlevel, - opt_closure_layout: _, - } => { + LowLevel { op: lowlevel } => { + let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); + + alloc + .text(format!("lowlevel {:?} ", lowlevel)) + .append(alloc.intersperse(it, " ")) + } + HigherOrderLowLevel { op: lowlevel, .. } => { let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); alloc @@ -1092,8 +1096,13 @@ pub enum CallType<'a> { }, LowLevel { op: LowLevel, + }, + HigherOrderLowLevel { + op: LowLevel, /// the layout of the closure argument, if any - opt_closure_layout: Option>, + closure_layout: Layout<'a>, + /// does the function need to own the closure data + function_owns_closure_data: bool, }, } @@ -2654,7 +2663,11 @@ macro_rules! match_on_closure_argument { lambda_set, $closure_data_symbol, |top_level_function, closure_data, function_layout| self::Call { - call_type: CallType::LowLevel { op: $op, opt_closure_layout: Some(function_layout) }, + call_type: CallType::HigherOrderLowLevel { + op: $op, + closure_layout: function_layout, + function_owns_closure_data: false + }, arguments: arena.alloc([$($x,)* top_level_function, closure_data]), }, arena.alloc(top_level).full(), @@ -4345,10 +4358,7 @@ pub fn with_hole<'a>( } _ => { let call = self::Call { - call_type: CallType::LowLevel { - op, - opt_closure_layout: None, - }, + call_type: CallType::LowLevel { op }, arguments: arg_symbols, }; @@ -4568,7 +4578,6 @@ pub fn from_can<'a>( let call_type = CallType::LowLevel { op: LowLevel::ExpectTrue, - opt_closure_layout: None, }; let arguments = env.arena.alloc([cond_symbol]); let call = self::Call { @@ -5339,6 +5348,7 @@ fn substitute_in_call<'a>( }), CallType::Foreign { .. } => None, CallType::LowLevel { .. } => None, + CallType::HigherOrderLowLevel { .. } => None, }; let mut did_change = false; @@ -6052,6 +6062,12 @@ fn can_throw_exception(call: &Call) -> bool { CallType::LowLevel { .. } => { // lowlevel operations themselves don't throw + // TODO except for on allocation? + false + } + CallType::HigherOrderLowLevel { .. } => { + // TODO throwing is based on whether the HOF can throw + // or if there is (potentially) allocation in the lowlevel false } }