about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs47
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs12
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs28
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs5
-rw-r--r--library/core/src/char/methods.rs20
-rw-r--r--library/core/src/num/mod.rs20
-rw-r--r--library/core/src/primitive_docs.rs8
-rw-r--r--library/std/src/primitive_docs.rs8
-rw-r--r--src/doc/rustdoc/src/unstable-features.md1
-rw-r--r--src/librustdoc/doctest.rs17
-rw-r--r--src/librustdoc/externalfiles.rs6
-rw-r--r--src/librustdoc/html/render/print_item.rs24
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css29
-rw-r--r--src/test/rustdoc-gui/headings.goml40
-rw-r--r--src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs18
-rw-r--r--src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout24
-rw-r--r--src/test/rustdoc/enum-headings.rs40
-rw-r--r--src/test/rustdoc/tuple-struct-fields-doc.rs4
-rw-r--r--src/test/ui/consts/const-eval/simd/insert_extract.rs22
-rw-r--r--src/test/ui/lang-items/issue-87573.rs28
-rw-r--r--src/test/ui/lang-items/issue-87573.stderr21
21 files changed, 293 insertions, 129 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 5e7bbc01132..9bbb42eb019 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -419,48 +419,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             sym::simd_insert => {
                 let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
                 let elem = &args[2];
-                let input = &args[0];
-                let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx);
+                let (input, input_len) = self.operand_to_simd(&args[0])?;
+                let (dest, dest_len) = self.place_to_simd(dest)?;
+                assert_eq!(input_len, dest_len, "Return vector length must match input length");
                 assert!(
-                    index < len,
-                    "Index `{}` must be in bounds of vector type `{}`: `[0, {})`",
+                    index < dest_len,
+                    "Index `{}` must be in bounds of vector with length {}`",
                     index,
-                    e_ty,
-                    len
-                );
-                assert_eq!(
-                    input.layout, dest.layout,
-                    "Return type `{}` must match vector type `{}`",
-                    dest.layout.ty, input.layout.ty
-                );
-                assert_eq!(
-                    elem.layout.ty, e_ty,
-                    "Scalar element type `{}` must match vector element type `{}`",
-                    elem.layout.ty, e_ty
+                    dest_len
                 );
 
-                for i in 0..len {
-                    let place = self.place_index(dest, i)?;
-                    let value = if i == index { *elem } else { self.operand_index(input, i)? };
-                    self.copy_op(&value, &place)?;
+                for i in 0..dest_len {
+                    let place = self.mplace_index(&dest, i)?;
+                    let value =
+                        if i == index { *elem } else { self.mplace_index(&input, i)?.into() };
+                    self.copy_op(&value, &place.into())?;
                 }
             }
             sym::simd_extract => {
                 let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
-                let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx);
+                let (input, input_len) = self.operand_to_simd(&args[0])?;
                 assert!(
-                    index < len,
-                    "index `{}` is out-of-bounds of vector type `{}` with length `{}`",
+                    index < input_len,
+                    "index `{}` must be in bounds of vector with length `{}`",
                     index,
-                    e_ty,
-                    len
-                );
-                assert_eq!(
-                    e_ty, dest.layout.ty,
-                    "Return type `{}` must match vector element type `{}`",
-                    dest.layout.ty, e_ty
+                    input_len
                 );
-                self.copy_op(&self.operand_index(&args[0], index)?, dest)?;
+                self.copy_op(&self.mplace_index(&input, index)?.into(), dest)?;
             }
             sym::likely | sym::unlikely | sym::black_box => {
                 // These just return their argument
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index b6682b13ed2..de9e94ce2ac 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -437,6 +437,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         })
     }
 
+    /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
+    /// Also returns the number of elements.
+    pub fn operand_to_simd(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
+        // Basically we just transmute this place into an array following simd_size_and_type.
+        // This only works in memory, but repr(simd) types should never be immediates anyway.
+        assert!(base.layout.ty.is_simd());
+        self.mplace_to_simd(&base.assert_mem_place())
+    }
+
     /// Read from a local. Will not actually access the local if reading from a ZST.
     /// Will not access memory, instead an indirect `Operand` is returned.
     ///
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index d425b84bdaf..d7f2853fc86 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -200,7 +200,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
             }
         } else {
             // Go through the layout.  There are lots of types that support a length,
-            // e.g., SIMD types.
+            // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
             match self.layout.fields {
                 FieldsShape::Array { count, .. } => Ok(count),
                 _ => bug!("len not supported on sized type {:?}", self.layout.ty),
@@ -533,6 +533,22 @@ where
         })
     }
 
+    /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
+    /// Also returns the number of elements.
+    pub fn mplace_to_simd(
+        &self,
+        base: &MPlaceTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
+        // Basically we just transmute this place into an array following simd_size_and_type.
+        // (Transmuting is okay since this is an in-memory place. We also double-check the size
+        // stays the same.)
+        let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx);
+        let array = self.tcx.mk_array(e_ty, len);
+        let layout = self.layout_of(array)?;
+        assert_eq!(layout.size, base.layout.size);
+        Ok((MPlaceTy { layout, ..*base }, len))
+    }
+
     /// Gets the place of a field inside the place, and also the field's type.
     /// Just a convenience function, but used quite a bit.
     /// This is the only projection that might have a side-effect: We cannot project
@@ -594,6 +610,16 @@ where
         })
     }
 
+    /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
+    /// Also returns the number of elements.
+    pub fn place_to_simd(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
+        let mplace = self.force_allocation(base)?;
+        self.mplace_to_simd(&mplace)
+    }
+
     /// Computes a place. You should only use this if you intend to write into this
     /// place; for reading, a more efficient alternative is `eval_place_for_read`.
     pub fn eval_place(
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 610f9bd8f82..c79e25f4781 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1805,10 +1805,13 @@ impl<'tcx> TyS<'tcx> {
     pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
         match self.kind() {
             Adt(def, substs) => {
+                assert!(def.repr.simd(), "`simd_size_and_type` called on non-SIMD type");
                 let variant = def.non_enum_variant();
                 let f0_ty = variant.fields[0].ty(tcx, substs);
 
                 match f0_ty.kind() {
+                    // If the first field is an array, we assume it is the only field and its
+                    // elements are the SIMD components.
                     Array(f0_elem_ty, f0_len) => {
                         // FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112
                         // The way we evaluate the `N` in `[T; N]` here only works since we use
@@ -1816,6 +1819,8 @@ impl<'tcx> TyS<'tcx> {
                         // if we use it in generic code. See the `simd-array-trait` ui test.
                         (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty)
                     }
+                    // Otherwise, the fields of this Adt are the SIMD components (and we assume they
+                    // all have the same type).
                     _ => (variant.fields.len() as u64, f0_ty),
                 }
             }
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index d5ad0c385c7..7250dca2adf 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -1250,7 +1250,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_alphabetic());
     /// assert!(uppercase_g.is_ascii_alphabetic());
@@ -1284,7 +1284,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_uppercase());
     /// assert!(uppercase_g.is_ascii_uppercase());
@@ -1318,7 +1318,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_lowercase());
     /// assert!(!uppercase_g.is_ascii_lowercase());
@@ -1355,7 +1355,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_alphanumeric());
     /// assert!(uppercase_g.is_ascii_alphanumeric());
@@ -1389,7 +1389,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_digit());
     /// assert!(!uppercase_g.is_ascii_digit());
@@ -1426,7 +1426,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_hexdigit());
     /// assert!(!uppercase_g.is_ascii_hexdigit());
@@ -1464,7 +1464,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_punctuation());
     /// assert!(!uppercase_g.is_ascii_punctuation());
@@ -1498,7 +1498,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_graphic());
     /// assert!(uppercase_g.is_ascii_graphic());
@@ -1549,7 +1549,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_whitespace());
     /// assert!(!uppercase_g.is_ascii_whitespace());
@@ -1585,7 +1585,7 @@ impl char {
     /// let percent = '%';
     /// let space = ' ';
     /// let lf = '\n';
-    /// let esc: char = 0x1b_u8.into();
+    /// let esc = '\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_control());
     /// assert!(!uppercase_g.is_ascii_control());
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index a8f2ded4659..e3eab07b9df 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -428,7 +428,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_alphabetic());
     /// assert!(uppercase_g.is_ascii_alphabetic());
@@ -462,7 +462,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_uppercase());
     /// assert!(uppercase_g.is_ascii_uppercase());
@@ -496,7 +496,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_lowercase());
     /// assert!(!uppercase_g.is_ascii_lowercase());
@@ -533,7 +533,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_alphanumeric());
     /// assert!(uppercase_g.is_ascii_alphanumeric());
@@ -567,7 +567,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_digit());
     /// assert!(!uppercase_g.is_ascii_digit());
@@ -604,7 +604,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_hexdigit());
     /// assert!(!uppercase_g.is_ascii_hexdigit());
@@ -642,7 +642,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_punctuation());
     /// assert!(!uppercase_g.is_ascii_punctuation());
@@ -676,7 +676,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(uppercase_a.is_ascii_graphic());
     /// assert!(uppercase_g.is_ascii_graphic());
@@ -727,7 +727,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_whitespace());
     /// assert!(!uppercase_g.is_ascii_whitespace());
@@ -763,7 +763,7 @@ impl u8 {
     /// let percent = b'%';
     /// let space = b' ';
     /// let lf = b'\n';
-    /// let esc = 0x1b_u8;
+    /// let esc = b'\x1b';
     ///
     /// assert!(!uppercase_a.is_ascii_control());
     /// assert!(!uppercase_g.is_ascii_control());
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 0de9126dab2..f47a30c9b5d 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1104,11 +1104,10 @@ mod prim_usize {}
 /// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!)
 /// * [`Deref`]
 /// * [`Borrow`]
-/// * [`Pointer`]
+/// * [`fmt::Pointer`]
 ///
 /// [`Deref`]: ops::Deref
 /// [`Borrow`]: borrow::Borrow
-/// [`Pointer`]: fmt::Pointer
 ///
 /// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
 /// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
@@ -1124,7 +1123,7 @@ mod prim_usize {}
 /// The following traits are implemented on `&T` references if the underlying `T` also implements
 /// that trait:
 ///
-/// * All the traits in [`std::fmt`] except [`Pointer`] and [`fmt::Write`]
+/// * All the traits in [`std::fmt`] except [`fmt::Pointer`] (which is implemented regardless of the type of its referent) and [`fmt::Write`]
 /// * [`PartialOrd`]
 /// * [`Ord`]
 /// * [`PartialEq`]
@@ -1133,9 +1132,9 @@ mod prim_usize {}
 /// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`)
 /// * [`Hash`]
 /// * [`ToSocketAddrs`]
+/// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
 ///
 /// [`std::fmt`]: fmt
-/// ['Pointer`]: fmt::Pointer
 /// [`Hash`]: hash::Hash
 #[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
 ///
@@ -1150,7 +1149,6 @@ mod prim_usize {}
 /// * [`ExactSizeIterator`]
 /// * [`FusedIterator`]
 /// * [`TrustedLen`]
-/// * [`Send`] \(note that `&T` references only get `Send` if <code>T: [Sync]</code>)
 /// * [`io::Write`]
 /// * [`Read`]
 /// * [`Seek`]
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 0de9126dab2..f47a30c9b5d 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1104,11 +1104,10 @@ mod prim_usize {}
 /// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!)
 /// * [`Deref`]
 /// * [`Borrow`]
-/// * [`Pointer`]
+/// * [`fmt::Pointer`]
 ///
 /// [`Deref`]: ops::Deref
 /// [`Borrow`]: borrow::Borrow
-/// [`Pointer`]: fmt::Pointer
 ///
 /// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
 /// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
@@ -1124,7 +1123,7 @@ mod prim_usize {}
 /// The following traits are implemented on `&T` references if the underlying `T` also implements
 /// that trait:
 ///
-/// * All the traits in [`std::fmt`] except [`Pointer`] and [`fmt::Write`]
+/// * All the traits in [`std::fmt`] except [`fmt::Pointer`] (which is implemented regardless of the type of its referent) and [`fmt::Write`]
 /// * [`PartialOrd`]
 /// * [`Ord`]
 /// * [`PartialEq`]
@@ -1133,9 +1132,9 @@ mod prim_usize {}
 /// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`)
 /// * [`Hash`]
 /// * [`ToSocketAddrs`]
+/// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
 ///
 /// [`std::fmt`]: fmt
-/// ['Pointer`]: fmt::Pointer
 /// [`Hash`]: hash::Hash
 #[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
 ///
@@ -1150,7 +1149,6 @@ mod prim_usize {}
 /// * [`ExactSizeIterator`]
 /// * [`FusedIterator`]
 /// * [`TrustedLen`]
-/// * [`Send`] \(note that `&T` references only get `Send` if <code>T: [Sync]</code>)
 /// * [`io::Write`]
 /// * [`Read`]
 /// * [`Seek`]
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 16532215c6f..8da1d22a4d1 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -398,6 +398,7 @@ undocumented item:
 ```rust
 /// This item has documentation
 pub fn foo() {}
+
 pub fn no_documentation() {}
 ```
 
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index c10eebf49fc..37db20aaefa 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,4 +1,4 @@
-use rustc_ast as ast;
+use rustc_ast::{self as ast, token};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{ColorConfig, ErrorReported, FatalError};
@@ -537,7 +537,6 @@ crate fn make_test(
             use rustc_errors::emitter::{Emitter, EmitterWriter};
             use rustc_errors::Handler;
             use rustc_parse::maybe_new_parser_from_source_str;
-            use rustc_parse::parser::ForceCollect;
             use rustc_session::parse::ParseSess;
             use rustc_span::source_map::FilePathMapping;
 
@@ -573,9 +572,9 @@ crate fn make_test(
                 }
             };
 
-            loop {
-                match parser.parse_item(ForceCollect::No) {
-                    Ok(Some(item)) => {
+            match parser.parse_mod(&token::Eof) {
+                Ok((_attrs, items, _span)) => {
+                    for item in items {
                         if !found_main {
                             if let ast::ItemKind::Fn(..) = item.kind {
                                 if item.ident.name == sym::main {
@@ -607,11 +606,9 @@ crate fn make_test(
                             break;
                         }
                     }
-                    Ok(None) => break,
-                    Err(mut e) => {
-                        e.cancel();
-                        break;
-                    }
+                }
+                Err(mut e) => {
+                    e.cancel();
                 }
             }
 
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index 302fc5a6777..6ed911b8d24 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -8,13 +8,13 @@ use serde::Serialize;
 
 #[derive(Clone, Debug, Serialize)]
 crate struct ExternalHtml {
-    /// Content that will be included inline in the <head> section of a
+    /// Content that will be included inline in the `<head>` section of a
     /// rendered Markdown file or generated documentation
     crate in_header: String,
-    /// Content that will be included inline between <body> and the content of
+    /// Content that will be included inline between `<body>` and the content of
     /// a rendered Markdown file or generated documentation
     crate before_content: String,
-    /// Content that will be included inline between the content and </body> of
+    /// Content that will be included inline between the content and `</body>` of
     /// a rendered Markdown file or generated documentation
     crate after_content: String,
 }
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 7ef773fe5ff..049d17a4b47 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1080,7 +1080,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                 cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap()));
             write!(
                 w,
-                "<div id=\"{id}\" class=\"variant small-section-header\">\
+                "<h3 id=\"{id}\" class=\"variant small-section-header\">\
                     <a href=\"#{id}\" class=\"anchor field\"></a>\
                     <code>{name}",
                 id = id,
@@ -1093,9 +1093,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
             }
             w.write_str("</code>");
             render_stability_since(w, variant, it, cx.tcx());
-            w.write_str("</div>");
-            document(w, cx, variant, Some(it), HeadingOffset::H3);
-            document_non_exhaustive(w, variant);
+            w.write_str("</h3>");
 
             use crate::clean::Variant;
             if let Some((extra, fields)) = match *variant.kind {
@@ -1109,12 +1107,8 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                     variant.name.as_ref().unwrap()
                 ));
                 write!(w, "<div class=\"sub-variant\" id=\"{id}\">", id = variant_id);
-                write!(
-                    w,
-                    "<h3>{extra}Fields of <b>{name}</b></h3><div>",
-                    extra = extra,
-                    name = variant.name.as_ref().unwrap(),
-                );
+                write!(w, "<h4>{extra}Fields</h4>", extra = extra,);
+                document_non_exhaustive(w, variant);
                 for field in fields {
                     match *field.kind {
                         clean::StrippedItem(box clean::StructFieldItem(_)) => {}
@@ -1126,7 +1120,8 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                             ));
                             write!(
                                 w,
-                                "<span id=\"{id}\" class=\"variant small-section-header\">\
+                                "<div class=\"sub-variant-field\">\
+                                 <span id=\"{id}\" class=\"variant small-section-header\">\
                                     <a href=\"#{id}\" class=\"anchor field\"></a>\
                                     <code>{f}:&nbsp;{t}</code>\
                                 </span>",
@@ -1134,13 +1129,16 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                                 f = field.name.as_ref().unwrap(),
                                 t = ty.print(cx)
                             );
-                            document(w, cx, field, Some(variant), HeadingOffset::H4);
+                            document(w, cx, field, Some(variant), HeadingOffset::H5);
+                            write!(w, "</div>");
                         }
                         _ => unreachable!(),
                     }
                 }
-                w.write_str("</div></div>");
+                w.write_str("</div>");
             }
+
+            document(w, cx, variant, Some(it), HeadingOffset::H4);
         }
     }
     let def_id = it.def_id.expect_def_id();
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 89a763ef6d7..cb1df15bbb1 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1102,25 +1102,28 @@ a.test-arrow:hover{
 	margin-right: 5px;
 }
 
-.sub-variant, .sub-variant > h3 {
-	margin-top: 0px !important;
-	padding-top: 1px;
+h3.variant {
+	font-weight: 600;
+	font-size: 1.1em;
+	margin-bottom: 10px;
+	border-bottom: none;
 }
 
-#main .sub-variant > h3 {
-	font-size: 15px;
-	margin-left: 25px;
-	margin-bottom: 5px;
+.sub-variant h4 {
+	font-size: 1em;
+	font-weight: 400;
+	border-bottom: none;
+	margin-top: 0;
+	margin-bottom: 0;
 }
 
-.sub-variant > div {
-	margin-left: 20px;
-	margin-bottom: 10px;
+.sub-variant {
+	margin-left: 24px;
+	margin-bottom: 40px;
 }
 
-.sub-variant > div > span {
-	display: block;
-	position: relative;
+.sub-variant > .sub-variant-field {
+	margin-left: 24px;
 }
 
 .toggle-label {
diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml
index bdf17ec4570..87c512468e0 100644
--- a/src/test/rustdoc-gui/headings.goml
+++ b/src/test/rustdoc-gui/headings.goml
@@ -1,4 +1,4 @@
-// This test check that headers (a) have the correct heading level, (b) are the right size,
+// This test checks that headers (a) have the correct heading level, (b) are the right size,
 // and (c) have the correct underlining (or absence of underlining).
 // The sizes may change as design changes, but try to make sure a lower header is never bigger than
 // its parent headers. Also make sure lower headers don't have underlines when their parents lack
@@ -67,25 +67,25 @@ assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"})
 assert-css: ("h2#variants", {"font-size": "22.4px"})
 assert-css: ("h2#variants", {"border-bottom-width": "1px"})
 
-assert-css: ("h3#none-prose-title", {"font-size": "20.8px"})
-assert-css: ("h3#none-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h4#none-prose-sub-heading", {"font-size": "16px"})
-assert-css: ("h4#none-prose-sub-heading", {"border-bottom-width": "0px"})
-
-assert-css: ("h3#wrapped-prose-title", {"font-size": "20.8px"})
-assert-css: ("h3#wrapped-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h4#wrapped-prose-sub-heading", {"font-size": "16px"})
-assert-css: ("h4#wrapped-prose-sub-heading", {"border-bottom-width": "0px"})
-
-assert-css: ("h4#wrapped0-prose-title", {"font-size": "16px"})
-assert-css: ("h4#wrapped0-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h5#wrapped0-prose-sub-heading", {"font-size": "16px"})
-assert-css: ("h5#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"})
-
-assert-css: ("h4#structy-prose-title", {"font-size": "16px"})
-assert-css: ("h4#structy-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h5#structy-prose-sub-heading", {"font-size": "16px"})
-assert-css: ("h5#structy-prose-sub-heading", {"border-bottom-width": "0px"})
+assert-css: ("h4#none-prose-title", {"font-size": "16px"})
+assert-css: ("h4#none-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h5#none-prose-sub-heading", {"font-size": "16px"})
+assert-css: ("h5#none-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h4#wrapped-prose-title", {"font-size": "16px"})
+assert-css: ("h4#wrapped-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h5#wrapped-prose-sub-heading", {"font-size": "16px"})
+assert-css: ("h5#wrapped-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#wrapped0-prose-title", {"font-size": "16px"})
+assert-css: ("h5#wrapped0-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h6#wrapped0-prose-sub-heading", {"font-size": "15.2px"})
+assert-css: ("h6#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#structy-prose-title", {"font-size": "16px"})
+assert-css: ("h5#structy-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h6#structy-prose-sub-heading", {"font-size": "15.2px"})
+assert-css: ("h6#structy-prose-sub-heading", {"border-bottom-width": "0px"})
 
 assert-css: ("h2#implementations", {"font-size": "22.4px"})
 assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
diff --git a/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs
new file mode 100644
index 00000000000..16d737106ea
--- /dev/null
+++ b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs
@@ -0,0 +1,18 @@
+// FIXME: if/when the output of the test harness can be tested on its own, this test should be
+// adapted to use that, and that normalize line can go away
+
+// compile-flags:--test
+// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
+// failure-status: 101
+
+/// <https://github.com/rust-lang/rust/issues/91014>
+///
+/// ```rust
+/// struct S {}; // unexpected semicolon after struct def
+///
+/// fn main() {
+///    assert_eq!(0, 1);
+/// }
+/// ```
+mod m {}
diff --git a/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout
new file mode 100644
index 00000000000..61468b6c745
--- /dev/null
+++ b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout
@@ -0,0 +1,24 @@
+
+running 1 test
+test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED
+
+failures:
+
+---- $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) stdout ----
+error: expected item, found `;`
+  --> $DIR/failed-doctest-extra-semicolon-on-item.rs:12:12
+   |
+LL | struct S {}; // unexpected semicolon after struct def
+   |            ^ help: remove this semicolon
+   |
+   = help: braced struct declarations are not followed by a semicolon
+
+error: aborting due to previous error
+
+Couldn't compile the test.
+
+failures:
+    $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11)
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/src/test/rustdoc/enum-headings.rs b/src/test/rustdoc/enum-headings.rs
new file mode 100644
index 00000000000..2e5c34391c4
--- /dev/null
+++ b/src/test/rustdoc/enum-headings.rs
@@ -0,0 +1,40 @@
+#![crate_name = "foo"]
+// @has foo/enum.Token.html
+/// A token!
+/// # First
+/// Some following text...
+// @has - '//h2[@id="first"]' "First"
+pub enum Token {
+    /// A declaration!
+    /// # Variant-First
+    /// Some following text...
+    // @has - '//h4[@id="variant-first"]' "Variant-First"
+    Declaration {
+        /// A version!
+        /// # Variant-Field-First
+        /// Some following text...
+        // @has - '//h5[@id="variant-field-first"]' "Variant-Field-First"
+        version: String,
+    },
+    /// A Zoople!
+    /// # Variant-First
+    Zoople(
+        // @has - '//h5[@id="variant-tuple-field-first"]' "Variant-Tuple-Field-First"
+        /// Zoople's first variant!
+        /// # Variant-Tuple-Field-First
+        /// Some following text...
+        usize,
+    ),
+    /// Unfinished business!
+    /// # Non-Exhaustive-First
+    /// Some following text...
+    // @has - '//h4[@id="non-exhaustive-first"]' "Non-Exhaustive-First"
+    #[non_exhaustive]
+    Unfinished {
+        /// This is x.
+        /// # X-First
+        /// Some following text...
+        // @has - '//h5[@id="x-first"]' "X-First"
+        x: usize,
+    },
+}
diff --git a/src/test/rustdoc/tuple-struct-fields-doc.rs b/src/test/rustdoc/tuple-struct-fields-doc.rs
index f3d8e39ea2d..139c5b4391a 100644
--- a/src/test/rustdoc/tuple-struct-fields-doc.rs
+++ b/src/test/rustdoc/tuple-struct-fields-doc.rs
@@ -20,10 +20,10 @@ pub struct Foo(
 
 // @has foo/enum.Bar.html
 // @has - '//pre[@class="rust enum"]' 'BarVariant(String),'
-// @matches - '//*[@id="variant.BarVariant.fields"]/h3' '^Tuple Fields of BarVariant$'
+// @matches - '//*[@id="variant.BarVariant.fields"]/h4' '^Tuple Fields$'
 // @has - '//*[@id="variant.BarVariant.field.0"]' '0: String'
 // @has - '//*[@id="variant.BarVariant.fields"]//*[@class="docblock"]' 'Hello docs'
-// @matches - '//*[@id="variant.FooVariant.fields"]/h3' '^Fields of FooVariant$'
+// @matches - '//*[@id="variant.FooVariant.fields"]/h4' '^Fields$'
 pub enum Bar {
     BarVariant(
         /// Hello docs
diff --git a/src/test/ui/consts/const-eval/simd/insert_extract.rs b/src/test/ui/consts/const-eval/simd/insert_extract.rs
index cae8fcf1068..a1d6c5e51b4 100644
--- a/src/test/ui/consts/const-eval/simd/insert_extract.rs
+++ b/src/test/ui/consts/const-eval/simd/insert_extract.rs
@@ -7,7 +7,9 @@
 
 #[repr(simd)] struct i8x1(i8);
 #[repr(simd)] struct u16x2(u16, u16);
-#[repr(simd)] struct f32x4(f32, f32, f32, f32);
+// Make some of them array types to ensure those also work.
+#[repr(simd)] struct i8x1_arr([i8; 1]);
+#[repr(simd)] struct f32x4([f32; 4]);
 
 extern "platform-intrinsic" {
     #[rustc_const_stable(feature = "foo", since = "1.3.37")]
@@ -26,6 +28,14 @@ fn main() {
         assert_eq!(Y0, 42);
     }
     {
+        const U: i8x1_arr = i8x1_arr([13]);
+        const V: i8x1_arr = unsafe { simd_insert(U, 0_u32, 42_i8) };
+        const X0: i8 = V.0[0];
+        const Y0: i8 = unsafe { simd_extract(V, 0) };
+        assert_eq!(X0, 42);
+        assert_eq!(Y0, 42);
+    }
+    {
         const U: u16x2 = u16x2(13, 14);
         const V: u16x2 = unsafe { simd_insert(U, 1_u32, 42_u16) };
         const X0: u16 = V.0;
@@ -38,12 +48,12 @@ fn main() {
         assert_eq!(Y1, 42);
     }
     {
-        const U: f32x4 = f32x4(13., 14., 15., 16.);
+        const U: f32x4 = f32x4([13., 14., 15., 16.]);
         const V: f32x4 = unsafe { simd_insert(U, 1_u32, 42_f32) };
-        const X0: f32 = V.0;
-        const X1: f32 = V.1;
-        const X2: f32 = V.2;
-        const X3: f32 = V.3;
+        const X0: f32 = V.0[0];
+        const X1: f32 = V.0[1];
+        const X2: f32 = V.0[2];
+        const X3: f32 = V.0[3];
         const Y0: f32 = unsafe { simd_extract(V, 0) };
         const Y1: f32 = unsafe { simd_extract(V, 1) };
         const Y2: f32 = unsafe { simd_extract(V, 2) };
diff --git a/src/test/ui/lang-items/issue-87573.rs b/src/test/ui/lang-items/issue-87573.rs
new file mode 100644
index 00000000000..aeb0c245a72
--- /dev/null
+++ b/src/test/ui/lang-items/issue-87573.rs
@@ -0,0 +1,28 @@
+// Regression test for #87573, ensures that duplicate lang items or invalid generics
+// for lang items doesn't cause ICE.
+
+#![feature(no_core, lang_items)]
+#![no_core]
+#![crate_type = "lib"]
+
+pub static STATIC_BOOL: bool = true;
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+#[lang = "sync"]
+trait Sync {}
+impl Sync for bool {}
+
+#[lang = "drop_in_place"]
+//~^ ERROR: `drop_in_place` language item must be applied to a function with at least 1 generic argument
+fn drop_fn() {
+    while false {}
+}
+
+#[lang = "start"]
+//~^ ERROR: `start` language item must be applied to a function with 1 generic argument
+fn start(){}
diff --git a/src/test/ui/lang-items/issue-87573.stderr b/src/test/ui/lang-items/issue-87573.stderr
new file mode 100644
index 00000000000..25560cfa0e6
--- /dev/null
+++ b/src/test/ui/lang-items/issue-87573.stderr
@@ -0,0 +1,21 @@
+error[E0718]: `drop_in_place` language item must be applied to a function with at least 1 generic argument
+  --> $DIR/issue-87573.rs:20:1
+   |
+LL | #[lang = "drop_in_place"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | fn drop_fn() {
+   |           - this function has 0 generic arguments
+
+error[E0718]: `start` language item must be applied to a function with 1 generic argument
+  --> $DIR/issue-87573.rs:26:1
+   |
+LL | #[lang = "start"]
+   | ^^^^^^^^^^^^^^^^^
+LL |
+LL | fn start(){}
+   |         - this function has 0 generic arguments
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0718`.