blob: 21a6fee7c92118919896f0cd88b3107668a61a05 [file] [log] [blame]
// ignore-windows: Unwind panicking does not currently work on Windows
// normalize-stderr-test "[^ ]*libcore/macros/mod.rs[0-9:]*" -> "$$LOC"
#![feature(never_type)]
#![allow(unconditional_panic)]
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::cell::Cell;
thread_local! {
static MY_COUNTER: Cell<usize> = Cell::new(0);
static DROPPED: Cell<bool> = Cell::new(false);
static HOOK_CALLED: Cell<bool> = Cell::new(false);
}
struct DropTester;
impl Drop for DropTester {
fn drop(&mut self) {
DROPPED.with(|c| {
c.set(true);
});
}
}
fn do_panic_counter(do_panic: impl FnOnce(usize) -> !) {
// If this gets leaked, it will be easy to spot
// in Miri's leak report
let _string = "LEAKED FROM do_panic_counter".to_string();
// When we panic, this should get dropped during unwinding
let _drop_tester = DropTester;
// Check for bugs in Miri's panic implementation.
// If do_panic_counter() somehow gets called more than once,
// we'll generate a different panic message and stderr will differ.
let old_val = MY_COUNTER.with(|c| {
let val = c.get();
c.set(val + 1);
val
});
do_panic(old_val);
}
fn main() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
HOOK_CALLED.with(|h| h.set(true));
prev(panic_info)
}));
// Std panics
test(|_old_val| std::panic!("Hello from panic: std"));
test(|old_val| std::panic!(format!("Hello from panic: {:?}", old_val)));
test(|old_val| std::panic!("Hello from panic: {:?}", old_val));
test(|_old_val| std::panic!(1337));
// Core panics
test(|_old_val| core::panic!("Hello from panic: core"));
test(|old_val| core::panic!(&format!("Hello from panic: {:?}", old_val)));
test(|old_val| core::panic!("Hello from panic: {:?}", old_val));
// Built-in panics
test(|_old_val| { let _val = [0, 1, 2][4]; loop {} });
test(|_old_val| { let _val = 1/0; loop {} });
// Assertion and debug assertion
test(|_old_val| { assert!(false); loop {} });
test(|_old_val| { debug_assert!(false); loop {} });
test(|_old_val| { unsafe { (1 as *const i32).read() }; loop {} }); // trigger debug-assertion in libstd
// Cleanup: reset to default hook.
drop(std::panic::take_hook());
eprintln!("Success!"); // Make sure we get this in stderr
}
fn test(do_panic: impl FnOnce(usize) -> !) {
// Reset test flags.
DROPPED.with(|c| c.set(false));
HOOK_CALLED.with(|c| c.set(false));
// Cause and catch a panic.
let res = catch_unwind(AssertUnwindSafe(|| {
let _string = "LEAKED FROM CLOSURE".to_string();
do_panic_counter(do_panic)
})).expect_err("do_panic() did not panic!");
// See if we can extract the panic message.
if let Some(s) = res.downcast_ref::<String>() {
eprintln!("Caught panic message (String): {}", s);
} else if let Some(s) = res.downcast_ref::<&str>() {
eprintln!("Caught panic message (&str): {}", s);
} else {
eprintln!("Failed get caught panic message.");
}
// Test flags.
assert!(DROPPED.with(|c| c.get()));
assert!(HOOK_CALLED.with(|c| c.get()));
}