Auto merge of #102732 - RalfJung:assert_unsafe_precondition2, r=bjorn3
nicer errors from assert_unsafe_precondition This makes the errors shown by cargo-careful nicer, and since `panic_no_unwind` is `nounwind noreturn` it hopefully doesn't have bad codegen impact. Thanks to `@bjorn3` for the hint! Would be nice if we could somehow supply our own (static) message to print, currently it always prints `panic in a function that cannot unwind`. But still, this is better than before.
This commit is contained in:
commit
538f118da1
8 changed files with 90 additions and 71 deletions
|
@ -536,7 +536,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
|
|
|
@ -1582,13 +1582,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The panic_no_unwind function called by TerminatorKind::Abort will never
|
|
||||||
// unwind. If the panic handler that it invokes unwind then it will simply
|
|
||||||
// call the panic handler again.
|
|
||||||
if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() {
|
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
|
||||||
}
|
|
||||||
|
|
||||||
let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
|
let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
|
||||||
|
|
||||||
let mut inline_span = None;
|
let mut inline_span = None;
|
||||||
|
@ -1649,7 +1642,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
} else if attr.has_name(sym::rustc_allocator_nounwind) {
|
} else if attr.has_name(sym::rustc_nounwind) {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||||
} else if attr.has_name(sym::rustc_reallocator) {
|
} else if attr.has_name(sym::rustc_reallocator) {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
|
||||||
|
|
|
@ -1231,7 +1231,6 @@ symbols! {
|
||||||
rust_oom,
|
rust_oom,
|
||||||
rustc,
|
rustc,
|
||||||
rustc_allocator,
|
rustc_allocator,
|
||||||
rustc_allocator_nounwind,
|
|
||||||
rustc_allocator_zeroed,
|
rustc_allocator_zeroed,
|
||||||
rustc_allow_const_fn_unstable,
|
rustc_allow_const_fn_unstable,
|
||||||
rustc_allow_incoherent_impl,
|
rustc_allow_incoherent_impl,
|
||||||
|
@ -1278,6 +1277,7 @@ symbols! {
|
||||||
rustc_mir,
|
rustc_mir,
|
||||||
rustc_must_implement_one_of,
|
rustc_must_implement_one_of,
|
||||||
rustc_nonnull_optimization_guaranteed,
|
rustc_nonnull_optimization_guaranteed,
|
||||||
|
rustc_nounwind,
|
||||||
rustc_object_lifetime_default,
|
rustc_object_lifetime_default,
|
||||||
rustc_on_unimplemented,
|
rustc_on_unimplemented,
|
||||||
rustc_outlives,
|
rustc_outlives,
|
||||||
|
|
|
@ -28,16 +28,20 @@ extern "Rust" {
|
||||||
// The rustc fork of LLVM 14 and earlier also special-cases these function names to be able to optimize them
|
// The rustc fork of LLVM 14 and earlier also special-cases these function names to be able to optimize them
|
||||||
// like `malloc`, `realloc`, and `free`, respectively.
|
// like `malloc`, `realloc`, and `free`, respectively.
|
||||||
#[rustc_allocator]
|
#[rustc_allocator]
|
||||||
#[rustc_allocator_nounwind]
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
||||||
#[rustc_deallocator]
|
#[rustc_deallocator]
|
||||||
#[rustc_allocator_nounwind]
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
|
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
|
||||||
#[rustc_reallocator]
|
#[rustc_reallocator]
|
||||||
#[rustc_allocator_nounwind]
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
||||||
#[rustc_allocator_zeroed]
|
#[rustc_allocator_zeroed]
|
||||||
#[rustc_allocator_nounwind]
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2210,8 +2210,8 @@ macro_rules! assert_unsafe_precondition {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
|
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
|
||||||
if !$e {
|
if !$e {
|
||||||
// abort instead of panicking to reduce impact on code size
|
// don't unwind to reduce impact on code size
|
||||||
::core::intrinsics::abort();
|
::core::panicking::panic_str_nounwind("unsafe precondition violated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
|
@ -29,6 +29,73 @@
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::panic::{Location, PanicInfo};
|
use crate::panic::{Location, PanicInfo};
|
||||||
|
|
||||||
|
// First we define the two main entry points that all panics go through.
|
||||||
|
// In the end both are just convenience wrappers around `panic_impl`.
|
||||||
|
|
||||||
|
/// The entry point for panicking with a formatted message.
|
||||||
|
///
|
||||||
|
/// This is designed to reduce the amount of code required at the call
|
||||||
|
/// site as much as possible (so that `panic!()` has as low an impact
|
||||||
|
/// on (e.g.) the inlining of other functions as possible), by moving
|
||||||
|
/// the actual formatting into this shared place.
|
||||||
|
#[cold]
|
||||||
|
// If panic_immediate_abort, inline the abort call,
|
||||||
|
// otherwise avoid inlining because of it is cold path.
|
||||||
|
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
||||||
|
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||||
|
#[track_caller]
|
||||||
|
#[lang = "panic_fmt"] // needed for const-evaluated panics
|
||||||
|
#[rustc_do_not_const_check] // hooked by const-eval
|
||||||
|
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
|
||||||
|
pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||||
|
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<'_>) -> !;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like panic_fmt, but without unwinding and track_caller to reduce the impact on codesize.
|
||||||
|
/// Also just works on `str`, as a `fmt::Arguments` needs more space to be passed.
|
||||||
|
#[cold]
|
||||||
|
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
||||||
|
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
|
pub fn panic_str_nounwind(msg: &'static str) -> ! {
|
||||||
|
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 pieces = [msg];
|
||||||
|
let fmt = fmt::Arguments::new_v1(&pieces, &[]);
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we define a bunch of higher-level wrappers that all bottom out in the two core functions
|
||||||
|
// above.
|
||||||
|
|
||||||
/// The underlying implementation of libcore's `panic!` macro when no formatting is used.
|
/// The underlying implementation of libcore's `panic!` macro when no formatting is used.
|
||||||
#[cold]
|
#[cold]
|
||||||
// never inline unless panic_immediate_abort to avoid code
|
// never inline unless panic_immediate_abort to avoid code
|
||||||
|
@ -84,62 +151,17 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
|
||||||
panic!("index out of bounds: the len is {len} but the index is {index}")
|
panic!("index out of bounds: the len is {len} but the index is {index}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called directly by the codegen backend, and must not have
|
/// Panic because we cannot unwind out of a function.
|
||||||
// any extra arguments (including those synthesized by track_caller).
|
///
|
||||||
|
/// This function is called directly by the codegen backend, and must not have
|
||||||
|
/// any extra arguments (including those synthesized by track_caller).
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
|
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_nounwind)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
|
||||||
fn panic_no_unwind() -> ! {
|
fn panic_no_unwind() -> ! {
|
||||||
if cfg!(feature = "panic_immediate_abort") {
|
panic_str_nounwind("panic in a function that cannot unwind")
|
||||||
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
|
|
||||||
/// site as much as possible (so that `panic!()` has as low an impact
|
|
||||||
/// on (e.g.) the inlining of other functions as possible), by moving
|
|
||||||
/// the actual formatting into this shared place.
|
|
||||||
#[cold]
|
|
||||||
// If panic_immediate_abort, inline the abort call,
|
|
||||||
// otherwise avoid inlining because of it is cold path.
|
|
||||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
|
||||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
|
||||||
#[track_caller]
|
|
||||||
#[lang = "panic_fmt"] // needed for const-evaluated panics
|
|
||||||
#[rustc_do_not_const_check] // hooked by const-eval
|
|
||||||
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
|
|
||||||
pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
|
||||||
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<'_>) -> !;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is used instead of panic_fmt in const eval.
|
/// This function is used instead of panic_fmt in const eval.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//@revisions: extern_block definition both
|
//@revisions: extern_block definition both
|
||||||
#![feature(rustc_attrs, c_unwind)]
|
#![feature(rustc_attrs, c_unwind)]
|
||||||
|
|
||||||
#[cfg_attr(any(definition, both), rustc_allocator_nounwind)]
|
#[cfg_attr(any(definition, both), rustc_nounwind)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C-unwind" fn nounwind() {
|
extern "C-unwind" fn nounwind() {
|
||||||
//[definition]~^ ERROR: abnormal termination: the program aborted execution
|
//[definition]~^ ERROR: abnormal termination: the program aborted execution
|
||||||
|
@ -11,7 +11,7 @@ extern "C-unwind" fn nounwind() {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
extern "C-unwind" {
|
extern "C-unwind" {
|
||||||
#[cfg_attr(any(extern_block, both), rustc_allocator_nounwind)]
|
#[cfg_attr(any(extern_block, both), rustc_nounwind)]
|
||||||
fn nounwind();
|
fn nounwind();
|
||||||
}
|
}
|
||||||
unsafe { nounwind() }
|
unsafe { nounwind() }
|
||||||
|
|
|
@ -379,7 +379,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||||
gated!(
|
gated!(
|
||||||
alloc_error_handler, Normal, template!(Word), WarnFollowing,
|
alloc_error_handler, Normal, template!(Word), WarnFollowing,
|
||||||
experimental!(alloc_error_handler)
|
experimental!(alloc_error_handler)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue