about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-05 23:12:34 +0000
committerbors <bors@rust-lang.org>2022-07-05 23:12:34 +0000
commitf342bea9d19f14616c6559312552e6d0ee529cfd (patch)
treeebcd7b37e2b6a58192dd7e9a9d4f6e377fa23136 /src
parent7b46aa594c4bdc507fbd904b6777ca30c37a9209 (diff)
parent0eded16705a493d53c8b5c646e2eb4be68ba9224 (diff)
downloadrust-f342bea9d19f14616c6559312552e6d0ee529cfd.tar.gz
rust-f342bea9d19f14616c6559312552e6d0ee529cfd.zip
Auto merge of #98963 - GuillaumeGomez:rollup-n030us5, r=GuillaumeGomez
Rollup of 6 pull requests

Successful merges:

 - #95503 (bootstrap: Allow building individual crates)
 - #96814 (Fix repr(align) enum handling)
 - #98256 (Fix whitespace handling after where clause)
 - #98880 (Proper macOS libLLVM symlink when cross compiling)
 - #98944 (Edit `rustc_mir_dataflow::framework::lattice::FlatSet` docs)
 - #98951 (Update books)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/builder.rs19
-rw-r--r--src/bootstrap/builder/tests.rs125
-rw-r--r--src/bootstrap/cache.rs123
-rw-r--r--src/bootstrap/check.rs4
-rw-r--r--src/bootstrap/compile.rs77
-rw-r--r--src/bootstrap/dist.rs6
-rw-r--r--src/bootstrap/doc.rs12
-rw-r--r--src/bootstrap/native.rs14
-rw-r--r--src/bootstrap/test.rs32
-rw-r--r--src/bootstrap/tool.rs10
m---------src/doc/book0
m---------src/doc/embedded-book0
m---------src/doc/nomicon0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/librustdoc/html/format.rs4
-rw-r--r--src/librustdoc/html/render/print_item.rs55
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.enum.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.enum2.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.rs77
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.struct.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.struct2.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.trait.html6
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.trait2.html6
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.union.html3
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.union2.html3
-rw-r--r--src/test/ui/aligned_enum_cast.rs12
-rw-r--r--src/test/ui/layout/issue-96185-overaligned-enum.rs19
-rw-r--r--src/test/ui/layout/issue-96185-overaligned-enum.stderr172
29 files changed, 590 insertions, 205 deletions
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index fa2a530d9db..dc6a0f6f241 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -13,7 +13,6 @@ use std::process::{Command, Stdio};
 use std::time::{Duration, Instant};
 
 use crate::cache::{Cache, Interned, INTERNER};
-use crate::compile;
 use crate::config::{SplitDebuginfo, TargetSelection};
 use crate::dist;
 use crate::doc;
@@ -26,6 +25,7 @@ use crate::tool::{self, SourceType};
 use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
 use crate::EXTRA_CHECK_CFGS;
 use crate::{check, Config};
+use crate::{compile, Crate};
 use crate::{Build, CLang, DocTests, GitRepo, Mode};
 
 pub use crate::Compiler;
@@ -304,9 +304,7 @@ impl StepDescription {
         if paths.is_empty() || builder.config.include_default_paths {
             for (desc, should_run) in v.iter().zip(&should_runs) {
                 if desc.default && should_run.is_really_default() {
-                    for pathset in &should_run.paths {
-                        desc.maybe_run(builder, vec![pathset.clone()]);
-                    }
+                    desc.maybe_run(builder, should_run.paths.iter().cloned().collect());
                 }
             }
         }
@@ -424,8 +422,16 @@ impl<'a> ShouldRun<'a> {
     /// any of its (local) dependencies.
     ///
     /// `make_run` will be called a single time with all matching command-line paths.
-    pub fn krate(mut self, name: &str) -> Self {
-        for krate in self.builder.in_tree_crates(name, None) {
+    pub fn crate_or_deps(self, name: &str) -> Self {
+        let crates = self.builder.in_tree_crates(name, None);
+        self.crates(crates)
+    }
+
+    /// Indicates it should run if the command-line selects any of the given crates.
+    ///
+    /// `make_run` will be called a single time with all matching command-line paths.
+    pub(crate) fn crates(mut self, crates: Vec<&Crate>) -> Self {
+        for krate in crates {
             let path = krate.local_path(self.builder);
             self.paths.insert(PathSet::one(path, self.kind));
         }
@@ -581,6 +587,7 @@ impl<'a> Builder<'a> {
         match kind {
             Kind::Build => describe!(
                 compile::Std,
+                compile::Rustc,
                 compile::Assemble,
                 compile::CodegenBackend,
                 compile::StartupObjects,
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index 70cb0de7cce..c084e77d3a9 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -57,6 +57,24 @@ fn check_cli<const N: usize>(paths: [&str; N]) {
     );
 }
 
+macro_rules! std {
+    ($host:ident => $target:ident, stage = $stage:literal) => {
+        compile::Std::new(
+            Compiler { host: TargetSelection::from_user(stringify!($host)), stage: $stage },
+            TargetSelection::from_user(stringify!($target)),
+        )
+    };
+}
+
+macro_rules! rustc {
+    ($host:ident => $target:ident, stage = $stage:literal) => {
+        compile::Rustc::new(
+            Compiler { host: TargetSelection::from_user(stringify!($host)), stage: $stage },
+            TargetSelection::from_user(stringify!($target)),
+        )
+    };
+}
+
 #[test]
 fn test_valid() {
     // make sure multi suite paths are accepted
@@ -117,6 +135,17 @@ fn test_exclude_kind() {
     assert!(run_build(&[path], config).contains::<tool::CargoTest>());
 }
 
+/// Ensure that if someone passes both a single crate and `library`, all library crates get built.
+#[test]
+fn alias_and_path_for_library() {
+    let mut cache =
+        run_build(&["library".into(), "core".into()], configure("build", &["A"], &["A"]));
+    assert_eq!(
+        first(cache.all::<compile::Std>()),
+        &[std!(A => A, stage = 0), std!(A => A, stage = 1)]
+    );
+}
+
 mod defaults {
     use super::{configure, first, run_build};
     use crate::builder::*;
@@ -130,10 +159,7 @@ mod defaults {
         let a = TargetSelection::from_user("A");
         assert_eq!(
             first(cache.all::<compile::Std>()),
-            &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            ]
+            &[std!(A => A, stage = 0), std!(A => A, stage = 1),]
         );
         assert!(!cache.all::<compile::Assemble>().is_empty());
         // Make sure rustdoc is only built once.
@@ -143,10 +169,7 @@ mod defaults {
             // - this is the compiler it's _linked_ to, not built with.
             &[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }],
         );
-        assert_eq!(
-            first(cache.all::<compile::Rustc>()),
-            &[compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },]
-        );
+        assert_eq!(first(cache.all::<compile::Rustc>()), &[rustc!(A => A, stage = 0)],);
     }
 
     #[test]
@@ -155,10 +178,7 @@ mod defaults {
         let mut cache = run_build(&[], config);
 
         let a = TargetSelection::from_user("A");
-        assert_eq!(
-            first(cache.all::<compile::Std>()),
-            &[compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },]
-        );
+        assert_eq!(first(cache.all::<compile::Std>()), &[std!(A => A, stage = 0)]);
         assert!(!cache.all::<compile::Assemble>().is_empty());
         assert_eq!(
             first(cache.all::<tool::Rustdoc>()),
@@ -185,10 +205,10 @@ mod defaults {
         assert_eq!(
             first(cache.all::<compile::Std>()),
             &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: b },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                std!(A => A, stage = 0),
+                std!(A => A, stage = 1),
+                std!(A => B, stage = 0),
+                std!(A => B, stage = 1),
             ]
         );
         assert_eq!(
@@ -208,10 +228,7 @@ mod defaults {
         );
         assert_eq!(
             first(cache.all::<compile::Rustc>()),
-            &[
-                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: b },
-            ]
+            &[rustc!(A => A, stage = 0), rustc!(A => B, stage = 0),]
         );
     }
 
@@ -334,11 +351,11 @@ mod dist {
         assert_eq!(
             first(cache.all::<compile::Std>()),
             &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+                std!(A => A, stage = 0),
+                std!(A => A, stage = 1),
+                std!(A => A, stage = 2),
+                std!(A => B, stage = 1),
+                std!(A => B, stage = 2),
             ],
         );
         assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
@@ -346,7 +363,6 @@ mod dist {
 
     #[test]
     fn dist_only_cross_host() {
-        let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
         let mut config = configure(&["A", "B"], &["A", "B"]);
         config.docs = false;
@@ -360,10 +376,7 @@ mod dist {
         );
         assert_eq!(
             first(cache.all::<compile::Rustc>()),
-            &[
-                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
-            ]
+            &[rustc!(A => A, stage = 0), rustc!(A => B, stage = 1),]
         );
     }
 
@@ -450,11 +463,11 @@ mod dist {
         assert_eq!(
             first(cache.all::<compile::Std>()),
             &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+                std!(A => A, stage = 0),
+                std!(A => A, stage = 1),
+                std!(A => A, stage = 2),
+                std!(A => B, stage = 1),
+                std!(A => B, stage = 2),
             ]
         );
         assert_eq!(
@@ -474,33 +487,29 @@ mod dist {
         let mut builder = Builder::new(&build);
         builder.run_step_descriptions(
             &Builder::get_step_descriptions(Kind::Build),
-            &["compiler/rustc".into(), "library/std".into()],
+            &["compiler/rustc".into(), "library".into()],
         );
 
-        let a = TargetSelection::from_user("A");
-        let b = TargetSelection::from_user("B");
-        let c = TargetSelection::from_user("C");
-
         assert_eq!(
             first(builder.cache.all::<compile::Std>()),
             &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
+                std!(A => A, stage = 0),
+                std!(A => A, stage = 1),
+                std!(A => A, stage = 2),
+                std!(A => B, stage = 1),
+                std!(A => B, stage = 2),
+                std!(A => C, stage = 2),
             ]
         );
-        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        assert_eq!(builder.cache.all::<compile::Assemble>().len(), 5);
         assert_eq!(
             first(builder.cache.all::<compile::Rustc>()),
             &[
-                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
-                compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b },
+                rustc!(A => A, stage = 0),
+                rustc!(A => A, stage = 1),
+                rustc!(A => A, stage = 2),
+                rustc!(A => B, stage = 1),
+                rustc!(A => B, stage = 2),
             ]
         );
     }
@@ -513,15 +522,10 @@ mod dist {
         builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
 
         let a = TargetSelection::from_user("A");
-        let c = TargetSelection::from_user("C");
 
         assert_eq!(
             first(builder.cache.all::<compile::Std>()),
-            &[
-                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-            ]
+            &[std!(A => A, stage = 0), std!(A => A, stage = 1), std!(A => C, stage = 2),]
         );
         assert_eq!(
             first(builder.cache.all::<compile::Assemble>()),
@@ -533,10 +537,7 @@ mod dist {
         );
         assert_eq!(
             first(builder.cache.all::<compile::Rustc>()),
-            &[
-                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
-            ]
+            &[rustc!(A => A, stage = 0), rustc!(A => A, stage = 1),]
         );
     }
 
diff --git a/src/bootstrap/cache.rs b/src/bootstrap/cache.rs
index 97f0bfdc484..be5c9bb0788 100644
--- a/src/bootstrap/cache.rs
+++ b/src/bootstrap/cache.rs
@@ -4,13 +4,12 @@ use std::cell::RefCell;
 use std::cmp::{Ord, Ordering, PartialOrd};
 use std::collections::HashMap;
 use std::convert::AsRef;
-use std::ffi::OsStr;
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::Deref;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 use std::sync::Mutex;
 
 // FIXME: replace with std::lazy after it gets stabilized and reaches beta
@@ -20,15 +19,9 @@ use crate::builder::Step;
 
 pub struct Interned<T>(usize, PhantomData<*const T>);
 
-impl Default for Interned<String> {
+impl<T: Internable + Default> Default for Interned<T> {
     fn default() -> Self {
-        INTERNER.intern_string(String::default())
-    }
-}
-
-impl Default for Interned<PathBuf> {
-    fn default() -> Self {
-        INTERNER.intern_path(PathBuf::default())
+        T::default().intern()
     }
 }
 
@@ -77,87 +70,48 @@ impl fmt::Display for Interned<String> {
     }
 }
 
-impl fmt::Debug for Interned<String> {
+impl<T, U: ?Sized + fmt::Debug> fmt::Debug for Interned<T>
+where
+    Self: Deref<Target = U>,
+{
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let s: &str = &*self;
-        f.write_fmt(format_args!("{:?}", s))
-    }
-}
-impl fmt::Debug for Interned<PathBuf> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let s: &Path = &*self;
+        let s: &U = &*self;
         f.write_fmt(format_args!("{:?}", s))
     }
 }
 
-impl Hash for Interned<String> {
+impl<T: Internable + Hash> Hash for Interned<T> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        let l = INTERNER.strs.lock().unwrap();
+        let l = T::intern_cache().lock().unwrap();
         l.get(*self).hash(state)
     }
 }
 
-impl Hash for Interned<PathBuf> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        let l = INTERNER.paths.lock().unwrap();
-        l.get(*self).hash(state)
+impl<T: Internable + Deref> Deref for Interned<T> {
+    type Target = T::Target;
+    fn deref(&self) -> &'static Self::Target {
+        let l = T::intern_cache().lock().unwrap();
+        unsafe { mem::transmute::<&Self::Target, &'static Self::Target>(l.get(*self)) }
     }
 }
 
-impl Deref for Interned<String> {
-    type Target = str;
-    fn deref(&self) -> &'static str {
-        let l = INTERNER.strs.lock().unwrap();
-        unsafe { mem::transmute::<&str, &'static str>(l.get(*self)) }
+impl<T: Internable + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
+    fn as_ref(&self) -> &'static U {
+        let l = T::intern_cache().lock().unwrap();
+        unsafe { mem::transmute::<&U, &'static U>(l.get(*self).as_ref()) }
     }
 }
 
-impl Deref for Interned<PathBuf> {
-    type Target = Path;
-    fn deref(&self) -> &'static Path {
-        let l = INTERNER.paths.lock().unwrap();
-        unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) }
-    }
-}
-
-impl AsRef<Path> for Interned<PathBuf> {
-    fn as_ref(&self) -> &'static Path {
-        let l = INTERNER.paths.lock().unwrap();
-        unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) }
-    }
-}
-
-impl AsRef<Path> for Interned<String> {
-    fn as_ref(&self) -> &'static Path {
-        let l = INTERNER.strs.lock().unwrap();
-        unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self).as_ref()) }
-    }
-}
-
-impl AsRef<OsStr> for Interned<PathBuf> {
-    fn as_ref(&self) -> &'static OsStr {
-        let l = INTERNER.paths.lock().unwrap();
-        unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) }
-    }
-}
-
-impl AsRef<OsStr> for Interned<String> {
-    fn as_ref(&self) -> &'static OsStr {
-        let l = INTERNER.strs.lock().unwrap();
-        unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) }
-    }
-}
-
-impl PartialOrd<Interned<String>> for Interned<String> {
+impl<T: Internable + PartialOrd> PartialOrd for Interned<T> {
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        let l = INTERNER.strs.lock().unwrap();
+        let l = T::intern_cache().lock().unwrap();
         l.get(*self).partial_cmp(l.get(*other))
     }
 }
 
-impl Ord for Interned<String> {
+impl<T: Internable + Ord> Ord for Interned<T> {
     fn cmp(&self, other: &Self) -> Ordering {
-        let l = INTERNER.strs.lock().unwrap();
+        let l = T::intern_cache().lock().unwrap();
         l.get(*self).cmp(l.get(*other))
     }
 }
@@ -208,6 +162,33 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
 pub struct Interner {
     strs: Mutex<TyIntern<String>>,
     paths: Mutex<TyIntern<PathBuf>>,
+    lists: Mutex<TyIntern<Vec<String>>>,
+}
+
+trait Internable: Clone + Eq + Hash + 'static {
+    fn intern_cache() -> &'static Mutex<TyIntern<Self>>;
+
+    fn intern(self) -> Interned<Self> {
+        Self::intern_cache().lock().unwrap().intern(self)
+    }
+}
+
+impl Internable for String {
+    fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
+        &INTERNER.strs
+    }
+}
+
+impl Internable for PathBuf {
+    fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
+        &INTERNER.paths
+    }
+}
+
+impl Internable for Vec<String> {
+    fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
+        &INTERNER.lists
+    }
 }
 
 impl Interner {
@@ -221,6 +202,10 @@ impl Interner {
     pub fn intern_path(&self, s: PathBuf) -> Interned<PathBuf> {
         self.paths.lock().unwrap().intern(s)
     }
+
+    pub fn intern_list(&self, v: Vec<String>) -> Interned<Vec<String>> {
+        self.lists.lock().unwrap().intern(v)
+    }
 }
 
 pub static INTERNER: Lazy<Interner> = Lazy::new(Interner::default);
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 731ebc41bb9..4985b054678 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -184,8 +184,8 @@ impl Step for Rustc {
             // the sysroot for the compiler to find. Otherwise, we're going to
             // fail when building crates that need to generate code (e.g., build
             // scripts and their dependencies).
-            builder.ensure(crate::compile::Std { target: compiler.host, compiler });
-            builder.ensure(crate::compile::Std { target, compiler });
+            builder.ensure(crate::compile::Std::new(compiler, compiler.host));
+            builder.ensure(crate::compile::Std::new(compiler, target));
         } else {
             builder.ensure(Std { target });
         }
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index b4807d1ab3a..399be26d5ac 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -29,10 +29,31 @@ use crate::util::{exe, is_debug_info, is_dylib, output, symlink_dir, t, up_to_da
 use crate::LLVM_TOOLS;
 use crate::{CLang, Compiler, DependencyType, GitRepo, Mode};
 
-#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Std {
     pub target: TargetSelection,
     pub compiler: Compiler,
+    /// Whether to build only a subset of crates in the standard library.
+    ///
+    /// This shouldn't be used from other steps; see the comment on [`Rustc`].
+    crates: Interned<Vec<String>>,
+}
+
+impl Std {
+    pub fn new(compiler: Compiler, target: TargetSelection) -> Self {
+        Self { target, compiler, crates: Default::default() }
+    }
+}
+
+/// Return a `-p=x -p=y` string suitable for passing to a cargo invocation.
+fn build_crates_in_set(run: &RunConfig<'_>) -> Interned<Vec<String>> {
+    let mut crates = Vec::new();
+    for krate in &run.paths {
+        let path = krate.assert_single_path();
+        let crate_name = run.builder.crate_paths[&path.path];
+        crates.push(format!("-p={crate_name}"));
+    }
+    INTERNER.intern_list(crates)
 }
 
 impl Step for Std {
@@ -43,15 +64,22 @@ impl Step for Std {
         // When downloading stage1, the standard library has already been copied to the sysroot, so
         // there's no need to rebuild it.
         let builder = run.builder;
-        run.all_krates("test")
+        run.crate_or_deps("test")
             .path("library")
             .lazy_default_condition(Box::new(|| !builder.download_rustc()))
     }
 
     fn make_run(run: RunConfig<'_>) {
+        // Normally, people will pass *just* library if they pass it.
+        // But it's possible (although strange) to pass something like `library std core`.
+        // Build all crates anyway, as if they hadn't passed the other args.
+        let has_library =
+            run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
+        let crates = if has_library { Default::default() } else { build_crates_in_set(&run) };
         run.builder.ensure(Std {
             compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
+            crates,
         });
     }
 
@@ -86,7 +114,7 @@ impl Step for Std {
 
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
         if compiler_to_use != compiler {
-            builder.ensure(Std { compiler: compiler_to_use, target });
+            builder.ensure(Std::new(compiler_to_use, target));
             builder.info(&format!("Uplifting stage1 std ({} -> {})", compiler_to_use.host, target));
 
             // Even if we're not building std this stage, the new sysroot must
@@ -115,7 +143,7 @@ impl Step for Std {
         run_cargo(
             builder,
             cargo,
-            vec![],
+            self.crates.to_vec(),
             &libstd_stamp(builder, compiler, target),
             target_deps,
             false,
@@ -524,6 +552,18 @@ impl Step for StartupObjects {
 pub struct Rustc {
     pub target: TargetSelection,
     pub compiler: Compiler,
+    /// Whether to build a subset of crates, rather than the whole compiler.
+    ///
+    /// This should only be requested by the user, not used within rustbuild itself.
+    /// Using it within rustbuild can lead to confusing situation where lints are replayed
+    /// in two different steps.
+    crates: Interned<Vec<String>>,
+}
+
+impl Rustc {
+    pub fn new(compiler: Compiler, target: TargetSelection) -> Self {
+        Self { target, compiler, crates: Default::default() }
+    }
 }
 
 impl Step for Rustc {
@@ -532,13 +572,22 @@ impl Step for Rustc {
     const DEFAULT: bool = false;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.never()
+        let mut crates = run.builder.in_tree_crates("rustc-main", None);
+        for (i, krate) in crates.iter().enumerate() {
+            if krate.name == "rustc-main" {
+                crates.swap_remove(i);
+                break;
+            }
+        }
+        run.crates(crates)
     }
 
     fn make_run(run: RunConfig<'_>) {
+        let crates = build_crates_in_set(&run);
         run.builder.ensure(Rustc {
             compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
+            crates,
         });
     }
 
@@ -560,7 +609,7 @@ impl Step for Rustc {
             return;
         }
 
-        builder.ensure(Std { compiler, target });
+        builder.ensure(Std::new(compiler, target));
 
         if builder.config.keep_stage.contains(&compiler.stage) {
             builder.info("Warning: Using a potentially old librustc. This may not behave well.");
@@ -571,7 +620,7 @@ impl Step for Rustc {
 
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
         if compiler_to_use != compiler {
-            builder.ensure(Rustc { compiler: compiler_to_use, target });
+            builder.ensure(Rustc::new(compiler_to_use, target));
             builder
                 .info(&format!("Uplifting stage1 rustc ({} -> {})", builder.config.build, target));
             builder.ensure(RustcLink {
@@ -583,10 +632,10 @@ impl Step for Rustc {
         }
 
         // Ensure that build scripts and proc macros have a std / libproc_macro to link against.
-        builder.ensure(Std {
-            compiler: builder.compiler(self.compiler.stage, builder.config.build),
-            target: builder.config.build,
-        });
+        builder.ensure(Std::new(
+            builder.compiler(self.compiler.stage, builder.config.build),
+            builder.config.build,
+        ));
 
         let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build");
         rustc_cargo(builder, &mut cargo, target);
@@ -633,7 +682,7 @@ impl Step for Rustc {
         run_cargo(
             builder,
             cargo,
-            vec![],
+            self.crates.to_vec(),
             &librustc_stamp(builder, compiler, target),
             vec![],
             false,
@@ -821,7 +870,7 @@ impl Step for CodegenBackend {
         let target = self.target;
         let backend = self.backend;
 
-        builder.ensure(Rustc { compiler, target });
+        builder.ensure(Rustc::new(compiler, target));
 
         if builder.config.keep_stage.contains(&compiler.stage) {
             builder.info(
@@ -1103,7 +1152,7 @@ impl Step for Assemble {
         // link to these. (FIXME: Is that correct? It seems to be correct most
         // of the time but I think we do link to these for stage2/bin compilers
         // when not performing a full bootstrap).
-        builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host });
+        builder.ensure(Rustc::new(build_compiler, target_compiler.host));
 
         for &backend in builder.config.rust_codegen_backends.iter() {
             if backend == "llvm" {
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index cd85654db01..4aadc3943c9 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -557,7 +557,7 @@ impl Step for Std {
             return None;
         }
 
-        builder.ensure(compile::Std { compiler, target });
+        builder.ensure(compile::Std::new(compiler, target));
 
         let mut tarball = Tarball::new(builder, "rust-std", &target.triple);
         tarball.include_target_in_component_name(true);
@@ -603,7 +603,7 @@ impl Step for RustcDev {
             return None;
         }
 
-        builder.ensure(compile::Rustc { compiler, target });
+        builder.ensure(compile::Rustc::new(compiler, target));
 
         let tarball = Tarball::new(builder, "rustc-dev", &target.triple);
 
@@ -666,7 +666,7 @@ impl Step for Analysis {
             return None;
         }
 
-        builder.ensure(compile::Std { compiler, target });
+        builder.ensure(compile::Std::new(compiler, target));
         let src = builder
             .stage_out(compiler, Mode::Std)
             .join(target.triple)
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 3cf0f9b9f99..5b961456076 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -534,7 +534,9 @@ impl Step for Rustc {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.krate("rustc-main").path("compiler").default_condition(builder.config.compiler_docs)
+        run.crate_or_deps("rustc-main")
+            .path("compiler")
+            .default_condition(builder.config.compiler_docs)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -567,7 +569,7 @@ impl Step for Rustc {
         // Build the standard library, so that proc-macros can use it.
         // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.)
         let compiler = builder.compiler(stage, builder.config.build);
-        builder.ensure(compile::Std { compiler, target: builder.config.build });
+        builder.ensure(compile::Std::new(compiler, builder.config.build));
 
         builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
 
@@ -656,7 +658,7 @@ macro_rules! tool_doc {
 
             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                 let builder = run.builder;
-                run.krate($should_run).default_condition(builder.config.compiler_docs)
+                run.crate_or_deps($should_run).default_condition(builder.config.compiler_docs)
             }
 
             fn make_run(run: RunConfig<'_>) {
@@ -683,7 +685,7 @@ macro_rules! tool_doc {
                 // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed
                 // with strange errors, but only on a full bors test ...
                 let compiler = builder.compiler(stage, builder.config.build);
-                builder.ensure(compile::Rustc { compiler, target });
+                builder.ensure(compile::Rustc::new(compiler, target));
 
                 builder.info(
                     &format!(
@@ -890,7 +892,7 @@ impl Step for RustcBook {
         let rustc = builder.rustc(self.compiler);
         // The tool runs `rustc` for extracting output examples, so it needs a
         // functional sysroot.
-        builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
+        builder.ensure(compile::Std::new(self.compiler, self.target));
         let mut cmd = builder.tool_cmd(Tool::LintDocs);
         cmd.arg("--src");
         cmd.arg(builder.src.join("compiler"));
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 8395be40f9b..503a2fc469e 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -487,16 +487,14 @@ impl Step for Llvm {
             let version = output(cmd.arg("--version"));
             let major = version.split('.').next().unwrap();
             let lib_name = match llvm_version_suffix {
-                Some(s) => format!("lib/libLLVM-{}{}.dylib", major, s),
-                None => format!("lib/libLLVM-{}.dylib", major),
+                Some(s) => format!("libLLVM-{}{}.dylib", major, s),
+                None => format!("libLLVM-{}.dylib", major),
             };
 
-            // The reason why we build the library path from llvm-config is because
-            // the output of llvm-config depends on its location in the file system.
-            // Make sure we create the symlink exactly where it's needed.
-            let llvm_base = build_llvm_config.parent().unwrap().parent().unwrap();
-            let lib_llvm = llvm_base.join(lib_name);
-            t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
+            let lib_llvm = out_dir.join("build").join("lib").join(lib_name);
+            if !lib_llvm.exists() {
+                t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
+            }
         }
 
         t!(stamp.write());
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index f3395507bb0..6be3da55291 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -225,7 +225,7 @@ impl Step for Cargotest {
     /// test` to ensure that we don't regress the test suites there.
     fn run(self, builder: &Builder<'_>) {
         let compiler = builder.compiler(self.stage, self.host);
-        builder.ensure(compile::Rustc { compiler, target: compiler.host });
+        builder.ensure(compile::Rustc::new(compiler, compiler.host));
         let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host });
 
         // Note that this is a short, cryptic, and not scoped directory name. This
@@ -603,7 +603,7 @@ impl Step for CompiletestTest {
 
         // We need `ToolStd` for the locally-built sysroot because
         // compiletest uses unstable features of the `test` crate.
-        builder.ensure(compile::Std { compiler, target: host });
+        builder.ensure(compile::Std::new(compiler, host));
         let cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
@@ -896,7 +896,7 @@ impl Step for RustdocGUI {
         let nodejs = builder.config.nodejs.as_ref().expect("nodejs isn't available");
         let npm = builder.config.npm.as_ref().expect("npm isn't available");
 
-        builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
+        builder.ensure(compile::Std::new(self.compiler, self.target));
 
         // The goal here is to check if the necessary packages are installed, and if not, we
         // panic.
@@ -1273,12 +1273,12 @@ note: if you're sure you want to do this, please open an issue as to why. In the
         }
 
         if suite.ends_with("fulldeps") {
-            builder.ensure(compile::Rustc { compiler, target });
+            builder.ensure(compile::Rustc::new(compiler, target));
         }
 
-        builder.ensure(compile::Std { compiler, target });
+        builder.ensure(compile::Std::new(compiler, target));
         // ensure that `libproc_macro` is available on the host.
-        builder.ensure(compile::Std { compiler, target: compiler.host });
+        builder.ensure(compile::Std::new(compiler, compiler.host));
 
         // Also provide `rust_test_helpers` for the host.
         builder.ensure(native::TestHelpers { target: compiler.host });
@@ -1643,7 +1643,7 @@ impl BookTest {
     fn run_ext_doc(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
 
-        builder.ensure(compile::Std { compiler, target: compiler.host });
+        builder.ensure(compile::Std::new(compiler, compiler.host));
 
         // mdbook just executes a binary named "rustdoc", so we need to update
         // PATH so that it points to our rustdoc.
@@ -1671,7 +1671,7 @@ impl BookTest {
     fn run_local_doc(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
 
-        builder.ensure(compile::Std { compiler, target: compiler.host });
+        builder.ensure(compile::Std::new(compiler, compiler.host));
 
         // Do a breadth-first traversal of the `src/doc` directory and just run
         // tests for all files that end in `*.md`
@@ -1790,7 +1790,7 @@ impl Step for ErrorIndex {
         builder.run_quiet(&mut tool);
         // The tests themselves need to link to std, so make sure it is
         // available.
-        builder.ensure(compile::Std { compiler, target: compiler.host });
+        builder.ensure(compile::Std::new(compiler, compiler.host));
         markdown_test(builder, compiler, &output);
     }
 }
@@ -1867,7 +1867,7 @@ impl Step for CrateLibrustc {
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.krate("rustc-main")
+        run.crate_or_deps("rustc-main")
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1909,7 +1909,7 @@ impl Step for Crate {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.krate("test")
+        run.crate_or_deps("test")
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1940,7 +1940,7 @@ impl Step for Crate {
         let mode = self.mode;
         let test_kind = self.test_kind;
 
-        builder.ensure(compile::Std { compiler, target });
+        builder.ensure(compile::Std::new(compiler, target));
         builder.ensure(RemoteCopyLibs { compiler, target });
 
         // If we're not doing a full bootstrap but we're testing a stage2
@@ -2062,7 +2062,7 @@ impl Step for CrateRustdoc {
             // isn't really necessary.
             builder.compiler_for(builder.top_stage, target, target)
         };
-        builder.ensure(compile::Rustc { compiler, target });
+        builder.ensure(compile::Rustc::new(compiler, target));
 
         let mut cargo = tool::prepare_tool_cargo(
             builder,
@@ -2177,7 +2177,7 @@ impl Step for CrateRustdocJsonTypes {
         // `compiler`, then it would cause rustdoc to be built *again*, which
         // isn't really necessary.
         let compiler = builder.compiler_for(builder.top_stage, target, target);
-        builder.ensure(compile::Rustc { compiler, target });
+        builder.ensure(compile::Rustc::new(compiler, target));
 
         let mut cargo = tool::prepare_tool_cargo(
             builder,
@@ -2245,7 +2245,7 @@ impl Step for RemoteCopyLibs {
             return;
         }
 
-        builder.ensure(compile::Std { compiler, target });
+        builder.ensure(compile::Std::new(compiler, target));
 
         builder.info(&format!("REMOTE copy libs to emulator ({})", target));
 
@@ -2415,7 +2415,7 @@ impl Step for TierCheck {
 
     /// Tests the Platform Support page in the rustc book.
     fn run(self, builder: &Builder<'_>) {
-        builder.ensure(compile::Std { compiler: self.compiler, target: self.compiler.host });
+        builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             self.compiler,
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 5be6841e988..74a793d257e 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -51,10 +51,10 @@ impl Step for ToolBuild {
 
         match self.mode {
             Mode::ToolRustc => {
-                builder.ensure(compile::Std { compiler, target: compiler.host });
-                builder.ensure(compile::Rustc { compiler, target });
+                builder.ensure(compile::Std::new(compiler, compiler.host));
+                builder.ensure(compile::Rustc::new(compiler, target));
             }
-            Mode::ToolStd => builder.ensure(compile::Std { compiler, target }),
+            Mode::ToolStd => builder.ensure(compile::Std::new(compiler, target)),
             Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
             _ => panic!("unexpected Mode for tool build"),
         }
@@ -512,8 +512,8 @@ impl Step for Rustdoc {
         // When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually
         // build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build
         // it.
-        builder.ensure(compile::Std { compiler: build_compiler, target: target_compiler.host });
-        builder.ensure(compile::Rustc { compiler: build_compiler, target: target_compiler.host });
+        builder.ensure(compile::Std::new(build_compiler, target_compiler.host));
+        builder.ensure(compile::Rustc::new(build_compiler, target_compiler.host));
         // NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0
         // compiler, since you do just as much work.
         if !builder.config.dry_run && builder.download_rustc() && build_compiler.stage == 0 {
diff --git a/src/doc/book b/src/doc/book
-Subproject efbafdba3618487fbc9305318fcab9775132ac1
+Subproject cf2653a5ca553cbbb4a17f1a7db1947820f6a77
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject e17dcef5e96346ee3d7fa56820ddc7e5c39636b
+Subproject 766979590da8100998f0d662499d4a901d8d164
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 3a43983b76174342b7dbd3e12ea2c49f762e52b
+Subproject 70db9e4189f64d1d8e2451b1046111fb356b6dc
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 1095df2a5850f2d345fad43a30633133365875b
+Subproject 83724ca387a2a1cd3e8d848f62820020760e358
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject 048d925f0a955aac601c4160c0e7f05771bcf63
+Subproject eb83839e903a0a8f1406f7e941886273f189b26
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 5584ecd287a..0982c4b3ace 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -146,6 +146,10 @@ impl Buffer {
     pub(crate) fn reserve(&mut self, additional: usize) {
         self.buffer.reserve(additional)
     }
+
+    pub(crate) fn len(&self) -> usize {
+        self.buffer.len()
+    }
 }
 
 fn comma_sep<T: fmt::Display>(
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index fe00f952e04..3525007baf3 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -62,6 +62,17 @@ struct ItemVars<'a> {
     src_href: Option<&'a str>,
 }
 
+/// Calls `print_where_clause` and returns `true` if a `where` clause was generated.
+fn print_where_clause_and_check<'a, 'tcx: 'a>(
+    buffer: &mut Buffer,
+    gens: &'a clean::Generics,
+    cx: &'a Context<'tcx>,
+) -> bool {
+    let len_before = buffer.len();
+    write!(buffer, "{}", print_where_clause(gens, cx, 0, true));
+    len_before != buffer.len()
+}
+
 pub(super) fn print_item(
     cx: &mut Context<'_>,
     item: &clean::Item,
@@ -1152,17 +1163,21 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             render_attributes_in_pre(w, it, "");
             write!(
                 w,
-                "{}enum {}{}{}",
+                "{}enum {}{}",
                 it.visibility.print_with_space(it.item_id, cx),
                 it.name.unwrap(),
                 e.generics.print(cx),
-                print_where_clause(&e.generics, cx, 0, true),
             );
+            if !print_where_clause_and_check(w, &e.generics, cx) {
+                // If there wasn't a `where` clause, we add a whitespace.
+                w.write_str(" ");
+            }
+
             let variants_stripped = e.has_stripped_entries();
             if count_variants == 0 && !variants_stripped {
-                w.write_str(" {}");
+                w.write_str("{}");
             } else {
-                w.write_str(" {\n");
+                w.write_str("{\n");
                 let toggle = should_hide_fields(count_variants);
                 if toggle {
                     toggle_open(w, format_args!("{} variants", count_variants));
@@ -1643,13 +1658,21 @@ fn render_union(
     tab: &str,
     cx: &Context<'_>,
 ) {
-    write!(w, "{}union {}", it.visibility.print_with_space(it.item_id, cx), it.name.unwrap());
-    if let Some(g) = g {
-        write!(w, "{}", g.print(cx));
-        write!(w, "{}", print_where_clause(g, cx, 0, true));
+    write!(w, "{}union {}", it.visibility.print_with_space(it.item_id, cx), it.name.unwrap(),);
+
+    let where_displayed = g
+        .map(|g| {
+            write!(w, "{}", g.print(cx));
+            print_where_clause_and_check(w, g, cx)
+        })
+        .unwrap_or(false);
+
+    // If there wasn't a `where` clause, we add a whitespace.
+    if !where_displayed {
+        w.write_str(" ");
     }
 
-    write!(w, " {{\n{}", tab);
+    write!(w, "{{\n{}", tab);
     let count_fields =
         fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
     let toggle = should_hide_fields(count_fields);
@@ -1701,10 +1724,14 @@ fn render_struct(
     }
     match ty {
         CtorKind::Fictive => {
-            if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx, 0, true),)
+            let where_diplayed = g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false);
+
+            // If there wasn't a `where` clause, we add a whitespace.
+            if !where_diplayed {
+                w.write_str(" {");
+            } else {
+                w.write_str("{");
             }
-            w.write_str(" {");
             let count_fields =
                 fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
             let has_visible_fields = count_fields > 0;
@@ -1759,7 +1786,7 @@ fn render_struct(
             }
             w.write_str(")");
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx, 0, false),)
+                write!(w, "{}", print_where_clause(g, cx, 0, false));
             }
             // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
             if structhead {
@@ -1769,7 +1796,7 @@ fn render_struct(
         CtorKind::Const => {
             // Needed for PhantomData.
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx, 0, false),)
+                write!(w, "{}", print_where_clause(g, cx, 0, false));
             }
             w.write_str(";");
         }
diff --git a/src/test/rustdoc/whitespace-after-where-clause.enum.html b/src/test/rustdoc/whitespace-after-where-clause.enum.html
new file mode 100644
index 00000000000..9e5bf45ae7d
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.enum.html
@@ -0,0 +1,4 @@
+<div class="docblock item-decl"><pre class="rust enum"><code>pub enum Cow&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a&gt; <span class="where fmt-newline">where<br />&#160;&#160;&#160;&#160;B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt;,&#160;</span>{
+    Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&amp;'a </a>B),
+    Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.enum2.html b/src/test/rustdoc/whitespace-after-where-clause.enum2.html
new file mode 100644
index 00000000000..6bc47beaed1
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.enum2.html
@@ -0,0 +1,4 @@
+<div class="docblock item-decl"><pre class="rust enum"><code>pub enum Cow2&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt; + 'a&gt; {
+    Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&amp;'a </a>B),
+    Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.rs b/src/test/rustdoc/whitespace-after-where-clause.rs
new file mode 100644
index 00000000000..c36386a2aa2
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.rs
@@ -0,0 +1,77 @@
+// This test ensures there is no whitespace before the first brace of
+// trait, enum, struct and union items when they have a where clause.
+
+#![crate_name = "foo"]
+
+// @has 'foo/trait.ToOwned.html'
+// @snapshot trait - '//*[@class="docblock item-decl"]'
+pub trait ToOwned<T>
+where T: Clone
+{
+    type Owned;
+    fn to_owned(&self) -> Self::Owned;
+    fn whatever(&self) -> T;
+}
+
+// @has 'foo/trait.ToOwned2.html'
+// @snapshot trait2 - '//*[@class="docblock item-decl"]'
+// There should be a whitespace before `{` in this case!
+pub trait ToOwned2<T: Clone> {
+    type Owned;
+    fn to_owned(&self) -> Self::Owned;
+    fn whatever(&self) -> T;
+}
+
+// @has 'foo/enum.Cow.html'
+// @snapshot enum - '//*[@class="docblock item-decl"]'
+pub enum Cow<'a, B: ?Sized + 'a>
+where
+    B: ToOwned<Clone>,
+{
+    Borrowed(&'a B),
+    Whatever(u32),
+}
+
+// @has 'foo/enum.Cow2.html'
+// @snapshot enum2 - '//*[@class="docblock item-decl"]'
+// There should be a whitespace before `{` in this case!
+pub enum Cow2<'a, B: ?Sized + ToOwned<Clone> + 'a> {
+    Borrowed(&'a B),
+    Whatever(u32),
+}
+
+// @has 'foo/struct.Struct.html'
+// @snapshot struct - '//*[@class="docblock item-decl"]'
+pub struct Struct<'a, B: ?Sized + 'a>
+where
+    B: ToOwned<Clone>,
+{
+    pub a: &'a B,
+    pub b: u32,
+}
+
+// @has 'foo/struct.Struct2.html'
+// @snapshot struct2 - '//*[@class="docblock item-decl"]'
+// There should be a whitespace before `{` in this case!
+pub struct Struct2<'a, B: ?Sized + ToOwned<Clone> + 'a> {
+    pub a: &'a B,
+    pub b: u32,
+}
+
+// @has 'foo/union.Union.html'
+// @snapshot union - '//*[@class="docblock item-decl"]'
+pub union Union<'a, B: ?Sized + 'a>
+where
+    B: ToOwned<Clone>,
+{
+    a: &'a B,
+    b: u32,
+}
+
+// @has 'foo/union.Union2.html'
+// @snapshot union2 - '//*[@class="docblock item-decl"]'
+// There should be a whitespace before `{` in this case!
+pub union Union2<'a, B: ?Sized + ToOwned<Clone> + 'a> {
+    a: &'a B,
+    b: u32,
+}
diff --git a/src/test/rustdoc/whitespace-after-where-clause.struct.html b/src/test/rustdoc/whitespace-after-where-clause.struct.html
new file mode 100644
index 00000000000..236cc3b30d0
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.struct.html
@@ -0,0 +1,4 @@
+<div class="docblock item-decl"><pre class="rust struct"><code>pub struct Struct&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a&gt; <span class="where fmt-newline">where<br />&#160;&#160;&#160;&#160;B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt;,&#160;</span>{
+    pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&amp;'a </a>B,
+    pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.struct2.html b/src/test/rustdoc/whitespace-after-where-clause.struct2.html
new file mode 100644
index 00000000000..47f5c6ba9c8
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.struct2.html
@@ -0,0 +1,4 @@
+<div class="docblock item-decl"><pre class="rust struct"><code>pub struct Struct2&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt; + 'a&gt; {
+    pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&amp;'a </a>B,
+    pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait.html b/src/test/rustdoc/whitespace-after-where-clause.trait.html
new file mode 100644
index 00000000000..98f03b837b5
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.trait.html
@@ -0,0 +1,6 @@
+<div class="docblock item-decl"><pre class="rust trait"><code>pub trait ToOwned&lt;T&gt; <span class="where fmt-newline">where<br />&#160;&#160;&#160;&#160;T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,&#160;</span>{
+    type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
+
+    fn <a href="#tymethod.to_owned" class="fnname">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
+<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fnname">whatever</a>(&amp;self) -&gt; T;
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait2.html b/src/test/rustdoc/whitespace-after-where-clause.trait2.html
new file mode 100644
index 00000000000..35052869e76
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.trait2.html
@@ -0,0 +1,6 @@
+<div class="docblock item-decl"><pre class="rust trait"><code>pub trait ToOwned2&lt;T:&#160;<a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt; {
+    type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
+
+    fn <a href="#tymethod.to_owned" class="fnname">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
+<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fnname">whatever</a>(&amp;self) -&gt; T;
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.union.html b/src/test/rustdoc/whitespace-after-where-clause.union.html
new file mode 100644
index 00000000000..97e1bbcf339
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.union.html
@@ -0,0 +1,3 @@
+<div class="docblock item-decl"><pre class="rust union"><code>pub union Union&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a&gt; <span class="where fmt-newline">where<br />&#160;&#160;&#160;&#160;B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt;,&#160;</span>{
+    /* private fields */
+}</code></pre></div>
diff --git a/src/test/rustdoc/whitespace-after-where-clause.union2.html b/src/test/rustdoc/whitespace-after-where-clause.union2.html
new file mode 100644
index 00000000000..6c752a8b4c5
--- /dev/null
+++ b/src/test/rustdoc/whitespace-after-where-clause.union2.html
@@ -0,0 +1,3 @@
+<div class="docblock item-decl"><pre class="rust union"><code>pub union Union2&lt;'a, B:&#160;?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a>&lt;dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt; + 'a&gt; {
+    /* private fields */
+}</code></pre></div>
diff --git a/src/test/ui/aligned_enum_cast.rs b/src/test/ui/aligned_enum_cast.rs
index 4b5776a6aa8..1ddf127172e 100644
--- a/src/test/ui/aligned_enum_cast.rs
+++ b/src/test/ui/aligned_enum_cast.rs
@@ -11,5 +11,15 @@ enum Aligned {
 fn main() {
     let aligned = Aligned::Zero;
     let fo = aligned as u8;
-    println!("foo {}",fo);
+    println!("foo {}", fo);
+    assert_eq!(fo, 0);
+    println!("{}", tou8(Aligned::Zero));
+    assert_eq!(tou8(Aligned::Zero), 0);
+}
+
+#[inline(never)]
+fn tou8(al: Aligned) -> u8 {
+    // Cast behind a function call so ConstProp does not see it
+    // (so that we can test codegen).
+    al as u8
 }
diff --git a/src/test/ui/layout/issue-96185-overaligned-enum.rs b/src/test/ui/layout/issue-96185-overaligned-enum.rs
new file mode 100644
index 00000000000..ae1e6b012c3
--- /dev/null
+++ b/src/test/ui/layout/issue-96185-overaligned-enum.rs
@@ -0,0 +1,19 @@
+// normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN"
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+// This cannot use `Scalar` abi since there is padding.
+#[rustc_layout(debug)]
+#[repr(align(8))]
+pub enum Aligned1 { //~ ERROR: layout_of
+    Zero = 0,
+    One = 1,
+}
+
+// This should use `Scalar` abi.
+#[rustc_layout(debug)]
+#[repr(align(1))]
+pub enum Aligned2 { //~ ERROR: layout_of
+    Zero = 0,
+    One = 1,
+}
diff --git a/src/test/ui/layout/issue-96185-overaligned-enum.stderr b/src/test/ui/layout/issue-96185-overaligned-enum.stderr
new file mode 100644
index 00000000000..8dc364fa7c9
--- /dev/null
+++ b/src/test/ui/layout/issue-96185-overaligned-enum.stderr
@@ -0,0 +1,172 @@
+error: layout_of(Aligned1) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size(0 bytes),
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               tag_encoding: Direct,
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align(8 bytes),
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size(8 bytes),
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align(8 bytes),
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size(8 bytes),
+                   },
+               ],
+           },
+           abi: Aggregate {
+               sized: true,
+           },
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align(8 bytes),
+               pref: $PREF_ALIGN,
+           },
+           size: Size(8 bytes),
+       }
+  --> $DIR/issue-96185-overaligned-enum.rs:8:1
+   |
+LL | pub enum Aligned1 {
+   | ^^^^^^^^^^^^^^^^^
+
+error: layout_of(Aligned2) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size(0 bytes),
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               tag_encoding: Direct,
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align(1 bytes),
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size(1 bytes),
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align(1 bytes),
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size(1 bytes),
+                   },
+               ],
+           },
+           abi: Scalar(
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align(1 bytes),
+               pref: $PREF_ALIGN,
+           },
+           size: Size(1 bytes),
+       }
+  --> $DIR/issue-96185-overaligned-enum.rs:16:1
+   |
+LL | pub enum Aligned2 {
+   | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+