Skip to content

Commit 0391a7e

Browse files
committedNov 5, 2024·
Auto merge of rust-lang#132571 - RalfJung:const_eval_select_macro, r=<try>
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 96477c5 + 08f5449 commit 0391a7e

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

+72-10
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,67 @@ 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+
/// @capture { arg1: i32 = some_expr, arg2: T = other_expr } -> U:
2795+
/// if const #[attributes_for_const_arm] {
2796+
/// // Compile-time code goes here.
2797+
/// } else #[attributes_for_runtime_arm] {
2798+
/// // Run-time code goes here.
2799+
/// }
2800+
/// )
2801+
/// ```
2802+
/// The `@capture` block declares which surrounding variables / expressions can be
2803+
/// used inside the `if const`.
2804+
/// Note that the two arms of this `if` really each become their own function, which is why the
2805+
/// macro supports setting attributes for those functions. The runtime function is always
2806+
/// markes as `#[inline]`.
2807+
///
2808+
/// See [`const_eval_select()`] for the rules and requirements around that intrinsic.
2809+
pub(crate) macro const_eval_select {
2810+
(
2811+
@capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? :
2812+
if const
2813+
$(#[$compiletime_attr:meta])* $compiletime:block
2814+
else
2815+
$(#[$runtime_attr:meta])* $runtime:block
2816+
) => {{
2817+
$(#[$runtime_attr])*
2818+
#[inline]
2819+
fn runtime($($arg: $ty),*) $( -> $ret )? {
2820+
$runtime
2821+
}
2822+
2823+
$(#[$compiletime_attr])*
2824+
const fn compiletime($($arg: $ty),*) $( -> $ret )? {
2825+
// Don't warn if one of the arguments is unused.
2826+
$(let _ = $arg;)*
2827+
2828+
$compiletime
2829+
}
2830+
2831+
const_eval_select(($($val,)*), compiletime, runtime)
2832+
}},
2833+
// We support leaving away the `val` expressions for *all* arguments
2834+
// (but not for *some* arguments, that's too tricky).
2835+
(
2836+
@capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? :
2837+
if const
2838+
$(#[$compiletime_attr:meta])* $compiletime:block
2839+
else
2840+
$(#[$runtime_attr:meta])* $runtime:block
2841+
) => {
2842+
$crate::intrinsics::const_eval_select!(
2843+
@capture { $($arg : $ty = $arg),* } $(-> $ret)? :
2844+
if const
2845+
$(#[$compiletime_attr])* $compiletime
2846+
else
2847+
$(#[$runtime_attr])* $runtime
2848+
)
2849+
},
2850+
}
2851+
27912852
/// Returns whether the argument's value is statically known at
27922853
/// compile-time.
27932854
///
@@ -2830,7 +2891,7 @@ where
28302891
/// # Stability concerns
28312892
///
28322893
/// While it is safe to call, this intrinsic may behave differently in
2833-
/// a `const` context than otherwise. See the [`const_eval_select`]
2894+
/// a `const` context than otherwise. See the [`const_eval_select()`]
28342895
/// documentation for an explanation of the issues this can cause. Unlike
28352896
/// `const_eval_select`, this intrinsic isn't guaranteed to behave
28362897
/// deterministically even in a `const` context.
@@ -3734,14 +3795,15 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
37343795
fn miri_promise_symbolic_alignment(ptr: *const (), align: usize);
37353796
}
37363797

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

‎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};

0 commit comments

Comments
 (0)