We got a test working for panicking with the appropriate number fo failures. Ultimatly we want:

+ An error maessage that says what the failures were
+ Not panicking (so these are effectively error productions)
This commit is contained in:
Chelsea Troy 2022-01-30 18:55:09 -06:00
parent 085c02ffee
commit 4f8d0776b3
8 changed files with 103 additions and 24 deletions

View file

@ -23,12 +23,13 @@ extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
comptime {
const builtin = @import("builtin");
// During tetsts, use the testing allocators to satisfy these functions.
// During tests, use the testing allocators to satisfy these functions.
if (builtin.is_test) {
@export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong });
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
@export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong });
@export(testing_roc_memcpy, .{ .name = "roc_memcpy", .linkage = .Strong });
}
}
@ -56,6 +57,14 @@ fn testing_roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
@panic("Roc panicked");
}
fn testing_roc_memcpy(dest: *c_void, src: *c_void, bytes: usize) callconv(.C) ?*c_void {
const zig_dest = @ptrCast([*]u8, dest);
const zig_src = @ptrCast([*]u8, src);
@memcpy(zig_dest, zig_src, bytes);
return dest;
}
pub fn alloc(size: usize, alignment: u32) [*]u8 {
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
}

View file

@ -6030,7 +6030,10 @@ fn run_low_level<'a, 'ctx, 'env>(
{
bd.position_at_end(throw_block);
let func = env.module.get_function("roc_builtins.utils.expect_failed").unwrap();
let func = env
.module
.get_function("roc_builtins.utils.expect_failed")
.unwrap();
let callable = CallableValue::try_from(func).unwrap();
let start_line = context.i32_type().const_int(0, false);
let end_line = context.i32_type().const_int(0, false);
@ -6039,7 +6042,12 @@ fn run_low_level<'a, 'ctx, 'env>(
bd.build_call(
callable,
&[start_line.into(), end_line.into(), start_col.into(), end_col.into()],
&[
start_line.into(),
end_line.into(),
start_col.into(),
end_col.into(),
],
"call_expect_failed",
);

View file

@ -42,6 +42,43 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
}
}
// roc_memcpy
{
// The type of this function (but not the implementation) should have
// already been defined by the builtins, which rely on it.
let fn_val = module.get_function("roc_memcpy").unwrap();
let mut params = fn_val.get_param_iter();
let dest_arg = params.next().unwrap();
let dest_alignment = 1;
let src_arg = params.next().unwrap();
let src_alignment = 1;
let bytes_arg = params.next().unwrap();
debug_assert!(params.next().is_none());
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
// Call libc memcpy()
let _retval = builder
.build_memcpy(
dest_arg.into_pointer_value(),
dest_alignment,
src_arg.into_pointer_value(),
src_alignment,
bytes_arg.into_int_value(),
)
.unwrap();
builder.build_return(None);
if cfg!(debug_assertions) {
crate::llvm::build::verify_fn(fn_val);
}
}
// roc_realloc
{
let libc_realloc_val = {

View file

@ -45,22 +45,45 @@ macro_rules! run_jit_function {
use roc_gen_llvm::run_roc::RocCallResult;
use std::mem::MaybeUninit;
#[derive(Debug, Copy, Clone)]
struct Failure {
start_line: u32,
end_line: u32,
start_col: u16,
end_col: u16,
}
unsafe {
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<$ty>) -> ()> =
$lib.get($main_fn_name.as_bytes())
.ok()
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
.expect("errored");
let get_expect_failures: libloading::Symbol<unsafe extern "C" fn() -> (usize, usize)> =
$lib.get("roc_builtins.utils.get_expect_failures".as_bytes())
let get_expect_failures: libloading::Symbol<
unsafe extern "C" fn() -> (*const Failure, usize),
> = $lib
.get("roc_builtins.utils.get_expect_failures".as_bytes())
.ok()
.ok_or(format!("Unable to JIT compile `{}`", "roc_builtins.utils.get_expect_failures"))
.ok_or(format!(
"Unable to JIT compile `{}`",
"roc_builtins.utils.get_expect_failures"
))
.expect("errored");
let mut result = MaybeUninit::uninit();
let mut main_result = MaybeUninit::uninit();
main(result.as_mut_ptr());
main(main_result.as_mut_ptr());
let (failures_ptr, num_failures) = get_expect_failures();
let mut failures = std::vec::Vec::new();
match result.assume_init().into() {
for index in 0..num_failures {
failures.push(*failures_ptr.add(index));
}
if (num_failures > 0) {
panic!("Failed with {} failures. Failures: ", num_failures);
}
match main_result.assume_init().into() {
Ok(success) => {
// only if there are no exceptions thrown, check for errors
assert!($errors.is_empty(), "Encountered errors:\n{}", $errors);

View file

@ -2513,6 +2513,7 @@ fn call_invalid_layout() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[should_panic(expected = "Failed with 1 failures. Failures: ")]
fn expect_fail() {
assert_expect_failed!(
indoc!(

View file

@ -272,10 +272,9 @@ macro_rules! assert_expect_failed {
};
run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors);
todo!("Actually look up the failures and check them")
};
}};
}
#[allow(unused_imports)]
pub(crate) use assert_evals_to;
pub(crate) use assert_expect_failed;

View file

@ -192,7 +192,11 @@ fn create_llvm_module<'a>(
for function in FunctionIterator::from_module(module) {
let name = function.get_name().to_str().unwrap();
if name.starts_with("roc_builtins") {
function.set_linkage(Linkage::Internal);
if name.starts_with("roc_builtins.utils") {
function.set_linkage(Linkage::External);
} else {
function.set_linkage(Linkage::Internal);
}
}
if name.starts_with("roc_builtins.dict") {
@ -613,19 +617,14 @@ macro_rules! assert_expect_failed {
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
let is_gen_test = true;
let (main_fn_name, errors, lib) = $crate::helpers::llvm::helper(
&arena,
$src,
stdlib,
is_gen_test,
false,
&context,
);
let (main_fn_name, errors, lib) =
$crate::helpers::llvm::helper(&arena, $src, stdlib, is_gen_test, false, &context);
let transform = |success| {
let expected = $expected;
assert_eq!(&success, &expected, "LLVM test failed");
};
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
};
@ -644,7 +643,6 @@ macro_rules! assert_expect_failed {
};
}
#[allow(dead_code)]
pub fn identity<T>(value: T) -> T {
value
@ -674,11 +672,10 @@ macro_rules! assert_non_opt_evals_to {
#[allow(unused_imports)]
pub(crate) use assert_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_expect_failed;
#[allow(unused_imports)]
pub(crate) use assert_llvm_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_non_opt_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_wasm_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_expect_failed;

View file

@ -23,6 +23,11 @@ pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
libc::malloc(size)
}
#[no_mangle]
pub unsafe fn roc_memcpy(dest: *mut c_void, src: *const c_void, bytes: usize) -> *mut c_void {
libc::memcpy(dest, src, bytes)
}
#[no_mangle]
pub unsafe fn roc_realloc(
c_ptr: *mut c_void,