mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-18 17:49:47 +00:00
1929 lines
35 KiB
Rust
1929 lines
35 KiB
Rust
#![cfg(test)]
|
|
#![warn(clippy::dbg_macro)]
|
|
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
|
#![allow(clippy::large_enum_variant)]
|
|
// we actually want to compare against the literal float bits
|
|
#![allow(clippy::float_cmp)]
|
|
|
|
#[macro_use]
|
|
extern crate indoc;
|
|
|
|
/// 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;
|
|
|
|
use roc_load::ExecutionMode;
|
|
use roc_load::LoadConfig;
|
|
use test_mono_macros::*;
|
|
|
|
use roc_collections::all::MutMap;
|
|
use roc_load::Threading;
|
|
use roc_module::symbol::Symbol;
|
|
use roc_mono::ir::Proc;
|
|
use roc_mono::ir::ProcLayout;
|
|
use roc_mono::layout::STLayoutInterner;
|
|
|
|
const TARGET_INFO: roc_target::TargetInfo = roc_target::TargetInfo::default_x86_64();
|
|
|
|
/// 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()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
fn compiles_to_ir(test_name: &str, src: &str) {
|
|
use bumpalo::Bump;
|
|
use std::path::PathBuf;
|
|
|
|
let arena = &Bump::new();
|
|
|
|
let filename = PathBuf::from("Test.roc");
|
|
let src_dir = PathBuf::from("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 load_config = LoadConfig {
|
|
target_info: TARGET_INFO,
|
|
threading: Threading::Single,
|
|
render: roc_reporting::report::RenderTarget::Generic,
|
|
exec_mode: ExecutionMode::Executable,
|
|
};
|
|
let loaded = roc_load::load_and_monomorphize_from_str(
|
|
arena,
|
|
filename,
|
|
module_src,
|
|
src_dir,
|
|
Default::default(),
|
|
load_config,
|
|
);
|
|
|
|
let mut loaded = match loaded {
|
|
Ok(x) => x,
|
|
Err(roc_load::LoadingProblem::FormattedReport(report)) => {
|
|
println!("{}", report);
|
|
panic!();
|
|
}
|
|
Err(e) => panic!("{:?}", e),
|
|
};
|
|
|
|
use roc_load::MonomorphizedModule;
|
|
let MonomorphizedModule {
|
|
module_id: home,
|
|
procedures,
|
|
exposed_to_host,
|
|
layout_interner,
|
|
..
|
|
} = loaded;
|
|
|
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
|
|
|
if !can_problems.is_empty() {
|
|
println!("Ignoring {} canonicalization problems", can_problems.len());
|
|
}
|
|
|
|
assert!(type_problems.is_empty());
|
|
|
|
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
|
|
|
let main_fn_symbol = exposed_to_host.values.keys().copied().next().unwrap();
|
|
|
|
verify_procedures(test_name, layout_interner, procedures, main_fn_symbol);
|
|
}
|
|
|
|
#[cfg(debug_assertions)]
|
|
fn verify_procedures<'a>(
|
|
test_name: &str,
|
|
interner: STLayoutInterner<'a>,
|
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
|
main_fn_symbol: Symbol,
|
|
) {
|
|
let index = procedures
|
|
.keys()
|
|
.position(|(s, _)| *s == main_fn_symbol)
|
|
.unwrap();
|
|
|
|
let mut procs_string = procedures
|
|
.values()
|
|
.map(|proc| proc.to_pretty(&interner, 200))
|
|
.collect::<Vec<_>>();
|
|
|
|
let main_fn = procs_string.swap_remove(index);
|
|
|
|
procs_string.sort();
|
|
procs_string.push(main_fn);
|
|
|
|
let result = procs_string.join("\n");
|
|
|
|
let path = format!("generated/{}.txt", test_name);
|
|
std::fs::create_dir_all("generated").unwrap();
|
|
std::fs::write(&path, result).unwrap();
|
|
|
|
use std::process::Command;
|
|
|
|
let is_tracked = Command::new("git")
|
|
.args(&["ls-files", "--error-unmatch", &path])
|
|
.output()
|
|
.unwrap();
|
|
|
|
if !is_tracked.status.success() {
|
|
panic!(
|
|
"The file {:?} is not tracked by git. Try using `git add` on it",
|
|
&path
|
|
);
|
|
}
|
|
|
|
let has_changes = Command::new("git")
|
|
.args(&["diff", "--color=always", &path])
|
|
.output()
|
|
.unwrap();
|
|
|
|
if !has_changes.status.success() {
|
|
eprintln!("`git diff {:?}` failed", &path);
|
|
unreachable!();
|
|
}
|
|
|
|
if !has_changes.stdout.is_empty() {
|
|
println!("{}", std::str::from_utf8(&has_changes.stdout).unwrap());
|
|
panic!("Output changed: resolve conflicts and `git add` the file.");
|
|
}
|
|
}
|
|
|
|
// NOTE because the Show instance of module names is different in --release mode,
|
|
// these tests would all fail. In the future, when we do interesting optimizations,
|
|
// we'll likely want some tests for --release too.
|
|
#[cfg(not(debug_assertions))]
|
|
fn verify_procedures(
|
|
_expected: &str,
|
|
_interner: STLayoutInterner<'_>,
|
|
_procedures: MutMap<(Symbol, ProcLayout<'_>), Proc<'_>>,
|
|
_main_fn_symbol: Symbol,
|
|
) {
|
|
// Do nothing
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_int_literal() {
|
|
r#"
|
|
5
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_int_add() {
|
|
r#"
|
|
x = [1,2]
|
|
5 + 4 + 3 + List.len x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_assignment() {
|
|
r#"
|
|
x = 5
|
|
|
|
x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_when_maybe() {
|
|
r#"
|
|
when Just 3 is
|
|
Just n -> n
|
|
Nothing -> 0
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_when_these() {
|
|
r#"
|
|
when These 1 2 is
|
|
This x -> x
|
|
That y -> y
|
|
These x _ -> x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_when_record() {
|
|
r#"
|
|
when { x: 1, y: 3.14 } is
|
|
{ x } -> x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_plus() {
|
|
r#"
|
|
1 + 2
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_round() {
|
|
r#"
|
|
Num.round 3.6
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_when_idiv() {
|
|
r#"
|
|
when Num.divTruncChecked 1000 10 is
|
|
Ok val -> val
|
|
Err _ -> -1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_two_defs() {
|
|
r#"
|
|
x = 3
|
|
y = 4
|
|
|
|
x + y
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn ir_when_just() {
|
|
r#"
|
|
x : [Nothing, Just I64]
|
|
x = Just 41
|
|
|
|
when x is
|
|
Just v -> v + 0x1
|
|
Nothing -> 0x1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn one_element_tag() {
|
|
r#"
|
|
x : [Pair I64]
|
|
x = Pair 2
|
|
|
|
x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn guard_pattern_true() {
|
|
r#"
|
|
wrapper = \{} ->
|
|
when 2 is
|
|
2 if False -> 42
|
|
_ -> 0
|
|
|
|
wrapper {}
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn when_on_record() {
|
|
r#"
|
|
when { x: 0x2 } is
|
|
{ x } -> x + 3
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn when_nested_maybe() {
|
|
r#"
|
|
Maybe a : [Nothing, Just a]
|
|
|
|
x : Maybe (Maybe I64)
|
|
x = Just (Just 41)
|
|
|
|
when x is
|
|
Just (Just v) -> v + 0x1
|
|
_ -> 0x1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn when_on_two_values() {
|
|
r#"
|
|
when Pair 2 3 is
|
|
Pair 4 3 -> 9
|
|
Pair a b -> a + b
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn dict() {
|
|
r#"
|
|
Dict.len Dict.empty
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_append_closure() {
|
|
r#"
|
|
myFunction = \l -> List.append l 42
|
|
|
|
myFunction [1, 2]
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_append() {
|
|
// TODO this leaks at the moment
|
|
// ListAppend needs to decrement its arguments
|
|
r#"
|
|
List.append [1] 2
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_len() {
|
|
r#"
|
|
x = [1,2,3]
|
|
y = [1.0]
|
|
|
|
List.len x + List.len y
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn when_joinpoint() {
|
|
r#"
|
|
wrapper = \{} ->
|
|
x : [Red, White, Blue]
|
|
x = Blue
|
|
|
|
y =
|
|
when x is
|
|
Red -> 1
|
|
White -> 2
|
|
Blue -> 3
|
|
|
|
y
|
|
|
|
wrapper {}
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn simple_if() {
|
|
r#"
|
|
if True then
|
|
1
|
|
else
|
|
2
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn if_multi_branch() {
|
|
r#"
|
|
if True then
|
|
1
|
|
else if False then
|
|
2
|
|
else
|
|
3
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn when_on_result() {
|
|
r#"
|
|
wrapper = \{} ->
|
|
x : Result I64 I64
|
|
x = Ok 2
|
|
|
|
y =
|
|
when x is
|
|
Ok 3 -> 1
|
|
Ok _ -> 2
|
|
Err _ -> 3
|
|
y
|
|
|
|
wrapper {}
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn let_with_record_pattern() {
|
|
r#"
|
|
{ x } = { x: 0x2, y: 3.14 }
|
|
|
|
x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn let_with_record_pattern_list() {
|
|
r#"
|
|
{ x } = { x: [1, 3, 4], y: 3.14 }
|
|
|
|
x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn if_guard_bind_variable_false() {
|
|
r#"
|
|
wrapper = \{} ->
|
|
when 10 is
|
|
x if x == 5 -> 0
|
|
_ -> 42
|
|
|
|
wrapper {}
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn alias_variable() {
|
|
r#"
|
|
x = 5
|
|
y = x
|
|
|
|
3
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn alias_variable_and_return_it() {
|
|
r#"
|
|
x = 5
|
|
y = x
|
|
|
|
y
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn branch_store_variable() {
|
|
r#"
|
|
when 0 is
|
|
1 -> 12
|
|
a -> a
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_pass_to_function() {
|
|
r#"
|
|
x : List I64
|
|
x = [1,2,3]
|
|
|
|
id : List I64 -> List I64
|
|
id = \y -> List.set y 0 0
|
|
|
|
id x
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn record_optional_field_let_no_use_default() {
|
|
r#"
|
|
f = \r ->
|
|
{ x ? 10, y } = r
|
|
x + y
|
|
|
|
|
|
f { x: 4, y: 9 }
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn record_optional_field_let_use_default() {
|
|
r#"
|
|
f = \r ->
|
|
{ x ? 10, y } = r
|
|
x + y
|
|
|
|
|
|
f { y: 9 }
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn record_optional_field_function_no_use_default() {
|
|
r#"
|
|
f = \{ x ? 10, y } -> x + y
|
|
|
|
|
|
f { x: 4, y: 9 }
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn record_optional_field_function_use_default() {
|
|
r#"
|
|
f = \{ x ? 10, y } -> x + y
|
|
|
|
|
|
f { y: 9 }
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn quicksort_help() {
|
|
// do we still need with_larger_debug_stack?
|
|
r#"
|
|
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
|
quicksortHelp = \list, low, high ->
|
|
if low < high then
|
|
(Pair partitionIndex partitioned) = Pair 0 []
|
|
|
|
partitioned
|
|
|> quicksortHelp low (partitionIndex - 1)
|
|
|> quicksortHelp (partitionIndex + 1) high
|
|
else
|
|
list
|
|
|
|
quicksortHelp [] 0 0
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn quicksort_swap() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
swap = \list ->
|
|
when Pair (List.get list 0) (List.get list 0) is
|
|
Pair (Ok atI) (Ok atJ) ->
|
|
list
|
|
|> List.set 0 atJ
|
|
|> List.set 0 atI
|
|
|
|
_ ->
|
|
[]
|
|
|
|
main =
|
|
swap [1, 2]
|
|
"#
|
|
)
|
|
}
|
|
|
|
// #[ignore]
|
|
// #[mono_test]
|
|
// fn quicksort_partition_help() {
|
|
// indoc!(
|
|
// r#"
|
|
// app "test" provides [main] to "./platform"
|
|
|
|
// partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [Pair I64 (List (Num a))]
|
|
// partitionHelp = \i, j, list, high, pivot ->
|
|
// if j < high then
|
|
// when List.get list j is
|
|
// Ok value ->
|
|
// if value <= pivot then
|
|
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
|
// else
|
|
// partitionHelp i (j + 1) list high pivot
|
|
|
|
// Err _ ->
|
|
// Pair i list
|
|
// else
|
|
// Pair i list
|
|
|
|
// main =
|
|
// partitionHelp 0 0 [] 0 0
|
|
// "#
|
|
// )
|
|
// }
|
|
|
|
// #[ignore]
|
|
// #[mono_test]
|
|
// fn quicksort_full() {
|
|
// indoc!(
|
|
// r#"
|
|
// app "test" provides [main] to "./platform"
|
|
|
|
// quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
|
// quicksortHelp = \list, low, high ->
|
|
// if low < high then
|
|
// (Pair partitionIndex partitioned) = partition low high list
|
|
|
|
// partitioned
|
|
// |> quicksortHelp low (partitionIndex - 1)
|
|
// |> quicksortHelp (partitionIndex + 1) high
|
|
// else
|
|
// list
|
|
|
|
// swap : I64, I64, List a -> List a
|
|
// swap = \i, j, list ->
|
|
// when Pair (List.get list i) (List.get list j) is
|
|
// Pair (Ok atI) (Ok atJ) ->
|
|
// list
|
|
// |> List.set i atJ
|
|
// |> List.set j atI
|
|
|
|
// _ ->
|
|
// []
|
|
|
|
// partition : I64, I64, List (Num a) -> [Pair I64 (List (Num a))]
|
|
// partition = \low, high, initialList ->
|
|
// when List.get initialList high is
|
|
// Ok pivot ->
|
|
// when partitionHelp (low - 1) low initialList high pivot is
|
|
// Pair newI newList ->
|
|
// Pair (newI + 1) (swap (newI + 1) high newList)
|
|
|
|
// Err _ ->
|
|
// Pair (low - 1) initialList
|
|
|
|
// partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [Pair I64 (List (Num a))]
|
|
// partitionHelp = \i, j, list, high, pivot ->
|
|
// if j < high then
|
|
// when List.get list j is
|
|
// Ok value ->
|
|
// if value <= pivot then
|
|
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
|
// else
|
|
// partitionHelp i (j + 1) list high pivot
|
|
|
|
// Err _ ->
|
|
// Pair i list
|
|
// else
|
|
// Pair i list
|
|
|
|
// quicksort = \originalList ->
|
|
// n = List.len originalList
|
|
// quicksortHelp originalList 0 (n - 1)
|
|
|
|
// main =
|
|
// quicksort [1,2,3]
|
|
// "#
|
|
// )
|
|
// }
|
|
|
|
#[mono_test]
|
|
fn factorial() {
|
|
r#"
|
|
factorial = \n, accum ->
|
|
when n is
|
|
0 ->
|
|
accum
|
|
|
|
_ ->
|
|
factorial (n - 1) (n * accum)
|
|
|
|
factorial 10 1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn is_nil() {
|
|
r#"
|
|
ConsList a : [Cons a (ConsList a), Nil]
|
|
|
|
isNil : ConsList a -> Bool
|
|
isNil = \list ->
|
|
when list is
|
|
Nil -> True
|
|
Cons _ _ -> False
|
|
|
|
isNil (Cons 0x2 Nil)
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore]
|
|
fn has_none() {
|
|
r#"
|
|
Maybe a : [Just a, Nothing]
|
|
ConsList a : [Cons a (ConsList a), Nil]
|
|
|
|
hasNone : ConsList (Maybe a) -> Bool
|
|
hasNone = \list ->
|
|
when list is
|
|
Nil -> False
|
|
Cons Nothing _ -> True
|
|
Cons (Just _) xs -> hasNone xs
|
|
|
|
hasNone (Cons (Just 3) Nil)
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn mk_pair_of() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
mkPairOf = \x -> Pair x x
|
|
|
|
main =
|
|
mkPairOf [1,2,3]
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn fst() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
fst = \x, _ -> x
|
|
|
|
main =
|
|
fst [1,2,3] [3,2,1]
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_cannot_update_inplace() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
x : List I64
|
|
x = [1,2,3]
|
|
|
|
add : List I64 -> List I64
|
|
add = \y -> List.set y 0 0
|
|
|
|
main =
|
|
List.len (add x) + List.len x
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_get() {
|
|
r#"
|
|
wrapper = \{} ->
|
|
List.get [1,2,3] 0
|
|
|
|
wrapper {}
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn peano() {
|
|
r#"
|
|
Peano : [S Peano, Z]
|
|
|
|
three : Peano
|
|
three = S (S (S Z))
|
|
|
|
three
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn peano1() {
|
|
r#"
|
|
Peano : [S Peano, Z]
|
|
|
|
three : Peano
|
|
three = S (S (S Z))
|
|
|
|
when three is
|
|
Z -> 0
|
|
S _ -> 1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn peano2() {
|
|
r#"
|
|
Peano : [S Peano, Z]
|
|
|
|
three : Peano
|
|
three = S (S (S Z))
|
|
|
|
when three is
|
|
S (S _) -> 1
|
|
S (_) -> 0
|
|
Z -> 0
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn optional_when() {
|
|
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
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn nested_pattern_match() {
|
|
r#"
|
|
Maybe a : [Nothing, Just a]
|
|
|
|
x : Maybe (Maybe I64)
|
|
x = Just (Just 41)
|
|
|
|
when x is
|
|
Just (Just v) -> v + 0x1
|
|
_ -> 0x1
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore]
|
|
fn linked_list_length_twice() {
|
|
r#"
|
|
LinkedList a : [Nil, Cons a (LinkedList a)]
|
|
|
|
nil : LinkedList I64
|
|
nil = Nil
|
|
|
|
length : LinkedList a -> I64
|
|
length = \list ->
|
|
when list is
|
|
Nil -> 0
|
|
Cons _ rest -> 1 + length rest
|
|
|
|
length nil + length nil
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn rigids() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
swap : Nat, Nat, List a -> List a
|
|
swap = \i, j, list ->
|
|
when Pair (List.get list i) (List.get list j) is
|
|
Pair (Ok atI) (Ok atJ) ->
|
|
foo = atJ
|
|
|
|
list
|
|
|> List.set i foo
|
|
|> List.set j atI
|
|
|
|
_ ->
|
|
[]
|
|
|
|
main =
|
|
swap 0 0 [0x1]
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn let_x_in_x() {
|
|
r#"
|
|
x = 5
|
|
|
|
answer =
|
|
1337
|
|
|
|
unused =
|
|
nested = 17
|
|
nested
|
|
|
|
answer
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn let_x_in_x_indirect() {
|
|
r#"
|
|
x = 5
|
|
|
|
answer =
|
|
1337
|
|
|
|
unused =
|
|
nested = 17
|
|
|
|
i = 1
|
|
|
|
nested
|
|
|
|
{ answer, unused }.answer
|
|
"#
|
|
}
|
|
|
|
#[mono_test]
|
|
fn nested_closure() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
foo = \{} ->
|
|
x = 42
|
|
f = \{} -> x
|
|
f
|
|
|
|
main =
|
|
f = foo {}
|
|
f {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn closure_in_list() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
foo = \{} ->
|
|
x = 41
|
|
|
|
f = \{} -> x
|
|
|
|
[f]
|
|
|
|
main =
|
|
items = foo {}
|
|
|
|
List.len items
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[ignore]
|
|
#[mono_test]
|
|
fn somehow_drops_definitions() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
one : I64
|
|
one = 1
|
|
|
|
two : I64
|
|
two = 2
|
|
|
|
increment : I64 -> I64
|
|
increment = \x -> x + one
|
|
|
|
double : I64 -> I64
|
|
double = \x -> x * two
|
|
|
|
apply : (a -> a), a -> a
|
|
apply = \f, x -> f x
|
|
|
|
main =
|
|
apply (if True then increment else double) 42
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn specialize_closures() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
|
|
apply : (a -> a), a -> a
|
|
apply = \f, x -> f x
|
|
|
|
main =
|
|
one : I64
|
|
one = 1
|
|
|
|
two : I64
|
|
two = 2
|
|
|
|
b : Bool
|
|
b = True
|
|
|
|
increment : I64 -> I64
|
|
increment = \x -> x + one
|
|
|
|
double : I64 -> I64
|
|
double = \x -> if b then x * two else x
|
|
|
|
apply (if True then increment else double) 42
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn specialize_lowlevel() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
apply : (a -> a), a -> a
|
|
apply = \f, x -> f x
|
|
|
|
main =
|
|
one : I64
|
|
one = 1
|
|
|
|
two : I64
|
|
two = 2
|
|
|
|
increment : I64 -> I64
|
|
increment = \x -> x + one
|
|
|
|
double : I64 -> I64
|
|
double = \x -> x * two
|
|
|
|
(if True then increment else double) 42
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn empty_list_of_function_type() {
|
|
// see https://github.com/roc-lang/roc/issues/1732
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
myList : List (Str -> Str)
|
|
myList = []
|
|
|
|
myClosure : Str -> Str
|
|
myClosure = \_ -> "bar"
|
|
|
|
choose =
|
|
if False then
|
|
myList
|
|
else
|
|
[myClosure]
|
|
|
|
when List.get choose 0 is
|
|
Ok f -> f "foo"
|
|
Err _ -> "bad!"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_ints() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
x = 100
|
|
|
|
f : U8, U32 -> Nat
|
|
f = \_, _ -> 18
|
|
|
|
f x x
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_floats() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
x = 100.0
|
|
|
|
f : F32, F64 -> Nat
|
|
f = \_, _ -> 18
|
|
|
|
f x x
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore = "TODO"]
|
|
fn monomorphized_ints_aliased() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
y = 100
|
|
w1 = y
|
|
w2 = y
|
|
|
|
f = \_, _ -> 1
|
|
|
|
f1 : U8, U32 -> Nat
|
|
f1 = f
|
|
|
|
f2 : U32, U8 -> Nat
|
|
f2 = f
|
|
|
|
f1 w1 w2 + f2 w1 w2
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_tag() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
b = False
|
|
f : Bool, [True, False, Idk] -> U8
|
|
f = \_, _ -> 18
|
|
f b b
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_tag_with_aliased_args() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
b = False
|
|
c = False
|
|
a = A b c
|
|
f : [A Bool Bool] -> Nat
|
|
f = \_ -> 1
|
|
f a
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_list() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
l = [1, 2, 3]
|
|
|
|
f : List U8, List U16 -> Nat
|
|
f = \_, _ -> 18
|
|
|
|
f l l
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn monomorphized_applied_tag() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
a = A "A"
|
|
f = \x ->
|
|
when x is
|
|
A y -> y
|
|
B y -> y
|
|
f a
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore = "Cannot compile polymorphic closures yet"]
|
|
fn aliased_polymorphic_closure() {
|
|
indoc!(
|
|
r#"
|
|
n : U8
|
|
n = 1
|
|
f = \{} -> (\a -> n)
|
|
g = f {}
|
|
g {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_2535_polymorphic_fields_referenced_in_list() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [nums] to "./platform"
|
|
|
|
alpha = { a: 1, b: 2 }
|
|
|
|
nums : List U8
|
|
nums =
|
|
[
|
|
alpha.a,
|
|
alpha.b,
|
|
]
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_2725_alias_polymorphic_lambda() {
|
|
indoc!(
|
|
r#"
|
|
wrap = \value -> Tag value
|
|
wrapIt = wrap
|
|
wrapIt 42
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_2583_specialize_errors_behind_unified_branches() {
|
|
indoc!(
|
|
r#"
|
|
if True then List.first [] else Str.toI64 ""
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_2810() {
|
|
indoc!(
|
|
r#"
|
|
Command : [Command Tool]
|
|
|
|
Job : [Job Command]
|
|
|
|
Tool : [SystemTool, FromJob Job]
|
|
|
|
a : Job
|
|
a = Job (Command (FromJob (Job (Command SystemTool))))
|
|
a
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_2811() {
|
|
indoc!(
|
|
r#"
|
|
x = Command { tool: "bash" }
|
|
Command c = x
|
|
c.tool
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn specialize_ability_call() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
Hash has
|
|
hash : a -> U64 | a has Hash
|
|
|
|
Id := U64 has [Hash {hash}]
|
|
|
|
hash : Id -> U64
|
|
hash = \@Id n -> n
|
|
|
|
main = hash (@Id 1234)
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn opaque_assign_to_symbol() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [out] to "./platform"
|
|
|
|
Variable := U8
|
|
|
|
fromUtf8 : U8 -> Result Variable [InvalidVariableUtf8]
|
|
fromUtf8 = \char ->
|
|
Ok (@Variable char)
|
|
|
|
out = fromUtf8 98
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [myU8Bytes] to "./platform"
|
|
|
|
Encoder fmt := List U8, fmt -> List U8 | fmt has Format
|
|
|
|
Encoding has
|
|
toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format
|
|
|
|
Format has
|
|
u8 : U8 -> Encoder fmt | fmt has Format
|
|
|
|
|
|
Linear := {} has [Format {u8}]
|
|
|
|
u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n)
|
|
|
|
MyU8 := U8 has [Encoding {toEncoder}]
|
|
|
|
toEncoder = \@MyU8 n -> u8 n
|
|
|
|
myU8Bytes =
|
|
when toEncoder (@MyU8 15) is
|
|
@Encoder doEncode -> doEncode [] (@Linear {})
|
|
"#
|
|
)
|
|
}
|
|
|
|
// #[ignore]
|
|
// #[mono_test]
|
|
// fn static_str_closure() {
|
|
// indoc!(
|
|
// r#"
|
|
// app "test" provides [main] to "./platform"
|
|
|
|
// main : Str
|
|
// main =
|
|
// x = "long string that is malloced"
|
|
|
|
// f : {} -> Str
|
|
// f = (\_ -> x)
|
|
|
|
// f {}
|
|
// "#
|
|
// )
|
|
// }
|
|
|
|
#[mono_test]
|
|
fn list_map_closure_borrows() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [out] to "./platform"
|
|
|
|
list = [Str.concat "lllllllllllllllllllllooooooooooong" "g"]
|
|
|
|
example1 = List.map list \string -> Str.repeat string 2
|
|
|
|
out =
|
|
when List.get example1 0 is
|
|
Ok s -> s
|
|
Err _ -> "Hello, World!\n"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_map_closure_owns() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [out] to "./platform"
|
|
|
|
list = [Str.concat "lllllllllllllllllllllooooooooooong" "g"]
|
|
|
|
example2 = List.map list \string -> Str.concat string "!"
|
|
|
|
out =
|
|
when List.get example2 0 is
|
|
Ok s -> s
|
|
Err _ -> "Hello, World!\n"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn list_sort_asc() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [out] to "./platform"
|
|
|
|
out = List.sortAsc [4, 3, 2, 1]
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore]
|
|
fn encode_custom_type() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
HelloWorld := {}
|
|
toEncoder = \@HelloWorld {} ->
|
|
Encode.custom \bytes, fmt ->
|
|
bytes
|
|
|> Encode.appendWith (Encode.string "Hello, World!\n") fmt
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes (@HelloWorld {}) Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_string() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes "abc" Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
#[ignore = "TODO"]
|
|
fn encode_derived_record() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes {a: "a"} Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn tail_call_elimination() {
|
|
indoc!(
|
|
r#"
|
|
sum = \n, accum ->
|
|
when n is
|
|
0 -> accum
|
|
_ -> sum (n - 1) (n + accum)
|
|
|
|
sum 1_000_000 0
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn tail_call_with_same_layout_different_lambda_sets() {
|
|
indoc!(
|
|
r#"
|
|
chain = \in, buildLazy ->
|
|
\{} ->
|
|
thunk = buildLazy in
|
|
thunk {}
|
|
|
|
chain 1u8 \_ -> chain 1u8 \_ -> (\{} -> "")
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn tail_call_with_different_layout() {
|
|
indoc!(
|
|
r#"
|
|
chain = \in, buildLazy ->
|
|
\{} ->
|
|
thunk = buildLazy in
|
|
thunk {}
|
|
|
|
chain 1u8 \_ -> chain 1u16 \_ -> (\{} -> "")
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn lambda_capture_niche_u8_vs_u64() {
|
|
indoc!(
|
|
r#"
|
|
capture : _ -> ({} -> Str)
|
|
capture = \val ->
|
|
\{} ->
|
|
Num.toStr val
|
|
|
|
x : [True, False]
|
|
x = True
|
|
|
|
fun =
|
|
when x is
|
|
True -> capture 123u64
|
|
False -> capture 18u8
|
|
|
|
fun {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn lambda_capture_niches_with_other_lambda_capture() {
|
|
indoc!(
|
|
r#"
|
|
capture : a -> ({} -> Str)
|
|
capture = \val ->
|
|
\{} ->
|
|
when val is
|
|
_ -> ""
|
|
|
|
capture2 = \val -> \{} -> "\(val)"
|
|
|
|
x : [A, B, C]
|
|
x = A
|
|
|
|
fun =
|
|
when x is
|
|
A -> capture {}
|
|
B -> capture2 "foo"
|
|
C -> capture 1u64
|
|
|
|
fun {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn lambda_capture_niches_with_non_capturing_function() {
|
|
indoc!(
|
|
r#"
|
|
capture : a -> ({} -> Str)
|
|
capture = \val ->
|
|
\{} ->
|
|
when val is
|
|
_ -> ""
|
|
|
|
triv = \{} -> ""
|
|
|
|
x : [A, B, C]
|
|
x = A
|
|
|
|
fun =
|
|
when x is
|
|
A -> capture {}
|
|
B -> triv
|
|
C -> capture 1u64
|
|
|
|
fun {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn lambda_capture_niches_have_captured_function_in_closure() {
|
|
indoc!(
|
|
r#"
|
|
Lazy a : {} -> a
|
|
|
|
after : Lazy a, (a -> Lazy b) -> Lazy b
|
|
after = \effect, map ->
|
|
thunk = \{} ->
|
|
when map (effect {}) is
|
|
b -> b {}
|
|
thunk
|
|
|
|
f = \_ -> \_ -> ""
|
|
g = \{ s1 } -> \_ -> s1
|
|
|
|
x : [True, False]
|
|
x = True
|
|
|
|
fun =
|
|
when x is
|
|
True -> after (\{} -> "") f
|
|
False -> after (\{} -> {s1: "s1"}) g
|
|
|
|
fun {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn lambda_set_niche_same_layout_different_constructor() {
|
|
indoc!(
|
|
r#"
|
|
capture : a -> ({} -> Str)
|
|
capture = \val ->
|
|
thunk =
|
|
\{} ->
|
|
when val is
|
|
_ -> ""
|
|
thunk
|
|
|
|
x : [True, False]
|
|
x = True
|
|
|
|
fun =
|
|
when x is
|
|
True -> capture {a: ""}
|
|
False -> capture (A "")
|
|
fun
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn choose_u64_layout() {
|
|
indoc!(
|
|
r#"
|
|
9999999999999999999 + 1
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn choose_i128_layout() {
|
|
indoc!(
|
|
r#"
|
|
{
|
|
a: 18446744073709551616 + 1,
|
|
b: -9223372036854775809 + 1,
|
|
}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn choose_u128_layout() {
|
|
indoc!(
|
|
r#"
|
|
170141183460469231731687303715884105728 + 1
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn recursive_call_capturing_function() {
|
|
indoc!(
|
|
r#"
|
|
a = \b ->
|
|
c : U32 -> U32
|
|
c = \d ->
|
|
if True then d else c (d+b)
|
|
c 0
|
|
|
|
a 6
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn call_function_in_empty_list() {
|
|
indoc!(
|
|
r#"
|
|
lst : List ({} -> {})
|
|
lst = []
|
|
List.map lst \f -> f {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn call_function_in_empty_list_unbound() {
|
|
indoc!(
|
|
r#"
|
|
lst = []
|
|
List.map lst \f -> f {}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn instantiate_annotated_as_recursive_alias_toplevel() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [it] to "./platform"
|
|
|
|
Value : [Nil, Array (List Value)]
|
|
|
|
foo : [Nil]*
|
|
foo = Nil
|
|
|
|
it : Value
|
|
it = foo
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn instantiate_annotated_as_recursive_alias_polymorphic_expr() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
Value : [Nil, Array (List Value)]
|
|
|
|
foo : [Nil]*
|
|
foo = Nil
|
|
|
|
it : Value
|
|
it = foo
|
|
|
|
it
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
|
|
indoc!(
|
|
r#"
|
|
app "test" provides [main] to "./platform"
|
|
|
|
main =
|
|
Value : [Nil, Array (List Value)]
|
|
|
|
foo : [Nil]*
|
|
foo = Nil
|
|
|
|
v1 : Value
|
|
v1 = foo
|
|
|
|
Value2 : [Nil, B U16, Array (List Value)]
|
|
|
|
v2 : Value2
|
|
v2 = foo
|
|
|
|
{v1, v2}
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_record_one_field_string() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes {a: "foo"} Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_record_two_field_strings() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes {a: "foo", b: "bar"} Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_nested_record_string() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
result = Str.fromUtf8 (Encode.toBytes {a: {b: "bar"}} Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_tag_one_field_string() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
x : [A Str]
|
|
x = A "foo"
|
|
result = Str.fromUtf8 (Encode.toBytes x Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn encode_derived_tag_two_payloads_string() {
|
|
indoc!(
|
|
r#"
|
|
app "test"
|
|
imports [Encode.{ toEncoder }, Json]
|
|
provides [main] to "./platform"
|
|
|
|
main =
|
|
x : [A Str Str]
|
|
x = A "foo" "foo"
|
|
result = Str.fromUtf8 (Encode.toBytes x Json.toUtf8)
|
|
when result is
|
|
Ok s -> s
|
|
_ -> "<bad>"
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_3560_nested_tag_constructor_is_newtype() {
|
|
indoc!(
|
|
r#"
|
|
when Wrapper (Payload "err") is
|
|
Wrapper (Payload str) -> str
|
|
Wrapper (AlternatePayload str) -> str
|
|
"#
|
|
)
|
|
}
|
|
|
|
#[mono_test]
|
|
fn issue_3669() {
|
|
indoc!(
|
|
r#"
|
|
Peano a := [
|
|
Zero,
|
|
Successor (Peano a)
|
|
]
|
|
|
|
unwrap : Peano a -> {}
|
|
unwrap = \@Peano p ->
|
|
when p is
|
|
Zero -> {}
|
|
Successor inner -> unwrap inner
|
|
|
|
when unwrap (@Peano Zero) == {} is
|
|
_ -> ""
|
|
"#
|
|
)
|
|
}
|