From 4adfbbfbadf0fb74dee05d4bc5110e00596c5919 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 18:30:49 +0330 Subject: [PATCH] partially support panic message in `MirEvalError` --- crates/hir-def/src/body.rs | 9 +++ crates/hir-def/src/body/pretty.rs | 11 +++ .../macro_expansion_tests/builtin_fn_macro.rs | 10 +-- crates/hir-expand/src/builtin_fn_macro.rs | 12 ++- crates/hir-expand/src/name.rs | 2 + crates/hir-ty/src/consteval/tests.rs | 39 +++++++--- crates/hir-ty/src/mir/eval/shim.rs | 47 +++++++++++- crates/hir-ty/src/mir/lower.rs | 22 +++++- crates/hir-ty/src/tests/regression.rs | 7 +- crates/ide/src/inlay_hints/chaining.rs | 12 +-- crates/test-utils/src/minicore.rs | 73 +++++++++++++++++-- 11 files changed, 199 insertions(+), 45 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index d4b4cbe6c7..ea682f5cbd 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -187,6 +187,15 @@ impl Body { pretty::print_body_hir(db, self, owner) } + pub fn pretty_print_expr( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr: ExprId, + ) -> String { + pretty::print_expr_hir(db, self, owner, expr) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 093dd67970..318f654c16 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -61,6 +61,17 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo p.buf } +pub(super) fn print_expr_hir( + _db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + expr: ExprId, +) -> String { + let mut p = Printer { body, buf: String::new(), indent_level: 0, needs_indent: false }; + p.print_expr(expr); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index f5346898c2..13aa6130be 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -201,7 +201,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -229,7 +229,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -262,7 +262,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[$crate::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), $crate::fmt::Display::fmt), ]); + ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); } "##]], ); @@ -296,7 +296,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[$crate::fmt::ArgumentV1::new(&(2), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -327,7 +327,7 @@ macro_rules! format_args { fn main() { let _ = /* error: no rule matches input tokens *//* parse error: expected field name or number */ -$crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(), $crate::fmt::Debug::fmt), ]); +::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]); } "##]], ); diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index e9e1c6c3b3..0640ba774b 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -363,16 +363,14 @@ fn format_args_expand_general( quote!(#ident) }; let formatter = match &*format_spec { - "?" => quote!(#DOLLAR_CRATE::fmt::Debug::fmt), - "" => quote!(#DOLLAR_CRATE::fmt::Display::fmt), + "?" => quote!(::core::fmt::Debug::fmt), + "" => quote!(::core::fmt::Display::fmt), _ => { // FIXME: implement the rest and return expand error here - quote!(#DOLLAR_CRATE::fmt::Display::fmt) + quote!(::core::fmt::Display::fmt) } }; - arg_tts.push( - quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }, - ); + arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }); } '}' => { if format_iter.peek() == Some(&'}') { @@ -400,7 +398,7 @@ fn format_args_expand_general( }); let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); let expanded = quote! { - #DOLLAR_CRATE::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) + ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) }; ExpandResult { value: expanded, err } } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index d6bad1a48c..10a251ba78 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -362,6 +362,8 @@ pub mod known { gt, le, lt, + // known fields of lang items + pieces, // lang items add_assign, add, diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 5ab1f97020..b4b5fdd891 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -26,9 +26,11 @@ fn simplify(e: ConstEvalError) -> ConstEvalError { #[track_caller] fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) { let (db, file_id) = TestDB::with_single_file(ra_fixture); - match eval_goal(&db, file_id).map_err(simplify) { + match eval_goal(&db, file_id) { Ok(_) => panic!("Expected fail, but it succeeded"), - Err(e) => assert!(error(e)), + Err(e) => { + assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db)) + } } } @@ -38,13 +40,7 @@ fn check_number(ra_fixture: &str, answer: i128) { let r = match eval_goal(&db, file_id) { Ok(t) => t, Err(e) => { - let mut err = String::new(); - let span_formatter = |file, range| format!("{:?} {:?}", file, range); - match e { - ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter), - ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter), - } - .unwrap(); + let err = pretty_print_err(e, db); panic!("Error in evaluating goal: {}", err); } }; @@ -64,6 +60,17 @@ fn check_number(ra_fixture: &str, answer: i128) { } } +fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { + let mut err = String::new(); + let span_formatter = |file, range| format!("{:?} {:?}", file, range); + match e { + ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter), + ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter), + } + .unwrap(); + err +} + fn eval_goal(db: &TestDB, file_id: FileId) -> Result { let module_id = db.module_for_file(file_id); let def_map = module_id.def_map(db); @@ -2187,6 +2194,20 @@ fn const_trait_assoc() { ); } +#[test] +fn panic_messages() { + check_fail( + r#" + //- minicore: panic + const GOAL: u8 = { + let x: u16 = 2; + panic!("hello"); + }; + "#, + |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())), + ); +} + #[test] fn exec_limits() { check_fail( diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index ac295b5837..9ff58b27bb 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -153,10 +153,49 @@ impl Evaluator<'_> { use LangItem::*; let mut args = args.iter(); match x { - // FIXME: we want to find the panic message from arguments, but it wouldn't work - // currently even if we do that, since macro expansion of panic related macros - // is dummy. - PanicFmt | BeginPanic => Err(MirEvalError::Panic("".to_string())), + BeginPanic => Err(MirEvalError::Panic("".to_string())), + PanicFmt => { + let message = (|| { + let arguments_struct = + self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?; + let arguments_layout = self + .layout_adt(arguments_struct.into(), Substitution::empty(Interner)) + .ok()?; + let arguments_field_pieces = + self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?; + let pieces_offset = arguments_layout + .fields + .offset(u32::from(arguments_field_pieces.into_raw()) as usize) + .bytes_usize(); + let ptr_size = self.ptr_size(); + let arg = args.next()?; + let pieces_array_addr = + Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?; + let pieces_array_len = usize::from_le_bytes( + (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size]) + .try_into() + .ok()?, + ); + let mut message = "".to_string(); + for i in 0..pieces_array_len { + let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size); + let piece_addr = + Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?) + .ok()?; + let piece_len = usize::from_le_bytes( + self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size) + .ok()? + .try_into() + .ok()?, + ); + let piece_data = self.read_memory(piece_addr, piece_len).ok()?; + message += &std::string::String::from_utf8_lossy(piece_data); + } + Some(message) + })() + .unwrap_or_else(|| "".to_string()); + Err(MirEvalError::Panic(message)) + } SliceLen => { let arg = args .next() diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4bf8070fa4..292a771baf 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -81,7 +81,7 @@ pub enum MirLowerError { UnresolvedMethod(String), UnresolvedField, UnsizedTemporary(Ty), - MissingFunctionDefinition, + MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch), /// This should be never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -113,6 +113,22 @@ impl MirLowerError { ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, } } + MirLowerError::MissingFunctionDefinition(owner, x) => { + let body = db.body(*owner); + writeln!( + f, + "Missing function definition for {}", + body.pretty_print_expr(db.upcast(), *owner, *x) + )?; + } + MirLowerError::TypeMismatch(e) => { + writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db), + e.actual.display(db), + )?; + } MirLowerError::LayoutError(_) | MirLowerError::UnsizedTemporary(_) | MirLowerError::IncompleteExpr @@ -122,8 +138,6 @@ impl MirLowerError { | MirLowerError::RecordLiteralWithoutPath | MirLowerError::UnresolvedMethod(_) | MirLowerError::UnresolvedField - | MirLowerError::MissingFunctionDefinition - | MirLowerError::TypeMismatch(_) | MirLowerError::TypeError(_) | MirLowerError::NotSupported(_) | MirLowerError::ContinueWithoutLoop @@ -599,7 +613,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) } - TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), + TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), _ => return Err(MirLowerError::TypeError("function call on bad type")), } } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 4af143829f..9f5f1ea325 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1475,13 +1475,12 @@ fn regression_11688_3() { struct Ar(T); fn f( num_zeros: usize, - ) -> dyn Iterator; LEN]> { + ) -> &dyn Iterator; LEN]> { loop {} } fn dynamic_programming() { - for board in f::<9, u8, 7>(1) { - //^^^^^ [Ar; 9] - } + let board = f::<9, u8, 7>(1).next(); + //^^^^^ Option<[Ar; 9]> } "#, ); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 451c15a25c..db98bf2f9b 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 960934cb69..22cef04983 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -23,7 +23,7 @@ //! drop: //! eq: sized //! error: fmt -//! fmt: result +//! fmt: result, transmute, coerce_unsized //! fn: //! from: sized //! future: pin @@ -37,7 +37,7 @@ //! non_zero: //! option: panic //! ord: eq, option -//! panic: +//! panic: fmt //! pin: //! range: //! result: @@ -45,6 +45,7 @@ //! sized: //! slice: //! sync: sized +//! transmute: //! try: infallible //! unsize: sized @@ -289,8 +290,8 @@ pub mod convert { // endregion:infallible } -// region:drop pub mod mem { + // region:drop // region:manually_drop #[lang = "manually_drop"] #[repr(transparent)] @@ -323,15 +324,23 @@ pub mod mem { result } } + // endregion:drop + + // region:transmute + extern "rust-intrinsic" { + pub fn transmute(src: Src) -> Dst; + } + // endregion:transmute } pub mod ptr { + // region:drop #[lang = "drop_in_place"] pub unsafe fn drop_in_place(to_drop: *mut T) { unsafe { drop_in_place(to_drop) } } + // endregion:drop } -// endregion:drop pub mod ops { // region:coerce_unsized @@ -812,6 +821,38 @@ pub mod fmt { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + extern "C" { + type Opaque; + } + + #[lang = "format_argument"] + pub struct ArgumentV1<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, + } + + impl<'a> ArgumentV1<'a> { + pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + use crate::mem::transmute; + unsafe { ArgumentV1 { formatter: transmute(f), value: transmute(x) } } + } + } + + #[lang = "format_arguments"] + pub struct Arguments<'a> { + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + } + + impl<'a> Arguments<'a> { + pub const fn new_v1( + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + ) -> Arguments<'a> { + Arguments { pieces, args } + } + } + // region:derive #[rustc_builtin_macro] pub macro Debug($item:item) {} @@ -1147,8 +1188,17 @@ pub mod iter { // region:panic mod panic { - pub macro panic_2021($($t:tt)+) { - /* Nothing yet */ + pub macro panic_2021 { + ($($t:tt)+) => ( + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) + ), + } +} + +mod panicking { + #[lang = "panic_fmt"] + pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + loop {} } } // endregion:panic @@ -1166,6 +1216,17 @@ mod macros { pub(crate) use panic; // endregion:panic + // region:fmt + #[macro_export] + #[rustc_builtin_macro] + macro_rules! const_format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + pub(crate) use const_format_args; + // endregion:fmt + // region:derive pub(crate) mod builtin { #[rustc_builtin_macro]