Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-expand/src/name.rs5
-rw-r--r--crates/hir-ty/src/consteval/tests.rs14
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs75
-rw-r--r--crates/hir-ty/src/layout/adt.rs2
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs162
-rw-r--r--crates/hir-ty/src/mir/eval/shim/simd.rs50
-rw-r--r--crates/ide/src/hover/tests.rs2
7 files changed, 221 insertions, 89 deletions
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 01e499f2ae..581ac40eb4 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -282,8 +282,10 @@ pub mod known {
alloc,
iter,
ops,
+ fmt,
future,
result,
+ string,
boxed,
option,
prelude,
@@ -311,6 +313,7 @@ pub mod known {
RangeToInclusive,
RangeTo,
Range,
+ String,
Neg,
Not,
None,
@@ -321,6 +324,7 @@ pub mod known {
iter_mut,
len,
is_empty,
+ as_str,
new,
// Builtin macros
asm,
@@ -334,6 +338,7 @@ pub mod known {
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 c37c5912f2..d5feb6c7fe 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -2457,20 +2457,6 @@ 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(
r#"
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index bb8fca178a..f51aceb87a 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -45,6 +45,50 @@ fn size_of_val() {
);
check_number(
r#"
+ //- minicore: coerce_unsized, transmute
+ use core::mem::transmute;
+
+ extern "rust-intrinsic" {
+ pub fn size_of_val<T: ?Sized>(_: *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<T: ?Sized>(_: *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
extern "rust-intrinsic" {
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
@@ -75,6 +119,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<T: ?Sized>(_: *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<T: ?Sized>(_: *const T) -> usize;
+ }
+
+ const GOAL: usize = {
+ let x: &[i32] = &[1, 2, 3];
+ min_align_of_val(x)
+ };
+ "#,
+ 4,
+ );
+}
+
+#[test]
fn transmute() {
check_number(
r#"
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 0012b4179d..186b8e2bc6 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 73ed52f3b3..8fc93c85d8 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 @@ impl Evaluator<'_> {
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".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(|| "<format-args-evaluation-failed>".to_string());
+ .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
Err(MirEvalError::Panic(message))
}
SliceLen => {
@@ -544,6 +528,13 @@ impl Evaluator<'_> {
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 @@ impl Evaluator<'_> {
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 @@ impl Evaluator<'_> {
}
}
+ 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 89b04eb36b..8ba6e18b9a 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<usize> {
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 @@ impl Evaluator<'_> {
.collect::<Vec<_>>();
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 @@ impl Evaluator<'_> {
}
};
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 2070386e98..00e21433da 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
```
"#]],
);