1 // normalize-stderr-test "[^ ]*core/[a-z_/]+.rs[0-9:]*" -> "$$LOC"
2 // normalize-stderr-test "catch_panic\.rs:[0-9]{2}" -> "catch_panic.rs:LL"
3 // We test the `align_offset` panic below, make sure we test the interpreter impl and not the "real" one.
4 // compile-flags: -Zmiri-symbolic-alignment-check
5 #![feature(never_type)]
6 #![allow(unconditional_panic, non_fmt_panics)]
7 
8 use std::panic::{catch_unwind, AssertUnwindSafe};
9 use std::cell::Cell;
10 
11 thread_local! {
12     static MY_COUNTER: Cell<usize> = Cell::new(0);
13     static DROPPED: Cell<bool> = Cell::new(false);
14     static HOOK_CALLED: Cell<bool> = Cell::new(false);
15 }
16 
17 struct DropTester;
18 impl Drop for DropTester {
drop(&mut self)19     fn drop(&mut self) {
20         DROPPED.with(|c| {
21             c.set(true);
22         });
23     }
24 }
25 
do_panic_counter(do_panic: impl FnOnce(usize) -> !)26 fn do_panic_counter(do_panic: impl FnOnce(usize) -> !) {
27     // If this gets leaked, it will be easy to spot
28     // in Miri's leak report
29     let _string = "LEAKED FROM do_panic_counter".to_string();
30 
31     // When we panic, this should get dropped during unwinding
32     let _drop_tester = DropTester;
33 
34     // Check for bugs in Miri's panic implementation.
35     // If do_panic_counter() somehow gets called more than once,
36     // we'll generate a different panic message and stderr will differ.
37     let old_val = MY_COUNTER.with(|c| {
38         let val = c.get();
39         c.set(val + 1);
40         val
41     });
42     do_panic(old_val);
43 }
44 
main()45 fn main() {
46     let prev = std::panic::take_hook();
47     std::panic::set_hook(Box::new(move |panic_info| {
48         HOOK_CALLED.with(|h| h.set(true));
49         prev(panic_info)
50     }));
51 
52     // Std panics
53     test(None, |_old_val| std::panic!("Hello from panic: std"));
54     test(None, |old_val| std::panic!(format!("Hello from panic: {:?}", old_val)));
55     test(None, |old_val| std::panic!("Hello from panic: {:?}", old_val));
56     test(None, |_old_val| std::panic!(1337));
57 
58     // Core panics
59     test(None, |_old_val| core::panic!("Hello from panic: core"));
60     test(None, |old_val| core::panic!(&format!("Hello from panic: {:?}", old_val)));
61     test(None, |old_val| core::panic!("Hello from panic: {:?}", old_val));
62 
63     // Built-in panics; also make sure the message is right.
64     test(
65         Some("index out of bounds: the len is 3 but the index is 4"),
66         |_old_val| { let _val = [0, 1, 2][4]; loop {} },
67     );
68     test(
69         Some("attempt to divide by zero"),
70         |_old_val| { let _val = 1/0; loop {} },
71     );
72 
73     test(
74         Some("align_offset: align is not a power-of-two"),
75         |_old_val| { (0usize as *const u8).align_offset(3); loop {} },
76     );
77 
78     // Assertion and debug assertion
79     test(None, |_old_val| { assert!(false); loop {} });
80     test(None, |_old_val| { debug_assert!(false); loop {} });
81 
82     eprintln!("Success!"); // Make sure we get this in stderr
83 }
84 
test(expect_msg: Option<&str>, do_panic: impl FnOnce(usize) -> !)85 fn test(expect_msg: Option<&str>, do_panic: impl FnOnce(usize) -> !) {
86     // Reset test flags.
87     DROPPED.with(|c| c.set(false));
88     HOOK_CALLED.with(|c| c.set(false));
89 
90     // Cause and catch a panic.
91     let res = catch_unwind(AssertUnwindSafe(|| {
92         let _string = "LEAKED FROM CLOSURE".to_string();
93         do_panic_counter(do_panic)
94     })).expect_err("do_panic() did not panic!");
95 
96     // See if we can extract the panic message.
97     let msg = if let Some(s) = res.downcast_ref::<String>() {
98         eprintln!("Caught panic message (String): {}", s);
99         Some(s.as_str())
100     } else if let Some(s) = res.downcast_ref::<&str>() {
101         eprintln!("Caught panic message (&str): {}", s);
102         Some(*s)
103     } else {
104         eprintln!("Failed to get caught panic message.");
105         None
106     };
107     if let Some(expect_msg) = expect_msg {
108         assert_eq!(expect_msg, msg.unwrap());
109     }
110 
111     // Test flags.
112     assert!(DROPPED.with(|c| c.get()));
113     assert!(HOOK_CALLED.with(|c| c.get()));
114 }
115