use core::{
any::Any,
fmt, intrinsics,
mem::{self, ManuallyDrop},
panic::{Location, PanicPayload},
};
use alloc::{boxed::Box, string::String};
use crate::process;
macro_rules! rtprintpanic {
($($t:tt)*) => {
#[cfg(not(panic = "immediate-abort"))] {
print(format_args!($($t)*));
}
#[cfg(panic = "immediate-abort")]
{
let _ = format_args!($($t)*);
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
print_and_die(format_args!("fatal runtime error: {}, aborting\n", format_args!($($t)*)));
}
}
}
#[cfg(not(feature = "std"))]
fn print(args: core::fmt::Arguments) {
use io_core::io::Write;
cfg_select! {
not(panic = "immediate-abort") => {
if cfg!(pbp) {
crate::io::printing::_dprint(args);
}
if let Some(mut out) = crate::os::stdio::panic_output() {
let _ = out.write_fmt(args);
}
},
_ => {}
}
}
#[cfg(not(feature = "std"))]
fn print_and_die(args: core::fmt::Arguments) -> ! {
print(args);
crate::process::abort();
}
#[inline(never)]
#[cfg(all(not(feature = "std"), panic = "immediate-abort"))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
crate::process::abort()
}
#[allow(improper_ctypes)]
unsafe extern "C" {
#[rustc_std_internal_symbol]
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
unsafe extern "Rust" {
#[rustc_std_internal_symbol]
fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
}
#[cfg(not(test))]
#[cfg(not(feature = "std"))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown")
}
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions")
}
#[doc(hidden)]
#[cfg(not(test))]
#[cfg(panic = "immediate-abort")]
pub mod panic_count {
#[derive(Debug)]
pub enum MustAbort {
AlwaysAbort,
PanicInHook,
}
#[inline]
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
None
}
#[inline]
pub fn finished_panic_hook() {}
#[inline]
pub fn decrease() {}
#[inline]
pub fn set_always_abort() {}
#[inline]
#[must_use]
pub fn get_count() -> usize {
0
}
#[must_use]
#[inline]
pub fn count_is_zero() -> bool {
true
}
}
#[doc(hidden)]
#[cfg(not(test))]
#[cfg(not(panic = "immediate-abort"))]
pub mod panic_count {
use core::sync::atomic::{AtomicUsize, Ordering};
const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
#[derive(Debug)]
pub enum MustAbort {
AlwaysAbort,
PanicInHook,
}
static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
#[must_use = "MustAbort may not be ignored"]
pub fn increase(_run_panic_hook: bool) -> Option<MustAbort> {
let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
if global_count & ALWAYS_ABORT_FLAG != 0 {
return Some(MustAbort::AlwaysAbort);
}
None
}
pub fn finished_panic_hook() {
}
pub fn decrease() {
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
}
pub fn set_always_abort() {
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
}
#[must_use]
pub fn get_count() -> usize {
GLOBAL_PANIC_COUNT.load(Ordering::Relaxed)
}
#[must_use]
#[inline]
pub fn count_is_zero() -> bool {
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
true
} else {
is_zero_slow_path()
}
}
#[cold]
#[inline(never)]
fn is_zero_slow_path() -> bool {
GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) == 0
}
}
#[cfg(all(feature = "std", not(target_os = "psp")))]
pub use std::panic::catch_unwind;
#[cfg(not(feature = "std"))]
#[cfg(panic = "immediate-abort")]
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
Ok(f())
}
#[cfg(not(feature = "std"))]
#[cfg(not(panic = "immediate-abort"))]
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
union Data<F, R> {
f: ManuallyDrop<F>,
r: ManuallyDrop<R>,
p: ManuallyDrop<Box<dyn Any + Send>>,
}
let mut data = Data {
f: ManuallyDrop::new(f),
};
unsafe {
return if intrinsics::catch_unwind(do_call, &raw mut data, do_catch) {
Err(ManuallyDrop::into_inner(data.p))
} else {
Ok(ManuallyDrop::into_inner(data.r))
};
}
#[cold]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
panic_count::decrease();
obj
}
#[inline]
unsafe fn do_call<F: FnOnce() -> R, R>(data: *mut Data<F, R>) {
unsafe {
let f = ManuallyDrop::take(&mut (*data).f);
(*data).r = ManuallyDrop::new(f());
}
}
#[inline]
#[rustc_nounwind] unsafe fn do_catch<F: FnOnce() -> R, R>(data: *mut Data<F, R>, payload: *mut u8) {
unsafe {
let obj = cleanup(payload);
(*data).p = ManuallyDrop::new(obj);
}
}
}
#[inline]
pub fn panicking() -> bool {
!panic_count::count_is_zero()
}
#[cfg(not(feature = "std"))]
#[cfg(not(any(test, doctest)))]
#[panic_handler]
pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
struct FormatStringPayload<'a> {
inner: &'a core::panic::PanicMessage<'a>,
string: Option<String>,
}
impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String {
let inner = self.inner;
self.string.get_or_insert_with(|| alloc::format!("{inner}"))
}
}
unsafe impl PanicPayload for FormatStringPayload<'_> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
let contents = mem::take(self.fill());
Box::into_raw(Box::new(contents))
}
fn get(&mut self) -> &(dyn Any + Send) {
self.fill()
}
}
impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = &self.string {
f.write_str(s)
} else {
fmt::Display::fmt(&self.inner, f)
}
}
}
struct StaticStrPayload(&'static str);
unsafe impl PanicPayload for StaticStrPayload {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(Box::new(self.0))
}
fn get(&mut self) -> &(dyn Any + Send) {
&self.0
}
fn as_str(&mut self) -> Option<&str> {
Some(self.0)
}
}
impl fmt::Display for StaticStrPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
let loc = info.location().unwrap(); let msg = info.message();
if let Some(s) = msg.as_str() {
panic_with_hook(
&mut StaticStrPayload(s),
loc,
true, false, );
} else {
panic_with_hook(
&mut FormatStringPayload {
inner: &msg,
string: None,
},
loc,
true, false, );
}
}
#[cfg_attr(not(any(test, doctest)), lang = "begin_panic")]
#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)]
#[cfg_attr(panic = "immediate-abort", inline)]
#[track_caller]
pub fn begin_panic<M: Any + Send>(msg: M) -> ! {
if cfg!(panic = "immediate-abort") {
process::abort()
}
struct Payload<A> {
inner: Option<A>,
}
unsafe impl<A: Send + 'static> PanicPayload for Payload<A> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
let data = match self.inner.take() {
Some(a) => Box::new(a) as Box<dyn Any + Send>,
None => process::abort(),
};
Box::into_raw(data)
}
fn get(&mut self) -> &(dyn Any + Send) {
match self.inner {
Some(ref a) => a,
None => process::abort(),
}
}
}
impl<A: 'static> fmt::Display for Payload<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
Some(a) => f.write_str(payload_as_str(a)),
None => process::abort(),
}
}
}
let loc = Location::caller();
panic_with_hook(
&mut Payload { inner: Some(msg) },
loc,
true,
false,
)
}
fn payload_as_str(payload: &dyn Any) -> &str {
if let Some(&s) = payload.downcast_ref::<&'static str>() {
s
} else if let Some(s) = payload.downcast_ref::<String>() {
s.as_str()
} else {
"Box<dyn Any>"
}
}
fn panic_with_hook(
payload: &mut dyn PanicPayload, location: &'static Location<'static>, can_unwind: bool,
_force_no_backtrace: bool,
) -> ! {
let must_abort = panic_count::increase(true);
if let Some(must_abort) = must_abort {
match must_abort {
panic_count::MustAbort::PanicInHook => {
let message: &str = payload.as_str().unwrap_or_default();
rtprintpanic!(
"panicked at {location}:\n{message}\nthread panicked while processing panic. \
aborting.\n"
);
},
panic_count::MustAbort::AlwaysAbort => {
rtprintpanic!("aborting due to panic at {location}:\n{payload}\n");
},
}
crate::process::abort();
}
panic_count::finished_panic_hook();
if !can_unwind {
rtprintpanic!("thread caused non-unwinding panic. aborting.\n");
crate::process::abort();
}
let panics = panic_count::get_count();
if panics > 1 {
rtabort!("thread panicked while processing panic. aborting.")
}
rust_panic(payload)
}
#[cfg_attr(panic = "immediate-abort", inline)]
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
if let Some(must_abort) = panic_count::increase(false) {
match must_abort {
panic_count::MustAbort::PanicInHook => {
rtprintpanic!("thread panicked while processing panic. aborting.\n");
},
panic_count::MustAbort::AlwaysAbort => {
rtprintpanic!("aborting due to panic\n");
},
}
crate::process::abort();
}
struct RewrapBox(Box<dyn Any + Send>);
unsafe impl PanicPayload for RewrapBox {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(mem::replace(&mut self.0, Box::new(())))
}
fn get(&mut self) -> &(dyn Any + Send) {
&*self.0
}
}
impl fmt::Display for RewrapBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(payload_as_str(&self.0))
}
}
rust_panic(&mut RewrapBox(payload))
}
#[inline(never)]
#[cfg_attr(not(test), rustc_std_internal_symbol)]
#[cfg(not(panic = "immediate-abort"))]
#[cfg(not(feature = "std"))]
fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {code}")
}
#[cfg_attr(not(test), rustc_std_internal_symbol)]
#[cfg(panic = "immediate-abort")]
#[cfg(not(feature = "std"))]
fn rust_panic(_: &mut dyn PanicPayload) -> ! {
crate::process::abort();
}
pub(crate) struct AssertUnwindSafe<T>(pub T);
impl<T> core::panic::UnwindSafe for AssertUnwindSafe<T> {}
#[cfg(not(feature = "std"))]
#[lang = "eh_personality"]
unsafe extern "C" fn rust_eh_personality() {}
#[cfg_attr(panic = "unwind", link(name = "unwind", kind = "static"))]
unsafe extern "C" {}
#[cfg(all(target_os = "psp", feature = "non-stub-code", not(panic = "immediate-abort")))]
mod libunwind_shims {
#[unsafe(no_mangle)]
unsafe extern "C" fn fprintf(_stream: *const u8, _format: *const u8, _: ...) -> isize {
-1
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fflush(_stream: *const u8) -> i32 {
-1
}
#[unsafe(no_mangle)]
#[allow(deprecated)]
unsafe extern "C" fn abort() {
crate::process::abort()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
use alloc::alloc::{alloc, Layout};
let size = size + 4;
unsafe {
let data = alloc(Layout::from_size_align_unchecked(size, 4));
*(data as *mut usize) = size;
data.offset(4)
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn free(data: *mut u8) {
use alloc::alloc::{dealloc, Layout};
unsafe {
let base = data.sub(4);
let size = *(base as *mut usize);
dealloc(base, Layout::from_size_align_unchecked(size, 4));
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn getenv(_name: *const u8) -> *const u8 {
core::ptr::null()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn __assert_func(_: *const u8, _: i32, _: *const u8, _: *const u8) {}
#[unsafe(no_mangle)]
static _impure_ptr: [usize; 0] = [];
}