Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add const_panic macro to make it easier to fall back to non-formatting panic in const #132542

Merged
merged 1 commit into from
Nov 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 19 additions & 24 deletions 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::intrinsics::const_eval_select;
use crate::macros::const_panic;
use crate::slice;
use crate::str::from_utf8_unchecked_mut;
use crate::unicode::printable::is_printable;
@@ -1774,17 +1774,7 @@ const fn len_utf16(code: u32) -> usize {
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))]
#[doc(hidden)]
#[inline]
#[rustc_allow_const_fn_unstable(const_eval_select)]
pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
// Note that we cannot format in constant expressions.
panic!("encode_utf8: buffer does not have enough bytes to encode code point");
}
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
panic!(
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
);
}
let len = len_utf8(code);
match (len, &mut *dst) {
(1, [a, ..]) => {
@@ -1805,8 +1795,15 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
*c = (code >> 6 & 0x3F) as u8 | TAG_CONT;
*d = (code & 0x3F) as u8 | TAG_CONT;
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
_ => {
const_panic!(
"encode_utf8: buffer does not have enough bytes to encode code point",
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
code: u32 = code,
len: usize = len,
dst_len: usize = dst.len(),
)
}
};
// SAFETY: `<&mut [u8]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) }
@@ -1827,15 +1824,6 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
#[doc(hidden)]
#[inline]
pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
// Note that we cannot format in constant expressions.
panic!("encode_utf16: buffer does not have enough bytes to encode code point");
}
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
panic!(
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
);
}
let len = len_utf16(code);
match (len, &mut *dst) {
(1, [a, ..]) => {
@@ -1846,8 +1834,15 @@ pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
*a = (code >> 10) as u16 | 0xD800;
*b = (code & 0x3FF) as u16 | 0xDC00;
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
_ => {
const_panic!(
"encode_utf16: buffer does not have enough bytes to encode code point",
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
code: u32 = code,
len: usize = len,
dst_len: usize = dst.len(),
)
}
};
// SAFETY: `<&mut [u16]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) }
61 changes: 61 additions & 0 deletions library/core/src/macros/mod.rs
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, maybe I shouldn't put them in this file, since this is generally for macros that are stable / intended to be stabilized?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you have it in core::panic before? I guess that could make sense, considering most of the macros in core::panic are doc(hidden) and meant for internal use.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've moved them in #132571.

Original file line number Diff line number Diff line change
@@ -12,6 +12,54 @@ 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),* $(,)?) => {{
#[inline]
#[track_caller]
fn runtime($($arg: $ty),*) -> ! {
$crate::panic!($runtime_msg);
}

#[inline]
#[track_caller]
const fn compiletime($(_: $ty),*) -> ! {
$crate::panic!($const_msg);
}

// 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(($($arg),* ,), compiletime, runtime)
}

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
@@ -196,6 +244,19 @@ 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
20 changes: 9 additions & 11 deletions library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;

@@ -1263,17 +1264,14 @@ impl f128 {
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn clamp(mut self, min: f128, max: f128) -> f128 {
#[inline] // inline to avoid LLVM crash
const fn assert_at_const(min: f128, max: f128) {
// Note that we cannot format in constant expressions.
assert!(min <= max, "min > max, or either was NaN");
}
#[inline] // inline to avoid codegen regression
fn assert_at_rt(min: f128, max: f128) {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
const_assert!(
min <= max,
"min > max, or either was NaN",
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
min: f128,
max: f128,
);

if self < min {
self = min;
}
20 changes: 9 additions & 11 deletions library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;

@@ -1238,17 +1239,14 @@ impl f16 {
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn clamp(mut self, min: f16, max: f16) -> f16 {
#[inline] // inline to avoid LLVM crash
const fn assert_at_const(min: f16, max: f16) {
// Note that we cannot format in constant expressions.
assert!(min <= max, "min > max, or either was NaN");
}
#[inline] // inline to avoid codegen regression
fn assert_at_rt(min: f16, max: f16) {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
const_assert!(
min <= max,
"min > max, or either was NaN",
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
min: f16,
max: f16,
);

if self < min {
self = min;
}
19 changes: 9 additions & 10 deletions library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;

@@ -1409,16 +1410,14 @@ impl f32 {
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[inline]
pub const fn clamp(mut self, min: f32, max: f32) -> f32 {
const fn assert_at_const(min: f32, max: f32) {
// Note that we cannot format in constant expressions.
assert!(min <= max, "min > max, or either was NaN");
}
#[inline] // inline to avoid codegen regression
fn assert_at_rt(min: f32, max: f32) {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
const_assert!(
min <= max,
"min > max, or either was NaN",
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
min: f32,
max: f32,
);

if self < min {
self = min;
}
19 changes: 9 additions & 10 deletions library/core/src/num/f64.rs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
use crate::convert::FloatToInt;
#[cfg(not(test))]
use crate::intrinsics;
use crate::macros::const_assert;
use crate::mem;
use crate::num::FpCategory;

@@ -1409,16 +1410,14 @@ impl f64 {
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[inline]
pub const fn clamp(mut self, min: f64, max: f64) -> f64 {
const fn assert_at_const(min: f64, max: f64) {
// Note that we cannot format in constant expressions.
assert!(min <= max, "min > max, or either was NaN");
}
#[inline] // inline to avoid codegen regression
fn assert_at_rt(min: f64, max: f64) {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
}
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
const_assert!(
min <= max,
"min > max, or either was NaN",
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
min: f64,
max: f64,
);

if self < min {
self = min;
}
21 changes: 7 additions & 14 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

#![stable(feature = "rust1", since = "1.0.0")]

use crate::macros::const_panic;
use crate::str::FromStr;
use crate::ub_checks::assert_unsafe_precondition;
use crate::{ascii, intrinsics, mem};
@@ -1460,24 +1461,16 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8])
radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize
}

#[track_caller]
const fn from_str_radix_panic_ct(_radix: u32) -> ! {
panic!("from_str_radix_int: must lie in the range `[2, 36]`");
}

#[track_caller]
fn from_str_radix_panic_rt(radix: u32) -> ! {
panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix);
}

#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[cold]
#[track_caller]
#[rustc_allow_const_fn_unstable(const_eval_select)]
const fn from_str_radix_panic(radix: u32) {
// The only difference between these two functions is their panic message.
intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt);
const fn from_str_radix_panic(radix: u32) -> ! {
const_panic!(
"from_str_radix_int: must lie in the range `[2, 36]`",
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
radix: u32 = radix,
)
}

macro_rules! from_str_radix {
68 changes: 19 additions & 49 deletions 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::intrinsics::const_eval_select;
use crate::macros::const_panic;
use crate::ub_checks::assert_unsafe_precondition;
use crate::{ops, range};

@@ -31,67 +31,37 @@ where
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_allow_const_fn_unstable(const_eval_select)]
const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
// FIXME(const-hack): once integer formatting in panics is possible, we
// should use the same implementation at compiletime and runtime.
const_eval_select((index, len), slice_start_index_len_fail_ct, slice_start_index_len_fail_rt)
}

#[inline]
#[track_caller]
fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range start index {index} out of range for slice of length {len}");
}

#[inline]
#[track_caller]
const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice start index is out of range for slice");
const_panic!(
"slice start index is out of range for slice",
"range start index {index} out of range for slice of length {len}",
index: usize,
len: usize,
)
}

#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_allow_const_fn_unstable(const_eval_select)]
const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
// FIXME(const-hack): once integer formatting in panics is possible, we
// should use the same implementation at compiletime and runtime.
const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt)
}

#[inline]
#[track_caller]
fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range end index {index} out of range for slice of length {len}");
}

#[inline]
#[track_caller]
const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice end index is out of range for slice");
const_panic!(
"slice end index is out of range for slice",
"range end index {index} out of range for slice of length {len}",
index: usize,
len: usize,
)
}

#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_allow_const_fn_unstable(const_eval_select)]
const fn slice_index_order_fail(index: usize, end: usize) -> ! {
// FIXME(const-hack): once integer formatting in panics is possible, we
// should use the same implementation at compiletime and runtime.
const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt)
}

#[inline]
#[track_caller]
fn slice_index_order_fail_rt(index: usize, end: usize) -> ! {
panic!("slice index starts at {index} but ends at {end}");
}

#[inline]
#[track_caller]
const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! {
panic!("slice index start is larger than end");
const_panic!(
"slice index start is larger than end",
"slice index starts at {index} but ends at {end}",
index: usize,
end: usize,
)
}

#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]