Unnamed repository; edit this file 'description' to name the repository.
Handle panicking like rustc CTFE does
Instead of using `core::fmt::format` to format panic messages, which may in turn panic too and cause recursive panics and other messy things, redirect `panic_fmt` to `const_panic_fmt` like CTFE, which in turn goes to `panic_display` and does the things normally. See the tests for the full call stack.
Nilstrieb 2024-04-18
parent 062e1b9 · commit 805f569
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs37
-rw-r--r--crates/test-utils/src/minicore.rs34
2 files changed, 63 insertions, 8 deletions
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 381522c9ab..031ab51ed7 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
db.trait_environment(func_id.into()),
)
.map_err(|e| MirEvalError::MirLowerError(func_id, e))?;
+
let (result, output) = interpret_mir(db, body, false, None);
result?;
Ok((output.stdout().into_owned(), output.stderr().into_owned()))
@@ -88,6 +89,42 @@ fn main() {
}
#[test]
+fn panic_fmt() {
+ // panic!
+ // -> panic_2021 (builtin macro redirection)
+ // -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection)
+ // -> core::panicking::const_panic_fmt
+ // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
+ // -> Err(ConstEvalError::Panic)
+ check_pass(
+ r#"
+//- minicore: fmt, panic
+fn main() {
+ panic!("hello, world!");
+}
+ "#,
+ );
+ panic!("a");
+}
+
+#[test]
+fn panic_display() {
+ // panic!
+ // -> panic_2021 (builtin macro redirection)
+ // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
+ // -> Err(ConstEvalError::Panic)
+ check_pass(
+ r#"
+//- minicore: fmt, panic
+
+fn main() {
+ panic!("{}", "hello, world!");
+}
+ "#,
+ );
+}
+
+#[test]
fn drop_basic() {
check_pass(
r#"
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index f125792d12..15bee61381 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -1356,18 +1356,36 @@ pub mod iter {
// region:panic
mod panic {
pub macro panic_2021 {
- () => (
- $crate::panicking::panic("explicit panic")
- ),
- ($($t:tt)+) => (
- $crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
- ),
+ () => ({
+ const fn panic_cold_explicit() -> ! {
+ $crate::panicking::panic_explicit()
+ }
+ panic_cold_explicit();
+ }),
+ // Special-case the single-argument case for const_panic.
+ ("{}", $arg:expr $(,)?) => ({
+ #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
+ const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! {
+ loop {}
+ }
+ panic_cold_display(&$arg);
+ }),
+ ($($t:tt)+) => ({
+ // Semicolon to prevent temporaries inside the formatting machinery from
+ // being considered alive in the caller after the panic_fmt call.
+ $crate::panicking::panic_fmt($crate::const_format_args!($($t)+));
+ }),
}
}
mod panicking {
- #[lang = "panic_fmt"]
- pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! {
+ #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
+ pub const fn panic_display<T: fmt::Display>(x: &T) -> ! {
+ panic_fmt(format_args!("{}", *x));
+ }
+
+ #[lang = "panic_fmt"] // needed for const-evaluated panics
+ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
loop {}
}