Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ed4f110

Browse files
committedNov 3, 2024
Auto merge of rust-lang#132542 - RalfJung:const_panic, r=tgross35
add const_panic macro to make it easier to fall back to non-formatting panic in const Suggested by `@tgross35` r? `@tgross35`
2 parents 9e57964 + ff9178b commit ed4f110

File tree

8 files changed

+142
-129
lines changed

8 files changed

+142
-129
lines changed
 

‎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::macros::const_panic;
55
use crate::slice;
66
use crate::str::from_utf8_unchecked_mut;
77
use crate::unicode::printable::is_printable;
@@ -1773,17 +1773,7 @@ const fn len_utf16(code: u32) -> usize {
17731773
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))]
17741774
#[doc(hidden)]
17751775
#[inline]
1776-
#[rustc_allow_const_fn_unstable(const_eval_select)]
17771776
pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
1778-
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
1779-
// Note that we cannot format in constant expressions.
1780-
panic!("encode_utf8: buffer does not have enough bytes to encode code point");
1781-
}
1782-
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
1783-
panic!(
1784-
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1785-
);
1786-
}
17871777
let len = len_utf8(code);
17881778
match (len, &mut *dst) {
17891779
(1, [a, ..]) => {
@@ -1804,8 +1794,15 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
18041794
*c = (code >> 6 & 0x3F) as u8 | TAG_CONT;
18051795
*d = (code & 0x3F) as u8 | TAG_CONT;
18061796
}
1807-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1808-
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
1797+
_ => {
1798+
const_panic!(
1799+
"encode_utf8: buffer does not have enough bytes to encode code point",
1800+
"encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1801+
code: u32 = code,
1802+
len: usize = len,
1803+
dst_len: usize = dst.len(),
1804+
)
1805+
}
18091806
};
18101807
// SAFETY: `<&mut [u8]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
18111808
unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) }
@@ -1826,15 +1823,6 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
18261823
#[doc(hidden)]
18271824
#[inline]
18281825
pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
1829-
const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) {
1830-
// Note that we cannot format in constant expressions.
1831-
panic!("encode_utf16: buffer does not have enough bytes to encode code point");
1832-
}
1833-
fn panic_at_rt(code: u32, len: usize, dst_len: usize) {
1834-
panic!(
1835-
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1836-
);
1837-
}
18381826
let len = len_utf16(code);
18391827
match (len, &mut *dst) {
18401828
(1, [a, ..]) => {
@@ -1845,8 +1833,15 @@ pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] {
18451833
*a = (code >> 10) as u16 | 0xD800;
18461834
*b = (code & 0x3FF) as u16 | 0xDC00;
18471835
}
1848-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1849-
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
1836+
_ => {
1837+
const_panic!(
1838+
"encode_utf16: buffer does not have enough bytes to encode code point",
1839+
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1840+
code: u32 = code,
1841+
len: usize = len,
1842+
dst_len: usize = dst.len(),
1843+
)
1844+
}
18501845
};
18511846
// SAFETY: `<&mut [u16]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
18521847
unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) }

‎core/src/macros/mod.rs

+61
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,54 @@ 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+
1563
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
1664
///
1765
/// Assertions are always checked in both debug and release builds, and cannot
@@ -196,6 +244,19 @@ pub macro assert_matches {
196244
},
197245
}
198246

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+
199260
/// A macro for defining `#[cfg]` match-like statements.
200261
///
201262
/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of

‎core/src/num/f128.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17+
use crate::macros::const_assert;
1718
use crate::mem;
1819
use crate::num::FpCategory;
1920

@@ -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
}

‎core/src/num/f16.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17+
use crate::macros::const_assert;
1718
use crate::mem;
1819
use crate::num::FpCategory;
1920

@@ -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
}

‎core/src/num/f32.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17+
use crate::macros::const_assert;
1718
use crate::mem;
1819
use crate::num::FpCategory;
1920

@@ -1407,16 +1408,14 @@ impl f32 {
14071408
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
14081409
#[inline]
14091410
pub const fn clamp(mut self, min: f32, max: f32) -> f32 {
1410-
const fn assert_at_const(min: f32, max: f32) {
1411-
// Note that we cannot format in constant expressions.
1412-
assert!(min <= max, "min > max, or either was NaN");
1413-
}
1414-
#[inline] // inline to avoid codegen regression
1415-
fn assert_at_rt(min: f32, max: f32) {
1416-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1417-
}
1418-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1419-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1411+
const_assert!(
1412+
min <= max,
1413+
"min > max, or either was NaN",
1414+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1415+
min: f32,
1416+
max: f32,
1417+
);
1418+
14201419
if self < min {
14211420
self = min;
14221421
}

‎core/src/num/f64.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use crate::convert::FloatToInt;
1515
#[cfg(not(test))]
1616
use crate::intrinsics;
17+
use crate::macros::const_assert;
1718
use crate::mem;
1819
use crate::num::FpCategory;
1920

@@ -1407,16 +1408,14 @@ impl f64 {
14071408
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
14081409
#[inline]
14091410
pub const fn clamp(mut self, min: f64, max: f64) -> f64 {
1410-
const fn assert_at_const(min: f64, max: f64) {
1411-
// Note that we cannot format in constant expressions.
1412-
assert!(min <= max, "min > max, or either was NaN");
1413-
}
1414-
#[inline] // inline to avoid codegen regression
1415-
fn assert_at_rt(min: f64, max: f64) {
1416-
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
1417-
}
1418-
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1419-
intrinsics::const_eval_select((min, max), assert_at_const, assert_at_rt);
1411+
const_assert!(
1412+
min <= max,
1413+
"min > max, or either was NaN",
1414+
"min > max, or either was NaN. min = {min:?}, max = {max:?}",
1415+
min: f64,
1416+
max: f64,
1417+
);
1418+
14201419
if self < min {
14211420
self = min;
14221421
}

‎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::macros::const_panic;
56
use crate::str::FromStr;
67
use crate::ub_checks::assert_unsafe_precondition;
78
use crate::{ascii, intrinsics, mem};
@@ -1452,24 +1453,16 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8])
14521453
radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize
14531454
}
14541455

1455-
#[track_caller]
1456-
const fn from_str_radix_panic_ct(_radix: u32) -> ! {
1457-
panic!("from_str_radix_int: must lie in the range `[2, 36]`");
1458-
}
1459-
1460-
#[track_caller]
1461-
fn from_str_radix_panic_rt(radix: u32) -> ! {
1462-
panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix);
1463-
}
1464-
14651456
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
14661457
#[cfg_attr(feature = "panic_immediate_abort", inline)]
14671458
#[cold]
14681459
#[track_caller]
1469-
#[rustc_allow_const_fn_unstable(const_eval_select)]
1470-
const fn from_str_radix_panic(radix: u32) {
1471-
// The only difference between these two functions is their panic message.
1472-
intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt);
1460+
const fn from_str_radix_panic(radix: u32) -> ! {
1461+
const_panic!(
1462+
"from_str_radix_int: must lie in the range `[2, 36]`",
1463+
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
1464+
radix: u32 = radix,
1465+
)
14731466
}
14741467

14751468
macro_rules! from_str_radix {
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.