Skip to content

Commit

Permalink
move const_panic/assert macros into core::panic module (since they ar…
Browse files Browse the repository at this point in the history
…e just internal helpers)
  • Loading branch information
RalfJung committed Nov 4, 2024
1 parent 5605568 commit d731552
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 197 deletions.
2 changes: 1 addition & 1 deletion library/core/src/char/methods.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! impl char {}
use super::*;
use crate::macros::const_panic;
use crate::panic::const_panic;
use crate::slice;
use crate::str::from_utf8_unchecked_mut;
use crate::unicode::printable::is_printable;
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2803,7 +2803,7 @@ where
pub(crate) macro const_eval_select {
(
$(#[$attr:meta])*
($($arg:ident : $ty:ty = $val:expr),* $(,)?) $( -> $ret:ty )?:
($($arg:ident : $ty:ty = $val:expr),* $(,)?) $( -> $ret:ty )? :
if const
$(#[$compiletime_attr:meta])* $compiletime:block
else
Expand All @@ -2830,15 +2830,15 @@ pub(crate) macro const_eval_select {
// (but not for *some* arguments, that's too tricky).
(
$(#[$attr:meta])*
($($arg:ident : $ty:ty),* $(,)?) -> $ret:ty:
($($arg:ident : $ty:ty),* $(,)?) $( -> $ret:ty )? :
if const
$(#[$compiletime_attr:meta])* $compiletime:block
else
$(#[$runtime_attr:meta])* $runtime:block
) => {
$crate::intrinsics::const_eval_select!(
$(#[$attr])*
($($arg : $ty = $arg),*) -> $ret:
($($arg : $ty = $arg),*) $(-> $ret)? :
if const
$(#[$compiletime_attr])* $compiletime
else
Expand Down
58 changes: 0 additions & 58 deletions library/core/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,6 @@ macro_rules! panic {
};
}

/// Helper macro for panicking in a `const fn`.
/// Invoke as:
/// ```rust,ignore (just an example)
/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar);
/// ```
/// where the first message will be printed in const-eval,
/// and the second message will be printed at runtime.
// All uses of this macro are FIXME(const-hack).
#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
pub macro const_panic {
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{
// Wrap call to `const_eval_select` in a function so that we can
// add the `rustc_allow_const_fn_unstable`. This is okay to do
// because both variants will panic, just with different messages.
#[rustc_allow_const_fn_unstable(const_eval_select)]
#[inline(always)]
#[track_caller]
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
const fn do_panic($($arg: $ty),*) -> ! {
$crate::intrinsics::const_eval_select!(
#[inline]
#[track_caller]
($($arg: $ty),*) -> !:
if const {
$crate::panic!($const_msg)
} else {
$crate::panic!($runtime_msg)
}
)
}

do_panic($($val),*)
}},
// We support leaving away the `val` expressions for *all* arguments
// (but not for *some* arguments, that's too tricky).
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => {
$crate::macros::const_panic!(
$const_msg,
$runtime_msg,
$($arg: $ty = $arg),*
)
},
}

/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
///
/// Assertions are always checked in both debug and release builds, and cannot
Expand Down Expand Up @@ -241,19 +196,6 @@ pub macro assert_matches {
},
}

/// A version of `assert` that prints a non-formatting message in const contexts.
///
/// See [`const_panic!`].
#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
pub macro const_assert {
($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{
if !$crate::intrinsics::likely($condition) {
$crate::macros::const_panic!($const_msg, $runtime_msg, $($arg)*)
}
}}
}

/// A macro for defining `#[cfg]` match-like statements.
///
/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;
use crate::panic::const_assert;

/// Basic mathematical constants.
#[unstable(feature = "f128", issue = "116909")]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;
use crate::panic::const_assert;

/// Basic mathematical constants.
#[unstable(feature = "f16", issue = "116909")]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;
use crate::panic::const_assert;

/// The radix or base of the internal representation of `f32`.
/// Use [`f32::RADIX`] instead.
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;
use crate::panic::const_assert;

/// The radix or base of the internal representation of `f64`.
/// Use [`f64::RADIX`] instead.
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::macros::const_panic;
use crate::panic::const_panic;
use crate::str::FromStr;
use crate::ub_checks::assert_unsafe_precondition;
use crate::{ascii, intrinsics, mem};
Expand Down
58 changes: 58 additions & 0 deletions library/core/src/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,61 @@ pub unsafe trait PanicPayload: crate::fmt::Display {
None
}
}

/// Helper macro for panicking in a `const fn`.
/// Invoke as:
/// ```rust,ignore (just an example)
/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar);
/// ```
/// where the first message will be printed in const-eval,
/// and the second message will be printed at runtime.
// All uses of this macro are FIXME(const-hack).
#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
pub macro const_panic {
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{
// Wrap call to `const_eval_select` in a function so that we can
// add the `rustc_allow_const_fn_unstable`. This is okay to do
// because both variants will panic, just with different messages.
#[rustc_allow_const_fn_unstable(const_eval_select)]
#[inline(always)]
#[track_caller]
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
const fn do_panic($($arg: $ty),*) -> ! {
$crate::intrinsics::const_eval_select!(
#[inline]
#[track_caller]
($($arg: $ty),*) -> !:
if const {
$crate::panic!($const_msg)
} else {
$crate::panic!($runtime_msg)
}
)
}

do_panic($($val),*)
}},
// We support leaving away the `val` expressions for *all* arguments
// (but not for *some* arguments, that's too tricky).
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => {
$crate::panic::const_panic!(
$const_msg,
$runtime_msg,
$($arg: $ty = $arg),*
)
},
}

/// A version of `assert` that prints a non-formatting message in const contexts.
///
/// See [`const_panic!`].
#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
pub macro const_assert {
($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{
if !$crate::intrinsics::likely($condition) {
$crate::panic::const_panic!($const_msg, $runtime_msg, $($arg)*)
}
}}
}
152 changes: 75 additions & 77 deletions library/core/src/slice/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,89 +351,87 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool {
const fn is_ascii(s: &[u8]) -> bool {
// The runtime version behaves the same as the compiletime version, it's
// just more optimized.
return const_eval_select((s,), compiletime, runtime);

const fn compiletime(s: &[u8]) -> bool {
is_ascii_simple(s)
}

#[inline]
fn runtime(s: &[u8]) -> bool {
const USIZE_SIZE: usize = mem::size_of::<usize>();

let len = s.len();
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);

// If we wouldn't gain anything from the word-at-a-time implementation, fall
// back to a scalar loop.
//
// We also do this for architectures where `size_of::<usize>()` isn't
// sufficient alignment for `usize`, because it's a weird edge case.
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
return is_ascii_simple(s);
}
const_eval_select!(
(s: &[u8]) -> bool:
if const {
is_ascii_simple(s)
} else #[inline] {
const USIZE_SIZE: usize = mem::size_of::<usize>();

let len = s.len();
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);

// If we wouldn't gain anything from the word-at-a-time implementation, fall
// back to a scalar loop.
//
// We also do this for architectures where `size_of::<usize>()` isn't
// sufficient alignment for `usize`, because it's a weird edge case.
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
return is_ascii_simple(s);
}

// We always read the first word unaligned, which means `align_offset` is
// 0, we'd read the same value again for the aligned read.
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };
// We always read the first word unaligned, which means `align_offset` is
// 0, we'd read the same value again for the aligned read.
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };

let start = s.as_ptr();
// SAFETY: We verify `len < USIZE_SIZE` above.
let first_word = unsafe { (start as *const usize).read_unaligned() };
let start = s.as_ptr();
// SAFETY: We verify `len < USIZE_SIZE` above.
let first_word = unsafe { (start as *const usize).read_unaligned() };

if contains_nonascii(first_word) {
return false;
}
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
// above.
debug_assert!(offset_to_aligned <= len);

// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
// middle chunk of the slice.
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };

// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
let mut byte_pos = offset_to_aligned;

// Paranoia check about alignment, since we're about to do a bunch of
// unaligned loads. In practice this should be impossible barring a bug in
// `align_offset` though.
// While this method is allowed to spuriously fail in CTFE, if it doesn't
// have alignment information it should have given a `usize::MAX` for
// `align_offset` earlier, sending things through the scalar path instead of
// this one, so this check should pass if it's reachable.
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));

// Read subsequent words until the last aligned word, excluding the last
// aligned word by itself to be done in tail check later, to ensure that
// tail is always one `usize` at most to extra branch `byte_pos == len`.
while byte_pos < len - USIZE_SIZE {
// Sanity check that the read is in bounds
debug_assert!(byte_pos + USIZE_SIZE <= len);
// And that our assumptions about `byte_pos` hold.
debug_assert!(word_ptr.cast::<u8>() == start.wrapping_add(byte_pos));

// SAFETY: We know `word_ptr` is properly aligned (because of
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
let word = unsafe { word_ptr.read() };
if contains_nonascii(word) {
if contains_nonascii(first_word) {
return false;
}
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
// above.
debug_assert!(offset_to_aligned <= len);

// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
// middle chunk of the slice.
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };

// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
let mut byte_pos = offset_to_aligned;

// Paranoia check about alignment, since we're about to do a bunch of
// unaligned loads. In practice this should be impossible barring a bug in
// `align_offset` though.
// While this method is allowed to spuriously fail in CTFE, if it doesn't
// have alignment information it should have given a `usize::MAX` for
// `align_offset` earlier, sending things through the scalar path instead of
// this one, so this check should pass if it's reachable.
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));

// Read subsequent words until the last aligned word, excluding the last
// aligned word by itself to be done in tail check later, to ensure that
// tail is always one `usize` at most to extra branch `byte_pos == len`.
while byte_pos < len - USIZE_SIZE {
// Sanity check that the read is in bounds
debug_assert!(byte_pos + USIZE_SIZE <= len);
// And that our assumptions about `byte_pos` hold.
debug_assert!(word_ptr.cast::<u8>() == start.wrapping_add(byte_pos));

// SAFETY: We know `word_ptr` is properly aligned (because of
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
let word = unsafe { word_ptr.read() };
if contains_nonascii(word) {
return false;
}

byte_pos += USIZE_SIZE;
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
// after this `add`, `word_ptr` will be at most one-past-the-end.
word_ptr = unsafe { word_ptr.add(1) };
}

byte_pos += USIZE_SIZE;
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
// after this `add`, `word_ptr` will be at most one-past-the-end.
word_ptr = unsafe { word_ptr.add(1) };
}

// Sanity check to ensure there really is only one `usize` left. This should
// be guaranteed by our loop condition.
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);
// Sanity check to ensure there really is only one `usize` left. This should
// be guaranteed by our loop condition.
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);

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

!contains_nonascii(last_word)
}
!contains_nonascii(last_word)
}
)
}
2 changes: 1 addition & 1 deletion library/core/src/slice/index.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Indexing implementations for `[T]`.
use crate::macros::const_panic;
use crate::panic::const_panic;
use crate::ub_checks::assert_unsafe_precondition;
use crate::{ops, range};

Expand Down
Loading

0 comments on commit d731552

Please sign in to comment.