diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 60fc0b8f9b214..dbc17318bc4f2 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -6,6 +6,7 @@ use rustc_hir::def::DefKind; use rustc_hir_analysis::check::check_function_signature; use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::traits::WellFormedLoc; +use rustc_lint_defs::builtin::UNSAFE_PANIC_HANDLERS; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::sym; @@ -13,6 +14,7 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument}; use crate::coercion::CoerceMany; +use crate::diagnostics::UnsafePanicHandlers; use crate::gather_locals::GatherLocalsVisitor; use crate::{CoroutineTypes, Diverges, FnCtxt}; @@ -177,6 +179,21 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> tcx.dcx().span_err(span, "should have no const parameters"); } + if fn_sig.safety().is_unsafe() { + let hir_id = tcx.local_def_id_to_hir_id(fn_id); + let span = tcx.def_span(fn_id); + // If the function is implicitly "unsafe" because it has a `#[target_feature]` attribute, + // then we should already have emitted an error. Don't also emit a warning. + // See `tests/ui/panic-handler/panic-handler-with-target-feature.rs` + if let Some(hir_fn_sig) = tcx.hir_fn_sig_by_hir_id(hir_id) + && hir_fn_sig.header.safety == hir::HeaderSafety::SafeTargetFeatures + { + tcx.dcx().span_delayed_bug(span, "`unsafe_panic_handlers` lint suppressed because there should already be an error for `#[target_feature]`"); + } else { + tcx.emit_node_span_lint(UNSAFE_PANIC_HANDLERS, hir_id, span, UnsafePanicHandlers); + } + } + let panic_info_did = tcx.require_lang_item(LangItem::PanicInfo, span); // build type `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !` diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index 3b7de7790ac02..50e238fffb6c5 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -1337,3 +1337,7 @@ impl ExprParenthesesNeeded { ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } } } + +#[derive(Diagnostic)] +#[diag("`#[panic_handler]` functions can't be `unsafe`")] +pub(crate) struct UnsafePanicHandlers; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2a0901e0243b6..1094bdefcd163 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5858,3 +5858,42 @@ declare_lint! { "`repr(C, align)` types nested inside `repr(C, packed)` types \ do not always have a C-compatible layout", } + +declare_lint! { + /// The `unsafe_panic_handlers` lint detects unsafe functions with + /// the `#[panic_handler]` attribute. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![no_std] + /// + /// use core::panic::PanicInfo; + /// + /// #[panic_handler] + /// unsafe fn handle(_: &PanicInfo<'_>) -> ! { + /// loop {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unsafe functions (declared as `unsafe fn`) are functions that can only be called + /// when the caller ensures that their safety requirements are met. On the other hand, + /// `#[panic_handler]` functions get called automatically by the compiler without + /// checking for any preconditions. Therefore, using the `#[panic_handler]` attribute + /// on unsafe functions is either incorrect or a misuse of `unsafe fn`. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #163263] for more details. + /// + /// [issue #163263]: https://github.com/rust-lang/rust/issues/163263 + pub UNSAFE_PANIC_HANDLERS, + Warn, + "detects unsafe functions with the `#[panic_handler]` attribute", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #163263), + }; +} diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.rs b/tests/ui/codegen/no-mangle-on-panic-handler.rs index 1dc0cce0a2ece..4ef9fd2b8d372 100644 --- a/tests/ui/codegen/no-mangle-on-panic-handler.rs +++ b/tests/ui/codegen/no-mangle-on-panic-handler.rs @@ -9,6 +9,6 @@ use core::panic::PanicInfo; #[unsafe(no_mangle)] //~ ERROR `#[no_mangle]` cannot be used on internal language items #[panic_handler] -pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! { +pub fn panic_fmt(pi: &PanicInfo) -> ! { loop {} } diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.stderr b/tests/ui/codegen/no-mangle-on-panic-handler.stderr index dc88b66d1b5d7..46cf8e8aec5e9 100644 --- a/tests/ui/codegen/no-mangle-on-panic-handler.stderr +++ b/tests/ui/codegen/no-mangle-on-panic-handler.stderr @@ -4,8 +4,8 @@ error: `#[no_mangle]` cannot be used on internal language items LL | #[unsafe(no_mangle)] | ^^^^^^^^^^^^^^^^^^^^ LL | #[panic_handler] -LL | pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! { - | -------------------------------------------- should be the internal language item +LL | pub fn panic_fmt(pi: &PanicInfo) -> ! { + | ------------------------------------- should be the internal language item | = note: Rustc requires this item to have a specific mangled name. = note: If you are trying to prevent mangling to ease debugging, many diff --git a/tests/ui/panic-handler/unsafe_panic_handler.rs b/tests/ui/panic-handler/unsafe_panic_handler.rs new file mode 100644 index 0000000000000..95ecad168d42d --- /dev/null +++ b/tests/ui/panic-handler/unsafe_panic_handler.rs @@ -0,0 +1,12 @@ +//@ check-pass +#![crate_type = "lib"] +#![no_std] + +use core::panic::PanicInfo; + +#[panic_handler] +unsafe fn handle(_: &PanicInfo) -> ! { + //~^ WARN `#[panic_handler]` functions can't be `unsafe` + //~| WARN this was previously accepted by the compiler but is being phased out + loop {} +} diff --git a/tests/ui/panic-handler/unsafe_panic_handler.stderr b/tests/ui/panic-handler/unsafe_panic_handler.stderr new file mode 100644 index 0000000000000..7111d313b6148 --- /dev/null +++ b/tests/ui/panic-handler/unsafe_panic_handler.stderr @@ -0,0 +1,12 @@ +warning: `#[panic_handler]` functions can't be `unsafe` + --> $DIR/unsafe_panic_handler.rs:8:1 + | +LL | unsafe fn handle(_: &PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #163263 + = note: `#[warn(unsafe_panic_handlers)]` on by default + +warning: 1 warning emitted +