//@ run-pass //@ needs-unwind //@ ignore-emscripten no threads support // rust-lang/rust#64655: with panic=unwind, a panic from a subroutine // should still run destructors as it unwinds the stack. However, // bugs with how the nounwind LLVM attribute was applied led to this // simple case being mishandled *if* you had optimization *and* fat // LTO turned on. // This test is the closest thing to a "regression test" we can do // without actually spawning subprocesses and comparing stderr // results. // // This test takes the code from the above issue and adapts it to // better fit our test infrastructure: // // * Instead of relying on `println!` to observe whether the destructor // is run, we instead run the code in a spawned thread and // communicate the destructor's operation via a synchronous atomic // in static memory. // // * To keep the output from confusing a casual user, we override the // panic hook to be a no-op (rather than printing a message to // stderr). // // (pnkfelix has confirmed by hand that these additions do not mask // the underlying bug.) // LTO settings cannot be combined with -C prefer-dynamic //@ no-prefer-dynamic // The revisions combine each lto setting with each optimization // setting; pnkfelix observed three differing behaviors at opt-levels // 0/1/2+3 for this test, so it seems prudent to be thorough. //@ revisions: no0 no1 no2 no3 thin0 thin1 thin2 thin3 fat0 fat1 fat2 fat3 //@[no0]compile-flags: -C opt-level=0 -C lto=no //@[no1]compile-flags: -C opt-level=1 -C lto=no //@[no2]compile-flags: -C opt-level=2 -C lto=no //@[no3]compile-flags: -C opt-level=3 -C lto=no //@[thin0]compile-flags: -C opt-level=0 -C lto=thin //@[thin1]compile-flags: -C opt-level=1 -C lto=thin //@[thin2]compile-flags: -C opt-level=2 -C lto=thin //@[thin3]compile-flags: -C opt-level=3 -C lto=thin //@[fat0]compile-flags: -C opt-level=0 -C lto=fat //@[fat1]compile-flags: -C opt-level=1 -C lto=fat //@[fat2]compile-flags: -C opt-level=2 -C lto=fat //@[fat3]compile-flags: -C opt-level=3 -C lto=fat fn main() { use std::sync::atomic::{AtomicUsize, Ordering}; static SHARED: AtomicUsize = AtomicUsize::new(0); assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 0); let old_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| { } )); // no-op on panic. let handle = std::thread::spawn(|| { struct Droppable; impl Drop for Droppable { fn drop(&mut self) { SHARED.fetch_add(1, Ordering::SeqCst); } } let _guard = Droppable; None::<()>.expect("???"); }); let wait = handle.join(); // reinstate handler to ease observation of assertion failures. std::panic::set_hook(old_hook); assert!(wait.is_err()); assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 1); }