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

@ -0,0 +1,233 @@
use std::cell::Cell;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::{MutMap, MutSet};
use roc_gen_wasm::combine_and_serialize;
// use roc_std::{RocDec, RocList, RocOrder, RocStr};
use crate::helpers::wasm32_test_result::Wasm32TestResult;
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
const TEST_WRAPPER_NAME: &str = "test_wrapper";
std::thread_local! {
static TEST_COUNTER: Cell<u32> = Cell::new(0);
}
fn promote_expr_to_module(src: &str) -> String {
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
for line in src.lines() {
// indent the body!
buffer.push_str(" ");
buffer.push_str(line);
buffer.push('\n');
}
buffer
}
#[allow(dead_code)]
pub fn helper_wasm<'a, T: Wasm32TestResult>(
arena: &'a bumpalo::Bump,
src: &str,
stdlib: &'a roc_builtins::std::StdLib,
_result_type_dummy: &T,
) -> wasmer::Instance {
use std::path::{Path, PathBuf};
let filename = PathBuf::from("Test.roc");
let src_dir = Path::new("fake/test/path");
let module_src;
let temp;
if src.starts_with("app") {
// this is already a module
module_src = src;
} else {
// this is an expression, promote it to a module
temp = promote_expr_to_module(src);
module_src = &temp;
}
let exposed_types = MutMap::default();
let loaded = roc_load::file::load_and_monomorphize_from_str(
arena,
filename,
module_src,
stdlib,
src_dir,
exposed_types,
8,
builtin_defs_map,
);
let loaded = loaded.expect("failed to load module");
use roc_load::file::MonomorphizedModule;
let MonomorphizedModule {
procedures,
interns,
exposed_to_host,
..
} = loaded;
// You can comment and uncomment this block out to get more useful information
// while you're working on the wasm backend!
// {
// println!("=========== Procedures ==========");
// println!("{:?}", procedures);
// println!("=================================\n");
// println!("=========== Interns ==========");
// println!("{:?}", interns);
// println!("=================================\n");
// println!("=========== Exposed ==========");
// println!("{:?}", exposed_to_host);
// println!("=================================\n");
// }
debug_assert_eq!(exposed_to_host.len(), 1);
let main_fn_symbol = loaded.entry_point.symbol;
let main_fn_index = procedures
.keys()
.position(|(s, _)| *s == main_fn_symbol)
.unwrap();
let exposed_to_host = exposed_to_host.keys().copied().collect::<MutSet<_>>();
let env = roc_gen_wasm::Env {
arena,
interns,
exposed_to_host,
};
let (mut parity_builder, mut wasm_module) =
roc_gen_wasm::build_module_help(&env, procedures).unwrap();
T::insert_test_wrapper(
arena,
&mut parity_builder,
&mut wasm_module,
TEST_WRAPPER_NAME,
main_fn_index as u32,
);
let mut module_bytes = std::vec::Vec::with_capacity(4096);
combine_and_serialize(&mut module_bytes, parity_builder, &mut wasm_module);
// for debugging (e.g. with wasm2wat or wasm-objdump)
if true {
use std::io::Write;
let mut hash_state = DefaultHasher::new();
src.hash(&mut hash_state);
let src_hash = hash_state.finish();
// Filename contains a hash of the Roc test source code. Helpful when comparing across commits.
let dir = "/tmp/roc/compiler/gen_wasm/output";
std::fs::create_dir_all(dir).unwrap();
let path = format!("{}/test-{:016x}.wasm", dir, src_hash);
// Print out filename (appears just after test name)
println!("dumping file {:?}", path);
match std::fs::File::create(path) {
Err(e) => eprintln!("Problem creating wasm debug file: {:?}", e),
Ok(mut file) => {
file.write_all(&module_bytes).unwrap();
}
}
}
// now, do wasmer stuff
use wasmer::{Instance, Module, Store};
let store = Store::default();
// let module = Module::from_file(&store, &test_wasm_path).unwrap();
let wasmer_module = Module::from_binary(&store, &module_bytes).unwrap();
// First, we create the `WasiEnv`
use wasmer_wasi::WasiState;
let mut wasi_env = WasiState::new("hello").finalize().unwrap();
// Then, we get the import object related to our WASI
// and attach it to the Wasm instance.
let import_object = wasi_env
.import_object(&wasmer_module)
.unwrap_or_else(|_| wasmer::imports!());
Instance::new(&wasmer_module, &import_object).unwrap()
}
#[allow(dead_code)]
pub fn assert_wasm_evals_to_help<T>(src: &str, expected: T) -> Result<T, String>
where
T: FromWasm32Memory + Wasm32TestResult,
{
let arena = bumpalo::Bump::new();
// NOTE the stdlib must be in the arena; just taking a reference will segfault
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
let instance = crate::helpers::eval::helper_wasm(&arena, src, stdlib, &expected);
let memory = instance.exports.get_memory("memory").unwrap();
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();
match test_wrapper.call(&[]) {
Err(e) => Err(format!("{:?}", e)),
Ok(result) => {
let address = match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!(),
};
let output = <T as FromWasm32Memory>::decode(memory, address as u32);
Ok(output)
}
}
}
#[macro_export]
macro_rules! assert_wasm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
match $crate::helpers::eval::assert_wasm_evals_to_help::<$ty>($src, $expected) {
Err(msg) => panic!("{:?}", msg),
Ok(actual) => {
assert_eq!($transform(actual), $expected)
}
}
};
($src:expr, $expected:expr, $ty:ty) => {
$crate::assert_wasm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform);
};
}
#[macro_export]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{
assert_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
}};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
// Same as above, except with an additional transformation argument.
{
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform);
}
};
}
#[allow(dead_code)]
pub fn identity<T>(value: T) -> T {
value
}

View file

@ -0,0 +1,43 @@
#[macro_use]
pub mod eval;
pub mod wasm32_test_result;
/// Used in the with_larger_debug_stack() function, for tests that otherwise
/// run out of stack space in debug builds (but don't in --release builds)
#[allow(dead_code)]
const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
/// Without this, some tests pass in `cargo test --release` but fail without
/// the --release flag because they run out of stack space. This increases
/// stack size for debug builds only, while leaving the stack space at the default
/// amount for release builds.
#[allow(dead_code)]
#[cfg(debug_assertions)]
pub fn with_larger_debug_stack<F>(run_test: F)
where
F: FnOnce(),
F: Send,
F: 'static,
{
std::thread::Builder::new()
.stack_size(EXPANDED_STACK_SIZE)
.spawn(run_test)
.expect("Error while spawning expanded dev stack size thread")
.join()
.expect("Error while joining expanded dev stack size thread")
}
/// In --release builds, don't increase the stack size. Run the test normally.
/// This way, we find out if any of our tests are blowing the stack even after
/// optimizations in release builds.
#[allow(dead_code)]
#[cfg(not(debug_assertions))]
#[inline(always)]
pub fn with_larger_debug_stack<F>(run_test: F)
where
F: FnOnce() -> (),
F: Send,
F: 'static,
{
run_test()
}

View file

@ -0,0 +1,270 @@
use parity_wasm::builder;
use roc_gen_wasm::code_builder::{Align, CodeBuilder, ValueType};
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
use roc_gen_wasm::module_builder::{Export, ExportType, WasmModule};
use roc_gen_wasm::LocalId;
use roc_std::{RocDec, RocList, RocOrder, RocStr};
pub trait Wasm32TestResult {
fn insert_test_wrapper<'a>(
arena: &'a bumpalo::Bump,
module_builder: &mut builder::ModuleBuilder,
wasm_module: &mut WasmModule<'a>,
wrapper_name: &str,
main_function_index: u32,
) {
let signature = builder::signature()
.with_result(parity_wasm::elements::ValueType::I32)
.build_sig();
// parity-wasm FunctionDefinition with no instructions
let empty_fn_def = builder::function().with_signature(signature).build();
let location = module_builder.push_function(empty_fn_def);
wasm_module.export.entries.push(Export {
name: wrapper_name.to_string(),
ty: ExportType::Func,
index: location.body,
});
let mut code_builder = CodeBuilder::new(arena);
Self::build_wrapper_body(&mut code_builder, main_function_index);
wasm_module.code.code_builders.push(code_builder);
}
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32);
}
macro_rules! build_wrapper_body_primitive {
($store_instruction: ident, $align: expr) => {
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
let frame_pointer_id = LocalId(0);
let frame_pointer = Some(frame_pointer_id);
let local_types = &[ValueType::I32];
let frame_size = 8;
code_builder.get_local(frame_pointer_id);
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
code_builder.inst_imm32(roc_gen_wasm::opcodes::CALL, 0, true, main_function_index);
code_builder.$store_instruction($align, 0);
code_builder.get_local(frame_pointer_id);
code_builder.finalize(local_types, frame_size, frame_pointer);
}
};
}
macro_rules! wasm_test_result_primitive {
($type_name: ident, $store_instruction: ident, $align: expr) => {
impl Wasm32TestResult for $type_name {
build_wrapper_body_primitive!($store_instruction, $align);
}
};
}
fn build_wrapper_body_stack_memory(
code_builder: &mut CodeBuilder,
main_function_index: u32,
size: usize,
) {
let local_id = LocalId(0);
let local_types = &[ValueType::I32];
let frame_pointer = Some(local_id);
code_builder.get_local(local_id);
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
code_builder.inst_imm32(roc_gen_wasm::opcodes::CALL, 0, true, main_function_index);
code_builder.get_local(local_id);
code_builder.finalize(local_types, size as i32, frame_pointer);
}
macro_rules! wasm_test_result_stack_memory {
($type_name: ident) => {
impl Wasm32TestResult for $type_name {
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
$type_name::ACTUAL_WIDTH,
)
}
}
};
}
wasm_test_result_primitive!(bool, i32_store8, Align::Bytes1);
wasm_test_result_primitive!(RocOrder, i32_store8, Align::Bytes1);
wasm_test_result_primitive!(u8, i32_store8, Align::Bytes1);
wasm_test_result_primitive!(i8, i32_store8, Align::Bytes1);
wasm_test_result_primitive!(u16, i32_store16, Align::Bytes2);
wasm_test_result_primitive!(i16, i32_store16, Align::Bytes2);
wasm_test_result_primitive!(u32, i32_store, Align::Bytes4);
wasm_test_result_primitive!(i32, i32_store, Align::Bytes4);
wasm_test_result_primitive!(u64, i64_store, Align::Bytes8);
wasm_test_result_primitive!(i64, i64_store, Align::Bytes8);
wasm_test_result_primitive!(f32, f32_store, Align::Bytes8);
wasm_test_result_primitive!(f64, f64_store, Align::Bytes8);
wasm_test_result_stack_memory!(u128);
wasm_test_result_stack_memory!(i128);
wasm_test_result_stack_memory!(RocDec);
wasm_test_result_stack_memory!(RocStr);
impl<T: Wasm32TestResult> Wasm32TestResult for RocList<T> {
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(code_builder, main_function_index, 12)
}
}
impl<T: Wasm32TestResult> Wasm32TestResult for &'_ T {
build_wrapper_body_primitive!(i32_store, Align::Bytes4);
}
impl<T, const N: usize> Wasm32TestResult for [T; N]
where
T: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(code_builder, main_function_index, N * T::ACTUAL_WIDTH)
}
}
impl<T, U> Wasm32TestResult for (T, U)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH,
)
}
}
impl<T, U, V> Wasm32TestResult for (T, U, V)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH,
)
}
}
impl<T, U, V, W> Wasm32TestResult for (T, U, V, W)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
W: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH + W::ACTUAL_WIDTH,
)
}
}
impl<T, U, V, W, X> Wasm32TestResult for (T, U, V, W, X)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
W: Wasm32TestResult + FromWasm32Memory,
X: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH + W::ACTUAL_WIDTH + X::ACTUAL_WIDTH,
)
}
}
impl<T, U, V, W, X, Y> Wasm32TestResult for (T, U, V, W, X, Y)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
W: Wasm32TestResult + FromWasm32Memory,
X: Wasm32TestResult + FromWasm32Memory,
Y: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH
+ U::ACTUAL_WIDTH
+ V::ACTUAL_WIDTH
+ W::ACTUAL_WIDTH
+ X::ACTUAL_WIDTH
+ Y::ACTUAL_WIDTH,
)
}
}
impl<T, U, V, W, X, Y, Z> Wasm32TestResult for (T, U, V, W, X, Y, Z)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
W: Wasm32TestResult + FromWasm32Memory,
X: Wasm32TestResult + FromWasm32Memory,
Y: Wasm32TestResult + FromWasm32Memory,
Z: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH
+ U::ACTUAL_WIDTH
+ V::ACTUAL_WIDTH
+ W::ACTUAL_WIDTH
+ X::ACTUAL_WIDTH
+ Y::ACTUAL_WIDTH
+ Z::ACTUAL_WIDTH,
)
}
}
impl<T, U, V, W, X, Y, Z, A> Wasm32TestResult for (T, U, V, W, X, Y, Z, A)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
W: Wasm32TestResult + FromWasm32Memory,
X: Wasm32TestResult + FromWasm32Memory,
Y: Wasm32TestResult + FromWasm32Memory,
Z: Wasm32TestResult + FromWasm32Memory,
A: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
build_wrapper_body_stack_memory(
code_builder,
main_function_index,
T::ACTUAL_WIDTH
+ U::ACTUAL_WIDTH
+ V::ACTUAL_WIDTH
+ W::ACTUAL_WIDTH
+ X::ACTUAL_WIDTH
+ Y::ACTUAL_WIDTH
+ Z::ACTUAL_WIDTH
+ A::ACTUAL_WIDTH,
)
}
}

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
// );
// }