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:
parent
e265e3d518
commit
2dfe7de8b6
@ -2317,7 +2317,7 @@ fn get_mir_or_dyn_index(
|
|||||||
|
|
||||||
fn exec_fn_with_args(
|
fn exec_fn_with_args(
|
||||||
&mut self,
|
&mut self,
|
||||||
def: FunctionId,
|
mut def: FunctionId,
|
||||||
args: &[IntervalAndTy],
|
args: &[IntervalAndTy],
|
||||||
generic_args: Substitution,
|
generic_args: Substitution,
|
||||||
locals: &Locals,
|
locals: &Locals,
|
||||||
@ -2335,6 +2335,9 @@ fn exec_fn_with_args(
|
|||||||
)? {
|
)? {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? {
|
||||||
|
def = redirect_def;
|
||||||
|
}
|
||||||
let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
|
let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
|
||||||
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
|
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
|
||||||
MirOrDynIndex::Dyn(self_ty_idx) => {
|
MirOrDynIndex::Dyn(self_ty_idx) => {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
|
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
|
||||||
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
|
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
|
||||||
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
|
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
|
||||||
ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
|
Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod simd;
|
mod simd;
|
||||||
@ -158,6 +158,25 @@ pub(super) fn detect_and_exec_special_function(
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn detect_and_redirect_special_function(
|
||||||
|
&mut self,
|
||||||
|
def: FunctionId,
|
||||||
|
) -> Result<Option<FunctionId>> {
|
||||||
|
// `PanicFmt` is redirected to `ConstPanicFmt`
|
||||||
|
if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) {
|
||||||
|
let resolver =
|
||||||
|
self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
|
||||||
|
|
||||||
|
let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) =
|
||||||
|
self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt)
|
||||||
|
else {
|
||||||
|
not_supported!("const_panic_fmt lang item not found or not a function");
|
||||||
|
};
|
||||||
|
return Ok(Some(const_panic_fmt));
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Clone has special impls for tuples and function pointers
|
/// Clone has special impls for tuples and function pointers
|
||||||
fn exec_clone(
|
fn exec_clone(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -291,9 +310,14 @@ fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
|
|||||||
use LangItem::*;
|
use LangItem::*;
|
||||||
let candidate = self.db.lang_attr(def.into())?;
|
let candidate = self.db.lang_attr(def.into())?;
|
||||||
// We want to execute these functions with special logic
|
// We want to execute these functions with special logic
|
||||||
if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
|
// `PanicFmt` is not detected here as it's redirected later.
|
||||||
|
if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
|
||||||
return Some(candidate);
|
return Some(candidate);
|
||||||
}
|
}
|
||||||
|
if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
|
||||||
|
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
|
||||||
|
return Some(LangItem::BeginPanic);
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,43 +333,6 @@ fn exec_lang_item(
|
|||||||
let mut args = args.iter();
|
let mut args = args.iter();
|
||||||
match it {
|
match it {
|
||||||
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
|
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
|
||||||
PanicFmt => {
|
|
||||||
let message = (|| {
|
|
||||||
let resolver = self
|
|
||||||
.db
|
|
||||||
.crate_def_map(self.crate_id)
|
|
||||||
.crate_root()
|
|
||||||
.resolver(self.db.upcast());
|
|
||||||
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
|
|
||||||
self.db.upcast(),
|
|
||||||
&hir_def::path::Path::from_known_path_with_no_generic(
|
|
||||||
ModPath::from_segments(
|
|
||||||
hir_expand::mod_path::PathKind::Abs,
|
|
||||||
[name![std], name![fmt], name![format]],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
) else {
|
|
||||||
not_supported!("std::fmt::format not found");
|
|
||||||
};
|
|
||||||
let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
|
|
||||||
not_supported!("std::fmt::format is not a function")
|
|
||||||
};
|
|
||||||
let interval = self.interpret_mir(
|
|
||||||
self.db
|
|
||||||
.mir_body(format_fn.into())
|
|
||||||
.map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
|
|
||||||
args.map(|x| IntervalOrOwned::Owned(x.clone())),
|
|
||||||
)?;
|
|
||||||
let message_string = interval.get(self)?;
|
|
||||||
let addr =
|
|
||||||
Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
|
|
||||||
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
|
|
||||||
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
|
|
||||||
.into_owned())
|
|
||||||
})()
|
|
||||||
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
|
|
||||||
Err(MirEvalError::Panic(message))
|
|
||||||
}
|
|
||||||
SliceLen => {
|
SliceLen => {
|
||||||
let arg = args.next().ok_or(MirEvalError::InternalError(
|
let arg = args.next().ok_or(MirEvalError::InternalError(
|
||||||
"argument of <[T]>::len() is not provided".into(),
|
"argument of <[T]>::len() is not provided".into(),
|
||||||
|
@ -5105,6 +5105,32 @@ fn foo(e: E) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_const_value() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
pub enum AA {
|
||||||
|
BB,
|
||||||
|
}
|
||||||
|
const CONST: AA = AA::BB;
|
||||||
|
pub fn the_function() -> AA {
|
||||||
|
CON$0ST
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*CONST*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const CONST: AA = BB
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn array_repeat_exp() {
|
fn array_repeat_exp() {
|
||||||
check(
|
check(
|
||||||
|
Loading…
Reference in New Issue
Block a user