adding initial List.all

This commit is contained in:
Michael Downey 2021-11-16 16:34:36 -05:00
parent b03ed18553
commit d946b84e63
17 changed files with 160 additions and 4 deletions

View file

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

View file

@ -51,6 +51,7 @@ comptime {
exportListFn(list.listSetInPlace, "set_in_place");
exportListFn(list.listSwap, "swap");
exportListFn(list.listAny, "any");
exportListFn(list.listAll, "all");
exportListFn(list.listFindUnsafe, "find_unsafe");
}

View file

@ -691,6 +691,10 @@ all : List elem, (elem -> Bool) -> Bool
## any of the elements satisfy it.
any : List elem, (elem -> Bool) -> Bool
## Run the given predicate on each element of the list, returning `True` if
## all of the elements satisfy it.
all : List elem, (elem -> Bool) -> Bool
## Returns the first element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned.
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*

View file

@ -196,6 +196,7 @@ 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 LIST_ALL: &str = "roc_builtins.list.all";
pub const LIST_FIND_UNSAFE: &str = "roc_builtins.list.find_unsafe";
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";

View file

@ -1109,6 +1109,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(bool_type()),
);
// all: List elem, (elem -> Bool) -> Bool
add_top_level_function_type!(
Symbol::LIST_ALL,
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

@ -111,6 +111,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_WALK_UNTIL => list_walk_until,
LIST_SORT_WITH => list_sort_with,
LIST_ANY => list_any,
LIST_ALL => list_all,
LIST_FIND => list_find,
DICT_LEN => dict_len,
DICT_EMPTY => dict_empty,
@ -2855,6 +2856,11 @@ fn list_any(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListAny, var_store)
}
/// List.all: List elem, (elem -> Bool) -> Bool
fn list_all(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListAll, var_store)
}
/// List.find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
fn list_find(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list = Symbol::ARG_1;

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_any, list_append, list_concat,
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_all, list_append, list_concat,
list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe, 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,
@ -5141,6 +5141,31 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
_ => unreachable!("invalid list layout"),
}
}
ListAll { 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) => env.context.bool_type().const_zero().into(),
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,
Layout::Builtin(Builtin::Int1),
);
list_all(env, roc_function_call, list, element_layout)
}
_ => unreachable!("invalid list layout"),
}
}
ListFindUnsafe { xs } => {
let (list, list_layout) = load_symbol_and_layout(scope, xs);
@ -6041,7 +6066,7 @@ fn run_low_level<'a, 'ctx, 'env>(
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| ListAny | ListFindUnsafe | DictWalk => {
| ListAny | ListAll | ListFindUnsafe | DictWalk => {
unreachable!("these are higher order, and are handled elsewhere")
}
}

View file

@ -926,6 +926,27 @@ pub fn list_any<'a, 'ctx, 'env>(
)
}
/// List.all : List elem, \(elem -> Bool) -> Bool
pub fn list_all<'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(),
layout_width(env, element_layout),
],
bitcode::LIST_ALL,
)
}
/// List.findUnsafe : List elem, (elem -> Bool) -> { value: elem, found: bool }
pub fn list_find_unsafe<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -34,7 +34,7 @@ pub fn decode_low_level<'a>(
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap
| ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty
| ListSublist | ListDropAt | ListSwap | ListAny | ListAll | ListFindUnsafe | DictSize | DictEmpty
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
return NotImplemented;

View file

@ -50,6 +50,7 @@ pub enum LowLevel {
ListDropAt,
ListSwap,
ListAny,
ListAll,
ListFindUnsafe,
DictSize,
DictEmpty,
@ -131,6 +132,7 @@ macro_rules! higher_order {
| ListKeepErrs
| ListSortWith
| ListAny
| ListAll
| ListFindUnsafe
| DictWalk
};
@ -162,6 +164,7 @@ impl LowLevel {
ListKeepErrs => 1,
ListSortWith => 1,
ListAny => 1,
ListAll => 1,
ListFindUnsafe => 1,
DictWalk => 2,
_ => unreachable!(),
@ -220,6 +223,7 @@ impl LowLevel {
Symbol::LIST_DROP_AT => Some(ListDropAt),
Symbol::LIST_SWAP => Some(ListSwap),
Symbol::LIST_ANY => Some(ListAny),
Symbol::LIST_ALL => Some(ListAll),
Symbol::LIST_FIND => None,
Symbol::DICT_LEN => Some(DictSize),
Symbol::DICT_EMPTY => Some(DictEmpty),

View file

@ -1072,6 +1072,7 @@ define_builtins! {
47 LIST_FIND: "find"
48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
49 LIST_SUBLIST: "sublist"
50 LIST_ALL: "all"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -1093,6 +1093,25 @@ fn call_spec(
add_loop(builder, block, state_type, init_state, loop_body)
}
ListAll { 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)
}
ListFindUnsafe { xs } => {
let list = env.symbols[xs];

View file

@ -619,6 +619,7 @@ impl<'a> BorrowInfState<'a> {
| ListKeepOks { xs }
| ListKeepErrs { xs }
| ListAny { xs }
| ListAll { xs }
| ListFindUnsafe { xs } => {
// own the list if the function wants to own the element
if !function_ps[0].borrow {
@ -953,7 +954,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 | ListAny => {
ListKeepIf | ListKeepOks | ListKeepErrs | ListAny | ListAll => {
arena.alloc_slice_copy(&[owned, function, closure_data])
}
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),

View file

@ -536,6 +536,7 @@ impl<'a> Context<'a> {
| ListKeepOks { xs }
| ListKeepErrs { xs }
| ListAny { xs }
| ListAll { xs }
| ListFindUnsafe { xs } => {
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];

View file

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

View file

@ -50,6 +50,9 @@ pub enum HigherOrder {
ListAny {
xs: Symbol,
},
ListAll {
xs: Symbol,
},
ListFindUnsafe {
xs: Symbol,
},
@ -77,6 +80,7 @@ impl HigherOrder {
HigherOrder::ListFindUnsafe { .. } => 1,
HigherOrder::DictWalk { .. } => 2,
HigherOrder::ListAny { .. } => 1,
HigherOrder::ListAll { .. } => 1,
}
}
}

View file

@ -2352,6 +2352,28 @@ fn list_any_empty_with_unknown_element_type() {
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_all() {
assert_evals_to!("List.all [] (\\e -> e > 3)", false, bool);
assert_evals_to!("List.all [ 1, 2, 3 ] (\\e -> e > 3)", false, bool);
assert_evals_to!("List.all [ 1, 2, 4 ] (\\e -> e > 3)", false, bool);
assert_evals_to!("List.all [ 1, 2, 3 ] (\\e -> e >= 1", true, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)]
fn list_all_empty_with_unknown_element_type() {
// Segfaults with invalid memory reference. Running this as a stand-alone
// Roc program, generates the following error message:
//
// Application crashed with message
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
// Shutting down
assert_evals_to!("List.all [] (\\_ -> True)", false, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]