Skip to content

Commit e528318

Browse files
committed
Add rustc_panic_entrypoint
1 parent 7e7280f commit e528318

31 files changed

Lines changed: 274 additions & 97 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_session::config::OptLevel;
1515
use rustc_span::Span;
1616
use rustc_span::source_map::Spanned;
1717
use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode};
18+
use rustc_target::spec::PanicStrategy;
1819
use tracing::{debug, info};
1920

2021
use super::operand::OperandRef;
@@ -730,6 +731,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
730731
bx.switch_to_block(panic_block);
731732
self.set_debug_loc(bx, terminator.source_info);
732733

734+
if bx.tcx().sess.panic_strategy() == PanicStrategy::ImmediateAbort {
735+
bx.abort();
736+
bx.unreachable();
737+
return MergingSucc::False;
738+
}
739+
733740
// Get the location information.
734741
let location = self.get_caller_location(bx, terminator.source_info).immediate();
735742

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
13351335
"`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \
13361336
the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`."
13371337
),
1338+
rustc_attr!(
1339+
rustc_panic_entrypoint, Normal, template!(Word), WarnFollowing,
1340+
EncodeCrossCrate::Yes, "`#[rustc_panic_entrypoint]` makes this function patchable by panic=immediate-abort",
1341+
),
13381342

13391343
BuiltinAttribute {
13401344
name: sym::rustc_diagnostic_item,

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ language_item_table! {
247247
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
248248
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
249249

250+
AbortIntrinsic, sym::abort_intrinsic, abort_intrinsic, Target::Fn, GenericRequirement::Exact(0);
250251
AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
251252
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
252253
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);

compiler/rustc_lint/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,8 @@ lint_mismatched_lifetime_syntaxes_suggestion_mixed =
599599
lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths =
600600
use `'_` for type paths
601601
602+
lint_missing_panic_entrypoint = "diverging functions should usually have #[inline] or #[rustc_panic_entrypoint]"
603+
602604
lint_mixed_script_confusables =
603605
the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables
604606
.includes_note = the usage includes {$includes}

compiler/rustc_lint/src/builtin.rs

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ use rustc_attr_parsing::AttributeParser;
2525
use rustc_errors::{Applicability, LintDiagnostic};
2626
use rustc_feature::GateIssue;
2727
use rustc_hir as hir;
28-
use rustc_hir::attrs::{AttributeKind, DocAttribute};
28+
use rustc_hir::attrs::{AttributeKind, DocAttribute, InlineAttr};
2929
use rustc_hir::def::{DefKind, Res};
3030
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
3131
use rustc_hir::intravisit::FnKind as HirFnKind;
32-
use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
32+
use rustc_hir::{AttrPath, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
3333
use rustc_middle::bug;
3434
use rustc_middle::lint::LevelAndSource;
3535
use rustc_middle::ty::layout::LayoutOf;
@@ -3187,3 +3187,97 @@ impl EarlyLintPass for SpecialModuleName {
31873187
}
31883188
}
31893189
}
3190+
3191+
declare_lint! {
3192+
/// The `missing_panic_entrypoint` lint detects forgotten use of #[rustc_panic_entrypoint]
3193+
///
3194+
/// ### Example
3195+
///
3196+
/// ```rust,compile_fail
3197+
/// #[deny(missing_panic_entrypoint)]
3198+
/// pub fn panic() -> ! {}
3199+
/// ```
3200+
///
3201+
/// {{produces}}
3202+
///
3203+
/// ### Explanation
3204+
///
3205+
/// This lint is intended to ensure that panic=immediate-abort can function as designed,
3206+
/// because it uses #[rustc_panic_entrypoint] to locate functions that should be outlined
3207+
/// for other panic modes, and be deleted entirely when immediate-abort is enabled.
3208+
pub MISSING_PANIC_ENTRYPOINT,
3209+
Allow,
3210+
"detects missing #[rustc_panic_entrypoint]",
3211+
}
3212+
3213+
#[derive(Default)]
3214+
pub struct MissingPanicEntrypoint;
3215+
3216+
impl_lint_pass!(MissingPanicEntrypoint => [MISSING_PANIC_ENTRYPOINT]);
3217+
3218+
fn has_panic_entrypoint(attrs: &[hir::Attribute]) -> bool {
3219+
attrs.iter().any(|attr| {
3220+
if let hir::Attribute::Unparsed(box hir::AttrItem {
3221+
path: AttrPath { segments, .. }, ..
3222+
}) = attr
3223+
{
3224+
if segments[0].name == sym::rustc_panic_entrypoint {
3225+
return true;
3226+
}
3227+
}
3228+
false
3229+
})
3230+
}
3231+
3232+
fn has_inline_encouragement(attrs: &[hir::Attribute]) -> bool {
3233+
attrs.iter().any(|attr| {
3234+
matches!(
3235+
attr,
3236+
hir::Attribute::Parsed(hir::attrs::AttributeKind::Inline(
3237+
InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. },
3238+
_
3239+
))
3240+
)
3241+
})
3242+
}
3243+
3244+
fn has_rustc_intrinsic(attrs: &[hir::Attribute]) -> bool {
3245+
attrs.iter().any(|attr| {
3246+
if let hir::Attribute::Unparsed(box hir::AttrItem {
3247+
path: AttrPath { segments, .. }, ..
3248+
}) = attr
3249+
{
3250+
if segments[0].name == sym::rustc_intrinsic {
3251+
return true;
3252+
}
3253+
}
3254+
false
3255+
})
3256+
}
3257+
3258+
impl<'tcx> LateLintPass<'tcx> for MissingPanicEntrypoint {
3259+
fn check_fn(
3260+
&mut self,
3261+
cx: &LateContext<'tcx>,
3262+
_: HirFnKind<'tcx>,
3263+
fn_decl: &'tcx FnDecl<'tcx>,
3264+
_: &'tcx Body<'tcx>,
3265+
span: Span,
3266+
def_id: LocalDefId,
3267+
) {
3268+
if matches!(fn_decl.output, hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::Never, .. }))
3269+
{
3270+
let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));
3271+
if has_rustc_intrinsic(attrs) {
3272+
return;
3273+
}
3274+
if !has_inline_encouragement(attrs) && !has_panic_entrypoint(attrs) {
3275+
cx.emit_span_lint(
3276+
MISSING_PANIC_ENTRYPOINT,
3277+
span,
3278+
crate::lints::MissingPanicEntrypoint,
3279+
);
3280+
}
3281+
}
3282+
}
3283+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ late_lint_methods!(
238238
MapUnitFn: MapUnitFn,
239239
MissingDebugImplementations: MissingDebugImplementations,
240240
MissingDoc: MissingDoc,
241+
MissingPanicEntrypoint: MissingPanicEntrypoint,
241242
AsyncClosureUsage: AsyncClosureUsage,
242243
AsyncFnInTrait: AsyncFnInTrait,
243244
NonLocalDefinitions: NonLocalDefinitions::default(),

compiler/rustc_lint/src/lints.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,3 +3299,7 @@ pub(crate) struct DocTestUnknown {
32993299
#[derive(LintDiagnostic)]
33003300
#[diag(lint_doc_test_literal)]
33013301
pub(crate) struct DocTestLiteral;
3302+
3303+
#[derive(LintDiagnostic)]
3304+
#[diag(lint_missing_panic_entrypoint)]
3305+
pub(crate) struct MissingPanicEntrypoint;

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,52 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
508508
body
509509
}
510510

511+
fn remap_mir_for_panic_immediate_abort<'tcx>(
512+
tcx: TyCtxt<'tcx>,
513+
mut body: Body<'tcx>,
514+
) -> Body<'tcx> {
515+
use rustc_middle::mir::{Const, ConstValue, UnwindAction};
516+
use rustc_middle::ty::Ty;
517+
use rustc_target::spec::PanicStrategy;
518+
519+
if tcx.sess.panic_strategy() != PanicStrategy::ImmediateAbort {
520+
return body;
521+
}
522+
523+
let make_abort_intrinsic_operand = || {
524+
let abort_intrin = tcx.lang_items().abort_intrinsic().unwrap();
525+
let no_args: [ty::GenericArg<'_>; 0] = [];
526+
Operand::Constant(Box::new(ConstOperand {
527+
span: DUMMY_SP,
528+
user_ty: None,
529+
const_: Const::Val(ConstValue::ZeroSized, Ty::new_fn_def(tcx, abort_intrin, no_args)),
530+
}))
531+
};
532+
533+
for bb in body.basic_blocks.as_mut().iter_mut() {
534+
let terminator = bb.terminator.as_mut().expect("invalid terminator");
535+
let TerminatorKind::Call { func, args, unwind, .. } = &mut terminator.kind else {
536+
continue;
537+
};
538+
let Operand::Constant(box ConstOperand { const_, .. }) = &func else {
539+
continue;
540+
};
541+
let ty::FnDef(def_id, _) = *const_.ty().kind() else {
542+
continue;
543+
};
544+
if !tcx.has_attr(def_id, sym::rustc_panic_entrypoint) {
545+
continue;
546+
}
547+
548+
debug!("Remapping call from {:?} to {:?}", body.source, def_id);
549+
550+
*func = make_abort_intrinsic_operand();
551+
*args = Box::new([]);
552+
*unwind = UnwindAction::Unreachable;
553+
}
554+
body
555+
}
556+
511557
/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
512558
/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
513559
/// end up missing the source MIR due to stealing happening.
@@ -796,7 +842,8 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
796842
}
797843
debug!("about to call mir_drops_elaborated...");
798844
let body = tcx.mir_drops_elaborated_and_const_checked(did).steal();
799-
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
845+
let body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
846+
let mut body = remap_mir_for_panic_immediate_abort(tcx, body);
800847

801848
if body.tainted_by_errors.is_some() {
802849
return body;

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ symbols! {
424424
abi_vectorcall,
425425
abi_x86_interrupt,
426426
abort,
427+
abort_intrinsic,
427428
add,
428429
add_assign,
429430
add_with_overflow,
@@ -1981,6 +1982,7 @@ symbols! {
19811982
rustc_offload_kernel,
19821983
rustc_on_unimplemented,
19831984
rustc_outlives,
1985+
rustc_panic_entrypoint,
19841986
rustc_paren_sugar,
19851987
rustc_partition_codegened,
19861988
rustc_partition_reused,

library/alloc/src/alloc.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,9 @@ unsafe extern "Rust" {
396396
#[cfg(not(no_global_oom_handling))]
397397
#[cold]
398398
#[optimize(size)]
399+
#[rustc_panic_entrypoint]
399400
pub const fn handle_alloc_error(layout: Layout) -> ! {
401+
#[inline]
400402
const fn ct_error(_: Layout) -> ! {
401403
panic!("allocation failed");
402404
}
@@ -408,13 +410,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! {
408410
}
409411
}
410412

411-
#[cfg(not(panic = "immediate-abort"))]
412-
{
413-
core::intrinsics::const_eval_select((layout,), ct_error, rt_error)
414-
}
415-
416-
#[cfg(panic = "immediate-abort")]
417-
ct_error(layout)
413+
core::intrinsics::const_eval_select((layout,), ct_error, rt_error)
418414
}
419415

420416
#[cfg(not(no_global_oom_handling))]
@@ -425,6 +421,7 @@ pub mod __alloc_error_handler {
425421
// called via generated `__rust_alloc_error_handler` if there is no
426422
// `#[alloc_error_handler]`.
427423
#[rustc_std_internal_symbol]
424+
#[allow(missing_panic_entrypoint)]
428425
pub unsafe fn __rdl_alloc_error_handler(size: usize, _align: usize) -> ! {
429426
core::panicking::panic_nounwind_fmt(
430427
format_args!("memory allocation of {size} bytes failed"),

0 commit comments

Comments
 (0)