Skip to content

Commit 7260ff8

Browse files
NoratriebVeykril
authored andcommitted
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.
1 parent dacfa72 commit 7260ff8

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

crates/hir-ty/src/mir/eval/tests.rs

+37
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
3131
db.trait_environment(func_id.into()),
3232
)
3333
.map_err(|e| MirEvalError::MirLowerError(func_id, e))?;
34+
3435
let (result, output) = interpret_mir(db, body, false, None);
3536
result?;
3637
Ok((output.stdout().into_owned(), output.stderr().into_owned()))
@@ -87,6 +88,42 @@ fn main() {
8788
);
8889
}
8990

91+
#[test]
92+
fn panic_fmt() {
93+
// panic!
94+
// -> panic_2021 (builtin macro redirection)
95+
// -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection)
96+
// -> core::panicking::const_panic_fmt
97+
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
98+
// -> Err(ConstEvalError::Panic)
99+
check_pass(
100+
r#"
101+
//- minicore: fmt, panic
102+
fn main() {
103+
panic!("hello, world!");
104+
}
105+
"#,
106+
);
107+
panic!("a");
108+
}
109+
110+
#[test]
111+
fn panic_display() {
112+
// panic!
113+
// -> panic_2021 (builtin macro redirection)
114+
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
115+
// -> Err(ConstEvalError::Panic)
116+
check_pass(
117+
r#"
118+
//- minicore: fmt, panic
119+
120+
fn main() {
121+
panic!("{}", "hello, world!");
122+
}
123+
"#,
124+
);
125+
}
126+
90127
#[test]
91128
fn drop_basic() {
92129
check_pass(

crates/test-utils/src/minicore.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -1356,18 +1356,36 @@ pub mod iter {
13561356
// region:panic
13571357
mod panic {
13581358
pub macro panic_2021 {
1359-
() => (
1360-
$crate::panicking::panic("explicit panic")
1361-
),
1362-
($($t:tt)+) => (
1363-
$crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
1364-
),
1359+
() => ({
1360+
const fn panic_cold_explicit() -> ! {
1361+
$crate::panicking::panic_explicit()
1362+
}
1363+
panic_cold_explicit();
1364+
}),
1365+
// Special-case the single-argument case for const_panic.
1366+
("{}", $arg:expr $(,)?) => ({
1367+
#[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
1368+
const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! {
1369+
loop {}
1370+
}
1371+
panic_cold_display(&$arg);
1372+
}),
1373+
($($t:tt)+) => ({
1374+
// Semicolon to prevent temporaries inside the formatting machinery from
1375+
// being considered alive in the caller after the panic_fmt call.
1376+
$crate::panicking::panic_fmt($crate::const_format_args!($($t)+));
1377+
}),
13651378
}
13661379
}
13671380

13681381
mod panicking {
1369-
#[lang = "panic_fmt"]
1370-
pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! {
1382+
#[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
1383+
pub const fn panic_display<T: fmt::Display>(x: &T) -> ! {
1384+
panic_fmt(format_args!("{}", *x));
1385+
}
1386+
1387+
#[lang = "panic_fmt"] // needed for const-evaluated panics
1388+
pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
13711389
loop {}
13721390
}
13731391

0 commit comments

Comments
 (0)