diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 45315e0acf..2cc6dbc996 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -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_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type); + 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_str_ptr = load_list_ptr(builder, second_str_wrapper, ptr_type); + 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( diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index f403580299..05698953e0 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -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,154 +377,89 @@ 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); + // 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 - // 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 ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); - 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); + let reversed_list_ptr = allocate_list(env, elem_layout, len); - 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 start_alloca = builder.build_alloca(ctx.i64_type(), index_name); + let loop_bb = ctx.append_basic_block(parent, "loop"); + builder.build_unconditional_branch(loop_bb); + builder.position_at_end(loop_bb); - // 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); + // #index = #index - 1 + 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 loop_bb = ctx.append_basic_block(parent, "loop"); - builder.build_unconditional_branch(loop_bb); - builder.position_at_end(loop_bb); + builder.build_store(start_alloca, next_index); - // #index = #index - 1 - 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 list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type); - 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 input list - let elem_ptr = - unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") }; - - // The pointer to the element in the reversed list - let reverse_elem_ptr = unsafe { - builder.build_in_bounds_gep( - reversed_list_ptr, - &[builder.build_int_sub( - len, - builder.build_int_add( - curr_index, - 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", + // The pointer to the element in the reversed list + let reverse_elem_ptr = unsafe { + builder.build_in_bounds_gep( + reversed_list_ptr, + &[builder.build_int_sub( + len, + builder.build_int_add( + curr_index, + ctx.i64_type().const_int(1, false), + "curr_index_plus_one", + ), + "next_index", + )], + "load_index_reversed_list", ) }; - 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( - env, - parent, - comparison, - build_then, - build_else, - BasicTypeEnum::StructType(struct_type), - ) - } - _ => { - unreachable!("Invalid List layout for List.reverse {:?}", list_layout); - } - } + // #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); + + 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>( @@ -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,116 +663,56 @@ 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 ctx = env.context; - let builder = env.builder; + 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 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 - // 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 list_ptr = load_list_ptr(builder, list_wrapper, ptr_type); - 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 ret_list_ptr = allocate_list(env, ret_elem_layout, len); + let call_site_value = + builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func"); - 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); + // set the calling convention explicitly for this call + call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV); - 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 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", - ); - - // 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", - ) + // 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") }; - 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); - } - } + // Mutate the new array in-place to change the element. + builder.build_store(after_elem_ptr, after_elem); + }; + + incrementing_elem_loop( + builder, + parent, + ctx, + LoopListArg { ptr: list_ptr, len }, + "#index", + 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!( @@ -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)); + let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type); + // 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", - ) - }; - + 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>, + 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", + ) +} diff --git a/compiler/gen/tests/gen_list.rs b/compiler/gen/tests/gen_list.rs index 00683035a0..3721e2ecab 100644 --- a/compiler/gen/tests/gen_list.rs +++ b/compiler/gen/tests/gen_list.rs @@ -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 = - [] + empty : List Int + empty = + [] - List.keepIf empty (\x -> True) - main {} + List.keepIf empty (\x -> True) "# ), &[], @@ -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#" - main = \{} -> - nonEmpty : List Int - nonEmpty = - [ 1 ] + empty : List Int + empty = + [] - List.map nonEmpty (\x -> x) - main {} + List.map empty (\x -> x) + "# + ), + &[], + &'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], &'static [i64] ); + } + + #[test] + fn list_map_changes_input() { assert_evals_to!( indoc!( r#" - main = \{} -> - nonEmpty : List Int - nonEmpty = - [ 1 ] + nonEmpty : List Int + nonEmpty = + [ 1 ] - List.map nonEmpty (\x -> x + 1) - main {} + List.map nonEmpty (\x -> x + 1) "# ), &[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 ] - List.map nonEmpty (\x -> x * 2) - 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 ] + + List.map nonEmpty (\x -> x * 2) "# ), - &[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 {} + nonEmpty : List Int + nonEmpty = + [ 1, 1, -4, 1, 2 ] + + + List.map nonEmpty (\x -> x > 0) "# ), &[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 ] - - greaterThanOne : Int -> Bool - greaterThanOne = \i -> - i > 0 - - List.map nonEmpty greaterThanOne - - main {} + nonEmpty : List Int + nonEmpty = + [ 2, 2, -4, 2, 3 ] + + greaterThanOne : Int -> Bool + greaterThanOne = \i -> + i > 1 + + List.map nonEmpty greaterThanOne "# ), &[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 {} + List.map [] (\x -> x > 0) "# ), &[], @@ -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) + + + 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 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() { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 74d844be2d..8f9c4ac3a4 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -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)