about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--library/alloc/src/collections/btree/map.rs4
-rw-r--r--library/alloc/src/collections/btree/navigate.rs104
-rw-r--r--library/core/src/num/mod.rs12
-rw-r--r--library/core/src/ptr/const_ptr.rs32
-rw-r--r--library/core/src/ptr/mut_ptr.rs32
-rw-r--r--library/core/src/str/mod.rs15
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/collections/hash/map.rs8
-rw-r--r--library/std/src/net/ip.rs13
-rw-r--r--library/std/src/panicking.rs9
-rw-r--r--library/std/src/rt.rs10
-rw-r--r--library/std/src/sys/unix/fd.rs4
-rw-r--r--library/std/src/sys_common/backtrace.rs39
-rw-r--r--library/test/src/lib.rs5
-rw-r--r--src/etc/gdb_providers.py11
-rw-r--r--src/etc/lldb_providers.py11
-rw-r--r--src/etc/natvis/libstd.natvis35
-rw-r--r--src/librustc_ast/ast.rs4
-rw-r--r--src/librustc_ast/attr/mod.rs25
-rw-r--r--src/librustc_ast/mut_visit.rs2
-rw-r--r--src/librustc_ast/token.rs13
-rw-r--r--src/librustc_ast/util/comments.rs120
-rw-r--r--src/librustc_ast/util/comments/tests.rs46
-rw-r--r--src/librustc_ast/visit.rs2
-rw-r--r--src/librustc_ast_lowering/expr.rs2
-rw-r--r--src/librustc_ast_lowering/lib.rs2
-rw-r--r--src/librustc_ast_pretty/pprust.rs52
-rw-r--r--src/librustc_error_codes/error_codes/E0746.md10
-rw-r--r--src/librustc_error_codes/error_codes/E0747.md4
-rw-r--r--src/librustc_expand/parse/lexer/tests.rs16
-rw-r--r--src/librustc_expand/parse/tests.rs6
-rw-r--r--src/librustc_expand/proc_macro_server.rs9
-rw-r--r--src/librustc_feature/active.rs2
-rw-r--r--src/librustc_hir/def.rs12
-rw-r--r--src/librustc_incremental/assert_module_sources.rs11
-rw-r--r--src/librustc_metadata/rmeta/decoder/cstore_impl.rs2
-rw-r--r--src/librustc_middle/ty/flags.rs6
-rw-r--r--src/librustc_middle/ty/fold.rs6
-rw-r--r--src/librustc_middle/ty/instance.rs97
-rw-r--r--src/librustc_middle/ty/mod.rs4
-rw-r--r--src/librustc_mir/transform/const_prop.rs130
-rw-r--r--src/librustc_parse/lexer/mod.rs24
-rw-r--r--src/librustc_parse/lib.rs6
-rw-r--r--src/librustc_parse/parser/attr.rs15
-rw-r--r--src/librustc_parse/parser/diagnostics.rs2
-rw-r--r--src/librustc_parse/parser/item.rs4
-rw-r--r--src/librustc_parse/parser/mod.rs18
-rw-r--r--src/librustc_resolve/diagnostics.rs7
-rw-r--r--src/librustc_save_analysis/lib.rs9
-rw-r--r--src/librustc_trait_selection/traits/select/mod.rs2
-rw-r--r--src/librustdoc/clean/types.rs9
-rw-r--r--src/librustdoc/clean/utils.rs3
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs188
-rw-r--r--src/librustdoc/test.rs7
-rw-r--r--src/test/codegen-units/polymorphization/pr-75255.rs52
-rw-r--r--src/test/debuginfo/function-arguments-naked.rs10
-rw-r--r--src/test/debuginfo/pretty-std-collections-hash.rs30
-rw-r--r--src/test/mir-opt/const-promotion-extern-static.rs2
-rw-r--r--src/test/mir-opt/const_allocation.rs2
-rw-r--r--src/test/mir-opt/const_allocation2.rs2
-rw-r--r--src/test/mir-opt/const_allocation3.rs2
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit85
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit85
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.rs7
-rw-r--r--src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff6
-rw-r--r--src/test/mir-opt/inline/inline-into-box-place.rs2
-rw-r--r--src/test/rustdoc-ui/auxiliary/extern_macros.rs7
-rw-r--r--src/test/rustdoc-ui/doctest-output.rs12
-rw-r--r--src/test/rustdoc-ui/doctest-output.stdout9
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs68
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr95
-rw-r--r--src/test/rustdoc/intra-link-trait-item.rs12
-rw-r--r--src/test/ui/asm/bad-arch.rs18
-rw-r--r--src/test/ui/asm/bad-arch.stderr8
-rw-r--r--src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs23
-rw-r--r--src/test/ui/feature-gates/feature-gate-asm.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-asm2.rs2
-rw-r--r--src/test/ui/panics/issue-47429-short-backtraces.rs18
-rw-r--r--src/test/ui/panics/issue-47429-short-backtraces.run.stderr5
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fn.rs29
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fnmut.rs34
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fnonce.rs34
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/other.rs38
-rw-r--r--src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs10
-rw-r--r--src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr15
-rw-r--r--src/test/ui/proc-macro/doc-comment-preserved.rs24
-rw-r--r--src/test/ui/proc-macro/doc-comment-preserved.stdout54
-rw-r--r--src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr6
-rw-r--r--src/test/ui/simd/simd-intrinsic-generic-bitmask.rs1
-rw-r--r--src/test/ui/simd/simd-intrinsic-generic-select.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs81
-rw-r--r--src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs7
-rwxr-xr-xsrc/tools/clippy/clippy_lints/src/utils/ast_utils.rs2
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr22
-rw-r--r--src/tools/compiletest/src/common.rs2
-rw-r--r--src/tools/compiletest/src/header.rs1
-rw-r--r--src/tools/compiletest/src/runtest.rs19
-rw-r--r--src/tools/compiletest/src/util.rs20
m---------src/tools/miri18
-rwxr-xr-xsrc/tools/publish_toolstate.py2
-rw-r--r--src/tools/unicode-table-generator/src/main.rs2
102 files changed, 1623 insertions, 606 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d4f4ec7f6f0..75d644c1ed9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,12 +139,6 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-
-[[package]]
-name = "autocfg"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -766,7 +760,7 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
 dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
  "cfg-if",
  "lazy_static",
 ]
@@ -1245,11 +1239,11 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.6.2"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab"
+checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
 dependencies = [
- "autocfg 0.1.7",
+ "autocfg",
  "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -2079,7 +2073,7 @@ version = "0.9.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
 dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
  "cc",
  "libc",
  "openssl-src",
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 1db629c3bdf..1d5fa73d228 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -1294,7 +1294,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V> {
     type Item = (&'a K, &'a V);
     type IntoIter = Iter<'a, K, V>;
 
@@ -1363,7 +1363,7 @@ impl<K, V> Clone for Iter<'_, K, V> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a mut BTreeMap<K, V> {
     type Item = (&'a K, &'a mut V);
     type IntoIter = IterMut<'a, K, V>;
 
diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs
index 0dcb5930964..33b1ee003ed 100644
--- a/library/alloc/src/collections/btree/navigate.rs
+++ b/library/alloc/src/collections/btree/navigate.rs
@@ -1,3 +1,5 @@
+use core::intrinsics;
+use core::mem;
 use core::ptr;
 
 use super::node::{marker, ForceResult::*, Handle, NodeRef};
@@ -79,16 +81,24 @@ def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv}
 def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv}
 
 /// This replaces the value behind the `v` unique reference by calling the
-/// relevant function.
+/// relevant function, and returns a result obtained along the way.
 ///
-/// Safety: The change closure must not panic.
+/// If a panic occurs in the `change` closure, the entire process will be aborted.
 #[inline]
-unsafe fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+    struct PanicGuard;
+    impl Drop for PanicGuard {
+        fn drop(&mut self) {
+            intrinsics::abort()
+        }
+    }
+    let guard = PanicGuard;
     let value = unsafe { ptr::read(v) };
     let (new_value, ret) = change(value);
     unsafe {
         ptr::write(v, new_value);
     }
+    mem::forget(guard);
     ret
 }
 
@@ -97,26 +107,22 @@ impl<'a, K, V> Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Ed
     /// key and value in between.
     /// Unsafe because the caller must ensure that the leaf edge is not the last one in the tree.
     pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (kv.next_leaf_edge(), kv.into_kv())
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (kv.next_leaf_edge(), kv.into_kv())
+        })
     }
 
     /// Moves the leaf edge handle to the previous leaf edge and returns references to the
     /// key and value in between.
     /// Unsafe because the caller must ensure that the leaf edge is not the first one in the tree.
     pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_back_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (kv.next_back_leaf_edge(), kv.into_kv())
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_back_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (kv.next_back_leaf_edge(), kv.into_kv())
+        })
     }
 }
 
@@ -127,16 +133,14 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
     /// - The caller must ensure that the leaf edge is not the last one in the tree.
     /// - Using the updated handle may well invalidate the returned references.
     pub unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
-        unsafe {
-            let kv = replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (ptr::read(&kv).next_leaf_edge(), kv)
-            });
-            // Doing the descend (and perhaps another move) invalidates the references
-            // returned by `into_kv_mut`, so we have to do this last.
-            kv.into_kv_mut()
-        }
+        let kv = replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv)
+        });
+        // Doing the descend (and perhaps another move) invalidates the references
+        // returned by `into_kv_mut`, so we have to do this last.
+        kv.into_kv_mut()
     }
 
     /// Moves the leaf edge handle to the previous leaf and returns references to the
@@ -145,16 +149,14 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
     /// - The caller must ensure that the leaf edge is not the first one in the tree.
     /// - Using the updated handle may well invalidate the returned references.
     pub unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
-        unsafe {
-            let kv = replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_back_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (ptr::read(&kv).next_back_leaf_edge(), kv)
-            });
-            // Doing the descend (and perhaps another move) invalidates the references
-            // returned by `into_kv_mut`, so we have to do this last.
-            kv.into_kv_mut()
-        }
+        let kv = replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_back_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv)
+        });
+        // Doing the descend (and perhaps another move) invalidates the references
+        // returned by `into_kv_mut`, so we have to do this last.
+        kv.into_kv_mut()
     }
 }
 
@@ -172,14 +174,12 @@ impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
     ///   call this method again subject to both preconditions listed in the first point,
     ///   or call counterpart `next_back_unchecked` subject to its preconditions.
     pub unsafe fn next_unchecked(&mut self) -> (K, V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = next_kv_unchecked_dealloc(leaf_edge);
-                let k = ptr::read(kv.reborrow().into_kv().0);
-                let v = ptr::read(kv.reborrow().into_kv().1);
-                (kv.next_leaf_edge(), (k, v))
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) };
+            let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+            let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+            (kv.next_leaf_edge(), (k, v))
+        })
     }
 
     /// Moves the leaf edge handle to the previous leaf edge and returns the key
@@ -195,14 +195,12 @@ impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
     ///   call this method again subject to both preconditions listed in the first point,
     ///   or call counterpart `next_unchecked` subject to its preconditions.
     pub unsafe fn next_back_unchecked(&mut self) -> (K, V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = next_back_kv_unchecked_dealloc(leaf_edge);
-                let k = ptr::read(kv.reborrow().into_kv().0);
-                let v = ptr::read(kv.reborrow().into_kv().1);
-                (kv.next_back_leaf_edge(), (k, v))
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) };
+            let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+            let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+            (kv.next_back_leaf_edge(), (k, v))
+        })
     }
 }
 
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index eb50dc28b9f..95eae7e2a73 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -4383,8 +4383,8 @@ assert_eq!(
         }
 
         doc_comment! {
-            concat!("Create an integer value from its representation as a byte array in
-big endian.
+            concat!("Create a native endian integer value from its representation
+as a byte array in big endian.
 ",
 $from_xe_bytes_doc,
 "
@@ -4416,8 +4416,8 @@ fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
 
         doc_comment! {
             concat!("
-Create an integer value from its representation as a byte array in
-little endian.
+Create a native endian integer value from its representation
+as a byte array in little endian.
 ",
 $from_xe_bytes_doc,
 "
@@ -4448,8 +4448,8 @@ fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
         }
 
         doc_comment! {
-            concat!("Create an integer value from its memory representation as a byte
-array in native endianness.
+            concat!("Create a native endian integer value from its memory representation
+as a byte array in native endianness.
 
 As the target platform's native endianness is used, portable code
 likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index a2acc239bd3..a16970e9fd1 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -656,6 +656,38 @@ impl<T: ?Sized> *const T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Sets the pointer value to `ptr`.
+    ///
+    /// In case `self` is a (fat) pointer to an unsized type, this operation
+    /// will only affect the pointer part, whereas for (thin) pointers to
+    /// sized types, this has the same effect as a simple assignment.
+    ///
+    /// # Examples
+    ///
+    /// This function is primarily useful for allowing byte-wise pointer
+    /// arithmetic on potentially fat pointers:
+    ///
+    /// ```
+    /// #![feature(set_ptr_value)]
+    /// # use core::fmt::Debug;
+    /// let arr: [i32; 3] = [1, 2, 3];
+    /// let mut ptr = &arr[0] as *const dyn Debug;
+    /// let thin = ptr as *const u8;
+    /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+    /// assert_eq!(unsafe { *(ptr as *const i32) }, 3);
+    /// ```
+    #[unstable(feature = "set_ptr_value", issue = "75091")]
+    #[inline]
+    pub fn set_ptr_value(mut self, val: *const ()) -> Self {
+        let thin = &mut self as *mut *const T as *mut *const ();
+        // SAFETY: In case of a thin pointer, this operations is identical
+        // to a simple assignment. In case of a fat pointer, with the current
+        // fat pointer layout implementation, the first field of such a
+        // pointer is always the data pointer, which is likewise assigned.
+        unsafe { *thin = val };
+        self
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 17fa90ecc08..b47f90c5996 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -712,6 +712,38 @@ impl<T: ?Sized> *mut T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Sets the pointer value to `ptr`.
+    ///
+    /// In case `self` is a (fat) pointer to an unsized type, this operation
+    /// will only affect the pointer part, whereas for (thin) pointers to
+    /// sized types, this has the same effect as a simple assignment.
+    ///
+    /// # Examples
+    ///
+    /// This function is primarily useful for allowing byte-wise pointer
+    /// arithmetic on potentially fat pointers:
+    ///
+    /// ```
+    /// #![feature(set_ptr_value)]
+    /// # use core::fmt::Debug;
+    /// let mut arr: [i32; 3] = [1, 2, 3];
+    /// let mut ptr = &mut arr[0] as *mut dyn Debug;
+    /// let thin = ptr as *mut u8;
+    /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+    /// assert_eq!(unsafe { *(ptr as *mut i32) }, 3);
+    /// ```
+    #[unstable(feature = "set_ptr_value", issue = "75091")]
+    #[inline]
+    pub fn set_ptr_value(mut self, val: *mut ()) -> Self {
+        let thin = &mut self as *mut *mut T as *mut *mut ();
+        // SAFETY: In case of a thin pointer, this operations is identical
+        // to a simple assignment. In case of a fat pointer, with the current
+        // fat pointer layout implementation, the first field of such a
+        // pointer is always the data pointer, which is likewise assigned.
+        unsafe { *thin = val };
+        self
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 9d7e38d0e18..eac4741cd26 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -1923,7 +1923,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let (start, end) = (self.start, self.end);
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, start, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
@@ -1995,7 +1998,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let end = self.end;
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, 0, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, 0, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
@@ -2068,7 +2074,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let (start, end) = (self.start, slice.len());
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, start, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 474765d8638..fc07fa77b85 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -20,7 +20,7 @@ libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of
 compiler_builtins = { version = "0.1.32" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
-hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-dep-of-std'] }
+hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] }
 
 # Dependencies of the `backtrace` crate
 addr2line = { version = "0.13.0", optional = true, default-features = false }
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 7b48deee1ab..c40d6119fdf 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -580,7 +580,7 @@ where
     #[inline]
     #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
     pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.base.try_reserve(additional).map_err(map_collection_alloc_err)
+        self.base.try_reserve(additional).map_err(map_try_reserve_error)
     }
 
     /// Shrinks the capacity of the map as much as possible. It will drop
@@ -2569,10 +2569,10 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K,
 }
 
 #[inline]
-fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> TryReserveError {
+fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
     match err {
-        hashbrown::CollectionAllocErr::CapacityOverflow => TryReserveError::CapacityOverflow,
-        hashbrown::CollectionAllocErr::AllocErr { layout } => {
+        hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow,
+        hashbrown::TryReserveError::AllocError { layout } => {
             TryReserveError::AllocError { layout, non_exhaustive: () }
         }
     }
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index a0c0981ebf9..159ab981b23 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -961,11 +961,6 @@ impl AsInner<c::in_addr> for Ipv4Addr {
         &self.inner
     }
 }
-impl FromInner<c::in_addr> for Ipv4Addr {
-    fn from_inner(addr: c::in_addr) -> Ipv4Addr {
-        Ipv4Addr { inner: addr }
-    }
-}
 
 #[stable(feature = "ip_u32", since = "1.1.0")]
 impl From<Ipv4Addr> for u32 {
@@ -976,8 +971,8 @@ impl From<Ipv4Addr> for u32 {
     /// ```
     /// use std::net::Ipv4Addr;
     ///
-    /// let addr = Ipv4Addr::new(13, 12, 11, 10);
-    /// assert_eq!(0x0d0c0b0au32, u32::from(addr));
+    /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe);
+    /// assert_eq!(0xcafebabe, u32::from(addr));
     /// ```
     fn from(ip: Ipv4Addr) -> u32 {
         let ip = ip.octets();
@@ -994,8 +989,8 @@ impl From<u32> for Ipv4Addr {
     /// ```
     /// use std::net::Ipv4Addr;
     ///
-    /// let addr = Ipv4Addr::from(0x0d0c0b0au32);
-    /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
+    /// let addr = Ipv4Addr::from(0xcafebabe);
+    /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr);
     /// ```
     fn from(ip: u32) -> Ipv4Addr {
         Ipv4Addr::from(ip.to_be_bytes())
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index ab2a6010306..21ab0faed3e 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -434,7 +434,9 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
 
     let loc = info.location().unwrap(); // The current implementation always returns Some
     let msg = info.message().unwrap(); // The current implementation always returns Some
-    rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+    crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+        rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+    })
 }
 
 /// This is the entry point of panicking for the non-format-string variants of
@@ -453,7 +455,10 @@ pub fn begin_panic<M: Any + Send>(msg: M) -> ! {
         intrinsics::abort()
     }
 
-    rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller());
+    let loc = Location::caller();
+    return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+        rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc)
+    });
 
     struct PanicPayload<A> {
         inner: Option<A>,
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index fb825ab16eb..45af9f68a0f 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -48,9 +48,7 @@ fn lang_start_internal(
         sys::args::init(argc, argv);
 
         // Let's run some code!
-        let exit_code = panic::catch_unwind(|| {
-            sys_common::backtrace::__rust_begin_short_backtrace(move || main())
-        });
+        let exit_code = panic::catch_unwind(main);
 
         sys_common::cleanup();
         exit_code.unwrap_or(101) as isize
@@ -64,5 +62,9 @@ fn lang_start<T: crate::process::Termination + 'static>(
     argc: isize,
     argv: *const *const u8,
 ) -> isize {
-    lang_start_internal(&move || main().report(), argc, argv)
+    lang_start_internal(
+        &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(),
+        argc,
+        argv,
+    )
 }
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index e36a53084ba..ba169b251b0 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -283,12 +283,12 @@ impl Drop for FileDesc {
 #[cfg(test)]
 mod tests {
     use super::{FileDesc, IoSlice};
+    use core::mem::ManuallyDrop;
 
     #[test]
     fn limit_vector_count() {
-        let stdout = FileDesc { fd: 1 };
+        let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
         let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
-
         assert!(stdout.write_vectored(&bufs).is_ok());
     }
 }
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index d386a656e4f..1c5fbf7d701 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -74,6 +74,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
     bt_fmt.add_context()?;
     let mut idx = 0;
     let mut res = Ok(());
+    // Start immediately if we're not using a short backtrace.
+    let mut start = print_fmt != PrintFmt::Short;
     backtrace_rs::trace_unsynchronized(|frame| {
         if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
             return false;
@@ -89,16 +91,24 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
                         stop = true;
                         return;
                     }
+                    if sym.contains("__rust_end_short_backtrace") {
+                        start = true;
+                        return;
+                    }
                 }
             }
 
-            res = bt_fmt.frame().symbol(frame, symbol);
+            if start {
+                res = bt_fmt.frame().symbol(frame, symbol);
+            }
         });
         if stop {
             return false;
         }
         if !hit {
-            res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+            if start {
+                res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+            }
         }
 
         idx += 1;
@@ -123,10 +133,29 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
 where
     F: FnOnce() -> T,
-    F: Send,
-    T: Send,
 {
-    f()
+    let result = f();
+
+    // prevent this frame from being tail-call optimised away
+    crate::hint::black_box(());
+
+    result
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
+where
+    F: FnOnce() -> T,
+{
+    let result = f();
+
+    // prevent this frame from being tail-call optimised away
+    crate::hint::black_box(());
+
+    result
 }
 
 pub enum RustBacktrace {
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 933b647071f..6bd708ef487 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -514,7 +514,10 @@ pub fn run_test(
 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
 #[inline(never)]
 fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
-    f()
+    f();
+
+    // prevent this frame from being tail-call optimised away
+    black_box(());
 }
 
 fn run_test_in_process(
diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py
index cec9c56a235..67f99ec4e40 100644
--- a/src/etc/gdb_providers.py
+++ b/src/etc/gdb_providers.py
@@ -352,8 +352,13 @@ class StdHashMapProvider:
         ctrl = table["ctrl"]["pointer"]
 
         self.size = int(table["items"])
-        self.data_ptr = table["data"]["pointer"]
-        self.pair_type = self.data_ptr.dereference().type
+        self.pair_type = table.type.template_argument(0)
+
+        self.new_layout = not table.type.has_key("data")
+        if self.new_layout:
+            self.data_ptr = ctrl.cast(self.pair_type.pointer())
+        else:
+            self.data_ptr = table["data"]["pointer"]
 
         self.valid_indices = []
         for idx in range(capacity):
@@ -374,6 +379,8 @@ class StdHashMapProvider:
 
         for index in range(self.size):
             idx = self.valid_indices[index]
+            if self.new_layout:
+                idx = -(idx + 1)
             element = (pairs_start + idx).dereference()
             if self.show_values:
                 yield "key{}".format(index), element[ZERO_FIELD]
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 3c7817b3a61..19da75c35b4 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -514,6 +514,8 @@ class StdHashMapSyntheticProvider:
         # type: (int) -> SBValue
         pairs_start = self.data_ptr.GetValueAsUnsigned()
         idx = self.valid_indices[index]
+        if self.new_layout:
+            idx = -(idx + 1)
         address = pairs_start + idx * self.pair_type_size
         element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
         if self.show_values:
@@ -529,10 +531,15 @@ class StdHashMapSyntheticProvider:
         ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
 
         self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned()
-        self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
-        self.pair_type = self.data_ptr.Dereference().GetType()
+        self.pair_type = table.type.template_args[0]
         self.pair_type_size = self.pair_type.GetByteSize()
 
+        self.new_layout = not table.GetChildMemberWithName("data").IsValid()
+        if self.new_layout:
+            self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
+        else:
+            self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
+
         u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
         u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
 
diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis
index b3fc3d17af7..4e81173d3d0 100644
--- a/src/etc/natvis/libstd.natvis
+++ b/src/etc/natvis/libstd.natvis
@@ -30,6 +30,7 @@
     <Expand>
       <Item Name="[size]">base.table.items</Item>
       <Item Name="[capacity]">base.table.items + base.table.growth_left</Item>
+      <Item Name="[state]">base.hash_builder</Item>
 
       <CustomListItems>
         <Variable Name="i" InitialValue="0" />
@@ -40,7 +41,7 @@
           <If Condition="(base.table.ctrl.pointer[i] &amp; 0x80) == 0">
             <!-- Bucket is populated -->
             <Exec>n--</Exec>
-            <Item Name="{base.table.data.pointer[i].__0}">base.table.data.pointer[i].__1</Item>
+            <Item Name="{static_cast&lt;tuple&lt;$T1, $T2&gt;*&gt;(base.table.ctrl.pointer)[-(i + 1)].__0}">static_cast&lt;tuple&lt;$T1, $T2&gt;*&gt;(base.table.ctrl.pointer)[-(i + 1)].__1</Item>
           </If>
           <Exec>i++</Exec>
         </Loop>
@@ -53,6 +54,7 @@
     <Expand>
       <Item Name="[size]">map.base.table.items</Item>
       <Item Name="[capacity]">map.base.table.items + map.base.table.growth_left</Item>
+      <Item Name="[state]">map.base.hash_builder</Item>
 
       <CustomListItems>
         <Variable Name="i" InitialValue="0" />
@@ -63,36 +65,7 @@
           <If Condition="(map.base.table.ctrl.pointer[i] &amp; 0x80) == 0">
             <!-- Bucket is populated -->
             <Exec>n--</Exec>
-            <Item>map.base.table.data.pointer[i].__0</Item>
-          </If>
-          <Exec>i++</Exec>
-        </Loop>
-      </CustomListItems>
-    </Expand>
-  </Type>
-
-  <Type Name="hashbrown::raw::RawTable&lt;*&gt;">
-    <!-- RawTable has a nice and simple layout.
-      items                     Number of *populated* values in the RawTable (less than the size of ctrl.pointer / data.pointer)
-      growth_left               Remaining capacity before growth
-      ctrl.pointer[i] & 0x80    Indicates the bucket is empty / should be skipped / doesn't count towards items.
-      data.pointer[i]           The (K,V) tuple, if not empty.
-    -->
-    <DisplayString>{{ size={items} }}</DisplayString>
-    <Expand>
-      <Item Name="[size]">items</Item>
-      <Item Name="[capacity]">items + growth_left</Item>
-
-      <CustomListItems>
-        <Variable Name="i" InitialValue="0" />
-        <Variable Name="n" InitialValue="items" />
-        <Size>items</Size>
-        <Loop>
-          <Break Condition="n == 0" />
-          <If Condition="(ctrl.pointer[i] &amp; 0x80) == 0">
-            <!-- Bucket is populated -->
-            <Exec>n--</Exec>
-            <Item>data.pointer[i]</Item>
+            <Item>static_cast&lt;$T1*&gt;(map.base.table.ctrl.pointer)[-(i + 1)]</Item>
           </If>
           <Exec>i++</Exec>
         </Loop>
diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs
index 6543117774a..9337b27e5e9 100644
--- a/src/librustc_ast/ast.rs
+++ b/src/librustc_ast/ast.rs
@@ -23,7 +23,7 @@ pub use GenericArgs::*;
 pub use UnsafeSource::*;
 
 use crate::ptr::P;
-use crate::token::{self, DelimToken};
+use crate::token::{self, CommentKind, DelimToken};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -2365,7 +2365,7 @@ pub enum AttrKind {
     /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
     /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
     /// variant (which is much less compact and thus more expensive).
-    DocComment(Symbol),
+    DocComment(CommentKind, Symbol),
 }
 
 /// `TraitRef`s appear in impls.
diff --git a/src/librustc_ast/attr/mod.rs b/src/librustc_ast/attr/mod.rs
index 809fda86542..847d126b3ef 100644
--- a/src/librustc_ast/attr/mod.rs
+++ b/src/librustc_ast/attr/mod.rs
@@ -7,7 +7,7 @@ use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
-use crate::token::{self, Token};
+use crate::token::{self, CommentKind, Token};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
 
 use rustc_data_structures::sync::Lock;
@@ -169,7 +169,7 @@ impl Attribute {
     pub fn has_name(&self, name: Symbol) -> bool {
         match self.kind {
             AttrKind::Normal(ref item) => item.path == name,
-            AttrKind::DocComment(_) => false,
+            AttrKind::DocComment(..) => false,
         }
     }
 
@@ -198,7 +198,7 @@ impl Attribute {
                     None
                 }
             }
-            AttrKind::DocComment(_) => None,
+            AttrKind::DocComment(..) => None,
         }
     }
     pub fn name_or_empty(&self) -> Symbol {
@@ -218,7 +218,7 @@ impl Attribute {
                 Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
                 _ => None,
             },
-            AttrKind::DocComment(_) => None,
+            AttrKind::DocComment(..) => None,
         }
     }
 
@@ -314,13 +314,13 @@ impl Attribute {
     pub fn is_doc_comment(&self) -> bool {
         match self.kind {
             AttrKind::Normal(_) => false,
-            AttrKind::DocComment(_) => true,
+            AttrKind::DocComment(..) => true,
         }
     }
 
     pub fn doc_str(&self) -> Option<Symbol> {
         match self.kind {
-            AttrKind::DocComment(symbol) => Some(symbol),
+            AttrKind::DocComment(.., data) => Some(data),
             AttrKind::Normal(ref item) if item.path == sym::doc => {
                 item.meta(self.span).and_then(|meta| meta.value_str())
             }
@@ -331,14 +331,14 @@ impl Attribute {
     pub fn get_normal_item(&self) -> &AttrItem {
         match self.kind {
             AttrKind::Normal(ref item) => item,
-            AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
 
     pub fn unwrap_normal_item(self) -> AttrItem {
         match self.kind {
             AttrKind::Normal(item) => item,
-            AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
 
@@ -405,8 +405,13 @@ pub fn mk_attr_outer(item: MetaItem) -> Attribute {
     mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
 }
 
-pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
-    Attribute { kind: AttrKind::DocComment(comment), id: mk_attr_id(), style, span }
+pub fn mk_doc_comment(
+    comment_kind: CommentKind,
+    style: AttrStyle,
+    data: Symbol,
+    span: Span,
+) -> Attribute {
+    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
 }
 
 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs
index 54f81ef106f..df6e8218f6c 100644
--- a/src/librustc_ast/mut_visit.rs
+++ b/src/librustc_ast/mut_visit.rs
@@ -582,7 +582,7 @@ pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
-        AttrKind::DocComment(_) => {}
+        AttrKind::DocComment(..) => {}
     }
     vis.visit_span(span);
 }
diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs
index e1c94ddf782..bcce881ed48 100644
--- a/src/librustc_ast/token.rs
+++ b/src/librustc_ast/token.rs
@@ -17,6 +17,12 @@ use rustc_span::{self, Span, DUMMY_SP};
 use std::borrow::Cow;
 use std::{fmt, mem};
 
+#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+pub enum CommentKind {
+    Line,
+    Block,
+}
+
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 #[derive(HashStable_Generic)]
 pub enum BinOpToken {
@@ -238,9 +244,10 @@ pub enum TokenKind {
 
     Interpolated(Lrc<Nonterminal>),
 
-    // Can be expanded into several tokens.
-    /// A doc comment.
-    DocComment(Symbol),
+    /// A doc comment token.
+    /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
+    /// similarly to symbols in string literal tokens.
+    DocComment(CommentKind, ast::AttrStyle, Symbol),
 
     // Junk. These carry no data because we don't really care about the data
     // they *would* carry, and don't really want to allocate a new ident for
diff --git a/src/librustc_ast/util/comments.rs b/src/librustc_ast/util/comments.rs
index 39921b20226..a73891db160 100644
--- a/src/librustc_ast/util/comments.rs
+++ b/src/librustc_ast/util/comments.rs
@@ -1,11 +1,7 @@
-pub use CommentStyle::*;
-
-use crate::ast;
+use crate::ast::AttrStyle;
 use rustc_span::source_map::SourceMap;
 use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol};
 
-use log::debug;
-
 #[cfg(test)]
 mod tests;
 
@@ -28,43 +24,48 @@ pub struct Comment {
     pub pos: BytePos,
 }
 
-pub fn is_line_doc_comment(s: &str) -> bool {
-    let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/')
-        || s.starts_with("//!");
-    debug!("is {:?} a doc comment? {}", s, res);
-    res
-}
-
-pub fn is_block_doc_comment(s: &str) -> bool {
-    // Prevent `/**/` from being parsed as a doc comment
-    let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*')
-        || s.starts_with("/*!"))
-        && s.len() >= 5;
-    debug!("is {:?} a doc comment? {}", s, res);
-    res
-}
-
-// FIXME(#64197): Try to privatize this again.
-pub fn is_doc_comment(s: &str) -> bool {
-    (s.starts_with("///") && is_line_doc_comment(s))
-        || s.starts_with("//!")
-        || (s.starts_with("/**") && is_block_doc_comment(s))
-        || s.starts_with("/*!")
+/// For a full line comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn line_doc_comment_style(line_comment: &str) -> Option<AttrStyle> {
+    let line_comment = line_comment.as_bytes();
+    assert!(line_comment.starts_with(b"//"));
+    match line_comment.get(2) {
+        // `//!` is an inner line doc comment.
+        Some(b'!') => Some(AttrStyle::Inner),
+        Some(b'/') => match line_comment.get(3) {
+            // `////` (more than 3 slashes) is not considered a doc comment.
+            Some(b'/') => None,
+            // Otherwise `///` is an outer line doc comment.
+            _ => Some(AttrStyle::Outer),
+        },
+        _ => None,
+    }
 }
 
-pub fn doc_comment_style(comment: Symbol) -> ast::AttrStyle {
-    let comment = &comment.as_str();
-    assert!(is_doc_comment(comment));
-    if comment.starts_with("//!") || comment.starts_with("/*!") {
-        ast::AttrStyle::Inner
-    } else {
-        ast::AttrStyle::Outer
+/// For a full block comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn block_doc_comment_style(block_comment: &str, terminated: bool) -> Option<AttrStyle> {
+    let block_comment = block_comment.as_bytes();
+    assert!(block_comment.starts_with(b"/*"));
+    assert!(!terminated || block_comment.ends_with(b"*/"));
+    match block_comment.get(2) {
+        // `/*!` is an inner block doc comment.
+        Some(b'!') => Some(AttrStyle::Inner),
+        Some(b'*') => match block_comment.get(3) {
+            // `/***` (more than 2 stars) is not considered a doc comment.
+            Some(b'*') => None,
+            // `/**/` is not considered a doc comment.
+            Some(b'/') if block_comment.len() == 4 => None,
+            // Otherwise `/**` is an outer block doc comment.
+            _ => Some(AttrStyle::Outer),
+        },
+        _ => None,
     }
 }
 
-pub fn strip_doc_comment_decoration(comment: Symbol) -> String {
-    let comment = &comment.as_str();
-
+/// Makes a doc string more presentable to users.
+/// Used by rustdoc and perhaps other tools, but not by rustc.
+pub fn beautify_doc_string(data: Symbol) -> String {
     /// remove whitespace-only lines from the start/end of lines
     fn vertical_trim(lines: Vec<String>) -> Vec<String> {
         let mut i = 0;
@@ -126,26 +127,15 @@ pub fn strip_doc_comment_decoration(comment: Symbol) -> String {
         }
     }
 
-    // one-line comments lose their prefix
-    const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
-
-    for prefix in ONELINERS {
-        if comment.starts_with(*prefix) {
-            return (&comment[prefix.len()..]).to_string();
-        }
-    }
-
-    if comment.starts_with("/*") {
-        let lines =
-            comment[3..comment.len() - 2].lines().map(|s| s.to_string()).collect::<Vec<String>>();
-
+    let data = data.as_str();
+    if data.contains('\n') {
+        let lines = data.lines().map(|s| s.to_string()).collect::<Vec<String>>();
         let lines = vertical_trim(lines);
         let lines = horizontal_trim(lines);
-
-        return lines.join("\n");
+        lines.join("\n")
+    } else {
+        data.to_string()
     }
-
-    panic!("not a doc-comment: {}", comment);
 }
 
 /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
@@ -203,7 +193,7 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
 
     if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
         comments.push(Comment {
-            style: Isolated,
+            style: CommentStyle::Isolated,
             lines: vec![text[..shebang_len].to_string()],
             pos: start_bpos,
         });
@@ -219,23 +209,23 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
                     while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
                         idx = idx + 1 + next_newline;
                         comments.push(Comment {
-                            style: BlankLine,
+                            style: CommentStyle::BlankLine,
                             lines: vec![],
                             pos: start_bpos + BytePos((pos + idx) as u32),
                         });
                     }
                 }
             }
-            rustc_lexer::TokenKind::BlockComment { terminated: _ } => {
-                if !is_block_doc_comment(token_text) {
+            rustc_lexer::TokenKind::BlockComment { terminated } => {
+                if block_doc_comment_style(token_text, terminated).is_none() {
                     let code_to_the_right = match text[pos + token.len..].chars().next() {
                         Some('\r' | '\n') => false,
                         _ => true,
                     };
                     let style = match (code_to_the_left, code_to_the_right) {
-                        (_, true) => Mixed,
-                        (false, false) => Isolated,
-                        (true, false) => Trailing,
+                        (_, true) => CommentStyle::Mixed,
+                        (false, false) => CommentStyle::Isolated,
+                        (true, false) => CommentStyle::Trailing,
                     };
 
                     // Count the number of chars since the start of the line by rescanning.
@@ -249,9 +239,13 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
                 }
             }
             rustc_lexer::TokenKind::LineComment => {
-                if !is_doc_comment(token_text) {
+                if line_doc_comment_style(token_text).is_none() {
                     comments.push(Comment {
-                        style: if code_to_the_left { Trailing } else { Isolated },
+                        style: if code_to_the_left {
+                            CommentStyle::Trailing
+                        } else {
+                            CommentStyle::Isolated
+                        },
                         lines: vec![token_text.to_string()],
                         pos: start_bpos + BytePos(pos as u32),
                     })
diff --git a/src/librustc_ast/util/comments/tests.rs b/src/librustc_ast/util/comments/tests.rs
index f08011fe4f8..e95365d8337 100644
--- a/src/librustc_ast/util/comments/tests.rs
+++ b/src/librustc_ast/util/comments/tests.rs
@@ -2,10 +2,17 @@ use super::*;
 use crate::with_default_session_globals;
 
 #[test]
+fn line_doc_comments() {
+    assert!(line_doc_comment_style("///").is_some());
+    assert!(line_doc_comment_style("/// blah").is_some());
+    assert!(line_doc_comment_style("////").is_none());
+}
+
+#[test]
 fn test_block_doc_comment_1() {
     with_default_session_globals(|| {
-        let comment = "/**\n * Test \n **  Test\n *   Test\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n * Test \n **  Test\n *   Test\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " Test \n*  Test\n   Test");
     })
 }
@@ -13,8 +20,8 @@ fn test_block_doc_comment_1() {
 #[test]
 fn test_block_doc_comment_2() {
     with_default_session_globals(|| {
-        let comment = "/**\n * Test\n *  Test\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n * Test\n *  Test\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " Test\n  Test");
     })
 }
@@ -22,37 +29,22 @@ fn test_block_doc_comment_2() {
 #[test]
 fn test_block_doc_comment_3() {
     with_default_session_globals(|| {
-        let comment = "/**\n let a: *i32;\n *a = 5;\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n let a: *i32;\n *a = 5;\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " let a: *i32;\n *a = 5;");
     })
 }
 
 #[test]
-fn test_block_doc_comment_4() {
-    with_default_session_globals(|| {
-        let comment = "/*******************\n test\n *********************/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
-        assert_eq!(stripped, " test");
-    })
-}
-
-#[test]
 fn test_line_doc_comment() {
     with_default_session_globals(|| {
-        let stripped = strip_doc_comment_decoration(Symbol::intern("/// test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///! test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
+        let stripped = beautify_doc_string(Symbol::intern(" test"));
         assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///test"));
-        assert_eq!(stripped, "test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///!test"));
-        assert_eq!(stripped, "test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("//test"));
+        let stripped = beautify_doc_string(Symbol::intern("! test"));
+        assert_eq!(stripped, "! test");
+        let stripped = beautify_doc_string(Symbol::intern("test"));
         assert_eq!(stripped, "test");
+        let stripped = beautify_doc_string(Symbol::intern("!test"));
+        assert_eq!(stripped, "!test");
     })
 }
diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs
index ccab46703df..2c3d1e97df9 100644
--- a/src/librustc_ast/visit.rs
+++ b/src/librustc_ast/visit.rs
@@ -880,7 +880,7 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
 pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
     match attr.kind {
         AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
-        AttrKind::DocComment(_) => {}
+        AttrKind::DocComment(..) => {}
     }
 }
 
diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs
index abd5df537db..f9e54903a66 100644
--- a/src/librustc_ast_lowering/expr.rs
+++ b/src/librustc_ast_lowering/expr.rs
@@ -1067,7 +1067,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             .collect();
 
         // Stop if there were any errors when lowering the register classes
-        if operands.len() != asm.operands.len() {
+        if operands.len() != asm.operands.len() || sess.asm_arch.is_none() {
             return hir::ExprKind::Err;
         }
 
diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs
index 9df7ad2a9ac..077a07c1bfa 100644
--- a/src/librustc_ast_lowering/lib.rs
+++ b/src/librustc_ast_lowering/lib.rs
@@ -981,7 +981,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 path: item.path.clone(),
                 args: self.lower_mac_args(&item.args),
             }),
-            AttrKind::DocComment(comment) => AttrKind::DocComment(comment),
+            AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
 
         Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs
index 4b228629ad7..9d9ca78de55 100644
--- a/src/librustc_ast_pretty/pprust.rs
+++ b/src/librustc_ast_pretty/pprust.rs
@@ -8,10 +8,11 @@ use rustc_ast::ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::util::classify;
+use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
 use rustc_ast::util::parser::{self, AssocOp, Fixity};
-use rustc_ast::util::{classify, comments};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
@@ -50,17 +51,17 @@ impl PpAnn for NoAnn {}
 
 pub struct Comments<'a> {
     sm: &'a SourceMap,
-    comments: Vec<comments::Comment>,
+    comments: Vec<Comment>,
     current: usize,
 }
 
 impl<'a> Comments<'a> {
     pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
-        let comments = comments::gather_comments(sm, filename, input);
+        let comments = gather_comments(sm, filename, input);
         Comments { sm, comments, current: 0 }
     }
 
-    pub fn next(&self) -> Option<comments::Comment> {
+    pub fn next(&self) -> Option<Comment> {
         self.comments.get(self.current).cloned()
     }
 
@@ -68,9 +69,9 @@ impl<'a> Comments<'a> {
         &mut self,
         span: rustc_span::Span,
         next_pos: Option<BytePos>,
-    ) -> Option<comments::Comment> {
+    ) -> Option<Comment> {
         if let Some(cmnt) = self.next() {
-            if cmnt.style != comments::Trailing {
+            if cmnt.style != CommentStyle::Trailing {
                 return None;
             }
             let span_line = self.sm.lookup_char_pos(span.hi());
@@ -152,8 +153,8 @@ pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
 // and also addresses some specific regressions described in #63896 and #73345.
 fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
     if let TokenTree::Token(token) = prev {
-        if let token::DocComment(s) = token.kind {
-            return !s.as_str().starts_with("//");
+        if let token::DocComment(comment_kind, ..) = token.kind {
+            return comment_kind != CommentKind::Line;
         }
     }
     match tt {
@@ -194,6 +195,19 @@ fn binop_to_string(op: BinOpToken) -> &'static str {
     }
 }
 
+fn doc_comment_to_string(
+    comment_kind: CommentKind,
+    attr_style: ast::AttrStyle,
+    data: Symbol,
+) -> String {
+    match (comment_kind, attr_style) {
+        (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
+        (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
+        (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
+        (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
+    }
+}
+
 pub fn literal_to_string(lit: token::Lit) -> String {
     let token::Lit { kind, symbol, suffix } = lit;
     let mut out = match kind {
@@ -271,7 +285,9 @@ fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>)
         token::Lifetime(s) => s.to_string(),
 
         /* Other */
-        token::DocComment(s) => s.to_string(),
+        token::DocComment(comment_kind, attr_style, data) => {
+            doc_comment_to_string(comment_kind, attr_style, data)
+        }
         token::Eof => "<eof>".to_string(),
         token::Whitespace => " ".to_string(),
         token::Comment => "/* */".to_string(),
@@ -447,9 +463,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn print_comment(&mut self, cmnt: &comments::Comment) {
+    fn print_comment(&mut self, cmnt: &Comment) {
         match cmnt.style {
-            comments::Mixed => {
+            CommentStyle::Mixed => {
                 if !self.is_beginning_of_line() {
                     self.zerobreak();
                 }
@@ -468,7 +484,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 }
                 self.zerobreak()
             }
-            comments::Isolated => {
+            CommentStyle::Isolated => {
                 self.hardbreak_if_not_bol();
                 for line in &cmnt.lines {
                     // Don't print empty lines because they will end up as trailing
@@ -479,7 +495,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.hardbreak();
                 }
             }
-            comments::Trailing => {
+            CommentStyle::Trailing => {
                 if !self.is_beginning_of_line() {
                     self.word(" ");
                 }
@@ -497,7 +513,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.end();
                 }
             }
-            comments::BlankLine => {
+            CommentStyle::BlankLine => {
                 // We need to do at least one, possibly two hardbreaks.
                 let twice = match self.last_token() {
                     pp::Token::String(s) => ";" == s,
@@ -516,7 +532,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn next_comment(&mut self) -> Option<comments::Comment> {
+    fn next_comment(&mut self) -> Option<Comment> {
         self.comments().as_mut().and_then(|c| c.next())
     }
 
@@ -599,8 +615,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 self.print_attr_item(&item, attr.span);
                 self.word("]");
             }
-            ast::AttrKind::DocComment(comment) => {
-                self.word(comment.to_string());
+            ast::AttrKind::DocComment(comment_kind, data) => {
+                self.word(doc_comment_to_string(comment_kind, attr.style, data));
                 self.hardbreak()
             }
         }
diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md
index 305667e58f8..90755d47f67 100644
--- a/src/librustc_error_codes/error_codes/E0746.md
+++ b/src/librustc_error_codes/error_codes/E0746.md
@@ -1,4 +1,4 @@
-Return types cannot be `dyn Trait`s as they must be `Sized`.
+An unboxed trait object was used as a return value.
 
 Erroneous code example:
 
@@ -13,11 +13,13 @@ impl T for S {
 
 // Having the trait `T` as return type is invalid because
 // unboxed trait objects do not have a statically known size:
-fn foo() -> dyn T {
+fn foo() -> dyn T { // error!
     S(42)
 }
 ```
 
+Return types cannot be `dyn Trait`s as they must be `Sized`.
+
 To avoid the error there are a couple of options.
 
 If there is a single type involved, you can use [`impl Trait`]:
@@ -32,7 +34,7 @@ If there is a single type involved, you can use [`impl Trait`]:
 # }
 // The compiler will select `S(usize)` as the materialized return type of this
 // function, but callers will only know that the return type implements `T`.
-fn foo() -> impl T {
+fn foo() -> impl T { // ok!
     S(42)
 }
 ```
@@ -57,7 +59,7 @@ impl T for O {
 
 // This now returns a "trait object" and callers are only be able to access
 // associated items from `T`.
-fn foo(x: bool) -> Box<dyn T> {
+fn foo(x: bool) -> Box<dyn T> { // ok!
     if x {
         Box::new(S(42))
     } else {
diff --git a/src/librustc_error_codes/error_codes/E0747.md b/src/librustc_error_codes/error_codes/E0747.md
index df1afbfef46..caf7e0fba07 100644
--- a/src/librustc_error_codes/error_codes/E0747.md
+++ b/src/librustc_error_codes/error_codes/E0747.md
@@ -1,4 +1,4 @@
-Generic arguments must be provided in the same order as the corresponding
+Generic arguments were not provided in the same order as the corresponding
 generic parameters are declared.
 
 Erroneous code example:
@@ -11,7 +11,7 @@ type X = S<(), 'static>; // error: the type argument is provided before the
 ```
 
 The argument order should be changed to match the parameter declaration
-order, as in the following.
+order, as in the following:
 
 ```
 struct S<'a, T>(&'a T);
diff --git a/src/librustc_expand/parse/lexer/tests.rs b/src/librustc_expand/parse/lexer/tests.rs
index b3775c78e73..0b51abf385f 100644
--- a/src/librustc_expand/parse/lexer/tests.rs
+++ b/src/librustc_expand/parse/lexer/tests.rs
@@ -1,5 +1,5 @@
-use rustc_ast::token::{self, Token, TokenKind};
-use rustc_ast::util::comments::is_doc_comment;
+use rustc_ast::ast::AttrStyle;
+use rustc_ast::token::{self, CommentKind, Token, TokenKind};
 use rustc_ast::with_default_session_globals;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{emitter::EmitterWriter, Handler};
@@ -224,13 +224,6 @@ fn literal_suffixes() {
 }
 
 #[test]
-fn line_doc_comments() {
-    assert!(is_doc_comment("///"));
-    assert!(is_doc_comment("/// blah"));
-    assert!(!is_doc_comment("////"));
-}
-
-#[test]
 fn nested_block_comments() {
     with_default_session_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
@@ -251,6 +244,9 @@ fn crlf_comments() {
         assert_eq!(comment.kind, token::Comment);
         assert_eq!((comment.span.lo(), comment.span.hi()), (BytePos(0), BytePos(7)));
         assert_eq!(lexer.next_token(), token::Whitespace);
-        assert_eq!(lexer.next_token(), token::DocComment(Symbol::intern("/// test")));
+        assert_eq!(
+            lexer.next_token(),
+            token::DocComment(CommentKind::Line, AttrStyle::Outer, Symbol::intern(" test"))
+        );
     })
 }
diff --git a/src/librustc_expand/parse/tests.rs b/src/librustc_expand/parse/tests.rs
index fc9b9f2dab0..d6301c8a82e 100644
--- a/src/librustc_expand/parse/tests.rs
+++ b/src/librustc_expand/parse/tests.rs
@@ -244,20 +244,20 @@ fn crlf_doc_comments() {
         let source = "/// doc comment\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_1, source, &sess).unwrap().unwrap();
         let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
-        assert_eq!(doc.as_str(), "/// doc comment");
+        assert_eq!(doc.as_str(), " doc comment");
 
         let name_2 = FileName::Custom("crlf_source_2".to_string());
         let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_2, source, &sess).unwrap().unwrap();
         let docs = item.attrs.iter().filter_map(|at| at.doc_str()).collect::<Vec<_>>();
-        let b: &[_] = &[Symbol::intern("/// doc comment"), Symbol::intern("/// line 2")];
+        let b: &[_] = &[Symbol::intern(" doc comment"), Symbol::intern(" line 2")];
         assert_eq!(&docs[..], b);
 
         let name_3 = FileName::Custom("clrf_source_3".to_string());
         let source = "/** doc comment\r\n *  with CRLF */\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap();
         let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
-        assert_eq!(doc.as_str(), "/** doc comment\n *  with CRLF */");
+        assert_eq!(doc.as_str(), " doc comment\n *  with CRLF ");
     });
 }
 
diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs
index 881d7b84b70..005db35da7a 100644
--- a/src/librustc_expand/proc_macro_server.rs
+++ b/src/librustc_expand/proc_macro_server.rs
@@ -3,7 +3,6 @@ use crate::base::ExtCtxt;
 use rustc_ast::ast;
 use rustc_ast::token;
 use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
-use rustc_ast::util::comments;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
@@ -148,11 +147,9 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
                 tt!(Punct::new('\'', true))
             }
             Literal(lit) => tt!(Literal { lit }),
-            DocComment(c) => {
-                let style = comments::doc_comment_style(c);
-                let stripped = comments::strip_doc_comment_decoration(c);
+            DocComment(_, attr_style, data) => {
                 let mut escaped = String::new();
-                for ch in stripped.chars() {
+                for ch in data.as_str().chars() {
                     escaped.extend(ch.escape_debug());
                 }
                 let stream = vec![
@@ -169,7 +166,7 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
                     span: DelimSpan::from_single(span),
                     flatten: false,
                 }));
-                if style == ast::AttrStyle::Inner {
+                if attr_style == ast::AttrStyle::Inner {
                     stack.push(tt!(Punct::new('!', false)));
                 }
                 tt!(Punct::new('#', false))
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index d7c310a8b4c..7e42f219ce2 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -576,7 +576,7 @@ declare_features! (
     /// Lazily evaluate constants. This allows constants to depend on type parameters.
     (active, lazy_normalization_consts, "1.46.0", Some(72219), None),
 
-    /// Alloc calling `transmute` in const fn
+    /// Allows calling `transmute` in const fn
     (active, const_fn_transmute, "1.46.0", Some(53605), None),
 
     // -------------------------------------------------------------------------
diff --git a/src/librustc_hir/def.rs b/src/librustc_hir/def.rs
index af1860ca6bf..618f3e99c3f 100644
--- a/src/librustc_hir/def.rs
+++ b/src/librustc_hir/def.rs
@@ -150,7 +150,7 @@ impl DefKind {
         }
     }
 
-    pub fn matches_ns(&self, ns: Namespace) -> bool {
+    pub fn ns(&self) -> Option<Namespace> {
         match self {
             DefKind::Mod
             | DefKind::Struct
@@ -163,7 +163,7 @@ impl DefKind {
             | DefKind::ForeignTy
             | DefKind::TraitAlias
             | DefKind::AssocTy
-            | DefKind::TyParam => ns == Namespace::TypeNS,
+            | DefKind::TyParam => Some(Namespace::TypeNS),
 
             DefKind::Fn
             | DefKind::Const
@@ -171,9 +171,9 @@ impl DefKind {
             | DefKind::Static
             | DefKind::Ctor(..)
             | DefKind::AssocFn
-            | DefKind::AssocConst => ns == Namespace::ValueNS,
+            | DefKind::AssocConst => Some(Namespace::ValueNS),
 
-            DefKind::Macro(..) => ns == Namespace::MacroNS,
+            DefKind::Macro(..) => Some(Namespace::MacroNS),
 
             // Not namespaced.
             DefKind::AnonConst
@@ -185,7 +185,7 @@ impl DefKind {
             | DefKind::Use
             | DefKind::ForeignMod
             | DefKind::GlobalAsm
-            | DefKind::Impl => false,
+            | DefKind::Impl => None,
         }
     }
 }
@@ -453,7 +453,7 @@ impl<Id> Res<Id> {
 
     pub fn matches_ns(&self, ns: Namespace) -> bool {
         match self {
-            Res::Def(kind, ..) => kind.matches_ns(ns),
+            Res::Def(kind, ..) => kind.ns() == Some(ns),
             Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
             Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
             Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs
index d451d9a22a4..29d615b04a3 100644
--- a/src/librustc_incremental/assert_module_sources.rs
+++ b/src/librustc_incremental/assert_module_sources.rs
@@ -39,8 +39,8 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
             .collect_and_partition_mono_items(LOCAL_CRATE)
             .1
             .iter()
-            .map(|cgu| cgu.name())
-            .collect::<BTreeSet<Symbol>>();
+            .map(|cgu| cgu.name().to_string())
+            .collect::<BTreeSet<String>>();
 
         let ams = AssertModuleSource { tcx, available_cgus };
 
@@ -52,7 +52,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
 
 struct AssertModuleSource<'tcx> {
     tcx: TyCtxt<'tcx>,
-    available_cgus: BTreeSet<Symbol>,
+    available_cgus: BTreeSet<String>,
 }
 
 impl AssertModuleSource<'tcx> {
@@ -121,12 +121,11 @@ impl AssertModuleSource<'tcx> {
 
         debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);
 
-        if !self.available_cgus.contains(&cgu_name) {
+        if !self.available_cgus.contains(&*cgu_name.as_str()) {
             self.tcx.sess.span_err(
                 attr.span,
                 &format!(
-                    "no module named `{}` (mangled: {}). \
-                          Available modules: {}",
+                    "no module named `{}` (mangled: {}). Available modules: {}",
                     user_path,
                     cgu_name,
                     self.available_cgus
diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
index e51862be9a8..2897480c32b 100644
--- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
+++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
@@ -37,7 +37,7 @@ macro_rules! provide {
                 def_id_arg: ty::query::query_keys::$name<$lt>,
             ) -> ty::query::query_values::$name<$lt> {
                 let _prof_timer =
-                    $tcx.prof.generic_activity("metadata_decode_entry");
+                    $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name)));
 
                 #[allow(unused_variables)]
                 let ($def_id, $other) = def_id_arg.into_args();
diff --git a/src/librustc_middle/ty/flags.rs b/src/librustc_middle/ty/flags.rs
index 27f50c240db..81f7474962c 100644
--- a/src/librustc_middle/ty/flags.rs
+++ b/src/librustc_middle/ty/flags.rs
@@ -85,6 +85,8 @@ impl FlagComputation {
             }
 
             &ty::Generator(_, ref substs, _) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 let substs = substs.as_generator();
                 let should_remove_further_specializable =
                     !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
@@ -107,6 +109,8 @@ impl FlagComputation {
             }
 
             &ty::Closure(_, substs) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 let substs = substs.as_closure();
                 let should_remove_further_specializable =
                     !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
@@ -192,6 +196,8 @@ impl FlagComputation {
             }
 
             &ty::FnDef(_, substs) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 self.add_substs(substs);
             }
 
diff --git a/src/librustc_middle/ty/fold.rs b/src/librustc_middle/ty/fold.rs
index 492f8ce9ef1..87434f3f267 100644
--- a/src/librustc_middle/ty/fold.rs
+++ b/src/librustc_middle/ty/fold.rs
@@ -150,6 +150,12 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
         self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
     }
 
+    /// Does this value contain closures, generators or functions such that it may require
+    /// polymorphization?
+    fn may_polymorphize(&self) -> bool {
+        self.has_type_flags(TypeFlags::MAY_POLYMORPHIZE)
+    }
+
     /// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`.
     fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool {
         pub struct Visitor<F>(F);
diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs
index 289a9db7327..cf876db26bc 100644
--- a/src/librustc_middle/ty/instance.rs
+++ b/src/librustc_middle/ty/instance.rs
@@ -474,26 +474,7 @@ impl<'tcx> Instance<'tcx> {
         }
 
         if let InstanceDef::Item(def) = self.def {
-            let unused = tcx.unused_generic_params(def.did);
-
-            if unused.is_empty() {
-                // Exit early if every parameter was used.
-                return self;
-            }
-
-            debug!("polymorphize: unused={:?}", unused);
-            let polymorphized_substs =
-                InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
-                // If parameter is a const or type parameter..
-                ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
-                    // ..and is within range and unused..
-                    unused.contains(param.index).unwrap_or(false) =>
-                        // ..then use the identity for this parameter.
-                        tcx.mk_param_from_def(param),
-                // Otherwise, use the parameter as before.
-                _ => self.substs[param.index as usize],
-            });
-
+            let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
             debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
             Self { def: self.def, substs: polymorphized_substs }
         } else {
@@ -502,6 +483,82 @@ impl<'tcx> Instance<'tcx> {
     }
 }
 
+fn polymorphize<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    substs: SubstsRef<'tcx>,
+) -> SubstsRef<'tcx> {
+    debug!("polymorphize({:?}, {:?})", def_id, substs);
+    let unused = tcx.unused_generic_params(def_id);
+    debug!("polymorphize: unused={:?}", unused);
+
+    struct PolymorphizationFolder<'tcx> {
+        tcx: TyCtxt<'tcx>,
+    };
+
+    impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> {
+        fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+            debug!("fold_ty: ty={:?}", ty);
+            match ty.kind {
+                ty::Closure(def_id, substs) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_closure(def_id, polymorphized_substs)
+                    }
+                }
+                ty::FnDef(def_id, substs) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_fn_def(def_id, polymorphized_substs)
+                    }
+                }
+                ty::Generator(def_id, substs, movability) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_generator(def_id, polymorphized_substs, movability)
+                    }
+                }
+                _ => ty.super_fold_with(self),
+            }
+        }
+    }
+
+    InternalSubsts::for_item(tcx, def_id, |param, _| {
+        let is_unused = unused.contains(param.index).unwrap_or(false);
+        debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
+        match param.kind {
+            // If parameter is a const or type parameter..
+            ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
+                // ..and is within range and unused..
+                unused.contains(param.index).unwrap_or(false) =>
+                    // ..then use the identity for this parameter.
+                    tcx.mk_param_from_def(param),
+
+            // If the parameter does not contain any closures or generators, then use the
+            // substitution directly.
+            _ if !substs.may_polymorphize() => substs[param.index as usize],
+
+            // Otherwise, use the substitution after polymorphizing.
+            _ => {
+                let arg = substs[param.index as usize];
+                let polymorphized_arg = arg.fold_with(&mut PolymorphizationFolder { tcx });
+                debug!("polymorphize: arg={:?} polymorphized_arg={:?}", arg, polymorphized_arg);
+                ty::GenericArg::from(polymorphized_arg)
+            }
+        }
+    })
+}
+
 fn needs_fn_once_adapter_shim(
     actual_closure_kind: ty::ClosureKind,
     trait_closure_kind: ty::ClosureKind,
diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs
index bd45f866abc..c82fb2712c2 100644
--- a/src/librustc_middle/ty/mod.rs
+++ b/src/librustc_middle/ty/mod.rs
@@ -575,6 +575,10 @@ bitflags! {
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
         const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
+
+        /// Does this value contain closures, generators or functions such that it may require
+        /// polymorphization?
+        const MAY_POLYMORPHIZE = 1 << 18;
     }
 }
 
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 8e2fd709d66..db0b0415728 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -34,7 +34,9 @@ use crate::interpret::{
 };
 use crate::transform::{MirPass, MirSource};
 
-/// The maximum number of bytes that we'll allocate space for a return value.
+/// The maximum number of bytes that we'll allocate space for a local or the return value.
+/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
+/// Severely regress performance.
 const MAX_ALLOC_LIMIT: u64 = 1024;
 
 /// Macro for machine-specific `InterpError` without allocation.
@@ -155,14 +157,19 @@ struct ConstPropMachine<'mir, 'tcx> {
     written_only_inside_own_block_locals: FxHashSet<Local>,
     /// Locals that need to be cleared after every block terminates.
     only_propagate_inside_block_locals: BitSet<Local>,
+    can_const_prop: IndexVec<Local, ConstPropMode>,
 }
 
 impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
-    fn new(only_propagate_inside_block_locals: BitSet<Local>) -> Self {
+    fn new(
+        only_propagate_inside_block_locals: BitSet<Local>,
+        can_const_prop: IndexVec<Local, ConstPropMode>,
+    ) -> Self {
         Self {
             stack: Vec::new(),
             written_only_inside_own_block_locals: Default::default(),
             only_propagate_inside_block_locals,
+            can_const_prop,
         }
     }
 }
@@ -241,6 +248,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
         local: Local,
     ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>>
     {
+        if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
+            throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
+        }
         if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
             ecx.machine.written_only_inside_own_block_locals.insert(local);
         }
@@ -285,7 +295,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
 struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
     tcx: TyCtxt<'tcx>,
-    can_const_prop: IndexVec<Local, ConstPropMode>,
     param_env: ParamEnv<'tcx>,
     // FIXME(eddyb) avoid cloning these two fields more than once,
     // by accessing them through `ecx` instead.
@@ -331,7 +340,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         let param_env = tcx.param_env_reveal_all_normalized(def_id);
 
         let span = tcx.def_span(def_id);
-        let can_const_prop = CanConstProp::check(body);
+        // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
+        // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
+        // `layout_of` query invocations.
+        let can_const_prop = CanConstProp::check(tcx, param_env, body);
         let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
         for (l, mode) in can_const_prop.iter_enumerated() {
             if *mode == ConstPropMode::OnlyInsideOwnBlock {
@@ -342,7 +354,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             tcx,
             span,
             param_env,
-            ConstPropMachine::new(only_propagate_inside_block_locals),
+            ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
             (),
         );
 
@@ -368,7 +380,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             ecx,
             tcx,
             param_env,
-            can_const_prop,
             // FIXME(eddyb) avoid cloning these two fields more than once,
             // by accessing them through `ecx` instead.
             source_scopes: body.source_scopes.clone(),
@@ -612,15 +623,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     fn const_prop(
         &mut self,
         rvalue: &Rvalue<'tcx>,
-        place_layout: TyAndLayout<'tcx>,
         source_info: SourceInfo,
         place: Place<'tcx>,
     ) -> Option<()> {
-        // #66397: Don't try to eval into large places as that can cause an OOM
-        if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
-            return None;
-        }
-
         // Perform any special handling for specific Rvalue types.
         // Generally, checks here fall into one of two categories:
         //   1. Additional checking to provide useful lints to the user
@@ -893,7 +898,11 @@ struct CanConstProp {
 
 impl CanConstProp {
     /// Returns true if `local` can be propagated
-    fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
+    fn check(
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        body: &Body<'tcx>,
+    ) -> IndexVec<Local, ConstPropMode> {
         let mut cpv = CanConstProp {
             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
             found_assignment: BitSet::new_empty(body.local_decls.len()),
@@ -903,6 +912,16 @@ impl CanConstProp {
             ),
         };
         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
+            let ty = body.local_decls[local].ty;
+            match tcx.layout_of(param_env.and(ty)) {
+                Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
+                // Either the layout fails to compute, then we can't use this local anyway
+                // or the local is too large, then we don't want to.
+                _ => {
+                    *val = ConstPropMode::NoPropagation;
+                    continue;
+                }
+            }
             // Cannot use args at all
             // Cannot use locals because if x < y { y - x } else { x - y } would
             //        lint for x != y
@@ -1018,61 +1037,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
         let source_info = statement.source_info;
         self.source_info = Some(source_info);
         if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
-            let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
-            if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
-                let can_const_prop = self.can_const_prop[place.local];
-                if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
-                    // This will return None if the above `const_prop` invocation only "wrote" a
-                    // type whose creation requires no write. E.g. a generator whose initial state
-                    // consists solely of uninitialized memory (so it doesn't capture any locals).
-                    if let Some(value) = self.get_const(place) {
-                        if self.should_const_prop(value) {
-                            trace!("replacing {:?} with {:?}", rval, value);
-                            self.replace_with_const(rval, value, source_info);
-                            if can_const_prop == ConstPropMode::FullConstProp
-                                || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
-                            {
-                                trace!("propagated into {:?}", place);
-                            }
+            let can_const_prop = self.ecx.machine.can_const_prop[place.local];
+            if let Some(()) = self.const_prop(rval, source_info, place) {
+                // This will return None if the above `const_prop` invocation only "wrote" a
+                // type whose creation requires no write. E.g. a generator whose initial state
+                // consists solely of uninitialized memory (so it doesn't capture any locals).
+                if let Some(value) = self.get_const(place) {
+                    if self.should_const_prop(value) {
+                        trace!("replacing {:?} with {:?}", rval, value);
+                        self.replace_with_const(rval, value, source_info);
+                        if can_const_prop == ConstPropMode::FullConstProp
+                            || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
+                        {
+                            trace!("propagated into {:?}", place);
                         }
                     }
-                    match can_const_prop {
-                        ConstPropMode::OnlyInsideOwnBlock => {
-                            trace!(
-                                "found local restricted to its block. \
+                }
+                match can_const_prop {
+                    ConstPropMode::OnlyInsideOwnBlock => {
+                        trace!(
+                            "found local restricted to its block. \
                                 Will remove it from const-prop after block is finished. Local: {:?}",
-                                place.local
-                            );
-                        }
-                        ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
-                            trace!("can't propagate into {:?}", place);
-                            if place.local != RETURN_PLACE {
-                                Self::remove_const(&mut self.ecx, place.local);
-                            }
+                            place.local
+                        );
+                    }
+                    ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+                        trace!("can't propagate into {:?}", place);
+                        if place.local != RETURN_PLACE {
+                            Self::remove_const(&mut self.ecx, place.local);
                         }
-                        ConstPropMode::FullConstProp => {}
                     }
-                } else {
-                    // Const prop failed, so erase the destination, ensuring that whatever happens
-                    // from here on, does not know about the previous value.
-                    // This is important in case we have
-                    // ```rust
-                    // let mut x = 42;
-                    // x = SOME_MUTABLE_STATIC;
-                    // // x must now be undefined
-                    // ```
-                    // FIXME: we overzealously erase the entire local, because that's easier to
-                    // implement.
-                    trace!(
-                        "propagation into {:?} failed.
-                        Nuking the entire site from orbit, it's the only way to be sure",
-                        place,
-                    );
-                    Self::remove_const(&mut self.ecx, place.local);
+                    ConstPropMode::FullConstProp => {}
                 }
             } else {
+                // Const prop failed, so erase the destination, ensuring that whatever happens
+                // from here on, does not know about the previous value.
+                // This is important in case we have
+                // ```rust
+                // let mut x = 42;
+                // x = SOME_MUTABLE_STATIC;
+                // // x must now be undefined
+                // ```
+                // FIXME: we overzealously erase the entire local, because that's easier to
+                // implement.
                 trace!(
-                    "cannot propagate into {:?}, because the type of the local is generic.",
+                    "propagation into {:?} failed.
+                        Nuking the entire site from orbit, it's the only way to be sure",
                     place,
                 );
                 Self::remove_const(&mut self.ecx, place.local);
diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs
index 2b0e637c74e..c3a79660eb9 100644
--- a/src/librustc_parse/lexer/mod.rs
+++ b/src/librustc_parse/lexer/mod.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Token, TokenKind};
 use rustc_ast::util::comments;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError};
@@ -170,22 +170,20 @@ impl<'a> StringReader<'a> {
         match token {
             rustc_lexer::TokenKind::LineComment => {
                 let string = self.str_from(start);
-                // comments with only more "/"s are not doc comments
-                if comments::is_line_doc_comment(string) {
+                if let Some(attr_style) = comments::line_doc_comment_style(string) {
                     self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment");
-                    token::DocComment(Symbol::intern(string))
+                    // Opening delimiter of the length 3 is not included into the symbol.
+                    token::DocComment(CommentKind::Line, attr_style, Symbol::intern(&string[3..]))
                 } else {
                     token::Comment
                 }
             }
             rustc_lexer::TokenKind::BlockComment { terminated } => {
                 let string = self.str_from(start);
-                // block comments starting with "/**" or "/*!" are doc-comments
-                // but comments with only "*"s between two "/"s are not
-                let is_doc_comment = comments::is_block_doc_comment(string);
+                let attr_style = comments::block_doc_comment_style(string, terminated);
 
                 if !terminated {
-                    let msg = if is_doc_comment {
+                    let msg = if attr_style.is_some() {
                         "unterminated block doc-comment"
                     } else {
                         "unterminated block comment"
@@ -202,9 +200,15 @@ impl<'a> StringReader<'a> {
                     FatalError.raise();
                 }
 
-                if is_doc_comment {
+                if let Some(attr_style) = attr_style {
                     self.forbid_bare_cr(start, string, "bare CR not allowed in block doc-comment");
-                    token::DocComment(Symbol::intern(string))
+                    // Opening delimiter of the length 3 and closing delimiter of the length 2
+                    // are not included into the symbol.
+                    token::DocComment(
+                        CommentKind::Block,
+                        attr_style,
+                        Symbol::intern(&string[3..string.len() - if terminated { 2 } else { 0 }]),
+                    )
                 } else {
                     token::Comment
                 }
diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs
index 3319ca44da4..723e4333790 100644
--- a/src/librustc_parse/lib.rs
+++ b/src/librustc_parse/lib.rs
@@ -486,7 +486,9 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
 
         (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b,
 
-        (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b,
+        (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3,
+
+        (&Shebang(a), &Shebang(b)) => a == b,
 
         (&Literal(a), &Literal(b)) => a == b,
 
@@ -524,7 +526,7 @@ fn prepend_attrs(
 
         let item = match attr.kind {
             ast::AttrKind::Normal(ref item) => item,
-            ast::AttrKind::DocComment(_) => {
+            ast::AttrKind::DocComment(..) => {
                 let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
                 builder.push(stream);
                 continue;
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index 8b67f4743c6..b6a8ee71beb 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -2,10 +2,9 @@ use super::{Parser, PathStyle};
 use rustc_ast::ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
-use rustc_ast::util::comments;
 use rustc_ast_pretty::pprust;
 use rustc_errors::{error_code, PResult};
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
 
 use log::debug;
 
@@ -47,8 +46,8 @@ impl<'a> Parser<'a> {
                 let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
                 attrs.push(attr);
                 just_parsed_doc_comment = false;
-            } else if let token::DocComment(s) = self.token.kind {
-                let attr = self.mk_doc_comment(s);
+            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
+                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
                 if attr.style != ast::AttrStyle::Outer {
                     self.sess
                         .span_diagnostic
@@ -73,10 +72,6 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
-    fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
-        attr::mk_doc_comment(comments::doc_comment_style(s), s, self.token.span)
-    }
-
     /// Matches `attribute = # ! [ meta_item ]`.
     ///
     /// If `permit_inner` is `true`, then a leading `!` indicates an inner
@@ -184,9 +179,9 @@ impl<'a> Parser<'a> {
                 let attr = self.parse_attribute(true)?;
                 assert_eq!(attr.style, ast::AttrStyle::Inner);
                 attrs.push(attr);
-            } else if let token::DocComment(s) = self.token.kind {
+            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
                 // We need to get the position of this token before we bump.
-                let attr = self.mk_doc_comment(s);
+                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
                 if attr.style == ast::AttrStyle::Inner {
                     attrs.push(attr);
                     self.bump();
diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs
index 5e9411327ca..2854356ab0f 100644
--- a/src/librustc_parse/parser/diagnostics.rs
+++ b/src/librustc_parse/parser/diagnostics.rs
@@ -1419,7 +1419,7 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
-        if let token::DocComment(_) = self.token.kind {
+        if let token::DocComment(..) = self.token.kind {
             self.struct_span_err(
                 self.token.span,
                 "documentation comments cannot be applied to a function parameter's type",
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 5923a185dcf..10d214e52ab 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -610,7 +610,7 @@ impl<'a> Parser<'a> {
 
     /// Recover on a doc comment before `}`.
     fn recover_doc_comment_before_brace(&mut self) -> bool {
-        if let token::DocComment(_) = self.token.kind {
+        if let token::DocComment(..) = self.token.kind {
             if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
                 struct_span_err!(
                     self.diagnostic(),
@@ -1231,7 +1231,7 @@ impl<'a> Parser<'a> {
                 self.bump();
             }
             token::CloseDelim(token::Brace) => {}
-            token::DocComment(_) => {
+            token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
                 let mut err = self.span_fatal_err(self.token.span, Error::UselessDocComment);
                 self.bump(); // consume the doc comment
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 2509a979221..ededfc43669 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -22,7 +22,6 @@ use rustc_ast::ast::{
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, DelimToken, Token, TokenKind};
 use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
-use rustc_ast::util::comments::{doc_comment_style, strip_doc_comment_decoration};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
@@ -209,18 +208,18 @@ impl TokenCursor {
     }
 
     fn next_desugared(&mut self) -> Token {
-        let (name, sp) = match self.next() {
-            Token { kind: token::DocComment(name), span } => (name, span),
+        let (data, attr_style, sp) = match self.next() {
+            Token { kind: token::DocComment(_, attr_style, data), span } => {
+                (data, attr_style, span)
+            }
             tok => return tok,
         };
 
-        let stripped = strip_doc_comment_decoration(name);
-
         // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
         // required to wrap the text.
         let mut num_of_hashes = 0;
         let mut count = 0;
-        for ch in stripped.chars() {
+        for ch in data.as_str().chars() {
             count = match ch {
                 '"' => 1,
                 '#' if count > 0 => count + 1,
@@ -236,10 +235,7 @@ impl TokenCursor {
             [
                 TokenTree::token(token::Ident(sym::doc, false), sp),
                 TokenTree::token(token::Eq, sp),
-                TokenTree::token(
-                    TokenKind::lit(token::StrRaw(num_of_hashes), Symbol::intern(&stripped), None),
-                    sp,
-                ),
+                TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), sp),
             ]
             .iter()
             .cloned()
@@ -251,7 +247,7 @@ impl TokenCursor {
             TokenCursorFrame::new(
                 delim_span,
                 token::NoDelim,
-                &if doc_comment_style(name) == AttrStyle::Inner {
+                &if attr_style == AttrStyle::Inner {
                     [TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body]
                         .iter()
                         .cloned()
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index 81e29047dc5..a6b5b0ff301 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -1075,10 +1075,9 @@ impl<'a> Resolver<'a> {
         ) = binding.kind
         {
             let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor");
-            if let Some(fields) = self.field_names.get(&def_id) {
-                let first_field = fields.first().expect("empty field list in the map");
-                return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
-            }
+            let fields = self.field_names.get(&def_id)?;
+            let first_field = fields.first()?; // Handle `struct Foo()`
+            return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
         }
         None
     }
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 8e379a35100..d854835a024 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -10,7 +10,7 @@ mod span_utils;
 mod sig;
 
 use rustc_ast::ast::{self};
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
 use rustc_ast_pretty::pprust::attribute_to_string;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind as HirDefKind, Res};
@@ -822,11 +822,8 @@ impl<'tcx> SaveContext<'tcx> {
 
         for attr in attrs {
             if let Some(val) = attr.doc_str() {
-                if attr.is_doc_comment() {
-                    result.push_str(&strip_doc_comment_decoration(val));
-                } else {
-                    result.push_str(&val.as_str());
-                }
+                // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
+                result.push_str(&beautify_doc_string(val));
                 result.push('\n');
             } else if attr.check_name(sym::doc) {
                 if let Some(meta_list) = attr.meta_item_list() {
diff --git a/src/librustc_trait_selection/traits/select/mod.rs b/src/librustc_trait_selection/traits/select/mod.rs
index 75e11619924..dbb64e57178 100644
--- a/src/librustc_trait_selection/traits/select/mod.rs
+++ b/src/librustc_trait_selection/traits/select/mod.rs
@@ -440,7 +440,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             obligation
         );
 
-        // `previous_stack` stores a `TraitObligatiom`, while `obligation` is
+        // `previous_stack` stores a `TraitObligation`, while `obligation` is
         // a `PredicateObligation`. These are distinct types, so we can't
         // use any `Option` combinator method that would force them to be
         // the same.
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b261df4073d..50eca75d7ca 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -10,7 +10,7 @@ use std::{slice, vec};
 
 use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::attr;
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -506,10 +506,11 @@ impl Attributes {
             .iter()
             .filter_map(|attr| {
                 if let Some(value) = attr.doc_str() {
-                    let (value, mk_fragment): (_, fn(_, _, _) -> _) = if attr.is_doc_comment() {
-                        (strip_doc_comment_decoration(value), DocFragment::SugaredDoc)
+                    let value = beautify_doc_string(value);
+                    let mk_fragment: fn(_, _, _) -> _ = if attr.is_doc_comment() {
+                        DocFragment::SugaredDoc
                     } else {
-                        (value.to_string(), DocFragment::RawDoc)
+                        DocFragment::RawDoc
                     };
 
                     let line = doc_line;
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index adef4c83224..c22538f21f6 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -610,6 +610,9 @@ pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId {
         Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
         Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
         Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+        Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
+            (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+        }
         Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
         Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
         Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index bf7a43236e0..f84486347af 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -17,6 +17,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::symbol::Symbol;
 use rustc_span::DUMMY_SP;
 
+use std::cell::Cell;
 use std::ops::Range;
 
 use crate::clean::*;
@@ -62,11 +63,15 @@ struct LinkCollector<'a, 'tcx> {
     cx: &'a DocContext<'tcx>,
     // NOTE: this may not necessarily be a module in the current crate
     mod_ids: Vec<DefId>,
+    /// This is used to store the kind of associated items,
+    /// because `clean` and the disambiguator code expect them to be different.
+    /// See the code for associated items on inherent impls for details.
+    kind_side_channel: Cell<Option<DefKind>>,
 }
 
 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
     fn new(cx: &'a DocContext<'tcx>) -> Self {
-        LinkCollector { cx, mod_ids: Vec::new() }
+        LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
     }
 
     fn variant_field(
@@ -174,7 +179,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
     fn resolve(
         &self,
         path_str: &str,
-        disambiguator: Option<&str>,
+        disambiguator: Option<Disambiguator>,
         ns: Namespace,
         current_item: &Option<String>,
         parent_id: Option<DefId>,
@@ -214,7 +219,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                     Res::Def(DefKind::Mod, _) => {
                         // This resolved to a module, but if we were passed `type@`,
                         // we want primitive types to take precedence instead.
-                        if disambiguator == Some("type") {
+                        if disambiguator == Some(Disambiguator::Namespace(Namespace::TypeNS)) {
                             if let Some(prim) = is_primitive(path_str, ns) {
                                 if extra_fragment.is_some() {
                                     return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
@@ -347,6 +352,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                                 AnchorFailure::AssocConstant
                             }))
                         } else {
+                            // HACK(jynelson): `clean` expects the type, not the associated item.
+                            // but the disambiguator logic expects the associated item.
+                            // Store the kind in a side channel so that only the disambiguator logic looks at it.
+                            self.kind_side_channel.replace(Some(item.kind.as_def_kind()));
                             Ok((ty_res, Some(format!("{}.{}", out, item_name))))
                         }
                     } else {
@@ -415,7 +424,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                                 AnchorFailure::Method
                             }))
                         } else {
-                            Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
+                            let res = Res::Def(item.kind.as_def_kind(), item.def_id);
+                            Ok((res, Some(format!("{}.{}", kind, item_name))))
                         }
                     } else {
                         self.variant_field(path_str, current_item, module_id)
@@ -574,46 +584,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             };
             let resolved_self;
             let mut path_str;
+            let disambiguator;
             let (res, fragment) = {
-                let mut kind = None;
-                let mut disambiguator = None;
-                path_str = if let Some(prefix) =
-                    ["struct@", "enum@", "type@", "trait@", "union@", "module@", "mod@"]
-                        .iter()
-                        .find(|p| link.starts_with(**p))
-                {
-                    kind = Some(TypeNS);
-                    disambiguator = Some(&prefix[..prefix.len() - 1]);
-                    link.trim_start_matches(prefix)
-                } else if let Some(prefix) =
-                    ["const@", "static@", "value@", "function@", "fn@", "method@"]
-                        .iter()
-                        .find(|p| link.starts_with(**p))
-                {
-                    kind = Some(ValueNS);
-                    disambiguator = Some(&prefix[..prefix.len() - 1]);
-                    link.trim_start_matches(prefix)
-                } else if link.ends_with("!()") {
-                    kind = Some(MacroNS);
-                    link.trim_end_matches("!()")
-                } else if link.ends_with("()") {
-                    kind = Some(ValueNS);
-                    disambiguator = Some("fn");
-                    link.trim_end_matches("()")
-                } else if link.starts_with("macro@") {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("macro");
-                    link.trim_start_matches("macro@")
-                } else if link.starts_with("derive@") {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("derive");
-                    link.trim_start_matches("derive@")
-                } else if link.ends_with('!') {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("macro");
-                    link.trim_end_matches('!')
+                path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
+                    disambiguator = Some(d);
+                    path
                 } else {
-                    &link[..]
+                    disambiguator = None;
+                    &link
                 }
                 .trim();
 
@@ -646,7 +624,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                     }
                 }
 
-                match kind {
+                match disambiguator.map(Disambiguator::ns) {
                     Some(ns @ ValueNS) => {
                         match self.resolve(
                             path_str,
@@ -789,6 +767,42 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             } else {
                 debug!("intra-doc link to {} resolved to {:?}", path_str, res);
 
+                // Disallow e.g. linking to enums with `struct@`
+                if let Res::Def(kind, id) = res {
+                    debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
+                    match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
+                        | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
+                        // NOTE: this allows 'method' to mean both normal functions and associated functions
+                        // This can't cause ambiguity because both are in the same namespace.
+                        | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
+                        // These are namespaces; allow anything in the namespace to match
+                        | (_, Some(Disambiguator::Namespace(_)))
+                        // If no disambiguator given, allow anything
+                        | (_, None)
+                        // All of these are valid, so do nothing
+                        => {}
+                        (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
+                        (_, Some(Disambiguator::Kind(expected))) => {
+                            // The resolved item did not match the disambiguator; give a better error than 'not found'
+                            let msg = format!("incompatible link kind for `{}`", path_str);
+                            report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
+                                // HACK(jynelson): by looking at the source I saw the DefId we pass
+                                // for `expected.descr()` doesn't matter, since it's not a crate
+                                let note = format!("this link resolved to {} {}, which is not {} {}", kind.article(), kind.descr(id), expected.article(), expected.descr(id));
+                                let suggestion = Disambiguator::display_for(kind, path_str);
+                                let help_msg = format!("to link to the {}, use its disambiguator", kind.descr(id));
+                                diag.note(&note);
+                                if let Some(sp) = sp {
+                                    diag.span_suggestion(sp, &help_msg, suggestion, Applicability::MaybeIncorrect);
+                                } else {
+                                    diag.help(&format!("{}: {}", help_msg, suggestion));
+                                }
+                            });
+                            continue;
+                        }
+                    }
+                }
+
                 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
                 if let Some((src_id, dst_id)) = res
                     .opt_def_id()
@@ -837,6 +851,94 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
     }
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum Disambiguator {
+    Kind(DefKind),
+    Namespace(Namespace),
+}
+
+impl Disambiguator {
+    /// (disambiguator, path_str)
+    fn from_str(link: &str) -> Result<(Self, &str), ()> {
+        use Disambiguator::{Kind, Namespace as NS};
+
+        let find_suffix = || {
+            let suffixes = [
+                ("!()", DefKind::Macro(MacroKind::Bang)),
+                ("()", DefKind::Fn),
+                ("!", DefKind::Macro(MacroKind::Bang)),
+            ];
+            for &(suffix, kind) in &suffixes {
+                if link.ends_with(suffix) {
+                    return Ok((Kind(kind), link.trim_end_matches(suffix)));
+                }
+            }
+            Err(())
+        };
+
+        if let Some(idx) = link.find('@') {
+            let (prefix, rest) = link.split_at(idx);
+            let d = match prefix {
+                "struct" => Kind(DefKind::Struct),
+                "enum" => Kind(DefKind::Enum),
+                "trait" => Kind(DefKind::Trait),
+                "union" => Kind(DefKind::Union),
+                "module" | "mod" => Kind(DefKind::Mod),
+                "const" | "constant" => Kind(DefKind::Const),
+                "static" => Kind(DefKind::Static),
+                "function" | "fn" | "method" => Kind(DefKind::Fn),
+                "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
+                "type" => NS(Namespace::TypeNS),
+                "value" => NS(Namespace::ValueNS),
+                "macro" => NS(Namespace::MacroNS),
+                _ => return find_suffix(),
+            };
+            Ok((d, &rest[1..]))
+        } else {
+            find_suffix()
+        }
+    }
+
+    fn display_for(kind: DefKind, path_str: &str) -> String {
+        if kind == DefKind::Macro(MacroKind::Bang) {
+            return format!("{}!", path_str);
+        } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
+            return format!("{}()", path_str);
+        }
+        let prefix = match kind {
+            DefKind::Struct => "struct",
+            DefKind::Enum => "enum",
+            DefKind::Trait => "trait",
+            DefKind::Union => "union",
+            DefKind::Mod => "mod",
+            DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
+                "const"
+            }
+            DefKind::Static => "static",
+            DefKind::Macro(MacroKind::Derive) => "derive",
+            // Now handle things that don't have a specific disambiguator
+            _ => match kind
+                .ns()
+                .expect("tried to calculate a disambiguator for a def without a namespace?")
+            {
+                Namespace::TypeNS => "type",
+                Namespace::ValueNS => "value",
+                Namespace::MacroNS => "macro",
+            },
+        };
+        format!("{}@{}", prefix, path_str)
+    }
+
+    fn ns(self) -> Namespace {
+        match self {
+            Self::Namespace(n) => n,
+            Self::Kind(k) => {
+                k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
+            }
+        }
+    }
+}
+
 /// Reports a diagnostic for an intra-doc link.
 ///
 /// If no link range is provided, or the source span of the link cannot be determined, the span of
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index b86a105ff76..969f5a2eaf1 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -943,7 +943,12 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
         // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
         // anything else, this will combine them for us.
         if let Some(doc) = attrs.collapsed_doc_value() {
-            self.collector.set_position(attrs.span.unwrap_or(DUMMY_SP));
+            // Use the outermost invocation, so that doctest names come from where the docs were written.
+            let span = attrs
+                .span
+                .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
+                .unwrap_or(DUMMY_SP);
+            self.collector.set_position(span);
             markdown::find_testable_code(
                 &doc,
                 self.collector,
diff --git a/src/test/codegen-units/polymorphization/pr-75255.rs b/src/test/codegen-units/polymorphization/pr-75255.rs
new file mode 100644
index 00000000000..af47b440640
--- /dev/null
+++ b/src/test/codegen-units/polymorphization/pr-75255.rs
@@ -0,0 +1,52 @@
+// compile-flags:-Zpolymorphize=on -Zprint-mono-items=lazy -Copt-level=1
+// ignore-tidy-linelength
+
+#![crate_type = "rlib"]
+
+// Test that only one copy of `Iter::map` and `iter::repeat` are generated.
+
+fn unused<T>() -> u64 {
+    42
+}
+
+fn foo<T>() {
+    let x = [1, 2, 3, std::mem::size_of::<T>()];
+    x.iter().map(|_| ());
+}
+
+//~ MONO_ITEM fn core::iter[0]::adapters[0]::{{impl}}[29]::new[0]<core::slice[0]::Iter[0]<usize>, pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.0[External]
+//~ MONO_ITEM fn core::iter[0]::traits[0]::iterator[0]::Iterator[0]::map[0]<core::slice[0]::Iter[0]<usize>, (), pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.1[Internal]
+
+fn bar<T>() {
+    std::iter::repeat(unused::<T>);
+}
+
+//~ MONO_ITEM fn core::iter[0]::sources[0]::repeat[0]<fn() -> u64> @@ pr_75255-cgu.1[Internal]
+
+pub fn dispatch() {
+    foo::<String>();
+    foo::<Vec<String>>();
+
+    bar::<String>();
+    bar::<Vec<String>>();
+}
+
+// These are all the items that aren't relevant to the test.
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::add[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::is_null[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::offset[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_add[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_offset[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::non_null[0]::{{impl}}[3]::new_unchecked[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::null[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::as_ptr[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::iter[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::len[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::dispatch[0] @@ pr_75255-cgu.1[External]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
diff --git a/src/test/debuginfo/function-arguments-naked.rs b/src/test/debuginfo/function-arguments-naked.rs
index e88a99b322e..5f3a1eb44e4 100644
--- a/src/test/debuginfo/function-arguments-naked.rs
+++ b/src/test/debuginfo/function-arguments-naked.rs
@@ -3,6 +3,9 @@
 // We have to ignore android because of this issue:
 // https://github.com/rust-lang/rust/issues/74847
 // ignore-android
+//
+// We need to use inline assembly, so just use one platform
+// only-x86_64
 
 // compile-flags:-g
 
@@ -24,6 +27,7 @@
 // lldb-command:continue
 
 
+#![feature(asm)]
 #![feature(naked_functions)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
@@ -33,8 +37,6 @@ fn main() {
 }
 
 #[naked]
-fn naked(x: usize, y: usize) {
-    zzz(); // #break
+extern "C" fn naked(x: usize, y: usize) {
+    unsafe { asm!("ret"); } // #break
 }
-
-fn zzz() { () }
diff --git a/src/test/debuginfo/pretty-std-collections-hash.rs b/src/test/debuginfo/pretty-std-collections-hash.rs
index 361b300f28c..e8f52deabd8 100644
--- a/src/test/debuginfo/pretty-std-collections-hash.rs
+++ b/src/test/debuginfo/pretty-std-collections-hash.rs
@@ -9,35 +9,35 @@
 // cdb-check:hash_set,d [...] : { size=15 } [Type: [...]::HashSet<u64, [...]>]
 // cdb-check:    [size]           : 15 [Type: [...]]
 // cdb-check:    [capacity]       : [...]
-// cdb-check:    [[...]] [...]    : 0 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 0 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 1 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 1 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 2 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 2 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 3 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 3 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 4 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 4 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 5 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 5 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 6 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 6 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 7 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 7 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 8 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 8 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 9 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 9 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 10 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 10 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 11 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 11 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 12 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 12 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 13 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 13 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 14 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 14 [Type: u64]
 
 // cdb-command: dx hash_map,d
 // cdb-check:hash_map,d [...] : { size=15 } [Type: [...]::HashMap<u64, u64, [...]>]
diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs
index e63309a9bd2..9c30e040031 100644
--- a/src/test/mir-opt/const-promotion-extern-static.rs
+++ b/src/test/mir-opt/const-promotion-extern-static.rs
@@ -1,7 +1,7 @@
+// ignore-endian-big
 extern "C" {
     static X: i32;
 }
-
 static Y: i32 = 42;
 
 // EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff
diff --git a/src/test/mir-opt/const_allocation.rs b/src/test/mir-opt/const_allocation.rs
index bb1c48e8e3c..b0fcb86fcee 100644
--- a/src/test/mir-opt/const_allocation.rs
+++ b/src/test/mir-opt/const_allocation.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 static FOO: &[(Option<i32>, &[&str])] =
     &[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])];
 
diff --git a/src/test/mir-opt/const_allocation2.rs b/src/test/mir-opt/const_allocation2.rs
index 56839255c0e..30afedffb39 100644
--- a/src/test/mir-opt/const_allocation2.rs
+++ b/src/test/mir-opt/const_allocation2.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 // EMIT_MIR const_allocation2.main.ConstProp.after.mir
 fn main() {
     FOO;
diff --git a/src/test/mir-opt/const_allocation3.rs b/src/test/mir-opt/const_allocation3.rs
index 2ce289aea3f..ddeb32ab9a5 100644
--- a/src/test/mir-opt/const_allocation3.rs
+++ b/src/test/mir-opt/const_allocation3.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 // EMIT_MIR const_allocation3.main.ConstProp.after.mir
 fn main() {
     FOO;
diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit
new file mode 100644
index 00000000000..721766f9849
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit
@@ -0,0 +1,85 @@
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+      let _1: u8;                          // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+      let mut _2: [u8; 5000];              // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+      let _3: usize;                       // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+      let mut _4: usize;                   // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      let mut _5: bool;                    // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+          StorageLive(_2);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+          _2 = [const 0_u8; 5000];         // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+                                           // ty::Const
+                                           // + ty: u8
+                                           // + val: Value(Scalar(0x00))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:18: 6:22
+                                           // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+          StorageLive(_3);                 // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+          _3 = const 2_usize;              // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x00000002))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:30: 6:31
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+          _4 = const 5000_usize;           // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x00001388))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:17: 6:32
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
+-         _5 = Lt(_3, _4);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+-         assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++         _5 = const true;                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++         assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x00001388))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x00000002))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+      }
+  
+      bb1: {
+          _1 = _2[_3];                     // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+          StorageDead(_3);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          StorageDead(_2);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          _0 = const ();                   // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:4:11: 7:2
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          StorageDead(_1);                 // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+          return;                          // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit
new file mode 100644
index 00000000000..eae2ce6671c
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit
@@ -0,0 +1,85 @@
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+      let _1: u8;                          // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+      let mut _2: [u8; 5000];              // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+      let _3: usize;                       // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+      let mut _4: usize;                   // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      let mut _5: bool;                    // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+          StorageLive(_2);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+          _2 = [const 0_u8; 5000];         // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+                                           // ty::Const
+                                           // + ty: u8
+                                           // + val: Value(Scalar(0x00))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:18: 6:22
+                                           // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+          StorageLive(_3);                 // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+          _3 = const 2_usize;              // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x0000000000000002))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:30: 6:31
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+          _4 = const 5000_usize;           // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x0000000000001388))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:17: 6:32
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
+-         _5 = Lt(_3, _4);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+-         assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++         _5 = const true;                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++         assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x0000000000001388))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x0000000000000002))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+      }
+  
+      bb1: {
+          _1 = _2[_3];                     // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+          StorageDead(_3);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          StorageDead(_2);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          _0 = const ();                   // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:4:11: 7:2
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          StorageDead(_1);                 // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+          return;                          // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/large_array_index.rs b/src/test/mir-opt/const_prop/large_array_index.rs
new file mode 100644
index 00000000000..48d134376db
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.rs
@@ -0,0 +1,7 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR large_array_index.main.ConstProp.diff
+fn main() {
+    // check that we don't propagate this, because it's too large
+    let x: u8 = [0_u8; 5000][2];
+}
diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
index e78bc31b774..6e2ee0957ab 100644
--- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
@@ -23,15 +23,13 @@
                                            // + ty: i32
                                            // + val: Value(Scalar(0x0000002a))
                                            // mir::Constant
--                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
-+                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+                                           // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
                                            // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
                                            // ty::Const
                                            // + ty: i32
                                            // + val: Value(Scalar(0x0000002b))
                                            // mir::Constant
--                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
-+                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+                                           // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
                                            // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) }
           StorageLive(_2);                 // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10
           _2 = &mut _1;                    // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19
diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs
index 30c9a5d6b8f..57298605b18 100644
--- a/src/test/mir-opt/inline/inline-into-box-place.rs
+++ b/src/test/mir-opt/inline/inline-into-box-place.rs
@@ -1,8 +1,8 @@
+// ignore-endian-big
 // ignore-wasm32-bare compiled with panic=abort by default
 // compile-flags: -Z mir-opt-level=3
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 #![feature(box_syntax)]
-
 // EMIT_MIR inline_into_box_place.main.Inline.diff
 fn main() {
     let _x: Box<Vec<u32>> = box Vec::new();
diff --git a/src/test/rustdoc-ui/auxiliary/extern_macros.rs b/src/test/rustdoc-ui/auxiliary/extern_macros.rs
new file mode 100644
index 00000000000..ee1fec4c5c2
--- /dev/null
+++ b/src/test/rustdoc-ui/auxiliary/extern_macros.rs
@@ -0,0 +1,7 @@
+#[macro_export]
+macro_rules! attrs_on_struct {
+    ( $( #[$attr:meta] )* ) => {
+        $( #[$attr] )*
+        pub struct ExpandedStruct;
+    }
+}
diff --git a/src/test/rustdoc-ui/doctest-output.rs b/src/test/rustdoc-ui/doctest-output.rs
index f812263c252..e0e1e061ac7 100644
--- a/src/test/rustdoc-ui/doctest-output.rs
+++ b/src/test/rustdoc-ui/doctest-output.rs
@@ -1,3 +1,5 @@
+// edition:2018
+// aux-build:extern_macros.rs
 // compile-flags:--test --test-args=--test-threads=1
 // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
 // check-pass
@@ -6,6 +8,10 @@
 //! assert_eq!(1 + 1, 2);
 //! ```
 
+extern crate extern_macros as macros;
+
+use macros::attrs_on_struct;
+
 pub mod foo {
 
     /// ```
@@ -13,3 +19,9 @@ pub mod foo {
     /// ```
     pub fn bar() {}
 }
+
+attrs_on_struct! {
+    /// ```
+    /// assert!(true);
+    /// ```
+}
diff --git a/src/test/rustdoc-ui/doctest-output.stdout b/src/test/rustdoc-ui/doctest-output.stdout
index 9a55bf50196..c72bd91d1dd 100644
--- a/src/test/rustdoc-ui/doctest-output.stdout
+++ b/src/test/rustdoc-ui/doctest-output.stdout
@@ -1,7 +1,8 @@
 
-running 2 tests
-test $DIR/doctest-output.rs - (line 5) ... ok
-test $DIR/doctest-output.rs - foo::bar (line 11) ... ok
+running 3 tests
+test $DIR/doctest-output.rs - (line 7) ... ok
+test $DIR/doctest-output.rs - ExpandedStruct (line 23) ... ok
+test $DIR/doctest-output.rs - foo::bar (line 17) ... ok
 
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
 
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
new file mode 100644
index 00000000000..1a7a2fce7a3
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
@@ -0,0 +1,68 @@
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+pub enum S {}
+
+macro_rules! m {
+    () => {};
+}
+
+static s: usize = 0;
+const c: usize = 0;
+
+trait T {}
+
+/// Link to [struct@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [mod@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [union@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [trait@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [struct@T]
+//~^ ERROR incompatible link kind for `T`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [derive@m]
+//~^ ERROR incompatible link kind for `m`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@s]
+//~^ ERROR incompatible link kind for `s`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [static@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [fn@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [c()]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@f]
+//~^ ERROR incompatible link kind for `f`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+pub fn f() {}
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
new file mode 100644
index 00000000000..9edf838f9d8
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
@@ -0,0 +1,95 @@
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:14:14
+   |
+LL | /// Link to [struct@S]
+   |              ^^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+note: the lint level is defined here
+  --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   = note: this link resolved to an enum, which is not a struct
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:19:14
+   |
+LL | /// Link to [mod@S]
+   |              ^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a module
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
+   |
+LL | /// Link to [union@S]
+   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a union
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:29:14
+   |
+LL | /// Link to [trait@S]
+   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a trait
+
+error: incompatible link kind for `T`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:34:14
+   |
+LL | /// Link to [struct@T]
+   |              ^^^^^^^^ help: to link to the trait, use its disambiguator: `trait@T`
+   |
+   = note: this link resolved to a trait, which is not a struct
+
+error: incompatible link kind for `m`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:39:14
+   |
+LL | /// Link to [derive@m]
+   |              ^^^^^^^^ help: to link to the macro, use its disambiguator: `m!`
+   |
+   = note: this link resolved to a macro, which is not a derive macro
+
+error: incompatible link kind for `s`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
+   |
+LL | /// Link to [const@s]
+   |              ^^^^^^^ help: to link to the static, use its disambiguator: `static@s`
+   |
+   = note: this link resolved to a static, which is not a constant
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:49:14
+   |
+LL | /// Link to [static@c]
+   |              ^^^^^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a static
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:54:14
+   |
+LL | /// Link to [fn@c]
+   |              ^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:59:14
+   |
+LL | /// Link to [c()]
+   |              ^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `f`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:64:14
+   |
+LL | /// Link to [const@f]
+   |              ^^^^^^^ help: to link to the function, use its disambiguator: `f()`
+   |
+   = note: this link resolved to a function, which is not a constant
+
+error: aborting due to 11 previous errors
+
diff --git a/src/test/rustdoc/intra-link-trait-item.rs b/src/test/rustdoc/intra-link-trait-item.rs
new file mode 100644
index 00000000000..54270414c9d
--- /dev/null
+++ b/src/test/rustdoc/intra-link-trait-item.rs
@@ -0,0 +1,12 @@
+// ignore-tidy-linelength
+#![deny(broken_intra_doc_links)]
+
+/// Link to [S::assoc_fn()]
+/// Link to [Default::default()]
+// @has intra_link_trait_item/struct.S.html '//*[@href="../intra_link_trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
+// @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()'
+pub struct S;
+
+impl S {
+    pub fn assoc_fn() {}
+}
diff --git a/src/test/ui/asm/bad-arch.rs b/src/test/ui/asm/bad-arch.rs
new file mode 100644
index 00000000000..eeeeb17dd4f
--- /dev/null
+++ b/src/test/ui/asm/bad-arch.rs
@@ -0,0 +1,18 @@
+// compile-flags: --target wasm32-unknown-unknown
+
+#![feature(no_core, lang_items, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+    () => {};
+}
+#[lang = "sized"]
+trait Sized {}
+
+fn main() {
+    unsafe {
+        asm!("");
+        //~^ ERROR asm! is unsupported on this target
+    }
+}
diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr
new file mode 100644
index 00000000000..daccc46c6e3
--- /dev/null
+++ b/src/test/ui/asm/bad-arch.stderr
@@ -0,0 +1,8 @@
+error[E0472]: asm! is unsupported on this target
+  --> $DIR/bad-arch.rs:15:9
+   |
+LL |         asm!("");
+   |         ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs
new file mode 100644
index 00000000000..fda825bc65e
--- /dev/null
+++ b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs
@@ -0,0 +1,23 @@
+// check-pass
+
+use std::collections::{BTreeMap, HashMap};
+
+trait Map
+where
+    for<'a> &'a Self: IntoIterator<Item = (&'a Self::Key, &'a Self::Value)>,
+{
+    type Key;
+    type Value;
+}
+
+impl<K, V> Map for HashMap<K, V> {
+    type Key = K;
+    type Value = V;
+}
+
+impl<K, V> Map for BTreeMap<K, V> {
+  type Key = K;
+  type Value = V;
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs
index 753e924f004..59f04372fff 100644
--- a/src/test/ui/feature-gates/feature-gate-asm.rs
+++ b/src/test/ui/feature-gates/feature-gate-asm.rs
@@ -1,4 +1,4 @@
-// ignore-emscripten
+// only-x86_64
 
 fn main() {
     unsafe {
diff --git a/src/test/ui/feature-gates/feature-gate-asm2.rs b/src/test/ui/feature-gates/feature-gate-asm2.rs
index e9349acb643..aa63aff1c5e 100644
--- a/src/test/ui/feature-gates/feature-gate-asm2.rs
+++ b/src/test/ui/feature-gates/feature-gate-asm2.rs
@@ -1,4 +1,4 @@
-// ignore-emscripten
+// only-x86_64
 
 fn main() {
     unsafe {
diff --git a/src/test/ui/panics/issue-47429-short-backtraces.rs b/src/test/ui/panics/issue-47429-short-backtraces.rs
new file mode 100644
index 00000000000..1e8c38cc342
--- /dev/null
+++ b/src/test/ui/panics/issue-47429-short-backtraces.rs
@@ -0,0 +1,18 @@
+// Regression test for #47429: short backtraces were not terminating correctly
+
+// compile-flags: -O
+// run-fail
+// check-run-results
+// exec-env:RUST_BACKTRACE=1
+
+// ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
+// ignore-android FIXME #17520
+// ignore-cloudabi spawning processes is not supported
+// ignore-openbsd no support for libbacktrace without filename
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+// ignore-sgx no subprocess support
+
+fn main() {
+    panic!()
+}
diff --git a/src/test/ui/panics/issue-47429-short-backtraces.run.stderr b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr
new file mode 100644
index 00000000000..99ee26fe55e
--- /dev/null
+++ b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr
@@ -0,0 +1,5 @@
+thread 'main' panicked at 'explicit panic', $DIR/issue-47429-short-backtraces.rs:17:5
+stack backtrace:
+   0: std::panicking::begin_panic
+   1: issue_47429_short_backtraces::main
+note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fn.rs b/src/test/ui/polymorphization/closure_in_upvar/fn.rs
new file mode 100644
index 00000000000..b0b39dbd3df
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fn.rs
@@ -0,0 +1,29 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    let x = |_: ()| ();
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs
new file mode 100644
index 00000000000..ba75f6c5a10
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs
@@ -0,0 +1,34 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    // Mutate an upvar from `x` so that it implements `FnMut`.
+    let mut outer = 3;
+    let mut x = |_: ()| {
+        outer = 4;
+        ()
+    };
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let mut y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs
new file mode 100644
index 00000000000..e9761ad0bcb
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs
@@ -0,0 +1,34 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    // Move a non-copy type into `x` so that it implements `FnOnce`.
+    let outer = Vec::<u32>::new();
+    let x = move |_: ()| {
+        let inner = outer;
+        ()
+    };
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/other.rs b/src/test/ui/polymorphization/closure_in_upvar/other.rs
new file mode 100644
index 00000000000..7614aa83fcd
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/other.rs
@@ -0,0 +1,38 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn y_uses_f(f: impl Fn()) {
+    let x = |_: ()| ();
+
+    let y = || {
+        f();
+        x(());
+    };
+
+    f();
+    y();
+}
+
+fn x_uses_f(f: impl Fn()) {
+    let x = |_: ()| { f(); };
+
+    let y = || x(());
+
+    f();
+    y();
+}
+
+fn entry_a() {
+    x_uses_f(|| ());
+    y_uses_f(|| ());
+}
+
+fn entry_b() {
+    x_uses_f(|| ());
+    y_uses_f(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs
new file mode 100644
index 00000000000..4601a3d4741
--- /dev/null
+++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs
@@ -0,0 +1,10 @@
+// Regression test for issue #75062
+// Tests that we don't ICE on a privacy error for a fieldless tuple struct.
+
+mod foo {
+    struct Bar();
+}
+
+fn main() {
+    foo::Bar(); //~ ERROR tuple struct
+}
diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr
new file mode 100644
index 00000000000..14a12003e2d
--- /dev/null
+++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr
@@ -0,0 +1,15 @@
+error[E0603]: tuple struct `Bar` is private
+  --> $DIR/issue-75062-fieldless-tuple-struct.rs:9:10
+   |
+LL |     foo::Bar();
+   |          ^^^ private tuple struct
+   |
+note: the tuple struct `Bar` is defined here
+  --> $DIR/issue-75062-fieldless-tuple-struct.rs:5:5
+   |
+LL |     struct Bar();
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/src/test/ui/proc-macro/doc-comment-preserved.rs b/src/test/ui/proc-macro/doc-comment-preserved.rs
new file mode 100644
index 00000000000..c2724ae1806
--- /dev/null
+++ b/src/test/ui/proc-macro/doc-comment-preserved.rs
@@ -0,0 +1,24 @@
+// check-pass
+// aux-build:test-macros.rs
+
+// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
+// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
+// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
+
+#[macro_use]
+extern crate test_macros;
+
+print_bang! {
+
+/**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+pub struct S;
+
+}
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/doc-comment-preserved.stdout b/src/test/ui/proc-macro/doc-comment-preserved.stdout
new file mode 100644
index 00000000000..f7904536a76
--- /dev/null
+++ b/src/test/ui/proc-macro/doc-comment-preserved.stdout
@@ -0,0 +1,54 @@
+PRINT-BANG INPUT (DISPLAY): /**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+ pub struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): #[doc = "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n"] pub struct S ;
+PRINT-BANG INPUT (DEBUG): TokenStream [
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: #0 bytes(LO..HI),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "doc",
+                span: #0 bytes(LO..HI),
+            },
+            Punct {
+                ch: '=',
+                spacing: Alone,
+                span: #0 bytes(LO..HI),
+            },
+            Literal {
+                kind: Str,
+                symbol: "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n",
+                suffix: None,
+                span: #0 bytes(LO..HI),
+            },
+        ],
+        span: #0 bytes(LO..HI),
+    },
+    Ident {
+        ident: "pub",
+        span: #0 bytes(LO..HI),
+    },
+    Ident {
+        ident: "struct",
+        span: #0 bytes(LO..HI),
+    },
+    Ident {
+        ident: "S",
+        span: #0 bytes(LO..HI),
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #0 bytes(LO..HI),
+    },
+]
diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
index be55890c08c..7f197a238e5 100644
--- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
+++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
@@ -1,7 +1,7 @@
-error[E0391]: cycle detected when computing layout of `std::option::Option<S>`
+error[E0391]: cycle detected when computing layout of `S`
    |
-   = note: ...which requires computing layout of `S`...
-   = note: ...which again requires computing layout of `std::option::Option<S>`, completing the cycle
+   = note: ...which requires computing layout of `std::option::Option<S>`...
+   = note: ...which again requires computing layout of `S`, completing the cycle
 note: cycle used when optimizing MIR for `main`
   --> $DIR/issue-26548-recursion-via-normalize.rs:15:1
    |
diff --git a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
index a323bd9e82b..8c436841b44 100644
--- a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
+++ b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
@@ -2,6 +2,7 @@
 #![allow(non_camel_case_types)]
 
 // ignore-emscripten
+// ignore-endian-big behavior of simd_bitmask is endian-specific
 
 // Test that the simd_bitmask intrinsic produces correct results.
 
diff --git a/src/test/ui/simd/simd-intrinsic-generic-select.rs b/src/test/ui/simd/simd-intrinsic-generic-select.rs
index 22bda4fc9d9..dc9ec5d2760 100644
--- a/src/test/ui/simd/simd-intrinsic-generic-select.rs
+++ b/src/test/ui/simd/simd-intrinsic-generic-select.rs
@@ -2,10 +2,7 @@
 #![allow(non_camel_case_types)]
 
 // ignore-emscripten
-// ignore-mips       behavior of simd_select_bitmask is endian-specific
-// ignore-mips64     behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc    behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc64  behavior of simd_select_bitmask is endian-specific
+// ignore-endian-big behavior of simd_select_bitmask is endian-specific
 
 // Test that the simd_select intrinsics produces correct results.
 
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index e87c33d1b09..6ce36fd2360 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -2,6 +2,7 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item,
 use if_chain::if_chain;
 use itertools::Itertools;
 use rustc_ast::ast::{AttrKind, Attribute};
+use rustc_ast::token::CommentKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -249,7 +250,7 @@ fn lint_for_missing_headers<'tcx>(
     }
 }
 
-/// Cleanup documentation decoration (`///` and such).
+/// Cleanup documentation decoration.
 ///
 /// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 /// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
@@ -257,54 +258,45 @@ fn lint_for_missing_headers<'tcx>(
 /// the spans but this function is inspired from the later.
 #[allow(clippy::cast_possible_truncation)]
 #[must_use]
-pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
     // one-line comments lose their prefix
-    const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
-    for prefix in ONELINERS {
-        if comment.starts_with(*prefix) {
-            let doc = &comment[prefix.len()..];
-            let mut doc = doc.to_owned();
-            doc.push('\n');
-            return (
-                doc.to_owned(),
-                vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))],
-            );
-        }
+    if comment_kind == CommentKind::Line {
+        let mut doc = doc.to_owned();
+        doc.push('\n');
+        let len = doc.len();
+        // +3 skips the opening delimiter
+        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
     }
 
-    if comment.starts_with("/*") {
-        let doc = &comment[3..comment.len() - 2];
-        let mut sizes = vec![];
-        let mut contains_initial_stars = false;
-        for line in doc.lines() {
-            let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
-            debug_assert_eq!(offset as u32 as usize, offset);
-            contains_initial_stars |= line.trim_start().starts_with('*');
-            // +1 for the newline
-            sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32))));
-        }
-        if !contains_initial_stars {
-            return (doc.to_string(), sizes);
-        }
-        // remove the initial '*'s if any
-        let mut no_stars = String::with_capacity(doc.len());
-        for line in doc.lines() {
-            let mut chars = line.chars();
-            while let Some(c) = chars.next() {
-                if c.is_whitespace() {
-                    no_stars.push(c);
-                } else {
-                    no_stars.push(if c == '*' { ' ' } else { c });
-                    break;
-                }
+    let mut sizes = vec![];
+    let mut contains_initial_stars = false;
+    for line in doc.lines() {
+        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+        debug_assert_eq!(offset as u32 as usize, offset);
+        contains_initial_stars |= line.trim_start().starts_with('*');
+        // +1 adds the newline, +3 skips the opening delimiter
+        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+    }
+    if !contains_initial_stars {
+        return (doc.to_string(), sizes);
+    }
+    // remove the initial '*'s if any
+    let mut no_stars = String::with_capacity(doc.len());
+    for line in doc.lines() {
+        let mut chars = line.chars();
+        while let Some(c) = chars.next() {
+            if c.is_whitespace() {
+                no_stars.push(c);
+            } else {
+                no_stars.push(if c == '*' { ' ' } else { c });
+                break;
             }
-            no_stars.push_str(chars.as_str());
-            no_stars.push('\n');
         }
-        return (no_stars, sizes);
+        no_stars.push_str(chars.as_str());
+        no_stars.push('\n');
     }
 
-    panic!("not a doc-comment: {}", comment);
+    (no_stars, sizes)
 }
 
 #[derive(Copy, Clone)]
@@ -318,9 +310,8 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
     let mut spans = vec![];
 
     for attr in attrs {
-        if let AttrKind::DocComment(ref comment) = attr.kind {
-            let comment = comment.to_string();
-            let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span);
+        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+            let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
             spans.extend_from_slice(&current_spans);
             doc.push_str(&comment);
         } else if attr.has_name(sym!(doc)) {
diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
index 7b673e15b76..74ccd9235de 100644
--- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
@@ -60,13 +60,14 @@ declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
 
 impl TabsInDocComments {
     fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if let ast::AttrKind::DocComment(comment) = attr.kind {
+        if let ast::AttrKind::DocComment(_, comment) = attr.kind {
             let comment = comment.as_str();
 
             for (lo, hi) in get_chunks_of_tabs(&comment) {
+                // +3 skips the opening delimiter
                 let new_span = Span::new(
-                    attr.span.lo() + BytePos(lo),
-                    attr.span.lo() + BytePos(hi),
+                    attr.span.lo() + BytePos(3 + lo),
+                    attr.span.lo() + BytePos(3 + hi),
                     attr.span.ctxt(),
                 );
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
index 58c1103da9f..ad02bc5fd8e 100755
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -506,7 +506,7 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
     use AttrKind::*;
     l.style == r.style
         && match (&l.kind, &r.kind) {
-            (DocComment(l), DocComment(r)) => l == r,
+            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
             (Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
             _ => false,
         }
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index ac5f0d0a39e..2b3f9be2dfb 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -1,23 +1,3 @@
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:11:5
-   |
-LL |     x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^ index out of bounds: the len is 4 but the index is 4
-   |
-   = note: `#[deny(unconditional_panic)]` on by default
-
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:12:5
-   |
-LL |     x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8
-
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:27:5
-   |
-LL |     x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^ index out of bounds: the len is 4 but the index is 15
-
 error: indexing may panic.
   --> $DIR/indexing_slicing_index.rs:10:5
    |
@@ -75,5 +55,5 @@ LL |     v[M];
    |
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
-error: aborting due to 10 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 9269a63b41a..848bd3a43e8 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -169,7 +169,7 @@ impl fmt::Display for Debugger {
 }
 
 /// Configuration for compiletest
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct Config {
     /// `true` to to overwrite stderr/stdout files instead of complaining about changes in output.
     pub bless: bool,
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 047fbe9da14..edbb8372633 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -827,6 +827,7 @@ impl Config {
             name == util::get_pointer_width(&self.target) ||    // pointer width
             name == self.stage_id.split('-').next().unwrap() || // stage
             (self.target != self.host && name == "cross-compile") ||
+            (name == "endian-big" && util::is_big_endian(&self.target)) ||
             (self.remote_test_client.is_some() && name == "remote") ||
             match self.compare_mode {
                 Some(CompareMode::Nll) => name == "compare-mode-nll",
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 940e16720f6..7f49cb913b1 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1885,7 +1885,8 @@ impl<'test> TestCx<'test> {
         emit_metadata: EmitMetadata,
         allow_unused: AllowUnused,
     ) -> Command {
-        let is_rustdoc = self.is_rustdoc();
+        let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
+        let is_rustdoc = self.is_rustdoc() && !is_aux;
         let mut rustc = if !is_rustdoc {
             Command::new(&self.config.rustc_path)
         } else {
@@ -3246,8 +3247,19 @@ impl<'test> TestCx<'test> {
     }
 
     fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
-        let before = self.get_mir_dump_dir().join(before);
-        let after = self.get_mir_dump_dir().join(after);
+        let to_full_path = |path: PathBuf| {
+            let full = self.get_mir_dump_dir().join(&path);
+            if !full.exists() {
+                panic!(
+                    "the mir dump file for {} does not exist (requested in {})",
+                    path.display(),
+                    self.testpaths.file.display(),
+                );
+            }
+            full
+        };
+        let before = to_full_path(before);
+        let after = to_full_path(after);
         debug!("comparing the contents of: {} with {}", before.display(), after.display());
         let before = fs::read_to_string(before).unwrap();
         let after = fs::read_to_string(after).unwrap();
@@ -3573,6 +3585,7 @@ impl ProcRes {
     }
 }
 
+#[derive(Debug)]
 enum TargetLocation {
     ThisFile(PathBuf),
     ThisDirectory(PathBuf),
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 0437ff8c944..ddd7941b114 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -99,6 +99,20 @@ pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] =
 pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] =
     &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
 
+const BIG_ENDIAN: &'static [&'static str] = &[
+    "armebv7r",
+    "mips",
+    "mips64",
+    "mipsisa32r6",
+    "mipsisa64r6",
+    "powerpc",
+    "powerpc64",
+    "s390x",
+    "sparc",
+    "sparc64",
+    "sparcv9",
+];
+
 pub fn matches_os(triple: &str, name: &str) -> bool {
     // For the wasm32 bare target we ignore anything also ignored on emscripten
     // and then we also recognize `wasm32-bare` as the os for the target
@@ -125,6 +139,12 @@ pub fn get_arch(triple: &str) -> &'static str {
     panic!("Cannot determine Architecture from triple");
 }
 
+/// Determine the endianness from `triple`
+pub fn is_big_endian(triple: &str) -> bool {
+    let triple_arch = triple.split('-').next().unwrap();
+    BIG_ENDIAN.contains(&triple_arch)
+}
+
 pub fn matches_env(triple: &str, name: &str) -> bool {
     if let Some(env) = triple.split('-').nth(3) { env.starts_with(name) } else { false }
 }
diff --git a/src/tools/miri b/src/tools/miri
-Subproject 55bdb3174653039f47362742f8dc941bfc086e8
+Subproject cf633d0e897c065381b7b7d14984830176caf8b
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index c0631fcedd3..55e2d7cf827 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -42,7 +42,7 @@ MAINTAINERS = {
 LABELS = {
     'miri': ['A-miri', 'C-bug'],
     'rls': ['A-rls', 'C-bug'],
-    'rustfmt': ['C-bug'],
+    'rustfmt': ['A-rustfmt', 'C-bug'],
     'book': ['C-bug'],
     'nomicon': ['C-bug'],
     'reference': ['C-bug'],
diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs
index eabbcee9d73..d2d1807b3bb 100644
--- a/src/tools/unicode-table-generator/src/main.rs
+++ b/src/tools/unicode-table-generator/src/main.rs
@@ -15,7 +15,7 @@
 //! We have two separate encoding schemes: a skiplist-like approach, and a
 //! compressed bitset. The datasets we consider mostly use the skiplist (it's
 //! smaller) but the lowercase and uppercase sets are sufficiently sparse for
-//! the bitset to be worthwhile -- for those sets the biset is a 2x size win.
+//! the bitset to be worthwhile -- for those sets the bitset is a 2x size win.
 //! Since the bitset is also faster, this seems an obvious choice. (As a
 //! historical note, the bitset was also the prior implementation, so its
 //! relative complexity had already been paid).