add integration tests, unwind across FFI boundary

### Integration Tests

    This commit introduces some new fixtures to the `run-make-fulldeps`
    test suite.

        * c-unwind-abi-catch-panic: Exercise unwinding a panic. This
          catches a panic across an FFI boundary and downcasts it into
          an integer.

        * c-unwind-abi-catch-lib-panic: This is similar to the previous
         `*catch-panic` test, however in this case the Rust code that
         panics resides in a separate crate.

 ### Add `rust_eh_personality` to `#[no_std]` alloc tests

    This commit addresses some test failures that now occur in the
    following two tests:

        * no_std-alloc-error-handler-custom.rs
        * no_std-alloc-error-handler-default.rs

    Each test now defines a `rust_eh_personality` extern function, in
    the same manner as shown in the "Writing an executable without
    stdlib" section of the `lang_items` documentation here:
    https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib

    Without this change, these tests would fail to compile due to a
    linking error explaining that there was an "undefined reference
    to `rust_eh_personality'."

 ### Updated hash

    * update 32-bit hash in `impl1` test

 ### Panics

    This commit uses `panic!` macro invocations that return a string,
    rather than using an integer as a panic payload.

    Doing so avoids the following warnings that were observed during
    rollup for the `*-msvc-1` targets:

    ```
    warning: panic message is not a string literal
      --> panic.rs:10:16
       |
    10 |         panic!(x); // That is too big!
       |                ^
       |
       = note: `#[warn(non_fmt_panic)]` on by default
       = note: this is no longer accepted in Rust 2021
    help: add a "{}" format string to Display the message
       |
    10 |         panic!("{}", x); // That is too big!
       |                ^^^^^
    help: or use std::panic::panic_any instead
       |
    10 |         std::panic::panic_any(x); // That is too big!
       |         ^^^^^^^^^^^^^^^^^^^^^

    warning: 1 warning emitted
    ```

    See: https://github.com/rust-lang-ci/rust/runs/1992118428

    As these errors imply, panicking without a format string will be
    disallowed in Rust 2021, per #78500.
This commit is contained in:
katelyn a. martin 2020-12-11 20:54:47 -05:00
parent 0f33e9f281
commit baf227ea0c
10 changed files with 166 additions and 6 deletions

View File

@ -0,0 +1,30 @@
-include ../tools.mk
all: archive
# Compile `main.rs`, which will link into our library, and run it.
$(RUSTC) main.rs
$(call RUN,main)
ifdef IS_MSVC
archive: add.o panic.o
# Now, create an archive using these two objects.
$(AR) crus $(TMPDIR)/add.lib $(TMPDIR)/add.o $(TMPDIR)/panic.o
else
archive: add.o panic.o
# Now, create an archive using these two objects.
$(AR) crus $(TMPDIR)/libadd.a $(TMPDIR)/add.o $(TMPDIR)/panic.o
endif
# Compile `panic.rs` into an object file.
#
# Note that we invoke `rustc` directly, so we may emit an object rather
# than an archive. We'll do that later.
panic.o:
$(BARE_RUSTC) $(RUSTFLAGS) \
--out-dir $(TMPDIR) \
--emit=obj panic.rs
# Compile `add.c` into an object file.
add.o:
$(call COMPILE_OBJ,$(TMPDIR)/add.o,add.c)

View File

@ -0,0 +1,12 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
// An external function, defined in Rust.
extern void panic_if_greater_than_10(unsigned x);
unsigned add_small_numbers(unsigned a, unsigned b) {
unsigned c = a + b;
panic_if_greater_than_10(c);
return c;
}

View File

@ -0,0 +1,35 @@
//! A test for calling `C-unwind` functions across foreign function boundaries.
//!
//! This test triggers a panic in a Rust library that our foreign function invokes. This shows
//! that we can unwind through the C code in that library, and catch the underlying panic.
#![feature(c_unwind)]
use std::panic::{catch_unwind, AssertUnwindSafe};
fn main() {
// Call `add_small_numbers`, passing arguments that will NOT trigger a panic.
let (a, b) = (9, 1);
let c = unsafe { add_small_numbers(a, b) };
assert_eq!(c, 10);
// Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it.
let caught_unwind = catch_unwind(AssertUnwindSafe(|| {
let (a, b) = (10, 1);
let _c = unsafe { add_small_numbers(a, b) };
unreachable!("should have unwound instead of returned");
}));
// Assert that we did indeed panic, then unwrap and downcast the panic into the sum.
assert!(caught_unwind.is_err());
let panic_obj = caught_unwind.unwrap_err();
let msg = panic_obj.downcast_ref::<String>().unwrap();
assert_eq!(msg, "11");
}
#[link(name = "add", kind = "static")]
extern "C-unwind" {
/// An external function, defined in C.
///
/// Returns the sum of two numbers, or panics if the sum is greater than 10.
fn add_small_numbers(a: u32, b: u32) -> u32;
}

View File

@ -0,0 +1,12 @@
#![crate_type = "staticlib"]
#![feature(c_unwind)]
/// This function will panic if `x` is greater than 10.
///
/// This function is called by `add_small_numbers`.
#[no_mangle]
pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) {
if x > 10 {
panic!("{}", x); // That is too big!
}
}

View File

@ -0,0 +1,5 @@
-include ../tools.mk
all: $(call NATIVE_STATICLIB,add)
$(RUSTC) main.rs
$(call RUN,main) || exit 1

View File

@ -0,0 +1,12 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
// An external function, defined in Rust.
extern void panic_if_greater_than_10(unsigned x);
unsigned add_small_numbers(unsigned a, unsigned b) {
unsigned c = a + b;
panic_if_greater_than_10(c);
return c;
}

View File

@ -0,0 +1,44 @@
//! A test for calling `C-unwind` functions across foreign function boundaries.
//!
//! This test triggers a panic when calling a foreign function that calls *back* into Rust.
#![feature(c_unwind)]
use std::panic::{catch_unwind, AssertUnwindSafe};
fn main() {
// Call `add_small_numbers`, passing arguments that will NOT trigger a panic.
let (a, b) = (9, 1);
let c = unsafe { add_small_numbers(a, b) };
assert_eq!(c, 10);
// Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it.
let caught_unwind = catch_unwind(AssertUnwindSafe(|| {
let (a, b) = (10, 1);
let _c = unsafe { add_small_numbers(a, b) };
unreachable!("should have unwound instead of returned");
}));
// Assert that we did indeed panic, then unwrap and downcast the panic into the sum.
assert!(caught_unwind.is_err());
let panic_obj = caught_unwind.unwrap_err();
let msg = panic_obj.downcast_ref::<String>().unwrap();
assert_eq!(msg, "11");
}
#[link(name = "add", kind = "static")]
extern "C-unwind" {
/// An external function, defined in C.
///
/// Returns the sum of two numbers, or panics if the sum is greater than 10.
fn add_small_numbers(a: u32, b: u32) -> u32;
}
/// This function will panic if `x` is greater than 10.
///
/// This function is called by `add_small_numbers`.
#[no_mangle]
pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) {
if x > 10 {
panic!("{}", x); // That is too big!
}
}

View File

@ -7,7 +7,7 @@
// compile-flags:-C panic=abort
// aux-build:helper.rs
#![feature(start, rustc_private, new_uninit, panic_info_message)]
#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
#![feature(alloc_error_handler)]
#![no_std]
@ -84,6 +84,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
}
}
// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
extern fn rust_eh_personality() {}
#[derive(Debug)]
struct Page([[u64; 32]; 16]);

View File

@ -8,7 +8,7 @@
// aux-build:helper.rs
// gate-test-default_alloc_error_handler
#![feature(start, rustc_private, new_uninit, panic_info_message)]
#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
#![feature(default_alloc_error_handler)]
#![no_std]
@ -71,6 +71,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
}
}
// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
extern fn rust_eh_personality() {}
#[derive(Debug)]
struct Page([[u64; 32]; 16]);

View File

@ -74,7 +74,3 @@ fn main() {
}
};
}
// FIXME(katie): The 32-bit symbol hash probably needs updating as well, but I'm slightly unsure
// about how to do that. This comment is here so that we don't break the test due to error messages
// including incorrect line numbers.