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.
This commit is contained in:
Nilstrieb 2024-03-24 13:53:55 +01:00 committed by Lukas Wirth
parent 062e1b9b81
commit 805f569adc
2 changed files with 63 additions and 8 deletions

View file

@ -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()))
@ -87,6 +88,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(

View file

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