mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge branch 'docs-type-aliases' of github.com:rtfeldman/roc into docs-type-aliases
This commit is contained in:
commit
b5656328e6
44 changed files with 961 additions and 197 deletions
|
@ -7,6 +7,7 @@ const Allocator = mem.Allocator;
|
|||
const TAG_WIDTH = 8;
|
||||
|
||||
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
||||
const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8;
|
||||
const Opaque = ?[*]u8;
|
||||
|
||||
const Inc = fn (?[*]u8) callconv(.C) void;
|
||||
|
@ -118,6 +119,59 @@ const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
|||
const Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
|
||||
pub fn listReverse(list: RocList, alignment: usize, element_width: usize) callconv(.C) RocList {
|
||||
if (list.bytes) |source_ptr| {
|
||||
const size = list.len();
|
||||
|
||||
var i: usize = 0;
|
||||
var end: usize = size - 1;
|
||||
|
||||
if (list.isUnique()) {
|
||||
const temp: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, element_width) catch unreachable);
|
||||
|
||||
// Working from the front and back so
|
||||
// we only need to go ~(n / 2) iterations.
|
||||
// If the length is an odd number the middle
|
||||
// element stays in the same place anyways.
|
||||
while (i < (end - i)) : (i += 1) {
|
||||
const last_position = end - i;
|
||||
|
||||
const last_element = source_ptr + (last_position * element_width);
|
||||
const first_element = source_ptr + (i * element_width);
|
||||
|
||||
// Store Last Element in temp
|
||||
@memcpy(temp, last_element, element_width);
|
||||
|
||||
// Swap Last Element with First Element
|
||||
@memcpy(last_element, first_element, element_width);
|
||||
|
||||
// Swap First Element with temp
|
||||
@memcpy(first_element, temp, element_width);
|
||||
}
|
||||
|
||||
std.heap.c_allocator.free(temp[0..element_width]);
|
||||
|
||||
return list;
|
||||
} else {
|
||||
const output = RocList.allocate(std.heap.c_allocator, alignment, size, element_width);
|
||||
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
while (i < size) : (i += 1) {
|
||||
const last_position = end - i;
|
||||
|
||||
@memcpy(target_ptr + (i * element_width), source_ptr + (last_position * element_width), element_width);
|
||||
}
|
||||
|
||||
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width);
|
||||
|
||||
return output;
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listMap(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, old_element_width: usize, new_element_width: usize) callconv(.C) RocList {
|
||||
if (list.bytes) |source_ptr| {
|
||||
const size = list.len();
|
||||
|
@ -688,3 +742,81 @@ fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocLi
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
||||
@memcpy(temporary, ptr1, width);
|
||||
@memcpy(ptr1, ptr2, width);
|
||||
@memcpy(ptr2, temporary, width);
|
||||
}
|
||||
|
||||
fn swap(source_ptr: [*]u8, element_width_initial: usize, index_1: usize, index_2: usize) void {
|
||||
const threshold: comptime usize = 64;
|
||||
|
||||
var element_width = element_width_initial;
|
||||
|
||||
var buffer_actual: [threshold]u8 = undefined;
|
||||
var buffer: [*]u8 = buffer_actual[0..];
|
||||
|
||||
var element_at_i = source_ptr + (index_1 * element_width);
|
||||
var element_at_j = source_ptr + (index_2 * element_width);
|
||||
|
||||
while (true) {
|
||||
if (element_width < threshold) {
|
||||
swapHelp(element_width, buffer, element_at_i, element_at_j);
|
||||
return;
|
||||
} else {
|
||||
swapHelp(threshold, buffer, element_at_i, element_at_j);
|
||||
|
||||
element_at_i += threshold;
|
||||
element_at_j += threshold;
|
||||
|
||||
element_width -= threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize {
|
||||
const pivot = source_ptr + (@intCast(usize, high) * element_width);
|
||||
var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far
|
||||
var j = low;
|
||||
|
||||
while (j <= high - 1) : (j += 1) {
|
||||
const current_elem = source_ptr + (@intCast(usize, j) * element_width);
|
||||
|
||||
const ordering = wrapper(transform, current_elem, pivot);
|
||||
const order = @intToEnum(utils.Ordering, ordering);
|
||||
|
||||
switch (order) {
|
||||
utils.Ordering.LT => {
|
||||
// the current element is smaller than the pivot; swap it
|
||||
i += 1;
|
||||
swap(source_ptr, element_width, @intCast(usize, i), @intCast(usize, j));
|
||||
},
|
||||
utils.Ordering.EQ, utils.Ordering.GT => {},
|
||||
}
|
||||
}
|
||||
swap(source_ptr, element_width, @intCast(usize, i + 1), @intCast(usize, high));
|
||||
return (i + 1);
|
||||
}
|
||||
|
||||
fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void {
|
||||
if (low < high) {
|
||||
// partition index
|
||||
const pi = partition(source_ptr, transform, wrapper, element_width, low, high);
|
||||
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listSortWith(input: RocList, transform: Opaque, wrapper: CompareFn, alignment: usize, element_width: usize) callconv(.C) RocList {
|
||||
var list = input.makeUnique(std.heap.c_allocator, alignment, element_width);
|
||||
|
||||
if (list.bytes) |source_ptr| {
|
||||
const low = 0;
|
||||
const high: isize = @intCast(isize, list.len()) - 1;
|
||||
quicksort(source_ptr, transform, wrapper, element_width, low, high);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ comptime {
|
|||
exportListFn(list.listRepeat, "repeat");
|
||||
exportListFn(list.listAppend, "append");
|
||||
exportListFn(list.listRange, "range");
|
||||
exportListFn(list.listReverse, "reverse");
|
||||
exportListFn(list.listSortWith, "sort_with");
|
||||
}
|
||||
|
||||
// Dict Module
|
||||
|
|
|
@ -157,3 +157,9 @@ pub const RocResult = extern struct {
|
|||
return !self.isOk();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ordering = packed enum(u8) {
|
||||
EQ = 0,
|
||||
GT = 1,
|
||||
LT = 2,
|
||||
};
|
||||
|
|
|
@ -76,3 +76,5 @@ pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
|||
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
|
||||
pub const LIST_APPEND: &str = "roc_builtins.list.append";
|
||||
pub const LIST_RANGE: &str = "roc_builtins.list.range";
|
||||
pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
|
||||
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
||||
|
|
|
@ -958,6 +958,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// sortWith : List a, (a, a -> Ordering) -> List a
|
||||
add_type(
|
||||
Symbol::LIST_SORT_WITH,
|
||||
top_level_function(
|
||||
vec![
|
||||
list_type(flex(TVAR1)),
|
||||
closure(
|
||||
vec![flex(TVAR1), flex(TVAR1)],
|
||||
TVAR2,
|
||||
Box::new(ordering_type()),
|
||||
),
|
||||
],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// Dict module
|
||||
|
||||
// Dict.hashTestOnly : Nat, v -> Nat
|
||||
|
|
|
@ -91,6 +91,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
LIST_WALK => list_walk,
|
||||
LIST_WALK_BACKWARDS => list_walk_backwards,
|
||||
LIST_WALK_UNTIL => list_walk_until,
|
||||
LIST_SORT_WITH => list_sort_with,
|
||||
DICT_TEST_HASH => dict_hash_test_only,
|
||||
DICT_LEN => dict_len,
|
||||
DICT_EMPTY => dict_empty,
|
||||
|
@ -2118,6 +2119,11 @@ fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
lowlevel_4(symbol, LowLevel::ListMap3, var_store)
|
||||
}
|
||||
|
||||
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
|
||||
}
|
||||
|
||||
/// Dict.hashTestOnly : k, v -> Nat
|
||||
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_2(symbol, LowLevel::Hash, var_store)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/// Helpers for interacting with the zig that generates bitcode
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{set_name, Env, FAST_CALL_CONV};
|
||||
use crate::llvm::build::{set_name, Env, C_CALL_CONV, FAST_CALL_CONV};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout, Mode};
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
/// Helpers for interacting with the zig that generates bitcode
|
||||
use inkwell::types::{BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
||||
use inkwell::AddressSpace;
|
||||
|
@ -383,3 +383,101 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
|
|||
|
||||
function_value
|
||||
}
|
||||
|
||||
pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let symbol = Symbol::GENERIC_COMPARE_REF;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
|
||||
let function_value = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.i8_type().into(),
|
||||
&[arg_type.into(), arg_type.into(), arg_type.into()],
|
||||
);
|
||||
|
||||
// we expose this function to zig; must use c calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let function_ptr = it.next().unwrap().into_pointer_value();
|
||||
let value_ptr1 = it.next().unwrap().into_pointer_value();
|
||||
let value_ptr2 = it.next().unwrap().into_pointer_value();
|
||||
|
||||
set_name(
|
||||
function_ptr.into(),
|
||||
Symbol::ARG_1.ident_string(&env.interns),
|
||||
);
|
||||
set_name(value_ptr1.into(), Symbol::ARG_2.ident_string(&env.interns));
|
||||
set_name(value_ptr2.into(), Symbol::ARG_3.ident_string(&env.interns));
|
||||
|
||||
let value_type = basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
let function_type = env
|
||||
.context
|
||||
.i8_type()
|
||||
.fn_type(&[value_type, value_type], false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
let value_ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_cast =
|
||||
env.builder
|
||||
.build_bitcast(function_ptr, function_type, "load_opaque");
|
||||
let value_cast1 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr1, value_ptr_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value_cast2 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr2, value_ptr_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value1 = env.builder.build_load(value_cast1, "load_opaque");
|
||||
let value2 = env.builder.build_load(value_cast2, "load_opaque");
|
||||
|
||||
let call = env.builder.build_call(
|
||||
function_cast.into_pointer_value(),
|
||||
&[value1, value2],
|
||||
"call_user_defined_function",
|
||||
);
|
||||
|
||||
let result = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
// IMPORTANT! we call a user function, so it has the fast calling convention
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
env.builder.build_return(Some(&result));
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::llvm::build_list::{
|
|||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
||||
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map,
|
||||
list_map2, list_map3, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse,
|
||||
list_set, list_single, list_walk_help,
|
||||
list_set, list_single, list_sort_with, list_walk_help,
|
||||
};
|
||||
use crate::llvm::build_str::{
|
||||
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
|
||||
|
@ -3693,7 +3693,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_reverse(env, parent, inplace, list, list_layout)
|
||||
list_reverse(env, inplace, list, list_layout)
|
||||
}
|
||||
ListConcat => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
@ -3949,6 +3949,22 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
list_join(env, inplace, parent, list, outer_list_layout)
|
||||
}
|
||||
ListSortWith => {
|
||||
// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
|
||||
let func = load_symbol(scope, &args[1]);
|
||||
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
|
||||
Layout::Builtin(Builtin::List(_, element_layout)) => {
|
||||
list_sort_with(env, layout_ids, func, list, element_layout)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
|
||||
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use crate::llvm::bitcode::{
|
||||
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, build_transform_caller,
|
||||
call_bitcode_fn, call_void_bitcode_fn,
|
||||
build_compare_wrapper, build_dec_wrapper, build_eq_wrapper, build_inc_wrapper,
|
||||
build_transform_caller, call_bitcode_fn, call_void_bitcode_fn,
|
||||
};
|
||||
use crate::llvm::build::{
|
||||
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, InPlace,
|
||||
|
@ -12,7 +12,6 @@ use crate::llvm::refcounting::{
|
|||
};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::types::{BasicTypeEnum, PointerType};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
|
@ -337,93 +336,14 @@ pub fn list_join<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
/// List.reverse : List elem -> List elem
|
||||
pub fn list_reverse_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
inplace: InPlace,
|
||||
length: IntValue<'ctx>,
|
||||
source_ptr: PointerValue<'ctx>,
|
||||
dest_ptr: PointerValue<'ctx>,
|
||||
) {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
// constant 1i64
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
|
||||
let low_alloca = builder.build_alloca(ctx.i64_type(), "low");
|
||||
let high_alloca = builder.build_alloca(ctx.i64_type(), "high");
|
||||
|
||||
let high_val = builder.build_int_sub(length, one, "subtract 1");
|
||||
|
||||
builder.build_store(low_alloca, ctx.i64_type().const_zero());
|
||||
builder.build_store(high_alloca, high_val);
|
||||
|
||||
// while (high > low)
|
||||
let condition_bb = ctx.append_basic_block(parent, "condition");
|
||||
builder.build_unconditional_branch(condition_bb);
|
||||
builder.position_at_end(condition_bb);
|
||||
|
||||
let high = builder.build_load(high_alloca, "high").into_int_value();
|
||||
let low = builder.build_load(low_alloca, "low").into_int_value();
|
||||
|
||||
// if updating in-place, then the "middle element" can be left untouched
|
||||
// otherwise, the middle element needs to be copied over from the source to the target
|
||||
let predicate = match inplace {
|
||||
InPlace::InPlace => IntPredicate::SGT,
|
||||
InPlace::Clone => IntPredicate::SGE,
|
||||
};
|
||||
|
||||
let condition = builder.build_int_compare(predicate, high, low, "loopcond");
|
||||
|
||||
let body_bb = ctx.append_basic_block(parent, "body");
|
||||
let cont_bb = ctx.append_basic_block(parent, "cont");
|
||||
builder.build_conditional_branch(condition, body_bb, cont_bb);
|
||||
|
||||
// loop body
|
||||
builder.position_at_end(body_bb);
|
||||
|
||||
// assumption: calculating pointer offsets for both the source and target is
|
||||
|
||||
let mut low_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[low], "low_ptr") };
|
||||
let mut high_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[high], "high_ptr") };
|
||||
|
||||
// TODO use memmove?
|
||||
let low_value = builder.build_load(low_ptr, "load_low");
|
||||
let high_value = builder.build_load(high_ptr, "load_high");
|
||||
|
||||
// swap the two values
|
||||
if let InPlace::Clone = inplace {
|
||||
low_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[low], "low_ptr") };
|
||||
high_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[high], "high_ptr") };
|
||||
}
|
||||
|
||||
builder.build_store(high_ptr, low_value);
|
||||
builder.build_store(low_ptr, high_value);
|
||||
|
||||
builder.build_store(low_alloca, builder.build_int_add(low, one, "increment"));
|
||||
builder.build_store(high_alloca, builder.build_int_sub(high, one, "decrement"));
|
||||
|
||||
builder.build_unconditional_branch(condition_bb);
|
||||
|
||||
// continuation
|
||||
builder.position_at_end(cont_bb);
|
||||
}
|
||||
|
||||
/// List.reverse : List elem -> List elem
|
||||
pub fn list_reverse<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
output_inplace: InPlace,
|
||||
_output_inplace: InPlace,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
list_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let wrapper_struct = list.into_struct_value();
|
||||
let (input_inplace, element_layout) = match *list_layout {
|
||||
let (_, element_layout) = match *list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => (
|
||||
InPlace::InPlace,
|
||||
// this pointer will never actually be dereferenced
|
||||
|
@ -440,74 +360,27 @@ pub fn list_reverse<'a, 'ctx, 'env>(
|
|||
_ => unreachable!("Invalid layout {:?} in List.reverse", list_layout),
|
||||
};
|
||||
|
||||
let list_type = basic_type_from_layout(env.arena, env.context, &element_layout, env.ptr_bytes);
|
||||
let ptr_type = list_type.ptr_type(AddressSpace::Generic);
|
||||
let list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128");
|
||||
|
||||
let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
||||
let length = list_len(builder, list.into_struct_value());
|
||||
let element_width = env
|
||||
.ptr_int()
|
||||
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
match input_inplace {
|
||||
InPlace::InPlace => {
|
||||
list_reverse_help(env, parent, input_inplace, length, list_ptr, list_ptr);
|
||||
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||
|
||||
list
|
||||
}
|
||||
let output = call_bitcode_fn(
|
||||
env,
|
||||
&[list_i128, alignment_iv.into(), element_width.into()],
|
||||
&bitcode::LIST_REVERSE,
|
||||
);
|
||||
|
||||
InPlace::Clone => {
|
||||
let len_0_block = ctx.append_basic_block(parent, "len_0_block");
|
||||
let len_1_block = ctx.append_basic_block(parent, "len_1_block");
|
||||
let len_n_block = ctx.append_basic_block(parent, "len_n_block");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont_block");
|
||||
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
let zero = ctx.i64_type().const_zero();
|
||||
|
||||
let result = builder.build_alloca(ptr_type, "result");
|
||||
|
||||
builder.build_switch(
|
||||
length,
|
||||
len_n_block,
|
||||
&[(zero, len_0_block), (one, len_1_block)],
|
||||
);
|
||||
|
||||
// build block for length 0
|
||||
{
|
||||
builder.position_at_end(len_0_block);
|
||||
|
||||
// store NULL pointer there
|
||||
builder.build_store(result, ptr_type.const_zero());
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
// build block for length 1
|
||||
{
|
||||
builder.position_at_end(len_1_block);
|
||||
|
||||
let new_list_ptr = clone_list(env, output_inplace, &element_layout, one, list_ptr);
|
||||
|
||||
builder.build_store(result, new_list_ptr);
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
// build block for length > 1
|
||||
{
|
||||
builder.position_at_end(len_n_block);
|
||||
|
||||
let new_list_ptr = allocate_list(env, output_inplace, &element_layout, length);
|
||||
|
||||
list_reverse_help(env, parent, InPlace::Clone, length, list_ptr, new_list_ptr);
|
||||
|
||||
// store new list pointer there
|
||||
builder.build_store(result, new_list_ptr);
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
builder.position_at_end(cont_block);
|
||||
let new_list_ptr = builder.build_load(result, "result").into_pointer_value();
|
||||
|
||||
store_list(env, new_list_ptr, length)
|
||||
}
|
||||
}
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
output,
|
||||
collection(env.context, env.ptr_bytes).into(),
|
||||
"from_i128",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
||||
|
@ -1132,6 +1005,52 @@ pub fn list_keep_result<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
pub fn list_sort_with<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
transform: BasicValueEnum<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128");
|
||||
|
||||
let transform_ptr = transform.into_pointer_value();
|
||||
|
||||
let compare_wrapper = build_compare_wrapper(env, layout_ids, element_layout)
|
||||
.as_global_value()
|
||||
.as_pointer_value();
|
||||
|
||||
let element_width = env
|
||||
.ptr_int()
|
||||
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||
|
||||
let output = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_i128,
|
||||
env.builder
|
||||
.build_bitcast(transform_ptr, u8_ptr, "to_opaque"),
|
||||
compare_wrapper.into(),
|
||||
alignment_iv.into(),
|
||||
element_width.into(),
|
||||
],
|
||||
bitcode::LIST_SORT_WITH,
|
||||
);
|
||||
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
output,
|
||||
collection(env.context, env.ptr_bytes).into(),
|
||||
"from_i128",
|
||||
)
|
||||
}
|
||||
|
||||
/// List.map : List before, (before -> after) -> List after
|
||||
pub fn list_map<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
|
|
@ -37,6 +37,7 @@ pub enum LowLevel {
|
|||
ListWalkBackwards,
|
||||
ListKeepOks,
|
||||
ListKeepErrs,
|
||||
ListSortWith,
|
||||
DictSize,
|
||||
DictEmpty,
|
||||
DictInsert,
|
||||
|
|
|
@ -756,6 +756,9 @@ define_builtins! {
|
|||
|
||||
// A caller (wrapper) that we pass to zig for it to be able to call Roc functions
|
||||
20 ZIG_FUNCTION_CALLER: "#zig_function_caller"
|
||||
|
||||
// a caller (wrapper) for comparison
|
||||
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
||||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
|
@ -919,6 +922,7 @@ define_builtins! {
|
|||
28 LIST_PRODUCT_MUL: "#productmul"
|
||||
29 LIST_WALK_UNTIL: "walkUntil"
|
||||
30 LIST_RANGE: "range"
|
||||
31 LIST_SORT_WITH: "sortWith"
|
||||
}
|
||||
5 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||
|
|
|
@ -659,6 +659,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
ListWalk | ListWalkUntil | ListWalkBackwards => {
|
||||
arena.alloc_slice_copy(&[owned, irrelevant, owned])
|
||||
}
|
||||
ListSortWith => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||
|
||||
// TODO when we have lists with capacity (if ever)
|
||||
// List.append should own its first argument
|
||||
|
|
|
@ -1839,19 +1839,30 @@ fn cleanup_because_exception() {
|
|||
|
||||
#[test]
|
||||
fn list_range() {
|
||||
assert_evals_to!("List.range 0 -1", RocList::from_slice(&[]), RocList<i64>);
|
||||
assert_evals_to!("List.range 0 0", RocList::from_slice(&[0]), RocList<i64>);
|
||||
assert_evals_to!(
|
||||
indoc!("List.range 0 -1"),
|
||||
RocList::from_slice(&[]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("List.range 0 0"),
|
||||
RocList::from_slice(&[0]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("List.range 0 5"),
|
||||
"List.range 0 5",
|
||||
RocList::from_slice(&[0, 1, 2, 3, 4]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_sort_with() {
|
||||
assert_evals_to!(
|
||||
"List.sortWith [] Num.compare",
|
||||
RocList::from_slice(&[]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sortWith [ 4,3,2,1 ] Num.compare",
|
||||
RocList::from_slice(&[1, 2, 3, 4]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sortWith [ 1,2,3,4] (\\a,b -> Num.compare b a)",
|
||||
RocList::from_slice(&[4, 3, 2, 1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue