Skip to content

Commit 6f6b03f

Browse files
committed
Auto merge of #16935 - Nilstrieb:dont-panic, r=HKalbasi
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. The tests don't work yet, I probably missed something in minicore. fixes #16907 in my local testing, I also need to add a test for it
2 parents 3dfd4c1 + 2dfe7de commit 6f6b03f

File tree

3 files changed

+56
-40
lines changed

3 files changed

+56
-40
lines changed

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -2356,7 +2356,7 @@ impl Evaluator<'_> {
23562356

23572357
fn exec_fn_with_args(
23582358
&mut self,
2359-
def: FunctionId,
2359+
mut def: FunctionId,
23602360
args: &[IntervalAndTy],
23612361
generic_args: Substitution,
23622362
locals: &Locals,
@@ -2374,6 +2374,9 @@ impl Evaluator<'_> {
23742374
)? {
23752375
return Ok(None);
23762376
}
2377+
if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? {
2378+
def = redirect_def;
2379+
}
23772380
let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
23782381
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
23792382
MirOrDynIndex::Dyn(self_ty_idx) => {

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

+26-39
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::mir::eval::{
1313
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
1414
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
1515
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
16-
ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
16+
Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
1717
};
1818

1919
mod simd;
@@ -158,6 +158,25 @@ impl Evaluator<'_> {
158158
Ok(false)
159159
}
160160

161+
pub(super) fn detect_and_redirect_special_function(
162+
&mut self,
163+
def: FunctionId,
164+
) -> Result<Option<FunctionId>> {
165+
// `PanicFmt` is redirected to `ConstPanicFmt`
166+
if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) {
167+
let resolver =
168+
self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
169+
170+
let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) =
171+
self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt)
172+
else {
173+
not_supported!("const_panic_fmt lang item not found or not a function");
174+
};
175+
return Ok(Some(const_panic_fmt));
176+
}
177+
Ok(None)
178+
}
179+
161180
/// Clone has special impls for tuples and function pointers
162181
fn exec_clone(
163182
&mut self,
@@ -291,9 +310,14 @@ impl Evaluator<'_> {
291310
use LangItem::*;
292311
let candidate = self.db.lang_attr(def.into())?;
293312
// We want to execute these functions with special logic
294-
if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
313+
// `PanicFmt` is not detected here as it's redirected later.
314+
if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
295315
return Some(candidate);
296316
}
317+
if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
318+
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
319+
return Some(LangItem::BeginPanic);
320+
}
297321
None
298322
}
299323

@@ -309,43 +333,6 @@ impl Evaluator<'_> {
309333
let mut args = args.iter();
310334
match it {
311335
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
312-
PanicFmt => {
313-
let message = (|| {
314-
let resolver = self
315-
.db
316-
.crate_def_map(self.crate_id)
317-
.crate_root()
318-
.resolver(self.db.upcast());
319-
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
320-
self.db.upcast(),
321-
&hir_def::path::Path::from_known_path_with_no_generic(
322-
ModPath::from_segments(
323-
hir_expand::mod_path::PathKind::Abs,
324-
[name![std], name![fmt], name![format]],
325-
),
326-
),
327-
) else {
328-
not_supported!("std::fmt::format not found");
329-
};
330-
let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
331-
not_supported!("std::fmt::format is not a function")
332-
};
333-
let interval = self.interpret_mir(
334-
self.db
335-
.mir_body(format_fn.into())
336-
.map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
337-
args.map(|x| IntervalOrOwned::Owned(x.clone())),
338-
)?;
339-
let message_string = interval.get(self)?;
340-
let addr =
341-
Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
342-
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
343-
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
344-
.into_owned())
345-
})()
346-
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
347-
Err(MirEvalError::Panic(message))
348-
}
349336
SliceLen => {
350337
let arg = args.next().ok_or(MirEvalError::InternalError(
351338
"argument of <[T]>::len() is not provided".into(),

crates/ide/src/hover/tests.rs

+26
Original file line numberDiff line numberDiff line change
@@ -5105,6 +5105,32 @@ fn foo(e: E) {
51055105
);
51065106
}
51075107

5108+
#[test]
5109+
fn hover_const_value() {
5110+
check(
5111+
r#"
5112+
pub enum AA {
5113+
BB,
5114+
}
5115+
const CONST: AA = AA::BB;
5116+
pub fn the_function() -> AA {
5117+
CON$0ST
5118+
}
5119+
"#,
5120+
expect![[r#"
5121+
*CONST*
5122+
5123+
```rust
5124+
test
5125+
```
5126+
5127+
```rust
5128+
const CONST: AA = BB
5129+
```
5130+
"#]],
5131+
);
5132+
}
5133+
51085134
#[test]
51095135
fn array_repeat_exp() {
51105136
check(

0 commit comments

Comments
 (0)