mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge in remote
This commit is contained in:
commit
8b31419b2c
4 changed files with 597 additions and 759 deletions
|
@ -1,9 +1,9 @@
|
|||
use crate::layout_id::LayoutIds;
|
||||
use crate::llvm::build_list::{
|
||||
allocate_list, build_basic_phi2, clone_nonempty_list, empty_list, empty_polymorphic_list,
|
||||
incrementing_index_loop, list_append, list_concat, list_get_unsafe, list_is_not_empty,
|
||||
incrementing_elem_loop, list_append, list_concat, list_get_unsafe, list_is_not_empty,
|
||||
list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat, list_reverse, list_set,
|
||||
list_single, load_list_ptr,
|
||||
list_single, load_list_ptr, store_list, LoopListArg,
|
||||
};
|
||||
use crate::llvm::compare::{build_eq, build_neq};
|
||||
use crate::llvm::convert::{
|
||||
|
@ -1674,9 +1674,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
// List.reverse : List elem -> List elem
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let list = &args[0];
|
||||
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
||||
list_reverse(env, parent, scope, list)
|
||||
list_reverse(env, parent, list, list_layout)
|
||||
}
|
||||
ListConcat => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
@ -1730,9 +1730,8 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
let outer_wrapper_struct = list.into_struct_value();
|
||||
|
||||
list_join(env, parent, outer_wrapper_struct, outer_list_layout)
|
||||
list_join(env, parent, list, outer_list_layout)
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
@ -2011,14 +2010,9 @@ fn str_concat<'a, 'ctx, 'env>(
|
|||
let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len);
|
||||
|
||||
// FIRST LOOP
|
||||
let first_loop = |first_index| {
|
||||
let first_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type);
|
||||
|
||||
// The pointer to the element in the first list
|
||||
let first_str_char_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(first_str_ptr, &[first_index], "load_index")
|
||||
};
|
||||
|
||||
let first_loop = |first_index, first_str_elem| {
|
||||
// The pointer to the element in the combined list
|
||||
let combined_str_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
|
@ -2028,19 +2022,20 @@ fn str_concat<'a, 'ctx, 'env>(
|
|||
)
|
||||
};
|
||||
|
||||
let first_str_elem = builder.build_load(first_str_char_ptr, "get_elem");
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(combined_str_elem_ptr, first_str_elem);
|
||||
};
|
||||
|
||||
let index_name = "#index";
|
||||
|
||||
let index_alloca = incrementing_index_loop(
|
||||
let index_alloca = incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
first_str_len,
|
||||
LoopListArg {
|
||||
ptr: first_str_ptr,
|
||||
len: first_str_len,
|
||||
},
|
||||
index_name,
|
||||
None,
|
||||
first_loop,
|
||||
|
@ -2050,14 +2045,9 @@ fn str_concat<'a, 'ctx, 'env>(
|
|||
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
|
||||
|
||||
// SECOND LOOP
|
||||
let second_loop = |second_index| {
|
||||
let second_str_ptr = load_list_ptr(builder, second_str_wrapper, ptr_type);
|
||||
|
||||
// The pointer to the element in the second list
|
||||
let second_str_char_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(second_str_ptr, &[second_index], "load_index")
|
||||
};
|
||||
|
||||
let second_loop = |second_index, second_str_elem| {
|
||||
// The pointer to the element in the combined str.
|
||||
// Note that the pointer does not start at the index
|
||||
// 0, it starts at the index of first_str_len. In that
|
||||
|
@ -2076,55 +2066,24 @@ fn str_concat<'a, 'ctx, 'env>(
|
|||
)
|
||||
};
|
||||
|
||||
let second_str_elem = builder.build_load(second_str_char_ptr, "get_elem");
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(combined_str_char_ptr, second_str_elem);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
second_str_len,
|
||||
LoopListArg {
|
||||
ptr: second_str_ptr,
|
||||
len: second_str_len,
|
||||
},
|
||||
index_name,
|
||||
Some(index_alloca),
|
||||
second_loop,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(combined_str_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
combined_str_len,
|
||||
Builtin::WRAPPER_LEN,
|
||||
"insert_len",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, combined_str_ptr, combined_str_len)
|
||||
};
|
||||
|
||||
build_basic_phi2(
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use crate::llvm::build::{load_symbol, load_symbol_and_layout, Env, InPlace, Scope};
|
||||
use crate::llvm::build::{Env, InPlace};
|
||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicTypeEnum, PointerType};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout, MemoryMode};
|
||||
|
||||
/// List.single : a -> List a
|
||||
|
@ -35,35 +34,7 @@ pub fn list_single<'a, 'ctx, 'env>(
|
|||
|
||||
builder.build_store(elem_ptr, elem);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(1, false));
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
//
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, ptr, env.ptr_int().const_int(1, false))
|
||||
}
|
||||
|
||||
/// List.repeat : Int, elem -> List elem
|
||||
|
@ -138,33 +109,7 @@ pub fn list_repeat<'a, 'ctx, 'env>(
|
|||
builder.build_conditional_branch(end_cond, loop_bb, after_bb);
|
||||
builder.position_at_end(after_bb);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, list_len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, list_ptr, list_len)
|
||||
};
|
||||
|
||||
let build_else = || empty_polymorphic_list(env);
|
||||
|
@ -204,12 +149,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
|||
"new_list_length",
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
// Allocate space for the new array that we'll copy into.
|
||||
let clone_ptr = allocate_list(env, elem_layout, new_list_len);
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
builder.build_store(clone_ptr, elem);
|
||||
|
||||
|
@ -232,6 +173,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
|||
.builder
|
||||
.build_int_mul(elem_bytes, len, "mul_old_len_by_elem_bytes");
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
if elem_layout.safe_to_memcpy() {
|
||||
// Copy the bytes from the original array into the new
|
||||
// one we just malloc'd.
|
||||
|
@ -242,37 +185,14 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
|||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||
}
|
||||
|
||||
// Create a fresh wrapper struct for the newly populated array
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, new_list_len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, clone_ptr, new_list_len)
|
||||
}
|
||||
|
||||
/// List.join : List (List elem) -> List elem
|
||||
pub fn list_join<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
outer_list_wrapper: StructValue<'ctx>,
|
||||
outer_list: BasicValueEnum<'ctx>,
|
||||
outer_list_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// List.join is implemented as follows:
|
||||
|
@ -300,6 +220,7 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
let inner_list_type =
|
||||
basic_type_from_layout(env.arena, ctx, &inner_list_layout, env.ptr_bytes);
|
||||
|
||||
let outer_list_wrapper = outer_list.into_struct_value();
|
||||
let outer_list_len = list_len(builder, outer_list_wrapper);
|
||||
let outer_list_ptr = {
|
||||
let elem_ptr_type = get_ptr_type(&inner_list_type, AddressSpace::Generic);
|
||||
|
@ -319,12 +240,7 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
builder.build_store(list_len_sum_alloca, ctx.i64_type().const_int(0, false));
|
||||
|
||||
// List Sum Loop
|
||||
let sum_loop = |sum_index| {
|
||||
let inner_list_wrapper_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(outer_list_ptr, &[sum_index], "load_index")
|
||||
};
|
||||
|
||||
let inner_list = builder.build_load(inner_list_wrapper_ptr, "inner_list");
|
||||
let sum_loop = |_, inner_list: BasicValueEnum<'ctx>| {
|
||||
let inner_list_len = list_len(builder, inner_list.into_struct_value());
|
||||
|
||||
let next_list_sum = builder.build_int_add(
|
||||
|
@ -338,11 +254,14 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
builder.build_store(list_len_sum_alloca, next_list_sum);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
outer_list_len,
|
||||
LoopListArg {
|
||||
ptr: outer_list_ptr,
|
||||
len: outer_list_len,
|
||||
},
|
||||
"#sum_index",
|
||||
None,
|
||||
sum_loop,
|
||||
|
@ -359,16 +278,8 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
builder.build_store(dest_elem_ptr_alloca, final_list_ptr);
|
||||
|
||||
// Inner List Loop
|
||||
let inner_list_loop = |index| {
|
||||
let inner_list_wrapper = {
|
||||
let wrapper_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(outer_list_ptr, &[index], "load_index")
|
||||
};
|
||||
|
||||
builder
|
||||
.build_load(wrapper_ptr, "inner_list_wrapper")
|
||||
.into_struct_value()
|
||||
};
|
||||
let inner_list_loop = |_, inner_list: BasicValueEnum<'ctx>| {
|
||||
let inner_list_wrapper = inner_list.into_struct_value();
|
||||
|
||||
let inner_list_len = list_len(builder, inner_list_wrapper);
|
||||
|
||||
|
@ -386,20 +297,10 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
);
|
||||
builder.position_at_end(inner_list_non_empty_block);
|
||||
|
||||
let inner_list_ptr = load_list_ptr(builder, inner_list_wrapper, elem_ptr_type);
|
||||
|
||||
// Element Inserting Loop
|
||||
let inner_elem_loop = |inner_index| {
|
||||
let src_elem_ptr = unsafe {
|
||||
let inner_list_ptr =
|
||||
load_list_ptr(builder, inner_list_wrapper, elem_ptr_type);
|
||||
|
||||
builder.build_in_bounds_gep(
|
||||
inner_list_ptr,
|
||||
&[inner_index],
|
||||
"load_index",
|
||||
)
|
||||
};
|
||||
|
||||
let src_elem = builder.build_load(src_elem_ptr, "get_elem");
|
||||
let inner_elem_loop = |_, src_elem| {
|
||||
// TODO clone src_elem
|
||||
|
||||
let curr_dest_elem_ptr = builder
|
||||
|
@ -419,11 +320,14 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
builder.build_store(dest_elem_ptr_alloca, inc_dest_elem_ptr);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
inner_list_len,
|
||||
LoopListArg {
|
||||
ptr: inner_list_ptr,
|
||||
len: inner_list_len,
|
||||
},
|
||||
"#inner_index",
|
||||
None,
|
||||
inner_elem_loop,
|
||||
|
@ -433,49 +337,20 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
builder.position_at_end(after_inner_list_non_empty_block);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
outer_list_len,
|
||||
LoopListArg {
|
||||
ptr: outer_list_ptr,
|
||||
len: outer_list_len,
|
||||
},
|
||||
"#inner_list_index",
|
||||
None,
|
||||
inner_list_loop,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int =
|
||||
builder.build_ptr_to_int(final_list_ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
final_list_sum,
|
||||
Builtin::WRAPPER_LEN,
|
||||
"insert_len",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, final_list_ptr, final_list_sum)
|
||||
};
|
||||
|
||||
let build_else = || empty_list(env);
|
||||
|
@ -502,33 +377,14 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
pub fn list_reverse<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
list: &Symbol,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
list_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let (_, list_layout) = load_symbol_and_layout(env, scope, list);
|
||||
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
|
||||
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
let wrapper_struct = load_symbol(env, scope, list).into_struct_value();
|
||||
|
||||
let non_empty_fn =
|
||||
|elem_layout: &Layout<'a>, len: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>| {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let len = list_len(builder, wrapper_struct);
|
||||
|
||||
// list_len > 0
|
||||
// We do this check to avoid allocating memory. If the input
|
||||
// list is empty, then we can just return an empty list.
|
||||
let comparison = builder.build_int_compare(
|
||||
IntPredicate::UGT,
|
||||
len,
|
||||
ctx.i64_type().const_int(0, false),
|
||||
"greaterthanzero",
|
||||
);
|
||||
|
||||
let build_then = || {
|
||||
// Allocate space for the new array that we'll copy into.
|
||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
|
||||
|
@ -554,11 +410,8 @@ pub fn list_reverse<'a, 'ctx, 'env>(
|
|||
let curr_index = builder
|
||||
.build_load(start_alloca, index_name)
|
||||
.into_int_value();
|
||||
let next_index = builder.build_int_sub(
|
||||
curr_index,
|
||||
ctx.i64_type().const_int(1, false),
|
||||
"nextindex",
|
||||
);
|
||||
let next_index =
|
||||
builder.build_int_sub(curr_index, ctx.i64_type().const_int(1, false), "nextindex");
|
||||
|
||||
builder.build_store(start_alloca, next_index);
|
||||
|
||||
|
@ -603,53 +456,10 @@ pub fn list_reverse<'a, 'ctx, 'env>(
|
|||
builder.build_conditional_branch(end_cond, loop_bb, after_bb);
|
||||
builder.position_at_end(after_bb);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int =
|
||||
builder.build_ptr_to_int(reversed_list_ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, reversed_list_ptr, len)
|
||||
};
|
||||
|
||||
let build_else = || empty_list(env);
|
||||
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
|
||||
build_basic_phi2(
|
||||
env,
|
||||
parent,
|
||||
comparison,
|
||||
build_then,
|
||||
build_else,
|
||||
BasicTypeEnum::StructType(struct_type),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Invalid List layout for List.reverse {:?}", list_layout);
|
||||
}
|
||||
}
|
||||
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.reverse")
|
||||
}
|
||||
|
||||
pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
||||
|
@ -723,8 +533,6 @@ pub fn list_append<'a, 'ctx, 'env>(
|
|||
|
||||
// Allocate space for the new array that we'll copy into.
|
||||
let clone_ptr = allocate_list(env, elem_layout, new_list_len);
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
|
||||
|
@ -738,34 +546,11 @@ pub fn list_append<'a, 'ctx, 'env>(
|
|||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||
}
|
||||
|
||||
// Create a fresh wrapper struct for the newly populated array
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, new_list_len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(clone_ptr, &[list_len], "load_index") };
|
||||
|
||||
builder.build_store(elem_ptr, elem);
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, clone_ptr, new_list_len)
|
||||
}
|
||||
|
||||
/// List.set : List elem, Int, elem -> List elem
|
||||
|
@ -878,46 +663,24 @@ pub fn list_map<'a, 'ctx, 'env>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
match (func, func_layout) {
|
||||
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
let non_empty_fn = |elem_layout: &Layout<'a>,
|
||||
len: IntValue<'ctx>,
|
||||
list_wrapper: StructValue<'ctx>| {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let list_wrapper = list.into_struct_value();
|
||||
|
||||
let len = list_len(builder, list_wrapper);
|
||||
|
||||
// len > 0
|
||||
// We do this check to avoid allocating memory. If the input
|
||||
// list is empty, then we can just return an empty list.
|
||||
let list_length_comparison = list_is_not_empty(builder, ctx, len);
|
||||
|
||||
let if_list_is_empty = || empty_list(env);
|
||||
|
||||
let if_list_is_not_empty = || {
|
||||
let ret_list_ptr = allocate_list(env, ret_elem_layout, len);
|
||||
|
||||
let elem_type =
|
||||
basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||
|
||||
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
|
||||
|
||||
let list_loop = |index| {
|
||||
let list_loop = |index, before_elem| {
|
||||
// The pointer to the element in the input list
|
||||
let before_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(list_ptr, &[index], "load_index")
|
||||
};
|
||||
|
||||
let before_elem =
|
||||
builder.build_load(before_elem_ptr, "get_before_elem");
|
||||
|
||||
let call_site_value = builder.build_call(
|
||||
func_ptr,
|
||||
env.arena.alloc([before_elem]),
|
||||
"map_func",
|
||||
);
|
||||
let call_site_value =
|
||||
builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func");
|
||||
|
||||
// set the calling convention explicitly for this call
|
||||
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
|
||||
|
@ -929,65 +692,27 @@ pub fn list_map<'a, 'ctx, 'env>(
|
|||
|
||||
// The pointer to the element in the mapped-over list
|
||||
let after_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
ret_list_ptr,
|
||||
&[index],
|
||||
"load_index_after_list",
|
||||
)
|
||||
builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
|
||||
};
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(after_elem_ptr, after_elem);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
builder, parent, ctx, len, "#index", None, list_loop,
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
LoopListArg { ptr: list_ptr, len },
|
||||
"#index",
|
||||
None,
|
||||
list_loop,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int =
|
||||
builder.build_ptr_to_int(ret_list_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, ret_list_ptr, len)
|
||||
};
|
||||
|
||||
build_basic_phi2(
|
||||
env,
|
||||
parent,
|
||||
list_length_comparison,
|
||||
if_list_is_not_empty,
|
||||
if_list_is_empty,
|
||||
BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes)),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Invalid List layout for List.map : {:?}", list_layout);
|
||||
}
|
||||
}
|
||||
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.map")
|
||||
}
|
||||
_ => {
|
||||
unreachable!(
|
||||
|
@ -1092,16 +817,7 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type);
|
||||
|
||||
// FIRST LOOP
|
||||
let first_loop = |first_index| {
|
||||
// The pointer to the element in the first list
|
||||
let first_list_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
first_list_ptr,
|
||||
&[first_index],
|
||||
"load_index",
|
||||
)
|
||||
};
|
||||
|
||||
let first_loop = |first_index, first_list_elem| {
|
||||
// The pointer to the element in the combined list
|
||||
let combined_list_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
|
@ -1111,19 +827,20 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
)
|
||||
};
|
||||
|
||||
let first_list_elem = builder.build_load(first_list_elem_ptr, "get_elem");
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(combined_list_elem_ptr, first_list_elem);
|
||||
};
|
||||
|
||||
let index_name = "#index";
|
||||
|
||||
let index_alloca = incrementing_index_loop(
|
||||
let index_alloca = incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
first_list_len,
|
||||
LoopListArg {
|
||||
ptr: first_list_ptr,
|
||||
len: first_list_len,
|
||||
},
|
||||
index_name,
|
||||
None,
|
||||
first_loop,
|
||||
|
@ -1132,19 +849,10 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
// Reset the index variable to 0
|
||||
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
|
||||
|
||||
// SECOND LOOP
|
||||
let second_loop = |second_index| {
|
||||
let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type);
|
||||
|
||||
// The pointer to the element in the second list
|
||||
let second_list_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
second_list_ptr,
|
||||
&[second_index],
|
||||
"load_index",
|
||||
)
|
||||
};
|
||||
|
||||
// SECOND LOOP
|
||||
let second_loop = |second_index, second_list_elem| {
|
||||
// The pointer to the element in the combined list.
|
||||
// Note that the pointer does not start at the index
|
||||
// 0, it starts at the index of first_list_len. In that
|
||||
|
@ -1167,56 +875,24 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
)
|
||||
};
|
||||
|
||||
let second_list_elem = builder.build_load(second_list_elem_ptr, "get_elem");
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(combined_list_elem_ptr, second_list_elem);
|
||||
};
|
||||
|
||||
incrementing_index_loop(
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
second_list_len,
|
||||
LoopListArg {
|
||||
ptr: second_list_ptr,
|
||||
len: second_list_len,
|
||||
},
|
||||
index_name,
|
||||
Some(index_alloca),
|
||||
second_loop,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int =
|
||||
builder.build_ptr_to_int(combined_list_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
combined_list_len,
|
||||
Builtin::WRAPPER_LEN,
|
||||
"insert_len",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
store_list(env, combined_list_ptr, combined_list_len)
|
||||
};
|
||||
|
||||
build_basic_phi2(
|
||||
|
@ -1247,9 +923,44 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
pub struct LoopListArg<'ctx> {
|
||||
pub ptr: PointerValue<'ctx>,
|
||||
pub len: IntValue<'ctx>,
|
||||
}
|
||||
|
||||
pub fn incrementing_elem_loop<'ctx, LoopFn>(
|
||||
builder: &Builder<'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
list: LoopListArg<'ctx>,
|
||||
index_name: &str,
|
||||
maybe_alloca: Option<PointerValue<'ctx>>,
|
||||
mut loop_fn: LoopFn,
|
||||
) -> PointerValue<'ctx>
|
||||
where
|
||||
LoopFn: FnMut(IntValue<'ctx>, BasicValueEnum<'ctx>),
|
||||
{
|
||||
incrementing_index_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
list.len,
|
||||
index_name,
|
||||
maybe_alloca,
|
||||
|index| {
|
||||
// The pointer to the element in the list
|
||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(list.ptr, &[index], "load_index") };
|
||||
|
||||
let elem = builder.build_load(elem_ptr, "get_elem");
|
||||
|
||||
loop_fn(index, elem);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// This helper simulates a basic for loop, where
|
||||
// and index increments up from 0 to some end value
|
||||
pub fn incrementing_index_loop<'ctx, LoopFn>(
|
||||
fn incrementing_index_loop<'ctx, LoopFn>(
|
||||
builder: &Builder<'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
|
@ -1299,6 +1010,59 @@ where
|
|||
index_alloca
|
||||
}
|
||||
|
||||
// This function checks if the list is empty, and
|
||||
// if it is, it returns an empty list, and if not
|
||||
// it runs whatever code is passed in under `build_non_empty`
|
||||
// This is the avoid allocating memory if the list is empty.
|
||||
fn if_list_is_not_empty<'a, 'ctx, 'env, 'b, NonEmptyFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
mut build_non_empty: NonEmptyFn,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
list_layout: &Layout<'a>,
|
||||
list_fn_name: &str,
|
||||
) -> BasicValueEnum<'ctx>
|
||||
where
|
||||
NonEmptyFn: FnMut(&Layout<'a>, IntValue<'ctx>, StructValue<'ctx>) -> BasicValueEnum<'ctx>,
|
||||
{
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
|
||||
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let wrapper_struct = list.into_struct_value();
|
||||
|
||||
let len = list_len(builder, wrapper_struct);
|
||||
|
||||
// list_len > 0
|
||||
let comparison = builder.build_int_compare(
|
||||
IntPredicate::UGT,
|
||||
len,
|
||||
ctx.i64_type().const_int(0, false),
|
||||
"greaterthanzero",
|
||||
);
|
||||
|
||||
let build_empty = || empty_list(env);
|
||||
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
|
||||
build_basic_phi2(
|
||||
env,
|
||||
parent,
|
||||
comparison,
|
||||
|| build_non_empty(elem_layout, len, wrapper_struct),
|
||||
build_empty,
|
||||
BasicTypeEnum::StructType(struct_type),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Invalid List layout for {} {:?}", list_fn_name, list_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_basic_phi2<'a, 'ctx, 'env, PassFn, FailFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
|
@ -1522,3 +1286,40 @@ pub fn allocate_list<'a, 'ctx, 'env>(
|
|||
|
||||
list_element_ptr
|
||||
}
|
||||
|
||||
pub fn store_list<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
list_ptr: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,15 @@ mod gen_list {
|
|||
fn list_append() {
|
||||
assert_evals_to!("List.append [1] 2", &[1, 2], &'static [i64]);
|
||||
assert_evals_to!("List.append [1, 1] 2", &[1, 1, 2], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_append_to_empty_list() {
|
||||
assert_evals_to!("List.append [] 3", &[3], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_append_to_empty_list_of_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -48,11 +56,19 @@ mod gen_list {
|
|||
&[3, 3],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_append_bools() {
|
||||
assert_evals_to!(
|
||||
"List.append [ True, False ] True",
|
||||
&[true, false, true],
|
||||
&'static [bool]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_append_longer_list() {
|
||||
assert_evals_to!(
|
||||
"List.append [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] 23",
|
||||
&[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
||||
|
@ -78,12 +94,19 @@ mod gen_list {
|
|||
&[6, 4],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_prepend_bools() {
|
||||
assert_evals_to!(
|
||||
"List.prepend [ True, False ] True",
|
||||
&[true, true, false],
|
||||
&'static [bool]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_prepend_big_list() {
|
||||
assert_evals_to!(
|
||||
"List.prepend [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100 ] 9",
|
||||
&[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100],
|
||||
|
@ -92,17 +115,15 @@ mod gen_list {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn list_keep_if() {
|
||||
fn list_keep_if_empty_list_of_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
empty : List Int
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.keepIf empty (\x -> True)
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[],
|
||||
|
@ -111,114 +132,131 @@ mod gen_list {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn list_map() {
|
||||
fn list_keep_if_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
empty : List Int
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.map empty (\x -> x)
|
||||
main {}
|
||||
List.keepIf [] (\x -> True)
|
||||
"#
|
||||
),
|
||||
&[],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_on_empty_list_with_int_layout() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.map empty (\x -> x)
|
||||
"#
|
||||
),
|
||||
&[],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_on_non_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
nonEmpty : List Int
|
||||
nonEmpty =
|
||||
[ 1 ]
|
||||
|
||||
List.map nonEmpty (\x -> x)
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[1],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_changes_input() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
nonEmpty : List Int
|
||||
nonEmpty =
|
||||
[ 1 ]
|
||||
|
||||
List.map nonEmpty (\x -> x + 1)
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[2],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_on_big_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
nonEmpty : List Int
|
||||
nonEmpty =
|
||||
[ 1, 2, 3, 4, 5 ]
|
||||
[ 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]
|
||||
|
||||
List.map nonEmpty (\x -> x * 2)
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[2, 4, 6, 8, 10],
|
||||
&[2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_with_type_change() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
nonEmpty : List Int
|
||||
nonEmpty =
|
||||
[ 1, 1, -4, 1, 2 ]
|
||||
|
||||
|
||||
List.map nonEmpty (\x -> x > 0)
|
||||
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[true, true, false, true, true],
|
||||
&'static [bool]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_using_defined_function() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
nonEmpty : List Int
|
||||
nonEmpty =
|
||||
[ 1, 1, -4, 1, 2 ]
|
||||
[ 2, 2, -4, 2, 3 ]
|
||||
|
||||
greaterThanOne : Int -> Bool
|
||||
greaterThanOne = \i ->
|
||||
i > 0
|
||||
i > 1
|
||||
|
||||
List.map nonEmpty greaterThanOne
|
||||
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[true, true, false, true, true],
|
||||
&'static [bool]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_all_inline() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
main = \{} ->
|
||||
List.map [] (\x -> x > 0)
|
||||
|
||||
main {}
|
||||
"#
|
||||
),
|
||||
&[],
|
||||
|
@ -227,20 +265,35 @@ mod gen_list {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn list_join() {
|
||||
fn list_join_empty_list() {
|
||||
assert_evals_to!("List.join []", &[], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_one_list() {
|
||||
assert_evals_to!("List.join [ [1, 2, 3 ] ]", &[1, 2, 3], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_two_non_empty_lists() {
|
||||
assert_evals_to!(
|
||||
"List.join [ [1, 2, 3 ] , [4 ,5, 6] ]",
|
||||
&[1, 2, 3, 4, 5, 6],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_two_non_empty_lists_of_float() {
|
||||
assert_evals_to!(
|
||||
"List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]",
|
||||
&[1.2, 1.1, 2.1, 2.2],
|
||||
&'static [f64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_to_big_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -262,7 +315,10 @@ mod gen_list {
|
|||
],
|
||||
&'static [f64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_defined_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -276,9 +332,15 @@ mod gen_list {
|
|||
&[0.2, 11.11],
|
||||
&'static [f64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_all_empty_lists() {
|
||||
assert_evals_to!("List.join [ [], [], [] ]", &[], &'static [f64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_join_one_empty_list() {
|
||||
assert_evals_to!(
|
||||
"List.join [ [ 1.2, 1.1 ], [] ]",
|
||||
&[1.2, 1.1],
|
||||
|
@ -328,6 +390,10 @@ mod gen_list {
|
|||
);
|
||||
assert_evals_to!("List.reverse [1, 2, 3]", &[3, 2, 1], &'static [i64]);
|
||||
assert_evals_to!("List.reverse [4]", &[4], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_reverse_empty_list_of_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -341,6 +407,10 @@ mod gen_list {
|
|||
&[],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_reverse_empty_list() {
|
||||
assert_evals_to!("List.reverse []", &[], &'static [i64]);
|
||||
}
|
||||
|
||||
|
@ -366,9 +436,12 @@ mod gen_list {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn list_concat_vanilla() {
|
||||
fn list_concat_two_empty_lists() {
|
||||
assert_evals_to!("List.concat [] []", &[], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_concat_two_empty_lists_of_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -386,16 +459,25 @@ mod gen_list {
|
|||
&[],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_concat_second_list_is_empty() {
|
||||
assert_evals_to!("List.concat [ 12, 13 ] []", &[12, 13], &'static [i64]);
|
||||
assert_evals_to!(
|
||||
"List.concat [ 34, 43 ] [ 64, 55, 66 ]",
|
||||
&[34, 43, 64, 55, 66],
|
||||
&'static [i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_concat_first_list_is_empty() {
|
||||
assert_evals_to!("List.concat [] [ 23, 24 ]", &[23, 24], &'static [i64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_concat_two_non_empty_lists() {
|
||||
assert_evals_to!(
|
||||
"List.concat [1, 2 ] [ 3, 4 ]",
|
||||
&[1, 2, 3, 4],
|
||||
|
@ -555,24 +637,20 @@ mod gen_list {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO getting this to work requires generating a runtime error for the Ok
|
||||
// branch here, which is not yet something we support as of when this
|
||||
// test was originally written.
|
||||
//
|
||||
// #[test]
|
||||
// fn first_empty_list() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when List.first [] is
|
||||
// Ok val -> val
|
||||
// Err _ -> -1
|
||||
// "#
|
||||
// ),
|
||||
// -1,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn first_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when List.first [] is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
),
|
||||
-1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_empty_list() {
|
||||
|
@ -980,151 +1058,151 @@ mod gen_list {
|
|||
})
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn foobar2() {
|
||||
// with_larger_debug_stack(|| {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// quicksort : List (Num a) -> List (Num a)
|
||||
// quicksort = \list ->
|
||||
// quicksortHelp list 0 (List.len list - 1)
|
||||
//
|
||||
//
|
||||
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
// quicksortHelp = \list, low, high ->
|
||||
// if low < high then
|
||||
// when partition low high list is
|
||||
// Pair partitionIndex partitioned ->
|
||||
// partitioned
|
||||
// |> quicksortHelp low (partitionIndex - 1)
|
||||
// |> quicksortHelp (partitionIndex + 1) high
|
||||
// else
|
||||
// list
|
||||
//
|
||||
//
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
// list
|
||||
// |> List.set i atJ
|
||||
// |> List.set j atI
|
||||
//
|
||||
// _ ->
|
||||
// []
|
||||
//
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
// when partitionHelp (low - 1) low initialList high pivot is
|
||||
// Pair newI newList ->
|
||||
// Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp = \i, j, list, high, pivot ->
|
||||
// # if j < high then
|
||||
// if False then
|
||||
// when List.get list j is
|
||||
// Ok value ->
|
||||
// if value <= pivot then
|
||||
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
// else
|
||||
// partitionHelp i (j + 1) list high pivot
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair i list
|
||||
// else
|
||||
// Pair i list
|
||||
//
|
||||
//
|
||||
//
|
||||
// quicksort [ 7, 4, 21, 19 ]
|
||||
// "#
|
||||
// ),
|
||||
// &[19, 7, 4, 21],
|
||||
// &'static [i64],
|
||||
// );
|
||||
// })
|
||||
// }
|
||||
#[test]
|
||||
fn foobar2() {
|
||||
with_larger_debug_stack(|| {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksort : List (Num a) -> List (Num a)
|
||||
quicksort = \list ->
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
// #[test]
|
||||
// fn foobar() {
|
||||
// with_larger_debug_stack(|| {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// quicksort : List (Num a) -> List (Num a)
|
||||
// quicksort = \list ->
|
||||
// quicksortHelp list 0 (List.len list - 1)
|
||||
//
|
||||
//
|
||||
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
// quicksortHelp = \list, low, high ->
|
||||
// if low < high then
|
||||
// when partition low high list is
|
||||
// Pair partitionIndex partitioned ->
|
||||
// partitioned
|
||||
// |> quicksortHelp low (partitionIndex - 1)
|
||||
// |> quicksortHelp (partitionIndex + 1) high
|
||||
// else
|
||||
// list
|
||||
//
|
||||
//
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
// list
|
||||
// |> List.set i atJ
|
||||
// |> List.set j atI
|
||||
//
|
||||
// _ ->
|
||||
// []
|
||||
//
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
// when partitionHelp (low - 1) low initialList high pivot is
|
||||
// Pair newI newList ->
|
||||
// Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp = \i, j, list, high, pivot ->
|
||||
// if j < high then
|
||||
// when List.get list j is
|
||||
// Ok value ->
|
||||
// if value <= pivot then
|
||||
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
// else
|
||||
// partitionHelp i (j + 1) list high pivot
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair i list
|
||||
// else
|
||||
// Pair i list
|
||||
//
|
||||
//
|
||||
//
|
||||
// when List.first (quicksort [0x1]) is
|
||||
// _ -> 4
|
||||
// "#
|
||||
// ),
|
||||
// 4,
|
||||
// i64,
|
||||
// );
|
||||
// })
|
||||
// }
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp (low - 1) low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
# if j < high then
|
||||
if False then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
|
||||
|
||||
|
||||
quicksort [ 7, 4, 21, 19 ]
|
||||
"#
|
||||
),
|
||||
&[19, 7, 4, 21],
|
||||
&'static [i64]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foobar() {
|
||||
with_larger_debug_stack(|| {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksort : List (Num a) -> List (Num a)
|
||||
quicksort = \list ->
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp (low - 1) low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
|
||||
|
||||
|
||||
when List.first (quicksort [0x1]) is
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
4,
|
||||
i64
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_list_increment_decrement() {
|
||||
|
|
|
@ -311,7 +311,7 @@ fn layout_from_flat_type<'a>(
|
|||
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let var = args.iter().next().unwrap();
|
||||
let var = args.get(0).unwrap();
|
||||
let content = subs.get_without_compacting(*var).content;
|
||||
|
||||
layout_from_num_content(content)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue