Skip to content

Commit

Permalink
Auto merge of #16938 - Nilstrieb:dont-panic-tests, r=Veykril
Browse files Browse the repository at this point in the history
Implement `BeginPanic` handling in const eval

for #16935, needs some figuring out of how to write these tests correctly
  • Loading branch information
bors committed Apr 21, 2024
2 parents 3077e69 + 3b9a2af commit 4c08e2d
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 85 deletions.
80 changes: 41 additions & 39 deletions crates/hir-def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1869,42 +1869,45 @@ impl ExprCollector<'_> {
) -> ExprId {
match count {
Some(FormatCount::Literal(n)) => {
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
Some(count_is) => {
let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
*n as u128,
Some(BuiltinUint::Usize),
)));
self.alloc_expr_desugared(Expr::Call {
callee: count_is,
args: Box::new([args]),
is_assignee_expr: false,
})
}
None => self.missing_expr(),
}
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
*n as u128,
Some(BuiltinUint::Usize),
)));
let count_is =
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
None => self.missing_expr(),
};
self.alloc_expr_desugared(Expr::Call {
callee: count_is,
args: Box::new([args]),
is_assignee_expr: false,
})
}
Some(FormatCount::Argument(arg)) => {
if let Ok(arg_index) = arg.index {
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));

match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
Some(count_param) => {
let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
i as u128,
Some(BuiltinUint::Usize),
)));
self.alloc_expr_desugared(Expr::Call {
callee: count_param,
args: Box::new([args]),
is_assignee_expr: false,
})
}
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
i as u128,
Some(BuiltinUint::Usize),
)));
let count_param = match LangItem::FormatCount.ty_rel_path(
self.db,
self.krate,
name![Param],
) {
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
None => self.missing_expr(),
}
};
self.alloc_expr_desugared(Expr::Call {
callee: count_param,
args: Box::new([args]),
is_assignee_expr: false,
})
} else {
// FIXME: This drops arg causing it to potentially not be resolved/type checked
// when typing?
self.missing_expr()
}
}
Expand All @@ -1925,7 +1928,8 @@ impl ExprCollector<'_> {
fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
use ArgumentType::*;
use FormatTrait::*;
match LangItem::FormatArgument.ty_rel_path(

let new_fn = match LangItem::FormatArgument.ty_rel_path(
self.db,
self.krate,
match ty {
Expand All @@ -1941,16 +1945,14 @@ impl ExprCollector<'_> {
Usize => name![from_usize],
},
) {
Some(new_fn) => {
let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
self.alloc_expr_desugared(Expr::Call {
callee: new_fn,
args: Box::new([arg]),
is_assignee_expr: false,
})
}
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
None => self.missing_expr(),
}
};
self.alloc_expr_desugared(Expr::Call {
callee: new_fn,
args: Box::new([arg]),
is_assignee_expr: false,
})
}
// endregion: format
}
Expand Down
26 changes: 14 additions & 12 deletions crates/hir-def/src/body/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,18 +318,20 @@ fn f() {

expect![[r#"
fn f() {
$crate::panicking::panic_fmt(
builtin#lang(Arguments::new_v1_formatted)(
&[
"cc",
],
&[],
&[],
unsafe {
builtin#lang(UnsafeArg::new)()
},
),
);
{
$crate::panicking::panic_fmt(
builtin#lang(Arguments::new_v1_formatted)(
&[
"cc",
],
&[],
&[],
unsafe {
builtin#lang(UnsafeArg::new)()
},
),
);
};
}"#]]
.assert_eq(&body.pretty_print(&db, def))
}
5 changes: 5 additions & 0 deletions crates/hir-ty/src/chalk_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub trait TyExt {
fn is_scalar(&self) -> bool;
fn is_floating_point(&self) -> bool;
fn is_never(&self) -> bool;
fn is_str(&self) -> bool;
fn is_unknown(&self) -> bool;
fn contains_unknown(&self) -> bool;
fn is_ty_var(&self) -> bool;
Expand Down Expand Up @@ -87,6 +88,10 @@ impl TyExt for Ty {
matches!(self.kind(Interner), TyKind::Never)
}

fn is_str(&self) -> bool {
matches!(self.kind(Interner), TyKind::Str)
}

fn is_unknown(&self) -> bool {
matches!(self.kind(Interner), TyKind::Error)
}
Expand Down
13 changes: 12 additions & 1 deletion crates/hir-ty/src/mir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,17 @@ impl MirEvalError {
}
Ok(())
}

pub fn is_panic(&self) -> Option<&str> {
let mut err = self;
while let MirEvalError::InFunction(e, _) = err {
err = e;
}
match err {
MirEvalError::Panic(msg) => Some(msg),
_ => None,
}
}
}

impl std::fmt::Debug for MirEvalError {
Expand Down Expand Up @@ -1138,7 +1149,7 @@ impl Evaluator<'_> {
let mut ty = self.operand_ty(lhs, locals)?;
while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
ty = z.clone();
let size = if ty.kind(Interner) == &TyKind::Str {
let size = if ty.is_str() {
if *op != BinOp::Eq {
never!("Only eq is builtin for `str`");
}
Expand Down
58 changes: 48 additions & 10 deletions crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl Evaluator<'_> {
if self.not_special_fn_cache.borrow().contains(&def) {
return Ok(false);
}

let function_data = self.db.function_data(def);
let is_intrinsic = match &function_data.abi {
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
Expand Down Expand Up @@ -131,9 +132,7 @@ impl Evaluator<'_> {
return Ok(true);
}
if let Some(it) = self.detect_lang_function(def) {
let arg_bytes =
args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::<Result<Vec<_>>>()?;
let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?;
let result = self.exec_lang_item(it, generic_args, args, locals, span)?;
destination.write_from_bytes(self, &result)?;
return Ok(true);
}
Expand Down Expand Up @@ -311,35 +310,73 @@ impl Evaluator<'_> {

fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
use LangItem::*;
let candidate = self.db.lang_attr(def.into())?;
let attrs = self.db.attrs(def.into());

if attrs.by_key("rustc_const_panic_str").exists() {
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
return Some(LangItem::BeginPanic);
}

let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?;
// We want to execute these functions with special logic
// `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
}

fn exec_lang_item(
&mut self,
it: LangItem,
generic_args: &Substitution,
args: &[Vec<u8>],
args: &[IntervalAndTy],
locals: &Locals,
span: MirSpan,
) -> Result<Vec<u8>> {
use LangItem::*;
let mut args = args.iter();
match it {
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
BeginPanic => {
let mut arg = args
.next()
.ok_or(MirEvalError::InternalError(
"argument of BeginPanic is not provided".into(),
))?
.clone();
while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) {
if ty.is_str() {
let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size());
let len = from_bytes!(usize, metadata);

return {
Err(MirEvalError::Panic(
std::str::from_utf8(
self.read_memory(Address::from_bytes(pointee)?, len)?,
)
.unwrap()
.to_owned(),
))
};
}
let size = self.size_of_sized(ty, locals, "begin panic arg")?;
let pointee = arg.interval.get(self)?;
arg = IntervalAndTy {
interval: Interval::new(Address::from_bytes(pointee)?, size),
ty: ty.clone(),
};
}
Err(MirEvalError::Panic(format!(
"unknown-panic-payload: {:?}",
arg.ty.kind(Interner)
)))
}
SliceLen => {
let arg = args.next().ok_or(MirEvalError::InternalError(
"argument of <[T]>::len() is not provided".into(),
))?;
let arg = arg.get(self)?;
let ptr_size = arg.len() / 2;
Ok(arg[ptr_size..].into())
}
Expand All @@ -353,6 +390,7 @@ impl Evaluator<'_> {
let arg = args.next().ok_or(MirEvalError::InternalError(
"argument of drop_in_place is not provided".into(),
))?;
let arg = arg.interval.get(self)?.to_owned();
self.run_drop_glue_deep(
ty.clone(),
locals,
Expand Down
45 changes: 45 additions & 0 deletions crates/hir-ty/src/mir/eval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down Expand Up @@ -72,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
}
}

fn check_panic(ra_fixture: &str, expected_panic: &str) {
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
let file_id = *file_ids.last().unwrap();
let e = eval_main(&db, file_id).unwrap_err();
assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic);
}

#[test]
fn function_with_extern_c_abi() {
check_pass(
Expand All @@ -87,6 +95,43 @@ 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_panic(
r#"
//- minicore: fmt, panic
fn main() {
panic!("hello, world!");
}
"#,
"hello, world!",
);
}

#[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_panic(
r#"
//- minicore: fmt, panic
fn main() {
panic!("{}", "hello, world!");
}
"#,
"hello, world!",
);
}

#[test]
fn drop_basic() {
check_pass(
Expand Down
1 change: 1 addition & 0 deletions crates/ide-diagnostics/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ fn minicore_smoke_test() {
// This should be ignored since we conditionally remove code which creates single item use with braces
config.disabled.insert("unused_braces".to_owned());
config.disabled.insert("unused_variables".to_owned());
config.disabled.insert("remove-unnecessary-else".to_owned());
check_diagnostics_with_config(config, &source);
}

Expand Down
8 changes: 4 additions & 4 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7864,8 +7864,8 @@ impl Iterator for S {
file_id: FileId(
1,
),
full_range: 6290..6498,
focus_range: 6355..6361,
full_range: 7791..7999,
focus_range: 7856..7862,
name: "Future",
kind: Trait,
container_name: "future",
Expand All @@ -7878,8 +7878,8 @@ impl Iterator for S {
file_id: FileId(
1,
),
full_range: 7128..7594,
focus_range: 7172..7180,
full_range: 8629..9095,
focus_range: 8673..8681,
name: "Iterator",
kind: Trait,
container_name: "iterator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@

<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="enum_variant default_library library">Some</span><span class="parenthesis">(</span><span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method consuming default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre>
Loading

0 comments on commit 4c08e2d

Please sign in to comment.