Repository: rust-lang/rust-analyzer
Pull request #15222 (2023-07-06 11:37:16)

Fix size_of_val and support min_align_of_val

HKalbasi: @bors r+
bors: :pushpin: Commit 5e064451dcb7dacffb538e1c6310550262a57fa1 has been approved by `HKalbasi`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust-analyzer) for this repository.

<!-- @bors r=HKalbasi 5e064451dcb7dacffb538e1c6310550262a57fa1 -->
<!-- homu: {"type":"Approved","sha":"5e064451dcb7dacffb538e1c6310550262a57fa1","approver":"HKalbasi","queue":"https://bors.rust-lang.org/homu/queue/rust-analyzer"} -->
bors: :hourglass: Testing commit 5e064451dcb7dacffb538e1c6310550262a57fa1 with merge 0383fdb519c81f250527b6c44ec0438437e19737...
<!-- homu: {"type":"BuildStarted","head_sha":"5e064451dcb7dacffb538e1c6310550262a57fa1","merge_sha":"0383fdb519c81f250527b6c44ec0438437e19737"} -->
bors: :broken_heart: Test failed - [checks-actions](https://github.com/rust-lang/rust-analyzer/actions/runs/5474991883/jobs/9970709837)
<!-- homu: {"type":"BuildFailed","builder_url":"https://github.com/rust-lang/rust-analyzer/actions/runs/5474991883/jobs/9970709837","builder_name":"checks-actions"} -->
HKalbasi: @bors r+
bors: :pushpin: Commit c70faa593a0f25a36a98cbce6b4af5c480f2d994 has been approved by `HKalbasi`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust-analyzer) for this repository.

<!-- @bors r=HKalbasi c70faa593a0f25a36a98cbce6b4af5c480f2d994 -->
<!-- homu: {"type":"Approved","sha":"c70faa593a0f25a36a98cbce6b4af5c480f2d994","approver":"HKalbasi","queue":"https://bors.rust-lang.org/homu/queue/rust-analyzer"} -->
HKalbasi: @bors r-

Patch:
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 0ffce333f1..581ac40eb4 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -96,15 +96,6 @@ impl Name {
         Name::new_inline("[missing name]")
     }
 
-    /// Returns true if this is a fake name for things missing in the source code. See
-    /// [`missing()`][Self::missing] for details.
-    ///
-    /// Use this method instead of comparing with `Self::missing()` as missing names
-    /// (ideally should) have a `gensym` semantics.
-    pub fn is_missing(&self) -> bool {
-        self == &Name::missing()
-    }
-
     /// Generates a new name which is only equal to itself, by incrementing a counter. Due
     /// its implementation, it should not be used in things that salsa considers, like
     /// type names or field names, and it should be only used in names of local variables
@@ -291,8 +282,10 @@ pub mod known {
         alloc,
         iter,
         ops,
+        fmt,
         future,
         result,
+        string,
         boxed,
         option,
         prelude,
@@ -320,6 +313,7 @@ pub mod known {
         RangeToInclusive,
         RangeTo,
         Range,
+        String,
         Neg,
         Not,
         None,
@@ -330,6 +324,7 @@ pub mod known {
         iter_mut,
         len,
         is_empty,
+        as_str,
         new,
         // Builtin macros
         asm,
@@ -343,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/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index fd9522e86a..8377eb2349 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -1449,13 +1449,6 @@ impl InferenceContext<'_> {
 
     fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
         let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
-
-        if name.is_missing() {
-            // Bail out early, don't even try to look up field. Also, we don't issue an unresolved
-            // field diagnostic because this is a syntax error rather than a semantic error.
-            return self.err_ty();
-        }
-
         match self.lookup_field(&receiver_ty, name) {
             Some((ty, field_id, adjustments, is_public)) => {
                 self.write_expr_adj(receiver, adjustments);
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-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 0758706e45..94d49d4d48 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -68,10 +68,7 @@ fn method_fix(
 }
 #[cfg(test)]
 mod tests {
-    use crate::{
-        tests::{check_diagnostics, check_diagnostics_with_config},
-        DiagnosticsConfig,
-    };
+    use crate::tests::check_diagnostics;
 
     #[test]
     fn smoke_test() {
@@ -149,11 +146,4 @@ fn foo() {
 "#,
         );
     }
-
-    #[test]
-    fn no_diagnostic_for_missing_name() {
-        let mut config = DiagnosticsConfig::test_sample();
-        config.disabled.insert("syntax-error".to_owned());
-        check_diagnostics_with_config(config, "fn foo() { (). }");
-    }
 }


Test patch:
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
@@ -2456,20 +2456,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(
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
@@ -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<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
@@ -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<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(
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
             ```
         "#]],
     );