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
Show file tree
Hide file tree
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;
Expand Down Expand Up @@ -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, ..]) => {
Expand All @@ -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) }
Expand All @@ -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, ..]) => {
Expand All @@ -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) }
Expand Down
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
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
20 changes: 9 additions & 11 deletions library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
20 changes: 9 additions & 11 deletions library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
19 changes: 9 additions & 10 deletions library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
19 changes: 9 additions & 10 deletions library/core/src/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
21 changes: 7 additions & 14 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down
Loading
Loading