Skip to content

Commit 3d607ed

Browse files
committed
add const_eval_select macro to reduce redundancy
1 parent e3a918e commit 3d607ed

File tree

5 files changed

+218
-175
lines changed

5 files changed

+218
-175
lines changed

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+
(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 #[inline] {
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+
(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 #[inline] {
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

+69-9
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,65 @@ where
27882788
unreachable!()
27892789
}
27902790

2791+
/// A macro to make it easier to invoke const_eval_select. Use as follows:
2792+
/// ```rust,ignore (just a macro example)
2793+
/// const_eval_select!(
2794+
/// #[inline]
2795+
/// (arg1: i32 = some_expr, arg2: T = other_expr) -> U:
2796+
/// if const {
2797+
/// // Compile-time code goes here.
2798+
/// } else {
2799+
/// // Run-time code goes here.
2800+
/// }
2801+
/// )
2802+
/// ```
2803+
pub(crate) macro const_eval_select {
2804+
(
2805+
$(#[$attr:meta])*
2806+
($($arg:ident : $ty:ty = $val:expr),* $(,)?) -> $ret:ty:
2807+
if const
2808+
$(#[$compiletime_attr:meta])* $compiletime:block
2809+
else
2810+
$(#[$runtime_attr:meta])* $runtime:block
2811+
) => {{
2812+
$(#[$attr])*
2813+
$(#[$runtime_attr])*
2814+
fn runtime($($arg: $ty),*) -> $ret {
2815+
$runtime
2816+
}
2817+
2818+
$(#[$attr])*
2819+
$(#[$compiletime_attr])*
2820+
const fn compiletime($($arg: $ty),*) -> $ret {
2821+
// Don't warn if one of the arguments is unused.
2822+
$(let _ = $arg;)*
2823+
2824+
$compiletime
2825+
}
2826+
2827+
const_eval_select(($($val,)*), compiletime, runtime)
2828+
}},
2829+
// We support leaving away the `val` expressions for *all* arguments
2830+
// (but not for *some* arguments, that's too tricky).
2831+
(
2832+
$(#[$attr:meta])*
2833+
($($arg:ident : $ty:ty),* $(,)?) -> $ret:ty:
2834+
if const
2835+
$(#[$compiletime_attr:meta])* $compiletime:block
2836+
else
2837+
$(#[$runtime_attr:meta])* $runtime:block
2838+
) => {
2839+
$crate::intrinsics::const_eval_select!(
2840+
$(#[$attr])*
2841+
($($arg : $ty = $arg),*) -> $ret:
2842+
if const
2843+
$(#[$compiletime_attr])* $compiletime
2844+
else
2845+
$(#[$runtime_attr])* $runtime
2846+
)
2847+
},
2848+
}
2849+
27912850
/// Returns whether the argument's value is statically known at
27922851
/// compile-time.
27932852
///
@@ -3734,14 +3793,15 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
37343793
fn miri_promise_symbolic_alignment(ptr: *const (), align: usize);
37353794
}
37363795

3737-
fn runtime(ptr: *const (), align: usize) {
3738-
// SAFETY: this call is always safe.
3739-
unsafe {
3740-
miri_promise_symbolic_alignment(ptr, align);
3796+
const_eval_select!(
3797+
(ptr: *const (), align: usize) -> ():
3798+
if const {
3799+
// Do nothing.
3800+
} else {
3801+
// SAFETY: this call is always safe.
3802+
unsafe {
3803+
miri_promise_symbolic_alignment(ptr, align);
3804+
}
37413805
}
3742-
}
3743-
3744-
const fn compiletime(_ptr: *const (), _align: usize) {}
3745-
3746-
const_eval_select((ptr, align), compiletime, runtime);
3806+
)
37473807
}

library/core/src/macros/mod.rs

+10-13
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,6 @@ macro_rules! panic {
2424
#[doc(hidden)]
2525
pub macro const_panic {
2626
($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-
3927
// Wrap call to `const_eval_select` in a function so that we can
4028
// add the `rustc_allow_const_fn_unstable`. This is okay to do
4129
// because both variants will panic, just with different messages.
@@ -44,7 +32,16 @@ pub macro const_panic {
4432
#[track_caller]
4533
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
4634
const fn do_panic($($arg: $ty),*) -> ! {
47-
$crate::intrinsics::const_eval_select(($($arg),* ,), compiletime, runtime)
35+
$crate::intrinsics::const_eval_select!(
36+
#[inline]
37+
#[track_caller]
38+
($($arg: $ty),*) -> !:
39+
if const {
40+
$crate::panic!($const_msg)
41+
} else {
42+
$crate::panic!($runtime_msg)
43+
}
44+
)
4845
}
4946

5047
do_panic($($val),*)

0 commit comments

Comments
 (0)