From 171ae2ee5d3256ac59412138a5566515cd785252 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 6 Jul 2023 15:41:52 +0330 Subject: [PATCH] Fix size_of_val and support min_align_of_val --- crates/hir-expand/src/name.rs | 5 + crates/hir-ty/src/consteval/tests.rs | 14 -- .../hir-ty/src/consteval/tests/intrinsics.rs | 75 ++++++++ crates/hir-ty/src/layout/adt.rs | 2 +- crates/hir-ty/src/mir/eval/shim.rs | 162 +++++++++++------- crates/hir-ty/src/mir/eval/shim/simd.rs | 50 ++++-- crates/ide/src/hover/tests.rs | 2 +- 7 files changed, 221 insertions(+), 89 deletions(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 01e499f2ae8..581ac40eb40 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -282,8 +282,10 @@ macro_rules! known_names { alloc, iter, ops, + fmt, future, result, + string, boxed, option, prelude, @@ -311,6 +313,7 @@ macro_rules! known_names { RangeToInclusive, RangeTo, Range, + String, Neg, Not, None, @@ -321,6 +324,7 @@ macro_rules! known_names { iter_mut, len, is_empty, + as_str, new, // Builtin macros asm, @@ -334,6 +338,7 @@ macro_rules! known_names { core_panic, env, file, + format, format_args_nl, format_args, global_asm, diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index c37c5912f28..d5feb6c7fef 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2456,20 +2456,6 @@ const fn f() -> usize { ); } -#[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/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index bb8fca178a2..f51aceb87a8 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -43,6 +43,50 @@ fn size_of_val() { "#, 12, ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i64, + y: u8, + t: [i32], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 3]); + size_of_val(y) + }; + "#, + 24, + ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i32, + y: i64, + t: [u8], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 15]); + size_of_val(y) + }; + "#, + 32, + ); check_number( r#" //- minicore: coerce_unsized, fmt, builtin_impls @@ -74,6 +118,37 @@ fn size_of_val() { ); } +#[test] +fn min_align_of_val() { + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + struct X(i32, u8); + + const GOAL: usize = min_align_of_val(&X(1, 2)); + "#, + 4, + ); + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + let x: &[i32] = &[1, 2, 3]; + min_align_of_val(x) + }; + "#, + 4, + ); +} + #[test] fn transmute() { check_number( diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 0012b4179d7..186b8e2bc68 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -105,7 +105,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|x| x.last().map(|x| x.is_unsized())) + .and_then(|x| x.last().map(|x| !x.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 73ed52f3b32..8fc93c85d8c 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -3,6 +3,10 @@ use std::cmp; +use chalk_ir::TyKind; +use hir_def::resolver::HasResolver; +use hir_expand::mod_path::ModPath; + use super::*; mod simd; @@ -186,44 +190,24 @@ fn exec_lang_item( 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) + let x = self.db.crate_def_map(self.crate_id).crate_root(); + let resolver = x.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]].into_iter(), + )), + ) 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 message_string = self.interpret_mir(&*self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.cloned())?; + 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(|| "".to_string()); + .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); Err(MirEvalError::Panic(message)) } SliceLen => { @@ -544,6 +528,13 @@ fn exec_intrinsic( let size = self.size_of_sized(ty, locals, "size_of arg")?; destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } + "min_align_of" | "pref_align_of" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("align_of generic arg is not provided")); + }; + let align = self.layout(ty)?.align.abi.bytes(); + destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) + } "size_of_val" => { let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { @@ -552,33 +543,28 @@ fn exec_intrinsic( let [arg] = args else { return Err(MirEvalError::TypeError("size_of_val args are not provided")); }; - let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); - let size = match ty.kind(Interner) { - TyKind::Str => return destination.write_from_interval(self, metadata), - TyKind::Slice(inner) => { - let len = from_bytes!(usize, metadata.get(self)?); - len * self.size_of_sized(inner, locals, "slice inner type")? - } - TyKind::Dyn(_) => self.size_of_sized( - self.vtable_map.ty_of_bytes(metadata.get(self)?)?, - locals, - "dyn concrete type", - )?, - _ => self.size_of_sized( - ty, - locals, - "unsized type other than str, slice, and dyn", - )?, - }; - destination.write_from_bytes(self, &size.to_le_bytes()) + if let Some((size, _)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &size.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &size.to_le_bytes()) + } } - "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) - else { - return Err(MirEvalError::TypeError("align_of generic arg is not provided")); + "min_align_of_val" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); }; - let align = self.layout(ty)?.align.abi.bytes(); - destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) + let [arg] = args else { + return Err(MirEvalError::TypeError("min_align_of_val args are not provided")); + }; + if let Some((_, align)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &align.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &align.to_le_bytes()) + } } "needs_drop" => { let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) @@ -905,6 +891,58 @@ fn exec_intrinsic( } } + fn size_align_of_unsized( + &mut self, + ty: &Ty, + metadata: Interval, + locals: &Locals<'_>, + ) -> Result<(usize, usize)> { + Ok(match ty.kind(Interner) { + TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), + TyKind::Slice(inner) => { + let len = from_bytes!(usize, metadata.get(self)?); + let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?; + (size * len, align) + } + TyKind::Dyn(_) => self.size_align_of_sized( + self.vtable_map.ty_of_bytes(metadata.get(self)?)?, + locals, + "dyn concrete type", + )?, + TyKind::Adt(id, subst) => { + let id = id.0; + let layout = self.layout_adt(id, subst.clone())?; + let id = match id { + AdtId::StructId(s) => s, + _ => not_supported!("unsized enum or union"), + }; + let field_types = &self.db.field_types(id.into()); + let last_field_ty = + field_types.iter().rev().next().unwrap().1.clone().substitute(Interner, subst); + let sized_part_size = + layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); + let sized_part_align = layout.align.abi.bytes() as usize; + let (unsized_part_size, unsized_part_align) = + self.size_align_of_unsized(&last_field_ty, metadata, locals)?; + let align = sized_part_align.max(unsized_part_align) as isize; + let size = (sized_part_size + unsized_part_size) as isize; + // Must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + let size = (size + (align - 1)) & (-align); + (size as usize, align as usize) + } + _ => not_supported!("unsized type other than str, slice, struct and dyn"), + }) + } + fn exec_atomic_intrinsic( &mut self, name: &str, diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index 89b04eb36bb..8ba6e18b9a5 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -1,5 +1,7 @@ //! Shim implementation for simd intrinsics +use std::cmp::Ordering; + use crate::TyKind; use super::*; @@ -22,10 +24,15 @@ macro_rules! not_supported { impl Evaluator<'_> { fn detect_simd_ty(&self, ty: &Ty) -> Result { match ty.kind(Interner) { - TyKind::Adt(_, subst) => { - let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) - else { - return Err(MirEvalError::TypeError("simd type without len param")); + TyKind::Adt(id, subst) => { + let len = match subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) { + Some(len) => len, + _ => { + if let AdtId::StructId(id) = id.0 { + return Ok(self.db.struct_data(id).variant_data.fields().len()); + } + return Err(MirEvalError::TypeError("simd type with no len param")); + } }; match try_const_usize(self.db, len) { Some(x) => Ok(x as usize), @@ -63,13 +70,34 @@ pub(super) fn exec_simd_intrinsic( .collect::>(); destination.write_from_bytes(self, &result) } - "eq" | "ne" => { + "eq" | "ne" | "lt" | "le" | "gt" | "ge" => { let [left, right] = args else { - return Err(MirEvalError::TypeError("simd_eq args are not provided")); + return Err(MirEvalError::TypeError("simd args are not provided")); }; - let result = left.get(self)? == right.get(self)?; - let result = result ^ (name == "ne"); - destination.write_from_bytes(self, &[u8::from(result)]) + let len = self.detect_simd_ty(&left.ty)?; + let size = left.interval.size / len; + let dest_size = destination.size / len; + let mut destination_bytes = vec![]; + let vector = left.get(self)?.chunks(size).zip(right.get(self)?.chunks(size)); + for (l, r) in vector { + let mut result = Ordering::Equal; + for (l, r) in l.iter().zip(r).rev() { + let x = l.cmp(r); + if x != Ordering::Equal { + result = x; + break; + } + } + let result = match result { + Ordering::Less => ["lt", "le", "ne"].contains(&name), + Ordering::Equal => ["ge", "le", "eq"].contains(&name), + Ordering::Greater => ["ge", "gt", "ne"].contains(&name), + }; + let result = if result { 255 } else { 0 }; + destination_bytes.extend(std::iter::repeat(result).take(dest_size)); + } + + destination.write_from_bytes(self, &destination_bytes) } "bitmask" => { let [op] = args else { @@ -103,9 +131,9 @@ pub(super) fn exec_simd_intrinsic( } }; let left_len = self.detect_simd_ty(&left.ty)?; - let left_count = left.interval.size / left_len; + let left_size = left.interval.size / left_len; let vector = - left.get(self)?.chunks(left_count).chain(right.get(self)?.chunks(left_count)); + left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size)); let mut result = vec![]; for index in index.get(self)?.chunks(index.interval.size / index_len) { let index = from_bytes!(u32, index) as usize; diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 2070386e984..00e21433daa 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -674,7 +674,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 1, align = 1, offset = 4 + field_a: u8 // size = 1, align = 1, offset = 6 ``` "#]], );