Merge branch 'trunk' of github.com:rtfeldman/roc into wasm_module_builder

This commit is contained in:
Brian Carroll 2021-11-02 23:02:00 +00:00
commit 6fca1caee5
38 changed files with 3040 additions and 2427 deletions

View file

@ -82,7 +82,7 @@ There are also alternative installation options at http://releases.llvm.org/down
## Using Nix ## Using Nix
:exclamation: **Our Nix setup is currently broken, you'll have to install manually for now** :exclamation: :exclamation: **Our Nix setup is not yet working on MacOS, you'll have to install manually for now** :exclamation:
### Install ### Install
@ -94,7 +94,7 @@ First, install nix:
`curl -L https://nixos.org/nix/install | sh` `curl -L https://nixos.org/nix/install | sh`
If MacOS and using a version >= 10.15: If you're on MacOS and using a OS version >= 10.15:
`sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume` `sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume`
@ -104,7 +104,7 @@ You may prefer to setup up the volume manually by following nix documentation.
### Usage ### Usage
Now with nix installed you just need to run one command: Now with nix installed, you just need to run one command:
`nix-shell` `nix-shell`
@ -120,33 +120,49 @@ You should be in a repl now. Have fun!
### Extra tips ### Extra tips
If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/target/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependecies into your current shell, so you never have to run nix-shell directly! If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/nix-community/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependecies into your current shell, so you never have to run nix-shell directly!
### Editor ### Editor
When you want to run the editor from Ubuntu inside nix you need to install [nixGL](https://github.com/guibou/nixGL) as well: `cargo run edit` should work from NixOS, if you use a nix-shell from inside another OS, follow the instructions below.
#### Nvidia GPU
Outside of a nix shell, execute the following:
```
nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
nix-env -iA nixgl.auto.nixVulkanNvidia
```
Running the editor does not work with `nix-shell --pure`.
```
nix-shell
```
460.91.03 may be different for you, type nixVulkanNvidia and press tab to autocomplete for your version.
```
nixVulkanNvidia-460.91.03 cargo run edit
```
#### Integrated Intel Graphics
:exclamation: ** Our Nix setup currently cannot run the editor with integrated intel graphics, see #1856 ** :exclamation:
Outside of a nix shell, run:
```bash ```bash
nix-shell
git clone https://github.com/guibou/nixGL git clone https://github.com/guibou/nixGL
cd nixGL cd nixGL
```
If you have an Nvidia graphics card, run:
```
nix-env -f ./ -iA nixVulkanNvidia
```
If you have integrated Intel graphics, run:
```
nix-env -f ./ -iA nixVulkanIntel nix-env -f ./ -iA nixVulkanIntel
``` ```
Check the [nixGL repo](https://github.com/guibou/nixGL) for other configurations.
Now you should be able to run the editor: cd to the roc repo, and run (without --pure):
```bash
cd roc
nixVulkanNvidia cargo run edit `# replace Nvidia with the config you chose in the previous step`
``` ```
nix-shell
nixVulkanIntel cargo run edit
```
#### Other configs
Check the [nixGL repo](https://github.com/guibou/nixGL) for other graphics configurations.
## Troubleshooting ## Troubleshooting

23
Cargo.lock generated
View file

@ -4748,6 +4748,29 @@ dependencies = [
"syn 1.0.76", "syn 1.0.76",
] ]
[[package]]
name = "test_wasm"
version = "0.1.0"
dependencies = [
"bumpalo",
"indoc 0.3.6",
"libc",
"parity-wasm",
"pretty_assertions 0.5.1",
"roc_builtins",
"roc_can",
"roc_collections",
"roc_gen_wasm",
"roc_load",
"roc_module",
"roc_std",
"roc_types",
"target-lexicon",
"tempfile",
"wasmer",
"wasmer-wasi",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"

View file

@ -24,6 +24,7 @@ members = [
"compiler/build", "compiler/build",
"compiler/arena_pool", "compiler/arena_pool",
"compiler/test_gen", "compiler/test_gen",
"compiler/test_wasm",
"vendor/ena", "vendor/ena",
"vendor/inkwell", "vendor/inkwell",
"vendor/pathfinding", "vendor/pathfinding",

View file

@ -1,4 +1,4 @@
FROM rust:1.54-slim-bullseye FROM rust:1.56.1-slim-bullseye
WORKDIR /earthbuild WORKDIR /earthbuild
prep-debian: prep-debian:

View file

@ -362,7 +362,7 @@ mod cli_run {
stdin: &[], stdin: &[],
input_file: Some("examples/hello.false"), input_file: Some("examples/hello.false"),
expected_ending:"Hello, World!\n", expected_ending:"Hello, World!\n",
use_valgrind: false, use_valgrind: true,
} }
}, },
} }

View file

@ -140,6 +140,7 @@ const Caller0 = fn (?[*]u8, ?[*]u8) callconv(.C) void;
const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller2 = fn (?[*]u8, ?[*]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; const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller4 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
pub fn listReverse(list: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList { pub fn listReverse(list: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList {
if (list.bytes) |source_ptr| { if (list.bytes) |source_ptr| {
@ -352,6 +353,70 @@ pub fn listMap3(
} }
} }
pub fn listMap4(
list1: RocList,
list2: RocList,
list3: RocList,
list4: RocList,
caller: Caller4,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
a_width: usize,
b_width: usize,
c_width: usize,
d_width: usize,
e_width: usize,
dec_a: Dec,
dec_b: Dec,
dec_c: Dec,
dec_d: Dec,
) callconv(.C) RocList {
const output_length = std.math.min(std.math.min(list1.len(), list2.len()), std.math.min(list3.len(), list4.len()));
decrementTail(list1, output_length, a_width, dec_a);
decrementTail(list2, output_length, b_width, dec_b);
decrementTail(list3, output_length, c_width, dec_c);
decrementTail(list4, output_length, d_width, dec_d);
if (data_is_owned) {
inc_n_data(data, output_length);
}
if (list1.bytes) |source_a| {
if (list2.bytes) |source_b| {
if (list3.bytes) |source_c| {
if (list4.bytes) |source_d| {
const output = RocList.allocate(alignment, output_length, e_width);
const target_ptr = output.bytes orelse unreachable;
var i: usize = 0;
while (i < output_length) : (i += 1) {
const element_a = source_a + i * a_width;
const element_b = source_b + i * b_width;
const element_c = source_c + i * c_width;
const element_d = source_d + i * d_width;
const target = target_ptr + i * e_width;
caller(data, element_a, element_b, element_c, element_d, target);
}
return output;
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
}
pub fn listKeepIf( pub fn listKeepIf(
list: RocList, list: RocList,
caller: Caller1, caller: Caller1,

View file

@ -26,6 +26,7 @@ comptime {
exportListFn(list.listMap, "map"); exportListFn(list.listMap, "map");
exportListFn(list.listMap2, "map2"); exportListFn(list.listMap2, "map2");
exportListFn(list.listMap3, "map3"); exportListFn(list.listMap3, "map3");
exportListFn(list.listMap4, "map4");
exportListFn(list.listMapWithIndex, "map_with_index"); exportListFn(list.listMapWithIndex, "map_with_index");
exportListFn(list.listKeepIf, "keep_if"); exportListFn(list.listKeepIf, "keep_if");
exportListFn(list.listWalk, "walk"); exportListFn(list.listWalk, "walk");

View file

@ -25,6 +25,7 @@ interface List
map, map,
map2, map2,
map3, map3,
map4,
mapWithIndex, mapWithIndex,
mapOrDrop, mapOrDrop,
mapJoin, mapJoin,
@ -269,6 +270,11 @@ map2 : List a, List b, (a, b -> c) -> List c
## Repeat until a list runs out of elements. ## Repeat until a list runs out of elements.
map3 : List a, List b, List c, (a, b, c -> d) -> List d map3 : List a, List b, List c, (a, b, c -> d) -> List d
## Run a transformation function on the first element of each list,
## and use that as the first element in the returned list.
## Repeat until a list runs out of elements.
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
## This works like [List.map], except it also passes the index ## This works like [List.map], except it also passes the index
## of the element to the conversion function. ## of the element to the conversion function.
mapWithIndex : List before, (before, Nat -> after) -> List after mapWithIndex : List before, (before, Nat -> after) -> List after

View file

@ -165,6 +165,7 @@ pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list";
pub const LIST_MAP: &str = "roc_builtins.list.map"; pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2"; pub const LIST_MAP2: &str = "roc_builtins.list.map2";
pub const LIST_MAP3: &str = "roc_builtins.list.map3"; pub const LIST_MAP3: &str = "roc_builtins.list.map3";
pub const LIST_MAP4: &str = "roc_builtins.list.map4";
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index"; pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if"; pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if";
pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks"; pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks";

View file

@ -64,7 +64,7 @@ const TVAR1: VarId = VarId::from_u32(1);
const TVAR2: VarId = VarId::from_u32(2); const TVAR2: VarId = VarId::from_u32(2);
const TVAR3: VarId = VarId::from_u32(3); const TVAR3: VarId = VarId::from_u32(3);
const TVAR4: VarId = VarId::from_u32(4); const TVAR4: VarId = VarId::from_u32(4);
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(5); const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(10);
pub fn types() -> MutMap<Symbol, (SolvedType, Region)> { pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
let mut types = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher()); let mut types = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
@ -930,6 +930,27 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
) )
}; };
{
let_tvars! {a, b, c, d, e, cvar};
// map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
add_top_level_function_type!(
Symbol::LIST_MAP4,
vec![
list_type(flex(a)),
list_type(flex(b)),
list_type(flex(c)),
list_type(flex(d)),
closure(
vec![flex(a), flex(b), flex(c), flex(d)],
cvar,
Box::new(flex(e))
),
],
Box::new(list_type(flex(e))),
)
};
// append : List elem, elem -> List elem // append : List elem, elem -> List elem
add_top_level_function_type!( add_top_level_function_type!(
Symbol::LIST_APPEND, Symbol::LIST_APPEND,

View file

@ -89,6 +89,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_MAP => list_map, LIST_MAP => list_map,
LIST_MAP2 => list_map2, LIST_MAP2 => list_map2,
LIST_MAP3 => list_map3, LIST_MAP3 => list_map3,
LIST_MAP4 => list_map4,
LIST_DROP => list_drop, LIST_DROP => list_drop,
LIST_DROP_AT => list_drop_at, LIST_DROP_AT => list_drop_at,
LIST_DROP_LAST => list_drop_last, LIST_DROP_LAST => list_drop_last,
@ -290,6 +291,41 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
) )
} }
fn lowlevel_5(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
let arg1_var = var_store.fresh();
let arg2_var = var_store.fresh();
let arg3_var = var_store.fresh();
let arg4_var = var_store.fresh();
let arg5_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = RunLowLevel {
op,
args: vec![
(arg1_var, Var(Symbol::ARG_1)),
(arg2_var, Var(Symbol::ARG_2)),
(arg3_var, Var(Symbol::ARG_3)),
(arg4_var, Var(Symbol::ARG_4)),
(arg5_var, Var(Symbol::ARG_5)),
],
ret_var,
};
defn(
symbol,
vec![
(arg1_var, Symbol::ARG_1),
(arg2_var, Symbol::ARG_2),
(arg3_var, Symbol::ARG_3),
(arg4_var, Symbol::ARG_4),
(arg5_var, Symbol::ARG_5),
],
var_store,
body,
ret_var,
)
}
/// Num.maxInt : Int /// Num.maxInt : Int
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh(); let int_var = var_store.fresh();
@ -2541,11 +2577,16 @@ fn list_map2(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_3(symbol, LowLevel::ListMap2, var_store) lowlevel_3(symbol, LowLevel::ListMap2, var_store)
} }
/// List.map3 : List a, List b, (a, b -> c) -> List c /// List.map3 : List a, List b, List c, (a, b, c -> d) -> List d
fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_4(symbol, LowLevel::ListMap3, var_store) lowlevel_4(symbol, LowLevel::ListMap3, var_store)
} }
/// List.map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
fn list_map4(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_5(symbol, LowLevel::ListMap4, var_store)
}
/// List.sortWith : List a, (a, a -> Ordering) -> List a /// List.sortWith : List a, (a, a -> Ordering) -> List a
fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListSortWith, var_store) lowlevel_2(symbol, LowLevel::ListSortWith, var_store)

View file

@ -830,8 +830,9 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
fields: &'a [Symbol], fields: &'a [Symbol],
) -> Result<(), String> { ) -> Result<(), String> {
let struct_size = layout.stack_size(PTR_SIZE);
if let Layout::Struct(field_layouts) = layout { if let Layout::Struct(field_layouts) = layout {
let struct_size = layout.stack_size(PTR_SIZE);
if struct_size > 0 { if struct_size > 0 {
let offset = self.claim_stack_size(struct_size)?; let offset = self.claim_stack_size(struct_size)?;
self.symbol_storage_map.insert( self.symbol_storage_map.insert(
@ -862,7 +863,6 @@ impl<
Ok(()) Ok(())
} else { } else {
// This is a single element struct. Just copy the single field to the stack. // This is a single element struct. Just copy the single field to the stack.
let struct_size = layout.stack_size(PTR_SIZE);
let offset = self.claim_stack_size(struct_size)?; let offset = self.claim_stack_size(struct_size)?;
self.symbol_storage_map.insert( self.symbol_storage_map.insert(
*sym, *sym,

View file

@ -10,9 +10,9 @@ use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{ use crate::llvm::build_list::{
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs, list_contains, list_drop, list_drop_at, 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_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
list_prepend, list_range, list_repeat, list_reverse, list_set, list_single, list_sort_with, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
list_swap, list_single, list_sort_with, list_swap,
}; };
use crate::llvm::build_str::{ use crate::llvm::build_str::{
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
@ -1453,7 +1453,33 @@ pub fn build_tag<'a, 'ctx, 'env>(
UnionLayout::NonRecursive(tags) => { UnionLayout::NonRecursive(tags) => {
debug_assert!(union_size > 1); debug_assert!(union_size > 1);
let ctx = env.context; let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes);
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
let wrapper_type = env
.context
.struct_type(&[internal_type, tag_id_type.into()], false);
let result_alloca = env.builder.build_alloca(wrapper_type, "tag_opaque");
// Initialize all memory of the alloca. This _should_ not be required, but currently
// LLVM can access uninitialized memory after applying some optimizations. Hopefully
// we can in the future adjust code gen so this is no longer an issue.
//
// An example is
//
// main : Task.Task {} []
// main =
// when List.len [ Ok "foo", Err 42, Ok "spam" ] is
// n -> Task.putLine (Str.fromInt n)
//
// Here the decrement function of result must first check it's an Ok tag,
// then defers to string decrement, which must check is the string is small or large
//
// After inlining, those checks are combined. That means that even if the tag is Err,
// a check is done on the "string" to see if it is big or small, which will touch the
// uninitialized memory.
let all_zeros = wrapper_type.const_zero();
env.builder.build_store(result_alloca, all_zeros);
// Determine types // Determine types
let num_fields = arguments.len() + 1; let num_fields = arguments.len() + 1;
@ -1484,54 +1510,46 @@ pub fn build_tag<'a, 'ctx, 'env>(
} }
} }
} }
// store the tag id
// Create the struct_type let tag_id_ptr = env
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); .builder
.build_struct_gep(result_alloca, TAG_ID_INDEX, "get_opaque_data")
// Insert field exprs into struct_val .unwrap();
let struct_val =
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
// How we create tag values
//
// The memory layout of tags can be different. e.g. in
//
// [ Ok Int, Err Str ]
//
// the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct.
// All tags of a union must have the same length, for easy addressing (e.g. array lookups).
// So we need to ask for the maximum of all tag's sizes, even if most tags won't use
// all that memory, and certainly won't use it in the same way (the tags have fields of
// different types/sizes)
//
// In llvm, we must be explicit about the type of value we're creating: we can't just
// make a unspecified block of memory. So what we do is create a byte array of the
// desired size. Then when we know which tag we have (which is here, in this function),
// we need to cast that down to the array of bytes that llvm expects
//
// There is the bitcast instruction, but it doesn't work for arrays. So we need to jump
// through some hoops using store and load to get this to work: the array is put into a
// one-element struct, which can be cast to the desired type.
//
// This tricks comes from
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes);
let data = cast_tag_to_block_of_memory(env, struct_val, internal_type);
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
let wrapper_type = env
.context
.struct_type(&[data.get_type(), tag_id_type.into()], false);
let tag_id_intval = tag_id_type.const_int(tag_id as u64, false); let tag_id_intval = tag_id_type.const_int(tag_id as u64, false);
env.builder.build_store(tag_id_ptr, tag_id_intval);
let field_vals = [ // Create the struct_type
(TAG_DATA_INDEX as usize, data), let struct_type = env
(TAG_ID_INDEX as usize, tag_id_intval.into()), .context
]; .struct_type(field_types.into_bump_slice(), false);
struct_from_fields(env, wrapper_type, field_vals.iter().copied()).into() let struct_opaque_ptr = env
.builder
.build_struct_gep(result_alloca, TAG_DATA_INDEX, "get_opaque_data")
.unwrap();
let struct_ptr = env.builder.build_pointer_cast(
struct_opaque_ptr,
struct_type.ptr_type(AddressSpace::Generic),
"to_specific",
);
// Insert field exprs into struct_val
//let struct_val =
//struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
// Insert field exprs into struct_val
for (index, field_val) in field_vals.iter().copied().enumerate() {
let index: u32 = index as u32;
let ptr = env
.builder
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
.unwrap();
env.builder.build_store(ptr, field_val);
}
env.builder.build_load(result_alloca, "load_result")
} }
UnionLayout::Recursive(tags) => { UnionLayout::Recursive(tags) => {
debug_assert!(union_size > 1); debug_assert!(union_size > 1);
@ -2653,14 +2671,6 @@ pub fn complex_bitcast_struct_struct<'ctx>(
complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value() complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value()
} }
fn cast_tag_to_block_of_memory<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
from_value: StructValue<'ctx>,
to_type: BasicTypeEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
complex_bitcast_check_size(env, from_value.into(), to_type, "tag_to_block_of_memory")
}
pub fn cast_block_of_memory_to_tag<'ctx>( pub fn cast_block_of_memory_to_tag<'ctx>(
builder: &Builder<'ctx>, builder: &Builder<'ctx>,
from_value: StructValue<'ctx>, from_value: StructValue<'ctx>,
@ -4609,6 +4619,67 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
_ => unreachable!("invalid list layout"), _ => unreachable!("invalid list layout"),
} }
} }
ListMap4 { xs, ys, zs, ws } => {
let (list1, list1_layout) = load_symbol_and_layout(scope, &xs);
let (list2, list2_layout) = load_symbol_and_layout(scope, &ys);
let (list3, list3_layout) = load_symbol_and_layout(scope, &zs);
let (list4, list4_layout) = load_symbol_and_layout(scope, &ws);
let (function, closure, closure_layout) = function_details!();
match (
list1_layout,
list2_layout,
list3_layout,
list4_layout,
return_layout,
) {
(
Layout::Builtin(Builtin::List(element1_layout)),
Layout::Builtin(Builtin::List(element2_layout)),
Layout::Builtin(Builtin::List(element3_layout)),
Layout::Builtin(Builtin::List(element4_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[
**element1_layout,
**element2_layout,
**element3_layout,
**element4_layout,
];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_map4(
env,
layout_ids,
roc_function_call,
list1,
list2,
list3,
list4,
element1_layout,
element2_layout,
element3_layout,
element4_layout,
result_layout,
)
}
(Layout::Builtin(Builtin::EmptyList), _, _, _, _)
| (_, Layout::Builtin(Builtin::EmptyList), _, _, _)
| (_, _, Layout::Builtin(Builtin::EmptyList), _, _)
| (_, _, _, Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
// List.mapWithIndex : List before, (Nat, before -> after) -> List after // List.mapWithIndex : List before, (Nat, before -> after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, &xs); let (list, list_layout) = load_symbol_and_layout(scope, &xs);
@ -5640,7 +5711,7 @@ fn run_low_level<'a, 'ctx, 'env>(
cond cond
} }
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"), | DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
} }

View file

@ -821,6 +821,51 @@ pub fn list_map3<'a, 'ctx, 'env>(
) )
} }
pub fn list_map4<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
roc_function_call: RocFunctionCall<'ctx>,
list1: BasicValueEnum<'ctx>,
list2: BasicValueEnum<'ctx>,
list3: BasicValueEnum<'ctx>,
list4: BasicValueEnum<'ctx>,
element1_layout: &Layout<'a>,
element2_layout: &Layout<'a>,
element3_layout: &Layout<'a>,
element4_layout: &Layout<'a>,
result_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let dec_a = build_dec_wrapper(env, layout_ids, element1_layout);
let dec_b = build_dec_wrapper(env, layout_ids, element2_layout);
let dec_c = build_dec_wrapper(env, layout_ids, element3_layout);
let dec_d = build_dec_wrapper(env, layout_ids, element4_layout);
call_bitcode_fn_returns_list(
env,
&[
pass_list_cc(env, list1),
pass_list_cc(env, list2),
pass_list_cc(env, list3),
pass_list_cc(env, list4),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(result_layout),
layout_width(env, element1_layout),
layout_width(env, element2_layout),
layout_width(env, element3_layout),
layout_width(env, element4_layout),
layout_width(env, result_layout),
dec_a.as_global_value().as_pointer_value().into(),
dec_b.as_global_value().as_pointer_value().into(),
dec_c.as_global_value().as_pointer_value().into(),
dec_d.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_MAP4,
)
}
/// List.concat : List elem, List elem -> List elem /// List.concat : List elem, List elem -> List elem
pub fn list_concat<'a, 'ctx, 'env>( pub fn list_concat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
@ -965,6 +1010,8 @@ where
let ctx = env.context; let ctx = env.context;
let builder = env.builder; let builder = env.builder;
let entry = env.builder.get_insert_block().unwrap();
// constant 1i64 // constant 1i64
let one = env.ptr_int().const_int(1, false); let one = env.ptr_int().const_int(1, false);
@ -976,15 +1023,15 @@ where
builder.build_unconditional_branch(loop_bb); builder.build_unconditional_branch(loop_bb);
builder.position_at_end(loop_bb); builder.position_at_end(loop_bb);
let curr_index = builder let current_index_phi = env.builder.build_phi(env.ptr_int(), "current_index");
.build_load(index_alloca, index_name) let current_index = current_index_phi.as_basic_value().into_int_value();
.into_int_value();
let next_index = builder.build_int_add(curr_index, one, "nextindex");
builder.build_store(index_alloca, next_index); let next_index = builder.build_int_add(current_index, one, "next_index");
current_index_phi.add_incoming(&[(&next_index, loop_bb), (&env.ptr_int().const_zero(), entry)]);
// The body of the loop // The body of the loop
loop_fn(curr_index); loop_fn(current_index);
// #index < end // #index < end
let loop_end_cond = bounds_check_comparison(builder, next_index, end); let loop_end_cond = bounds_check_comparison(builder, next_index, end);

File diff suppressed because it is too large Load diff

View file

@ -1,916 +0,0 @@
#[macro_use]
extern crate indoc;
#[macro_use]
mod helpers;
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
mod wasm_records {
// #[test]
// fn basic_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { y: 17, x: 15, z: 19 }.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: 17, z: 19 }.y
// "#
// ),
// 17,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: 17, z: 19 }.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn nested_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
// "#
// ),
// 12,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
// "#
// ),
// 2,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn f64_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.x
// "#
// ),
// 15.1,
// f64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.y
// "#
// ),
// 17.2,
// f64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.z
// "#
// ),
// 19.3,
// f64
// );
// }
// #[test]
// fn fn_record() {
// assert_evals_to!(
// indoc!(
// r#"
// getRec = \x -> { y: 17, x, z: 19 }
// (getRec 15).x
// "#
// ),
// 15,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.y
// "#
// ),
// 17,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z
// "#
// ),
// 19,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z + rec.x
// "#
// ),
// 34,
// i64
// );
// }
// #[test]
// fn def_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17, x: 15, z: 19 }
//
// rec.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
//
// rec.y
// "#
// ),
// 17,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
//
// rec.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn when_on_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2 } is
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn when_record_with_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: var } -> var + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn let_with_record_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// { x } = { x: 0x2, y: 3.14 }
//
// x
// "#
// ),
// 2,
// i64
// );
// }
//
// #[test]
// fn record_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: 0x4 } -> 5
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn twice_record_access() {
// assert_evals_to!(
// indoc!(
// r#"
// x = {a: 0x2, b: 0x3 }
//
// x.a + x.b
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// v = {}
//
// v
// "#
// ),
// (),
// ()
// );
// }
#[test]
fn i64_record1_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3 }
"#
),
3,
i64
);
}
#[test]
fn i64_record2_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5 }
"#
),
(3, 5),
(i64, i64)
);
}
#[test]
fn i64_record3_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5, z: 17 }
"#
),
(3, 5, 17),
(i64, i64, i64)
);
}
#[test]
fn f64_record2_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3.1, y: 5.1 }
"#
),
(3.1, 5.1),
(f64, f64)
);
}
#[test]
fn f64_record3_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3.1, y: 5.1, z: 17.1 }
"#
),
(3.1, 5.1, 17.1),
(f64, f64, f64)
);
}
#[test]
fn bool_record4_literal() {
assert_evals_to!(
indoc!(
r#"
record : { a : Bool, b : Bool, c : Bool, d : Bool }
record = { a: True, b: False, c : False, d : True }
record
"#
),
[true, false, false, true],
[bool; 4]
);
}
#[test]
fn i64_record9_literal() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
"#
),
[3, 5, 17, 1, 9, 12, 13, 14, 15],
[i64; 9]
);
}
#[test]
fn bool_literal() {
assert_evals_to!(
indoc!(
r#"
x : Bool
x = True
x
"#
),
true,
bool
);
}
// #[test]
// fn optional_field_when_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// main =
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_function_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \{ x ? 10, y } -> x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default_nested() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_singleton_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x : 4 } is
// { x ? 3 } -> x
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn optional_field_empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { } is
// { x ? 3 } -> x
// "#
// ),
// 3,
// i64
// );
// }
#[test]
fn return_record_3() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5, z: 4 }
"#
),
(3, 5, 4),
(i64, i64, i64)
);
}
#[test]
fn return_record_4() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2 }
"#
),
[3, 5, 4, 2],
[i64; 4]
);
}
#[test]
fn return_record_5() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
"#
),
[3, 5, 4, 2, 1],
[i64; 5]
);
}
#[test]
fn return_record_6() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
"#
),
[3, 5, 4, 2, 1, 7],
[i64; 6]
);
}
#[test]
fn return_record_7() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
"#
),
[3, 5, 4, 2, 1, 7, 8],
[i64; 7]
);
}
#[test]
fn return_record_float_int() {
assert_evals_to!(
indoc!(
r#"
{ a: 3.14, b: 0x1 }
"#
),
(3.14, 0x1),
(f64, i64)
);
}
#[test]
fn return_record_int_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 0x1, b: 3.14 }
"#
),
(0x1, 3.14),
(i64, f64)
);
}
#[test]
fn return_record_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14 }
"#
),
(6.28, 3.14),
(f64, f64)
);
}
#[test]
fn return_record_float_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14, c: 0.1 }
"#
),
(6.28, 3.14, 0.1),
(f64, f64, f64)
);
}
// #[test]
// fn return_nested_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
// "#
// ),
// (0x0, (6.28, 3.14, 0.1)),
// (i64, (f64, f64, f64))
// );
// }
// #[test]
// fn accessor() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn accessor_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 }
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn update_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42, bar: 6 }
// { rec & foo: rec.foo + 1 }
// "#
// ),
// (6, 43),
// (i64, i64)
// );
// }
// #[test]
// fn update_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42}
// { rec & foo: rec.foo + 1 }
// "#
// ),
// 43,
// i64
// );
// }
// #[test]
// fn booleans_in_record() {
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
// (true, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
// (false, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
// (true, false),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
// (false, false),
// (bool, bool)
// );
// }
// #[test]
// fn alignment_in_record() {
// assert_evals_to!(
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
// (32i64, true, 2u8),
// (i64, bool, u8)
// );
// }
#[test]
fn stack_memory_return_from_branch() {
// stack memory pointer should end up in the right place after returning from a branch
assert_evals_to!(
indoc!(
r#"
stackMemoryJunk = { x: 999, y: 111 }
if True then
{ x: 123, y: 321 }
else
stackMemoryJunk
"#
),
(123, 321),
(i64, i64)
);
}
// #[test]
// fn blue_and_present() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue, y: 7 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn blue_and_absent() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue }
// "#
// ),
// 3,
// i64
// );
// }
}

View file

@ -1004,7 +1004,7 @@ impl ModuleTiming {
.checked_sub(*read_roc_file) .checked_sub(*read_roc_file)
}; };
calculate(end_time.duration_since(*start_time)).unwrap_or_else(Duration::default) calculate(end_time.duration_since(*start_time)).unwrap_or_default()
} }
} }

View file

@ -33,6 +33,7 @@ pub enum LowLevel {
ListMap, ListMap,
ListMap2, ListMap2,
ListMap3, ListMap3,
ListMap4,
ListMapWithIndex, ListMapWithIndex,
ListKeepIf, ListKeepIf,
ListWalk, ListWalk,
@ -211,6 +212,7 @@ macro_rules! higher_order {
ListMap ListMap
| ListMap2 | ListMap2
| ListMap3 | ListMap3
| ListMap4
| ListMapWithIndex | ListMapWithIndex
| ListKeepIf | ListKeepIf
| ListWalk | ListWalk
@ -243,6 +245,7 @@ impl LowLevel {
ListMap => 1, ListMap => 1,
ListMap2 => 2, ListMap2 => 2,
ListMap3 => 3, ListMap3 => 3,
ListMap4 => 4,
ListMapWithIndex => 1, ListMapWithIndex => 1,
ListKeepIf => 1, ListKeepIf => 1,
ListWalk => 2, ListWalk => 2,

View file

@ -1060,6 +1060,7 @@ define_builtins! {
37 LIST_MIN_LT: "#minlt" 37 LIST_MIN_LT: "#minlt"
38 LIST_MAX: "max" 38 LIST_MAX: "max"
39 LIST_MAX_GT: "#maxGt" 39 LIST_MAX_GT: "#maxGt"
40 LIST_MAP4: "map4"
} }
5 RESULT: "Result" => { 5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias 0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -561,6 +561,84 @@ fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Resul
builder.add_tuple_type(&field_types) builder.add_tuple_type(&field_types)
} }
#[repr(u32)]
#[derive(Clone, Copy)]
enum KeepResult {
Errs = ERR_TAG_ID,
Oks = OK_TAG_ID,
}
impl KeepResult {
fn invert(&self) -> Self {
match self {
KeepResult::Errs => KeepResult::Oks,
KeepResult::Oks => KeepResult::Errs,
}
}
}
#[derive(Clone, Copy)]
enum ResultRepr<'a> {
Int1,
NonRecursive { err: Layout<'a>, ok: Layout<'a> },
}
impl<'a> ResultRepr<'a> {
fn from_layout(layout: &Layout<'a>) -> Self {
match layout {
Layout::Union(UnionLayout::NonRecursive(tags)) => ResultRepr::NonRecursive {
err: tags[ERR_TAG_ID as usize][0],
ok: tags[OK_TAG_ID as usize][0],
},
Layout::Builtin(Builtin::Int1) => ResultRepr::Int1,
other => unreachable!("unexpected layout: {:?}", other),
}
}
fn unwrap(
&self,
builder: &mut FuncDefBuilder,
block: BlockId,
err_or_ok: ValueId,
keep_tag_id: u32,
) -> Result<ValueId> {
match self {
ResultRepr::NonRecursive { .. } => {
let unwrapped = builder.add_unwrap_union(block, err_or_ok, keep_tag_id)?;
builder.add_get_tuple_field(block, unwrapped, 0)
}
ResultRepr::Int1 => builder.add_make_tuple(block, &[]),
}
}
}
fn add_loop(
builder: &mut FuncDefBuilder,
block: BlockId,
state_type: TypeId,
init_state: ValueId,
make_body: impl for<'a> FnOnce(&'a mut FuncDefBuilder, BlockId, ValueId) -> Result<ValueId>,
) -> Result<ValueId> {
let sub_block = builder.add_block();
let (loop_cont, loop_arg) = builder.declare_continuation(sub_block, state_type, state_type)?;
let body = builder.add_block();
let ret_branch = builder.add_block();
let loop_branch = builder.add_block();
let new_state = make_body(builder, loop_branch, loop_arg)?;
let unreachable = builder.add_jump(loop_branch, loop_cont, new_state, state_type)?;
let result = builder.add_choice(
body,
&[
BlockExpr(ret_branch, loop_arg),
BlockExpr(loop_branch, unreachable),
],
)?;
builder.define_continuation(loop_cont, BlockExpr(body, result))?;
let unreachable = builder.add_jump(sub_block, loop_cont, init_state, state_type)?;
builder.add_sub_block(block, BlockExpr(sub_block, unreachable))
}
fn call_spec( fn call_spec(
builder: &mut FuncDefBuilder, builder: &mut FuncDefBuilder,
env: &Env, env: &Env,
@ -613,6 +691,7 @@ fn call_spec(
HigherOrderLowLevel { HigherOrderLowLevel {
specialization_id, specialization_id,
closure_env_layout, closure_env_layout,
update_mode,
op, op,
arg_layouts, arg_layouts,
ret_layout, ret_layout,
@ -620,194 +699,405 @@ fn call_spec(
function_env, function_env,
.. ..
} => { } => {
use crate::low_level::HigherOrder::*;
let array = specialization_id.to_bytes(); let array = specialization_id.to_bytes();
let spec_var = CalleeSpecVar(&array); let spec_var = CalleeSpecVar(&array);
let mode = update_mode.to_bytes();
let update_mode_var = UpdateModeVar(&mode);
let it = arg_layouts.iter().copied(); let it = arg_layouts.iter().copied();
let bytes = func_name_bytes_help(*function_name, it, *ret_layout); let bytes = func_name_bytes_help(*function_name, it, *ret_layout);
let name = FuncName(&bytes); let name = FuncName(&bytes);
let module = MOD_APP; let module = MOD_APP;
use crate::low_level::HigherOrder::*; let closure_env = env.symbols[function_env];
macro_rules! call_function {
($builder: expr, $block:expr, [$($arg:expr),+ $(,)?]) => {{
let argument = if closure_env_layout.is_none() {
$builder.add_make_tuple($block, &[$($arg),+])?
} else {
$builder.add_make_tuple($block, &[$($arg),+, closure_env])?
};
$builder.add_call($block, spec_var, module, name, argument)?
}};
}
match op { match op {
DictWalk { xs, state } => { DictWalk { xs, state } => {
let dict = env.symbols[xs]; let dict = env.symbols[xs];
let state = env.symbols[state]; let state = env.symbols[state];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?; let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
let first = builder.add_bag_get(block, bag)?; let element = builder.add_bag_get(block, bag)?;
let key = builder.add_get_tuple_field(block, first, 0)?; let key = builder.add_get_tuple_field(block, element, 0)?;
let val = builder.add_get_tuple_field(block, first, 1)?; let val = builder.add_get_tuple_field(block, element, 1)?;
let argument = if closure_env_layout.is_none() { let new_state = call_function!(builder, block, [state, key, val]);
builder.add_make_tuple(block, &[state, key, val])?
} else { Ok(new_state)
builder.add_make_tuple(block, &[state, key, val, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let state_layout = arg_layouts[0];
let state_type = layout_spec(builder, &state_layout)?;
let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListWalk { xs, state } ListWalk { xs, state } | ListWalkBackwards { xs, state } => {
| ListWalkBackwards { xs, state }
| ListWalkUntil { xs, state } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
let state = env.symbols[state]; let state = env.symbols[state];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let first = builder.add_bag_get(block, bag)?; let element = builder.add_bag_get(block, bag)?;
let argument = if closure_env_layout.is_none() { let new_state = call_function!(builder, block, [state, element]);
builder.add_make_tuple(block, &[state, first])?
} else { Ok(new_state)
builder.add_make_tuple(block, &[state, first, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let state_layout = arg_layouts[0];
let state_type = layout_spec(builder, &state_layout)?;
let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListWalkUntil { xs, state } => {
let list = env.symbols[xs];
let state = env.symbols[state];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let element = builder.add_bag_get(block, bag)?;
let continue_or_stop = call_function!(builder, block, [state, element]);
// just assume it is a continue
let unwrapped = builder.add_unwrap_union(block, continue_or_stop, 0)?;
let new_state = builder.add_get_tuple_field(block, unwrapped, 0)?;
Ok(new_state)
};
let state_layout = arg_layouts[0];
let state_type = layout_spec(builder, &state_layout)?;
let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let first = builder.add_bag_get(block, bag)?; let element = builder.add_bag_get(block, input_bag)?;
let index = builder.add_make_tuple(block, &[])?; let index = builder.add_make_tuple(block, &[])?;
let argument = if closure_env_layout.is_none() { let new_element = call_function!(builder, block, [index, element]);
builder.add_make_tuple(block, &[index, first])?
} else { list_append(builder, block, update_mode_var, state, new_element)
builder.add_make_tuple(block, &[index, first, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let output_element_type = layout_spec(builder, ret_layout)?;
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListMap { xs } => { ListMap { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?; let element = builder.add_bag_get(block, input_bag)?;
let argument = if closure_env_layout.is_none() { let new_element = call_function!(builder, block, [element]);
builder.add_make_tuple(block, &[elem1])?
} else { list_append(builder, block, update_mode_var, state, new_element)
builder.add_make_tuple(block, &[elem1, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let output_element_type = layout_spec(builder, ret_layout)?;
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListSortWith { xs } => { ListSortWith { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let bag = builder.add_get_tuple_field(block, state, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, state, LIST_CELL_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?; let element_1 = builder.add_bag_get(block, bag)?;
let element_2 = builder.add_bag_get(block, bag)?;
let argument = if closure_env_layout.is_none() { let _ = call_function!(builder, block, [element_1, element_2]);
builder.add_make_tuple(block, &[elem1, elem1])?
} else { builder.add_update(block, update_mode_var, cell)?;
builder.add_make_tuple(block, &[elem1, elem1, closure_env])?
let new_cell = builder.add_new_heap_cell(block)?;
builder.add_make_tuple(block, &[new_cell, bag])
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let state_layout = Layout::Builtin(Builtin::List(&arg_layouts[0]));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = list;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListMap2 { xs, ys } => { ListMap2 { xs, ys } => {
let list1 = env.symbols[xs]; let list1 = env.symbols[xs];
let list2 = env.symbols[ys]; let list2 = env.symbols[ys];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; let input_bag_1 =
let elem1 = builder.add_bag_get(block, bag1)?; builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?; let element_1 = builder.add_bag_get(block, input_bag_1)?;
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?; let element_2 = builder.add_bag_get(block, input_bag_2)?;
let elem2 = builder.add_bag_get(block, bag2)?;
let argument = if closure_env_layout.is_none() { let new_element = call_function!(builder, block, [element_1, element_2]);
builder.add_make_tuple(block, &[elem1, elem2])?
} else { list_append(builder, block, update_mode_var, state, new_element)
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let output_element_type = layout_spec(builder, ret_layout)?;
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListMap3 { xs, ys, zs } => { ListMap3 { xs, ys, zs } => {
let list1 = env.symbols[xs]; let list1 = env.symbols[xs];
let list2 = env.symbols[ys]; let list2 = env.symbols[ys];
let list3 = env.symbols[zs]; let list3 = env.symbols[zs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; let input_bag_1 =
let elem1 = builder.add_bag_get(block, bag1)?; builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let input_bag_3 =
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?; let element_1 = builder.add_bag_get(block, input_bag_1)?;
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?; let element_2 = builder.add_bag_get(block, input_bag_2)?;
let elem2 = builder.add_bag_get(block, bag2)?; let element_3 = builder.add_bag_get(block, input_bag_3)?;
let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?; let new_element =
let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?; call_function!(builder, block, [element_1, element_2, element_3]);
let elem3 = builder.add_bag_get(block, bag3)?;
let argument = if closure_env_layout.is_none() { list_append(builder, block, update_mode_var, state, new_element)
builder.add_make_tuple(block, &[elem1, elem2, elem3])?
} else {
builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?;
let output_element_type = layout_spec(builder, ret_layout)?;
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListMap4 { xs, ys, zs, ws } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let list3 = env.symbols[zs];
let list4 = env.symbols[ws];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag_1 =
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let input_bag_3 =
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
let input_bag_4 =
builder.add_get_tuple_field(block, list4, LIST_BAG_INDEX)?;
let element_1 = builder.add_bag_get(block, input_bag_1)?;
let element_2 = builder.add_bag_get(block, input_bag_2)?;
let element_3 = builder.add_bag_get(block, input_bag_3)?;
let element_4 = builder.add_bag_get(block, input_bag_4)?;
let new_element = call_function!(
builder,
block,
[element_1, element_2, element_3, element_4]
);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type = layout_spec(builder, ret_layout)?;
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
} }
ListKeepIf { xs } | ListKeepOks { xs } | ListKeepErrs { xs } => { ListKeepIf { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let loop_body = |builder: &mut FuncDefBuilder, block, state| {
// let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let bag = builder.add_get_tuple_field(block, state, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, state, LIST_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?; let element = builder.add_bag_get(block, bag)?;
let argument = if closure_env_layout.is_none() { let _ = call_function!(builder, block, [element]);
builder.add_make_tuple(block, &[first])?
} else { // NOTE: we assume the element is not kept
builder.add_make_tuple(block, &[first, closure_env])? builder.add_update(block, update_mode_var, cell)?;
let removed = builder.add_bag_remove(block, bag)?;
// decrement the removed element
let removed_element = builder.add_get_tuple_field(block, removed, 1)?;
builder.add_recursive_touch(block, removed_element)?;
let new_bag = builder.add_get_tuple_field(block, removed, 0)?;
let new_cell = builder.add_new_heap_cell(block)?;
builder.add_make_tuple(block, &[new_cell, new_bag])
}; };
let result = builder.add_call(block, spec_var, module, name, argument)?;
let unit = builder.add_tuple_type(&[])?; let state_layout = Layout::Builtin(Builtin::List(&arg_layouts[0]));
builder.add_unknown_with(block, &[result], unit)?; let state_type = layout_spec(builder, &state_layout)?;
let init_state = list;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListKeepOks { xs } | ListKeepErrs { xs } => {
let list = env.symbols[xs];
let keep_result = match op {
ListKeepOks { .. } => KeepResult::Oks,
ListKeepErrs { .. } => KeepResult::Errs,
_ => unreachable!(),
};
let result_repr = ResultRepr::from_layout(ret_layout);
let output_element_layout = match (keep_result, result_repr) {
(KeepResult::Errs, ResultRepr::NonRecursive { err, .. }) => err,
(KeepResult::Oks, ResultRepr::NonRecursive { ok, .. }) => ok,
(_, ResultRepr::Int1) => Layout::Struct(&[]),
};
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let element = builder.add_bag_get(block, bag)?;
let err_or_ok = call_function!(builder, block, [element]);
let kept_branch = builder.add_block();
let not_kept_branch = builder.add_block();
let element_kept = {
let block = kept_branch;
// a Result can be represented as a Int1
let new_element = result_repr.unwrap(
builder,
block,
err_or_ok,
keep_result as u32,
)?;
list_append(builder, block, update_mode_var, state, new_element)?
};
let element_not_kept = {
let block = not_kept_branch;
// a Result can be represented as a Int1
let dropped_element = result_repr.unwrap(
builder,
block,
err_or_ok,
keep_result.invert() as u32,
)?;
// decrement the element we will not keep
builder.add_recursive_touch(block, dropped_element)?;
state
};
builder.add_choice(
block,
&[
BlockExpr(not_kept_branch, element_not_kept),
BlockExpr(kept_branch, element_kept),
],
)
};
let output_element_type = layout_spec(builder, &output_element_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
let state_type = layout_spec(builder, &state_layout)?;
add_loop(builder, block, state_type, init_state, loop_body)
} }
} }
// TODO overly pessimstic
// filter_map because one of the arguments is a function name, which
// is not defined in the env
let arguments: Vec<_> = call
.arguments
.iter()
.filter_map(|symbol| env.symbols.get(symbol))
.copied()
.collect();
let result_type = layout_spec(builder, layout)?;
builder.add_unknown_with(block, &arguments, result_type)
} }
} }
} }
fn list_append(
builder: &mut FuncDefBuilder,
block: BlockId,
update_mode_var: UpdateModeVar,
list: ValueId,
to_insert: ValueId,
) -> Result<ValueId> {
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let _unit = builder.add_update(block, update_mode_var, cell)?;
let new_bag = builder.add_bag_insert(block, bag, to_insert)?;
let new_cell = builder.add_new_heap_cell(block)?;
builder.add_make_tuple(block, &[new_cell, new_bag])
}
fn lowlevel_spec( fn lowlevel_spec(
builder: &mut FuncDefBuilder, builder: &mut FuncDefBuilder,
env: &Env, env: &Env,
@ -883,6 +1173,10 @@ fn lowlevel_spec(
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
// decrement the overwritten element
let overwritten = builder.add_bag_get(block, bag)?;
let _unit = builder.add_recursive_touch(block, overwritten)?;
let _unit = builder.add_update(block, update_mode_var, cell)?; let _unit = builder.add_update(block, update_mode_var, cell)?;
builder.add_bag_insert(block, bag, to_insert)?; builder.add_bag_insert(block, bag, to_insert)?;
@ -916,16 +1210,7 @@ fn lowlevel_spec(
let list = env.symbols[&arguments[0]]; let list = env.symbols[&arguments[0]];
let to_insert = env.symbols[&arguments[1]]; let to_insert = env.symbols[&arguments[1]];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; list_append(builder, block, update_mode_var, list, to_insert)
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let _unit = builder.add_update(block, update_mode_var, cell)?;
// TODO new heap cell
builder.add_bag_insert(block, bag, to_insert)?;
let new_cell = builder.add_new_heap_cell(block)?;
builder.add_make_tuple(block, &[new_cell, bag])
} }
StrToUtf8 => { StrToUtf8 => {
let string = env.symbols[&arguments[0]]; let string = env.symbols[&arguments[0]];
@ -1425,8 +1710,8 @@ fn static_list_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
builder.add_tuple_type(&[cell, bag]) builder.add_tuple_type(&[cell, bag])
} }
// const OK_TAG_ID: u8 = 1u8; const OK_TAG_ID: u32 = 1;
// const ERR_TAG_ID: u8 = 0u8; const ERR_TAG_ID: u32 = 0;
const LIST_CELL_INDEX: u32 = 0; const LIST_CELL_INDEX: u32 = 0;
const LIST_BAG_INDEX: u32 = 1; const LIST_BAG_INDEX: u32 = 1;

View file

@ -651,6 +651,21 @@ impl<'a> BorrowInfState<'a> {
self.own_var(*zs); self.own_var(*zs);
} }
} }
ListMap4 { xs, ys, zs, ws } => {
// own the lists if the function wants to own the element
if !function_ps[0].borrow {
self.own_var(*xs);
}
if !function_ps[1].borrow {
self.own_var(*ys);
}
if !function_ps[2].borrow {
self.own_var(*zs);
}
if !function_ps[3].borrow {
self.own_var(*ws);
}
}
ListSortWith { xs } => { ListSortWith { xs } => {
// always own the input list // always own the input list
self.own_var(*xs); self.own_var(*xs);
@ -933,6 +948,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, function, closure_data]), ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, function, closure_data]),
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]), ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]), ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
ListKeepIf | ListKeepOks | ListKeepErrs => { ListKeepIf | ListKeepOks | ListKeepErrs => {
arena.alloc_slice_copy(&[owned, function, closure_data]) arena.alloc_slice_copy(&[owned, function, closure_data])
} }

View file

@ -467,6 +467,7 @@ impl<'a> Context<'a> {
op, op,
closure_env_layout, closure_env_layout,
specialization_id, specialization_id,
update_mode,
arg_layouts, arg_layouts,
ret_layout, ret_layout,
function_name, function_name,
@ -485,6 +486,7 @@ impl<'a> Context<'a> {
closure_env_layout: *closure_env_layout, closure_env_layout: *closure_env_layout,
function_owns_closure_data: true, function_owns_closure_data: true,
specialization_id: *specialization_id, specialization_id: *specialization_id,
update_mode: *update_mode,
function_name: *function_name, function_name: *function_name,
function_env: *function_env, function_env: *function_env,
arg_layouts, arg_layouts,
@ -576,6 +578,27 @@ impl<'a> Context<'a> {
&*self.arena.alloc(Stmt::Let(z, v, l, b)) &*self.arena.alloc(Stmt::Let(z, v, l, b))
} }
ListMap4 { xs, ys, zs, ws } => {
let borrows = [
function_ps[0].borrow,
function_ps[1].borrow,
function_ps[2].borrow,
function_ps[3].borrow,
FUNCTION,
CLOSURE_DATA,
];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let b = decref_if_owned!(function_ps[0].borrow, *xs, b);
let b = decref_if_owned!(function_ps[1].borrow, *ys, b);
let b = decref_if_owned!(function_ps[2].borrow, *zs, b);
let b = decref_if_owned!(function_ps[3].borrow, *ws, b);
let v = create_call!(function_ps.get(3));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA]; let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];

View file

@ -1112,6 +1112,10 @@ pub enum CallType<'a> {
/// specialization id of the function argument, used for name generation /// specialization id of the function argument, used for name generation
specialization_id: CallSpecId, specialization_id: CallSpecId,
/// update mode of the higher order lowlevel itself
update_mode: UpdateModeId,
/// function layout, used for name generation /// function layout, used for name generation
arg_layouts: &'a [Layout<'a>], arg_layouts: &'a [Layout<'a>],
ret_layout: Layout<'a>, ret_layout: Layout<'a>,
@ -4006,11 +4010,12 @@ pub fn with_hole<'a>(
lambda_set, lambda_set,
op, op,
closure_data_symbol, closure_data_symbol,
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call { |(top_level_function, closure_data, closure_env_layout, specialization_id, update_mode)| self::Call {
call_type: CallType::HigherOrderLowLevel { call_type: CallType::HigherOrderLowLevel {
op: crate::low_level::HigherOrder::$ho { $($x,)* }, op: crate::low_level::HigherOrder::$ho { $($x,)* },
closure_env_layout, closure_env_layout,
specialization_id, specialization_id,
update_mode,
function_owns_closure_data: false, function_owns_closure_data: false,
function_env: closure_data_symbol, function_env: closure_data_symbol,
function_name: top_level_function, function_name: top_level_function,
@ -4134,6 +4139,16 @@ pub fn with_hole<'a>(
match_on_closure_argument!(ListMap3, [xs, ys, zs]) match_on_closure_argument!(ListMap3, [xs, ys, zs])
} }
ListMap4 => {
debug_assert_eq!(arg_symbols.len(), 5);
let xs = arg_symbols[0];
let ys = arg_symbols[1];
let zs = arg_symbols[2];
let ws = arg_symbols[3];
match_on_closure_argument!(ListMap4, [xs, ys, zs, ws])
}
_ => { _ => {
let call = self::Call { let call = self::Call {
call_type: CallType::LowLevel { call_type: CallType::LowLevel {
@ -7871,6 +7886,8 @@ pub fn num_argument_to_int_or_float(
} }
} }
type ToLowLevelCallArguments<'a> = (Symbol, Symbol, Option<Layout<'a>>, CallSpecId, UpdateModeId);
/// Use the lambda set to figure out how to make a lowlevel call /// Use the lambda set to figure out how to make a lowlevel call
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>( fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
@ -7884,7 +7901,7 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>, hole: &'a Stmt<'a>,
) -> Stmt<'a> ) -> Stmt<'a>
where where
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy, ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
{ {
match lambda_set.runtime_representation() { match lambda_set.runtime_representation() {
Layout::Union(union_layout) => { Layout::Union(union_layout) => {
@ -7919,12 +7936,14 @@ where
Layout::Struct(_) => match lambda_set.set.get(0) { Layout::Struct(_) => match lambda_set.set.get(0) {
Some((function_symbol, _)) => { Some((function_symbol, _)) => {
let call_spec_id = env.next_call_specialization_id(); let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call( let update_mode = env.next_update_mode_id();
let call = to_lowlevel_call((
*function_symbol, *function_symbol,
closure_data_symbol, closure_data_symbol,
lambda_set.is_represented(), lambda_set.is_represented(),
call_spec_id, call_spec_id,
); update_mode,
));
build_call(env, call, assigned, return_layout, env.arena.alloc(hole)) build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
} }
@ -7989,7 +8008,7 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>, hole: &'a Stmt<'a>,
) -> Stmt<'a> ) -> Stmt<'a>
where where
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy, ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
{ {
debug_assert!(!lambda_set.is_empty()); debug_assert!(!lambda_set.is_empty());
@ -8003,12 +8022,14 @@ where
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned])); let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
let call_spec_id = env.next_call_specialization_id(); let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call( let update_mode = env.next_update_mode_id();
let call = to_lowlevel_call((
*function_symbol, *function_symbol,
closure_data_symbol, closure_data_symbol,
closure_env_layout, closure_env_layout,
call_spec_id, call_spec_id,
); update_mode,
));
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole)); let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
branches.push((i as u64, BranchInfo::None, stmt)); branches.push((i as u64, BranchInfo::None, stmt));
@ -8426,7 +8447,7 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>, hole: &'a Stmt<'a>,
) -> Stmt<'a> ) -> Stmt<'a>
where where
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy, ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
{ {
debug_assert!(!lambda_set.is_empty()); debug_assert!(!lambda_set.is_empty());
@ -8440,12 +8461,14 @@ where
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol])); let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
let call_spec_id = env.next_call_specialization_id(); let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call( let update_mode = env.next_update_mode_id();
let call = to_lowlevel_call((
*function_symbol, *function_symbol,
closure_data_symbol, closure_data_symbol,
closure_env_layout, closure_env_layout,
call_spec_id, call_spec_id,
); update_mode,
));
let stmt = build_call( let stmt = build_call(
env, env,
call, call,

View file

@ -2,18 +2,55 @@ use roc_module::symbol::Symbol;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum HigherOrder { pub enum HigherOrder {
ListMap { xs: Symbol }, ListMap {
ListMap2 { xs: Symbol, ys: Symbol }, xs: Symbol,
ListMap3 { xs: Symbol, ys: Symbol, zs: Symbol }, },
ListMapWithIndex { xs: Symbol }, ListMap2 {
ListKeepIf { xs: Symbol }, xs: Symbol,
ListWalk { xs: Symbol, state: Symbol }, ys: Symbol,
ListWalkUntil { xs: Symbol, state: Symbol }, },
ListWalkBackwards { xs: Symbol, state: Symbol }, ListMap3 {
ListKeepOks { xs: Symbol }, xs: Symbol,
ListKeepErrs { xs: Symbol }, ys: Symbol,
ListSortWith { xs: Symbol }, zs: Symbol,
DictWalk { xs: Symbol, state: Symbol }, },
ListMap4 {
xs: Symbol,
ys: Symbol,
zs: Symbol,
ws: Symbol,
},
ListMapWithIndex {
xs: Symbol,
},
ListKeepIf {
xs: Symbol,
},
ListWalk {
xs: Symbol,
state: Symbol,
},
ListWalkUntil {
xs: Symbol,
state: Symbol,
},
ListWalkBackwards {
xs: Symbol,
state: Symbol,
},
ListKeepOks {
xs: Symbol,
},
ListKeepErrs {
xs: Symbol,
},
ListSortWith {
xs: Symbol,
},
DictWalk {
xs: Symbol,
state: Symbol,
},
} }
impl HigherOrder { impl HigherOrder {
@ -22,6 +59,7 @@ impl HigherOrder {
HigherOrder::ListMap { .. } => 1, HigherOrder::ListMap { .. } => 1,
HigherOrder::ListMap2 { .. } => 2, HigherOrder::ListMap2 { .. } => 2,
HigherOrder::ListMap3 { .. } => 3, HigherOrder::ListMap3 { .. } => 3,
HigherOrder::ListMap4 { .. } => 4,
HigherOrder::ListMapWithIndex { .. } => 2, HigherOrder::ListMapWithIndex { .. } => 2,
HigherOrder::ListKeepIf { .. } => 1, HigherOrder::ListKeepIf { .. } => 1,
HigherOrder::ListWalk { .. } => 2, HigherOrder::ListWalk { .. } => 2,
@ -128,202 +166,3 @@ enum FirstOrder {
Hash, Hash,
ExpectTrue, ExpectTrue,
} }
/*
enum FirstOrHigher {
First(FirstOrder),
Higher(HigherOrder),
}
fn from_low_level(low_level: &LowLevel, arguments: &[Symbol]) -> FirstOrHigher {
use FirstOrHigher::*;
use FirstOrder::*;
use HigherOrder::*;
match low_level {
LowLevel::StrConcat => First(StrConcat),
LowLevel::StrJoinWith => First(StrJoinWith),
LowLevel::StrIsEmpty => First(StrIsEmpty),
LowLevel::StrStartsWith => First(StrStartsWith),
LowLevel::StrStartsWithCodePt => First(StrStartsWithCodePt),
LowLevel::StrEndsWith => First(StrEndsWith),
LowLevel::StrSplit => First(StrSplit),
LowLevel::StrCountGraphemes => First(StrCountGraphemes),
LowLevel::StrFromInt => First(StrFromInt),
LowLevel::StrFromUtf8 => First(StrFromUtf8),
LowLevel::StrFromUtf8Range => First(StrFromUtf8Range),
LowLevel::StrToUtf8 => First(StrToUtf8),
LowLevel::StrRepeat => First(StrRepeat),
LowLevel::StrFromFloat => First(StrFromFloat),
LowLevel::ListLen => First(ListLen),
LowLevel::ListGetUnsafe => First(ListGetUnsafe),
LowLevel::ListSet => First(ListSet),
LowLevel::ListDrop => First(ListDrop),
LowLevel::ListDropAt => First(ListDropAt),
LowLevel::ListSingle => First(ListSingle),
LowLevel::ListRepeat => First(ListRepeat),
LowLevel::ListReverse => First(ListReverse),
LowLevel::ListConcat => First(ListConcat),
LowLevel::ListContains => First(ListContains),
LowLevel::ListAppend => First(ListAppend),
LowLevel::ListPrepend => First(ListPrepend),
LowLevel::ListJoin => First(ListJoin),
LowLevel::ListRange => First(ListRange),
LowLevel::ListSwap => First(ListSwap),
LowLevel::DictSize => First(DictSize),
LowLevel::DictEmpty => First(DictEmpty),
LowLevel::DictInsert => First(DictInsert),
LowLevel::DictRemove => First(DictRemove),
LowLevel::DictContains => First(DictContains),
LowLevel::DictGetUnsafe => First(DictGetUnsafe),
LowLevel::DictKeys => First(DictKeys),
LowLevel::DictValues => First(DictValues),
LowLevel::DictUnion => First(DictUnion),
LowLevel::DictIntersection => First(DictIntersection),
LowLevel::DictDifference => First(DictDifference),
LowLevel::SetFromList => First(SetFromList),
LowLevel::NumAdd => First(NumAdd),
LowLevel::NumAddWrap => First(NumAddWrap),
LowLevel::NumAddChecked => First(NumAddChecked),
LowLevel::NumSub => First(NumSub),
LowLevel::NumSubWrap => First(NumSubWrap),
LowLevel::NumSubChecked => First(NumSubChecked),
LowLevel::NumMul => First(NumMul),
LowLevel::NumMulWrap => First(NumMulWrap),
LowLevel::NumMulChecked => First(NumMulChecked),
LowLevel::NumGt => First(NumGt),
LowLevel::NumGte => First(NumGte),
LowLevel::NumLt => First(NumLt),
LowLevel::NumLte => First(NumLte),
LowLevel::NumCompare => First(NumCompare),
LowLevel::NumDivUnchecked => First(NumDivUnchecked),
LowLevel::NumRemUnchecked => First(NumRemUnchecked),
LowLevel::NumIsMultipleOf => First(NumIsMultipleOf),
LowLevel::NumAbs => First(NumAbs),
LowLevel::NumNeg => First(NumNeg),
LowLevel::NumSin => First(NumSin),
LowLevel::NumCos => First(NumCos),
LowLevel::NumSqrtUnchecked => First(NumSqrtUnchecked),
LowLevel::NumLogUnchecked => First(NumLogUnchecked),
LowLevel::NumRound => First(NumRound),
LowLevel::NumToFloat => First(NumToFloat),
LowLevel::NumPow => First(NumPow),
LowLevel::NumCeiling => First(NumCeiling),
LowLevel::NumPowInt => First(NumPowInt),
LowLevel::NumFloor => First(NumFloor),
LowLevel::NumIsFinite => First(NumIsFinite),
LowLevel::NumAtan => First(NumAtan),
LowLevel::NumAcos => First(NumAcos),
LowLevel::NumAsin => First(NumAsin),
LowLevel::NumBitwiseAnd => First(NumBitwiseAnd),
LowLevel::NumBitwiseXor => First(NumBitwiseXor),
LowLevel::NumBitwiseOr => First(NumBitwiseOr),
LowLevel::NumShiftLeftBy => First(NumShiftLeftBy),
LowLevel::NumShiftRightBy => First(NumShiftRightBy),
LowLevel::NumBytesToU16 => First(NumBytesToU16),
LowLevel::NumBytesToU32 => First(NumBytesToU32),
LowLevel::NumShiftRightZfBy => First(NumShiftRightZfBy),
LowLevel::NumIntCast => First(NumIntCast),
LowLevel::Eq => First(Eq),
LowLevel::NotEq => First(NotEq),
LowLevel::And => First(And),
LowLevel::Or => First(Or),
LowLevel::Not => First(Not),
LowLevel::Hash => First(Hash),
LowLevel::ExpectTrue => First(ExpectTrue),
LowLevel::ListMap => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListMap {
xs: arguments[0],
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListMap2 => {
debug_assert_eq!(arguments.len(), 4);
Higher(ListMap2 {
xs: arguments[0],
ys: arguments[1],
function_name: arguments[2],
function_env: arguments[3],
})
}
LowLevel::ListMap3 => {
debug_assert_eq!(arguments.len(), 5);
Higher(ListMap3 {
xs: arguments[0],
ys: arguments[1],
zs: arguments[2],
function_name: arguments[3],
function_env: arguments[4],
})
}
LowLevel::ListMapWithIndex => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListMapWithIndex {
xs: arguments[0],
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListKeepIf => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListKeepIf {
xs: arguments[0],
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListWalk => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListWalk {
xs: arguments[0],
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListWalkUntil => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListWalkUntil {
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListWalkBackwards => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListWalkBackwards {
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListKeepOks => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListKeepOks {
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListKeepErrs => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListKeepErrs {
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::ListSortWith => {
debug_assert_eq!(arguments.len(), 3);
Higher(ListSortWith {
function_name: arguments[1],
function_env: arguments[2],
})
}
LowLevel::DictWalk => {
debug_assert_eq!(arguments.len(), 3);
Higher(DictWalk {
function_name: arguments[1],
function_env: arguments[2],
})
}
}
}
*/

View file

@ -763,6 +763,37 @@ fn list_map_closure() {
); );
} }
#[test]
fn list_map4_group() {
assert_evals_to!(
indoc!(
r#"
List.map4 [1,2,3] [3,2,1] [2,1,3] [3,1,2] (\a, b, c, d -> Group a b c d)
"#
),
RocList::from_slice(&[(1, 3, 2, 3), (2, 2, 1, 1), (3, 1, 3, 2)]),
RocList<(i64, i64, i64, i64)>
);
}
#[test]
fn list_map4_different_length() {
assert_evals_to!(
indoc!(
r#"
List.map4
["h", "i", "j", "k"]
["o", "p", "q"]
["l", "m"]
["a"]
(\a, b, c, d -> Str.concat a (Str.concat b (Str.concat c d)))
"#
),
RocList::from_slice(&[RocStr::from_slice("hola".as_bytes()),]),
RocList<RocStr>
);
}
#[test] #[test]
fn list_map3_group() { fn list_map3_group() {
assert_evals_to!( assert_evals_to!(

View file

@ -0,0 +1,31 @@
[package]
name = "test_wasm"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# roc_module = { path = "../module" }
# roc_mono = { path = "../mono" }
# # TODO: switch to parity-wasm 0.44 once it's out (allows bumpalo vectors in some places)
parity-wasm = { git = "https://github.com/brian-carroll/parity-wasm", branch = "master" }
wasmer = "2.0.0"
wasmer-wasi = "2.0.0"
roc_collections = { path = "../collections" }
roc_std = { path = "../../roc_std" }
bumpalo = { version = "3.6.1", features = ["collections"] }
roc_gen_wasm = { path = "../gen_wasm" }
roc_can = { path = "../can" }
roc_builtins = { path = "../builtins" }
roc_load = { path = "../load" }
roc_types = { path = "../types" }
roc_module = { path = "../module" }
indoc = "0.3.3"
pretty_assertions = "0.5.1"
libc = "0.2"
target-lexicon = "0.12.2"
tempfile = "3.1.0"

View file

@ -1,5 +1,3 @@
extern crate bumpalo;
#[macro_use] #[macro_use]
pub mod eval; pub mod eval;
pub mod wasm32_test_result; pub mod wasm32_test_result;

View file

@ -0,0 +1,3 @@
mod helpers;
pub mod wasm_num;
pub mod wasm_records;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,911 @@
#![cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
use crate::assert_evals_to;
use indoc::indoc;
// #[test]
// fn basic_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { y: 17, x: 15, z: 19 }.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: 17, z: 19 }.y
// "#
// ),
// 17,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: 17, z: 19 }.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn nested_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
// "#
// ),
// 12,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
// "#
// ),
// 2,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn f64_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.x
// "#
// ),
// 15.1,
// f64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.y
// "#
// ),
// 17.2,
// f64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
//
// rec.z
// "#
// ),
// 19.3,
// f64
// );
// }
// #[test]
// fn fn_record() {
// assert_evals_to!(
// indoc!(
// r#"
// getRec = \x -> { y: 17, x, z: 19 }
// (getRec 15).x
// "#
// ),
// 15,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.y
// "#
// ),
// 17,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z
// "#
// ),
// 19,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z + rec.x
// "#
// ),
// 34,
// i64
// );
// }
// #[test]
// fn def_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17, x: 15, z: 19 }
//
// rec.x
// "#
// ),
// 15,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
//
// rec.y
// "#
// ),
// 17,
// i64
// );
//
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
//
// rec.z
// "#
// ),
// 19,
// i64
// );
// }
//
// #[test]
// fn when_on_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2 } is
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn when_record_with_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: var } -> var + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn let_with_record_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// { x } = { x: 0x2, y: 3.14 }
//
// x
// "#
// ),
// 2,
// i64
// );
// }
//
// #[test]
// fn record_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: 0x4 } -> 5
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
//
// #[test]
// fn twice_record_access() {
// assert_evals_to!(
// indoc!(
// r#"
// x = {a: 0x2, b: 0x3 }
//
// x.a + x.b
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// v = {}
//
// v
// "#
// ),
// (),
// ()
// );
// }
#[test]
fn i64_record1_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3 }
"#
),
3,
i64
);
}
#[test]
fn i64_record2_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5 }
"#
),
(3, 5),
(i64, i64)
);
}
#[test]
fn i64_record3_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5, z: 17 }
"#
),
(3, 5, 17),
(i64, i64, i64)
);
}
#[test]
fn f64_record2_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3.1, y: 5.1 }
"#
),
(3.1, 5.1),
(f64, f64)
);
}
#[test]
fn f64_record3_literal() {
assert_evals_to!(
indoc!(
r#"
{ x: 3.1, y: 5.1, z: 17.1 }
"#
),
(3.1, 5.1, 17.1),
(f64, f64, f64)
);
}
#[test]
fn bool_record4_literal() {
assert_evals_to!(
indoc!(
r#"
record : { a : Bool, b : Bool, c : Bool, d : Bool }
record = { a: True, b: False, c : False, d : True }
record
"#
),
[true, false, false, true],
[bool; 4]
);
}
#[test]
fn i64_record9_literal() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
"#
),
[3, 5, 17, 1, 9, 12, 13, 14, 15],
[i64; 9]
);
}
#[test]
fn bool_literal() {
assert_evals_to!(
indoc!(
r#"
x : Bool
x = True
x
"#
),
true,
bool
);
}
// #[test]
// fn optional_field_when_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// main =
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_function_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \{ x ? 10, y } -> x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default_nested() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_singleton_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x : 4 } is
// { x ? 3 } -> x
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn optional_field_empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { } is
// { x ? 3 } -> x
// "#
// ),
// 3,
// i64
// );
// }
#[test]
fn return_record_3() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5, z: 4 }
"#
),
(3, 5, 4),
(i64, i64, i64)
);
}
#[test]
fn return_record_4() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2 }
"#
),
[3, 5, 4, 2],
[i64; 4]
);
}
#[test]
fn return_record_5() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
"#
),
[3, 5, 4, 2, 1],
[i64; 5]
);
}
#[test]
fn return_record_6() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
"#
),
[3, 5, 4, 2, 1, 7],
[i64; 6]
);
}
#[test]
fn return_record_7() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
"#
),
[3, 5, 4, 2, 1, 7, 8],
[i64; 7]
);
}
#[test]
fn return_record_float_int() {
assert_evals_to!(
indoc!(
r#"
{ a: 3.14, b: 0x1 }
"#
),
(3.14, 0x1),
(f64, i64)
);
}
#[test]
fn return_record_int_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 0x1, b: 3.14 }
"#
),
(0x1, 3.14),
(i64, f64)
);
}
#[test]
fn return_record_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14 }
"#
),
(6.28, 3.14),
(f64, f64)
);
}
#[test]
fn return_record_float_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14, c: 0.1 }
"#
),
(6.28, 3.14, 0.1),
(f64, f64, f64)
);
}
// #[test]
// fn return_nested_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
// "#
// ),
// (0x0, (6.28, 3.14, 0.1)),
// (i64, (f64, f64, f64))
// );
// }
// #[test]
// fn accessor() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn accessor_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 }
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn update_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42, bar: 6 }
// { rec & foo: rec.foo + 1 }
// "#
// ),
// (6, 43),
// (i64, i64)
// );
// }
// #[test]
// fn update_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42}
// { rec & foo: rec.foo + 1 }
// "#
// ),
// 43,
// i64
// );
// }
// #[test]
// fn booleans_in_record() {
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
// (true, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
// (false, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
// (true, false),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
// (false, false),
// (bool, bool)
// );
// }
// #[test]
// fn alignment_in_record() {
// assert_evals_to!(
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
// (32i64, true, 2u8),
// (i64, bool, u8)
// );
// }
#[test]
fn stack_memory_return_from_branch() {
// stack memory pointer should end up in the right place after returning from a branch
assert_evals_to!(
indoc!(
r#"
stackMemoryJunk = { x: 999, y: 111 }
if True then
{ x: 123, y: 321 }
else
stackMemoryJunk
"#
),
(123, 321),
(i64, i64)
);
}
// #[test]
// fn blue_and_present() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue, y: 7 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn blue_and_absent() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue }
// "#
// ),
// 3,
// i64
// );
// }

View file

@ -79,7 +79,9 @@ fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Err
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}) })
.await .await
.expect("Request adapter"); .expect(r#"Request adapter
If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor
"#);
adapter adapter
.request_device( .request_device(

View file

@ -11,7 +11,7 @@ app "false"
# 1) The input files are considered too large to just read in at once. Instead it is read via buffer or line. # 1) The input files are considered too large to just read in at once. Instead it is read via buffer or line.
# 2) The output is also considered too large to generate in memory. It must be printed as we go via buffer or line. # 2) The output is also considered too large to generate in memory. It must be printed as we go via buffer or line.
# I think one of the biggest issues with this implementation is that it doesn't return the the platform frequently enough. # I think one of the biggest issues with this implementation is that it doesn't return to the platform frequently enough.
# What I mean by that is we build a chain of all Tasks period and return that to the host. # What I mean by that is we build a chain of all Tasks period and return that to the host.
# In something like the elm architecture you return a single step with one Task. # In something like the elm architecture you return a single step with one Task.
# The huge difference here is when it comes to things like stack overflows. # The huge difference here is when it comes to things like stack overflows.

View file

@ -30,12 +30,12 @@ extern "C" {
} }
#[no_mangle] #[no_mangle]
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
libc::malloc(size) libc::malloc(size)
} }
#[no_mangle] #[no_mangle]
pub unsafe fn roc_realloc( pub unsafe extern "C" fn roc_realloc(
c_ptr: *mut c_void, c_ptr: *mut c_void,
new_size: usize, new_size: usize,
_old_size: usize, _old_size: usize,
@ -45,12 +45,12 @@ pub unsafe fn roc_realloc(
} }
#[no_mangle] #[no_mangle]
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
libc::free(c_ptr) libc::free(c_ptr)
} }
#[no_mangle] #[no_mangle]
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
match tag_id { match tag_id {
0 => { 0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char); let slice = CStr::from_ptr(c_ptr as *const c_char);
@ -73,7 +73,7 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
} }
#[no_mangle] #[no_mangle]
pub fn rust_main() -> i32 { pub extern "C" fn rust_main() -> i32 {
let arg = env::args().skip(1).next().unwrap(); let arg = env::args().skip(1).next().unwrap();
let arg = RocStr::from_slice(arg.as_bytes()); let arg = RocStr::from_slice(arg.as_bytes());
@ -115,7 +115,7 @@ unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_getLine() -> RocStr { pub extern "C" fn roc_fx_getLine() -> RocStr {
use std::io::{self, BufRead}; use std::io::{self, BufRead};
let stdin = io::stdin(); let stdin = io::stdin();
@ -125,7 +125,7 @@ pub fn roc_fx_getLine() -> RocStr {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_getChar() -> u8 { pub extern "C" fn roc_fx_getChar() -> u8 {
use std::io::{self, BufRead}; use std::io::{self, BufRead};
let mut buffer = [0]; let mut buffer = [0];
@ -141,7 +141,7 @@ pub fn roc_fx_getChar() -> u8 {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_putLine(line: RocStr) -> () { pub extern "C" fn roc_fx_putLine(line: RocStr) -> () {
let bytes = line.as_slice(); let bytes = line.as_slice();
let string = unsafe { std::str::from_utf8_unchecked(bytes) }; let string = unsafe { std::str::from_utf8_unchecked(bytes) };
println!("{}", string); println!("{}", string);
@ -154,7 +154,7 @@ pub fn roc_fx_putLine(line: RocStr) -> () {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_putRaw(line: RocStr) -> () { pub extern "C" fn roc_fx_putRaw(line: RocStr) -> () {
let bytes = line.as_slice(); let bytes = line.as_slice();
let string = unsafe { std::str::from_utf8_unchecked(bytes) }; let string = unsafe { std::str::from_utf8_unchecked(bytes) };
print!("{}", string); print!("{}", string);
@ -167,7 +167,7 @@ pub fn roc_fx_putRaw(line: RocStr) -> () {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr { pub extern "C" fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
let br = unsafe { &mut *br_ptr }; let br = unsafe { &mut *br_ptr };
let mut line1 = String::default(); let mut line1 = String::default();
@ -178,7 +178,7 @@ pub fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> { pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
let br = unsafe { &mut *br_ptr }; let br = unsafe { &mut *br_ptr };
let mut buffer = [0; 0x10 /* This is intentially small to ensure correct implementation */]; let mut buffer = [0; 0x10 /* This is intentially small to ensure correct implementation */];
@ -190,22 +190,25 @@ pub fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) -> () { pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) -> () {
unsafe { unsafe {
Box::from_raw(br_ptr); Box::from_raw(br_ptr);
} }
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_openFile(name: RocStr) -> *mut BufReader<File> { pub extern "C" fn roc_fx_openFile(name: RocStr) -> *mut BufReader<File> {
let f = File::open(name.as_str()).expect("Unable to open file"); let f = File::open(name.as_str()).expect("Unable to open file");
let br = BufReader::new(f); let br = BufReader::new(f);
// don't mess with the refcount!
core::mem::forget(name);
Box::into_raw(Box::new(br)) Box::into_raw(Box::new(br))
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_withFileOpen(name: RocStr, buffer: *const u8) -> () { pub extern "C" fn roc_fx_withFileOpen(name: RocStr, buffer: *const u8) -> () {
// let f = File::open(name.as_str()).expect("Unable to open file"); // let f = File::open(name.as_str()).expect("Unable to open file");
// let mut br = BufReader::new(f); // let mut br = BufReader::new(f);

41
nix/zig.nix Normal file
View file

@ -0,0 +1,41 @@
{ pkgs }:
let
version = "0.8.0";
osName = if pkgs.stdenv.isDarwin then "macos" else "linux";
splitSystem = builtins.split "-" builtins.currentSystem;
arch = builtins.elemAt splitSystem 0;
isAarch64 = arch == "aarch64";
archiveName = "zig-${osName}-${arch}-${version}";
# If your system is not aarch64, we assume it's x86_64
sha256 = if pkgs.stdenv.isDarwin then
if isAarch64 then
"b32d13f66d0e1ff740b3326d66a469ee6baddbd7211fa111c066d3bd57683111"
else
"279f9360b5cb23103f0395dc4d3d0d30626e699b1b4be55e98fd985b62bc6fbe"
else if isAarch64 then
"ee204ca2c2037952cf3f8b10c609373a08a291efa4af7b3c73be0f2b27720470"
else
"502625d3da3ae595c5f44a809a87714320b7a40e6dff4a895b5fa7df3391d01e";
in pkgs.stdenv.mkDerivation {
pname = "zig";
version = version;
src = pkgs.fetchurl {
inherit sha256;
name = "${archiveName}.tar.xz";
url = "https://ziglang.org/download/${version}/${archiveName}.tar.xz";
};
phases = [ "unpackPhase" ];
unpackPhase = ''
mkdir -p $out/bin
tar -xf $src
cp ${archiveName}/zig $out/zig
cp -r ${archiveName}/lib $out/lib
ln -s "$out/zig" "$out/bin/zig"
chmod +x $out/bin/zig
'';
}

View file

@ -136,6 +136,7 @@ impl<T> RocList<T> {
where where
T: Clone, T: Clone,
{ {
assert!(capacity > 0);
assert!(slice.len() <= capacity); assert!(slice.len() <= capacity);
let ptr = slice.as_ptr(); let ptr = slice.as_ptr();
@ -197,7 +198,12 @@ impl<T> RocList<T> {
where where
T: Clone, T: Clone,
{ {
Self::from_slice_with_capacity(slice, slice.len()) // Avoid allocation with empty list.
if slice.is_empty() {
Self::default()
} else {
Self::from_slice_with_capacity(slice, slice.len())
}
} }
pub fn as_slice(&self) -> &[T] { pub fn as_slice(&self) -> &[T] {

View file

@ -32,6 +32,7 @@ let
llvmPkgs = pkgs.llvmPackages_12; llvmPkgs = pkgs.llvmPackages_12;
zig = import ./nix/zig.nix { inherit pkgs; };
debugir = import ./nix/debugir.nix { inherit pkgs; }; debugir = import ./nix/debugir.nix { inherit pkgs; };
inputs = with pkgs; [ inputs = with pkgs; [
@ -70,9 +71,39 @@ in pkgs.mkShell {
# Additional Env vars # Additional Env vars
LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}"; LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}";
NIX_GLIBC_PATH =
if pkgs.stdenv.isLinux then "${pkgs.glibc_multi.out}/lib" else "";
LD_LIBRARY_PATH = with pkgs; LD_LIBRARY_PATH = with pkgs;
lib.makeLibraryPath lib.makeLibraryPath
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs); ([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
NIX_GLIBC_PATH =
if pkgs.stdenv.isLinux then "${pkgs.glibc_multi.out}/lib" else ""; COREAUDIO_SDK_PATH = if pkgs.stdenv.isDarwin then
# The coreaudio-sys crate is configured to look for things in whatever the
# output of `xcrun --sdk macosx --show-sdk-path` is. However, this does not
# always contain the right frameworks, and it uses system versions instead of
# what we control via Nix. Instead of having to run a lot of extra scripts
# to set our systems up to build, we can just create a SDK directory with
# the same layout as the `MacOSX{version}.sdk` that XCode produces.
pkgs.symlinkJoin {
name = "sdk";
paths = with pkgs.darwin.apple_sdk.frameworks; [
AudioToolbox
AudioUnit
CoreAudio
CoreFoundation
CoreMIDI
OpenAL
];
postBuild = ''
mkdir $out/System
mv $out/Library $out/System
'';
}
else
# TODO: I'm not 100% confident that this being blank won't cause issues for
# Nix-on-Linux development. It may be sufficient to use the pkgs.symlinkJoin
# above regardless of system! That'd set us up for cross-compilation as well.
"";
} }