Merge in remote

This commit is contained in:
Chad Stearns 2020-08-30 15:38:43 -04:00
commit 8b31419b2c
4 changed files with 597 additions and 759 deletions

View file

@ -1,9 +1,9 @@
use crate::layout_id::LayoutIds; use crate::layout_id::LayoutIds;
use crate::llvm::build_list::{ use crate::llvm::build_list::{
allocate_list, build_basic_phi2, clone_nonempty_list, empty_list, empty_polymorphic_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_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::compare::{build_eq, build_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
@ -1674,9 +1674,9 @@ fn run_low_level<'a, 'ctx, 'env>(
// List.reverse : List elem -> List elem // List.reverse : List elem -> List elem
debug_assert_eq!(args.len(), 1); 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 => { ListConcat => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
@ -1730,9 +1730,8 @@ fn run_low_level<'a, 'ctx, 'env>(
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]); 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 => { NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
debug_assert_eq!(args.len(), 1); 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); let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len);
// FIRST LOOP // FIRST LOOP
let first_loop = |first_index| { let first_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type);
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 // The pointer to the element in the combined list
let combined_str_elem_ptr = unsafe { let combined_str_elem_ptr = unsafe {
builder.build_in_bounds_gep( 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_str_elem_ptr, first_str_elem); builder.build_store(combined_str_elem_ptr, first_str_elem);
}; };
let index_name = "#index"; let index_name = "#index";
let index_alloca = incrementing_index_loop( let index_alloca = incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
first_str_len, LoopListArg {
ptr: first_str_ptr,
len: first_str_len,
},
index_name, index_name,
None, None,
first_loop, first_loop,
@ -2050,14 +2045,9 @@ fn str_concat<'a, 'ctx, 'env>(
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false)); builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
// SECOND LOOP // SECOND LOOP
let second_loop = |second_index| { let second_str_ptr = load_list_ptr(builder, second_str_wrapper, ptr_type);
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. // The pointer to the element in the combined str.
// Note that the pointer does not start at the index // Note that the pointer does not start at the index
// 0, it starts at the index of first_str_len. In that // 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_str_char_ptr, second_str_elem); builder.build_store(combined_str_char_ptr, second_str_elem);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
second_str_len, LoopListArg {
ptr: second_str_ptr,
len: second_str_len,
},
index_name, index_name,
Some(index_alloca), Some(index_alloca),
second_loop, second_loop,
); );
let ptr_bytes = env.ptr_bytes; store_list(env, combined_str_ptr, combined_str_len)
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",
)
}; };
build_basic_phi2( build_basic_phi2(

View file

@ -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 crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
use inkwell::builder::Builder; use inkwell::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicTypeEnum, PointerType}; use inkwell::types::{BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, MemoryMode}; use roc_mono::layout::{Builtin, Layout, MemoryMode};
/// List.single : a -> List a /// List.single : a -> List a
@ -35,35 +34,7 @@ pub fn list_single<'a, 'ctx, 'env>(
builder.build_store(elem_ptr, elem); builder.build_store(elem_ptr, elem);
let ptr_bytes = env.ptr_bytes; store_list(env, ptr, env.ptr_int().const_int(1, false))
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",
)
} }
/// List.repeat : Int, elem -> List elem /// 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.build_conditional_branch(end_cond, loop_bb, after_bb);
builder.position_at_end(after_bb); builder.position_at_end(after_bb);
let ptr_bytes = env.ptr_bytes; store_list(env, list_ptr, list_len)
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",
)
}; };
let build_else = || empty_polymorphic_list(env); let build_else = || empty_polymorphic_list(env);
@ -204,12 +149,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
"new_list_length", "new_list_length",
); );
let ptr_bytes = env.ptr_bytes;
// Allocate space for the new array that we'll copy into. // Allocate space for the new array that we'll copy into.
let clone_ptr = allocate_list(env, elem_layout, new_list_len); 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); builder.build_store(clone_ptr, elem);
@ -232,6 +173,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
.builder .builder
.build_int_mul(elem_bytes, len, "mul_old_len_by_elem_bytes"); .build_int_mul(elem_bytes, len, "mul_old_len_by_elem_bytes");
let ptr_bytes = env.ptr_bytes;
if elem_layout.safe_to_memcpy() { if elem_layout.safe_to_memcpy() {
// Copy the bytes from the original array into the new // Copy the bytes from the original array into the new
// one we just malloc'd. // 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."); panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
} }
// Create a fresh wrapper struct for the newly populated array store_list(env, clone_ptr, new_list_len)
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",
)
} }
/// List.join : List (List elem) -> List elem /// List.join : List (List elem) -> List elem
pub fn list_join<'a, 'ctx, 'env>( pub fn list_join<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
outer_list_wrapper: StructValue<'ctx>, outer_list: BasicValueEnum<'ctx>,
outer_list_layout: &Layout<'a>, outer_list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
// List.join is implemented as follows: // List.join is implemented as follows:
@ -300,6 +220,7 @@ pub fn list_join<'a, 'ctx, 'env>(
let inner_list_type = let inner_list_type =
basic_type_from_layout(env.arena, ctx, &inner_list_layout, env.ptr_bytes); 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_len = list_len(builder, outer_list_wrapper);
let outer_list_ptr = { let outer_list_ptr = {
let elem_ptr_type = get_ptr_type(&inner_list_type, AddressSpace::Generic); 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)); builder.build_store(list_len_sum_alloca, ctx.i64_type().const_int(0, false));
// List Sum Loop // List Sum Loop
let sum_loop = |sum_index| { let sum_loop = |_, inner_list: BasicValueEnum<'ctx>| {
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 inner_list_len = list_len(builder, inner_list.into_struct_value()); let inner_list_len = list_len(builder, inner_list.into_struct_value());
let next_list_sum = builder.build_int_add( 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); builder.build_store(list_len_sum_alloca, next_list_sum);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
outer_list_len, LoopListArg {
ptr: outer_list_ptr,
len: outer_list_len,
},
"#sum_index", "#sum_index",
None, None,
sum_loop, sum_loop,
@ -359,16 +278,8 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(dest_elem_ptr_alloca, final_list_ptr); builder.build_store(dest_elem_ptr_alloca, final_list_ptr);
// Inner List Loop // Inner List Loop
let inner_list_loop = |index| { let inner_list_loop = |_, inner_list: BasicValueEnum<'ctx>| {
let inner_list_wrapper = { let inner_list_wrapper = inner_list.into_struct_value();
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_len = list_len(builder, inner_list_wrapper); 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); 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 // Element Inserting Loop
let inner_elem_loop = |inner_index| { let inner_elem_loop = |_, src_elem| {
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");
// TODO clone src_elem // TODO clone src_elem
let curr_dest_elem_ptr = builder 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); builder.build_store(dest_elem_ptr_alloca, inc_dest_elem_ptr);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
inner_list_len, LoopListArg {
ptr: inner_list_ptr,
len: inner_list_len,
},
"#inner_index", "#inner_index",
None, None,
inner_elem_loop, inner_elem_loop,
@ -433,49 +337,20 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.position_at_end(after_inner_list_non_empty_block); builder.position_at_end(after_inner_list_non_empty_block);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
outer_list_len, LoopListArg {
ptr: outer_list_ptr,
len: outer_list_len,
},
"#inner_list_index", "#inner_list_index",
None, None,
inner_list_loop, inner_list_loop,
); );
let ptr_bytes = env.ptr_bytes; store_list(env, final_list_ptr, final_list_sum)
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",
)
}; };
let build_else = || empty_list(env); let build_else = || empty_list(env);
@ -502,154 +377,89 @@ pub fn list_join<'a, 'ctx, 'env>(
pub fn list_reverse<'a, 'ctx, 'env>( pub fn list_reverse<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
scope: &Scope<'a, 'ctx>, list: BasicValueEnum<'ctx>,
list: &Symbol, list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let (_, list_layout) = load_symbol_and_layout(env, scope, list); let non_empty_fn =
|elem_layout: &Layout<'a>, len: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>| {
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 builder = env.builder; let builder = env.builder;
let ctx = env.context; let ctx = env.context;
let len = list_len(builder, wrapper_struct); // 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);
// list_len > 0 let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
// 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 = || { let reversed_list_ptr = allocate_list(env, elem_layout, len);
// 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);
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); // TODO check if malloc returned null; if so, runtime error for OOM!
let reversed_list_ptr = allocate_list(env, elem_layout, len); let index_name = "#index";
let start_alloca = builder.build_alloca(ctx.i64_type(), index_name);
// TODO check if malloc returned null; if so, runtime error for OOM! // Start at the last element in the list.
let last_elem_index =
builder.build_int_sub(len, ctx.i64_type().const_int(1, false), "lastelemindex");
builder.build_store(start_alloca, last_elem_index);
let index_name = "#index"; let loop_bb = ctx.append_basic_block(parent, "loop");
let start_alloca = builder.build_alloca(ctx.i64_type(), index_name); builder.build_unconditional_branch(loop_bb);
builder.position_at_end(loop_bb);
// Start at the last element in the list. // #index = #index - 1
let last_elem_index = let curr_index = builder
builder.build_int_sub(len, ctx.i64_type().const_int(1, false), "lastelemindex"); .build_load(start_alloca, index_name)
builder.build_store(start_alloca, last_elem_index); .into_int_value();
let next_index =
builder.build_int_sub(curr_index, ctx.i64_type().const_int(1, false), "nextindex");
let loop_bb = ctx.append_basic_block(parent, "loop"); builder.build_store(start_alloca, next_index);
builder.build_unconditional_branch(loop_bb);
builder.position_at_end(loop_bb);
// #index = #index - 1 let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
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",
);
builder.build_store(start_alloca, next_index); // The pointer to the element in the input list
let elem_ptr =
unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") };
let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type); // The pointer to the element in the reversed list
let reverse_elem_ptr = unsafe {
// The pointer to the element in the input list builder.build_in_bounds_gep(
let elem_ptr = reversed_list_ptr,
unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") }; &[builder.build_int_sub(
len,
// The pointer to the element in the reversed list builder.build_int_add(
let reverse_elem_ptr = unsafe { curr_index,
builder.build_in_bounds_gep( ctx.i64_type().const_int(1, false),
reversed_list_ptr, "curr_index_plus_one",
&[builder.build_int_sub( ),
len, "next_index",
builder.build_int_add( )],
curr_index, "load_index_reversed_list",
ctx.i64_type().const_int(1, false),
"curr_index_plus_one",
),
"next_index",
)],
"load_index_reversed_list",
)
};
let elem = builder.build_load(elem_ptr, "get_elem");
// Mutate the new array in-place to change the element.
builder.build_store(reverse_elem_ptr, elem);
// #index != 0
let end_cond = builder.build_int_compare(
IntPredicate::NE,
ctx.i64_type().const_int(0, false),
curr_index,
"loopcond",
);
let after_bb = ctx.append_basic_block(parent, "afterloop");
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",
) )
}; };
let build_else = || empty_list(env); let elem = builder.build_load(elem_ptr, "get_elem");
let struct_type = collection(ctx, env.ptr_bytes); // Mutate the new array in-place to change the element.
builder.build_store(reverse_elem_ptr, elem);
build_basic_phi2( // #index != 0
env, let end_cond = builder.build_int_compare(
parent, IntPredicate::NE,
comparison, ctx.i64_type().const_int(0, false),
build_then, curr_index,
build_else, "loopcond",
BasicTypeEnum::StructType(struct_type), );
)
} let after_bb = ctx.append_basic_block(parent, "afterloop");
_ => {
unreachable!("Invalid List layout for List.reverse {:?}", list_layout); builder.build_conditional_branch(end_cond, loop_bb, after_bb);
} builder.position_at_end(after_bb);
}
store_list(env, reversed_list_ptr, len)
};
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.reverse")
} }
pub fn list_get_unsafe<'a, 'ctx, 'env>( 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. // Allocate space for the new array that we'll copy into.
let clone_ptr = allocate_list(env, elem_layout, new_list_len); 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! // 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."); 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") }; let elem_ptr = unsafe { builder.build_in_bounds_gep(clone_ptr, &[list_len], "load_index") };
builder.build_store(elem_ptr, elem); builder.build_store(elem_ptr, elem);
builder.build_bitcast( store_list(env, clone_ptr, new_list_len)
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
} }
/// List.set : List elem, Int, elem -> List elem /// List.set : List elem, Int, elem -> List elem
@ -878,116 +663,56 @@ pub fn list_map<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match (func, func_layout) { match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => { (BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
match list_layout { let non_empty_fn = |elem_layout: &Layout<'a>,
Layout::Builtin(Builtin::EmptyList) => empty_list(env), len: IntValue<'ctx>,
Layout::Builtin(Builtin::List(_, elem_layout)) => { list_wrapper: StructValue<'ctx>| {
let ctx = env.context; let ctx = env.context;
let builder = env.builder; let builder = env.builder;
let list_wrapper = list.into_struct_value(); let ret_list_ptr = allocate_list(env, ret_elem_layout, len);
let len = list_len(builder, list_wrapper); 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);
// len > 0 let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
// 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 list_loop = |index, before_elem| {
// The pointer to the element in the input list
let if_list_is_not_empty = || { let call_site_value =
let ret_list_ptr = allocate_list(env, ret_elem_layout, len); builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func");
let elem_type = // set the calling convention explicitly for this call
basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes); call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type); let after_elem = call_site_value
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
let list_loop = |index| { // The pointer to the element in the mapped-over list
// The pointer to the element in the input list let after_elem_ptr = unsafe {
let before_elem_ptr = unsafe { builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
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",
);
// set the calling convention explicitly for this call
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
let after_elem = call_site_value
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
// 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",
)
};
// 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,
);
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",
)
}; };
build_basic_phi2( // Mutate the new array in-place to change the element.
env, builder.build_store(after_elem_ptr, after_elem);
parent, };
list_length_comparison,
if_list_is_not_empty, incrementing_elem_loop(
if_list_is_empty, builder,
BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes)), parent,
) ctx,
} LoopListArg { ptr: list_ptr, len },
_ => { "#index",
unreachable!("Invalid List layout for List.map : {:?}", list_layout); None,
} list_loop,
} );
store_list(env, ret_list_ptr, len)
};
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.map")
} }
_ => { _ => {
unreachable!( 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); let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type);
// FIRST LOOP // FIRST LOOP
let first_loop = |first_index| { let first_loop = |first_index, first_list_elem| {
// 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",
)
};
// The pointer to the element in the combined list // The pointer to the element in the combined list
let combined_list_elem_ptr = unsafe { let combined_list_elem_ptr = unsafe {
builder.build_in_bounds_gep( 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, first_list_elem); builder.build_store(combined_list_elem_ptr, first_list_elem);
}; };
let index_name = "#index"; let index_name = "#index";
let index_alloca = incrementing_index_loop( let index_alloca = incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
first_list_len, LoopListArg {
ptr: first_list_ptr,
len: first_list_len,
},
index_name, index_name,
None, None,
first_loop, first_loop,
@ -1132,19 +849,10 @@ pub fn list_concat<'a, 'ctx, 'env>(
// Reset the index variable to 0 // Reset the index variable to 0
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false)); builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type);
// SECOND LOOP // SECOND LOOP
let second_loop = |second_index| { let second_loop = |second_index, second_list_elem| {
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",
)
};
// The pointer to the element in the combined list. // The pointer to the element in the combined list.
// Note that the pointer does not start at the index // Note that the pointer does not start at the index
// 0, it starts at the index of first_list_len. In that // 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, second_list_elem); builder.build_store(combined_list_elem_ptr, second_list_elem);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
second_list_len, LoopListArg {
ptr: second_list_ptr,
len: second_list_len,
},
index_name, index_name,
Some(index_alloca), Some(index_alloca),
second_loop, second_loop,
); );
let ptr_bytes = env.ptr_bytes; store_list(env, combined_list_ptr, combined_list_len)
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",
)
}; };
build_basic_phi2( 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 // This helper simulates a basic for loop, where
// and index increments up from 0 to some end value // 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>, builder: &Builder<'ctx>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
ctx: &'ctx Context, ctx: &'ctx Context,
@ -1299,6 +1010,59 @@ where
index_alloca 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>( pub fn build_basic_phi2<'a, 'ctx, 'env, PassFn, FailFn>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
@ -1522,3 +1286,40 @@ pub fn allocate_list<'a, 'ctx, 'env>(
list_element_ptr 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",
)
}

View file

@ -34,7 +34,15 @@ mod gen_list {
fn list_append() { fn list_append() {
assert_evals_to!("List.append [1] 2", &[1, 2], &'static [i64]); assert_evals_to!("List.append [1] 2", &[1, 2], &'static [i64]);
assert_evals_to!("List.append [1, 1] 2", &[1, 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]); assert_evals_to!("List.append [] 3", &[3], &'static [i64]);
}
#[test]
fn list_append_to_empty_list_of_int() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -48,11 +56,19 @@ mod gen_list {
&[3, 3], &[3, 3],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_append_bools() {
assert_evals_to!( assert_evals_to!(
"List.append [ True, False ] True", "List.append [ True, False ] True",
&[true, false, true], &[true, false, true],
&'static [bool] &'static [bool]
); );
}
#[test]
fn list_append_longer_list() {
assert_evals_to!( assert_evals_to!(
"List.append [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] 23", "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], &[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
@ -78,12 +94,19 @@ mod gen_list {
&[6, 4], &[6, 4],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_prepend_bools() {
assert_evals_to!( assert_evals_to!(
"List.prepend [ True, False ] True", "List.prepend [ True, False ] True",
&[true, true, false], &[true, true, false],
&'static [bool] &'static [bool]
); );
}
#[test]
fn list_prepend_big_list() {
assert_evals_to!( assert_evals_to!(
"List.prepend [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100 ] 9", "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], &[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] #[test]
fn list_keep_if() { fn list_keep_if_empty_list_of_int() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> empty : List Int
empty : List Int empty =
empty = []
[]
List.keepIf empty (\x -> True) List.keepIf empty (\x -> True)
main {}
"# "#
), ),
&[], &[],
@ -111,114 +132,131 @@ mod gen_list {
} }
#[test] #[test]
fn list_map() { fn list_keep_if_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> List.keepIf [] (\x -> True)
empty : List Int
empty =
[]
List.map empty (\x -> x)
main {}
"# "#
), ),
&[], &[],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_map_on_empty_list_with_int_layout() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> empty : List Int
nonEmpty : List Int empty =
nonEmpty = []
[ 1 ]
List.map nonEmpty (\x -> x) List.map empty (\x -> x)
main {} "#
),
&[],
&'static [i64]
);
}
#[test]
fn list_map_on_non_empty_list() {
assert_evals_to!(
indoc!(
r#"
nonEmpty : List Int
nonEmpty =
[ 1 ]
List.map nonEmpty (\x -> x)
"# "#
), ),
&[1], &[1],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_map_changes_input() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> nonEmpty : List Int
nonEmpty : List Int nonEmpty =
nonEmpty = [ 1 ]
[ 1 ]
List.map nonEmpty (\x -> x + 1) List.map nonEmpty (\x -> x + 1)
main {}
"# "#
), ),
&[2], &[2],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_map_on_big_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> nonEmpty : List Int
nonEmpty : List Int nonEmpty =
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) 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] &'static [i64]
); );
}
#[test]
fn list_map_with_type_change() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> nonEmpty : List Int
nonEmpty : List Int nonEmpty =
nonEmpty = [ 1, 1, -4, 1, 2 ]
[ 1, 1, -4, 1, 2 ]
List.map nonEmpty (\x -> x > 0) List.map nonEmpty (\x -> x > 0)
main {}
"# "#
), ),
&[true, true, false, true, true], &[true, true, false, true, true],
&'static [bool] &'static [bool]
); );
}
#[test]
fn list_map_using_defined_function() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> nonEmpty : List Int
nonEmpty : List Int nonEmpty =
nonEmpty = [ 2, 2, -4, 2, 3 ]
[ 1, 1, -4, 1, 2 ]
greaterThanOne : Int -> Bool greaterThanOne : Int -> Bool
greaterThanOne = \i -> greaterThanOne = \i ->
i > 0 i > 1
List.map nonEmpty greaterThanOne List.map nonEmpty greaterThanOne
main {}
"# "#
), ),
&[true, true, false, true, true], &[true, true, false, true, true],
&'static [bool] &'static [bool]
); );
}
#[test]
fn list_map_all_inline() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
main = \{} -> List.map [] (\x -> x > 0)
List.map [] (\x -> x > 0)
main {}
"# "#
), ),
&[], &[],
@ -227,20 +265,35 @@ mod gen_list {
} }
#[test] #[test]
fn list_join() { fn list_join_empty_list() {
assert_evals_to!("List.join []", &[], &'static [i64]); 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]); assert_evals_to!("List.join [ [1, 2, 3 ] ]", &[1, 2, 3], &'static [i64]);
}
#[test]
fn list_join_two_non_empty_lists() {
assert_evals_to!( assert_evals_to!(
"List.join [ [1, 2, 3 ] , [4 ,5, 6] ]", "List.join [ [1, 2, 3 ] , [4 ,5, 6] ]",
&[1, 2, 3, 4, 5, 6], &[1, 2, 3, 4, 5, 6],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_join_two_non_empty_lists_of_float() {
assert_evals_to!( assert_evals_to!(
"List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]", "List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]",
&[1.2, 1.1, 2.1, 2.2], &[1.2, 1.1, 2.1, 2.2],
&'static [f64] &'static [f64]
); );
}
#[test]
fn list_join_to_big_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -262,7 +315,10 @@ mod gen_list {
], ],
&'static [f64] &'static [f64]
); );
}
#[test]
fn list_join_defined_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -276,9 +332,15 @@ mod gen_list {
&[0.2, 11.11], &[0.2, 11.11],
&'static [f64] &'static [f64]
); );
}
#[test]
fn list_join_all_empty_lists() {
assert_evals_to!("List.join [ [], [], [] ]", &[], &'static [f64]); assert_evals_to!("List.join [ [], [], [] ]", &[], &'static [f64]);
}
#[test]
fn list_join_one_empty_list() {
assert_evals_to!( assert_evals_to!(
"List.join [ [ 1.2, 1.1 ], [] ]", "List.join [ [ 1.2, 1.1 ], [] ]",
&[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 [1, 2, 3]", &[3, 2, 1], &'static [i64]);
assert_evals_to!("List.reverse [4]", &[4], &'static [i64]); assert_evals_to!("List.reverse [4]", &[4], &'static [i64]);
}
#[test]
fn list_reverse_empty_list_of_int() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -341,6 +407,10 @@ mod gen_list {
&[], &[],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_reverse_empty_list() {
assert_evals_to!("List.reverse []", &[], &'static [i64]); assert_evals_to!("List.reverse []", &[], &'static [i64]);
} }
@ -366,9 +436,12 @@ mod gen_list {
} }
#[test] #[test]
fn list_concat_vanilla() { fn list_concat_two_empty_lists() {
assert_evals_to!("List.concat [] []", &[], &'static [i64]); assert_evals_to!("List.concat [] []", &[], &'static [i64]);
}
#[test]
fn list_concat_two_empty_lists_of_int() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -386,16 +459,25 @@ mod gen_list {
&[], &[],
&'static [i64] &'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 [ 12, 13 ] []", &[12, 13], &'static [i64]);
assert_evals_to!( assert_evals_to!(
"List.concat [ 34, 43 ] [ 64, 55, 66 ]", "List.concat [ 34, 43 ] [ 64, 55, 66 ]",
&[34, 43, 64, 55, 66], &[34, 43, 64, 55, 66],
&'static [i64] &'static [i64]
); );
}
#[test]
fn list_concat_first_list_is_empty() {
assert_evals_to!("List.concat [] [ 23, 24 ]", &[23, 24], &'static [i64]); assert_evals_to!("List.concat [] [ 23, 24 ]", &[23, 24], &'static [i64]);
}
#[test]
fn list_concat_two_non_empty_lists() {
assert_evals_to!( assert_evals_to!(
"List.concat [1, 2 ] [ 3, 4 ]", "List.concat [1, 2 ] [ 3, 4 ]",
&[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 #[test]
// branch here, which is not yet something we support as of when this fn first_empty_list() {
// test was originally written. assert_evals_to!(
// indoc!(
// #[test] r#"
// fn first_empty_list() { when List.first [] is
// assert_evals_to!( Ok val -> val
// indoc!( Err _ -> -1
// r#" "#
// when List.first [] is ),
// Ok val -> val -1,
// Err _ -> -1 i64
// "# );
// ), }
// -1,
// i64
// );
// }
#[test] #[test]
fn get_empty_list() { fn get_empty_list() {
@ -980,151 +1058,151 @@ mod gen_list {
}) })
} }
// #[test] #[test]
// fn foobar2() { fn foobar2() {
// with_larger_debug_stack(|| { with_larger_debug_stack(|| {
// assert_evals_to!( assert_evals_to!(
// indoc!( indoc!(
// r#" r#"
// quicksort : List (Num a) -> List (Num a) quicksort : List (Num a) -> List (Num a)
// quicksort = \list -> quicksort = \list ->
// quicksortHelp list 0 (List.len list - 1) 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 foobar() { quicksortHelp : List (Num a), Int, Int -> List (Num a)
// with_larger_debug_stack(|| { quicksortHelp = \list, low, high ->
// assert_evals_to!( if low < high then
// indoc!( when partition low high list is
// r#" Pair partitionIndex partitioned ->
// quicksort : List (Num a) -> List (Num a) partitioned
// quicksort = \list -> |> quicksortHelp low (partitionIndex - 1)
// quicksortHelp list 0 (List.len list - 1) |> quicksortHelp (partitionIndex + 1) high
// else
// list
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
// quicksortHelp = \list, low, high ->
// if low < high then swap : Int, Int, List a -> List a
// when partition low high list is swap = \i, j, list ->
// Pair partitionIndex partitioned -> when Pair (List.get list i) (List.get list j) is
// partitioned Pair (Ok atI) (Ok atJ) ->
// |> quicksortHelp low (partitionIndex - 1) list
// |> quicksortHelp (partitionIndex + 1) high |> List.set i atJ
// else |> List.set j atI
// list
// _ ->
// []
// swap : Int, Int, List a -> List a
// swap = \i, j, list -> partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
// when Pair (List.get list i) (List.get list j) is partition = \low, high, initialList ->
// Pair (Ok atI) (Ok atJ) -> when List.get initialList high is
// list Ok pivot ->
// |> List.set i atJ when partitionHelp (low - 1) low initialList high pivot is
// |> List.set j atI Pair newI newList ->
// Pair (newI + 1) (swap (newI + 1) high newList)
// _ ->
// [] Err _ ->
// Pair (low - 1) initialList
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
// partition = \low, high, initialList ->
// when List.get initialList high is partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
// Ok pivot -> partitionHelp = \i, j, list, high, pivot ->
// when partitionHelp (low - 1) low initialList high pivot is # if j < high then
// Pair newI newList -> if False then
// Pair (newI + 1) (swap (newI + 1) high newList) when List.get list j is
// Ok value ->
// Err _ -> if value <= pivot then
// Pair (low - 1) initialList partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
// else
// partitionHelp i (j + 1) list high pivot
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
// partitionHelp = \i, j, list, high, pivot -> Err _ ->
// if j < high then Pair i list
// when List.get list j is else
// Ok value -> Pair i list
// if value <= pivot then
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
// else
// partitionHelp i (j + 1) list high pivot quicksort [ 7, 4, 21, 19 ]
// "#
// Err _ -> ),
// Pair i list &[19, 7, 4, 21],
// else &'static [i64]
// Pair i list );
// })
// }
//
// when List.first (quicksort [0x1]) is #[test]
// _ -> 4 fn foobar() {
// "# with_larger_debug_stack(|| {
// ), assert_evals_to!(
// 4, indoc!(
// i64, 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] #[test]
fn empty_list_increment_decrement() { fn empty_list_increment_decrement() {

View file

@ -311,7 +311,7 @@ fn layout_from_flat_type<'a>(
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer // Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1); 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; let content = subs.get_without_compacting(*var).content;
layout_from_num_content(content) layout_from_num_content(content)