From 5479fe5f70bb32f037ff97de03ed185bdf2f54b7 Mon Sep 17 00:00:00 2001
From: Ayush Singh <ayushsingh1325@gmail.com>
Date: Thu, 8 Dec 2022 18:12:15 +0530
Subject: [PATCH 01/11] Add read_to_end for AnonPipe

Add `read_to_end` method for `sys::{target}::pipe::AnonPipe`. This allows
having a more optimized version of `read_to_end` for ChildStdout.

Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
---
 library/std/src/process.rs              | 4 ++++
 library/std/src/sys/unix/pipe.rs        | 4 ++++
 library/std/src/sys/unsupported/pipe.rs | 4 ++++
 library/std/src/sys/windows/pipe.rs     | 6 +++++-
 4 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 400d25beb26..1ff7bba85d2 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -362,6 +362,10 @@ impl Read for ChildStdout {
     fn is_read_vectored(&self) -> bool {
         self.inner.is_read_vectored()
     }
+
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
+        self.inner.read_to_end(buf)
+    }
 }
 
 impl AsInner<AnonPipe> for ChildStdout {
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
index a56c275c942..a744d0ab640 100644
--- a/library/std/src/sys/unix/pipe.rs
+++ b/library/std/src/sys/unix/pipe.rs
@@ -58,6 +58,10 @@ impl AnonPipe {
         self.0.is_read_vectored()
     }
 
+    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+        self.0.read_to_end(buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs
index 25514c2322f..75ce7546796 100644
--- a/library/std/src/sys/unsupported/pipe.rs
+++ b/library/std/src/sys/unsupported/pipe.rs
@@ -15,6 +15,10 @@ impl AnonPipe {
         self.0
     }
 
+    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+        self.0
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         self.0
     }
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 9f26acc4520..7b25edaa556 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -1,7 +1,7 @@
 use crate::os::windows::prelude::*;
 
 use crate::ffi::OsStr;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut, Read};
 use crate::mem;
 use crate::path::Path;
 use crate::ptr;
@@ -261,6 +261,10 @@ impl AnonPipe {
         self.inner.is_read_vectored()
     }
 
+    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+        self.handle().read_to_end(buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         unsafe {
             let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;

From a94793d8d17e4cfe2e727c30c36e174b8d6b6ee3 Mon Sep 17 00:00:00 2001
From: Ayush Singh <ayushsingh1325@gmail.com>
Date: Thu, 8 Dec 2022 18:22:33 +0530
Subject: [PATCH 02/11] Implement blocking output

This allows decoupling `Command::spawn` and `Command::output`. This is
useful for targets which do support launching programs in blocking mode
but do not support multitasking (Eg: UEFI).

This was originally conceived when working on https://github.com/rust-lang/rust/pull/100316

Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
---
 library/std/src/process.rs                    |  6 ++--
 .../src/sys/unix/process/process_fuchsia.rs   |  5 +++
 .../std/src/sys/unix/process/process_unix.rs  |  5 +++
 .../sys/unix/process/process_unsupported.rs   |  4 +++
 .../src/sys/unix/process/process_vxworks.rs   |  5 +++
 library/std/src/sys/unsupported/pipe.rs       |  2 +-
 library/std/src/sys/unsupported/process.rs    |  4 +++
 library/std/src/sys/windows/process.rs        |  5 +++
 library/std/src/sys_common/process.rs         | 31 ++++++++++++++++++-
 9 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 1ff7bba85d2..17aff342c15 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -911,10 +911,8 @@ impl Command {
     /// ```
     #[stable(feature = "process", since = "1.0.0")]
     pub fn output(&mut self) -> io::Result<Output> {
-        self.inner
-            .spawn(imp::Stdio::MakePipe, false)
-            .map(Child::from_inner)
-            .and_then(|p| p.wait_with_output())
+        let (status, stdout, stderr) = self.inner.output()?;
+        Ok(Output { status: ExitStatus(status), stdout, stderr })
     }
 
     /// Executes a command as a child process, waiting for it to finish and
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index 66ea3db2015..4c99d758c93 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -35,6 +35,11 @@ impl Command {
         Ok((Process { handle: Handle::new(process_handle) }, ours))
     }
 
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+        crate::sys_common::process::wait_with_output(proc, pipes)
+    }
+
     pub fn exec(&mut self, default: Stdio) -> io::Error {
         if self.saw_nul() {
             return io::const_io_error!(
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 56a805cef73..45616850a37 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -132,6 +132,11 @@ impl Command {
         }
     }
 
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+        crate::sys_common::process::wait_with_output(proc, pipes)
+    }
+
     // Attempts to fork the process. If successful, returns Ok((0, -1))
     // in the child, and Ok((child_pid, -1)) in the parent.
     #[cfg(not(target_os = "linux"))]
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
index 72f9f3f9ca7..f28ca58d020 100644
--- a/library/std/src/sys/unix/process/process_unsupported.rs
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -20,6 +20,10 @@ impl Command {
         unsupported()
     }
 
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        unsupported()
+    }
+
     pub fn exec(&mut self, _default: Stdio) -> io::Error {
         unsupported_err()
     }
diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs
index 200ef671967..f549d37c301 100644
--- a/library/std/src/sys/unix/process/process_vxworks.rs
+++ b/library/std/src/sys/unix/process/process_vxworks.rs
@@ -108,6 +108,11 @@ impl Command {
         }
     }
 
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+        crate::sys_common::process::wait_with_output(proc, pipes)
+    }
+
     pub fn exec(&mut self, default: Stdio) -> io::Error {
         let ret = Command::spawn(self, default, false);
         match ret {
diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs
index 75ce7546796..0bba673b458 100644
--- a/library/std/src/sys/unsupported/pipe.rs
+++ b/library/std/src/sys/unsupported/pipe.rs
@@ -15,7 +15,7 @@ impl AnonPipe {
         self.0
     }
 
-    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+    pub fn read_to_end(&self, _buf: &mut Vec<u8>) -> io::Result<usize> {
         self.0
     }
 
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 633f17c054b..a494f2d6b4c 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -75,6 +75,10 @@ impl Command {
     ) -> io::Result<(Process, StdioPipes)> {
         unsupported()
     }
+
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        unsupported()
+    }
 }
 
 impl From<AnonPipe> for Stdio {
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 31e9b34fb9e..10bc949e1f4 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -351,6 +351,11 @@ impl Command {
             ))
         }
     }
+
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+        crate::sys_common::process::wait_with_output(proc, pipes)
+    }
 }
 
 impl fmt::Debug for Command {
diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs
index 9f978789a62..ae11412067b 100644
--- a/library/std/src/sys_common/process.rs
+++ b/library/std/src/sys_common/process.rs
@@ -4,7 +4,9 @@
 use crate::collections::BTreeMap;
 use crate::env;
 use crate::ffi::{OsStr, OsString};
-use crate::sys::process::EnvKey;
+use crate::io;
+use crate::sys::pipe::read2;
+use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
 
 // Stores a set of changes to an environment
 #[derive(Clone, Debug)]
@@ -117,3 +119,30 @@ impl<'a> ExactSizeIterator for CommandEnvs<'a> {
         self.iter.is_empty()
     }
 }
+
+pub fn wait_with_output(
+    mut process: Process,
+    mut pipes: StdioPipes,
+) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+    drop(pipes.stdin.take());
+
+    let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
+    match (pipes.stdout.take(), pipes.stderr.take()) {
+        (None, None) => {}
+        (Some(out), None) => {
+            let res = out.read_to_end(&mut stdout);
+            res.unwrap();
+        }
+        (None, Some(err)) => {
+            let res = err.read_to_end(&mut stderr);
+            res.unwrap();
+        }
+        (Some(out), Some(err)) => {
+            let res = read2(out, &mut stdout, err, &mut stderr);
+            res.unwrap();
+        }
+    }
+
+    let status = process.wait()?;
+    Ok((status, stdout, stderr))
+}

From 23a34c18f7b2ce8b20fed717ef48e2be1776246b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= <mkroening@posteo.net>
Date: Sun, 11 Dec 2022 12:19:17 +0100
Subject: [PATCH 03/11] bootstrap: Allow installing llvm-tools

---
 src/bootstrap/builder.rs | 1 +
 src/bootstrap/install.rs | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 8ee6d49da8f..3f551dc119b 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -744,6 +744,7 @@ impl<'a> Builder<'a> {
                 install::RustDemangler,
                 install::Clippy,
                 install::Miri,
+                install::LlvmTools,
                 install::Analysis,
                 install::Src,
                 install::Rustc
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 7672b7c9135..c53d0d7e4cb 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -205,6 +205,12 @@ install!((self, builder, _config),
             .expect("missing miri");
         install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
     };
+    LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, {
+        let tarball = builder
+            .ensure(dist::LlvmTools { target: self.target })
+            .expect("missing llvm-tools");
+        install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball);
+    };
     Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, {
         if let Some(tarball) = builder.ensure(dist::Rustfmt {
             compiler: self.compiler,

From f309934b5ee074a74229546d859252dd7e946b2d Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Fri, 16 Dec 2022 10:11:37 -0700
Subject: [PATCH 04/11] rustdoc: clean up margin CSS for scraped examples

* This stops applying a margin to the additional example links.
  Because these links are `display: inline`, it doesn't actually do anything.
* This switches from using a margin-bottom with a special exception for
  the examples themselves, plus an additional margin on the hide button,
  to instead using just margin-top on the examples, with an exception for
  the first one.

No user-visible changes should result from this.
---
 src/librustdoc/html/static/css/rustdoc.css | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index a527e9feaec..7b0e6439cfe 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -2009,7 +2009,6 @@ in storage.js
 
 .more-examples-toggle .hide-more {
 	margin-left: 25px;
-	margin-bottom: 5px;
 	cursor: pointer;
 }
 
@@ -2037,18 +2036,14 @@ in storage.js
 	height: 100%;
 }
 
-.more-scraped-examples .scraped-example {
-	margin-bottom: 20px;
-}
-
-.more-scraped-examples .scraped-example:last-child {
-	margin-bottom: 0;
-}
-
-.example-links a {
+.more-scraped-examples .scraped-example, .example-links {
 	margin-top: 20px;
 }
 
+.more-scraped-examples .scraped-example:first-child {
+	margin-top: 5px;
+}
+
 .example-links ul {
 	margin-bottom: 0;
 }

From fe528829866f08bac8cf6615085ec4609db352e9 Mon Sep 17 00:00:00 2001
From: Ezra Shaw <ezrasure@outlook.com>
Date: Fri, 16 Dec 2022 18:24:14 +1300
Subject: [PATCH 05/11] docs: add long error explanation for error E0320

---
 compiler/rustc_error_codes/src/error_codes.rs |  2 +-
 .../src/error_codes/E0320.md                  | 27 +++++++++++++++++++
 .../dropck_no_diverge_on_nonregular_1.stderr  |  1 +
 .../dropck_no_diverge_on_nonregular_2.stderr  |  1 +
 .../dropck_no_diverge_on_nonregular_3.stderr  |  1 +
 ...-38591-non-regular-dropck-recursion.stderr |  1 +
 6 files changed, 32 insertions(+), 1 deletion(-)
 create mode 100644 compiler/rustc_error_codes/src/error_codes/E0320.md

diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 31a709c36d4..67c512e98d6 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -163,6 +163,7 @@ E0311: include_str!("./error_codes/E0311.md"),
 E0312: include_str!("./error_codes/E0312.md"),
 E0316: include_str!("./error_codes/E0316.md"),
 E0317: include_str!("./error_codes/E0317.md"),
+E0320: include_str!("./error_codes/E0320.md"),
 E0321: include_str!("./error_codes/E0321.md"),
 E0322: include_str!("./error_codes/E0322.md"),
 E0323: include_str!("./error_codes/E0323.md"),
@@ -575,7 +576,6 @@ E0791: include_str!("./error_codes/E0791.md"),
 //  E0314, // closure outlives stack frame
 //  E0315, // cannot invoke closure outside of its lifetime
 //  E0319, // trait impls for defaulted traits allowed just for structs/enums
-    E0320, // recursive overflow during dropck
 //  E0372, // coherence not object safe
     E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
            // between structures with the same definition
diff --git a/compiler/rustc_error_codes/src/error_codes/E0320.md b/compiler/rustc_error_codes/src/error_codes/E0320.md
new file mode 100644
index 00000000000..e6e1b7c19a5
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0320.md
@@ -0,0 +1,27 @@
+Recursion limit reached while creating drop-check rules.
+
+Example of erroneous code:
+
+```compile_fail,E0320
+enum A<T> {
+    B,
+    C(T, Box<A<(T, T)>>)
+}
+
+fn foo<T>() {
+    A::<T>::B; // error: overflow while adding drop-check rules for A<T>
+}
+```
+
+The Rust compiler must be able to reason about how a type is [`Drop`]ped, and
+by extension the types of its fields, to be able to generate the glue to
+properly drop a value. The code example above shows a type where this inference
+is impossible because it is recursive. Note that this is *not* the same as
+[E0072](E0072.html), where a type has an infinite size; the type here has a
+finite size but any attempt to `Drop` it would recurse infinitely. For more
+information, read [the `Drop` docs](../std/ops/trait.Drop.html).
+
+It is not possible to define a type with recursive drop-check rules. All such
+recursion must be removed.
+
+[`Drop`]: ../std/ops/trait.Drop.html
diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr
index c447e2f7987..3e39d15f9b0 100644
--- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr
+++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr
@@ -8,3 +8,4 @@ LL |     let ft =
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0320`.
diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr
index cd4706dd903..dbb74354471 100644
--- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr
+++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr
@@ -8,3 +8,4 @@ LL |     let ft =
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0320`.
diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr
index 18cd1b6cd41..deaf116b647 100644
--- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr
+++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr
@@ -16,3 +16,4 @@ LL |         Some(Wrapper::Simple::<u32>);
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0320`.
diff --git a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr
index 1da29be43db..002dfe115b0 100644
--- a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr
+++ b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr
@@ -8,3 +8,4 @@ LL | fn f(x: S<u32>) {}
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0320`.

From 3d849ae44c0eabea326bd8ccccc3a6ab702115ff Mon Sep 17 00:00:00 2001
From: Jakob Degen <jakob.e.degen@gmail.com>
Date: Fri, 16 Dec 2022 19:41:40 -0800
Subject: [PATCH 06/11] Support call and drop terminators in custom mir

---
 .../src/build/custom/parse/instruction.rs     |  49 ++++++++
 library/core/src/intrinsics/mir.rs            |  39 ++++++-
 ...terminators.assert_nonzero.built.after.mir |  17 +++
 .../terminators.direct_call.built.after.mir   |  16 +++
 .../terminators.drop_first.built.after.mir    |  13 +++
 .../terminators.drop_second.built.after.mir   |  13 +++
 .../terminators.indirect_call.built.after.mir |  13 +++
 .../mir-opt/building/custom/terminators.rs    | 108 ++++++++++++++++++
 8 files changed, 266 insertions(+), 2 deletions(-)
 create mode 100644 src/test/mir-opt/building/custom/terminators.assert_nonzero.built.after.mir
 create mode 100644 src/test/mir-opt/building/custom/terminators.direct_call.built.after.mir
 create mode 100644 src/test/mir-opt/building/custom/terminators.drop_first.built.after.mir
 create mode 100644 src/test/mir-opt/building/custom/terminators.drop_second.built.after.mir
 create mode 100644 src/test/mir-opt/building/custom/terminators.indirect_call.built.after.mir
 create mode 100644 src/test/mir-opt/building/custom/terminators.rs

diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index 2f26499a3b6..ecc3e4de8d1 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -42,6 +42,29 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             @call("mir_goto", args) => {
                 Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
             },
+            @call("mir_unreachable", _args) => {
+                Ok(TerminatorKind::Unreachable)
+            },
+            @call("mir_drop", args) => {
+                Ok(TerminatorKind::Drop {
+                    place: self.parse_place(args[0])?,
+                    target: self.parse_block(args[1])?,
+                    unwind: None,
+                })
+            },
+            @call("mir_drop_and_replace", args) => {
+                Ok(TerminatorKind::DropAndReplace {
+                    place: self.parse_place(args[0])?,
+                    value: self.parse_operand(args[1])?,
+                    target: self.parse_block(args[2])?,
+                    unwind: None,
+                })
+            },
+            @call("mir_call", args) => {
+                let destination = self.parse_place(args[0])?;
+                let target = self.parse_block(args[1])?;
+                self.parse_call(args[2], destination, target)
+            },
             ExprKind::Match { scrutinee, arms } => {
                 let discr = self.parse_operand(*scrutinee)?;
                 self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t })
@@ -86,6 +109,32 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
         Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
     }
 
+    fn parse_call(
+        &self,
+        expr_id: ExprId,
+        destination: Place<'tcx>,
+        target: BasicBlock,
+    ) -> PResult<TerminatorKind<'tcx>> {
+        parse_by_kind!(self, expr_id, _, "function call",
+            ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => {
+                let fun = self.parse_operand(*fun)?;
+                let args = args
+                    .iter()
+                    .map(|arg| self.parse_operand(*arg))
+                    .collect::<PResult<Vec<_>>>()?;
+                Ok(TerminatorKind::Call {
+                    func: fun,
+                    args,
+                    destination,
+                    target: Some(target),
+                    cleanup: None,
+                    from_hir_call: *from_hir_call,
+                    fn_span: *fn_span,
+                })
+            },
+        )
+    }
+
     fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
         parse_by_kind!(self, expr_id, _, "rvalue",
             @call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index 0910ce599b7..e08a15571fc 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -44,7 +44,8 @@
 //! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
 //! "runtime", phase = "optimized")] if you don't.
 //!
-//! [dialect docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
+//! [dialect docs]:
+//!     https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
 //!
 //! The input to the [`mir!`] macro is:
 //!
@@ -99,6 +100,30 @@
 //!         Return()
 //!     })
 //! }
+//!
+//! #[custom_mir(dialect = "runtime", phase = "optimized")]
+//! fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
+//!     mir!(
+//!         let unused;
+//!         let popped;
+//!
+//!         {
+//!             Call(unused, pop, Vec::push(v, value))
+//!         }
+//!
+//!         pop = {
+//!             Call(popped, drop, Vec::pop(v))
+//!         }
+//!
+//!         drop = {
+//!             Drop(popped, ret)
+//!         }
+//!
+//!         ret = {
+//!             Return()
+//!         }
+//!     )
+//! }
 //! ```
 //!
 //! We can also set off compilation failures that happen in sufficiently late stages of the
@@ -195,10 +220,16 @@
 //!
 //! #### Terminators
 //!
-//!  - [`Goto`] and [`Return`] have associated functions.
+//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
+//! are no resume and abort terminators, and terminators that might unwind do not have any way to
+//! indicate the unwind block.
+//!
+//!  - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] have associated functions.
 //!  - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
 //!     - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
 //!       otherwise branch.
+//!  - [`Call`] has an associated function as well. The third argument of this function is a normal
+//!    function call expresion, for example `my_other_function(a, 5)`.
 //!
 
 #![unstable(
@@ -223,6 +254,10 @@ macro_rules! define {
 
 define!("mir_return", fn Return() -> BasicBlock);
 define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
+define!("mir_unreachable", fn Unreachable() -> BasicBlock);
+define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
+define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
+define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
 define!("mir_retag", fn Retag<T>(place: T));
 define!("mir_retag_raw", fn RetagRaw<T>(place: T));
 define!("mir_move", fn Move<T>(place: T) -> T);
diff --git a/src/test/mir-opt/building/custom/terminators.assert_nonzero.built.after.mir b/src/test/mir-opt/building/custom/terminators.assert_nonzero.built.after.mir
new file mode 100644
index 00000000000..a1a27226b4e
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.assert_nonzero.built.after.mir
@@ -0,0 +1,17 @@
+// MIR for `assert_nonzero` after built
+
+fn assert_nonzero(_1: i32) -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:27
+
+    bb0: {
+        switchInt(_1) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/terminators.rs:+3:13: +6:14
+    }
+
+    bb1: {
+        unreachable;                     // scope 0 at $DIR/terminators.rs:+10:13: +10:26
+    }
+
+    bb2: {
+        return;                          // scope 0 at $DIR/terminators.rs:+14:13: +14:21
+    }
+}
diff --git a/src/test/mir-opt/building/custom/terminators.direct_call.built.after.mir b/src/test/mir-opt/building/custom/terminators.direct_call.built.after.mir
new file mode 100644
index 00000000000..1b2345a965e
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.direct_call.built.after.mir
@@ -0,0 +1,16 @@
+// MIR for `direct_call` after built
+
+fn direct_call(_1: i32) -> i32 {
+    let mut _0: i32;                     // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:30
+
+    bb0: {
+        _0 = ident::<i32>(_1) -> bb1;    // scope 0 at $DIR/terminators.rs:+3:13: +3:42
+                                         // mir::Constant
+                                         // + span: $DIR/terminators.rs:15:33: 15:38
+                                         // + literal: Const { ty: fn(i32) -> i32 {ident::<i32>}, val: Value(<ZST>) }
+    }
+
+    bb1: {
+        return;                          // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+    }
+}
diff --git a/src/test/mir-opt/building/custom/terminators.drop_first.built.after.mir b/src/test/mir-opt/building/custom/terminators.drop_first.built.after.mir
new file mode 100644
index 00000000000..c903e594696
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.drop_first.built.after.mir
@@ -0,0 +1,13 @@
+// MIR for `drop_first` after built
+
+fn drop_first(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/terminators.rs:+0:59: +0:59
+
+    bb0: {
+        replace(_1 <- move _2) -> bb1;   // scope 0 at $DIR/terminators.rs:+3:13: +3:49
+    }
+
+    bb1: {
+        return;                          // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+    }
+}
diff --git a/src/test/mir-opt/building/custom/terminators.drop_second.built.after.mir b/src/test/mir-opt/building/custom/terminators.drop_second.built.after.mir
new file mode 100644
index 00000000000..f14246f2d12
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.drop_second.built.after.mir
@@ -0,0 +1,13 @@
+// MIR for `drop_second` after built
+
+fn drop_second(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/terminators.rs:+0:60: +0:60
+
+    bb0: {
+        drop(_2) -> bb1;                 // scope 0 at $DIR/terminators.rs:+3:13: +3:30
+    }
+
+    bb1: {
+        return;                          // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+    }
+}
diff --git a/src/test/mir-opt/building/custom/terminators.indirect_call.built.after.mir b/src/test/mir-opt/building/custom/terminators.indirect_call.built.after.mir
new file mode 100644
index 00000000000..2f1b14069ab
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.indirect_call.built.after.mir
@@ -0,0 +1,13 @@
+// MIR for `indirect_call` after built
+
+fn indirect_call(_1: i32, _2: fn(i32) -> i32) -> i32 {
+    let mut _0: i32;                     // return place in scope 0 at $DIR/terminators.rs:+0:48: +0:51
+
+    bb0: {
+        _0 = _2(_1) -> bb1;              // scope 0 at $DIR/terminators.rs:+3:13: +3:38
+    }
+
+    bb1: {
+        return;                          // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+    }
+}
diff --git a/src/test/mir-opt/building/custom/terminators.rs b/src/test/mir-opt/building/custom/terminators.rs
new file mode 100644
index 00000000000..c23233fcf9a
--- /dev/null
+++ b/src/test/mir-opt/building/custom/terminators.rs
@@ -0,0 +1,108 @@
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+fn ident<T>(t: T) -> T {
+    t
+}
+
+// EMIT_MIR terminators.direct_call.built.after.mir
+#[custom_mir(dialect = "built")]
+fn direct_call(x: i32) -> i32 {
+    mir!(
+        {
+            Call(RET, retblock, ident(x))
+        }
+
+        retblock = {
+            Return()
+        }
+    )
+}
+
+// EMIT_MIR terminators.indirect_call.built.after.mir
+#[custom_mir(dialect = "built")]
+fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 {
+    mir!(
+        {
+            Call(RET, retblock, f(x))
+        }
+
+        retblock = {
+            Return()
+        }
+    )
+}
+
+struct WriteOnDrop<'a>(&'a mut i32, i32);
+
+impl<'a> Drop for WriteOnDrop<'a> {
+    fn drop(&mut self) {
+        *self.0 = self.1;
+    }
+}
+
+// EMIT_MIR terminators.drop_first.built.after.mir
+#[custom_mir(dialect = "built")]
+fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
+    mir!(
+        {
+            DropAndReplace(a, Move(b), retblock)
+        }
+
+        retblock = {
+            Return()
+        }
+    )
+}
+
+// EMIT_MIR terminators.drop_second.built.after.mir
+#[custom_mir(dialect = "built")]
+fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
+    mir!(
+        {
+            Drop(b, retblock)
+        }
+
+        retblock = {
+            Return()
+        }
+    )
+}
+
+// EMIT_MIR terminators.assert_nonzero.built.after.mir
+#[custom_mir(dialect = "built")]
+fn assert_nonzero(a: i32) {
+    mir!(
+        {
+            match a {
+                0 => unreachable,
+                _ => retblock
+            }
+        }
+
+        unreachable = {
+            Unreachable()
+        }
+
+        retblock = {
+            Return()
+        }
+    )
+}
+
+fn main() {
+    assert_eq!(direct_call(5), 5);
+    assert_eq!(indirect_call(5, ident), 5);
+
+    let mut a = 0;
+    let mut b = 0;
+    drop_first(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
+    assert_eq!((a, b), (1, 0));
+
+    let mut a = 0;
+    let mut b = 0;
+    drop_second(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
+    assert_eq!((a, b), (0, 1));
+}

From f7bfc4879390117e850da74ad73eb9f9df588350 Mon Sep 17 00:00:00 2001
From: The 8472 <git@infinite-source.de>
Date: Tue, 22 Nov 2022 00:25:43 +0100
Subject: [PATCH 07/11] optimize line-by-line style checks in tidy

---
 src/tools/tidy/src/style.rs | 59 ++++++++++++++++++++++---------------
 1 file changed, 35 insertions(+), 24 deletions(-)

diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index e3a094caf91..f91e38262f6 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -17,7 +17,7 @@
 //! `// ignore-tidy-CHECK-NAME`.
 
 use crate::walk::{filter_dirs, walk};
-use regex::Regex;
+use regex::{Regex, RegexSet};
 use std::path::Path;
 
 /// Error code markdown is restricted to 80 columns because they can be
@@ -225,6 +225,7 @@ pub fn check(path: &Path, bad: &mut bool) {
         .chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:x}", v)))
         .chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:X}", v)))
         .collect();
+    let problematic_regex = RegexSet::new(problematic_consts_strings.as_slice()).unwrap();
     walk(path, &mut skip, &mut |entry, contents| {
         let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
@@ -281,7 +282,27 @@ pub fn check(path: &Path, bad: &mut bool) {
         let mut trailing_new_lines = 0;
         let mut lines = 0;
         let mut last_safety_comment = false;
+        let is_test = file.components().any(|c| c.as_os_str() == "tests");
+        // scanning the whole file for multiple needles at once is more efficient than
+        // executing lines times needles separate searches.
+        let any_problematic_line = problematic_regex.is_match(contents);
         for (i, line) in contents.split('\n').enumerate() {
+            if line.is_empty() {
+                if i == 0 {
+                    leading_new_lines = true;
+                }
+                trailing_new_lines += 1;
+                continue;
+            } else {
+                trailing_new_lines = 0;
+            }
+
+            let trimmed = line.trim();
+
+            if !trimmed.starts_with("//") {
+                lines += 1;
+            }
+
             let mut err = |msg: &str| {
                 tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
             };
@@ -308,28 +329,29 @@ pub fn check(path: &Path, bad: &mut bool) {
                 suppressible_tidy_err!(err, skip_cr, "CR character");
             }
             if filename != "style.rs" {
-                if line.contains("TODO") {
+                if trimmed.contains("TODO") {
                     err("TODO is deprecated; use FIXME")
                 }
-                if line.contains("//") && line.contains(" XXX") {
+                if trimmed.contains("//") && trimmed.contains(" XXX") {
                     err("XXX is deprecated; use FIXME")
                 }
-                for s in problematic_consts_strings.iter() {
-                    if line.contains(s) {
-                        err("Don't use magic numbers that spell things (consider 0x12345678)");
+                if any_problematic_line {
+                    for s in problematic_consts_strings.iter() {
+                        if trimmed.contains(s) {
+                            err("Don't use magic numbers that spell things (consider 0x12345678)");
+                        }
                     }
                 }
             }
-            let is_test = || file.components().any(|c| c.as_os_str() == "tests");
             // for now we just check libcore
-            if line.contains("unsafe {") && !line.trim().starts_with("//") && !last_safety_comment {
-                if file.components().any(|c| c.as_os_str() == "core") && !is_test() {
+            if trimmed.contains("unsafe {") && !trimmed.starts_with("//") && !last_safety_comment {
+                if file.components().any(|c| c.as_os_str() == "core") && !is_test {
                     suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe");
                 }
             }
-            if line.contains("// SAFETY:") {
+            if trimmed.contains("// SAFETY:") {
                 last_safety_comment = true;
-            } else if line.trim().starts_with("//") || line.trim().is_empty() {
+            } else if trimmed.starts_with("//") || trimmed.is_empty() {
                 // keep previous value
             } else {
                 last_safety_comment = false;
@@ -337,7 +359,8 @@ pub fn check(path: &Path, bad: &mut bool) {
             if (line.starts_with("// Copyright")
                 || line.starts_with("# Copyright")
                 || line.starts_with("Copyright"))
-                && (line.contains("Rust Developers") || line.contains("Rust Project Developers"))
+                && (trimmed.contains("Rust Developers")
+                    || trimmed.contains("Rust Project Developers"))
             {
                 suppressible_tidy_err!(
                     err,
@@ -351,18 +374,6 @@ pub fn check(path: &Path, bad: &mut bool) {
             if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                 err(LLVM_UNREACHABLE_INFO);
             }
-            if line.is_empty() {
-                if i == 0 {
-                    leading_new_lines = true;
-                }
-                trailing_new_lines += 1;
-            } else {
-                trailing_new_lines = 0;
-            }
-
-            if !line.trim().starts_with("//") {
-                lines += 1;
-            }
         }
         if leading_new_lines {
             let mut err = |_| {

From 5620afc4e088beb299dfdcc3c3a903ac1f80cc9f Mon Sep 17 00:00:00 2001
From: The 8472 <git@infinite-source.de>
Date: Tue, 22 Nov 2022 00:30:05 +0100
Subject: [PATCH 08/11] poll tidy threads for completion before waiting

---
 src/tools/tidy/src/main.rs | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index b0b11cafca5..6714c63ee62 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -35,15 +35,26 @@ fn main() {
 
     let bad = std::sync::Arc::new(AtomicBool::new(false));
 
+    let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
+        // poll all threads for completion before awaiting the oldest one
+        for i in (0..handles.len()).rev() {
+            if handles[i].is_finished() {
+                handles.swap_remove_back(i).unwrap().join().unwrap();
+            }
+        }
+
+        while handles.len() >= concurrency.get() {
+            handles.pop_front().unwrap().join().unwrap();
+        }
+    };
+
     scope(|s| {
         let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> =
             VecDeque::with_capacity(concurrency.get());
 
         macro_rules! check {
             ($p:ident $(, $args:expr)* ) => {
-                while handles.len() >= concurrency.get() {
-                    handles.pop_front().unwrap().join().unwrap();
-                }
+                drain_handles(&mut handles);
 
                 let handle = s.spawn(|| {
                     let mut flag = false;
@@ -97,9 +108,8 @@ fn main() {
         check!(alphabetical, &library_path);
 
         let collected = {
-            while handles.len() >= concurrency.get() {
-                handles.pop_front().unwrap().join().unwrap();
-            }
+            drain_handles(&mut handles);
+
             let mut flag = false;
             let r = features::check(&src_path, &compiler_path, &library_path, &mut flag, verbose);
             if flag {

From ab7d76965168f1104488549feaebd24ffa69b3b7 Mon Sep 17 00:00:00 2001
From: The 8472 <git@infinite-source.de>
Date: Tue, 22 Nov 2022 00:38:50 +0100
Subject: [PATCH 09/11] poll rustfmt child processes

---
 src/bootstrap/format.rs | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
index 5e7264fe765..b2f6afead79 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/format.rs
@@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 use std::sync::mpsc::SyncSender;
 
-fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut() {
+fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut(bool) -> bool {
     let mut cmd = Command::new(&rustfmt);
     // avoid the submodule config paths from coming into play,
     // we only allow a single global config for the workspace for now
@@ -23,7 +23,13 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
     let cmd_debug = format!("{:?}", cmd);
     let mut cmd = cmd.spawn().expect("running rustfmt");
     // poor man's async: return a closure that'll wait for rustfmt's completion
-    move || {
+    move |block: bool| -> bool {
+        if !block {
+            match cmd.try_wait() {
+                Ok(Some(_)) => {}
+                _ => return false,
+            }
+        }
         let status = cmd.wait().unwrap();
         if !status.success() {
             eprintln!(
@@ -34,6 +40,7 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
             );
             crate::detail_exit(1);
         }
+        true
     }
 }
 
@@ -146,15 +153,23 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
             let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check);
             children.push_back(child);
 
+            // poll completion before waiting
+            for i in (0..children.len()).rev() {
+                if children[i](false) {
+                    children.swap_remove_back(i);
+                    break;
+                }
+            }
+
             if children.len() >= max_processes {
                 // await oldest child
-                children.pop_front().unwrap()();
+                children.pop_front().unwrap()(true);
             }
         }
 
         // await remaining children
         for mut child in children {
-            child();
+            child(true);
         }
     });
 

From ab2151cbf8270be61db8add6bfb1aea03230803f Mon Sep 17 00:00:00 2001
From: Evan Jones <ej@evanjones.ca>
Date: Sat, 17 Dec 2022 13:43:08 -0500
Subject: [PATCH 10/11] std::fmt: Use args directly in example code

The lint "clippy::uninlined_format_args" recommends inline
variables in format strings. Fix two places in the docs that do
not do this. I noticed this because I copy/pasted one example in
to my project, then noticed this lint error. This fixes:

error: variables can be used directly in the `format!` string
  --> src/main.rs:30:22
   |
30 |         let string = format!("{:.*}", decimals, magnitude);
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: variables can be used directly in the `format!` string
  --> src/main.rs:39:2
   |
39 |  write!(&mut io::stdout(), "{}", args).unwrap();
---
 library/alloc/src/fmt.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index 799ce9d5daa..eadb35cb96d 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -419,7 +419,7 @@
 //!         // documentation for details, and the function `pad` can be used
 //!         // to pad strings.
 //!         let decimals = f.precision().unwrap_or(3);
-//!         let string = format!("{:.*}", decimals, magnitude);
+//!         let string = format!("{magnitude:.decimals$}");
 //!         f.pad_integral(true, "", &string)
 //!     }
 //! }
@@ -518,7 +518,7 @@
 //! write!(&mut some_writer, "{}", format_args!("print with a {}", "macro"));
 //!
 //! fn my_fmt_fn(args: fmt::Arguments) {
-//!     write!(&mut io::stdout(), "{}", args);
+//!     write!(&mut io::stdout(), "{args}");
 //! }
 //! my_fmt_fn(format_args!(", or a {} too", "function"));
 //! ```

From 6f0fc2fd337757a02fa0d948e78638d66a46f518 Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jnelson@cloudflare.com>
Date: Thu, 24 Nov 2022 20:39:40 -0500
Subject: [PATCH 11/11] Symlink `build/host` ->
 `build/x86_64-unknown-linux-gnu` (as appropriate per target)

This allows us to use a consistent path in the documentation, without having to worry about which platform people are using.
---
 src/bootstrap/lib.rs | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 570fe6484e3..f0c9a948727 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -108,6 +108,7 @@ use std::collections::{HashMap, HashSet};
 use std::env;
 use std::fs::{self, File};
 use std::io;
+use std::io::ErrorKind;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::str;
@@ -119,7 +120,9 @@ use once_cell::sync::OnceCell;
 
 use crate::builder::Kind;
 use crate::config::{LlvmLibunwind, TargetSelection};
-use crate::util::{exe, libdir, mtime, output, run, run_suppressed, try_run_suppressed, CiEnv};
+use crate::util::{
+    exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, CiEnv,
+};
 
 mod bolt;
 mod builder;
@@ -586,6 +589,20 @@ impl Build {
             metadata::build(&mut build);
         }
 
+        // Make a symbolic link so we can use a consistent directory in the documentation.
+        let build_triple = build.out.join(&build.build.triple);
+        let host = build.out.join("host");
+        if let Err(e) = symlink_dir(&build.config, &build_triple, &host) {
+            if e.kind() != ErrorKind::AlreadyExists {
+                panic!(
+                    "symlink_dir({} => {}) failed with {}",
+                    host.display(),
+                    build_triple.display(),
+                    e
+                );
+            }
+        }
+
         build
     }