about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-13 01:52:49 +0000
committerbors <bors@rust-lang.org>2021-07-13 01:52:49 +0000
commit14c0c3e55d311dd47aad792ef316bd5dde778886 (patch)
tree60377c410357e23f1b01e0f719c66244a22d201a
parentaa06edb4d5011a36ca7d6d505a7d210b359a3495 (diff)
parente46b790b9deb8f0c4db0a052f3cd9a875fc70d3b (diff)
downloadrust-14c0c3e55d311dd47aad792ef316bd5dde778886.tar.gz
rust-14c0c3e55d311dd47aad792ef316bd5dde778886.zip
Auto merge of #87095 - JohnTitor:rollup-mn7ggy2, r=JohnTitor
Rollup of 11 pull requests

Successful merges:

 - #86344 (Split MaybeUninit::write into new feature gate and stabilize it)
 - #86811 (Remove unstable `io::Cursor::remaining`)
 - #86846 (stdio_locked: add tracking issue)
 - #86887 (rustdoc: remove dead code in `clean`)
 - #87007 (Fix rust-analyzer install when not available.)
 - #87035 (Fix implementors display)
 - #87065 (Fix ICE with unsized type in const pattern)
 - #87070 (Simplify future incompatible reporting.)
 - #87077 (:arrow_up: rust-analyzer)
 - #87078 (Rustdoc: suggest removing disambiguator if linking to field)
 - #87089 (CTFE engine: small cleanups)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.gitignore3
-rw-r--r--compiler/rustc_errors/src/emitter.rs3
-rw-r--r--compiler/rustc_errors/src/json.rs12
-rw-r--r--compiler/rustc_errors/src/lib.rs3
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs6
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs13
-rw-r--r--compiler/rustc_middle/src/lint.rs11
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs19
-rw-r--r--compiler/rustc_mir/src/interpret/eval_context.rs6
-rw-r--r--compiler/rustc_mir/src/interpret/memory.rs19
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs34
-rw-r--r--compiler/rustc_session/src/session.rs21
-rw-r--r--library/core/src/mem/maybe_uninit.rs58
-rw-r--r--library/std/src/io/cursor.rs26
-rw-r--r--library/std/src/io/mod.rs2
-rw-r--r--library/std/src/io/stdio.rs12
-rw-r--r--src/bootstrap/install.rs25
-rw-r--r--src/bootstrap/test.rs26
-rw-r--r--src/librustdoc/clean/blanket_impl.rs2
-rw-r--r--src/librustdoc/clean/mod.rs45
-rw-r--r--src/librustdoc/html/static/js/main.js18
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs6
-rw-r--r--src/test/rustdoc-gui/implementors.goml16
-rw-r--r--src/test/rustdoc-gui/src/Cargo.lock18
-rw-r--r--src/test/rustdoc-gui/src/Cargo.toml6
-rw-r--r--src/test/rustdoc-gui/src/implementors/Cargo.toml7
-rw-r--r--src/test/rustdoc-gui/src/implementors/lib.rs7
-rw-r--r--src/test/rustdoc-gui/src/lib2/Cargo.toml10
-rw-r--r--src/test/rustdoc-gui/src/lib2/lib.rs (renamed from src/test/rustdoc-gui/src/lib2.rs)2
-rw-r--r--src/test/rustdoc-gui/src/lib2/src/lib.rs7
-rw-r--r--src/test/rustdoc-gui/src/test_docs/Cargo.toml7
-rw-r--r--src/test/rustdoc-gui/src/test_docs/lib.rs (renamed from src/test/rustdoc-gui/src/lib.rs)0
-rw-r--r--src/test/rustdoc-ui/intra-doc/field-ice.rs11
-rw-r--r--src/test/rustdoc-ui/intra-doc/field-ice.stderr15
-rw-r--r--src/test/ui/consts/issue-87046.rs34
-rw-r--r--src/test/ui/consts/issue-87046.stderr8
m---------src/tools/rust-analyzer34
37 files changed, 332 insertions, 220 deletions
diff --git a/.gitignore b/.gitignore
index 0cd6b9f648d..b7e8e8fa157 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,4 +72,7 @@ __pycache__/
 **node_modules
 **package-lock.json
 
+## Rustdoc GUI tests
+src/test/rustdoc-gui/src/**.lock
+
 # Before adding new lines, see the comment at the top.
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index fd024a8ecfa..becc1c6db5b 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -9,7 +9,6 @@
 
 use Destination::*;
 
-use rustc_lint_defs::FutureBreakage;
 use rustc_span::source_map::SourceMap;
 use rustc_span::{MultiSpan, SourceFile, Span};
 
@@ -193,7 +192,7 @@ pub trait Emitter {
     /// other formats can, and will, simply ignore it.
     fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
 
-    fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {}
+    fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
 
     /// Emit list of unused externs
     fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 485e7564587..1b6cd04cca6 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -16,7 +16,7 @@ use crate::registry::Registry;
 use crate::DiagnosticId;
 use crate::ToolMetadata;
 use crate::{CodeSuggestion, SubDiagnostic};
-use rustc_lint_defs::{Applicability, FutureBreakage};
+use rustc_lint_defs::Applicability;
 
 use rustc_data_structures::sync::Lrc;
 use rustc_span::hygiene::ExpnData;
@@ -134,17 +134,14 @@ impl Emitter for JsonEmitter {
         }
     }
 
-    fn emit_future_breakage_report(&mut self, diags: Vec<(FutureBreakage, crate::Diagnostic)>) {
+    fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) {
         let data: Vec<FutureBreakageItem> = diags
             .into_iter()
-            .map(|(breakage, mut diag)| {
+            .map(|mut diag| {
                 if diag.level == crate::Level::Allow {
                     diag.level = crate::Level::Warning;
                 }
-                FutureBreakageItem {
-                    future_breakage_date: breakage.date,
-                    diagnostic: Diagnostic::from_errors_diagnostic(&diag, self),
-                }
+                FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) }
             })
             .collect();
         let report = FutureIncompatReport { future_incompat_report: data };
@@ -326,7 +323,6 @@ struct ArtifactNotification<'a> {
 
 #[derive(Encodable)]
 struct FutureBreakageItem {
-    future_breakage_date: Option<&'static str>,
     diagnostic: Diagnostic,
 }
 
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index f8339d6e3f4..993a7c2c162 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -23,7 +23,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{self, Lock, Lrc};
 use rustc_data_structures::AtomicRef;
-use rustc_lint_defs::FutureBreakage;
 pub use rustc_lint_defs::{pluralize, Applicability};
 use rustc_serialize::json::Json;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@@ -790,7 +789,7 @@ impl Handler {
         self.inner.borrow_mut().emit_artifact_notification(path, artifact_type)
     }
 
-    pub fn emit_future_breakage_report(&self, diags: Vec<(FutureBreakage, Diagnostic)>) {
+    pub fn emit_future_breakage_report(&self, diags: Vec<Diagnostic>) {
         self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
     }
 
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 01291de51bd..5b1cd0bcb3f 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -6,7 +6,7 @@
 //! compiler code, rather than using their own custom pass. Those
 //! lints are all available in `rustc_lint::builtin`.
 
-use crate::{declare_lint, declare_lint_pass, FutureBreakage, FutureIncompatibilityReason};
+use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason};
 use rustc_span::edition::Edition;
 
 declare_lint! {
@@ -3176,9 +3176,7 @@ declare_lint! {
     "detects usage of old versions of certain proc-macro crates",
     @future_incompatible = FutureIncompatibleInfo {
         reference: "issue #83125 <https://github.com/rust-lang/rust/issues/83125>",
-        future_breakage: Some(FutureBreakage {
-            date: None
-        })
+        reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
     };
 }
 
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 89453e8e73a..001198226d9 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -152,10 +152,6 @@ pub struct FutureIncompatibleInfo {
     /// Set to false for lints that already include a more detailed
     /// explanation.
     pub explain_reason: bool,
-    /// Information about a future breakage, which will
-    /// be emitted in JSON messages to be displayed by Cargo
-    /// for upstream deps
-    pub future_breakage: Option<FutureBreakage>,
 }
 
 /// The reason for future incompatibility
@@ -164,6 +160,9 @@ pub enum FutureIncompatibilityReason {
     /// This will be an error in a future release
     /// for all editions
     FutureReleaseError,
+    /// This will be an error in a future release, and
+    /// Cargo should create a report even for dependencies
+    FutureReleaseErrorReportNow,
     /// Previously accepted code that will become an
     /// error in the provided edition
     EditionError(Edition),
@@ -182,18 +181,12 @@ impl FutureIncompatibilityReason {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
-pub struct FutureBreakage {
-    pub date: Option<&'static str>,
-}
-
 impl FutureIncompatibleInfo {
     pub const fn default_fields_for_macro() -> Self {
         FutureIncompatibleInfo {
             reference: "",
             reason: FutureIncompatibilityReason::FutureReleaseError,
             explain_reason: true,
-            future_breakage: None,
         }
     }
 }
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 63872ca9017..484e30027e5 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -8,7 +8,7 @@ use rustc_hir::HirId;
 use rustc_index::vec::IndexVec;
 use rustc_session::lint::{
     builtin::{self, FORBIDDEN_LINT_GROUPS},
-    FutureIncompatibilityReason, Level, Lint, LintId,
+    FutureIncompatibilityReason, FutureIncompatibleInfo, Level, Lint, LintId,
 };
 use rustc_session::{DiagnosticMessageId, Session};
 use rustc_span::hygiene::MacroKind;
@@ -223,8 +223,13 @@ pub fn struct_lint_level<'s, 'd>(
         let lint_id = LintId::of(lint);
         let future_incompatible = lint.future_incompatible;
 
-        let has_future_breakage =
-            future_incompatible.map_or(false, |incompat| incompat.future_breakage.is_some());
+        let has_future_breakage = matches!(
+            future_incompatible,
+            Some(FutureIncompatibleInfo {
+                reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
+                ..
+            })
+        );
 
         let mut err = match (level, span) {
             (Level::Allow, span) => {
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index c2645a09140..75cbb55239c 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -512,7 +512,7 @@ impl InitMaskCompressed {
 /// Transferring the initialization mask to other allocations.
 impl<Tag, Extra> Allocation<Tag, Extra> {
     /// Creates a run-length encoding of the initialization mask.
-    pub fn compress_uninit_range(&self, src: Pointer<Tag>, size: Size) -> InitMaskCompressed {
+    pub fn compress_uninit_range(&self, range: AllocRange) -> InitMaskCompressed {
         // Since we are copying `size` bytes from `src` to `dest + i * size` (`for i in 0..repeat`),
         // a naive initialization mask copying algorithm would repeatedly have to read the initialization mask from
         // the source and write it to the destination. Even if we optimized the memory accesses,
@@ -526,13 +526,13 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
         // where each element toggles the state.
 
         let mut ranges = smallvec::SmallVec::<[u64; 1]>::new();
-        let initial = self.init_mask.get(src.offset);
+        let initial = self.init_mask.get(range.start);
         let mut cur_len = 1;
         let mut cur = initial;
 
-        for i in 1..size.bytes() {
+        for i in 1..range.size.bytes() {
             // FIXME: optimize to bitshift the current uninitialized block's bits and read the top bit.
-            if self.init_mask.get(src.offset + Size::from_bytes(i)) == cur {
+            if self.init_mask.get(range.start + Size::from_bytes(i)) == cur {
                 cur_len += 1;
             } else {
                 ranges.push(cur_len);
@@ -550,24 +550,23 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
     pub fn mark_compressed_init_range(
         &mut self,
         defined: &InitMaskCompressed,
-        dest: Pointer<Tag>,
-        size: Size,
+        range: AllocRange,
         repeat: u64,
     ) {
         // An optimization where we can just overwrite an entire range of initialization
         // bits if they are going to be uniformly `1` or `0`.
         if defined.ranges.len() <= 1 {
             self.init_mask.set_range_inbounds(
-                dest.offset,
-                dest.offset + size * repeat, // `Size` operations
+                range.start,
+                range.start + range.size * repeat, // `Size` operations
                 defined.initial,
             );
             return;
         }
 
         for mut j in 0..repeat {
-            j *= size.bytes();
-            j += dest.offset.bytes();
+            j *= range.size.bytes();
+            j += range.start.bytes();
             let mut cur = defined.initial;
             for range in &defined.ranges {
                 let old_j = j;
diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs
index 648a7abfdc7..8cd459265df 100644
--- a/compiler/rustc_mir/src/interpret/eval_context.rs
+++ b/compiler/rustc_mir/src/interpret/eval_context.rs
@@ -18,8 +18,8 @@ use rustc_span::{Pos, Span};
 use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
 
 use super::{
-    Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, Operand, Place, PlaceTy,
-    ScalarMaybeUninit, StackPopJump,
+    Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place,
+    PlaceTy, ScalarMaybeUninit, StackPopJump,
 };
 use crate::transform::validate::equal_up_to_regions;
 use crate::util::storage::AlwaysLiveLocals;
@@ -900,7 +900,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // due to the local having ZST type.
             let ptr = ptr.assert_ptr();
             trace!("deallocating local: {:?}", self.memory.dump_alloc(ptr.alloc_id));
-            self.memory.deallocate_local(ptr)?;
+            self.memory.deallocate(ptr, None, MemoryKind::Stack)?;
         };
         Ok(())
     }
diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs
index cb929c21850..5f719cc1607 100644
--- a/compiler/rustc_mir/src/interpret/memory.rs
+++ b/compiler/rustc_mir/src/interpret/memory.rs
@@ -276,17 +276,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         Ok(new_ptr)
     }
 
-    /// Deallocate a local, or do nothing if that local has been made into a global.
-    pub fn deallocate_local(&mut self, ptr: Pointer<M::PointerTag>) -> InterpResult<'tcx> {
-        // The allocation might be already removed by global interning.
-        // This can only really happen in the CTFE instance, not in miri.
-        if self.alloc_map.contains_key(&ptr.alloc_id) {
-            self.deallocate(ptr, None, MemoryKind::Stack)
-        } else {
-            Ok(())
-        }
-    }
-
     pub fn deallocate(
         &mut self,
         ptr: Pointer<M::PointerTag>,
@@ -1049,7 +1038,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             num_copies,
         );
         // Prepare a copy of the initialization mask.
-        let compressed = src_alloc.compress_uninit_range(src, size);
+        let compressed = src_alloc.compress_uninit_range(alloc_range(src.offset, size));
         // This checks relocation edges on the src.
         let src_bytes = src_alloc
             .get_bytes_with_uninit_and_ptr(&tcx, alloc_range(src.offset, size))
@@ -1110,7 +1099,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         }
 
         // now fill in all the "init" data
-        dest_alloc.mark_compressed_init_range(&compressed, dest, size, num_copies);
+        dest_alloc.mark_compressed_init_range(
+            &compressed,
+            alloc_range(dest.offset, size),
+            num_copies,
+        );
         // copy the relocations to the destination
         dest_alloc.mark_relocation_range(relocations);
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index cb9c89324d6..3859b22223c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -490,17 +490,29 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 // convert the dereferenced constant to a pattern that is the sub-pattern of the
                 // deref pattern.
                 _ => {
-                    let old = self.behind_reference.replace(true);
-                    // In case there are structural-match violations somewhere in this subpattern,
-                    // we fall back to a const pattern. If we do not do this, we may end up with
-                    // a !structural-match constant that is not of reference type, which makes it
-                    // very hard to invoke `PartialEq::eq` on it as a fallback.
-                    let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
-                        Ok(subpattern) => PatKind::Deref { subpattern },
-                        Err(_) => PatKind::Constant { value: cv },
-                    };
-                    self.behind_reference.set(old);
-                    val
+                    if !pointee_ty.is_sized(tcx.at(span), param_env) {
+                        // `tcx.deref_const()` below will ICE with an unsized type
+                        // (except slices, which are handled in a separate arm above).
+                        let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
+                        if self.include_lint_checks {
+                            tcx.sess.span_err(span, &msg);
+                        } else {
+                            tcx.sess.delay_span_bug(span, &msg);
+                        }
+                        PatKind::Wild
+                    } else {
+                        let old = self.behind_reference.replace(true);
+                        // In case there are structural-match violations somewhere in this subpattern,
+                        // we fall back to a const pattern. If we do not do this, we may end up with
+                        // a !structural-match constant that is not of reference type, which makes it
+                        // very hard to invoke `PartialEq::eq` on it as a fallback.
+                        let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
+                            Ok(subpattern) => PatKind::Deref { subpattern },
+                            Err(_) => PatKind::Constant { value: cv },
+                        };
+                        self.behind_reference.set(old);
+                        val
+                    }
                 }
             },
             ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 8270bbbe8fd..f3ce78d2d78 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -20,8 +20,7 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
 use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
-use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported};
-use rustc_lint_defs::FutureBreakage;
+use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
 use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
@@ -317,23 +316,7 @@ impl Session {
         if diags.is_empty() {
             return;
         }
-        // If any future-breakage lints were registered, this lint store
-        // should be available
-        let lint_store = self.lint_store.get().expect("`lint_store` not initialized!");
-        let diags_and_breakage: Vec<(FutureBreakage, Diagnostic)> = diags
-            .into_iter()
-            .map(|diag| {
-                let lint_name = match &diag.code {
-                    Some(DiagnosticId::Lint { name, has_future_breakage: true, .. }) => name,
-                    _ => panic!("Unexpected code in diagnostic {:?}", diag),
-                };
-                let lint = lint_store.name_to_lint(&lint_name);
-                let future_breakage =
-                    lint.lint.future_incompatible.unwrap().future_breakage.unwrap();
-                (future_breakage, diag)
-            })
-            .collect();
-        self.parse_sess.span_diagnostic.emit_future_breakage_report(diags_and_breakage);
+        self.parse_sess.span_diagnostic.emit_future_breakage_report(diags);
     }
 
     pub fn local_stable_crate_id(&self) -> StableCrateId {
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index 3dfbd98fa6c..5122421ea8c 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -79,7 +79,7 @@ use crate::ptr;
 /// // a `MaybeUninit<T>` may be invalid, and hence this is not UB:
 /// let mut x = MaybeUninit::<&i32>::uninit();
 /// // Set it to a valid value.
-/// unsafe { x.as_mut_ptr().write(&0); }
+/// x.write(&0);
 /// // Extract the initialized data -- this is only allowed *after* properly
 /// // initializing `x`!
 /// let x = unsafe { x.assume_init() };
@@ -135,7 +135,7 @@ use crate::ptr;
 ///     // this loop, we have a memory leak, but there is no memory safety
 ///     // issue.
 ///     for elem in &mut data[..] {
-///         *elem = MaybeUninit::new(vec![42]);
+///         elem.write(vec![42]);
 ///     }
 ///
 ///     // Everything is initialized. Transmute the array to the
@@ -161,7 +161,7 @@ use crate::ptr;
 /// let mut data_len: usize = 0;
 ///
 /// for elem in &mut data[0..500] {
-///     *elem = MaybeUninit::new(String::from("hello"));
+///     elem.write(String::from("hello"));
 ///     data_len += 1;
 /// }
 ///
@@ -410,7 +410,7 @@ impl<T> MaybeUninit<T> {
     /// (now safely initialized) contents of `self`.
     ///
     /// As the content is stored inside a `MaybeUninit`, the destructor is not
-    /// ran for the inner data if the MaybeUninit leaves scope without a call to
+    /// run for the inner data if the MaybeUninit leaves scope without a call to
     /// [`assume_init`], [`assume_init_drop`], or similar. Code that receives
     /// the mutable reference returned by this function needs to keep this in
     /// mind. The safety model of Rust regards leaks as safe, but they are
@@ -426,7 +426,6 @@ impl<T> MaybeUninit<T> {
     /// Correct usage of this method:
     ///
     /// ```rust
-    /// #![feature(maybe_uninit_extra)]
     /// use std::mem::MaybeUninit;
     ///
     /// let mut x = MaybeUninit::<Vec<u8>>::uninit();
@@ -445,7 +444,6 @@ impl<T> MaybeUninit<T> {
     /// This usage of the method causes a leak:
     ///
     /// ```rust
-    /// #![feature(maybe_uninit_extra)]
     /// use std::mem::MaybeUninit;
     ///
     /// let mut x = MaybeUninit::<String>::uninit();
@@ -456,8 +454,38 @@ impl<T> MaybeUninit<T> {
     /// // x is initialized now:
     /// let s = unsafe { x.assume_init() };
     /// ```
-    #[unstable(feature = "maybe_uninit_extra", issue = "63567")]
-    #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")]
+    ///
+    /// This method can be used to avoid unsafe in some cases. The example below
+    /// shows a part of an implementation of a fixed sized arena that lends out
+    /// pinned references.
+    /// With `write`, we can avoid the need to write through a raw pointer:
+    ///
+    /// ```rust
+    /// #![feature(maybe_uninit_extra)]
+    /// use core::pin::Pin;
+    /// use core::mem::MaybeUninit;
+    ///
+    /// struct PinArena<T> {
+    ///     memory: Box<[MaybeUninit<T>]>,
+    ///     len: usize,
+    /// }
+    ///
+    /// impl <T> PinArena<T> {
+    ///     pub fn capacity(&self) -> usize {
+    ///         self.memory.len()
+    ///     }
+    ///     pub fn push(&mut self, val: T) -> Pin<&mut T> {
+    ///         if self.len >= self.capacity() {
+    ///             panic!("Attempted to push to a full pin arena!");
+    ///         }
+    ///         let ref_ = self.memory[self.len].write(val);
+    ///         self.len += 1;
+    ///         unsafe { Pin::new_unchecked(ref_) }
+    ///     }
+    /// }
+    /// ```
+    #[stable(feature = "maybe_uninit_write", since = "1.55.0")]
+    #[rustc_const_unstable(feature = "const_maybe_uninit_write", issue = "63567")]
     #[inline(always)]
     pub const fn write(&mut self, val: T) -> &mut T {
         *self = MaybeUninit::new(val);
@@ -478,7 +506,7 @@ impl<T> MaybeUninit<T> {
     /// use std::mem::MaybeUninit;
     ///
     /// let mut x = MaybeUninit::<Vec<u32>>::uninit();
-    /// unsafe { x.as_mut_ptr().write(vec![0, 1, 2]); }
+    /// x.write(vec![0, 1, 2]);
     /// // Create a reference into the `MaybeUninit<T>`. This is okay because we initialized it.
     /// let x_vec = unsafe { &*x.as_ptr() };
     /// assert_eq!(x_vec.len(), 3);
@@ -515,7 +543,7 @@ impl<T> MaybeUninit<T> {
     /// use std::mem::MaybeUninit;
     ///
     /// let mut x = MaybeUninit::<Vec<u32>>::uninit();
-    /// unsafe { x.as_mut_ptr().write(vec![0, 1, 2]); }
+    /// x.write(vec![0, 1, 2]);
     /// // Create a reference into the `MaybeUninit<Vec<u32>>`.
     /// // This is okay because we initialized it.
     /// let x_vec = unsafe { &mut *x.as_mut_ptr() };
@@ -574,7 +602,7 @@ impl<T> MaybeUninit<T> {
     /// use std::mem::MaybeUninit;
     ///
     /// let mut x = MaybeUninit::<bool>::uninit();
-    /// unsafe { x.as_mut_ptr().write(true); }
+    /// x.write(true);
     /// let x_init = unsafe { x.assume_init() };
     /// assert_eq!(x_init, true);
     /// ```
@@ -723,7 +751,7 @@ impl<T> MaybeUninit<T> {
     ///
     /// let mut x = MaybeUninit::<Vec<u32>>::uninit();
     /// // Initialize `x`:
-    /// unsafe { x.as_mut_ptr().write(vec![1, 2, 3]); }
+    /// x.write(vec![1, 2, 3]);
     /// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to
     /// // create a shared reference to it:
     /// let x: &Vec<u32> = unsafe {
@@ -897,9 +925,9 @@ impl<T> MaybeUninit<T> {
     /// use std::mem::MaybeUninit;
     ///
     /// let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
-    /// array[0] = MaybeUninit::new(0);
-    /// array[1] = MaybeUninit::new(1);
-    /// array[2] = MaybeUninit::new(2);
+    /// array[0].write(0);
+    /// array[1].write(1);
+    /// array[2].write(2);
     ///
     /// // SAFETY: Now safe as we initialised all elements
     /// let array = unsafe {
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index 04f13cdeb88..ae0cea985d7 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -209,32 +209,6 @@ impl<T> Cursor<T>
 where
     T: AsRef<[u8]>,
 {
-    /// Returns the remaining length.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(cursor_remaining)]
-    /// use std::io::Cursor;
-    ///
-    /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
-    ///
-    /// assert_eq!(buff.remaining(), 5);
-    ///
-    /// buff.set_position(2);
-    /// assert_eq!(buff.remaining(), 3);
-    ///
-    /// buff.set_position(4);
-    /// assert_eq!(buff.remaining(), 1);
-    ///
-    /// buff.set_position(6);
-    /// assert_eq!(buff.remaining(), 0);
-    /// ```
-    #[unstable(feature = "cursor_remaining", issue = "86369")]
-    pub fn remaining(&self) -> u64 {
-        (self.inner.as_ref().len() as u64).checked_sub(self.pos).unwrap_or(0)
-    }
-
     /// Returns the remaining slice.
     ///
     /// # Examples
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 9daeee711ad..ad8975c03f1 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -277,7 +277,7 @@ pub use self::error::{Error, ErrorKind, Result};
 pub use self::stdio::set_output_capture;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
-#[unstable(feature = "stdio_locked", issue = "none")]
+#[unstable(feature = "stdio_locked", issue = "86845")]
 pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 293f0e31ce0..206687e38fb 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -347,7 +347,7 @@ pub fn stdin() -> Stdin {
 ///     Ok(())
 /// }
 /// ```
-#[unstable(feature = "stdio_locked", issue = "none")]
+#[unstable(feature = "stdio_locked", issue = "86845")]
 pub fn stdin_locked() -> StdinLock<'static> {
     stdin().into_locked()
 }
@@ -442,7 +442,7 @@ impl Stdin {
     ///     Ok(())
     /// }
     /// ```
-    #[unstable(feature = "stdio_locked", issue = "none")]
+    #[unstable(feature = "stdio_locked", issue = "86845")]
     pub fn into_locked(self) -> StdinLock<'static> {
         self.lock_any()
     }
@@ -668,7 +668,7 @@ pub fn stdout() -> Stdout {
 ///     Ok(())
 /// }
 /// ```
-#[unstable(feature = "stdio_locked", issue = "none")]
+#[unstable(feature = "stdio_locked", issue = "86845")]
 pub fn stdout_locked() -> StdoutLock<'static> {
     stdout().into_locked()
 }
@@ -745,7 +745,7 @@ impl Stdout {
     ///     Ok(())
     /// }
     /// ```
-    #[unstable(feature = "stdio_locked", issue = "none")]
+    #[unstable(feature = "stdio_locked", issue = "86845")]
     pub fn into_locked(self) -> StdoutLock<'static> {
         self.lock_any()
     }
@@ -945,7 +945,7 @@ pub fn stderr() -> Stderr {
 ///     Ok(())
 /// }
 /// ```
-#[unstable(feature = "stdio_locked", issue = "none")]
+#[unstable(feature = "stdio_locked", issue = "86845")]
 pub fn stderr_locked() -> StderrLock<'static> {
     stderr().into_locked()
 }
@@ -1005,7 +1005,7 @@ impl Stderr {
     ///     Ok(())
     /// }
     /// ```
-    #[unstable(feature = "stdio_locked", issue = "none")]
+    #[unstable(feature = "stdio_locked", issue = "86845")]
     pub fn into_locked(self) -> StderrLock<'static> {
         self.lock_any()
     }
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 13ee909afd5..8a1b6df0daf 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -139,11 +139,17 @@ macro_rules! install {
 
 install!((self, builder, _config),
     Docs, "src/doc", _config.docs, only_hosts: false, {
-        let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
-        install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
+        if let Some(tarball) = builder.ensure(dist::Docs { host: self.target }) {
+            install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
+        } else {
+            panic!("docs are not available to install, \
+                check that `build.docs` is true in `config.toml`");
+        }
     };
     Std, "library/std", true, only_hosts: false, {
         for target in &builder.targets {
+            // `expect` should be safe, only None when host != build, but this
+            // only runs when host == build
             let tarball = builder.ensure(dist::Std {
                 compiler: self.compiler,
                 target: *target
@@ -165,10 +171,15 @@ install!((self, builder, _config),
         }
     };
     RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
-        let tarball = builder
-            .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
-            .expect("missing rust-analyzer");
-        install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
+        if let Some(tarball) =
+            builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
+        {
+            install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
+        } else {
+            builder.info(
+                &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target),
+            );
+        }
     };
     Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
         let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
@@ -212,6 +223,8 @@ install!((self, builder, _config),
         }
     };
     Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
+        // `expect` should be safe, only None with host != build, but this
+        // only uses the `build` compiler
         let tarball = builder.ensure(dist::Analysis {
             // Find the actual compiler (handling the full bootstrap option) which
             // produced the save-analysis data because that data isn't copied
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index e4d6a3f587b..61ffae47e2a 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -907,27 +907,25 @@ impl Step for RustdocGUI {
         // We remove existing folder to be sure there won't be artifacts remaining.
         let _ = fs::remove_dir_all(&out_dir);
 
-        let mut nb_generated = 0;
+        let src_path = "src/test/rustdoc-gui/src";
         // We generate docs for the libraries present in the rustdoc-gui's src folder.
-        let libs_dir = builder.build.src.join("src/test/rustdoc-gui/src");
-        for entry in libs_dir.read_dir().expect("read_dir call failed") {
-            let entry = entry.expect("invalid entry");
-            let path = entry.path();
-            if path.extension().map(|e| e == "rs").unwrap_or(false) {
-                let mut command = builder.rustdoc_cmd(self.compiler);
-                command.arg(path).arg("-o").arg(&out_dir);
-                builder.run(&mut command);
-                nb_generated += 1;
-            }
-        }
-        assert!(nb_generated > 0, "no documentation was generated...");
+        let mut cargo = Command::new(&builder.initial_cargo);
+        cargo
+            .arg("doc")
+            .arg("--workspace")
+            .arg("--target-dir")
+            .arg(&out_dir)
+            .env("RUSTDOC", builder.rustdoc(self.compiler))
+            .env("RUSTC", builder.rustc(self.compiler))
+            .current_dir(&builder.build.src.join(src_path));
+        builder.run(&mut cargo);
 
         // We now run GUI tests.
         let mut command = Command::new(&nodejs);
         command
             .arg(builder.build.src.join("src/tools/rustdoc-gui/tester.js"))
             .arg("--doc-folder")
-            .arg(out_dir)
+            .arg(out_dir.join("doc"))
             .arg("--tests-folder")
             .arg(builder.build.src.join("src/test/rustdoc-gui"));
         for path in &builder.paths {
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index c257d362694..8f74a48547d 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -98,7 +98,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     visibility: Inherited,
                     def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
                     kind: box ImplItem(Impl {
-                        span: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
+                        span: Span::from_rustc_span(self.cx.tcx.def_span(impl_def_id)),
                         unsafety: hir::Unsafety::Normal,
                         generics: (
                             self.cx.tcx.generics_of(impl_def_id),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 0ff708d01f5..80aaae15801 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -227,20 +227,6 @@ impl<'tcx> Clean<GenericBound> for ty::PolyTraitRef<'tcx> {
     }
 }
 
-impl<'tcx> Clean<Option<Vec<GenericBound>>> for InternalSubsts<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'_>) -> Option<Vec<GenericBound>> {
-        let mut v = Vec::new();
-        v.extend(self.regions().filter_map(|r| r.clean(cx)).map(GenericBound::Outlives));
-        v.extend(self.types().map(|t| {
-            GenericBound::TraitBound(
-                PolyTrait { trait_: t.clean(cx), generic_params: Vec::new() },
-                hir::TraitBoundModifier::None,
-            )
-        }));
-        if !v.is_empty() { Some(v) } else { None }
-    }
-}
-
 impl Clean<Lifetime> for hir::Lifetime {
     fn clean(&self, cx: &mut DocContext<'_>) -> Lifetime {
         let def = cx.tcx.named_region(self.hir_id);
@@ -296,12 +282,6 @@ impl Clean<Constant> for hir::ConstArg {
     }
 }
 
-impl Clean<Lifetime> for ty::GenericParamDef {
-    fn clean(&self, _cx: &mut DocContext<'_>) -> Lifetime {
-        Lifetime(self.name)
-    }
-}
-
 impl Clean<Option<Lifetime>> for ty::RegionKind {
     fn clean(&self, _cx: &mut DocContext<'_>) -> Option<Lifetime> {
         match *self {
@@ -1764,12 +1744,6 @@ impl Clean<Variant> for hir::VariantData<'_> {
     }
 }
 
-impl Clean<Span> for rustc_span::Span {
-    fn clean(&self, _cx: &mut DocContext<'_>) -> Span {
-        Span::from_rustc_span(*self)
-    }
-}
-
 impl Clean<Path> for hir::Path<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Path {
         Path {
@@ -2193,22 +2167,3 @@ impl Clean<TypeBindingKind> for hir::TypeBindingKind<'_> {
         }
     }
 }
-
-enum SimpleBound {
-    TraitBound(Vec<PathSegment>, Vec<SimpleBound>, Vec<GenericParamDef>, hir::TraitBoundModifier),
-    Outlives(Lifetime),
-}
-
-impl From<GenericBound> for SimpleBound {
-    fn from(bound: GenericBound) -> Self {
-        match bound.clone() {
-            GenericBound::Outlives(l) => SimpleBound::Outlives(l),
-            GenericBound::TraitBound(t, mod_) => match t.trait_ {
-                Type::ResolvedPath { path, .. } => {
-                    SimpleBound::TraitBound(path.segments, Vec::new(), t.generic_params, mod_)
-                }
-                _ => panic!("Unexpected bound {:?}", bound),
-            },
-        }
-    }
-}
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 98128878999..38ddbb3ad74 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -683,6 +683,9 @@ function hideThemeButtonState() {
             });
         }
 
+        var currentNbImpls = implementors.getElementsByClassName("impl").length;
+        var traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
+        var baseIdName = "impl-" + traitName + "-";
         var libs = Object.getOwnPropertyNames(imp);
         for (var i = 0, llength = libs.length; i < llength; ++i) {
             if (libs[i] === window.currentCrate) { continue; }
@@ -705,6 +708,7 @@ function hideThemeButtonState() {
 
                 var code = document.createElement("code");
                 code.innerHTML = struct.text;
+                addClass(code, "in-band");
 
                 onEachLazy(code.getElementsByTagName("a"), function(elem) {
                     var href = elem.getAttribute("href");
@@ -714,12 +718,18 @@ function hideThemeButtonState() {
                     }
                 });
 
-                var display = document.createElement("h3");
+                var currentId = baseIdName + currentNbImpls;
+                var anchor = document.createElement("a");
+                anchor.href = "#" + currentId;
+                addClass(anchor, "anchor");
+
+                var display = document.createElement("div");
+                display.id = currentId;
                 addClass(display, "impl");
-                display.innerHTML = "<span class=\"in-band\"><table class=\"table-display\">" +
-                    "<tbody><tr><td><code>" + code.outerHTML + "</code></td><td></td></tr>" +
-                    "</tbody></table></span>";
+                display.appendChild(anchor);
+                display.appendChild(code);
                 list.appendChild(display);
+                currentNbImpls += 1;
             }
         }
     };
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 44a3faf6f7b..21bd3ebd21b 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1610,6 +1610,8 @@ impl Disambiguator {
             return Suggestion::Macro;
         } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
             return Suggestion::Function;
+        } else if kind == DefKind::Field {
+            return Suggestion::RemoveDisambiguator;
         }
 
         let prefix = match kind {
@@ -1674,6 +1676,8 @@ enum Suggestion {
     Function,
     /// `m!`
     Macro,
+    /// `foo` without any disambiguator
+    RemoveDisambiguator,
 }
 
 impl Suggestion {
@@ -1682,6 +1686,7 @@ impl Suggestion {
             Self::Prefix(x) => format!("prefix with `{}@`", x).into(),
             Self::Function => "add parentheses".into(),
             Self::Macro => "add an exclamation mark".into(),
+            Self::RemoveDisambiguator => "remove the disambiguator".into(),
         }
     }
 
@@ -1691,6 +1696,7 @@ impl Suggestion {
             Self::Prefix(prefix) => format!("{}@{}", prefix, path_str),
             Self::Function => format!("{}()", path_str),
             Self::Macro => format!("{}!", path_str),
+            Self::RemoveDisambiguator => path_str.into(),
         }
     }
 }
diff --git a/src/test/rustdoc-gui/implementors.goml b/src/test/rustdoc-gui/implementors.goml
new file mode 100644
index 00000000000..a4db5cee7c7
--- /dev/null
+++ b/src/test/rustdoc-gui/implementors.goml
@@ -0,0 +1,16 @@
+// The goal of this test is to check that the external trait implementors, generated with JS,
+// have the same display than the "local" ones.
+goto: file://|DOC_PATH|/implementors/trait.Whatever.html
+assert: "#implementors-list"
+// There are supposed to be two implementors listed.
+assert-count: ("#implementors-list > .impl", 2)
+// Now we check that both implementors have an anchor, an ID and a similar DOM.
+assert: ("#implementors-list > .impl:nth-child(1) > a.anchor")
+assert-attribute: ("#implementors-list > .impl:nth-child(1)", {"id": "impl-Whatever"})
+assert-attribute: ("#implementors-list > .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever"})
+assert: "#implementors-list > .impl:nth-child(1) > code.in-band"
+
+assert: ("#implementors-list > .impl:nth-child(2) > a.anchor")
+assert-attribute: ("#implementors-list > .impl:nth-child(2)", {"id": "impl-Whatever-1"})
+assert-attribute: ("#implementors-list > .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"})
+assert: "#implementors-list > .impl:nth-child(2) > code.in-band"
diff --git a/src/test/rustdoc-gui/src/Cargo.lock b/src/test/rustdoc-gui/src/Cargo.lock
new file mode 100644
index 00000000000..a72ccffc6dd
--- /dev/null
+++ b/src/test/rustdoc-gui/src/Cargo.lock
@@ -0,0 +1,18 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "implementors"
+version = "0.1.0"
+
+[[package]]
+name = "lib2"
+version = "0.1.0"
+dependencies = [
+ "implementors",
+]
+
+[[package]]
+name = "test_docs"
+version = "0.1.0"
diff --git a/src/test/rustdoc-gui/src/Cargo.toml b/src/test/rustdoc-gui/src/Cargo.toml
new file mode 100644
index 00000000000..9c8c0c636f0
--- /dev/null
+++ b/src/test/rustdoc-gui/src/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+members = [
+    "test_docs",
+    "lib2",
+    "implementors",
+]
diff --git a/src/test/rustdoc-gui/src/implementors/Cargo.toml b/src/test/rustdoc-gui/src/implementors/Cargo.toml
new file mode 100644
index 00000000000..7ef1052c49f
--- /dev/null
+++ b/src/test/rustdoc-gui/src/implementors/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "implementors"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/src/test/rustdoc-gui/src/implementors/lib.rs b/src/test/rustdoc-gui/src/implementors/lib.rs
new file mode 100644
index 00000000000..4b2f6962e30
--- /dev/null
+++ b/src/test/rustdoc-gui/src/implementors/lib.rs
@@ -0,0 +1,7 @@
+pub trait Whatever {
+    fn method() {}
+}
+
+pub struct Struct;
+
+impl Whatever for Struct {}
diff --git a/src/test/rustdoc-gui/src/lib2/Cargo.toml b/src/test/rustdoc-gui/src/lib2/Cargo.toml
new file mode 100644
index 00000000000..6041a793f08
--- /dev/null
+++ b/src/test/rustdoc-gui/src/lib2/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "lib2"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+implementors = { path = "../implementors" }
diff --git a/src/test/rustdoc-gui/src/lib2.rs b/src/test/rustdoc-gui/src/lib2/lib.rs
index 77d384f3113..72ef3cbd202 100644
--- a/src/test/rustdoc-gui/src/lib2.rs
+++ b/src/test/rustdoc-gui/src/lib2/lib.rs
@@ -31,3 +31,5 @@ impl Trait for Foo {
     type X = u32;
     const Y: u32 = 0;
 }
+
+impl implementors::Whatever for Foo {}
diff --git a/src/test/rustdoc-gui/src/lib2/src/lib.rs b/src/test/rustdoc-gui/src/lib2/src/lib.rs
new file mode 100644
index 00000000000..31e1bb209f9
--- /dev/null
+++ b/src/test/rustdoc-gui/src/lib2/src/lib.rs
@@ -0,0 +1,7 @@
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        assert_eq!(2 + 2, 4);
+    }
+}
diff --git a/src/test/rustdoc-gui/src/test_docs/Cargo.toml b/src/test/rustdoc-gui/src/test_docs/Cargo.toml
new file mode 100644
index 00000000000..7f3c65746fc
--- /dev/null
+++ b/src/test/rustdoc-gui/src/test_docs/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "test_docs"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/src/test/rustdoc-gui/src/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs
index 3e753cb4de8..3e753cb4de8 100644
--- a/src/test/rustdoc-gui/src/lib.rs
+++ b/src/test/rustdoc-gui/src/test_docs/lib.rs
diff --git a/src/test/rustdoc-ui/intra-doc/field-ice.rs b/src/test/rustdoc-ui/intra-doc/field-ice.rs
new file mode 100644
index 00000000000..c5d501e38da
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/field-ice.rs
@@ -0,0 +1,11 @@
+#![deny(rustdoc::broken_intra_doc_links)]
+//~^NOTE the lint level is defined here
+
+/// [`Foo::bar`]
+/// [`Foo::bar()`]
+//~^ERROR incompatible link kind for `Foo::bar`
+//~|HELP to link to the field, remove the disambiguator
+//~|NOTE this link resolved to a field, which is not a function
+pub struct Foo {
+    pub bar: u8
+}
diff --git a/src/test/rustdoc-ui/intra-doc/field-ice.stderr b/src/test/rustdoc-ui/intra-doc/field-ice.stderr
new file mode 100644
index 00000000000..ccb05b84a72
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/field-ice.stderr
@@ -0,0 +1,15 @@
+error: incompatible link kind for `Foo::bar`
+  --> $DIR/field-ice.rs:5:6
+   |
+LL | /// [`Foo::bar()`]
+   |      ^^^^^^^^^^^^ help: to link to the field, remove the disambiguator: ``Foo::bar``
+   |
+note: the lint level is defined here
+  --> $DIR/field-ice.rs:1:9
+   |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this link resolved to a field, which is not a function
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/issue-87046.rs b/src/test/ui/consts/issue-87046.rs
new file mode 100644
index 00000000000..1f147439f8b
--- /dev/null
+++ b/src/test/ui/consts/issue-87046.rs
@@ -0,0 +1,34 @@
+// Regression test for the ICE described in #87046.
+
+#![crate_type="lib"]
+#![allow(unreachable_patterns)]
+#![feature(const_fn_union)]
+
+#[derive(PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Username(str);
+
+pub const ROOT_USER: &Username = Username::from_str("root");
+
+impl Username {
+    pub const fn from_str(raw: &str) -> &Self {
+        union Transmute<'a> {
+            raw: &'a str,
+            typed: &'a Username,
+        }
+
+        unsafe { Transmute { raw }.typed }
+    }
+
+    pub const fn as_str(&self) -> &str {
+        &self.0
+    }
+
+    pub fn is_root(&self) -> bool {
+        match self {
+            ROOT_USER => true,
+            //~^ ERROR: cannot use unsized non-slice type `Username` in constant patterns
+            _ => false,
+        }
+    }
+}
diff --git a/src/test/ui/consts/issue-87046.stderr b/src/test/ui/consts/issue-87046.stderr
new file mode 100644
index 00000000000..5da7a9e2390
--- /dev/null
+++ b/src/test/ui/consts/issue-87046.stderr
@@ -0,0 +1,8 @@
+error: cannot use unsized non-slice type `Username` in constant patterns
+  --> $DIR/issue-87046.rs:29:13
+   |
+LL |             ROOT_USER => true,
+   |             ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer
-Subproject e5c1c8cf2fcfae3e15c8bcf5256e84cad3bd343
+Subproject fe00358888a24c64878abc15f09b0e60e16db9d