Skip to content

Commit 0de2b26

Browse files
Auto merge of #150497 - saethlin:panic-entrypoints, r=<try>
Implement immediate-abort by hooking #[rustc_panic_entrypoint] and lint for functions that look like a panic entrypoint
2 parents 0e89999 + 2423f20 commit 0de2b26

File tree

38 files changed

+288
-157
lines changed

38 files changed

+288
-157
lines changed

‎compiler/rustc_codegen_cranelift/src/base.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_middle::ty::layout::FnAbiOf;
1414
use rustc_middle::ty::print::with_no_trimmed_paths;
1515
use rustc_session::config::OutputFilenames;
1616
use rustc_span::Symbol;
17+
use rustc_target::spec::PanicStrategy;
1718

1819
use crate::constant::ConstantCx;
1920
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
@@ -384,6 +385,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
384385
fx.bcx.switch_to_block(failure);
385386
fx.bcx.ins().nop();
386387

388+
if fx.tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
389+
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
390+
continue;
391+
}
392+
387393
match &**msg {
388394
AssertKind::BoundsCheck { len, index } => {
389395
let len = codegen_operand(fx, len).load_scalar(fx);
@@ -1052,6 +1058,10 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
10521058
msg_str: &str,
10531059
span: Span,
10541060
) {
1061+
if fx.tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
1062+
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
1063+
}
1064+
10551065
let msg_ptr = crate::constant::pointer_for_anonymous_str(fx, msg_str);
10561066
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
10571067
let args = [msg_ptr, msg_len];

‎compiler/rustc_codegen_ssa/src/codegen_attrs.rs‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_abi::{Align, ExternAbi};
44
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
55
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
66
use rustc_hir::attrs::{
7-
AttributeKind, InlineAttr, InstructionSetAttr, Linkage, RtsanSetting, UsedBy,
7+
AttributeKind, InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr, RtsanSetting, UsedBy,
88
};
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -19,7 +19,7 @@ use rustc_middle::ty::{self as ty, Instance, TyCtxt};
1919
use rustc_session::lint;
2020
use rustc_session::parse::feature_err;
2121
use rustc_span::{Ident, Span, Symbol, sym};
22-
use rustc_target::spec::Os;
22+
use rustc_target::spec::{Os, PanicStrategy};
2323

2424
use crate::errors;
2525
use crate::target_features::{
@@ -472,6 +472,24 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
472472
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
473473
}
474474
}
475+
476+
if tcx.is_panic_entrypoint(did) {
477+
// Panic entrypoints are always cold.
478+
//
479+
// If we have immediate-abort enabled, we want them to be inlined.
480+
// They shouldn't be called, but on the off-chance that they are, they should be inlined.
481+
//
482+
// When the panic strategies that support panic messages are enabled, we want panic
483+
// entrypoints outlined and optimized for size.
484+
// Most panic entrypoints want #[track_caller] but not all, so we do not add it.
485+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
486+
if tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
487+
codegen_fn_attrs.inline = InlineAttr::Always;
488+
} else {
489+
codegen_fn_attrs.inline = InlineAttr::Never;
490+
codegen_fn_attrs.optimize = OptimizeAttr::Size;
491+
}
492+
}
475493
}
476494

477495
fn check_result(

‎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: 99 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,100 @@ 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+
/// # #![cfg(not(bootstrap))]
3198+
/// #[deny(missing_panic_entrypoint)]
3199+
/// pub fn panic_wrapper() -> ! {
3200+
/// panic!("ouch!")
3201+
/// }
3202+
/// ```
3203+
///
3204+
/// {{produces}}
3205+
///
3206+
/// ### Explanation
3207+
///
3208+
/// This lint is intended to ensure that panic=immediate-abort can function as designed,
3209+
/// because it uses #[rustc_panic_entrypoint] to locate functions that should be outlined
3210+
/// for other panic modes, and be deleted entirely when immediate-abort is enabled.
3211+
pub MISSING_PANIC_ENTRYPOINT,
3212+
Allow,
3213+
"detects missing #[rustc_panic_entrypoint]",
3214+
}
3215+
3216+
#[derive(Default)]
3217+
pub struct MissingPanicEntrypoint;
3218+
3219+
impl_lint_pass!(MissingPanicEntrypoint => [MISSING_PANIC_ENTRYPOINT]);
3220+
3221+
fn has_panic_entrypoint(attrs: &[hir::Attribute]) -> bool {
3222+
attrs.iter().any(|attr| {
3223+
if let hir::Attribute::Unparsed(box hir::AttrItem {
3224+
path: AttrPath { segments, .. }, ..
3225+
}) = attr
3226+
{
3227+
if segments[0].name == sym::rustc_panic_entrypoint {
3228+
return true;
3229+
}
3230+
}
3231+
false
3232+
})
3233+
}
3234+
3235+
fn has_inline_encouragement(attrs: &[hir::Attribute]) -> bool {
3236+
attrs.iter().any(|attr| {
3237+
matches!(
3238+
attr,
3239+
hir::Attribute::Parsed(hir::attrs::AttributeKind::Inline(
3240+
InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. },
3241+
_
3242+
))
3243+
)
3244+
})
3245+
}
3246+
3247+
fn has_rustc_intrinsic(attrs: &[hir::Attribute]) -> bool {
3248+
attrs.iter().any(|attr| {
3249+
if let hir::Attribute::Unparsed(box hir::AttrItem {
3250+
path: AttrPath { segments, .. }, ..
3251+
}) = attr
3252+
{
3253+
if segments[0].name == sym::rustc_intrinsic {
3254+
return true;
3255+
}
3256+
}
3257+
false
3258+
})
3259+
}
3260+
3261+
impl<'tcx> LateLintPass<'tcx> for MissingPanicEntrypoint {
3262+
fn check_fn(
3263+
&mut self,
3264+
cx: &LateContext<'tcx>,
3265+
_: HirFnKind<'tcx>,
3266+
fn_decl: &'tcx FnDecl<'tcx>,
3267+
_: &'tcx Body<'tcx>,
3268+
span: Span,
3269+
def_id: LocalDefId,
3270+
) {
3271+
if matches!(fn_decl.output, hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::Never, .. }))
3272+
{
3273+
let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));
3274+
if has_rustc_intrinsic(attrs) {
3275+
return;
3276+
}
3277+
if !has_inline_encouragement(attrs) && !has_panic_entrypoint(attrs) {
3278+
cx.emit_span_lint(
3279+
MISSING_PANIC_ENTRYPOINT,
3280+
span,
3281+
crate::lints::MissingPanicEntrypoint,
3282+
);
3283+
}
3284+
}
3285+
}
3286+
}

‎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_middle/src/ty/context.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3561,6 +3561,10 @@ impl<'tcx> TyCtxt<'tcx> {
35613561
}
35623562
false
35633563
}
3564+
3565+
pub fn is_panic_entrypoint(self, def_id: impl Into<DefId>) -> bool {
3566+
self.has_attr(def_id, sym::rustc_panic_entrypoint)
3567+
}
35643568
}
35653569

35663570
pub fn provide(providers: &mut Providers) {

0 commit comments

Comments
 (0)