about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-11-03 16:20:51 +0000
committerbors <bors@rust-lang.org>2024-11-03 16:20:51 +0000
commite3a918ece026cec748fc64af5b4983095b46097e (patch)
tree342db9dd6cf4d8cf8a1d4a79c6df4d8dea178e46
parent7028d9318fadc20e5e3058d52e44d785d31a6aaa (diff)
parentbc757f9034c75a0e812c637f97432b222a150781 (diff)
downloadrust-e3a918ece026cec748fc64af5b4983095b46097e.tar.gz
rust-e3a918ece026cec748fc64af5b4983095b46097e.zip
Auto merge of #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`
-rw-r--r--library/core/src/char/methods.rs43
-rw-r--r--library/core/src/macros/mod.rs61
-rw-r--r--library/core/src/num/f128.rs20
-rw-r--r--library/core/src/num/f16.rs20
-rw-r--r--library/core/src/num/f32.rs19
-rw-r--r--library/core/src/num/f64.rs19
-rw-r--r--library/core/src/num/mod.rs21
-rw-r--r--library/core/src/slice/index.rs68
8 files changed, 142 insertions, 129 deletions
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index c37b9aa2aae..701e34b135e 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -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;
@@ -1773,17 +1773,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, ..]) => {
@@ -1804,8 +1794,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) }
@@ -1826,15 +1823,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, ..]) => {
@@ -1845,8 +1833,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) }
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 771c2d31b60..9a91ff82acd 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -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
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index e8161cce2fe..7709e7de01b 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -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;
         }
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index 8b3f3b7d19b..eb0225c58b8 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -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;
         }
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 2672fe773c2..686a6c50927 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -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;
 
@@ -1407,16 +1408,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;
         }
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 86082650e55..798cb4b1b5c 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -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;
 
@@ -1407,16 +1408,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;
         }
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 9e0af8b2ace..f4930ca5c7d 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -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};
@@ -1452,24 +1453,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 {
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 231ab7396ad..ebb4bdb1449 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -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)]