roc/crates/compiler/test_gen/src/gen_primitives.rs

4061 lines
90 KiB
Rust

#[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to;
#[cfg(feature = "gen-dev")]
use crate::helpers::dev::assert_evals_to;
#[cfg(feature = "gen-wasm")]
use crate::helpers::wasm::assert_evals_to;
use indoc::indoc;
#[allow(unused_imports)]
use roc_std::RocList;
#[allow(unused_imports)]
use roc_std::RocStr;
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn basic_int() {
assert_evals_to!("123", 123, i64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn basic_float() {
assert_evals_to!("1234.0", 1234.0, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn branch_first_float() {
assert_evals_to!(
indoc!(
r#"
when 1.23 is
1.23 -> 12
_ -> 34
"#
),
12,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn branch_second_float() {
assert_evals_to!(
indoc!(
r#"
when 2.34 is
1.23 -> 63
_ -> 48
"#
),
48,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn branch_third_float() {
assert_evals_to!(
indoc!(
r#"
when 10.0 is
1.0 -> 63
2.0 -> 48
_ -> 112
"#
),
112,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_first_int() {
assert_evals_to!(
indoc!(
r#"
when 1 is
1 -> 12
_ -> 34
"#
),
12,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_second_int() {
assert_evals_to!(
indoc!(
r#"
when 2 is
1 -> 63
_ -> 48
"#
),
48,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn branch_third_int() {
assert_evals_to!(
indoc!(
r#"
when 10 is
1 -> 63
2 -> 48
_ -> 112
"#
),
112,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn branch_store_variable() {
assert_evals_to!(
indoc!(
r#"
when 0 is
1 -> 12
a -> a
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn when_one_element_tag() {
assert_evals_to!(
indoc!(
r#"
x : [Pair (Int a) (Int a)]
x = Pair 0x2 0x3
when x is
Pair l r -> l + r
"#
),
5,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_two_element_tag_first() {
assert_evals_to!(
indoc!(
r#"
x : [A (Int a), B (Int a)]
x = A 0x2
when x is
A v -> v
B v -> v
"#
),
2,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_two_element_tag_second() {
assert_evals_to!(
indoc!(
r#"
x : [A (Int a), B (Int a)]
x = B 0x3
when x is
A v -> v
B v -> v
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn gen_when_one_branch() {
assert_evals_to!(
indoc!(
r#"
when 1.23 is
_ -> 23
"#
),
23,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_large_when_int() {
assert_evals_to!(
indoc!(
r#"
foo = \num ->
when num is
0 -> 200
-3 -> 111 # TODO adding more negative numbers reproduces parsing bugs here
3 -> 789
1 -> 123
2 -> 456
_ -> 1000
foo -3
"#
),
111,
i64
);
}
#[test]
#[cfg(any(feature = "gen-wasm"))]
fn gen_large_when_float() {
assert_evals_to!(
indoc!(
r#"
foo = \num ->
when num is
0.5 -> 200.1
-3.6 -> 111.2 # TODO adding more negative numbers reproduces parsing bugs here
3.6 -> 789.5
1.7 -> 123.3
2.8 -> 456.4
_ -> 1000.6
foo -3.6
"#
),
111.2,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn or_pattern() {
assert_evals_to!(
indoc!(
r#"
when 2 is
1 | 2 -> 42
_ -> 1
"#
),
42,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn apply_identity() {
assert_evals_to!(
indoc!(
r#"
identity = \a -> a
identity 5
"#
),
5,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn apply_unnamed_identity() {
assert_evals_to!(
indoc!(
r#"
wrapper = \{} ->
(\a -> a) 5
wrapper {}
"#
),
5,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn return_unnamed_fn() {
assert_evals_to!(
indoc!(
r#"
wrapper = \{} ->
alwaysFloatIdentity : Int * -> (Frac a -> Frac a)
alwaysFloatIdentity = \_ ->
(\a -> a)
(alwaysFloatIdentity 2) 1.23
wrapper {}
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_when_fn() {
assert_evals_to!(
indoc!(
r#"
limitedNegate = \num ->
when num is
1 -> -1
-1 -> 1
_ -> num
limitedNegate 1
"#
),
-1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn gen_basic_def() {
assert_evals_to!(
indoc!(
r#"
answer = 42
answer
"#
),
42,
i64
);
assert_evals_to!(
indoc!(
r#"
float = 1.23
float
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_multiple_defs() {
assert_evals_to!(
indoc!(
r#"
answer = 42
float = 1.23
if float > 3 then answer else answer
"#
),
42,
i64
);
assert_evals_to!(
indoc!(
r#"
answer = 42
float = 1.23
if answer > 3 then float else float
"#
),
1.23,
f64
);
}
// These tests caught a bug in how Defs are converted to the mono IR
// but they have UnusedDef or UnusedArgument problems, and don't run any more
// #[test]
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// fn gen_chained_defs() {
// assert_evals_to!(
// indoc!(
// r#"
// x = i1
// i3 = i2
// i1 = 1337
// i2 = i1
// y = 12.4
//
// i3
// "#
// ),
// 1337,
// i64
// );
// }
//
// #[test]
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// fn gen_nested_defs_old() {
// assert_evals_to!(
// indoc!(
// r#"
// x = 5
//
// answer =
// i3 = i2
//
// nested =
// a = 1.0
// b = 5
//
// i1
//
// i1 = 1337
// i2 = i1
//
//
// nested
//
// # None of this should affect anything, even though names
// # overlap with the previous nested defs
// unused =
// nested = 17
//
// i1 = 84.2
//
// nested
//
// y = 12.4
//
// answer
// "#
// ),
// 1337,
// i64
// );
// }
//
// #[test]
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// fn let_x_in_x() {
// assert_evals_to!(
// indoc!(
// r#"
// x = 5
//
// answer =
// 1337
//
// unused =
// nested = 17
// nested
//
// answer
// "#
// ),
// 1337,
// i64
// );
// }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn factorial() {
assert_evals_to!(
indoc!(
r#"
factorial = \n, accum ->
when n is
0 ->
accum
_ ->
factorial (n - 1) (n * accum)
factorial 10 1
"#
),
3628800,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn peano1() {
assert_evals_to!(
indoc!(
r#"
Peano : [S Peano, Z]
three : Peano
three = S (S (S Z))
when three is
Z -> 2
S _ -> 1
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn peano2() {
assert_evals_to!(
indoc!(
r#"
Peano : [S Peano, Z]
three : Peano
three = S (S (S Z))
when three is
S (S _) -> 1
S (_) -> 0
Z -> 0
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn top_level_constant() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
float = 1.2315
main =
float + float
"#
),
1.2315 + 1.2315,
f64
);
}
#[test]
#[ignore]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn top_level_destructure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
{a, b} = { a: 1, b: 2 }
main =
a + b
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_len_0() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
len : LinkedList a -> Int *
len = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + len rest
main =
nil : LinkedList F64
nil = Nil
len nil
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_len_twice_0() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
nil : LinkedList I64
nil = Nil
length : LinkedList a -> Int *
length = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + length rest
main =
length nil + length nil
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_len_1() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
one : LinkedList (Int *)
one = Cons 1 Nil
length : LinkedList a -> Int *
length = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + length rest
main =
length one
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_len_twice_1() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
one : LinkedList (Int *)
one = Cons 1 Nil
length : LinkedList a -> Int *
length = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + length rest
main =
length one + length one
"#
),
2,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_len_3() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three = Cons 3 (Cons 2 (Cons 1 Nil))
length : LinkedList a -> Int *
length = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + length rest
main =
length three
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_sum_num_a() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three = Cons 3 (Cons 2 (Cons 1 Nil))
sum : LinkedList (Num a) -> Num a
sum = \list ->
when list is
Nil -> 0
Cons x rest -> x + sum rest
main =
sum three
"#
),
3 + 2 + 1,
i64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_sum_int() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
zero : LinkedList (Int *)
zero = Nil
sum : LinkedList (Int a) -> Int a
sum = \list ->
when list is
Nil -> 0
Cons x rest -> x + sum rest
main =
sum zero
"#
),
0,
i64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_map() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three = Cons 3 (Cons 2 (Cons 1 Nil))
sum : LinkedList (Num a) -> Num a
sum = \list ->
when list is
Nil -> 0
Cons x rest -> x + sum rest
map : (a -> b), LinkedList a -> LinkedList b
map = \f, list ->
when list is
Nil -> Nil
Cons x rest -> Cons (f x) (map f rest)
main =
sum (map (\_ -> 1) three)
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_nested_maybe() {
assert_evals_to!(
indoc!(
r#"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int a))
x = Just (Just 41)
when x is
Just (Just v) -> v + 0x1
_ -> 0x1
"#
),
42,
i64
);
assert_evals_to!(
indoc!(
r#"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int *))
x = Just Nothing
when x is
Just (Just v) -> v + 0x1
Just Nothing -> 0x2
Nothing -> 0x1
"#
),
2,
i64
);
assert_evals_to!(
indoc!(
r#"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int *))
x = Nothing
when x is
Just (Just v) -> v + 0x1
Just Nothing -> 0x2
Nothing -> 0x1
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_peano() {
assert_evals_to!(
indoc!(
r#"
Peano : [S Peano, Z]
three : Peano
three = S (S (S Z))
when three is
S (S _) -> 1
S (_) -> 2
Z -> 3
"#
),
1,
i64
);
assert_evals_to!(
indoc!(
r#"
Peano : [S Peano, Z]
three : Peano
three = S Z
when three is
S (S _) -> 1
S (_) -> 2
Z -> 3
"#
),
2,
i64
);
assert_evals_to!(
indoc!(
r#"
Peano : [S Peano, Z]
three : Peano
three = Z
when three is
S (S _) -> 1
S (_) -> 2
Z -> 3
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Roc failed with message: ")]
fn overflow_frees_list() {
assert_evals_to!(
indoc!(
r#"
myList = [1,2,3]
# integer overflow; must use the list so it is defined before the overflow
# the list will then be freed in a cleanup block
n : I64
n = 9_223_372_036_854_775_807 + (Num.intCast (List.len myList))
index : Nat
index = Num.intCast n
List.get myList index
"#
),
(3, 0),
(i64, i8)
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Roc failed with message: ")]
fn undefined_variable() {
assert_evals_to!(
indoc!(
r#"
if True then
x + z
else
y + z
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Roc failed with message: ")]
fn annotation_without_body() {
assert_evals_to!(
indoc!(
r#"
foo : Int *
foo
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn simple_closure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
x = 42
f = \{} -> x
main =
f {}
"#
),
42,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn nested_closure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \{} ->
x = 41
y = 1
f = \{} -> x + y
f
main =
g = foo {}
g {}
"#
),
42,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn closure_in_list() {
use roc_std::RocList;
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \{} ->
x = 41
f = \{} -> x
[f]
main =
items = foo {}
items
"#
),
RocList::from_slice(&[41]),
RocList<i64>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn specialize_closure() {
use roc_std::RocList;
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \{} ->
x = 41
y = [1]
f = \{} -> x
g = \{} -> x + Num.intCast (List.len y)
[f, g]
apply = \f -> f {}
main =
items = foo {}
List.map items apply
"#
),
RocList::from_slice(&[41, 42]),
RocList<i64>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn io_poc_effect() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
succeed : a -> Effect a
succeed = \x -> @Effect \{} -> x
runEffect : Effect a -> a
runEffect = \@Effect thunk -> thunk {}
foo : Effect F64
foo =
succeed 1.23
main : F64
main =
runEffect foo
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn io_poc_desugared() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
# succeed : a -> ({} -> a)
succeed = \x -> \_ -> x
foo : Str -> F64
foo =
succeed 1.23
# runEffect : ({} -> a) -> a
runEffect = \thunk -> thunk ""
main : F64
main =
runEffect foo
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn return_wrapped_function_a() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
foo : Effect {}
foo = @Effect \{} -> {}
main : Effect {}
main = foo
"#
),
(),
()
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn return_wrapped_function_b() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : { x: (I64 -> Str) }
foo = { x: (\_ -> "foobar") }
main : { x: (I64 -> Str) }
main = foo
"#
),
(),
()
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn return_wrapped_closure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
foo : Effect {}
foo =
x = 5
@Effect (\{} -> if x > 3 then {} else {})
main : Effect {}
main = foo
"#
),
5,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_is_singleton() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
empty : ConsList a
empty = Nil
isSingleton : ConsList a -> Bool
isSingleton = \list ->
when list is
Cons _ Nil ->
True
_ ->
False
main : Bool
main =
myList : ConsList I64
myList = empty
isSingleton myList
"#
),
false,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_is_empty_1() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
empty : ConsList a
empty = Nil
isEmpty : ConsList a -> Bool
isEmpty = \list ->
when list is
Cons _ _ ->
False
Nil ->
True
main : Bool
main =
myList : ConsList (Int *)
myList = empty
isEmpty myList
"#
),
true,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_is_empty_2() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
isEmpty : ConsList a -> Bool
isEmpty = \list ->
when list is
Cons _ _ ->
False
Nil ->
True
main : Bool
main =
myList : ConsList I64
myList = Cons 0x1 Nil
isEmpty myList
"#
),
false,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_singleton() {
// verifies only that valid llvm is produced
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
main : ConsList I64
main = Cons 0x1 Nil
"#
),
0,
i64,
|_| 0
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_function_with_rigid() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
State a : { count : I64, x : a }
foo : State a -> Int *
foo = \state ->
if state.count == 0 then
0
else
1 + foo { count: state.count - 1, x: state.x }
main : Int *
main =
foo { count: 3, x: {} }
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn rbtree_insert() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
NodeColor : [Red, Black]
RedBlackTree k v : [Node NodeColor k v (RedBlackTree k v) (RedBlackTree k v), Empty]
Key k : Num k
insert : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
insert = \key, value, dict ->
when insertHelp key value dict is
Node Red k v l r ->
Node Black k v l r
x ->
x
insertHelp : (Key k), v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
insertHelp = \key, value, dict ->
when dict is
Empty ->
# New nodes are always red. If it violates the rules, it will be fixed
# when balancing.
Node Red key value Empty Empty
Node nColor nKey nValue nLeft nRight ->
when Num.compare key nKey is
LT ->
balance nColor nKey nValue (insertHelp key value nLeft) nRight
EQ ->
Node nColor nKey value nLeft nRight
GT ->
balance nColor nKey nValue nLeft (insertHelp key value nRight)
balance : NodeColor, k, v, RedBlackTree k v, RedBlackTree k v -> RedBlackTree k v
balance = \color, key, value, left, right ->
when right is
Node Red rK rV rLeft rRight ->
when left is
Node Red lK lV lLeft lRight ->
Node
Red
key
value
(Node Black lK lV lLeft lRight)
(Node Black rK rV rLeft rRight)
_ ->
Node color rK rV (Node Red key value left rLeft) rRight
_ ->
when left is
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
Node
Red
lK
lV
(Node Black llK llV llLeft llRight)
(Node Black key value lRight right)
_ ->
Node color key value left right
show : RedBlackTree I64 {} -> Str
show = \tree ->
when tree is
Empty -> "Empty"
Node _ _ _ _ _ -> "Node"
main : Str
main =
show (insert 0 {} Empty)
"#
),
RocStr::from("Node"),
RocStr
);
}
#[test]
#[cfg(all(
any(feature = "gen-llvm", feature = "gen-wasm"),
not(feature = "gen-llvm-wasm")
))]
fn rbtree_balance_3() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
RedBlackTree k : [Node k (RedBlackTree k) (RedBlackTree k), Empty]
balance : k, RedBlackTree k -> RedBlackTree k
balance = \key, left ->
Node key left Empty
main : RedBlackTree (Int *)
main =
balance 0 Empty
"#
),
false,
usize,
|x: usize| x == 0
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn rbtree_layout_issue() {
// there is a flex var in here somewhere that blows up layout creation
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
NodeColor : [Red, Black]
RedBlackTree k v : [Node NodeColor k v (RedBlackTree k v) (RedBlackTree k v), Empty]
# balance : NodeColor, k, v, RedBlackTree k v -> RedBlackTree k v
balance = \color, key, value, right ->
when right is
Node Red _ _ rLeft rRight ->
Node color key value rLeft rRight
_ ->
Empty
show : RedBlackTree * * -> Str
show = \tree ->
when tree is
Empty -> "Empty"
Node _ _ _ _ _ -> "Node"
zero : I64
zero = 0
main : Str
main = show (balance Red zero zero Empty)
"#
),
RocStr::from("Empty"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn rbtree_balance_mono_problem() {
// because of how the function is written, only `Red` is used and so in the function's
// type, the first argument is a unit and dropped. Apparently something is weird with
// constraint generation where the specialization required by `main` does not fix the
// problem. As a result, the first argument is dropped and we run into issues down the line
//
// concretely, the `rRight` symbol will not be defined
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
NodeColor : [Red, Black]
RedBlackTree k v : [Node NodeColor k v (RedBlackTree k v) (RedBlackTree k v), Empty]
# balance : NodeColor, k, v, RedBlackTree k v, RedBlackTree k v -> RedBlackTree k v
balance = \color, key, value, left, right ->
when right is
Node Red rK rV rLeft rRight ->
when left is
Node Red lK lV lLeft lRight ->
Node
Red
key
value
(Node Black lK lV lLeft lRight)
(Node Black rK rV rLeft rRight)
_ ->
Node color rK rV (Node Red key value left rLeft) rRight
_ ->
Empty
show : RedBlackTree * * -> Str
show = \tree ->
when tree is
Empty -> "Empty"
Node _ _ _ _ _ -> "Node"
main : Str
main = show (balance Red 0 0 Empty Empty)
"#
),
RocStr::from("Empty"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn rbtree_balance_full() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
NodeColor : [Red, Black]
RedBlackTree k v : [Node NodeColor k v (RedBlackTree k v) (RedBlackTree k v), Empty]
balance : NodeColor, k, v, RedBlackTree k v, RedBlackTree k v -> RedBlackTree k v
balance = \color, key, value, left, right ->
when right is
Node Red rK rV rLeft rRight ->
when left is
Node Red lK lV lLeft lRight ->
Node
Red
key
value
(Node Black lK lV lLeft lRight)
(Node Black rK rV rLeft rRight)
_ ->
Node color rK rV (Node Red key value left rLeft) rRight
_ ->
when left is
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
Node
Red
lK
lV
(Node Black llK llV llLeft llRight)
(Node Black key value lRight right)
_ ->
Node color key value left right
main : RedBlackTree F64 F64
main =
balance Red 0 0 Empty Empty
"#
),
true,
usize,
|x| x != 0
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn nested_pattern_match_two_ways() {
// exposed an issue in the ordering of pattern match checks when ran with `--release` mode
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
balance : ConsList (Int *) -> Int *
balance = \right ->
when right is
Cons 1 foo ->
when foo is
Cons 1 _ -> 3
_ -> 3
_ -> 3
main : Int *
main =
when balance Nil is
_ -> 3
"#
),
3,
i64
);
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
balance : ConsList (Int *) -> Int *
balance = \right ->
when right is
Cons 1 (Cons 1 _) -> 3
_ -> 3
main : Int *
main =
when balance Nil is
_ -> 3
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_guarded_double_pattern_match() {
// the important part here is that the first case (with the nested Cons) does not match
// TODO this also has undefined behavior
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
balance : ConsList (Int *) -> Int *
balance = \right ->
when right is
Cons 1 foo ->
when foo is
Cons 1 _ -> 3
_ -> 3
_ -> 3
main : Int *
main =
when balance Nil is
_ -> 3
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn linked_list_double_pattern_match() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
foo : ConsList (Int a) -> Int a
foo = \list ->
when list is
Cons _ (Cons x _) -> x
_ -> 0
main : Int *
main =
foo (Cons 1 (Cons 32 Nil))
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn binary_tree_double_pattern_match() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
BTree : [Node BTree BTree, Leaf I64]
foo : BTree -> I64
foo = \btree ->
when btree is
Node (Node (Leaf x) _) _ -> x
_ -> 1
main : I64
main =
foo (Node (Node (Leaf 32) (Leaf 2)) (Leaf 3))
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn unified_empty_closure_bool() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \{} ->
when A is
A -> (\_ -> 1.23)
B -> (\_ -> 1.23)
main : Frac *
main =
(foo {}) 0
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn unified_empty_closure_byte() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \{} ->
when A is
A -> (\_ -> 1.23)
B -> (\_ -> 1.23)
C -> (\_ -> 1.23)
main : Frac *
main =
(foo {}) 0
"#
),
1.23,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn task_always_twice() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
effectAlways : a -> Effect a
effectAlways = \x ->
inner = \{} -> x
@Effect inner
effectAfter : Effect a, (a -> Effect b) -> Effect b
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
Task a err : Effect (Result a err)
always : a -> Task a *
always = \x -> effectAlways (Ok x)
fail : err -> Task * err
fail = \x -> effectAlways (Err x)
after : Task a err, (a -> Task b err) -> Task b err
after = \task, transform ->
effectAfter task \res ->
when res is
Ok x -> transform x
Err e -> fail e
main : Task {} (Frac *)
main = after (always "foo") (\_ -> always {})
"#
),
(),
(f64, u8),
|_| ()
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn wildcard_rigid() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
Task a err : Effect (Result a err)
# this failed because of the `*`, but worked with `err`
always : a -> Task a *
always = \x ->
inner = \{} -> (Ok x)
@Effect inner
main : Task {} (Frac *)
main = always {}
"#
),
(),
()
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn alias_of_alias_with_type_arguments() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := a
Task a err : Effect (Result a err)
always : a -> Task a *
always = \x ->
inner = (Ok x)
@Effect inner
main : Task {} (Frac *)
main = always {}
"#
),
(),
(f64, u8),
|_| ()
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn todo_bad_error_message() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Effect a := {} -> a
effectAlways : a -> Effect a
effectAlways = \x ->
inner = \{} -> x
@Effect inner
effectAfter : Effect a, (a -> Effect b) -> Effect b
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
Task a err : Effect (Result a err)
always : a -> Task a (Frac *)
always = \x -> effectAlways (Ok x)
# the problem is that this restricts to `Task {} *`
fail : err -> Task {} err
fail = \x -> effectAlways (Err x)
after : Task a err, (a -> Task b err) -> Task b err
after = \task, transform ->
effectAfter task \res ->
when res is
Ok x -> transform x
# but here it must be `forall b. Task b {}`
Err e -> fail e
main : Task {} (Frac *)
main =
after (always "foo") (\_ -> always {})
"#
),
0,
i64,
|_| 0
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn hof_conditional() {
// exposed issue with the if condition being just a symbol
assert_evals_to!(
indoc!(
r#"
passTrue = \f -> f True
passTrue (\trueVal -> if trueVal then False else True)
"#
),
0,
u8
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(
expected = "Roc failed with message: \"Shadowing { original_region: @55-56, shadow: @88-89 Ident"
)]
fn pattern_shadowing() {
assert_evals_to!(
indoc!(
r#"
x = 4
when 4 is
x -> x
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
#[should_panic(expected = "")]
fn unsupported_pattern_str_interp() {
assert_evals_to!(
indoc!(
r#"
{ x: 4 } = { x : 4 }
x
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn fingertree_basic() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Some a : [One a, Two a a, Three a a a]
Tuple a : [Pair a a, Triple a a a]
# a FingerTree implementation
Seq a : [Nil, Unit a, More (Some a) (Seq (Tuple a)) (Some a)]
# cons : a, Seq a -> Seq a
cons = \x, s ->
when s is
Nil -> Unit x
Unit y -> More (One x) Nil (One y)
More some q u ->
when some is
One y -> More (Two x y) q u
Two y z -> More (Three x y z) q u
Three y z w -> More (Two x y) (consTuple (Pair z w) q) u
consTuple : Tuple a, Seq (Tuple a) -> Seq (Tuple a)
consTuple = \a, b -> cons a b
main : Bool
main =
when cons 0x1 Nil is
Unit 1 -> True
_ -> False
"#
),
true,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn case_or_pattern() {
// the `0` branch body should only be generated once in the future
// it is currently duplicated
assert_evals_to!(
indoc!(
r#"
x : [Red, Green, Blue]
x = Red
when x is
Red | Green -> 0
Blue -> 1
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn rosetree_basic() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Tree a : [Tree a (List (Tree a))]
singleton : a -> Tree a
singleton = \x -> Tree x []
main : Bool
main =
x : Tree F64
x = singleton 3
when x is
Tree 3.0 _ -> True
_ -> False
"#
),
true,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn case_jump() {
// the decision tree will generate a jump to the `1` branch here
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ConsList a : [Cons a (ConsList a), Nil]
x : ConsList I64
x = Nil
main =
when Pair x x is
Pair Nil _ -> 1
Pair _ Nil -> 2
Pair (Cons a _) (Cons b _) -> a + b + 3
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn nullable_eval_cfold() {
// the decision tree will generate a jump to the `1` branch here
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Expr : [Var, Val I64, Add Expr Expr, Mul Expr Expr]
mkExpr : I64, I64 -> Expr
mkExpr = \n , v ->
when n is
0 -> if v == 0 then Var else Val v
_ -> Add (mkExpr (n-1) (v+1)) (mkExpr (n-1) (max (v-1) 0))
max : I64, I64 -> I64
max = \a, b -> if a > b then a else b
eval : Expr -> I64
eval = \e ->
when e is
Var -> 0
Val v -> v
Add l r -> eval l + eval r
Mul l r -> eval l * eval r
main : I64
main = eval (mkExpr 3 1)
"#
),
11,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn nested_switch() {
// exposed bug with passing the right symbol/layout down into switch branch generation
// This is also the only test_gen test that exercises Reset/Reuse (as of Aug 2022)
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Expr : [ZAdd Expr Expr, Val I64, Var I64]
eval : Expr -> I64
eval = \e ->
when e is
Var _ -> 0
Val v -> v
ZAdd l r -> eval l + eval r
constFolding : Expr -> Expr
constFolding = \e ->
when e is
ZAdd e1 e2 ->
when Pair e1 e2 is
Pair (Val a) (Val b) -> Val (a+b)
Pair (Val a) (ZAdd x (Val b)) -> ZAdd (Val (a+b)) x
Pair _ _ -> ZAdd e1 e2
_ -> e
expr : Expr
expr = ZAdd (Val 3) (ZAdd (Val 4) (Val 5))
main : I64
main = eval (constFolding expr)
"#
),
12,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn count_deriv_x() {
// exposed bug with basing the block_of_memory on a specific (smaller) tag layout
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Expr : [Ln Expr, Pow Expr Expr, Var Str]
count : Expr -> I64
count = \expr ->
when expr is
(Var _) -> 1
(Pow f g) -> count f + count g
(Ln f) -> count f
main : I64
main = count (Var "x")
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn deriv_pow() {
// exposed bug with ordering of variable declarations before switch
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Expr : [Ln Expr, Pow Expr Expr, Var Str, Val I64]
count : Expr -> I64
count = \expr ->
when expr is
(Var _) -> 1
(Val n) -> n
(Pow f g) -> count f + count g
(Ln f) -> count f
pow : Expr, Expr -> Expr
pow = \a,b ->
when Pair a b is
Pair (Val _) (Val _) -> Val -1
Pair _ (Val 0) -> Val 1
Pair f (Val 1) -> f
Pair (Val 0) _ -> Val 0
Pair f g -> Pow f g
main : I64
main = count (pow (Var "x") (Var "x"))
"#
),
2,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn multiple_increment() {
// the `leaf` value will be incremented multiple times at once
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Color : [Red, Black]
Tree a b : [Leaf, Node Color (Tree a b) a b (Tree a b)]
Map : Tree I64 Bool
main : I64
main =
leaf : Map
leaf = Leaf
m : Map
m = Node Black (Node Black leaf 10 False leaf) 11 False (Node Black leaf 12 False (Node Red leaf 13 False leaf))
when m is
Leaf -> 0
Node _ _ _ _ _ -> 1
"#
),
1,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn switch_fuse_rc_non_exhaustive() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Foo : [A I64 Foo, B I64 Foo, C I64 Foo, Empty]
sum : Foo, I64 -> I64
sum = \foo, accum ->
when foo is
A x resta -> sum resta (x + accum)
B x restb -> sum restb (x + accum)
# Empty -> accum
# C x restc -> sum restc (x + accum)
_ -> accum
main : I64
main =
A 1 (B 2 (C 3 Empty))
|> sum 0
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn switch_fuse_rc_exhaustive() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Foo : [A I64 Foo, B I64 Foo, C I64 Foo, Empty]
sum : Foo, I64 -> I64
sum = \foo, accum ->
when foo is
A x resta -> sum resta (x + accum)
B x restb -> sum restb (x + accum)
C x restc -> sum restc (x + accum)
Empty -> accum
main : I64
main =
A 1 (B 2 (C 3 Empty))
|> sum 0
"#
),
6,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn build_then_apply_closure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : Str
main =
x = "long string that is malloced"
(\_ -> x) {}
"#
),
RocStr::from("long string that is malloced"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn expanded_result() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
a : Result I64 Str
a = Ok 4
after = \x, f ->
when x is
Ok v -> f v
Err e -> Err e
main : I64
main =
helper = after a (\x -> Ok x)
when helper is
Ok v -> v
Err _ -> 0
"#
),
4,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn backpassing_result() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
a : Result I64 Str
a = Ok 1
f = \x -> Ok (x + 1)
g = \y -> Ok (y * 2)
main : I64
main =
helper =
x <- Result.try a
y <- Result.try (f x)
z <- Result.try (g y)
Ok z
helper
|> Result.withDefault 0
"#
),
4,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Shadowing { original_region: @55-56, shadow: @72-73 Ident")]
fn function_malformed_pattern() {
assert_evals_to!(
indoc!(
r#"
x = 3
(\x -> x) 42
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Hit an erroneous type when creating a layout for")]
fn call_invalid_layout() {
assert_evals_to!(
indoc!(
r#"
f : I64 -> I64
f = \x -> x
f {}
"#
),
3,
i64,
|x| x,
true
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn increment_or_double_closure() {
assert_evals_to!(
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
f = (if True then increment else double)
apply f 42
"#
),
43,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn module_thunk_is_function() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main = helper "foo" "bar"
helper = Str.concat
"#
),
RocStr::from("foobar"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pass_through_unresolved_type_variable() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : Str
main =
(accept Bool.isEq) "B"
accept : * -> (b -> b)
accept = \_ ->
\input -> input
"#
),
RocStr::from("B"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn pattern_match_empty_record() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : I64
main =
when {} is
{} -> 0
"#
),
0,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pattern_match_unit_tag() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
unit : [Unit]
unit = Unit
main : I64
main =
when unit is
Unit -> 0
"#
),
0,
i64
);
}
// see for why this is disabled on wasm32 https://github.com/roc-lang/roc/issues/1687
#[cfg(not(feature = "gen-llvm-wasm"))]
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn mirror_llvm_alignment_padding() {
// see https://github.com/roc-lang/roc/issues/1569
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : Str
main =
p1 = {name : "test1", test: 1 == 1 }
List.map [p1, p1] (\{ test } -> if test then "pass" else "fail")
|> Str.joinWith "\n"
"#
),
RocStr::from("pass\npass"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_set_bool() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
p1 = (\u -> u == 97)
p2 = (\u -> u == 98)
main : I64
main =
oneOfResult = List.map [p1, p2] (\p -> p 42)
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_set_byte() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
p1 = (\u -> u == 97)
p2 = (\u -> u == 98)
p3 = (\u -> u == 99)
main : I64
main =
oneOfResult = List.map [p1, p2, p3] (\p -> p 42)
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_set_struct_byte() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : I64
main =
r : [Red, Green, Blue]
r = Red
p1 = (\u -> r == u)
foobarbaz = (\p -> p Green)
oneOfResult = List.map [p1, p1] foobarbaz
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_set_enum_byte_byte() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : I64
main =
r : [Red, Green, Blue]
r = Red
g : [Red, Green, Blue]
g = Green
p1 = (\u -> r == u)
p2 = (\u -> g == u)
oneOfResult = List.map [p1, p2] (\p -> p Green)
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_walk_until() {
// see https://github.com/roc-lang/roc/issues/1576
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
satisfyA : {} -> List {}
satisfyA = \_ -> []
oneOfResult =
List.walkUntil [satisfyA] [] \_, _ -> Break []
main =
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_literal_not_specialized_with_annotation() {
// see https://github.com/roc-lang/roc/issues/1600
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
satisfy : (U8 -> Str) -> Str
satisfy = \_ -> "foo"
myEq : a, a -> Str
myEq = \_, _ -> "bar"
p1 : Num * -> Str
p1 = (\u -> myEq u 64)
when satisfy p1 is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_literal_not_specialized_no_annotation() {
// see https://github.com/roc-lang/roc/issues/1600
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
satisfy : (U8 -> Str) -> Str
satisfy = \_ -> "foo"
myEq : a, a -> Str
myEq = \_, _ -> "bar"
p1 = (\u -> myEq u 64)
when satisfy p1 is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn unresolved_tvar_when_capture_is_unused() {
// see https://github.com/roc-lang/roc/issues/1585
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : I64
main =
r : Bool
r = False
p1 = (\_ -> r == (1 == 1))
oneOfResult = List.map [p1] (\p -> p Green)
when oneOfResult is
_ -> 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Roc failed with message: ")]
fn value_not_exposed_hits_panic() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : I64
main =
Str.toInt 32
"#
),
32,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn mix_function_and_closure() {
// see https://github.com/roc-lang/roc/pull/1706
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
# foo does not capture any variables
# but through unification will get a lambda set that does store information
# we must handle that correctly
foo = \x -> x
bar = \y -> \_ -> y
main : Str
main =
(if 1 == 1 then foo else (bar "nope nope nope")) "hello world"
"#
),
RocStr::from("hello world"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn mix_function_and_closure_level_of_indirection() {
// see https://github.com/roc-lang/roc/pull/1706
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo = \x -> x
bar = \y -> \_ -> y
f = (if 1 == 1 then foo else (bar "nope nope nope"))
main : Str
main =
f "hello world"
"#
),
RocStr::from("hello world"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg_attr(debug_assertions, ignore)] // this test stack-overflows the compiler in debug mode
fn do_pass_bool_byte_closure_layout() {
// see https://github.com/roc-lang/roc/pull/1706
// the distinction is actually important, dropping that info means some functions just get
// skipped
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
## PARSER
Parser a : List U8 -> List [Pair a (List U8)]
## ANY
# If successful, the any parser consumes one character
any: Parser U8
any = \inp ->
when List.first inp is
Ok u -> [Pair u (List.drop inp 1)]
_ -> []
## SATISFY
satisfy : (U8 -> Bool) -> Parser U8
satisfy = \predicate ->
\input ->
walker = \accum, (Pair u rest) ->
if predicate u then
Break [Pair u rest]
else
Break accum
List.walkUntil (any input) [] walker
oneOf : List (Parser a) -> Parser a
oneOf = \parserList ->
\input ->
walker = \accum, p ->
output = p input
if List.len output == 1 then
Break output
else
Continue accum
List.walkUntil parserList [] walker
satisfyA = satisfy (\u -> u == 97) # recognize 97
satisfyB = satisfy (\u -> u == 98) # recognize 98
test1 = if List.len ((oneOf [satisfyA, satisfyB]) [97, 98, 99, 100] ) == 1 then "PASS" else "FAIL"
test2 = if List.len ((oneOf [satisfyA, satisfyB]) [98, 99, 100, 97] ) == 1 then "PASS" else "FAIL"
test3 = if List.len ((oneOf [satisfyB , satisfyA]) [98, 99, 100, 97] ) == 1 then "PASS" else "FAIL"
test4 = if List.len ((oneOf [satisfyA, satisfyB]) [99, 100, 101] ) == 0 then "PASS" else "FAIL"
main : Str
main = [test1, test2, test3, test4] |> Str.joinWith ", "
"#
),
RocStr::from("PASS, PASS, PASS, PASS"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn nested_rigid_list() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : List a -> List a
foo = \list ->
p2 : List a
p2 = list
p2
main =
when foo [] is
_ -> "hello world"
"#
),
RocStr::from("hello world"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn nested_rigid_alias() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
Identity a := a
foo : Identity a -> Identity a
foo = \list ->
p2 : Identity a
p2 = list
p2
main =
when foo (@Identity "foo") is
_ -> "hello world"
"#
),
RocStr::from("hello world"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn nested_rigid_tag_union() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : [Identity a] -> [Identity a]
foo = \list ->
p2 : [Identity a]
p2 = list
p2
main =
when foo (Identity "foo") is
_ -> "hello world"
"#
),
RocStr::from("hello world"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn call_that_needs_closure_parameter() {
// here both p2 is lifted to the top-level, which means that `list` must be
// passed to it from `manyAux`.
assert_evals_to!(
indoc!(
r#"
Step state a : [Loop state, Done a]
manyAux : List a -> [Pair (Step (List a) (List a))]
manyAux = \list ->
p2 = \_ -> Pair (Done list)
p2 "foo"
manyAuxTest = (manyAux []) == Pair (Loop [97])
runTest = \t -> if t then "PASS" else "FAIL"
runTest manyAuxTest
"#
),
RocStr::from("FAIL"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn alias_defined_out_of_order() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : Foo
main = "foo"
Foo : Bar
Bar : Str
"#
),
RocStr::from("foo"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursively_build_effect() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
greeting =
hi = "Hello"
name = "World"
"\(hi), \(name)!"
main =
when nestHelp 4 is
_ -> greeting
nestHelp : I64 -> XEffect {}
nestHelp = \m ->
when m is
0 ->
always {}
_ ->
always {} |> after \_ -> nestHelp (m - 1)
XEffect a := {} -> a
always : a -> XEffect a
always = \x -> @XEffect (\{} -> x)
after : XEffect a, (a -> XEffect b) -> XEffect b
after = \(@XEffect e), toB ->
@XEffect \{} ->
when toB (e {}) is
@XEffect e2 ->
e2 {}
"#
),
RocStr::from("Hello, World!"),
RocStr
);
}
#[test]
#[ignore = "TODO; currently generates bad code because `a` isn't specialized inside the closure."]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn polymophic_expression_captured_inside_closure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
asU8 : U8 -> U8
asU8 = \_ -> 30
main =
a = 15
f = \{} ->
asU8 a
f {}
"#
),
30,
u8
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn issue_2322() {
assert_evals_to!(
indoc!(
r#"
double = \x -> x * 2
doubleBind = \x -> (\_ -> double x)
doubleThree = doubleBind 3
doubleThree {}
"#
),
6,
i64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn box_and_unbox_string() {
assert_evals_to!(
indoc!(
r#"
Str.concat "Leverage " "agile frameworks to provide a robust synopsis for high level overviews"
|> Box.box
|> Box.unbox
"#
),
RocStr::from(
"Leverage agile frameworks to provide a robust synopsis for high level overviews"
),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn box_and_unbox_num() {
assert_evals_to!(
indoc!(
r#"
Box.unbox (Box.box (123u8))
"#
),
123,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn box_and_unbox_record() {
assert_evals_to!(
indoc!(
r#"
Box.unbox (Box.box { a: 15u8, b: 27u8 })
"#
),
(15, 27),
(u8, u8)
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn box_and_unbox_tag_union() {
assert_evals_to!(
indoc!(
r#"
v : [A U8, B U8] # usually stack allocated
v = B 27u8
Box.unbox (Box.box v)
"#
),
(27, 1),
(u8, u8)
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn closure_called_in_its_defining_scope() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : Str
main =
g : Str
g = "hello world"
getG : {} -> Str
getG = \{} -> g
getG {}
"#
),
RocStr::from("hello world"),
RocStr
)
}
#[test]
#[ignore]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn issue_2894() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : U32
main =
g : { x : U32 }
g = { x: 1u32 }
getG : {} -> { x : U32 }
getG = \{} -> g
h : {} -> U32
h = \{} -> (getG {}).x
h {}
"#
),
1u32,
u32
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn polymorphic_def_used_in_closure() {
assert_evals_to!(
indoc!(
r#"
a : I64 -> _
a = \g ->
f = { r: g, h: 32 }
h1 : U64
h1 = (\{} -> f.h) {}
h1
a 1
"#
),
32,
u64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn polymorphic_lambda_set_usage() {
assert_evals_to!(
indoc!(
r#"
id1 = \x -> x
id2 = \y -> y
id = if True then id1 else id2
id 9u8
"#
),
9,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn polymorphic_lambda_set_multiple_specializations() {
assert_evals_to!(
indoc!(
r#"
id1 = \x -> x
id2 = \y -> y
id = if True then id1 else id2
(id 9u8) + Num.toU8 (id 16u16)
"#
),
25,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_map2_conslist() {
// this had an RC problem, https://github.com/roc-lang/roc/issues/2968
assert_evals_to!(
indoc!(
r#"
ConsList a : [Nil, Cons a (ConsList a)]
x : List (ConsList Str)
x = List.map2 [] [Nil] Cons
when List.first x is
_ -> ""
"#
),
RocStr::default(),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn mutual_recursion_top_level_defs() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
isEven = \n ->
when n is
0 -> True
1 -> False
_ -> isOdd (n - 1)
isOdd = \n ->
when n is
0 -> False
1 -> True
_ -> isEven (n - 1)
main = isOdd 11
"#
),
true,
bool
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn polymorphic_lambda_captures_polymorphic_value() {
assert_evals_to!(
indoc!(
r#"
x = 2
f1 = \_ -> x
f = if True then f1 else f1
f {}
"#
),
2,
u64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_capture_niche_u64_vs_u8_capture() {
assert_evals_to!(
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 {}
"#
),
RocStr::from("123"),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_capture_niches_with_other_lambda_capture() {
assert_evals_to!(
indoc!(
r#"
capture : _ -> ({} -> Str)
capture = \val ->
\{} ->
Num.toStr val
capture2 = \val -> \{} -> val
f = \x ->
g =
when x is
A -> capture 11u8
B -> capture2 "lisa"
C -> capture 187128u64
g {}
{a: f A, b: f B, c: f C}
"#
),
(
RocStr::from("11"),
RocStr::from("lisa"),
RocStr::from("187128")
),
(RocStr, RocStr, RocStr)
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_capture_niches_with_non_capturing_function() {
assert_evals_to!(
indoc!(
r#"
capture : _ -> ({} -> Str)
capture = \val ->
\{} ->
Num.toStr val
triv = \{} -> "triv"
f = \x ->
g =
when x is
A -> capture 11u8
B -> triv
C -> capture 187128u64
g {}
{a: f A, b: f B, c: f C}
"#
),
(
RocStr::from("11"),
RocStr::from("triv"),
RocStr::from("187128")
),
(RocStr, RocStr, RocStr)
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lambda_capture_niches_have_captured_function_in_closure() {
assert_evals_to!(
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 = \_ -> \_ -> "fun f"
g = \{ s1 } -> \_ -> s1
fun = \x ->
h =
when x is
True -> after (\{} -> "") f
False -> after (\{} -> {s1: "s1"}) g
h {}
{a: fun False, b: fun True}
"#
),
(RocStr::from("s1"), RocStr::from("fun f")),
(RocStr, RocStr)
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_call_capturing_function() {
assert_evals_to!(
indoc!(
r#"
a = \b ->
c = \d ->
if d == 7 then d else c (d + b)
c 1
a 6
"#
),
7,
i64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn shared_pattern_variable_in_when_branches() {
assert_evals_to!(
indoc!(
r#"
f = \t ->
when t is
A x | B x -> x
{a: f (A 15u8), b: (B 31u8)}
"#
),
(15u8, 31u8),
(u8, u8)
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn symbol_not_bound_in_all_patterns_runs_when_no_bound_symbol_used() {
assert_evals_to!(
indoc!(
r#"
f = \t -> when t is
A x | B y -> 31u8
{a: f (A 15u8), b: f (B 15u8)}
"#
),
(31u8, 31u8),
(u8, u8),
|x| x,
true // allow errors
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn symbol_not_bound_in_all_patterns_runs_when_bound_pattern_reached() {
assert_evals_to!(
indoc!(
r#"
when A 15u8 is
A x | B y -> x
"#
),
15u8,
u8,
|x| x,
true // allow errors
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(
expected = r#"Roc failed with message: "Hit a branch pattern that does not bind all symbols its body needs"#
)]
fn runtime_error_when_degenerate_pattern_reached() {
assert_evals_to!(
indoc!(
r#"
when B 15u8 is
A x | B y -> x + 5u8
"#
),
15u8,
u8,
|x| x,
true // allow errors
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_issue_3444() {
assert_evals_to!(
indoc!(
r#"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
res "hello"
"#
),
RocStr::from("c"),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_toplevel_issue_3444() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
main = res "hello"
"#
),
RocStr::from("c"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_issue_3444_inferred() {
assert_evals_to!(
indoc!(
r#"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
res "hello"
"#
),
RocStr::from("c"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_toplevel() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
main =
res: Str -> Str
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_nested() {
assert_evals_to!(
indoc!(
r#"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
res: Str -> Str
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_inferred() {
assert_evals_to!(
indoc!(
r#"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn local_binding_aliases_function() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
f : {} -> List a
f = \_ -> []
main : List U8
main =
g = f
g {}
"#
),
RocList::<u8>::from_slice(&[]),
RocList<u8>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn local_binding_aliases_function_inferred() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
f = \_ -> []
main =
g = f
g {}
"#
),
RocList::from_slice(&[]),
RocList<std::convert::Infallible>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn transient_captures() {
assert_evals_to!(
indoc!(
r#"
x = "abc"
getX = \{} -> x
h = \{} -> getX {}
h {}
"#
),
RocStr::from("abc"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn transient_captures_after_def_ordering() {
assert_evals_to!(
indoc!(
r#"
h = \{} -> getX {}
getX = \{} -> x
x = "abc"
h {}
"#
),
RocStr::from("abc"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn deep_transient_capture_chain() {
assert_evals_to!(
indoc!(
r#"
z = "abc"
getX = \{} -> getY {}
getY = \{} -> getZ {}
getZ = \{} -> z
h = \{} -> getX {}
h {}
"#
),
RocStr::from("abc"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn deep_transient_capture_chain_with_multiple_captures() {
assert_evals_to!(
indoc!(
r#"
h = "h"
x = "x"
y = "y"
z = "z"
getX = \{} -> Str.concat x (getY {})
getY = \{} -> Str.concat y (getZ {})
getZ = \{} -> z
getH = \{} -> Str.concat h (getX {})
getH {}
"#
),
RocStr::from("hxyz"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn transient_captures_from_outer_scope() {
assert_evals_to!(
indoc!(
r#"
x = "abc"
getX = \{} -> x
innerScope =
h = \{} -> getX {}
h {}
innerScope
"#
),
RocStr::from("abc"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn mutually_recursive_captures() {
assert_evals_to!(
indoc!(
r#"
x : Bool
x = True
y : Bool
y = False
a = "foo"
b = "bar"
foo = \{} -> if x then a else bar {}
bar = \{} -> if y then b else foo {}
bar {}
"#
),
RocStr::from("foo"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn monomorphization_sees_polymorphic_recursion() {
assert_evals_to!(
indoc!(
r#"
foo : a, Bool -> Str
foo = \in, b -> if b then "done" else bar in
bar = \_ -> foo {} True
foo "" False
"#
),
RocStr::from("done"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_let_generalization() {
assert_evals_to!(
indoc!(
r#"
manyAux : {} -> I32
manyAux = \_ ->
output = \_ -> 42
output {}
when manyAux {} is
_ -> "done"
"#
),
RocStr::from("done"),
RocStr
);
}