Skip to content

Commit 9529a5d

Browse files
committed
Auto merge of #110303 - nbdd0121:master, r=Mark-Simulacrum
Add `debug_assert_nounwind` and convert `assert_unsafe_precondition` `assert_unsafe_precondition` checks non-CTFE-evaluable conditions in runtime and performs no-op in compile time, while many of its current usage can be checked during const eval.
2 parents 5c97719 + 2c03f21 commit 9529a5d

19 files changed

+391
-389
lines changed

library/core/src/num/nonzero.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ macro_rules! nonzero_integers {
7575
#[must_use]
7676
#[inline]
7777
pub const unsafe fn new_unchecked(n: $Int) -> Self {
78+
crate::panic::debug_assert_nounwind!(
79+
n != 0,
80+
concat!(stringify!($Ty), "::new_unchecked requires a non-zero argument")
81+
);
7882
// SAFETY: this is guaranteed to be safe by the caller.
7983
unsafe {
80-
core::intrinsics::assert_unsafe_precondition!(
81-
concat!(stringify!($Ty), "::new_unchecked requires a non-zero argument"),
82-
(n: $Int) => n != 0
83-
);
8484
Self(n)
8585
}
8686
}

library/core/src/ops/index_range.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub};
1+
use crate::intrinsics::{unchecked_add, unchecked_sub};
22
use crate::iter::{FusedIterator, TrustedLen};
33
use crate::num::NonZeroUsize;
44

@@ -19,13 +19,10 @@ impl IndexRange {
1919
/// - `start <= end`
2020
#[inline]
2121
pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
22-
// SAFETY: comparisons on usize are pure
23-
unsafe {
24-
assert_unsafe_precondition!(
25-
"IndexRange::new_unchecked requires `start <= end`",
26-
(start: usize, end: usize) => start <= end
27-
)
28-
};
22+
crate::panic::debug_assert_nounwind!(
23+
start <= end,
24+
"IndexRange::new_unchecked requires `start <= end`"
25+
);
2926
IndexRange { start, end }
3027
}
3128

library/core/src/panic.rs

+26
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,32 @@ pub macro unreachable_2021 {
139139
),
140140
}
141141

142+
/// Asserts that a boolean expression is `true`, and perform a non-unwinding panic otherwise.
143+
///
144+
/// This macro is similar to `debug_assert!`, but is intended to be used in code that should not
145+
/// unwind. For example, checks in `_unchecked` functions that are intended for debugging but should
146+
/// not compromise unwind safety.
147+
#[doc(hidden)]
148+
#[unstable(feature = "core_panic", issue = "none")]
149+
#[allow_internal_unstable(core_panic, const_format_args)]
150+
#[rustc_macro_transparency = "semitransparent"]
151+
pub macro debug_assert_nounwind {
152+
($cond:expr $(,)?) => {
153+
if $crate::cfg!(debug_assertions) {
154+
if !$cond {
155+
$crate::panicking::panic_nounwind($crate::concat!("assertion failed: ", $crate::stringify!($cond)));
156+
}
157+
}
158+
},
159+
($cond:expr, $($arg:tt)+) => {
160+
if $crate::cfg!(debug_assertions) {
161+
if !$cond {
162+
$crate::panicking::panic_nounwind_fmt($crate::const_format_args!($($arg)+), false);
163+
}
164+
}
165+
},
166+
}
167+
142168
/// An internal trait used by std to pass data from std to `panic_unwind` and
143169
/// other panic runtimes. Not intended to be stabilized any time soon, do not
144170
/// use.

library/core/src/panicking.rs

+35-19
Original file line numberDiff line numberDiff line change
@@ -82,28 +82,43 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
8282
// and unwinds anyway, we will hit the "unwinding out of nounwind function" guard,
8383
// which causes a "panic in a function that cannot unwind".
8484
#[rustc_nounwind]
85-
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
86-
if cfg!(feature = "panic_immediate_abort") {
87-
super::intrinsics::abort()
88-
}
85+
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
86+
pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
87+
#[track_caller]
88+
fn runtime(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
89+
if cfg!(feature = "panic_immediate_abort") {
90+
super::intrinsics::abort()
91+
}
8992

90-
// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
91-
// that gets resolved to the `#[panic_handler]` function.
92-
extern "Rust" {
93-
#[lang = "panic_impl"]
94-
fn panic_impl(pi: &PanicInfo<'_>) -> !;
93+
// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
94+
// that gets resolved to the `#[panic_handler]` function.
95+
extern "Rust" {
96+
#[lang = "panic_impl"]
97+
fn panic_impl(pi: &PanicInfo<'_>) -> !;
98+
}
99+
100+
// PanicInfo with the `can_unwind` flag set to false forces an abort.
101+
let pi = PanicInfo::internal_constructor(
102+
Some(&fmt),
103+
Location::caller(),
104+
/* can_unwind */ false,
105+
force_no_backtrace,
106+
);
107+
108+
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
109+
unsafe { panic_impl(&pi) }
95110
}
96111

97-
// PanicInfo with the `can_unwind` flag set to false forces an abort.
98-
let pi = PanicInfo::internal_constructor(
99-
Some(&fmt),
100-
Location::caller(),
101-
/* can_unwind */ false,
102-
force_no_backtrace,
103-
);
112+
#[inline]
113+
#[track_caller]
114+
const fn comptime(fmt: fmt::Arguments<'_>, _force_no_backtrace: bool) -> ! {
115+
panic_fmt(fmt);
116+
}
104117

105-
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
106-
unsafe { panic_impl(&pi) }
118+
// SAFETY: const panic does not care about unwinding
119+
unsafe {
120+
super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime);
121+
}
107122
}
108123

109124
// Next we define a bunch of higher-level wrappers that all bottom out in the two core functions
@@ -132,7 +147,8 @@ pub const fn panic(expr: &'static str) -> ! {
132147
#[cfg_attr(feature = "panic_immediate_abort", inline)]
133148
#[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics
134149
#[rustc_nounwind]
135-
pub fn panic_nounwind(expr: &'static str) -> ! {
150+
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
151+
pub const fn panic_nounwind(expr: &'static str) -> ! {
136152
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false);
137153
}
138154

library/core/src/ptr/alignment.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::convert::{TryFrom, TryInto};
2-
use crate::intrinsics::assert_unsafe_precondition;
32
use crate::num::NonZeroUsize;
43
use crate::{cmp, fmt, hash, mem, num};
54

@@ -77,13 +76,10 @@ impl Alignment {
7776
#[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")]
7877
#[inline]
7978
pub const unsafe fn new_unchecked(align: usize) -> Self {
80-
// SAFETY: Precondition passed to the caller.
81-
unsafe {
82-
assert_unsafe_precondition!(
83-
"Alignment::new_unchecked requires a power of two",
84-
(align: usize) => align.is_power_of_two()
85-
)
86-
};
79+
crate::panic::debug_assert_nounwind!(
80+
align.is_power_of_two(),
81+
"Alignment::new_unchecked requires a power of two"
82+
);
8783

8884
// SAFETY: By precondition, this must be a power of two, and
8985
// our variants encompass all possible powers of two.

library/core/src/slice/index.rs

+29-46
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Indexing implementations for `[T]`.
22
3-
use crate::intrinsics::assert_unsafe_precondition;
43
use crate::intrinsics::const_eval_select;
54
use crate::intrinsics::unchecked_sub;
65
use crate::ops;
6+
use crate::panic::debug_assert_nounwind;
77
use crate::ptr;
88

99
#[stable(feature = "rust1", since = "1.0.0")]
@@ -225,31 +225,25 @@ unsafe impl<T> SliceIndex<[T]> for usize {
225225

226226
#[inline]
227227
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
228-
let this = self;
228+
debug_assert_nounwind!(
229+
self < slice.len(),
230+
"slice::get_unchecked requires that the index is within the slice",
231+
);
229232
// SAFETY: the caller guarantees that `slice` is not dangling, so it
230233
// cannot be longer than `isize::MAX`. They also guarantee that
231234
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
232235
// so the call to `add` is safe.
233-
unsafe {
234-
assert_unsafe_precondition!(
235-
"slice::get_unchecked requires that the index is within the slice",
236-
[T](this: usize, slice: *const [T]) => this < slice.len()
237-
);
238-
slice.as_ptr().add(self)
239-
}
236+
unsafe { slice.as_ptr().add(self) }
240237
}
241238

242239
#[inline]
243240
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
244-
let this = self;
241+
debug_assert_nounwind!(
242+
self < slice.len(),
243+
"slice::get_unchecked_mut requires that the index is within the slice",
244+
);
245245
// SAFETY: see comments for `get_unchecked` above.
246-
unsafe {
247-
assert_unsafe_precondition!(
248-
"slice::get_unchecked_mut requires that the index is within the slice",
249-
[T](this: usize, slice: *mut [T]) => this < slice.len()
250-
);
251-
slice.as_mut_ptr().add(self)
252-
}
246+
unsafe { slice.as_mut_ptr().add(self) }
253247
}
254248

255249
#[inline]
@@ -293,32 +287,25 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
293287

294288
#[inline]
295289
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
296-
let end = self.end();
290+
debug_assert_nounwind!(
291+
self.end() <= slice.len(),
292+
"slice::get_unchecked requires that the index is within the slice"
293+
);
297294
// SAFETY: the caller guarantees that `slice` is not dangling, so it
298295
// cannot be longer than `isize::MAX`. They also guarantee that
299296
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
300297
// so the call to `add` is safe.
301-
302-
unsafe {
303-
assert_unsafe_precondition!(
304-
"slice::get_unchecked requires that the index is within the slice",
305-
[T](end: usize, slice: *const [T]) => end <= slice.len()
306-
);
307-
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len())
308-
}
298+
unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len()) }
309299
}
310300

311301
#[inline]
312302
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
313-
let end = self.end();
303+
debug_assert_nounwind!(
304+
self.end() <= slice.len(),
305+
"slice::get_unchecked_mut requires that the index is within the slice",
306+
);
314307
// SAFETY: see comments for `get_unchecked` above.
315-
unsafe {
316-
assert_unsafe_precondition!(
317-
"slice::get_unchecked_mut requires that the index is within the slice",
318-
[T](end: usize, slice: *mut [T]) => end <= slice.len()
319-
);
320-
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len())
321-
}
308+
unsafe { ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len()) }
322309
}
323310

324311
#[inline]
@@ -369,32 +356,28 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
369356

370357
#[inline]
371358
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
372-
let this = ops::Range { ..self };
359+
debug_assert_nounwind!(
360+
self.end >= self.start && self.end <= slice.len(),
361+
"slice::get_unchecked requires that the range is within the slice",
362+
);
373363
// SAFETY: the caller guarantees that `slice` is not dangling, so it
374364
// cannot be longer than `isize::MAX`. They also guarantee that
375365
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
376366
// so the call to `add` is safe and the length calculation cannot overflow.
377367
unsafe {
378-
assert_unsafe_precondition!(
379-
"slice::get_unchecked requires that the range is within the slice",
380-
[T](this: ops::Range<usize>, slice: *const [T]) =>
381-
this.end >= this.start && this.end <= slice.len()
382-
);
383368
let new_len = unchecked_sub(self.end, self.start);
384369
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len)
385370
}
386371
}
387372

388373
#[inline]
389374
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
390-
let this = ops::Range { ..self };
375+
debug_assert_nounwind!(
376+
self.end >= self.start && self.end <= slice.len(),
377+
"slice::get_unchecked_mut requires that the range is within the slice",
378+
);
391379
// SAFETY: see comments for `get_unchecked` above.
392380
unsafe {
393-
assert_unsafe_precondition!(
394-
"slice::get_unchecked_mut requires that the range is within the slice",
395-
[T](this: ops::Range<usize>, slice: *mut [T]) =>
396-
this.end >= this.start && this.end <= slice.len()
397-
);
398381
let new_len = unchecked_sub(self.end, self.start);
399382
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len)
400383
}

0 commit comments

Comments
 (0)