Implement List.any

This commit is contained in:
Kevin Sjöberg 2021-11-05 23:34:10 +01:00
parent 0e500ba33c
commit 44938a9e35
15 changed files with 137 additions and 6 deletions

View file

@ -1078,6 +1078,33 @@ pub fn listSortWith(
return list;
}
pub fn listAny(
list: RocList,
caller: Caller1,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
element_width: usize,
) callconv(.C) bool {
if (list.bytes) |source_ptr| {
const size = list.len();
var i: usize = 0;
var satisfied = false;
while (i < size) : (i += 1) {
const element = source_ptr + i * element_width;
caller(data, element, @ptrCast(?[*]u8, &satisfied));
if (satisfied) {
return satisfied;
}
}
}
return false;
}
// SWAP ELEMENTS
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {

View file

@ -49,6 +49,7 @@ comptime {
exportListFn(list.listSet, "set");
exportListFn(list.listSetInPlace, "set_in_place");
exportListFn(list.listSwap, "swap");
exportListFn(list.listAny, "any");
}
// Dict Module

View file

@ -188,6 +188,7 @@ pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
pub const LIST_SET: &str = "roc_builtins.list.set";
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
pub const LIST_ANY: &str = "roc_builtins.list.any";
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
pub const DEC_EQ: &str = "roc_builtins.dec.eq";

View file

@ -1055,6 +1055,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(bool_type())
);
// any: List elem, (elem -> Bool) -> Bool
add_top_level_function_type!(
Symbol::LIST_ANY,
vec![
list_type(flex(TVAR1)),
closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())),
],
Box::new(bool_type()),
);
// sortWith : List a, (a, a -> Ordering) -> List a
add_top_level_function_type!(
Symbol::LIST_SORT_WITH,

View file

@ -105,6 +105,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_WALK_BACKWARDS => list_walk_backwards,
LIST_WALK_UNTIL => list_walk_until,
LIST_SORT_WITH => list_sort_with,
LIST_ANY => list_any,
DICT_TEST_HASH => dict_hash_test_only,
DICT_LEN => dict_len,
DICT_EMPTY => dict_empty,
@ -2695,6 +2696,11 @@ fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
}
/// List.any: List elem, (elem -> Bool) -> Bool
fn list_any(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListAny, var_store)
}
/// Dict.hashTestOnly : k, v -> Nat
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::Hash, var_store)

View file

@ -8,7 +8,7 @@ use crate::llvm::build_dict::{
};
use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat,
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
@ -4863,6 +4863,30 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
_ => unreachable!("invalid list layout"),
}
}
ListAny { xs } => {
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
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_any(env, roc_function_call, list, element_layout)
}
_ => unreachable!("invalid list layout"),
}
}
DictWalk { xs, state } => {
let (dict, dict_layout) = load_symbol_and_layout(scope, &xs);
let (default, default_layout) = load_symbol_and_layout(scope, &state);
@ -5713,7 +5737,7 @@ fn run_low_level<'a, 'ctx, 'env>(
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
| ListAny | DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
}
}

View file

@ -896,6 +896,28 @@ pub fn list_concat<'a, 'ctx, 'env>(
}
}
/// List.any : List elem, \(elem -> Bool) -> Bool
pub fn list_any<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
roc_function_call: RocFunctionCall<'ctx>,
list: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
call_bitcode_fn(
env,
&[
pass_list_cc(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(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
],
bitcode::LIST_ANY,
)
}
pub fn decrementing_elem_loop<'ctx, LoopFn>(
builder: &Builder<'ctx>,
ctx: &'ctx Context,

View file

@ -45,6 +45,7 @@ pub enum LowLevel {
ListDrop,
ListDropAt,
ListSwap,
ListAny,
DictSize,
DictEmpty,
DictInsert,
@ -221,6 +222,7 @@ macro_rules! higher_order {
| ListKeepOks
| ListKeepErrs
| ListSortWith
| ListAny
| DictWalk
};
}
@ -254,6 +256,7 @@ impl LowLevel {
ListKeepOks => 1,
ListKeepErrs => 1,
ListSortWith => 1,
ListAny => 1,
DictWalk => 2,
}
}

View file

@ -1064,6 +1064,7 @@ define_builtins! {
41 LIST_DROP_FIRST: "dropFirst"
42 LIST_JOIN_MAP: "joinMap"
43 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
44 LIST_ANY: "any"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -967,7 +967,6 @@ fn call_spec(
add_loop(builder, block, state_type, init_state, loop_body)
}
ListKeepIf { xs } => {
let list = env.symbols[xs];
@ -1073,6 +1072,25 @@ fn call_spec(
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
let state_type = layout_spec(builder, &state_layout)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListAny { xs } => {
let list = env.symbols[xs];
let loop_body = |builder: &mut FuncDefBuilder, block, _state| {
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let element = builder.add_bag_get(block, bag)?;
let new_state = call_function!(builder, block, [element]);
Ok(new_state)
};
let state_layout = Layout::Builtin(Builtin::Int1);
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_num(builder, block)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
}

View file

@ -617,7 +617,8 @@ impl<'a> BorrowInfState<'a> {
ListMap { xs }
| ListKeepIf { xs }
| ListKeepOks { xs }
| ListKeepErrs { xs } => {
| ListKeepErrs { xs }
| ListAny { xs } => {
// own the list if the function wants to own the element
if !function_ps[0].borrow {
self.own_var(*xs);
@ -949,7 +950,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
ListKeepIf | ListKeepOks | ListKeepErrs => {
ListKeepIf | ListKeepOks | ListKeepErrs | ListAny => {
arena.alloc_slice_copy(&[owned, function, closure_data])
}
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),

View file

@ -530,7 +530,8 @@ impl<'a> Context<'a> {
ListMap { xs }
| ListKeepIf { xs }
| ListKeepOks { xs }
| ListKeepErrs { xs } => {
| ListKeepErrs { xs }
| ListAny { xs } => {
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);

View file

@ -4113,6 +4113,11 @@ pub fn with_hole<'a>(
let xs = arg_symbols[0];
match_on_closure_argument!(ListKeepIf, [xs])
}
ListAny => {
debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0];
match_on_closure_argument!(ListAny, [xs])
}
ListKeepOks => {
debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0];

View file

@ -47,6 +47,9 @@ pub enum HigherOrder {
ListSortWith {
xs: Symbol,
},
ListAny {
xs: Symbol,
},
DictWalk {
xs: Symbol,
state: Symbol,
@ -69,6 +72,7 @@ impl HigherOrder {
HigherOrder::ListKeepErrs { .. } => 1,
HigherOrder::ListSortWith { .. } => 2,
HigherOrder::DictWalk { .. } => 2,
HigherOrder::ListAny { .. } => 1,
}
}
}

View file

@ -2160,6 +2160,13 @@ fn list_sort_with() {
);
}
#[test]
fn list_any() {
assert_evals_to!("List.any [] (\\e -> e > 3)", false, bool);
assert_evals_to!("List.any [ 1, 2, 3 ] (\\e -> e > 3)", false, bool);
assert_evals_to!("List.any [ 1, 2, 4 ] (\\e -> e > 3)", true, bool);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
fn lists_with_incompatible_type_param_in_if() {