Skip to content

Commit 6a9bdd0

Browse files
committed
add const_panic macro to make it easier to fall back to non-formatting panic in const
1 parent 07cbbdd commit 6a9bdd0

File tree

8 files changed

+139
-129
lines changed

8 files changed

+139
-129
lines changed

library/core/src/char/methods.rs

+19-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! impl char {}
22
33
use super::*;
4-
use crate::intrinsics::const_eval_select;
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;
@@ -1774,17 +1774,7 @@ const fn len_utf16(code: u32) -> usize {
17741774
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))]
17751775
#[doc(hidden)]
17761776
#[inline]
1777-
#[rustc_allow_const_fn_unstable(const_eval_select)]
17781777
pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
1779-
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
1780-
// Note that we cannot format in constant expressions.
1781-
panic!("encode_utf8: buffer does not have enough bytes to encode code point");
1782-
}
1783-
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
1784-
panic!(
1785-
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1786-
);
1787-
}
17881778
let len = len_utf8(code);
17891779
match (len, &mut *dst) {
17901780
(1, [a, ..]) => {
@@ -1805,8 +1795,15 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
18051795
*c = (code >> 6 & 0x3F) as u8 | TAG_CONT;
18061796
*d = (code & 0x3F) as u8 | TAG_CONT;
18071797
}
1808-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1809-
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
1798+
_ => {
1799+
const_panic!(
1800+
"encode_utf8: buffer does not have enough bytes to encode code point",
1801+
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1802+
code: u32 = code,
1803+
len: usize = len,
1804+
dst_len: usize = dst.len(),
1805+
)
1806+
}
18101807
};
18111808
// SAFETY: `<&mut [u8]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
18121809
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] {
18271824
#[doc(hidden)]
18281825
#[inline]
18291826
pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
1830-
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
1831-
// Note that we cannot format in constant expressions.
1832-
panic!("encode_utf16: buffer does not have enough bytes to encode code point");
1833-
}
1834-
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
1835-
panic!(
1836-
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1837-
);
1838-
}
18391827
let len = len_utf16(code);
18401828
match (len, &mut *dst) {
18411829
(1, [a, ..]) => {
@@ -1846,8 +1834,15 @@ pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
18461834
*a = (code >> 10) as u16 | 0xD800;
18471835
*b = (code & 0x3FF) as u16 | 0xDC00;
18481836
}
1849-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1850-
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
1837+
_ => {
1838+
const_panic!(
1839+
"encode_utf16: buffer does not have enough bytes to encode code point",
1840+
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1841+
code: u32 = code,
1842+
len: usize = len,
1843+
dst_len: usize = dst.len(),
1844+
)
1845+
}
18511846
};
18521847
// SAFETY: `<&mut [u16]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
18531848
unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) }

library/core/src/num/f128.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::convert::FloatToInt;
1616
use crate::intrinsics;
1717
use crate::mem;
1818
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
1920

2021
/// Basic mathematical constants.
2122
#[unstable(feature = "f128", issue = "116909")]
@@ -1263,17 +1264,14 @@ impl f128 {
12631264
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
12641265
#[must_use = "method returns a new number and does not mutate the original value"]
12651266
pub const fn clamp(mut self, min: f128, max: f128) -> f128 {
1266-
#[inline] // inline to avoid LLVM crash
1267-
const fn assert_at_const(min: f128, max: f128) {
1268-
// Note that we cannot format in constant expressions.
1269-
assert!(min <= max, "min > max, or either was NaN");
1270-
}
1271-
#[inline] // inline to avoid codegen regression
1272-
fn assert_at_rt(min: f128, max: f128) {
1273-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1274-
}
1275-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1276-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1267+
const_assert!(
1268+
min <= max,
1269+
"min > max, or either was NaN",
1270+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1271+
min: f128,
1272+
max: f128,
1273+
);
1274+
12771275
if self < min {
12781276
self = min;
12791277
}

library/core/src/num/f16.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::convert::FloatToInt;
1616
use crate::intrinsics;
1717
use crate::mem;
1818
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
1920

2021
/// Basic mathematical constants.
2122
#[unstable(feature = "f16", issue = "116909")]
@@ -1238,17 +1239,14 @@ impl f16 {
12381239
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
12391240
#[must_use = "method returns a new number and does not mutate the original value"]
12401241
pub const fn clamp(mut self, min: f16, max: f16) -> f16 {
1241-
#[inline] // inline to avoid LLVM crash
1242-
const fn assert_at_const(min: f16, max: f16) {
1243-
// Note that we cannot format in constant expressions.
1244-
assert!(min <= max, "min > max, or either was NaN");
1245-
}
1246-
#[inline] // inline to avoid codegen regression
1247-
fn assert_at_rt(min: f16, max: f16) {
1248-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1249-
}
1250-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1251-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1242+
const_assert!(
1243+
min <= max,
1244+
"min > max, or either was NaN",
1245+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1246+
min: f16,
1247+
max: f16,
1248+
);
1249+
12521250
if self < min {
12531251
self = min;
12541252
}

library/core/src/num/f32.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::convert::FloatToInt;
1616
use crate::intrinsics;
1717
use crate::mem;
1818
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
1920

2021
/// The radix or base of the internal representation of `f32`.
2122
/// Use [`f32::RADIX`] instead.
@@ -1409,16 +1410,14 @@ impl f32 {
14091410
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
14101411
#[inline]
14111412
pub const fn clamp(mut self, min: f32, max: f32) -> f32 {
1412-
const fn assert_at_const(min: f32, max: f32) {
1413-
// Note that we cannot format in constant expressions.
1414-
assert!(min <= max, "min > max, or either was NaN");
1415-
}
1416-
#[inline] // inline to avoid codegen regression
1417-
fn assert_at_rt(min: f32, max: f32) {
1418-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1419-
}
1420-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1421-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1413+
const_assert!(
1414+
min <= max,
1415+
"min > max, or either was NaN",
1416+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1417+
min: f32,
1418+
max: f32,
1419+
);
1420+
14221421
if self < min {
14231422
self = min;
14241423
}

library/core/src/num/f64.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::convert::FloatToInt;
1616
use crate::intrinsics;
1717
use crate::mem;
1818
use crate::num::FpCategory;
19+
use crate::panic::const_assert;
1920

2021
/// The radix or base of the internal representation of `f64`.
2122
/// Use [`f64::RADIX`] instead.
@@ -1409,16 +1410,14 @@ impl f64 {
14091410
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
14101411
#[inline]
14111412
pub const fn clamp(mut self, min: f64, max: f64) -> f64 {
1412-
const fn assert_at_const(min: f64, max: f64) {
1413-
// Note that we cannot format in constant expressions.
1414-
assert!(min <= max, "min > max, or either was NaN");
1415-
}
1416-
#[inline] // inline to avoid codegen regression
1417-
fn assert_at_rt(min: f64, max: f64) {
1418-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1419-
}
1420-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1421-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1413+
const_assert!(
1414+
min <= max,
1415+
"min > max, or either was NaN",
1416+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1417+
min: f64,
1418+
max: f64,
1419+
);
1420+
14221421
if self < min {
14231422
self = min;
14241423
}

library/core/src/num/mod.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

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

1463-
#[track_caller]
1464-
const fn from_str_radix_panic_ct(_radix: u32) -> ! {
1465-
panic!("from_str_radix_int: must lie in the range `[2, 36]`");
1466-
}
1467-
1468-
#[track_caller]
1469-
fn from_str_radix_panic_rt(radix: u32) -> ! {
1470-
panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix);
1471-
}
1472-
14731464
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
14741465
#[cfg_attr(feature = "panic_immediate_abort", inline)]
14751466
#[cold]
14761467
#[track_caller]
1477-
#[rustc_allow_const_fn_unstable(const_eval_select)]
1478-
const fn from_str_radix_panic(radix: u32) {
1479-
// The only difference between these two functions is their panic message.
1480-
intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt);
1468+
const fn from_str_radix_panic(radix: u32) -> ! {
1469+
const_panic!(
1470+
"from_str_radix_int: must lie in the range `[2, 36]`",
1471+
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
1472+
radix: u32 = radix,
1473+
)
14811474
}
14821475

14831476
macro_rules! from_str_radix {

library/core/src/panic.rs

+58
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,61 @@ pub unsafe trait PanicPayload: crate::fmt::Display {
189189
None
190190
}
191191
}
192+
193+
/// Helper macro for panicking in a `const fn`.
194+
/// Invoke as:
195+
/// ```
196+
/// const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar);
197+
/// ```
198+
/// where the first message will be printed in const-eval,
199+
/// and the second message will be printed at runtime.
200+
// All uses of this macro are FIXME(const-hack).
201+
#[unstable(feature = "panic_internals", issue = "none")]
202+
#[doc(hidden)]
203+
pub macro const_panic {
204+
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{
205+
#[inline]
206+
#[track_caller]
207+
fn runtime($($arg: $ty),*) -> ! {
208+
$crate::panic!($runtime_msg);
209+
}
210+
211+
#[inline]
212+
#[track_caller]
213+
const fn compiletime($(_: $ty),*) -> ! {
214+
$crate::panic!($const_msg);
215+
}
216+
217+
// Wrap call to `const_eval_select` in a function so that we can
218+
// add the `rustc_allow_const_fn_unstable`. This is okay to do
219+
// because both variants will panic, just with different messages.
220+
#[rustc_allow_const_fn_unstable(const_eval_select)]
221+
#[inline(always)]
222+
#[track_caller]
223+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))]
224+
const fn do_panic($($arg: $ty),*) -> ! {
225+
$crate::intrinsics::const_eval_select(($($arg),* ,), compiletime, runtime)
226+
}
227+
228+
do_panic($($val),*)
229+
}},
230+
// We support leaving away the `val` expressions for *all* arguments
231+
// (but not for *some* arguments, that's too tricky).
232+
($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => {
233+
$crate::panic::const_panic!(
234+
$const_msg,
235+
$runtime_msg,
236+
$($arg: $ty = $arg),*
237+
)
238+
},
239+
}
240+
241+
#[unstable(feature = "panic_internals", issue = "none")]
242+
#[doc(hidden)]
243+
pub macro const_assert {
244+
($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{
245+
if !$condition {
246+
$crate::panic::const_panic!($const_msg, $runtime_msg, $($arg)*)
247+
}
248+
}}
249+
}

0 commit comments

Comments
 (0)