Merge remote-tracking branch 'origin/main' into abilities-syntax

This commit is contained in:
Richard Feldman 2023-08-10 20:29:27 -04:00
commit 2da41be29f
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
524 changed files with 47536 additions and 15089 deletions

View file

@ -18,7 +18,13 @@ wasi_libc_sys = { path = "../../wasi-libc-sys" }
tempfile.workspace = true
[dependencies]
roc_gen_llvm = { path = "../gen_llvm", optional = true }
inkwell = { workspace = true, optional = true }
[dev-dependencies]
roc_gen_dev = { path = "../gen_dev" }
roc_gen_wasm = { path = "../gen_wasm" }
roc_bitcode = { path = "../builtins/bitcode" }
roc_build = { path = "../build", features = ["target-aarch64", "target-x86_64", "target-wasm32"] }
roc_builtins = { path = "../builtins" }
@ -28,9 +34,6 @@ roc_command_utils = { path = "../../utils/command" }
roc_constrain = { path = "../constrain" }
roc_debug_flags = { path = "../debug_flags" }
roc_error_macros = { path = "../../error_macros" }
roc_gen_dev = { path = "../gen_dev" }
roc_gen_llvm = { path = "../gen_llvm" }
roc_gen_wasm = { path = "../gen_wasm" }
roc_load = { path = "../load" }
roc_module = { path = "../module" }
roc_mono = { path = "../mono" }
@ -50,8 +53,6 @@ roc_wasm_module = { path = "../../wasm_module" }
bumpalo.workspace = true
criterion.workspace = true
indoc.workspace = true
inkwell.workspace = true
lazy_static.workspace = true
libc.workspace = true
libloading.workspace = true
target-lexicon.workspace = true
@ -61,7 +62,7 @@ tempfile.workspace = true
[features]
default = ["gen-llvm"]
gen-dev = []
gen-llvm = []
gen-llvm = ["roc_gen_llvm", "inkwell"]
gen-llvm-wasm = ["gen-llvm"]
gen-wasm = []

View file

@ -65,7 +65,7 @@ fn roc_function<'a, 'b>(
let (main_fn_name, errors, lib) =
helpers::llvm::helper(arena, config, source, arena.alloc(context));
assert!(errors.is_empty(), "Encountered errors:\n{}", errors);
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
run_roc_dylib!(arena.alloc(lib), main_fn_name, &Input, Output)
}

View file

@ -94,7 +94,7 @@ fn roc_function<'a>(
let (main_fn_name, errors, lib) =
helpers::llvm::helper(arena, config, source, arena.alloc(context));
assert!(errors.is_empty(), "Encountered errors:\n{}", errors);
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
run_roc_dylib!(arena.alloc(lib), main_fn_name, *mut Input, Output)
}

View file

@ -41,8 +41,8 @@ fn build_wasm_linking_test_host() {
let host_wasm: &str = host_wasm_path.to_str().unwrap();
let host_native: &str = host_native_path.to_str().unwrap();
println!("cargo:rerun-if-changed={}", host_source);
println!("cargo:rerun-if-changed={}", import_source);
println!("cargo:rerun-if-changed={host_source}");
println!("cargo:rerun-if-changed={import_source}");
if !Path::new("build").exists() {
fs::create_dir("build").unwrap();
@ -57,7 +57,7 @@ fn build_wasm_linking_test_host() {
"-target",
"wasm32-freestanding-musl",
host_source,
&format!("-femit-bin={}", host_wasm),
&format!("-femit-bin={host_wasm}"),
]);
let mut import_obj_path = PathBuf::from("build").join("wasm_linking_host_imports");
@ -73,7 +73,7 @@ fn build_wasm_linking_test_host() {
"build-exe",
host_source,
import_obj,
&format!("-femit-bin={}", host_native),
&format!("-femit-bin={host_native}"),
#[cfg(windows)]
"--subsystem",
#[cfg(windows)]
@ -148,7 +148,7 @@ fn run_zig(args: &[&str]) {
let mut zig_cmd = zig();
let full_zig_cmd = zig_cmd.args(args);
println!("{:?}", full_zig_cmd);
println!("{full_zig_cmd:?}");
let zig_cmd_output = full_zig_cmd.output().unwrap();
@ -164,6 +164,6 @@ fn run_zig(args: &[&str]) {
panic!("zig call failed with status {:?}", zig_cmd_output.status);
}
assert!(zig_cmd_output.stdout.is_empty(), "{:#?}", zig_cmd_output);
assert!(zig_cmd_output.stderr.is_empty(), "{:#?}", zig_cmd_output);
assert!(zig_cmd_output.stdout.is_empty(), "{zig_cmd_output:#?}");
assert!(zig_cmd_output.stderr.is_empty(), "{zig_cmd_output:#?}");
}

View file

@ -12,6 +12,8 @@ use roc_std::RocList;
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
use roc_std::RocStr;
use crate::helpers::with_larger_debug_stack;
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn hash_specialization() {
@ -935,9 +937,10 @@ fn encode_derived_generic_tag_with_different_field_types() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn specialize_unique_newtype_records() {
assert_evals_to!(
indoc!(
r#"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode, TotallyNotJson]
provides [main] to "./platform"
@ -949,10 +952,11 @@ fn specialize_unique_newtype_records() {
_ -> "<bad>"
_ -> "<bad>"
"#
),
RocStr::from(r#"{"a":true}{"b":true}"#),
RocStr
)
),
RocStr::from(r#"{"a":true}{"b":true}"#),
RocStr
)
});
}
#[test]
@ -1056,23 +1060,27 @@ mod decode_immediate {
#[cfg(all(test, any(feature = "gen-llvm")))]
use roc_std::RocStr;
use crate::helpers::with_larger_debug_stack;
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn string() {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
main =
when Str.toUtf8 "\"foo\"" |> Decode.fromBytes TotallyNotJson.json is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from("foo"),
RocStr
)
main =
when Str.toUtf8 "\"foo\"" |> Decode.fromBytes TotallyNotJson.json is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from("foo"),
RocStr
)
});
}
#[test]
@ -1179,59 +1187,65 @@ mod decode_immediate {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn decode_list_of_strings() {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
main =
when Str.toUtf8 "[\"a\",\"b\",\"c\"]" |> Decode.fromBytes TotallyNotJson.json is
Ok l -> Str.joinWith l ","
_ -> "<bad>"
"#
),
RocStr::from("a,b,c"),
RocStr
)
main =
when Str.toUtf8 "[\"a\",\"b\",\"c\"]" |> Decode.fromBytes TotallyNotJson.json is
Ok l -> Str.joinWith l ","
_ -> "<bad>"
"#
),
RocStr::from("a,b,c"),
RocStr
)
});
}
#[test]
#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))]
fn encode_then_decode_list_of_strings() {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
main =
when Encode.toBytes ["a", "b", "c"] TotallyNotJson.json |> Decode.fromBytes TotallyNotJson.json is
Ok l -> Str.joinWith l ","
_ -> "something went wrong"
"#
),
RocStr::from("a,b,c"),
RocStr
)
main =
when Encode.toBytes ["a", "b", "c"] TotallyNotJson.json |> Decode.fromBytes TotallyNotJson.json is
Ok l -> Str.joinWith l ","
_ -> "something went wrong"
"#
),
RocStr::from("a,b,c"),
RocStr
)
});
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[ignore = "#3696: Currently hits some weird panic in borrow checking, not sure if it's directly related to abilities."]
fn encode_then_decode_list_of_lists_of_strings() {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform"
main =
when Encode.toBytes [["a", "b"], ["c", "d", "e"], ["f"]] TotallyNotJson.json |> Decode.fromBytes TotallyNotJson.json is
Ok list -> (List.map list \inner -> Str.joinWith inner ",") |> Str.joinWith l ";"
_ -> "something went wrong"
"#
),
RocStr::from("a,b;c,d,e;f"),
RocStr
)
main =
when Encode.toBytes [["a", "b"], ["c", "d", "e"], ["f"]] TotallyNotJson.json |> Decode.fromBytes TotallyNotJson.json is
Ok list -> (List.map list \inner -> Str.joinWith inner ",") |> Str.joinWith l ";"
_ -> "something went wrong"
"#
),
RocStr::from("a,b;c,d,e;f"),
RocStr
)
})
}
#[test]
@ -2135,32 +2149,34 @@ mod eq {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn issue_4772_weakened_monomorphic_destructure() {
assert_evals_to!(
indoc!(
r###"
app "test"
imports [TotallyNotJson]
provides [main] to "./platform"
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r###"
app "test"
imports [TotallyNotJson]
provides [main] to "./platform"
getNumber =
{ result, rest } = Decode.fromBytesPartial (Str.toUtf8 "\"1234\"") TotallyNotJson.json
when result is
Ok val ->
when Str.toI64 val is
Ok number ->
Ok {val : number, input : rest}
Err InvalidNumStr ->
Err (ParsingFailure "not a number")
getNumber =
{ result, rest } = Decode.fromBytesPartial (Str.toUtf8 "\"1234\"") TotallyNotJson.json
when result is
Ok val ->
when Str.toI64 val is
Ok number ->
Ok {val : number, input : rest}
Err InvalidNumStr ->
Err (ParsingFailure "not a number")
Err _ ->
Err (ParsingFailure "not a number")
Err _ ->
Err (ParsingFailure "not a number")
main =
getNumber |> Result.map .val |> Result.withDefault 0
"###
),
1234i64,
i64
);
main =
getNumber |> Result.map .val |> Result.withDefault 0
"###
),
1234i64,
i64
)
})
}

View file

@ -0,0 +1,47 @@
#[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 crate::helpers::with_larger_debug_stack;
//use crate::assert_wasm_evals_to as assert_evals_to;
#[allow(unused_imports)]
use indoc::indoc;
#[allow(unused_imports)]
use roc_std::{RocList, RocResult, RocStr};
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn def_closure_in_parens() {
assert_evals_to!(
indoc!(
r#"
id = (\x -> x)
id 42u32
"#
),
42,
u32
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn def_closure_in_multiple_parens() {
assert_evals_to!(
indoc!(
r#"
id = (((\x -> x)))
id 42u32
"#
),
42,
u32
);
}

View file

@ -0,0 +1,44 @@
#[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to_erased;
use indoc::indoc;
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn capture_multiple() {
assert_evals_to_erased!(
indoc!(
r#"
app "test" provides [main] to "./platform"
f = \n, m ->
\{} -> n + m + 15u8
main = (f 10u8 20u8) {}
"#
),
45,
u8
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn multi_branch_capturing() {
assert_evals_to_erased!(
indoc!(
r#"
app "test" provides [main] to "./platform"
f = \t, s ->
if t
then \{} -> 15nat
else \{} -> Str.countGraphemes s
main = ((f Bool.true "abc") {}, (f Bool.false "abc") {})
"#
),
(15, 3),
(usize, usize)
);
}

View file

@ -7,7 +7,6 @@ use crate::helpers::dev::assert_evals_to;
#[cfg(feature = "gen-wasm")]
use crate::helpers::wasm::assert_evals_to;
#[allow(unused_imports)]
use crate::helpers::with_larger_debug_stack;
//use crate::assert_wasm_evals_to as assert_evals_to;
#[allow(unused_imports)]
@ -1777,14 +1776,14 @@ fn assert_concat_worked(num_elems1: i64, num_elems2: i64) {
let vec2: Vec<i64> = (0..num_elems2)
.map(|i| 54321 % (i + num_elems1 + num_elems2 + 1))
.collect();
let slice_str1 = format!("{:?}", vec1);
let slice_str2 = format!("{:?}", vec2);
let slice_str1 = format!("{vec1:?}");
let slice_str2 = format!("{vec2:?}");
let mut expected = vec1;
expected.extend(vec2);
assert_evals_to!(
&format!("List.concat {} {}", slice_str1, slice_str2),
&format!("List.concat {slice_str1} {slice_str2}"),
RocList::from_slice(&expected),
RocList<i64>
);
@ -3823,6 +3822,8 @@ mod pattern_match {
#[cfg(feature = "gen-dev")]
use crate::helpers::dev::assert_evals_to;
use crate::helpers::with_larger_debug_stack;
use super::RocList;
#[test]
@ -3863,21 +3864,21 @@ mod pattern_match {
with_larger_debug_stack(|| {
assert_evals_to!(
r#"
helper = \l -> when l is
[] -> 1u8
[A] -> 2u8
[A, A, ..] -> 3u8
[A, B, ..] -> 4u8
[B, ..] -> 5u8
helper = \l -> when l is
[] -> 1u8
[A] -> 2u8
[A, A, ..] -> 3u8
[A, B, ..] -> 4u8
[B, ..] -> 5u8
[
helper [],
helper [A],
helper [A, A], helper [A, A, A], helper [A, A, B], helper [A, A, B, A],
helper [A, B], helper [A, B, A], helper [A, B, B], helper [A, B, A, B],
helper [B], helper [B, A], helper [B, B], helper [B, A, B, B],
]
"#,
[
helper [],
helper [A],
helper [A, A], helper [A, A, A], helper [A, A, B], helper [A, A, B, A],
helper [A, B], helper [A, B, A], helper [A, B, B], helper [A, B, A, B],
helper [B], helper [B, A], helper [B, B], helper [B, A, B, B],
]
"#,
RocList::from_slice(&[
1, //
2, //
@ -3887,7 +3888,7 @@ mod pattern_match {
]),
RocList<u8>
)
})
});
}
#[test]
@ -3895,21 +3896,21 @@ mod pattern_match {
with_larger_debug_stack(|| {
assert_evals_to!(
r#"
helper = \l -> when l is
[] -> 1u8
[A] -> 2u8
[.., A, A] -> 3u8
[.., B, A] -> 4u8
[.., B] -> 5u8
helper = \l -> when l is
[] -> 1u8
[A] -> 2u8
[.., A, A] -> 3u8
[.., B, A] -> 4u8
[.., B] -> 5u8
[
helper [],
helper [A],
helper [A, A], helper [A, A, A], helper [B, A, A], helper [A, B, A, A],
helper [B, A], helper [A, B, A], helper [B, B, A], helper [B, A, B, A],
helper [B], helper [A, B], helper [B, B], helper [B, A, B, B],
]
"#,
[
helper [],
helper [A],
helper [A, A], helper [A, A, A], helper [B, A, A], helper [A, B, A, A],
helper [B, A], helper [A, B, A], helper [B, B, A], helper [B, A, B, A],
helper [B], helper [A, B], helper [B, B], helper [B, A, B, B],
]
"#,
RocList::from_slice(&[
1, //
2, //

View file

@ -3966,3 +3966,27 @@ fn mul_checked_dec() {
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn num_min() {
assert_evals_to!(r#"Num.min 0 0"#, 0, i64);
assert_evals_to!(r#"Num.min 1 2"#, 1, i64);
assert_evals_to!(r#"Num.min 2 1"#, 1, i64);
assert_evals_to!(r#"Num.min 2 -2"#, -2, i64);
assert_evals_to!(r#"Num.min -2 2"#, -2, i64);
assert_evals_to!(r#"Num.min Num.minI64 Num.maxI64"#, i64::MIN, i64);
assert_evals_to!(r#"Num.min Num.maxI64 Num.minI64"#, i64::MIN, i64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn num_max() {
assert_evals_to!(r#"Num.max 0 0"#, 0, i64);
assert_evals_to!(r#"Num.max 1 2"#, 2, i64);
assert_evals_to!(r#"Num.max 2 1"#, 2, i64);
assert_evals_to!(r#"Num.max 2 -2"#, 2, i64);
assert_evals_to!(r#"Num.max -2 2"#, 2, i64);
assert_evals_to!(r#"Num.max Num.minI64 Num.maxI64"#, i64::MAX, i64);
assert_evals_to!(r#"Num.max Num.maxI64 Num.minI64"#, i64::MAX, i64);
}

View file

@ -304,7 +304,7 @@ fn apply_unnamed_identity() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_unnamed_fn() {
assert_evals_to!(
indoc!(
@ -325,7 +325,7 @@ fn return_unnamed_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_when_fn() {
assert_evals_to!(
indoc!(
@ -345,7 +345,7 @@ fn gen_when_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_basic_def() {
assert_evals_to!(
indoc!(
@ -373,7 +373,7 @@ fn gen_basic_def() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_multiple_defs() {
assert_evals_to!(
indoc!(
@ -492,7 +492,7 @@ fn gen_multiple_defs() {
// }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn factorial() {
assert_evals_to!(
indoc!(
@ -514,7 +514,7 @@ fn factorial() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn peano1() {
assert_evals_to!(
indoc!(
@ -535,7 +535,7 @@ fn peano1() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn peano2() {
assert_evals_to!(
indoc!(
@ -557,7 +557,7 @@ fn peano2() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn top_level_constant() {
assert_evals_to!(
indoc!(
@ -577,7 +577,7 @@ fn top_level_constant() {
#[test]
#[ignore]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn top_level_destructure() {
assert_evals_to!(
indoc!(
@ -829,7 +829,7 @@ fn linked_list_map() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn when_nested_maybe() {
assert_evals_to!(
indoc!(
@ -886,7 +886,7 @@ fn when_nested_maybe() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn when_peano() {
assert_evals_to!(
indoc!(
@ -992,6 +992,24 @@ fn undefined_variable() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
#[should_panic(expected = "User crash with message: \"a crash\"")]
fn a_crash() {
assert_evals_to!(
indoc!(
r#"
if Bool.true then
crash "a crash"
else
0u64
"#
),
3,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Roc failed with message: ")]
@ -1011,7 +1029,7 @@ fn annotation_without_body() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn simple_closure() {
assert_evals_to!(
indoc!(
@ -1033,7 +1051,7 @@ fn simple_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn nested_closure() {
assert_evals_to!(
indoc!(
@ -1057,7 +1075,7 @@ fn nested_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn closure_in_list() {
use roc_std::RocList;
@ -1085,7 +1103,7 @@ fn closure_in_list() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn specialize_closure() {
use roc_std::RocList;
@ -1117,7 +1135,7 @@ fn specialize_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn io_poc_effect() {
assert_evals_to!(
indoc!(
@ -1148,7 +1166,7 @@ fn io_poc_effect() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn io_poc_desugared() {
assert_evals_to!(
indoc!(
@ -1176,7 +1194,7 @@ fn io_poc_desugared() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_function_a() {
assert_evals_to!(
indoc!(
@ -1198,7 +1216,7 @@ fn return_wrapped_function_a() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_function_b() {
assert_evals_to!(
indoc!(
@ -1219,7 +1237,7 @@ fn return_wrapped_function_b() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_closure() {
assert_evals_to!(
indoc!(
@ -1367,7 +1385,7 @@ fn linked_list_singleton() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn recursive_function_with_rigid() {
assert_evals_to!(
indoc!(
@ -1394,7 +1412,7 @@ fn recursive_function_with_rigid() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn rbtree_insert() {
assert_evals_to!(
indoc!(
@ -1606,7 +1624,7 @@ fn rbtree_balance_mono_problem() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn rbtree_balance_full() {
assert_evals_to!(
indoc!(
@ -1658,7 +1676,7 @@ fn rbtree_balance_full() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn nested_pattern_match_two_ways() {
// exposed an issue in the ordering of pattern match checks when ran with `--release` mode
assert_evals_to!(
@ -1771,6 +1789,7 @@ fn linked_list_double_pattern_match() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// dev backend: this test somehow corrupts the errors vector ?!
fn binary_tree_double_pattern_match() {
assert_evals_to!(
indoc!(
@ -1796,7 +1815,7 @@ fn binary_tree_double_pattern_match() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn unified_empty_closure_bool() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
@ -1821,7 +1840,7 @@ fn unified_empty_closure_bool() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn unified_empty_closure_byte() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
@ -1847,7 +1866,7 @@ fn unified_empty_closure_byte() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn task_always_twice() {
assert_evals_to!(
indoc!(
@ -1892,7 +1911,7 @@ fn task_always_twice() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn wildcard_rigid() {
assert_evals_to!(
indoc!(
@ -1921,7 +1940,7 @@ fn wildcard_rigid() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn alias_of_alias_with_type_arguments() {
assert_evals_to!(
indoc!(
@ -1998,7 +2017,7 @@ fn todo_bad_error_message() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn hof_conditional() {
// exposed issue with the if condition being just a symbol
assert_evals_to!(
@ -2095,7 +2114,7 @@ fn fingertree_basic() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn case_or_pattern() {
// the `0` branch body should only be generated once in the future
// it is currently duplicated
@ -2210,9 +2229,10 @@ fn nullable_eval_cfold() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
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!(
crate::helpers::with_larger_debug_stack(||
// 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"
@ -2248,7 +2268,7 @@ fn nested_switch() {
),
12,
i64
);
));
}
#[test]
@ -3576,7 +3596,7 @@ fn polymorphic_lambda_captures_polymorphic_value() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niche_u64_vs_u8_capture() {
assert_evals_to!(
indoc!(
@ -3604,7 +3624,7 @@ fn lambda_capture_niche_u64_vs_u8_capture() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_with_other_lambda_capture() {
assert_evals_to!(
indoc!(
@ -3637,7 +3657,7 @@ fn lambda_capture_niches_with_other_lambda_capture() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_with_non_capturing_function() {
assert_evals_to!(
indoc!(
@ -3670,7 +3690,7 @@ fn lambda_capture_niches_with_non_capturing_function() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_have_captured_function_in_closure() {
assert_evals_to!(
indoc!(
@ -3862,7 +3882,7 @@ fn recursive_lambda_set_issue_3444_inferred() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn compose_recursive_lambda_set_productive_toplevel() {
assert_evals_to!(
indoc!(
@ -3930,7 +3950,7 @@ fn compose_recursive_lambda_set_productive_inferred() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn compose_recursive_lambda_set_productive_nullable_wrapped() {
assert_evals_to!(
indoc!(
@ -4216,7 +4236,7 @@ fn issue_4349() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn issue_4712() {
assert_evals_to!(
indoc!(
@ -4498,3 +4518,36 @@ fn pass_lambda_set_to_function() {
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn linked_list_trmc() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
LinkedList a : [Nil, Cons a (LinkedList a)]
repeat : a, Nat -> LinkedList a
repeat = \value, n ->
when n is
0 -> Nil
_ -> Cons value (repeat value (n - 1))
length : LinkedList a -> I64
length = \list ->
when list is
Nil -> 0
Cons _ rest -> 1 + length rest
main : I64
main =
repeat "foo" 5
|> length
"#
),
5,
i64
);
}

View file

@ -1218,15 +1218,15 @@ fn str_trim_small_to_small_shared() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr);
fn str_trim_start_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimStart " ""#), RocStr::from(""), RocStr);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_small_to_small() {
fn str_trim_start_small_to_small() {
assert_evals_to!(
indoc!(r#"Str.trimLeft " hello world ""#),
indoc!(r#"Str.trimStart " hello world ""#),
RocStr::from("hello world "),
RocStr
);
@ -1234,9 +1234,9 @@ fn str_trim_left_small_to_small() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_large_to_large_unique() {
fn str_trim_start_large_to_large_unique() {
assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello world from a large string ")"#),
indoc!(r#"Str.trimStart (Str.concat " " "hello world from a large string ")"#),
RocStr::from("hello world from a large string "),
RocStr
);
@ -1244,9 +1244,9 @@ fn str_trim_left_large_to_large_unique() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_large_to_small_unique() {
fn str_trim_start_large_to_small_unique() {
assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello world ")"#),
indoc!(r#"Str.trimStart (Str.concat " " "hello world ")"#),
RocStr::from("hello world "),
RocStr
);
@ -1254,14 +1254,14 @@ fn str_trim_left_large_to_small_unique() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_left_large_to_large_shared() {
fn str_trim_start_large_to_large_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world world "
{ trimmed: Str.trimLeft original, original: original }
{ trimmed: Str.trimStart original, original: original }
"#
),
(
@ -1274,14 +1274,14 @@ fn str_trim_left_large_to_large_shared() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_left_large_to_small_shared() {
fn str_trim_start_large_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world "
{ trimmed: Str.trimLeft original, original: original }
{ trimmed: Str.trimStart original, original: original }
"#
),
(
@ -1294,14 +1294,14 @@ fn str_trim_left_large_to_small_shared() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_left_small_to_small_shared() {
fn str_trim_start_small_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world "
{ trimmed: Str.trimLeft original, original: original }
{ trimmed: Str.trimStart original, original: original }
"#
),
(RocStr::from(" hello world "), RocStr::from("hello world "),),
@ -1311,15 +1311,15 @@ fn str_trim_left_small_to_small_shared() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
fn str_trim_end_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimEnd " ""#), RocStr::from(""), RocStr);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_small_to_small() {
fn str_trim_end_small_to_small() {
assert_evals_to!(
indoc!(r#"Str.trimRight " hello world ""#),
indoc!(r#"Str.trimEnd " hello world ""#),
RocStr::from(" hello world"),
RocStr
);
@ -1327,9 +1327,9 @@ fn str_trim_right_small_to_small() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_large_to_large_unique() {
fn str_trim_end_large_to_large_unique() {
assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
indoc!(r#"Str.trimEnd (Str.concat " hello world from a large string" " ")"#),
RocStr::from(" hello world from a large string"),
RocStr
);
@ -1337,9 +1337,9 @@ fn str_trim_right_large_to_large_unique() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_large_to_small_unique() {
fn str_trim_end_large_to_small_unique() {
assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#),
indoc!(r#"Str.trimEnd (Str.concat " hello world" " ")"#),
RocStr::from(" hello world"),
RocStr
);
@ -1347,14 +1347,14 @@ fn str_trim_right_large_to_small_unique() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_right_large_to_large_shared() {
fn str_trim_end_large_to_large_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world world "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(
@ -1367,14 +1367,14 @@ fn str_trim_right_large_to_large_shared() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_right_large_to_small_shared() {
fn str_trim_end_large_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(
@ -1387,14 +1387,14 @@ fn str_trim_right_large_to_small_shared() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_trim_right_small_to_small_shared() {
fn str_trim_end_small_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(RocStr::from(" hello world "), RocStr::from(" hello world"),),

View file

@ -30,8 +30,8 @@ fn width_and_alignment_u8_u8() {
let layout = LayoutRepr::Union(UnionLayout::NonRecursive(&tt));
assert_eq!(layout.alignment_bytes(&interner, target_info), 1);
assert_eq!(layout.stack_size(&interner, target_info), 2);
assert_eq!(layout.alignment_bytes(&interner), 1);
assert_eq!(layout.stack_size(&interner), 2);
}
#[test]

View file

@ -2,9 +2,13 @@ use libloading::Library;
use roc_build::link::{link, LinkType};
use roc_builtins::bitcode;
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
use roc_mono::ir::CrashTag;
use roc_mono::ir::SingleEntryPoint;
use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
use roc_solve::FunctionKind;
use roc_std::RocStr;
use std::mem::MaybeUninit;
use tempfile::tempdir;
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
@ -58,6 +62,7 @@ pub fn helper(
palette: roc_reporting::report::DEFAULT_PALETTE,
threading: Threading::Single,
exec_mode: ExecutionMode::Executable,
function_kind: FunctionKind::LambdaSet,
};
let loaded = roc_load::load_and_monomorphize_from_str(
arena,
@ -210,8 +215,10 @@ pub fn helper(
let builtins_host_tempfile =
roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile");
// TODO make this an envrionment variable
if false {
std::fs::copy(&app_o_file, "/tmp/app.o").unwrap();
let file_path = std::env::temp_dir().join("app.o");
std::fs::copy(&app_o_file, file_path).unwrap();
}
let (mut child, dylib_path) = link(
@ -243,6 +250,81 @@ pub fn helper(
(main_fn_name, delayed_errors, lib)
}
#[derive(Debug)]
#[repr(C)]
pub struct RocCallResult<T> {
pub tag: u64,
pub error_msg: *mut RocStr,
pub value: MaybeUninit<T>,
}
impl<T> RocCallResult<T> {
pub fn new(value: T) -> Self {
Self {
tag: 0,
error_msg: std::ptr::null_mut(),
value: MaybeUninit::new(value),
}
}
}
impl<T: Default> Default for RocCallResult<T> {
fn default() -> Self {
Self {
tag: 0,
error_msg: std::ptr::null_mut(),
value: MaybeUninit::new(Default::default()),
}
}
}
impl<T> RocCallResult<T> {
pub fn into_result(self) -> Result<T, (String, CrashTag)> {
match self.tag {
0 => Ok(unsafe { self.value.assume_init() }),
n => Err({
let mut msg = RocStr::default();
unsafe { std::ptr::swap(&mut msg, self.error_msg) };
let tag = (n - 1) as u32;
let tag = tag
.try_into()
.unwrap_or_else(|_| panic!("received illegal tag: {tag} {msg}"));
(msg.as_str().to_owned(), tag)
}),
}
}
}
fn get_test_main_fn<T>(
lib: &libloading::Library,
) -> libloading::Symbol<unsafe extern "C" fn() -> RocCallResult<T>> {
let main_fn_name = "test_main";
unsafe {
lib.get(main_fn_name.as_bytes())
.ok()
.ok_or(format!("Unable to JIT compile `{main_fn_name}`"))
.expect("errored")
}
}
pub(crate) fn run_test_main<T>(lib: &libloading::Library) -> Result<T, (String, CrashTag)> {
let main = get_test_main_fn::<T>(lib);
let result = unsafe { main() };
result.into_result()
}
impl<T: Sized> From<RocCallResult<T>> for Result<T, (String, CrashTag)> {
fn from(call_result: RocCallResult<T>) -> Self {
call_result.into_result()
}
}
#[allow(unused_macros)]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{
@ -265,19 +347,40 @@ macro_rules! assert_evals_to {
};
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $lazy_literals:expr) => {
use bumpalo::Bump;
use roc_gen_dev::run_jit_function_raw;
let arena = Bump::new();
let (main_fn_name, errors, lib) =
let (_main_fn_name, errors, lib) =
$crate::helpers::dev::helper(&arena, $src, $leak, $lazy_literals);
let transform = |success| {
let expected = $expected;
#[allow(clippy::redundant_closure_call)]
let given = $transform(success);
assert_eq!(&given, &expected);
};
run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors)
let result = $crate::helpers::dev::run_test_main::<$ty>(&lib);
if !errors.is_empty() {
dbg!(&errors);
assert_eq!(
errors,
std::vec::Vec::new(),
"Encountered errors: {:?}",
errors
);
}
match result {
Ok(value) => {
let expected = $expected;
#[allow(clippy::redundant_closure_call)]
let given = $transform(value);
assert_eq!(&given, &expected, "output is different");
}
Err((msg, tag)) => {
use roc_mono::ir::CrashTag;
match tag {
CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#),
CrashTag::User => panic!(r#"User crash with message: "{msg}""#),
}
}
}
};
}

View file

@ -1,5 +1,6 @@
use std::mem::MaybeUninit;
use std::path::PathBuf;
use std::sync::OnceLock;
use inkwell::module::Module;
use libloading::Library;
@ -8,7 +9,9 @@ use roc_collections::all::MutSet;
use roc_command_utils::zig;
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
use roc_load::{
EntryPoint, ExecutionMode, FunctionKind, LoadConfig, LoadMonomorphizedError, Threading,
};
use roc_mono::ir::{CrashTag, OptLevel, SingleEntryPoint};
use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
@ -44,13 +47,13 @@ fn promote_expr_to_module(src: &str) -> String {
buffer
}
#[allow(clippy::too_many_arguments)]
fn create_llvm_module<'a>(
arena: &'a bumpalo::Bump,
src: &str,
config: HelperConfig,
context: &'a inkwell::context::Context,
target: &Triple,
function_kind: FunctionKind,
) -> (&'static str, String, &'a Module<'a>) {
let target_info = roc_target::TargetInfo::from(target);
@ -70,6 +73,7 @@ fn create_llvm_module<'a>(
let load_config = LoadConfig {
target_info,
function_kind,
render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE,
threading: Threading::Single,
@ -89,17 +93,18 @@ fn create_llvm_module<'a>(
Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
report,
))) => {
println!("{}", report);
println!("{report}");
panic!();
}
Err(e) => panic!("{:?}", e),
Err(e) => panic!("{e:?}"),
};
use roc_load::MonomorphizedModule;
let MonomorphizedModule {
procedures,
host_exposed_lambda_sets,
interns,
mut layout_interner,
layout_interner,
..
} = loaded;
@ -192,7 +197,7 @@ fn create_llvm_module<'a>(
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = context.create_enum_attribute(kind_id, 1);
let attr = context.create_enum_attribute(kind_id, 0);
for function in module.get_functions() {
let name = function.get_name().to_str().unwrap();
@ -257,16 +262,17 @@ fn create_llvm_module<'a>(
LlvmBackendMode::CliTest => unreachable!(),
LlvmBackendMode::WasmGenTest => roc_gen_llvm::llvm::build::build_wasm_test_wrapper(
&env,
&mut layout_interner,
&layout_interner,
config.opt_level,
procedures,
entry_point,
),
LlvmBackendMode::GenTest => roc_gen_llvm::llvm::build::build_procedures_return_main(
&env,
&mut layout_interner,
&layout_interner,
config.opt_level,
procedures,
host_exposed_lambda_sets,
entry_point,
),
};
@ -279,23 +285,25 @@ fn create_llvm_module<'a>(
// Uncomment this to see the module's un-optimized LLVM instruction output:
// env.module.print_to_stderr();
let panic_bad_llvm = |errors| {
let path = std::env::temp_dir().join("test.ll");
env.module.print_to_file(&path).unwrap();
panic!(
"Errors defining module:\n\n{errors}\n\nI have written the full module to `{path:?}`"
);
};
if main_fn.verify(true) {
function_pass.run_on(&main_fn);
} else {
panic!("Main function {} failed LLVM verification in NON-OPTIMIZED build. Uncomment things nearby to see more details.", main_fn_name);
panic_bad_llvm(main_fn_name);
}
module_pass.run_on(env.module);
// Verify the module
if let Err(errors) = env.module.verify() {
let path = std::env::temp_dir().join("test.ll");
env.module.print_to_file(&path).unwrap();
panic!(
"Errors defining module:\n\n{}\n\nI have written the full module to `{:?}`",
errors.to_string(),
path
);
panic_bad_llvm(&errors.to_string());
}
if let Ok(path) = std::env::var("ROC_DEBUG_LLVM") {
@ -328,11 +336,12 @@ pub fn helper<'a>(
config: HelperConfig,
src: &str,
context: &'a inkwell::context::Context,
function_kind: FunctionKind,
) -> (&'static str, String, Library) {
let target = target_lexicon::Triple::host();
let (main_fn_name, delayed_errors, module) =
create_llvm_module(arena, src, config, context, &target);
create_llvm_module(arena, src, config, context, &target, function_kind);
let res_lib = if config.add_debug_info {
let module = annotate_with_debug_info(module, context);
@ -371,7 +380,7 @@ fn annotate_with_debug_info<'ctx>(
ErrorKind::NotFound => panic!(
r"I could not find the `debugir` tool on the PATH, install it from https://github.com/vaivaswatha/debugir"
),
_ => panic!("{:?}", error),
_ => panic!("{error:?}"),
}
}
}
@ -408,24 +417,25 @@ fn write_final_wasm() -> bool {
false
}
lazy_static::lazy_static! {
static ref TEMP_DIR: tempfile::TempDir = tempfile::tempdir().unwrap();
}
#[allow(dead_code)]
fn compile_to_wasm_bytes<'a>(
arena: &'a bumpalo::Bump,
config: HelperConfig,
src: &str,
context: &'a inkwell::context::Context,
function_kind: FunctionKind,
) -> Vec<u8> {
// globally cache the temporary directory
static TEMP_DIR: OnceLock<tempfile::TempDir> = OnceLock::new();
let temp_dir = TEMP_DIR.get_or_init(|| tempfile::tempdir().unwrap());
let target = wasm32_target_tripple();
let (_main_fn_name, _delayed_errors, llvm_module) =
create_llvm_module(arena, src, config, context, &target);
create_llvm_module(arena, src, config, context, &target, function_kind);
let content_hash = crate::helpers::src_hash(src);
let wasm_file = llvm_module_to_wasm_file(&TEMP_DIR, content_hash, llvm_module);
let wasm_file = llvm_module_to_wasm_file(temp_dir, content_hash, llvm_module);
let compiled_bytes = std::fs::read(wasm_file).unwrap();
if write_final_wasm() {
@ -492,17 +502,14 @@ fn llvm_module_to_wasm_file(
let msg = String::from_utf8_lossy(&output.stderr);
if msg.contains("wasm-ld: error: unknown file type") {
panic!(
"{}\nThis can happen if multiple tests have the same input string",
msg
);
panic!("{msg}\nThis can happen if multiple tests have the same input string");
} else {
panic!("{}", msg);
}
}
assert!(output.status.success(), "{:#?}", output);
assert!(output.stdout.is_empty(), "{:#?}", output);
assert!(output.status.success(), "{output:#?}");
assert!(output.stdout.is_empty(), "{output:#?}");
test_wasm_path
}
@ -513,7 +520,11 @@ fn fake_wasm_main_function(_: u32, _: u32) -> u32 {
}
#[cfg(feature = "gen-llvm-wasm")]
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
pub fn assert_wasm_evals_to_help<T>(
src: &str,
ignore_problems: bool,
function_kind: FunctionKind,
) -> Result<T, String>
where
T: FromWasm32Memory + Wasm32Result,
{
@ -527,15 +538,19 @@ where
opt_level: OPT_LEVEL,
};
let wasm_bytes = compile_to_wasm_bytes(&arena, config, src, &context);
let wasm_bytes = compile_to_wasm_bytes(&arena, config, src, &context, function_kind);
crate::helpers::wasm::run_wasm_test_bytes::<T>(TEST_WRAPPER_NAME, wasm_bytes)
}
#[allow(unused_macros)]
#[cfg(feature = "gen-llvm-wasm")]
macro_rules! assert_wasm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
match $crate::helpers::llvm::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
match $crate::helpers::llvm::assert_wasm_evals_to_help::<$ty>(
$src,
$ignore_problems,
roc_load::FunctionKind::LambdaSet,
) {
Err(msg) => panic!("Wasm test failed: {}", msg),
Ok(actual) => {
assert_eq!($transform(actual), $expected, "Wasm test failed")
@ -567,7 +582,7 @@ pub fn try_run_lib_function<T>(
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<T>)> = lib
.get(main_fn_name.as_bytes())
.ok()
.ok_or(format!("Unable to JIT compile `{}`", main_fn_name))
.ok_or(format!("Unable to JIT compile `{main_fn_name}`"))
.expect("errored");
let mut main_result = MaybeUninit::uninit();
@ -578,9 +593,13 @@ pub fn try_run_lib_function<T>(
}
// only used in tests
#[allow(unused)]
pub(crate) fn llvm_evals_to<T, U, F>(src: &str, expected: U, transform: F, ignore_problems: bool)
where
pub(crate) fn llvm_evals_to<T, U, F>(
src: &str,
expected: U,
transform: F,
ignore_problems: bool,
function_kind: FunctionKind,
) where
U: PartialEq + std::fmt::Debug,
F: FnOnce(T) -> U,
{
@ -597,14 +616,15 @@ where
opt_level: crate::helpers::llvm::OPT_LEVEL,
};
let (main_fn_name, errors, lib) = crate::helpers::llvm::helper(&arena, config, src, &context);
let (main_fn_name, errors, lib) =
crate::helpers::llvm::helper(&arena, config, src, &context, function_kind);
let result = crate::helpers::llvm::try_run_lib_function::<T>(main_fn_name, &lib);
match result {
Ok(raw) => {
// only if there are no exceptions thrown, check for errors
assert!(errors.is_empty(), "Encountered errors:\n{}", errors);
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
#[allow(clippy::redundant_closure_call)]
let given = transform(raw);
@ -615,13 +635,12 @@ where
std::mem::forget(given);
}
Err((msg, tag)) => match tag {
CrashTag::Roc => panic!(r#"Roc failed with message: "{}""#, msg),
CrashTag::User => panic!(r#"User crash with message: "{}""#, msg),
CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#),
CrashTag::User => panic!(r#"User crash with message: "{msg}""#),
},
}
}
#[allow(unused_macros)]
macro_rules! assert_llvm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
crate::helpers::llvm::llvm_evals_to::<$ty, _, _>(
@ -629,6 +648,7 @@ macro_rules! assert_llvm_evals_to {
$expected,
$transform,
$ignore_problems,
roc_load::FunctionKind::LambdaSet,
);
};
@ -654,8 +674,6 @@ macro_rules! assert_llvm_evals_to {
//
// let (_main_fn_name, _delayed_errors, _module) =
// $crate::helpers::llvm::create_llvm_module(&arena, $src, config, &context, &target);
#[allow(unused_macros)]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity, false);
@ -686,14 +704,24 @@ macro_rules! assert_evals_to {
}};
}
#[allow(dead_code)]
macro_rules! assert_evals_to_erased {
($src:expr, $expected:expr, $ty:ty) => {{
crate::helpers::llvm::llvm_evals_to::<$ty, _, _>(
$src,
$expected,
$crate::helpers::llvm::identity,
false,
roc_load::FunctionKind::Erased,
);
}};
}
pub fn identity<T>(value: T) -> T {
value
}
#[allow(unused_imports)]
pub(crate) use assert_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_evals_to_erased;
pub(crate) use assert_llvm_evals_to;
#[allow(unused_imports)]
#[cfg(feature = "gen-llvm-wasm")]
pub(crate) use assert_wasm_evals_to;

View file

@ -24,7 +24,7 @@ pub(crate) fn src_hash(src: &str) -> u64 {
pub(crate) fn save_wasm_file(app_module_bytes: &[u8], build_dir_hash: u64) {
use std::path::Path;
let debug_dir_str = format!("/tmp/roc/gen_wasm/{:016x}", build_dir_hash);
let debug_dir_str = format!("/tmp/roc/gen_wasm/{build_dir_hash:016x}");
let debug_dir_path = Path::new(&debug_dir_str);
let final_wasm_file = debug_dir_path.join("final.wasm");

View file

@ -7,6 +7,7 @@ use roc_gen_wasm::DEBUG_SETTINGS;
use roc_load::{ExecutionMode, LoadConfig, Threading};
use roc_packaging::cache::RocCacheDir;
use roc_reporting::report::DEFAULT_PALETTE_HTML;
use roc_solve::FunctionKind;
use roc_std::RocStr;
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
use roc_wasm_module::{Export, ExportType, Value, WasmModule};
@ -92,6 +93,7 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32Result>(
palette: DEFAULT_PALETTE_HTML,
threading: Threading::Single,
exec_mode: ExecutionMode::Executable,
function_kind: FunctionKind::LambdaSet,
};
let loaded = roc_load::load_and_monomorphize_from_str(
arena,

View file

@ -1,4 +0,0 @@
//! Contains all of Roc's [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler))
//! tests.
#[cfg(test)]
pub mod helpers;

View file

@ -6,7 +6,9 @@
pub mod gen_abilities;
pub mod gen_compare;
pub mod gen_definitions;
pub mod gen_dict;
pub mod gen_erased;
pub mod gen_list;
pub mod gen_num;
pub mod gen_panic;

View file

@ -14,8 +14,7 @@ use roc_module::symbol::{
Symbol,
};
use roc_mono::ir::{
Call, CallType, Expr, HostExposedLayouts, Literal, Proc, ProcLayout, SelfRecursive, Stmt,
UpdateModeId,
Call, CallType, Expr, Literal, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId,
};
use roc_mono::layout::{LambdaName, Layout, Niche, STLayoutInterner};
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
@ -116,7 +115,7 @@ fn build_app_mono<'a>(
closure_data_layout: None,
ret_layout: int_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
};
let proc_layout = ProcLayout {

View file

@ -989,78 +989,78 @@ fn str_trim_small_to_small_shared() {
}
#[test]
fn str_trim_left_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr);
fn str_trim_start_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimStart " ""#), RocStr::from(""), RocStr);
}
#[test]
fn str_trim_left_small_to_small() {
fn str_trim_start_small_to_small() {
assert_evals_to!(
indoc!(r#"Str.trimLeft " hello ""#),
indoc!(r#"Str.trimStart " hello ""#),
RocStr::from("hello "),
RocStr
);
}
#[test]
fn str_trim_left_large_to_large_unique() {
fn str_trim_start_large_to_large_unique() {
assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello world from a large string ")"#),
indoc!(r#"Str.trimStart (Str.concat " " "hello world from a large string ")"#),
RocStr::from("hello world from a large string "),
RocStr
);
}
#[test]
fn str_trim_left_large_to_small_unique() {
fn str_trim_start_large_to_small_unique() {
assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello ")"#),
indoc!(r#"Str.trimStart (Str.concat " " "hello ")"#),
RocStr::from("hello "),
RocStr
);
}
#[test]
fn str_trim_right_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
fn str_trim_end_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimEnd " ""#), RocStr::from(""), RocStr);
}
#[test]
fn str_trim_right_small_to_small() {
fn str_trim_end_small_to_small() {
assert_evals_to!(
indoc!(r#"Str.trimRight " hello ""#),
indoc!(r#"Str.trimEnd " hello ""#),
RocStr::from(" hello"),
RocStr
);
}
#[test]
fn str_trim_right_large_to_large_unique() {
fn str_trim_end_large_to_large_unique() {
assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
indoc!(r#"Str.trimEnd (Str.concat " hello world from a large string" " ")"#),
RocStr::from(" hello world from a large string"),
RocStr
);
}
#[test]
fn str_trim_right_large_to_small_unique() {
fn str_trim_end_large_to_small_unique() {
assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello" " ")"#),
indoc!(r#"Str.trimEnd (Str.concat " hello" " ")"#),
RocStr::from(" hello"),
RocStr
);
}
#[test]
fn str_trim_right_large_to_large_shared() {
fn str_trim_end_large_to_large_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello world world "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(
@ -1072,14 +1072,14 @@ fn str_trim_right_large_to_large_shared() {
}
#[test]
fn str_trim_right_large_to_small_shared() {
fn str_trim_end_large_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(RocStr::from(" hello "), RocStr::from(" hello"),),
@ -1088,14 +1088,14 @@ fn str_trim_right_large_to_small_shared() {
}
#[test]
fn str_trim_right_small_to_small_shared() {
fn str_trim_end_small_to_small_shared() {
assert_evals_to!(
indoc!(
r#"
original : Str
original = " hello "
{ trimmed: Str.trimRight original, original: original }
{ trimmed: Str.trimEnd original, original: original }
"#
),
(RocStr::from(" hello "), RocStr::from(" hello"),),