#[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::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 ); } #[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 ); } #[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"))] fn return_wrapped_function_pointer() { assert_evals_to!( indoc!( r#" app "test" provides [main] to "./platform" Effect a := {} -> a foo : Effect {} foo = @Effect \{} -> {} main : Effect {} main = foo "# ), 1, usize, |_| 1 ); } #[test] #[cfg(any(feature = "gen-llvm"))] fn return_wrapped_function_pointer_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 "# ), 1, usize, |_| 1 ); } #[test] #[cfg(any(feature = "gen-llvm"))] 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 "# ), 1, usize, |_| 1 ); } #[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"))] 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 {}) "# ), 0, usize, |_| 0 ); } #[test] #[cfg(any(feature = "gen-llvm"))] 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 {} "# ), 0, usize, |_| 0 ); } #[test] #[cfg(any(feature = "gen-llvm"))] 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 {} "# ), 0, usize, |_| 0 ); } #[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"))] fn nested_switch() { // exposed bug with passing the right symbol/layout down into switch branch generation 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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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/rtfeldman/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"))] fn list_map2_conslist() { // this had an RC problem, https://github.com/rtfeldman/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"))] 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, 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 ); }