Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f0bf455

Browse files
authoredNov 5, 2024
Rollup merge of rust-lang#132571 - RalfJung:const_eval_select_macro, r=oli-obk
add const_eval_select macro to reduce redundancy I played around a bit with a macro to make const_eval_select invocations look a bit nicer and avoid repeating the argument lists. Here's what I got. What do you think? I didn't apply this everywhere yet because I wanted to gather feedback first. The second commit moves the macros from rust-lang#132542 into a more sensible place. It didn't seem worth its own PR and would conflict with this PR if done separately. Cc `@oli-obk` `@saethlin` `@tgross35` try-job: dist-aarch64-msvc
2 parents 252b8c7 + 613f53e commit f0bf455

19 files changed

+480
-458
lines changed
 

‎library/core/src/char/methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! impl char {}
22
33
use super::*;
4-
use crate::macros::const_panic;
4+
use crate::panic::const_panic;
55
use crate::slice;
66
use crate::str::from_utf8_unchecked_mut;
77
use crate::unicode::printable::is_printable;

‎library/core/src/ffi/c_str.rs

+48-51
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
use crate::cmp::Ordering;
44
use crate::error::Error;
55
use crate::ffi::c_char;
6+
use crate::intrinsics::const_eval_select;
67
use crate::iter::FusedIterator;
78
use crate::marker::PhantomData;
89
use crate::ptr::NonNull;
910
use crate::slice::memchr;
10-
use crate::{fmt, intrinsics, ops, slice, str};
11+
use crate::{fmt, ops, slice, str};
1112

1213
// FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link
1314
// depends on where the item is being documented. however, since this is libcore, we can't
@@ -411,37 +412,35 @@ impl CStr {
411412
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
412413
#[rustc_allow_const_fn_unstable(const_eval_select)]
413414
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
414-
#[inline]
415-
fn rt_impl(bytes: &[u8]) -> &CStr {
416-
// Chance at catching some UB at runtime with debug builds.
417-
debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
418-
419-
// SAFETY: Casting to CStr is safe because its internal representation
420-
// is a [u8] too (safe only inside std).
421-
// Dereferencing the obtained pointer is safe because it comes from a
422-
// reference. Making a reference is then safe because its lifetime
423-
// is bound by the lifetime of the given `bytes`.
424-
unsafe { &*(bytes as *const [u8] as *const CStr) }
425-
}
426-
427-
const fn const_impl(bytes: &[u8]) -> &CStr {
428-
// Saturating so that an empty slice panics in the assert with a good
429-
// message, not here due to underflow.
430-
let mut i = bytes.len().saturating_sub(1);
431-
assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated");
432-
433-
// Ending nul byte exists, skip to the rest.
434-
while i != 0 {
435-
i -= 1;
436-
let byte = bytes[i];
437-
assert!(byte != 0, "input contained interior nul");
415+
const_eval_select!(
416+
@capture { bytes: &[u8] } -> &CStr:
417+
if const {
418+
// Saturating so that an empty slice panics in the assert with a good
419+
// message, not here due to underflow.
420+
let mut i = bytes.len().saturating_sub(1);
421+
assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated");
422+
423+
// Ending nul byte exists, skip to the rest.
424+
while i != 0 {
425+
i -= 1;
426+
let byte = bytes[i];
427+
assert!(byte != 0, "input contained interior nul");
428+
}
429+
430+
// SAFETY: See runtime cast comment below.
431+
unsafe { &*(bytes as *const [u8] as *const CStr) }
432+
} else {
433+
// Chance at catching some UB at runtime with debug builds.
434+
debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
435+
436+
// SAFETY: Casting to CStr is safe because its internal representation
437+
// is a [u8] too (safe only inside std).
438+
// Dereferencing the obtained pointer is safe because it comes from a
439+
// reference. Making a reference is then safe because its lifetime
440+
// is bound by the lifetime of the given `bytes`.
441+
unsafe { &*(bytes as *const [u8] as *const CStr) }
438442
}
439-
440-
// SAFETY: See `rt_impl` cast.
441-
unsafe { &*(bytes as *const [u8] as *const CStr) }
442-
}
443-
444-
intrinsics::const_eval_select((bytes,), const_impl, rt_impl)
443+
)
445444
}
446445

447446
/// Returns the inner pointer to this C string.
@@ -735,29 +734,27 @@ impl AsRef<CStr> for CStr {
735734
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))]
736735
#[rustc_allow_const_fn_unstable(const_eval_select)]
737736
const unsafe fn strlen(ptr: *const c_char) -> usize {
738-
const fn strlen_ct(s: *const c_char) -> usize {
739-
let mut len = 0;
740-
741-
// SAFETY: Outer caller has provided a pointer to a valid C string.
742-
while unsafe { *s.add(len) } != 0 {
743-
len += 1;
744-
}
737+
const_eval_select!(
738+
@capture { s: *const c_char = ptr } -> usize:
739+
if const {
740+
let mut len = 0;
741+
742+
// SAFETY: Outer caller has provided a pointer to a valid C string.
743+
while unsafe { *s.add(len) } != 0 {
744+
len += 1;
745+
}
745746

746-
len
747-
}
747+
len
748+
} else {
749+
extern "C" {
750+
/// Provided by libc or compiler_builtins.
751+
fn strlen(s: *const c_char) -> usize;
752+
}
748753

749-
#[inline]
750-
fn strlen_rt(s: *const c_char) -> usize {
751-
extern "C" {
752-
/// Provided by libc or compiler_builtins.
753-
fn strlen(s: *const c_char) -> usize;
754+
// SAFETY: Outer caller has provided a pointer to a valid C string.
755+
unsafe { strlen(s) }
754756
}
755-
756-
// SAFETY: Outer caller has provided a pointer to a valid C string.
757-
unsafe { strlen(s) }
758-
}
759-
760-
intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt)
757+
)
761758
}
762759

763760
/// An iterator over the bytes of a [`CStr`], without the nul terminator.

‎library/core/src/intrinsics.rs

+73-10
Original file line numberDiff line numberDiff line change
@@ -2940,6 +2940,68 @@ where
29402940
unreachable!()
29412941
}
29422942

2943+
/// A macro to make it easier to invoke const_eval_select. Use as follows:
2944+
/// ```rust,ignore (just a macro example)
2945+
/// const_eval_select!(
2946+
/// @capture { arg1: i32 = some_expr, arg2: T = other_expr } -> U:
2947+
/// if const #[attributes_for_const_arm] {
2948+
/// // Compile-time code goes here.
2949+
/// } else #[attributes_for_runtime_arm] {
2950+
/// // Run-time code goes here.
2951+
/// }
2952+
/// )
2953+
/// ```
2954+
/// The `@capture` block declares which surrounding variables / expressions can be
2955+
/// used inside the `if const`.
2956+
/// Note that the two arms of this `if` really each become their own function, which is why the
2957+
/// macro supports setting attributes for those functions. The runtime function is always
2958+
/// markes as `#[inline]`.
2959+
///
2960+
/// See [`const_eval_select()`] for the rules and requirements around that intrinsic.
2961+
pub(crate) macro const_eval_select {
2962+
(
2963+
@capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
2964+
if const
2965+
$(#[$compiletime_attr:meta])* $compiletime:block
2966+
else
2967+
$(#[$runtime_attr:meta])* $runtime:block
2968+
) => {{
2969+
#[inline] // avoid the overhead of an extra fn call
2970+
$(#[$runtime_attr])*
2971+
fn runtime($($arg: $ty),*) $( -> $ret )? {
2972+
$runtime
2973+
}
2974+
2975+
#[inline] // prevent codegen on this function
2976+
$(#[$compiletime_attr])*
2977+
const fn compiletime($($arg: $ty),*) $( -> $ret )? {
2978+
// Don't warn if one of the arguments is unused.
2979+
$(let _ = $arg;)*
2980+
2981+
$compiletime
2982+
}
2983+
2984+
const_eval_select(($($val,)*), compiletime, runtime)
2985+
}},
2986+
// We support leaving away the `val` expressions for *all* arguments
2987+
// (but not for *some* arguments, that's too tricky).
2988+
(
2989+
@capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? :
2990+
if const
2991+
$(#[$compiletime_attr:meta])* $compiletime:block
2992+
else
2993+
$(#[$runtime_attr:meta])* $runtime:block
2994+
) => {
2995+
$crate::intrinsics::const_eval_select!(
2996+
@capture { $($arg : $ty = $arg),* } $(-> $ret)? :
2997+
if const
2998+
$(#[$compiletime_attr])* $compiletime
2999+
else
3000+
$(#[$runtime_attr])* $runtime
3001+
)
3002+
},
3003+
}
3004+
29433005
/// Returns whether the argument's value is statically known at
29443006
/// compile-time.
29453007
///
@@ -2982,7 +3044,7 @@ where
29823044
/// # Stability concerns
29833045
///
29843046
/// While it is safe to call, this intrinsic may behave differently in
2985-
/// a `const` context than otherwise. See the [`const_eval_select`]
3047+
/// a `const` context than otherwise. See the [`const_eval_select()`]
29863048
/// documentation for an explanation of the issues this can cause. Unlike
29873049
/// `const_eval_select`, this intrinsic isn't guaranteed to behave
29883050
/// deterministically even in a `const` context.
@@ -3868,14 +3930,15 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
38683930
fn miri_promise_symbolic_alignment(ptr: *const (), align: usize);
38693931
}
38703932

3871-
fn runtime(ptr: *const (), align: usize) {
3872-
// SAFETY: this call is always safe.
3873-
unsafe {
3874-
miri_promise_symbolic_alignment(ptr, align);
3933+
const_eval_select!(
3934+
@capture { ptr: *const (), align: usize}:
3935+
if const {
3936+
// Do nothing.
3937+
} else {
3938+
// SAFETY: this call is always safe.
3939+
unsafe {
3940+
miri_promise_symbolic_alignment(ptr, align);
3941+
}
38753942
}
3876-
}
3877-
3878-
const fn compiletime(_ptr: *const (), _align: usize) {}
3879-
3880-
const_eval_select((ptr, align), compiletime, runtime);
3943+
)
38813944
}

‎library/core/src/macros/mod.rs

-61
Original file line numberDiff line numberDiff line change
@@ -12,54 +12,6 @@ macro_rules! panic {
1212
};
1313
}
1414

15-
/// Helper macro for panicking in a `const fn`.
16-
/// Invoke as:
17-
/// ```rust,ignore (just an example)
18-
/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar);
19-
/// ```
20-
/// where the first message will be printed in const-eval,
21-
/// and the second message will be printed at runtime.
22-
// All uses of this macro are FIXME(const-hack).
23-
#[unstable(feature = "panic_internals", issue = "none")]
24-
#[doc(hidden)]
25-
pub macro const_panic {
26-
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{
27-
#[inline]
28-
#[track_caller]
29-
fn runtime($($arg: $ty),*) -> ! {
30-
$crate::panic!($runtime_msg);
31-
}
32-
33-
#[inline]
34-
#[track_caller]
35-
const fn compiletime($(_: $ty),*) -> ! {
36-
$crate::panic!($const_msg);
37-
}
38-
39-
// Wrap call to `const_eval_select` in a function so that we can
40-
// add the `rustc_allow_const_fn_unstable`. This is okay to do
41-
// because both variants will panic, just with different messages.
42-
#[rustc_allow_const_fn_unstable(const_eval_select)]
43-
#[inline(always)]
44-
#[track_caller]
45-
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
46-
const fn do_panic($($arg: $ty),*) -> ! {
47-
$crate::intrinsics::const_eval_select(($($arg),* ,), compiletime, runtime)
48-
}
49-
50-
do_panic($($val),*)
51-
}},
52-
// We support leaving away the `val` expressions for *all* arguments
53-
// (but not for *some* arguments, that's too tricky).
54-
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => {
55-
$crate::macros::const_panic!(
56-
$const_msg,
57-
$runtime_msg,
58-
$($arg: $ty = $arg),*
59-
)
60-
},
61-
}
62-
6315
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
6416
///
6517
/// Assertions are always checked in both debug and release builds, and cannot
@@ -244,19 +196,6 @@ pub macro assert_matches {
244196
},
245197
}
246198

247-
/// A version of `assert` that prints a non-formatting message in const contexts.
248-
///
249-
/// See [`const_panic!`].
250-
#[unstable(feature = "panic_internals", issue = "none")]
251-
#[doc(hidden)]
252-
pub macro const_assert {
253-
($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{
254-
if !$crate::intrinsics::likely($condition) {
255-
$crate::macros::const_panic!($const_msg, $runtime_msg, $($arg)*)
256-
}
257-
}}
258-
}
259-
260199
/// A macro for defining `#[cfg]` match-like statements.
261200
///
262201
/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of

‎library/core/src/num/f128.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17-
use crate::macros::const_assert;
1817
use crate::mem;
1918
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
2020

2121
/// Basic mathematical constants.
2222
#[unstable(feature = "f128", issue = "116909")]

‎library/core/src/num/f16.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17-
use crate::macros::const_assert;
1817
use crate::mem;
1918
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
2020

2121
/// Basic mathematical constants.
2222
#[unstable(feature = "f16", issue = "116909")]

‎library/core/src/num/f32.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17-
use crate::macros::const_assert;
1817
use crate::mem;
1918
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
2020

2121
/// The radix or base of the internal representation of `f32`.
2222
/// Use [`f32::RADIX`] instead.

‎library/core/src/num/f64.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17-
use crate::macros::const_assert;
1817
use crate::mem;
1918
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
2020

2121
/// The radix or base of the internal representation of `f64`.
2222
/// Use [`f64::RADIX`] instead.

‎library/core/src/num/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

5-
use crate::macros::const_panic;
5+
use crate::panic::const_panic;
66
use crate::str::FromStr;
77
use crate::ub_checks::assert_unsafe_precondition;
88
use crate::{ascii, intrinsics, mem};

‎library/core/src/panic.rs

+56
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,59 @@ pub unsafe trait PanicPayload: crate::fmt::Display {
189189
None
190190
}
191191
}
192+
193+
/// Helper macro for panicking in a `const fn`.
194+
/// Invoke as:
195+
/// ```rust,ignore (just an example)
196+
/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar);
197+
/// ```
198+
/// where the first message will be printed in const-eval,
199+
/// and the second message will be printed at runtime.
200+
// All uses of this macro are FIXME(const-hack).
201+
#[unstable(feature = "panic_internals", issue = "none")]
202+
#[doc(hidden)]
203+
pub macro const_panic {
204+
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{
205+
// Wrap call to `const_eval_select` in a function so that we can
206+
// add the `rustc_allow_const_fn_unstable`. This is okay to do
207+
// because both variants will panic, just with different messages.
208+
#[rustc_allow_const_fn_unstable(const_eval_select)]
209+
#[inline(always)]
210+
#[track_caller]
211+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
212+
const fn do_panic($($arg: $ty),*) -> ! {
213+
$crate::intrinsics::const_eval_select!(
214+
@capture { $($arg: $ty),* } -> !:
215+
if const #[track_caller] {
216+
$crate::panic!($const_msg)
217+
} else #[track_caller] {
218+
$crate::panic!($runtime_msg)
219+
}
220+
)
221+
}
222+
223+
do_panic($($val),*)
224+
}},
225+
// We support leaving away the `val` expressions for *all* arguments
226+
// (but not for *some* arguments, that's too tricky).
227+
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => {
228+
$crate::panic::const_panic!(
229+
$const_msg,
230+
$runtime_msg,
231+
$($arg: $ty = $arg),*
232+
)
233+
},
234+
}
235+
236+
/// A version of `assert` that prints a non-formatting message in const contexts.
237+
///
238+
/// See [`const_panic!`].
239+
#[unstable(feature = "panic_internals", issue = "none")]
240+
#[doc(hidden)]
241+
pub macro const_assert {
242+
($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{
243+
if !$crate::intrinsics::likely($condition) {
244+
$crate::panic::const_panic!($const_msg, $runtime_msg, $($arg)*)
245+
}
246+
}}
247+
}

‎library/core/src/panicking.rs

+29-33
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
)]
3030

3131
use crate::fmt;
32+
use crate::intrinsics::const_eval_select;
3233
use crate::panic::{Location, PanicInfo};
3334

3435
#[cfg(feature = "panic_immediate_abort")]
@@ -89,40 +90,35 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
8990
#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable
9091
#[rustc_allow_const_fn_unstable(const_eval_select)]
9192
pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
92-
#[inline] // this should always be inlined into `panic_nounwind_fmt`
93-
#[track_caller]
94-
fn runtime(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
95-
if cfg!(feature = "panic_immediate_abort") {
96-
super::intrinsics::abort()
93+
const_eval_select!(
94+
@capture { fmt: fmt::Arguments<'_>, force_no_backtrace: bool } -> !:
95+
if const #[track_caller] {
96+
// We don't unwind anyway at compile-time so we can call the regular `panic_fmt`.
97+
panic_fmt(fmt)
98+
} else #[track_caller] {
99+
if cfg!(feature = "panic_immediate_abort") {
100+
super::intrinsics::abort()
101+
}
102+
103+
// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
104+
// that gets resolved to the `#[panic_handler]` function.
105+
extern "Rust" {
106+
#[lang = "panic_impl"]
107+
fn panic_impl(pi: &PanicInfo<'_>) -> !;
108+
}
109+
110+
// PanicInfo with the `can_unwind` flag set to false forces an abort.
111+
let pi = PanicInfo::new(
112+
&fmt,
113+
Location::caller(),
114+
/* can_unwind */ false,
115+
force_no_backtrace,
116+
);
117+
118+
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
119+
unsafe { panic_impl(&pi) }
97120
}
98-
99-
// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
100-
// that gets resolved to the `#[panic_handler]` function.
101-
extern "Rust" {
102-
#[lang = "panic_impl"]
103-
fn panic_impl(pi: &PanicInfo<'_>) -> !;
104-
}
105-
106-
// PanicInfo with the `can_unwind` flag set to false forces an abort.
107-
let pi = PanicInfo::new(
108-
&fmt,
109-
Location::caller(),
110-
/* can_unwind */ false,
111-
force_no_backtrace,
112-
);
113-
114-
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
115-
unsafe { panic_impl(&pi) }
116-
}
117-
118-
#[inline]
119-
#[track_caller]
120-
const fn comptime(fmt: fmt::Arguments<'_>, _force_no_backtrace: bool) -> ! {
121-
// We don't unwind anyway at compile-time so we can call the regular `panic_fmt`.
122-
panic_fmt(fmt);
123-
}
124-
125-
super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime);
121+
)
126122
}
127123

128124
// Next we define a bunch of higher-level wrappers that all bottom out in the two core functions

‎library/core/src/ptr/const_ptr.rs

+60-68
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,23 @@ impl<T: ?Sized> *const T {
3333
#[rustc_diagnostic_item = "ptr_const_is_null"]
3434
#[inline]
3535
pub const fn is_null(self) -> bool {
36-
#[inline]
37-
fn runtime_impl(ptr: *const u8) -> bool {
38-
ptr.addr() == 0
39-
}
40-
41-
#[inline]
42-
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
43-
const fn const_impl(ptr: *const u8) -> bool {
44-
match (ptr).guaranteed_eq(null_mut()) {
45-
Some(res) => res,
46-
// To remain maximally convervative, we stop execution when we don't
47-
// know whether the pointer is null or not.
48-
// We can *not* return `false` here, that would be unsound in `NonNull::new`!
49-
None => panic!("null-ness of this pointer cannot be determined in const context"),
50-
}
51-
}
52-
5336
// Compare via a cast to a thin pointer, so fat pointers are only
5437
// considering their "data" part for null-ness.
55-
const_eval_select((self as *const u8,), const_impl, runtime_impl)
38+
let ptr = self as *const u8;
39+
const_eval_select!(
40+
@capture { ptr: *const u8 } -> bool:
41+
if const #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] {
42+
match (ptr).guaranteed_eq(null_mut()) {
43+
Some(res) => res,
44+
// To remain maximally convervative, we stop execution when we don't
45+
// know whether the pointer is null or not.
46+
// We can *not* return `false` here, that would be unsound in `NonNull::new`!
47+
None => panic!("null-ness of this pointer cannot be determined in const context"),
48+
}
49+
} else {
50+
ptr.addr() == 0
51+
}
52+
)
5653
}
5754

5855
/// Casts to a pointer of another type.
@@ -410,22 +407,21 @@ impl<T: ?Sized> *const T {
410407
#[inline]
411408
#[rustc_allow_const_fn_unstable(const_eval_select)]
412409
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
413-
#[inline]
414-
fn runtime(this: *const (), count: isize, size: usize) -> bool {
415-
// We know `size <= isize::MAX` so the `as` cast here is not lossy.
416-
let Some(byte_offset) = count.checked_mul(size as isize) else {
417-
return false;
418-
};
419-
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
420-
!overflow
421-
}
422-
423-
const fn comptime(_: *const (), _: isize, _: usize) -> bool {
424-
true
425-
}
426-
427410
// We can use const_eval_select here because this is only for UB checks.
428-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
411+
const_eval_select!(
412+
@capture { this: *const (), count: isize, size: usize } -> bool:
413+
if const {
414+
true
415+
} else {
416+
// `size` is the size of a Rust type, so we know that
417+
// `size <= isize::MAX` and thus `as` cast here is not lossy.
418+
let Some(byte_offset) = count.checked_mul(size as isize) else {
419+
return false;
420+
};
421+
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
422+
!overflow
423+
}
424+
)
429425
}
430426

431427
ub_checks::assert_unsafe_precondition!(
@@ -763,14 +759,14 @@ impl<T: ?Sized> *const T {
763759
{
764760
#[rustc_allow_const_fn_unstable(const_eval_select)]
765761
const fn runtime_ptr_ge(this: *const (), origin: *const ()) -> bool {
766-
fn runtime(this: *const (), origin: *const ()) -> bool {
767-
this >= origin
768-
}
769-
const fn comptime(_: *const (), _: *const ()) -> bool {
770-
true
771-
}
772-
773-
intrinsics::const_eval_select((this, origin), comptime, runtime)
762+
const_eval_select!(
763+
@capture { this: *const (), origin: *const () } -> bool:
764+
if const {
765+
true
766+
} else {
767+
this >= origin
768+
}
769+
)
774770
}
775771

776772
ub_checks::assert_unsafe_precondition!(
@@ -924,20 +920,18 @@ impl<T: ?Sized> *const T {
924920
#[inline]
925921
#[rustc_allow_const_fn_unstable(const_eval_select)]
926922
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
927-
#[inline]
928-
fn runtime(this: *const (), count: usize, size: usize) -> bool {
929-
let Some(byte_offset) = count.checked_mul(size) else {
930-
return false;
931-
};
932-
let (_, overflow) = this.addr().overflowing_add(byte_offset);
933-
byte_offset <= (isize::MAX as usize) && !overflow
934-
}
935-
936-
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
937-
true
938-
}
939-
940-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
923+
const_eval_select!(
924+
@capture { this: *const (), count: usize, size: usize } -> bool:
925+
if const {
926+
true
927+
} else {
928+
let Some(byte_offset) = count.checked_mul(size) else {
929+
return false;
930+
};
931+
let (_, overflow) = this.addr().overflowing_add(byte_offset);
932+
byte_offset <= (isize::MAX as usize) && !overflow
933+
}
934+
)
941935
}
942936

943937
#[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild.
@@ -1033,19 +1027,17 @@ impl<T: ?Sized> *const T {
10331027
#[inline]
10341028
#[rustc_allow_const_fn_unstable(const_eval_select)]
10351029
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
1036-
#[inline]
1037-
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1038-
let Some(byte_offset) = count.checked_mul(size) else {
1039-
return false;
1040-
};
1041-
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1042-
}
1043-
1044-
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1045-
true
1046-
}
1047-
1048-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1030+
const_eval_select!(
1031+
@capture { this: *const (), count: usize, size: usize } -> bool:
1032+
if const {
1033+
true
1034+
} else {
1035+
let Some(byte_offset) = count.checked_mul(size) else {
1036+
return false;
1037+
};
1038+
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1039+
}
1040+
)
10491041
}
10501042

10511043
#[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild.

‎library/core/src/ptr/mut_ptr.rs

+38-43
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::*;
22
use crate::cmp::Ordering::{Equal, Greater, Less};
3+
use crate::intrinsics::const_eval_select;
34
use crate::mem::SizedTypeProperties;
45
use crate::slice::{self, SliceIndex};
56

@@ -404,23 +405,21 @@ impl<T: ?Sized> *mut T {
404405
#[inline]
405406
#[rustc_allow_const_fn_unstable(const_eval_select)]
406407
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
407-
#[inline]
408-
fn runtime(this: *const (), count: isize, size: usize) -> bool {
409-
// `size` is the size of a Rust type, so we know that
410-
// `size <= isize::MAX` and thus `as` cast here is not lossy.
411-
let Some(byte_offset) = count.checked_mul(size as isize) else {
412-
return false;
413-
};
414-
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
415-
!overflow
416-
}
417-
418-
const fn comptime(_: *const (), _: isize, _: usize) -> bool {
419-
true
420-
}
421-
422408
// We can use const_eval_select here because this is only for UB checks.
423-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
409+
const_eval_select!(
410+
@capture { this: *const (), count: isize, size: usize } -> bool:
411+
if const {
412+
true
413+
} else {
414+
// `size` is the size of a Rust type, so we know that
415+
// `size <= isize::MAX` and thus `as` cast here is not lossy.
416+
let Some(byte_offset) = count.checked_mul(size as isize) else {
417+
return false;
418+
};
419+
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
420+
!overflow
421+
}
422+
)
424423
}
425424

426425
ub_checks::assert_unsafe_precondition!(
@@ -1002,20 +1001,18 @@ impl<T: ?Sized> *mut T {
10021001
#[inline]
10031002
#[rustc_allow_const_fn_unstable(const_eval_select)]
10041003
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
1005-
#[inline]
1006-
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1007-
let Some(byte_offset) = count.checked_mul(size) else {
1008-
return false;
1009-
};
1010-
let (_, overflow) = this.addr().overflowing_add(byte_offset);
1011-
byte_offset <= (isize::MAX as usize) && !overflow
1012-
}
1013-
1014-
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1015-
true
1016-
}
1017-
1018-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1004+
const_eval_select!(
1005+
@capture { this: *const (), count: usize, size: usize } -> bool:
1006+
if const {
1007+
true
1008+
} else {
1009+
let Some(byte_offset) = count.checked_mul(size) else {
1010+
return false;
1011+
};
1012+
let (_, overflow) = this.addr().overflowing_add(byte_offset);
1013+
byte_offset <= (isize::MAX as usize) && !overflow
1014+
}
1015+
)
10191016
}
10201017

10211018
#[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild.
@@ -1111,19 +1108,17 @@ impl<T: ?Sized> *mut T {
11111108
#[inline]
11121109
#[rustc_allow_const_fn_unstable(const_eval_select)]
11131110
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
1114-
#[inline]
1115-
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1116-
let Some(byte_offset) = count.checked_mul(size) else {
1117-
return false;
1118-
};
1119-
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1120-
}
1121-
1122-
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1123-
true
1124-
}
1125-
1126-
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1111+
const_eval_select!(
1112+
@capture { this: *const (), count: usize, size: usize } -> bool:
1113+
if const {
1114+
true
1115+
} else {
1116+
let Some(byte_offset) = count.checked_mul(size) else {
1117+
return false;
1118+
};
1119+
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1120+
}
1121+
)
11271122
}
11281123

11291124
#[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild.

‎library/core/src/slice/ascii.rs

+75-77
Original file line numberDiff line numberDiff line change
@@ -369,89 +369,87 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool {
369369
const fn is_ascii(s: &[u8]) -> bool {
370370
// The runtime version behaves the same as the compiletime version, it's
371371
// just more optimized.
372-
return const_eval_select((s,), compiletime, runtime);
373-
374-
const fn compiletime(s: &[u8]) -> bool {
375-
is_ascii_simple(s)
376-
}
377-
378-
#[inline]
379-
fn runtime(s: &[u8]) -> bool {
380-
const USIZE_SIZE: usize = mem::size_of::<usize>();
381-
382-
let len = s.len();
383-
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);
384-
385-
// If we wouldn't gain anything from the word-at-a-time implementation, fall
386-
// back to a scalar loop.
387-
//
388-
// We also do this for architectures where `size_of::<usize>()` isn't
389-
// sufficient alignment for `usize`, because it's a weird edge case.
390-
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
391-
return is_ascii_simple(s);
392-
}
372+
const_eval_select!(
373+
@capture { s: &[u8] } -> bool:
374+
if const {
375+
is_ascii_simple(s)
376+
} else {
377+
const USIZE_SIZE: usize = mem::size_of::<usize>();
378+
379+
let len = s.len();
380+
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);
381+
382+
// If we wouldn't gain anything from the word-at-a-time implementation, fall
383+
// back to a scalar loop.
384+
//
385+
// We also do this for architectures where `size_of::<usize>()` isn't
386+
// sufficient alignment for `usize`, because it's a weird edge case.
387+
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
388+
return is_ascii_simple(s);
389+
}
393390

394-
// We always read the first word unaligned, which means `align_offset` is
395-
// 0, we'd read the same value again for the aligned read.
396-
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };
391+
// We always read the first word unaligned, which means `align_offset` is
392+
// 0, we'd read the same value again for the aligned read.
393+
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };
397394

398-
let start = s.as_ptr();
399-
// SAFETY: We verify `len < USIZE_SIZE` above.
400-
let first_word = unsafe { (start as *const usize).read_unaligned() };
395+
let start = s.as_ptr();
396+
// SAFETY: We verify `len < USIZE_SIZE` above.
397+
let first_word = unsafe { (start as *const usize).read_unaligned() };
401398

402-
if contains_nonascii(first_word) {
403-
return false;
404-
}
405-
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
406-
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
407-
// above.
408-
debug_assert!(offset_to_aligned <= len);
409-
410-
// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
411-
// middle chunk of the slice.
412-
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };
413-
414-
// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
415-
let mut byte_pos = offset_to_aligned;
416-
417-
// Paranoia check about alignment, since we're about to do a bunch of
418-
// unaligned loads. In practice this should be impossible barring a bug in
419-
// `align_offset` though.
420-
// While this method is allowed to spuriously fail in CTFE, if it doesn't
421-
// have alignment information it should have given a `usize::MAX` for
422-
// `align_offset` earlier, sending things through the scalar path instead of
423-
// this one, so this check should pass if it's reachable.
424-
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));
425-
426-
// Read subsequent words until the last aligned word, excluding the last
427-
// aligned word by itself to be done in tail check later, to ensure that
428-
// tail is always one `usize` at most to extra branch `byte_pos == len`.
429-
while byte_pos < len - USIZE_SIZE {
430-
// Sanity check that the read is in bounds
431-
debug_assert!(byte_pos + USIZE_SIZE <= len);
432-
// And that our assumptions about `byte_pos` hold.
433-
debug_assert!(word_ptr.cast::<u8>() == start.wrapping_add(byte_pos));
434-
435-
// SAFETY: We know `word_ptr` is properly aligned (because of
436-
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
437-
let word = unsafe { word_ptr.read() };
438-
if contains_nonascii(word) {
399+
if contains_nonascii(first_word) {
439400
return false;
440401
}
402+
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
403+
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
404+
// above.
405+
debug_assert!(offset_to_aligned <= len);
406+
407+
// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
408+
// middle chunk of the slice.
409+
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };
410+
411+
// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
412+
let mut byte_pos = offset_to_aligned;
413+
414+
// Paranoia check about alignment, since we're about to do a bunch of
415+
// unaligned loads. In practice this should be impossible barring a bug in
416+
// `align_offset` though.
417+
// While this method is allowed to spuriously fail in CTFE, if it doesn't
418+
// have alignment information it should have given a `usize::MAX` for
419+
// `align_offset` earlier, sending things through the scalar path instead of
420+
// this one, so this check should pass if it's reachable.
421+
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));
422+
423+
// Read subsequent words until the last aligned word, excluding the last
424+
// aligned word by itself to be done in tail check later, to ensure that
425+
// tail is always one `usize` at most to extra branch `byte_pos == len`.
426+
while byte_pos < len - USIZE_SIZE {
427+
// Sanity check that the read is in bounds
428+
debug_assert!(byte_pos + USIZE_SIZE <= len);
429+
// And that our assumptions about `byte_pos` hold.
430+
debug_assert!(word_ptr.cast::<u8>() == start.wrapping_add(byte_pos));
431+
432+
// SAFETY: We know `word_ptr` is properly aligned (because of
433+
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
434+
let word = unsafe { word_ptr.read() };
435+
if contains_nonascii(word) {
436+
return false;
437+
}
438+
439+
byte_pos += USIZE_SIZE;
440+
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
441+
// after this `add`, `word_ptr` will be at most one-past-the-end.
442+
word_ptr = unsafe { word_ptr.add(1) };
443+
}
441444

442-
byte_pos += USIZE_SIZE;
443-
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
444-
// after this `add`, `word_ptr` will be at most one-past-the-end.
445-
word_ptr = unsafe { word_ptr.add(1) };
446-
}
447-
448-
// Sanity check to ensure there really is only one `usize` left. This should
449-
// be guaranteed by our loop condition.
450-
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);
445+
// Sanity check to ensure there really is only one `usize` left. This should
446+
// be guaranteed by our loop condition.
447+
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);
451448

452-
// SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start.
453-
let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() };
449+
// SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start.
450+
let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() };
454451

455-
!contains_nonascii(last_word)
456-
}
452+
!contains_nonascii(last_word)
453+
}
454+
)
457455
}

‎library/core/src/slice/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Indexing implementations for `[T]`.
22
3-
use crate::macros::const_panic;
3+
use crate::panic::const_panic;
44
use crate::ub_checks::assert_unsafe_precondition;
55
use crate::{ops, range};
66

‎library/core/src/slice/memchr.rs

+48-50
Original file line numberDiff line numberDiff line change
@@ -56,61 +56,59 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option<usize> {
5656
const fn memchr_aligned(x: u8, text: &[u8]) -> Option<usize> {
5757
// The runtime version behaves the same as the compiletime version, it's
5858
// just more optimized.
59-
return const_eval_select((x, text), compiletime, runtime);
60-
61-
const fn compiletime(x: u8, text: &[u8]) -> Option<usize> {
62-
memchr_naive(x, text)
63-
}
64-
65-
#[inline]
66-
fn runtime(x: u8, text: &[u8]) -> Option<usize> {
67-
// Scan for a single byte value by reading two `usize` words at a time.
68-
//
69-
// Split `text` in three parts
70-
// - unaligned initial part, before the first word aligned address in text
71-
// - body, scan by 2 words at a time
72-
// - the last remaining part, < 2 word size
73-
74-
// search up to an aligned boundary
75-
let len = text.len();
76-
let ptr = text.as_ptr();
77-
let mut offset = ptr.align_offset(USIZE_BYTES);
78-
79-
if offset > 0 {
80-
offset = offset.min(len);
81-
let slice = &text[..offset];
82-
if let Some(index) = memchr_naive(x, slice) {
83-
return Some(index);
59+
const_eval_select!(
60+
@capture { x: u8, text: &[u8] } -> Option<usize>:
61+
if const {
62+
memchr_naive(x, text)
63+
} else {
64+
// Scan for a single byte value by reading two `usize` words at a time.
65+
//
66+
// Split `text` in three parts
67+
// - unaligned initial part, before the first word aligned address in text
68+
// - body, scan by 2 words at a time
69+
// - the last remaining part, < 2 word size
70+
71+
// search up to an aligned boundary
72+
let len = text.len();
73+
let ptr = text.as_ptr();
74+
let mut offset = ptr.align_offset(USIZE_BYTES);
75+
76+
if offset > 0 {
77+
offset = offset.min(len);
78+
let slice = &text[..offset];
79+
if let Some(index) = memchr_naive(x, slice) {
80+
return Some(index);
81+
}
8482
}
85-
}
8683

87-
// search the body of the text
88-
let repeated_x = usize::repeat_u8(x);
89-
while offset <= len - 2 * USIZE_BYTES {
90-
// SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes
91-
// between the offset and the end of the slice.
92-
unsafe {
93-
let u = *(ptr.add(offset) as *const usize);
94-
let v = *(ptr.add(offset + USIZE_BYTES) as *const usize);
95-
96-
// break if there is a matching byte
97-
let zu = contains_zero_byte(u ^ repeated_x);
98-
let zv = contains_zero_byte(v ^ repeated_x);
99-
if zu || zv {
100-
break;
84+
// search the body of the text
85+
let repeated_x = usize::repeat_u8(x);
86+
while offset <= len - 2 * USIZE_BYTES {
87+
// SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes
88+
// between the offset and the end of the slice.
89+
unsafe {
90+
let u = *(ptr.add(offset) as *const usize);
91+
let v = *(ptr.add(offset + USIZE_BYTES) as *const usize);
92+
93+
// break if there is a matching byte
94+
let zu = contains_zero_byte(u ^ repeated_x);
95+
let zv = contains_zero_byte(v ^ repeated_x);
96+
if zu || zv {
97+
break;
98+
}
10199
}
100+
offset += USIZE_BYTES * 2;
102101
}
103-
offset += USIZE_BYTES * 2;
104-
}
105102

106-
// Find the byte after the point the body loop stopped.
107-
// FIXME(const-hack): Use `?` instead.
108-
// FIXME(const-hack, fee1-dead): use range slicing
109-
let slice =
110-
// SAFETY: offset is within bounds
111-
unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) };
112-
if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None }
113-
}
103+
// Find the byte after the point the body loop stopped.
104+
// FIXME(const-hack): Use `?` instead.
105+
// FIXME(const-hack, fee1-dead): use range slicing
106+
let slice =
107+
// SAFETY: offset is within bounds
108+
unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) };
109+
if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None }
110+
}
111+
)
114112
}
115113

116114
/// Returns the last index matching the byte `x` in `text`.

‎library/core/src/str/validations.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,16 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
132132

133133
let ascii_block_size = 2 * USIZE_BYTES;
134134
let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 };
135-
let align = {
136-
const fn compiletime(_v: &[u8]) -> usize {
135+
// Below, we safely fall back to a slower codepath if the offset is `usize::MAX`,
136+
// so the end-to-end behavior is the same at compiletime and runtime.
137+
let align = const_eval_select!(
138+
@capture { v: &[u8] } -> usize:
139+
if const {
137140
usize::MAX
138-
}
139-
140-
fn runtime(v: &[u8]) -> usize {
141+
} else {
141142
v.as_ptr().align_offset(USIZE_BYTES)
142143
}
143-
144-
// Below, we safely fall back to a slower codepath if the offset is `usize::MAX`,
145-
// so the end-to-end behavior is the same at compiletime and runtime.
146-
const_eval_select((v,), compiletime, runtime)
147-
};
144+
);
148145

149146
while index < len {
150147
let old_offset = index;

‎library/core/src/ub_checks.rs

+37-46
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,18 @@ pub use intrinsics::ub_checks as check_library_ub;
9595
#[inline]
9696
#[rustc_allow_const_fn_unstable(const_eval_select)]
9797
pub(crate) const fn check_language_ub() -> bool {
98-
#[inline]
99-
fn runtime() -> bool {
100-
// Disable UB checks in Miri.
101-
!cfg!(miri)
102-
}
103-
104-
#[inline]
105-
const fn comptime() -> bool {
106-
// Always disable UB checks.
107-
false
108-
}
109-
11098
// Only used for UB checks so we may const_eval_select.
111-
intrinsics::ub_checks() && const_eval_select((), comptime, runtime)
99+
intrinsics::ub_checks()
100+
&& const_eval_select!(
101+
@capture { } -> bool:
102+
if const {
103+
// Always disable UB checks.
104+
false
105+
} else {
106+
// Disable UB checks in Miri.
107+
!cfg!(miri)
108+
}
109+
)
112110
}
113111

114112
/// Checks whether `ptr` is properly aligned with respect to the given alignment, and
@@ -120,19 +118,15 @@ pub(crate) const fn check_language_ub() -> bool {
120118
#[inline]
121119
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
122120
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
123-
#[inline]
124-
fn runtime(ptr: *const (), align: usize, is_zst: bool) -> bool {
125-
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
126-
}
127-
128-
#[inline]
129-
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
130-
const fn comptime(ptr: *const (), _align: usize, is_zst: bool) -> bool {
131-
is_zst || !ptr.is_null()
132-
}
133-
134121
// This is just for safety checks so we can const_eval_select.
135-
const_eval_select((ptr, align, is_zst), comptime, runtime)
122+
const_eval_select!(
123+
@capture { ptr: *const (), align: usize, is_zst: bool } -> bool:
124+
if const #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] {
125+
is_zst || !ptr.is_null()
126+
} else {
127+
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
128+
}
129+
)
136130
}
137131

138132
#[inline]
@@ -154,26 +148,23 @@ pub(crate) const fn is_nonoverlapping(
154148
size: usize,
155149
count: usize,
156150
) -> bool {
157-
#[inline]
158-
fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool {
159-
let src_usize = src.addr();
160-
let dst_usize = dst.addr();
161-
let Some(size) = size.checked_mul(count) else {
162-
crate::panicking::panic_nounwind(
163-
"is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
164-
)
165-
};
166-
let diff = src_usize.abs_diff(dst_usize);
167-
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
168-
// they do not overlap.
169-
diff >= size
170-
}
171-
172-
#[inline]
173-
const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool {
174-
true
175-
}
176-
177151
// This is just for safety checks so we can const_eval_select.
178-
const_eval_select((src, dst, size, count), comptime, runtime)
152+
const_eval_select!(
153+
@capture { src: *const (), dst: *const (), size: usize, count: usize } -> bool:
154+
if const {
155+
true
156+
} else {
157+
let src_usize = src.addr();
158+
let dst_usize = dst.addr();
159+
let Some(size) = size.checked_mul(count) else {
160+
crate::panicking::panic_nounwind(
161+
"is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
162+
)
163+
};
164+
let diff = src_usize.abs_diff(dst_usize);
165+
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
166+
// they do not overlap.
167+
diff >= size
168+
}
169+
)
179170
}

‎tests/ui/consts/const-ptr-is-null.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ error[E0080]: evaluation of constant value failed
33
|
44
= note: the evaluated program panicked at 'null-ness of this pointer cannot be determined in const context', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
55
|
6-
note: inside `std::ptr::const_ptr::<impl *const T>::is_null::const_impl`
6+
note: inside `std::ptr::const_ptr::<impl *const T>::is_null::compiletime`
77
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
88
note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
99
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -12,7 +12,7 @@ note: inside `MAYBE_NULL`
1212
|
1313
LL | assert!(!ptr.wrapping_sub(512).is_null());
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15-
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)
1616

1717
error: aborting due to 1 previous error
1818

0 commit comments

Comments
 (0)
Please sign in to comment.