Skip to content

Commit bd5c08a

Browse files
committed
Auto merge of #16938 - Nilstrieb:dont-panic-tests, r=Veykril
Implement `BeginPanic` handling in const eval for #16935, needs some figuring out of how to write these tests correctly
2 parents 77ce295 + 4426142 commit bd5c08a

File tree

10 files changed

+229
-83
lines changed

10 files changed

+229
-83
lines changed

crates/hir-def/src/body/lower.rs

+41-39
Original file line numberDiff line numberDiff line change
@@ -1869,42 +1869,45 @@ impl ExprCollector<'_> {
18691869
) -> ExprId {
18701870
match count {
18711871
Some(FormatCount::Literal(n)) => {
1872-
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
1873-
Some(count_is) => {
1874-
let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
1875-
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
1876-
*n as u128,
1877-
Some(BuiltinUint::Usize),
1878-
)));
1879-
self.alloc_expr_desugared(Expr::Call {
1880-
callee: count_is,
1881-
args: Box::new([args]),
1882-
is_assignee_expr: false,
1883-
})
1884-
}
1885-
None => self.missing_expr(),
1886-
}
1872+
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
1873+
*n as u128,
1874+
Some(BuiltinUint::Usize),
1875+
)));
1876+
let count_is =
1877+
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
1878+
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
1879+
None => self.missing_expr(),
1880+
};
1881+
self.alloc_expr_desugared(Expr::Call {
1882+
callee: count_is,
1883+
args: Box::new([args]),
1884+
is_assignee_expr: false,
1885+
})
18871886
}
18881887
Some(FormatCount::Argument(arg)) => {
18891888
if let Ok(arg_index) = arg.index {
18901889
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
18911890

1892-
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
1893-
Some(count_param) => {
1894-
let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
1895-
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
1896-
i as u128,
1897-
Some(BuiltinUint::Usize),
1898-
)));
1899-
self.alloc_expr_desugared(Expr::Call {
1900-
callee: count_param,
1901-
args: Box::new([args]),
1902-
is_assignee_expr: false,
1903-
})
1904-
}
1891+
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
1892+
i as u128,
1893+
Some(BuiltinUint::Usize),
1894+
)));
1895+
let count_param = match LangItem::FormatCount.ty_rel_path(
1896+
self.db,
1897+
self.krate,
1898+
name![Param],
1899+
) {
1900+
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
19051901
None => self.missing_expr(),
1906-
}
1902+
};
1903+
self.alloc_expr_desugared(Expr::Call {
1904+
callee: count_param,
1905+
args: Box::new([args]),
1906+
is_assignee_expr: false,
1907+
})
19071908
} else {
1909+
// FIXME: This drops arg causing it to potentially not be resolved/type checked
1910+
// when typing?
19081911
self.missing_expr()
19091912
}
19101913
}
@@ -1925,7 +1928,8 @@ impl ExprCollector<'_> {
19251928
fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
19261929
use ArgumentType::*;
19271930
use FormatTrait::*;
1928-
match LangItem::FormatArgument.ty_rel_path(
1931+
1932+
let new_fn = match LangItem::FormatArgument.ty_rel_path(
19291933
self.db,
19301934
self.krate,
19311935
match ty {
@@ -1941,16 +1945,14 @@ impl ExprCollector<'_> {
19411945
Usize => name![from_usize],
19421946
},
19431947
) {
1944-
Some(new_fn) => {
1945-
let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
1946-
self.alloc_expr_desugared(Expr::Call {
1947-
callee: new_fn,
1948-
args: Box::new([arg]),
1949-
is_assignee_expr: false,
1950-
})
1951-
}
1948+
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
19521949
None => self.missing_expr(),
1953-
}
1950+
};
1951+
self.alloc_expr_desugared(Expr::Call {
1952+
callee: new_fn,
1953+
args: Box::new([arg]),
1954+
is_assignee_expr: false,
1955+
})
19541956
}
19551957
// endregion: format
19561958
}

crates/hir-def/src/body/tests.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -318,18 +318,20 @@ fn f() {
318318

319319
expect![[r#"
320320
fn f() {
321-
$crate::panicking::panic_fmt(
322-
builtin#lang(Arguments::new_v1_formatted)(
323-
&[
324-
"cc",
325-
],
326-
&[],
327-
&[],
328-
unsafe {
329-
builtin#lang(UnsafeArg::new)()
330-
},
331-
),
332-
);
321+
{
322+
$crate::panicking::panic_fmt(
323+
builtin#lang(Arguments::new_v1_formatted)(
324+
&[
325+
"cc",
326+
],
327+
&[],
328+
&[],
329+
unsafe {
330+
builtin#lang(UnsafeArg::new)()
331+
},
332+
),
333+
);
334+
};
333335
}"#]]
334336
.assert_eq(&body.pretty_print(&db, def))
335337
}

crates/hir-ty/src/chalk_ext.rs

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub trait TyExt {
2727
fn is_scalar(&self) -> bool;
2828
fn is_floating_point(&self) -> bool;
2929
fn is_never(&self) -> bool;
30+
fn is_str(&self) -> bool;
3031
fn is_unknown(&self) -> bool;
3132
fn contains_unknown(&self) -> bool;
3233
fn is_ty_var(&self) -> bool;
@@ -87,6 +88,10 @@ impl TyExt for Ty {
8788
matches!(self.kind(Interner), TyKind::Never)
8889
}
8990

91+
fn is_str(&self) -> bool {
92+
matches!(self.kind(Interner), TyKind::Str)
93+
}
94+
9095
fn is_unknown(&self) -> bool {
9196
matches!(self.kind(Interner), TyKind::Error)
9297
}

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,17 @@ impl MirEvalError {
428428
}
429429
Ok(())
430430
}
431+
432+
pub fn is_panic(&self) -> Option<&str> {
433+
let mut err = self;
434+
while let MirEvalError::InFunction(e, _) = err {
435+
err = e;
436+
}
437+
match err {
438+
MirEvalError::Panic(msg) => Some(msg),
439+
_ => None,
440+
}
441+
}
431442
}
432443

433444
impl std::fmt::Debug for MirEvalError {
@@ -1138,7 +1149,7 @@ impl Evaluator<'_> {
11381149
let mut ty = self.operand_ty(lhs, locals)?;
11391150
while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
11401151
ty = z.clone();
1141-
let size = if ty.kind(Interner) == &TyKind::Str {
1152+
let size = if ty.is_str() {
11421153
if *op != BinOp::Eq {
11431154
never!("Only eq is builtin for `str`");
11441155
}

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

+48-10
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl Evaluator<'_> {
4949
if self.not_special_fn_cache.borrow().contains(&def) {
5050
return Ok(false);
5151
}
52+
5253
let function_data = self.db.function_data(def);
5354
let is_intrinsic = match &function_data.abi {
5455
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
@@ -131,9 +132,7 @@ impl Evaluator<'_> {
131132
return Ok(true);
132133
}
133134
if let Some(it) = self.detect_lang_function(def) {
134-
let arg_bytes =
135-
args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::<Result<Vec<_>>>()?;
136-
let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?;
135+
let result = self.exec_lang_item(it, generic_args, args, locals, span)?;
137136
destination.write_from_bytes(self, &result)?;
138137
return Ok(true);
139138
}
@@ -311,35 +310,73 @@ impl Evaluator<'_> {
311310

312311
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
313312
use LangItem::*;
314-
let candidate = self.db.lang_attr(def.into())?;
313+
let attrs = self.db.attrs(def.into());
314+
315+
if attrs.by_key("rustc_const_panic_str").exists() {
316+
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
317+
return Some(LangItem::BeginPanic);
318+
}
319+
320+
let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?;
315321
// We want to execute these functions with special logic
316322
// `PanicFmt` is not detected here as it's redirected later.
317323
if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
318324
return Some(candidate);
319325
}
320-
if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
321-
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
322-
return Some(LangItem::BeginPanic);
323-
}
326+
324327
None
325328
}
326329

327330
fn exec_lang_item(
328331
&mut self,
329332
it: LangItem,
330333
generic_args: &Substitution,
331-
args: &[Vec<u8>],
334+
args: &[IntervalAndTy],
332335
locals: &Locals,
333336
span: MirSpan,
334337
) -> Result<Vec<u8>> {
335338
use LangItem::*;
336339
let mut args = args.iter();
337340
match it {
338-
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
341+
BeginPanic => {
342+
let mut arg = args
343+
.next()
344+
.ok_or(MirEvalError::InternalError(
345+
"argument of BeginPanic is not provided".into(),
346+
))?
347+
.clone();
348+
while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) {
349+
if ty.is_str() {
350+
let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size());
351+
let len = from_bytes!(usize, metadata);
352+
353+
return {
354+
Err(MirEvalError::Panic(
355+
std::str::from_utf8(
356+
self.read_memory(Address::from_bytes(pointee)?, len)?,
357+
)
358+
.unwrap()
359+
.to_owned(),
360+
))
361+
};
362+
}
363+
let size = self.size_of_sized(&ty, locals, "begin panic arg")?;
364+
let pointee = arg.interval.get(self)?;
365+
arg = IntervalAndTy {
366+
interval: Interval::new(Address::from_bytes(pointee)?, size),
367+
ty: ty.clone(),
368+
};
369+
}
370+
Err(MirEvalError::Panic(format!(
371+
"unknown-panic-payload: {:?}",
372+
arg.ty.kind(Interner)
373+
)))
374+
}
339375
SliceLen => {
340376
let arg = args.next().ok_or(MirEvalError::InternalError(
341377
"argument of <[T]>::len() is not provided".into(),
342378
))?;
379+
let arg = arg.get(self)?;
343380
let ptr_size = arg.len() / 2;
344381
Ok(arg[ptr_size..].into())
345382
}
@@ -353,6 +390,7 @@ impl Evaluator<'_> {
353390
let arg = args.next().ok_or(MirEvalError::InternalError(
354391
"argument of drop_in_place is not provided".into(),
355392
))?;
393+
let arg = arg.interval.get(self)?.to_owned();
356394
self.run_drop_glue_deep(
357395
ty.clone(),
358396
locals,

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

+45
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()))
@@ -72,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
7273
}
7374
}
7475

76+
fn check_panic(ra_fixture: &str, expected_panic: &str) {
77+
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
78+
let file_id = *file_ids.last().unwrap();
79+
let e = eval_main(&db, file_id).unwrap_err();
80+
assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic);
81+
}
82+
7583
#[test]
7684
fn function_with_extern_c_abi() {
7785
check_pass(
@@ -87,6 +95,43 @@ fn main() {
8795
);
8896
}
8997

98+
#[test]
99+
fn panic_fmt() {
100+
// panic!
101+
// -> panic_2021 (builtin macro redirection)
102+
// -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection)
103+
// -> core::panicking::const_panic_fmt
104+
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
105+
// -> Err(ConstEvalError::Panic)
106+
check_panic(
107+
r#"
108+
//- minicore: fmt, panic
109+
fn main() {
110+
panic!("hello, world!");
111+
}
112+
"#,
113+
"hello, world!",
114+
);
115+
}
116+
117+
#[test]
118+
fn panic_display() {
119+
// panic!
120+
// -> panic_2021 (builtin macro redirection)
121+
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
122+
// -> Err(ConstEvalError::Panic)
123+
check_panic(
124+
r#"
125+
//- minicore: fmt, panic
126+
127+
fn main() {
128+
panic!("{}", "hello, world!");
129+
}
130+
"#,
131+
"hello, world!",
132+
);
133+
}
134+
90135
#[test]
91136
fn drop_basic() {
92137
check_pass(

crates/ide/src/hover/tests.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -7864,8 +7864,8 @@ impl Iterator for S {
78647864
file_id: FileId(
78657865
1,
78667866
),
7867-
full_range: 6290..6498,
7868-
focus_range: 6355..6361,
7867+
full_range: 6729..6937,
7868+
focus_range: 6794..6800,
78697869
name: "Future",
78707870
kind: Trait,
78717871
container_name: "future",
@@ -7878,8 +7878,8 @@ impl Iterator for S {
78787878
file_id: FileId(
78797879
1,
78807880
),
7881-
full_range: 7128..7594,
7882-
focus_range: 7172..7180,
7881+
full_range: 7567..8033,
7882+
focus_range: 7611..7619,
78837883
name: "Iterator",
78847884
kind: Trait,
78857885
container_name: "iterator",

crates/ide/src/syntax_highlighting/test_data/highlight_strings.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@
158158
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
159159

160160
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
161-
<span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
161+
<span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
162162
<span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
163-
<span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
163+
<span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
164164
<span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
165165
<span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
166166
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>

0 commit comments

Comments
 (0)