diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5ce4e606fd20b..b1a76b80002db 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -477,6 +477,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup); } + fn codegen_abort_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + ) { + let span = terminator.source_info.span; + self.set_debug_loc(&mut bx, terminator.source_info); + + // Get the location information. + let location = self.get_caller_location(&mut bx, terminator.source_info).immediate(); + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); + let llfn = bx.get_fn_addr(instance); + + // Codegen the actual panic invoke/call. + helper.do_call(self, &mut bx, fn_abi, llfn, &[location], None, None); + } + /// Returns `true` if this is indeed a panic intrinsic and codegen is done. fn codegen_panic_intrinsic( &mut self, @@ -1014,10 +1036,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::TerminatorKind::Resume => self.codegen_resume_terminator(helper, bx), mir::TerminatorKind::Abort => { - bx.abort(); - // `abort` does not terminate the block, so we still need to generate - // an `unreachable` terminator after it. - bx.unreachable(); + self.codegen_abort_terminator(helper, bx, terminator); } mir::TerminatorKind::Goto { target } => { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index def0c1d06871b..be4849d0b846e 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -283,6 +283,7 @@ language_item_table! { PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; + PanicNoUnwind, sym::panic_no_unwind, panic_no_unwind, Target::Fn, GenericRequirement::Exact(0); /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 7e7f693870656..7f13da5d38f87 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -807,10 +807,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output.push(create_fn_mono_item(tcx, instance, source)); } } + mir::TerminatorKind::Abort { .. } => { + let instance = Instance::mono( + tcx, + tcx.require_lang_item(LangItem::PanicNoUnwind, Some(source)), + ); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(tcx, instance, source)); + } + } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Resume - | mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::GeneratorDrop diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 702e359466079..9870c90f2ec94 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -990,6 +990,7 @@ symbols! { panic_implementation, panic_info, panic_location, + panic_no_unwind, panic_runtime, panic_str, panic_unwind, diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index d8e421df5de5d..405224f8fb0b0 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -31,6 +31,7 @@ pub struct PanicInfo<'a> { payload: &'a (dyn Any + Send), message: Option<&'a fmt::Arguments<'a>>, location: &'a Location<'a>, + can_unwind: bool, } impl<'a> PanicInfo<'a> { @@ -44,9 +45,10 @@ impl<'a> PanicInfo<'a> { pub fn internal_constructor( message: Option<&'a fmt::Arguments<'a>>, location: &'a Location<'a>, + can_unwind: bool, ) -> Self { struct NoPayload; - PanicInfo { location, message, payload: &NoPayload } + PanicInfo { location, message, payload: &NoPayload, can_unwind } } #[unstable( @@ -127,6 +129,18 @@ impl<'a> PanicInfo<'a> { // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. Some(&self.location) } + + /// Returns whether the panic handler is allowed to unwind the stack from + /// the point where the panic occurred. + /// + /// This is true for most kinds of panics with the exception of panics + /// caused by trying to unwind out of a `Drop` implementation or a function + /// whose ABI does not support unwinding. + #[must_use] + #[unstable(feature = "panic_can_unwind", issue = "92988")] + pub fn can_unwind(&self) -> bool { + self.can_unwind + } } #[stable(feature = "panic_hook_display", since = "1.26.0")] diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index ccb82cda54ef7..5078eea07a15c 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -77,6 +77,31 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { panic!("index out of bounds: the len is {} but the index is {}", len, index) } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function +fn panic_no_unwind() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + // that gets resolved to the `#[panic_handler]` function. + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + // PanicInfo with the `can_unwind` flag set to false forces an abort. + let fmt = format_args!("panic in a function that cannot unwind"); + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false); + + // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. + unsafe { panic_impl(&pi) } +} + /// The entry point for panicking with a formatted message. /// /// This is designed to reduce the amount of code required at the call @@ -104,7 +129,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { fn panic_impl(pi: &PanicInfo<'_>) -> !; } - let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller()); + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true); // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. unsafe { panic_impl(&pi) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 17fe0011569b0..489d362be4276 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -311,6 +311,7 @@ #![feature(once_cell)] #![feature(panic_info_message)] #![feature(panic_internals)] +#![feature(panic_can_unwind)] #![feature(panic_unwind)] #![feature(pin_static_ref)] #![feature(portable_simd)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index d0f332fe5e810..83ab13c963dc5 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -576,9 +576,14 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { if let Some(msg) = msg.as_str() { - rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind()); } else { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + rust_panic_with_hook( + &mut PanicPayload::new(msg), + info.message(), + loc, + info.can_unwind(), + ); } }) } @@ -602,7 +607,7 @@ pub const fn begin_panic(msg: M) -> ! { let loc = Location::caller(); return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc) + rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true) }); struct PanicPayload { @@ -647,6 +652,7 @@ fn rust_panic_with_hook( payload: &mut dyn BoxMeUp, message: Option<&fmt::Arguments<'_>>, location: &Location<'_>, + can_unwind: bool, ) -> ! { let (must_abort, panics) = panic_count::increase(); @@ -663,14 +669,14 @@ fn rust_panic_with_hook( } else { // Unfortunately, this does not print a backtrace, because creating // a `Backtrace` will allocate, which we must to avoid here. - let panicinfo = PanicInfo::internal_constructor(message, location); + let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind); rtprintpanic!("{}\npanicked after panic::always_abort(), aborting.\n", panicinfo); } - intrinsics::abort() + crate::sys::abort_internal(); } unsafe { - let mut info = PanicInfo::internal_constructor(message, location); + let mut info = PanicInfo::internal_constructor(message, location, can_unwind); let _guard = HOOK_LOCK.read(); match HOOK { // Some platforms (like wasm) know that printing to stderr won't ever actually @@ -691,13 +697,13 @@ fn rust_panic_with_hook( }; } - if panics > 1 { + if panics > 1 || !can_unwind { // If a thread panics while it's already unwinding then we // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the thread cleanly. rtprintpanic!("thread panicked while panicking. aborting.\n"); - intrinsics::abort() + crate::sys::abort_internal(); } rust_panic(payload) diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index 157debf2d257b..560c62155d931 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -53,5 +53,10 @@ fn test_command_fork_no_unwind() { let status = got.expect("panic unexpectedly propagated"); dbg!(status); let signal = status.signal().expect("expected child process to die of signal"); - assert!(signal == libc::SIGABRT || signal == libc::SIGILL || signal == libc::SIGTRAP); + assert!( + signal == libc::SIGABRT + || signal == libc::SIGILL + || signal == libc::SIGTRAP + || signal == libc::SIGSEGV + ); } diff --git a/src/test/codegen/unwind-and-panic-abort.rs b/src/test/codegen/unwind-and-panic-abort.rs index 05d97f3256aca..f238741e599f6 100644 --- a/src/test/codegen/unwind-and-panic-abort.rs +++ b/src/test/codegen/unwind-and-panic-abort.rs @@ -9,7 +9,7 @@ extern "C-unwind" { // CHECK: Function Attrs:{{.*}}nounwind // CHECK-NEXT: define{{.*}}void @foo -// CHECK: call void @llvm.trap() +// CHECK: call void @_ZN4core9panicking15panic_no_unwind #[no_mangle] pub unsafe extern "C" fn foo() { bar();