Repository: rust-lang/rust-analyzer
Pull request #15135 (2023-06-25 22:28:08)

Fix some unsizing problems in mir


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index d5a31e1e26..d7bd830884 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -220,7 +220,7 @@ dependencies = [
  "chalk-derive",
  "chalk-ir",
  "ena",
- "indexmap 1.9.3",
+ "indexmap",
  "itertools",
  "petgraph",
  "rustc-hash",
@@ -313,7 +313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
 dependencies = [
  "cfg-if",
- "hashbrown 0.12.3",
+ "hashbrown",
  "lock_api",
  "once_cell",
  "parking_lot_core 0.9.6",
@@ -363,12 +363,6 @@ dependencies = [
  "log",
 ]
 
-[[package]]
-name = "equivalent"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
-
 [[package]]
 name = "expect-test"
 version = "1.4.1"
@@ -459,12 +453,6 @@ version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
-[[package]]
-name = "hashbrown"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
-
 [[package]]
 name = "heck"
 version = "0.3.3"
@@ -529,11 +517,11 @@ dependencies = [
  "either",
  "expect-test",
  "fst",
- "hashbrown 0.12.3",
+ "hashbrown",
  "hir-expand",
  "hkalbasi-rustc-ap-rustc_abi",
  "hkalbasi-rustc-ap-rustc_index",
- "indexmap 2.0.0",
+ "indexmap",
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -560,7 +548,7 @@ dependencies = [
  "cov-mark",
  "either",
  "expect-test",
- "hashbrown 0.12.3",
+ "hashbrown",
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -727,7 +715,7 @@ dependencies = [
  "expect-test",
  "fst",
  "hir",
- "indexmap 2.0.0",
+ "indexmap",
  "itertools",
  "limit",
  "line-index 0.1.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -804,17 +792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
- "hashbrown 0.12.3",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
-dependencies = [
- "equivalent",
- "hashbrown 0.14.0",
+ "hashbrown",
 ]
 
 [[package]]
@@ -851,7 +829,7 @@ name = "intern"
 version = "0.0.0"
 dependencies = [
  "dashmap",
- "hashbrown 0.12.3",
+ "hashbrown",
  "once_cell",
  "rustc-hash",
  "triomphe",
@@ -1304,7 +1282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
 dependencies = [
  "fixedbitset",
- "indexmap 1.9.3",
+ "indexmap",
 ]
 
 [[package]]
@@ -1510,7 +1488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
 dependencies = [
  "countme",
- "hashbrown 0.12.3",
+ "hashbrown",
  "memoffset 0.8.0",
  "rustc-hash",
  "text-size",
@@ -1597,7 +1575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b223dccb46c32753144d0b51290da7230bb4aedcd8379d6b4c9a474c18bf17a"
 dependencies = [
  "crossbeam-utils",
- "indexmap 1.9.3",
+ "indexmap",
  "lock_api",
  "log",
  "oorandom",
@@ -1684,7 +1662,7 @@ version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a"
 dependencies = [
- "indexmap 1.9.3",
+ "indexmap",
  "itoa",
  "ryu",
  "serde",
@@ -1798,7 +1776,7 @@ dependencies = [
  "cov-mark",
  "either",
  "expect-test",
- "indexmap 2.0.0",
+ "indexmap",
  "itertools",
  "once_cell",
  "parser",
@@ -2149,7 +2127,7 @@ name = "vfs"
 version = "0.0.0"
 dependencies = [
  "fst",
- "indexmap 2.0.0",
+ "indexmap",
  "nohash-hasher",
  "paths",
  "rustc-hash",
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index 30307deb79..0650a81f9c 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -21,7 +21,7 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] }
 drop_bomb = "0.1.5"
 either = "1.7.0"
 fst = { version = "0.4.7", default-features = false }
-indexmap = "2.0.0"
+indexmap = "1.9.1"
 itertools = "0.10.5"
 la-arena.workspace = true
 once_cell = "1.17.0"
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 78961e1982..01295a9fc3 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1263,50 +1263,81 @@ impl Evaluator<'_> {
         current_ty: &Ty,
         target_ty: &Ty,
     ) -> Result<IntervalOrOwned> {
-        use IntervalOrOwned::*;
         fn for_ptr(x: &TyKind) -> Option<Ty> {
             match x {
                 TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
                 _ => None,
             }
         }
-        Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
-            ty => match &ty.data(Interner).kind {
-                TyKind::Slice(_) => {
-                    match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
-                        ty => match &ty.data(Interner).kind {
-                            TyKind::Array(_, size) => {
-                                let len = match try_const_usize(self.db, size) {
-                                    None => not_supported!(
-                                        "unevaluatble len of array in coerce unsized"
-                                    ),
-                                    Some(x) => x as usize,
-                                };
-                                let mut r = Vec::with_capacity(16);
-                                let addr = addr.get(self)?;
-                                r.extend(addr.iter().copied());
-                                r.extend(len.to_le_bytes().into_iter());
-                                Owned(r)
-                            }
-                            t => {
-                                not_supported!("slice unsizing from non array type {t:?}")
-                            }
-                        },
-                    }
+        let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
+        let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
+
+        self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
+    }
+
+    /// Adds metadata to the address and create the fat pointer result of the unsizing operation.
+    fn unsizing_ptr_from_addr(
+        &mut self,
+        target_ty: Ty,
+        current_ty: Ty,
+        addr: Interval,
+    ) -> Result<IntervalOrOwned> {
+        use IntervalOrOwned::*;
+        Ok(match &target_ty.data(Interner).kind {
+            TyKind::Slice(_) => match &current_ty.data(Interner).kind {
+                TyKind::Array(_, size) => {
+                    let len = match try_const_usize(self.db, size) {
+                        None => {
+                            not_supported!("unevaluatble len of array in coerce unsized")
+                        }
+                        Some(x) => x as usize,
+                    };
+                    let mut r = Vec::with_capacity(16);
+                    let addr = addr.get(self)?;
+                    r.extend(addr.iter().copied());
+                    r.extend(len.to_le_bytes().into_iter());
+                    Owned(r)
                 }
-                TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
-                    TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
-                        let vtable = self.vtable_map.id(ty.clone());
-                        let mut r = Vec::with_capacity(16);
-                        let addr = addr.get(self)?;
-                        r.extend(addr.iter().copied());
-                        r.extend(vtable.to_le_bytes().into_iter());
-                        Owned(r)
+                t => {
+                    not_supported!("slice unsizing from non array type {t:?}")
+                }
+            },
+            TyKind::Dyn(_) => {
+                let vtable = self.vtable_map.id(current_ty.clone());
+                let mut r = Vec::with_capacity(16);
+                let addr = addr.get(self)?;
+                r.extend(addr.iter().copied());
+                r.extend(vtable.to_le_bytes().into_iter());
+                Owned(r)
+            }
+            TyKind::Adt(id, target_subst) => match &current_ty.data(Interner).kind {
+                TyKind::Adt(current_id, current_subst) => {
+                    if id != current_id {
+                        not_supported!("unsizing struct with different type");
                     }
-                    _ => not_supported!("dyn unsizing from non pointers"),
-                },
-                _ => not_supported!("unknown unsized cast"),
+                    let id = match id.0 {
+                        AdtId::StructId(s) => s,
+                        AdtId::UnionId(_) => not_supported!("unsizing unions"),
+                        AdtId::EnumId(_) => not_supported!("unsizing enums"),
+                    };
+                    let Some((last_field, _)) = self.db.struct_data(id).variant_data.fields().iter().rev().next() else {
+                        not_supported!("unsizing struct without field");
+                    };
+                    let target_last_field = self.db.field_types(id.into())[last_field]
+                        .clone()
+                        .substitute(Interner, target_subst);
+                    let current_last_field = self.db.field_types(id.into())[last_field]
+                        .clone()
+                        .substitute(Interner, current_subst);
+                    return self.unsizing_ptr_from_addr(
+                        target_last_field,
+                        current_last_field,
+                        addr,
+                    );
+                }
+                _ => not_supported!("unsizing struct with non adt type"),
             },
+            _ => not_supported!("unknown unsized cast"),
         })
     }
 
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 2cb29b4ab9..800e490e9a 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1742,17 +1742,17 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
         (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
             CastKind::Pointer(if a == b {
                 PointerCast::MutToConstPointer
-            } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
-                && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+            } else if matches!(b.kind(Interner), TyKind::Slice(_))
+                && matches!(a.kind(Interner), TyKind::Array(_, _))
+                || matches!(b.kind(Interner), TyKind::Dyn(_))
             {
-                // slice to slice cast is no-op (metadata is not touched), so we use this
-                PointerCast::MutToConstPointer
-            } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
                 PointerCast::Unsize
             } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
                 PointerCast::ArrayToPointer
             } else {
-                // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
+                // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like
+                // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata
+                // will be touched) but there is no specific variant
                 // for it in `PointerCast` so we use `MutToConstPointer`
                 PointerCast::MutToConstPointer
             })
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index faec742068..4e75dc4dba 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -21,7 +21,7 @@ once_cell = "1.17.0"
 either = "1.7.0"
 itertools = "0.10.5"
 arrayvec = "0.7.2"
-indexmap = "2.0.0"
+indexmap = "1.9.1"
 memchr = "2.5.0"
 triomphe.workspace = true
 nohash-hasher.workspace = true
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 5ee0c47928..fb38d25ab5 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -19,7 +19,7 @@ itertools = "0.10.5"
 rowan = "0.15.11"
 rustc-hash = "1.1.0"
 once_cell = "1.17.0"
-indexmap = "2.0.0"
+indexmap = "1.9.1"
 smol_str.workspace = true
 triomphe.workspace = true
 
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index c35785cf98..3ae3dc83ca 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
 [dependencies]
 rustc-hash = "1.1.0"
 fst = "0.4.7"
-indexmap = "2.0.0"
+indexmap = "1.9.1"
 nohash-hasher.workspace = true
 
 paths.workspace = true
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 5a4078f16f..b5c095fd9d 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -280,12 +280,12 @@ Also see the https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/[rust-a
 
 Note the excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm] on how to set-up Emacs for Rust development with LSP mode and several other packages.
 
-=== Vim/Neovim
+=== Vim/NeoVim
 
 Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
 
-There are several LSP client implementations for Vim or Neovim:
+There are several LSP client implementations for vim or neovim:
 
 ==== coc-rust-analyzer
 
@@ -308,7 +308,7 @@ Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected
    https://github.com/autozimu/LanguageClient-neovim[here]
    * The GitHub project wiki has extra tips on configuration
 
-2. Configure by adding this to your Vim/Neovim config file (replacing the existing Rust-specific line if it exists):
+2. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists):
 +
 [source,vim]
 ----
@@ -335,7 +335,7 @@ let g:ale_linters = {'rust': ['analyzer']}
 
 ==== nvim-lsp
 
-Neovim 0.5 has built-in language server support.
+NeoVim 0.5 has built-in language server support.
 For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig].
 Once `neovim/nvim-lspconfig` is installed, use `+lua require'lspconfig'.rust_analyzer.setup({})+` in your `init.vim`.
 
@@ -376,7 +376,7 @@ EOF
 
 See https://sharksforarms.dev/posts/neovim-rust/ for more tips on getting started.
 
-Check out https://github.com/simrat39/rust-tools.nvim for a batteries included rust-analyzer setup for Neovim.
+Check out https://github.com/simrat39/rust-tools.nvim for a batteries included rust-analyzer setup for neovim.
 
 ==== vim-lsp
 
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index 09dc27056b..6e118f0b3a 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -10,5 +10,6 @@
 !package-lock.json
 !package.json
 !ra_syntax_tree.tmGrammar.json
+!rustdoc.markdown.injection.tmGrammar.json
 !server
 !README.md
diff --git a/editors/code/package.json b/editors/code/package.json
index a95689193d..240ec1f836 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1646,6 +1646,16 @@
                 "language": "ra_syntax_tree",
                 "scopeName": "source.ra_syntax_tree",
                 "path": "ra_syntax_tree.tmGrammar.json"
+            },
+            {
+                "scopeName": "source.rustdoc.markdown.injection",
+                "path": "rustdoc.markdown.injection.tmGrammar.json",
+                "injectTo": [
+                    "source.rust"
+                ],
+                "embeddedLanguages": {
+                    "meta.embedded.block.markdown": "text.html.markdown"
+                }
             }
         ],
         "problemMatchers": [
diff --git a/editors/code/rustdoc.markdown.injection.tmGrammar.json b/editors/code/rustdoc.markdown.injection.tmGrammar.json
new file mode 100644
index 0000000000..b12e62e1b6
--- /dev/null
+++ b/editors/code/rustdoc.markdown.injection.tmGrammar.json
@@ -0,0 +1,23 @@
+{
+    "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+    "scopeName": "source.rustdoc.markdown.injection",
+    "injectionSelector": "L:source.rust",
+    "patterns": [
+        {
+            "include": "#doc-comment-line"
+        }
+    ],
+    "repository": {
+        "doc-comment-line": {
+            "name": "comment.line.documentation.rust",
+            "begin": "^\\s*//(/|!)",
+            "end": "$",
+            "contentName": "meta.embedded.block.markdown",
+            "patterns": [
+                {
+                    "include": "text.html.markdown"
+                }
+            ]
+        }
+    }
+}


Test patch:
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index b487d7d049..de3a947f35 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -166,14 +166,21 @@ fn casts() {
     check_number(
         r#"
     //- minicore: coerce_unsized, index, slice
+    struct X {
+        unsize_field: [u8],
+    }
+
     const GOAL: usize = {
         let a = [10, 20, 3, 15];
         let x: &[i32] = &a;
-        let y: *const [i32] = x;
-        let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
-        let q = z as *const str;
-        let p = q as *const [u8];
-        let w = unsafe { &*z };
+        let x: *const [i32] = x;
+        let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
+        let x = x as *const str;
+        let x = x as *const X;
+        let x = x as *const [i16];
+        let x = x as *const X;
+        let x = x as *const [u8];
+        let w = unsafe { &*x };
         w.len()
     };
         "#,
@@ -1873,6 +1880,38 @@ fn dyn_trait() {
     );
 }
 
+#[test]
+fn coerce_unsized() {
+    check_number(
+        r#"
+//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
+use core::ops::{Deref, DerefMut, CoerceUnsized};
+use core::{marker::Unsize, mem::transmute, ptr::NonNull};
+
+struct ArcInner<T: ?Sized> {
+    strong: usize,
+    weak: usize,
+    data: T,
+}
+
+pub struct Arc<T: ?Sized> {
+    inner: NonNull<ArcInner<T>>,
+}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
+
+const GOAL: usize = {
+    let x = transmute::<usize, Arc<[i32; 3]>>(12);
+    let y: Arc<[i32]> = x;
+    let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
+    z.1
+};
+
+        "#,
+        3,
+    );
+}
+
 #[test]
 fn boxes() {
     check_number(
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 0ff8c532d4..b75f213df2 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -369,11 +369,11 @@ fn tuple() {
 }
 
 #[test]
-fn non_zero() {
+fn non_zero_and_non_null() {
     size_and_align! {
-        minicore: non_zero, option;
-        use core::num::NonZeroU8;
-        struct Goal(Option<NonZeroU8>);
+        minicore: non_zero, non_null, option;
+        use core::{num::NonZeroU8, ptr::NonNull};
+        struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>);
     }
 }
 
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 0d6e4b98d8..d7201f996d 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -37,6 +37,7 @@
 //!     iterator: option
 //!     iterators: iterator, fn
 //!     manually_drop: drop
+//!     non_null:
 //!     non_zero:
 //!     option: panic
 //!     ord: eq, option
@@ -386,6 +387,19 @@ pub mod ptr {
         type Metadata;
     }
     // endregion:pointee
+    // region:non_null
+    #[rustc_layout_scalar_valid_range_start(1)]
+    #[rustc_nonnull_optimization_guaranteed]
+    pub struct NonNull<T: ?Sized> {
+        pointer: *const T,
+    }
+    // region:coerce_unsized
+    impl<T: ?Sized, U: ?Sized> crate::ops::CoerceUnsized<NonNull<U>> for NonNull<T> where
+        T: crate::marker::Unsize<U>
+    {
+    }
+    // endregion:coerce_unsized
+    // endregion:non_null
 }
 
 pub mod ops {