Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle panicking like rustc CTFE does #16935

Merged
merged 1 commit into from Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/hir-ty/src/mir/eval.rs
Expand Up @@ -2317,7 +2317,7 @@ impl Evaluator<'_> {

fn exec_fn_with_args(
&mut self,
def: FunctionId,
mut def: FunctionId,
args: &[IntervalAndTy],
generic_args: Substitution,
locals: &Locals,
Expand All @@ -2335,6 +2335,9 @@ impl Evaluator<'_> {
)? {
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));
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
MirOrDynIndex::Dyn(self_ty_idx) => {
Expand Down
65 changes: 26 additions & 39 deletions crates/hir-ty/src/mir/eval/shim.rs
Expand Up @@ -13,7 +13,7 @@ use crate::mir::eval::{
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
};

mod simd;
Expand Down Expand Up @@ -158,6 +158,25 @@ impl Evaluator<'_> {
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
fn exec_clone(
&mut self,
Expand Down Expand Up @@ -291,9 +310,14 @@ impl Evaluator<'_> {
use LangItem::*;
let candidate = self.db.lang_attr(def.into())?;
// 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);
}
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
}

Expand All @@ -309,43 +333,6 @@ impl Evaluator<'_> {
let mut args = args.iter();
match it {
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
PanicFmt => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to have something like this? How const_panic_fmt can return MirEvalError::Panic on its own?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we need to hook panic_display in r-a as well? What is the output of rust-analyzer(debug-command) interpret function on a function that panics? It should be MirEvalError::Panic("panic message") but I guess it is now execution limit exceeded or something like that as it will try to execute panic_fmt and panic_display over and over.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooking that function is what my rustc_const_panic_str change does.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. So we now lose the panic message? but let's track it in #16938 and merge this one.

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 => {
let arg = args.next().ok_or(MirEvalError::InternalError(
"argument of <[T]>::len() is not provided".into(),
Expand Down
26 changes: 26 additions & 0 deletions crates/ide/src/hover/tests.rs
Expand Up @@ -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]
fn array_repeat_exp() {
check(
Expand Down