From 14d288fe125813b130a6571bbf2ae49c5f247174 Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Sat, 23 Apr 2022 11:05:31 +0100
Subject: [PATCH 01/36] socket `set_mark` addition.

to be able to set a marker/id on the socket for network filtering
 (iptables/ipfw here) purpose.
---
 library/std/src/os/unix/net/stream.rs |  6 ++++++
 library/std/src/sys/unix/net.rs       | 10 ++++++++++
 2 files changed, 16 insertions(+)

diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 1d6083e66e172..7eb06be3e0907 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -424,6 +424,12 @@ impl UnixStream {
         self.0.passcred()
     }
 
+    #[cfg(any(doc, target_os = "linux", target_os = "freebsd",))]
+    #[unstable(feature = "unix_set_mark", issue = "none")]
+    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+        self.0.set_mark(mark)
+    }
+
     /// Returns the value of the `SO_ERROR` option.
     ///
     /// # Examples
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index a1bbc2d87b640..60ee52528c59a 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -427,6 +427,16 @@ impl Socket {
         self.0.set_nonblocking(nonblocking)
     }
 
+    #[cfg(target_os = "linux")]
+    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+        setsockopt(self, libc::SOL_SOCKET, libc::SO_MARK, mark as libc::c_int)
+    }
+
+    #[cfg(target_os = "freebsd")]
+    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+        setsockopt(self, libc::SOL_SOCKET, libc::SO_USER_COOKIE, mark)
+    }
+
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
         let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
         if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }

From 48ef00e36f58c1debaec8d5612297b8819f7a690 Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Wed, 27 Apr 2022 06:01:05 +0100
Subject: [PATCH 02/36] doc additions

---
 library/std/src/os/unix/net/datagram.rs | 19 +++++++++++++++++++
 library/std/src/os/unix/net/stream.rs   | 15 ++++++++++++++-
 library/std/src/sys/unix/net.rs         |  5 +++++
 3 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 8008acfd1c96f..7f5d760481b86 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -838,6 +838,25 @@ impl UnixDatagram {
         self.0.passcred()
     }
 
+    /// Set the id of the socket for network filtering purpose
+    /// and is only a setter.
+    ///
+    /// ```no_run
+    /// #![feature(unix_set_mark)]
+    /// use std::os::unix::net::UnixDatagram;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let sock = UnixDatagram::unbound()?;
+    ///     sock.set_mark(32 as u32).expect("set_mark function failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
+    #[unstable(feature = "unix_set_mark", issue = "none")]
+    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+        self.0.set_mark(mark)
+    }
+
     /// Returns the value of the `SO_ERROR` option.
     ///
     /// # Examples
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 7eb06be3e0907..7ecb81340ac8d 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -424,7 +424,20 @@ impl UnixStream {
         self.0.passcred()
     }
 
-    #[cfg(any(doc, target_os = "linux", target_os = "freebsd",))]
+    /// Set the id of the socket for network filtering purpose
+    /// and is only a setter.
+    ///
+    /// ```no_run
+    /// #![feature(unix_set_mark)]
+    /// use std::os::unix::net::UnixStream;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let sock = UnixStream::connect("/tmp/sock")?;
+    ///     sock.set_mark(32 as u32).expect("set_mark function failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
     #[unstable(feature = "unix_set_mark", issue = "none")]
     pub fn set_mark(&self, mark: u32) -> io::Result<()> {
         self.0.set_mark(mark)
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 60ee52528c59a..30667edafbaef 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -437,6 +437,11 @@ impl Socket {
         setsockopt(self, libc::SOL_SOCKET, libc::SO_USER_COOKIE, mark)
     }
 
+    #[cfg(target_os = "openbsd")]
+    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+        setsockopt(self, libc::SOL_SOCKET, libc::SO_RTABLE, mark as libc::c_int)
+    }
+
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
         let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
         if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }

From 10f5a19a4deac4a7300ed6bfad11731d451713b0 Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Wed, 6 Jul 2022 20:01:25 +0100
Subject: [PATCH 03/36] changes from feedback

---
 library/std/src/os/unix/net/datagram.rs |  5 ++---
 library/std/src/os/unix/net/stream.rs   |  5 ++---
 library/std/src/sys/unix/net.rs         | 20 ++++++++------------
 3 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 7f5d760481b86..02d0f24cd6526 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -839,7 +839,6 @@ impl UnixDatagram {
     }
 
     /// Set the id of the socket for network filtering purpose
-    /// and is only a setter.
     ///
     /// ```no_run
     /// #![feature(unix_set_mark)]
@@ -847,12 +846,12 @@ impl UnixDatagram {
     ///
     /// fn main() -> std::io::Result<()> {
     ///     let sock = UnixDatagram::unbound()?;
-    ///     sock.set_mark(32 as u32).expect("set_mark function failed");
+    ///     sock.set_mark(32)?;
     ///     Ok(())
     /// }
     /// ```
     #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
-    #[unstable(feature = "unix_set_mark", issue = "none")]
+    #[unstable(feature = "unix_set_mark", issue = "96467")]
     pub fn set_mark(&self, mark: u32) -> io::Result<()> {
         self.0.set_mark(mark)
     }
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 7ecb81340ac8d..ece0f91dad029 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -425,7 +425,6 @@ impl UnixStream {
     }
 
     /// Set the id of the socket for network filtering purpose
-    /// and is only a setter.
     ///
     /// ```no_run
     /// #![feature(unix_set_mark)]
@@ -433,12 +432,12 @@ impl UnixStream {
     ///
     /// fn main() -> std::io::Result<()> {
     ///     let sock = UnixStream::connect("/tmp/sock")?;
-    ///     sock.set_mark(32 as u32).expect("set_mark function failed");
+    ///     sock.set_mark(32)?;
     ///     Ok(())
     /// }
     /// ```
     #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
-    #[unstable(feature = "unix_set_mark", issue = "none")]
+    #[unstable(feature = "unix_set_mark", issue = "96467")]
     pub fn set_mark(&self, mark: u32) -> io::Result<()> {
         self.0.set_mark(mark)
     }
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 30667edafbaef..c942689eddf48 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -427,19 +427,15 @@ impl Socket {
         self.0.set_nonblocking(nonblocking)
     }
 
-    #[cfg(target_os = "linux")]
+    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
     pub fn set_mark(&self, mark: u32) -> io::Result<()> {
-        setsockopt(self, libc::SOL_SOCKET, libc::SO_MARK, mark as libc::c_int)
-    }
-
-    #[cfg(target_os = "freebsd")]
-    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
-        setsockopt(self, libc::SOL_SOCKET, libc::SO_USER_COOKIE, mark)
-    }
-
-    #[cfg(target_os = "openbsd")]
-    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
-        setsockopt(self, libc::SOL_SOCKET, libc::SO_RTABLE, mark as libc::c_int)
+        #[cfg(target_os = "linux")]
+        let option = libc::SO_MARK;
+        #[cfg(target_os = "freebsd")]
+        let option = libc::SO_USER_COOKIE;
+        #[cfg(target_os = "openbsd")]
+        let option = libc::SO_RTABLE;
+        setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int)
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {

From f6efb0b74f286dc806b2fb46b3bd880606533c64 Mon Sep 17 00:00:00 2001
From: David CARLIER <devnexen@gmail.com>
Date: Thu, 7 Jul 2022 13:45:05 +0100
Subject: [PATCH 04/36] Fix doc build on unsupported oses

---
 library/std/src/os/unix/net/datagram.rs | 9 ++++++++-
 library/std/src/os/unix/net/stream.rs   | 9 ++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 02d0f24cd6526..f758f88d0a370 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -840,7 +840,14 @@ impl UnixDatagram {
 
     /// Set the id of the socket for network filtering purpose
     ///
-    /// ```no_run
+    #[cfg_attr(
+        any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
+        doc = "```no_run"
+    )]
+    #[cfg_attr(
+        not(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd")),
+        doc = "```ignore"
+    )]
     /// #![feature(unix_set_mark)]
     /// use std::os::unix::net::UnixDatagram;
     ///
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index ece0f91dad029..240c5a77105d5 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -426,7 +426,14 @@ impl UnixStream {
 
     /// Set the id of the socket for network filtering purpose
     ///
-    /// ```no_run
+    #[cfg_attr(
+        any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
+        doc = "```no_run"
+    )]
+    #[cfg_attr(
+        not(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd")),
+        doc = "```ignore"
+    )]
     /// #![feature(unix_set_mark)]
     /// use std::os::unix::net::UnixStream;
     ///

From 7b4cd17f81e9350ab2d267668e945ccb3c988425 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 18 Jul 2022 02:58:59 +0400
Subject: [PATCH 05/36] Start uplifting `clippy::for_loops_over_fallibles`

I refactored the code:
- Removed handling of methods, as it felt entirely unnecessary
- Removed clippy utils (obviously...)
- Used some shiny compiler features
  (let-else is very handy for lints :eyes:)
- I also renamed the lint to `for_loop_over_fallibles` (note: no `s`).
  I'm not sure what's the naming convention here, so maybe I'm wrong.
---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 99 +++++++++++++++++++
 compiler/rustc_lint/src/lib.rs                |  3 +
 2 files changed, 102 insertions(+)
 create mode 100644 compiler/rustc_lint/src/for_loop_over_fallibles.rs

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
new file mode 100644
index 0000000000000..c96c9efe1d8a1
--- /dev/null
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -0,0 +1,99 @@
+use crate::{LateContext, LateLintPass, LintContext};
+
+use hir::{Expr, Pat};
+use rustc_hir as hir;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+declare_lint! {
+    /// ### What it does
+    ///
+    /// Checks for `for` loops over `Option` or `Result` values.
+    ///
+    /// ### Why is this bad?
+    /// Readability. This is more clearly expressed as an `if
+    /// let`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # let opt = Some(1);
+    /// # let res: Result<i32, std::io::Error> = Ok(1);
+    /// for x in opt {
+    ///     // ..
+    /// }
+    ///
+    /// for x in &res {
+    ///     // ..
+    /// }
+    ///
+    /// for x in res.iter() {
+    ///     // ..
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let opt = Some(1);
+    /// # let res: Result<i32, std::io::Error> = Ok(1);
+    /// if let Some(x) = opt {
+    ///     // ..
+    /// }
+    ///
+    /// if let Ok(x) = res {
+    ///     // ..
+    /// }
+    /// ```
+    pub FOR_LOOP_OVER_FALLIBLES,
+    Warn,
+    "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
+}
+
+declare_lint_pass!(ForLoopOverFallibles => [FOR_LOOP_OVER_FALLIBLES]);
+
+impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some((pat, arg)) = extract_for_loop(expr) else { return };
+
+        let ty = cx.typeck_results().expr_ty(arg);
+
+        let ty::Adt(adt, _) = ty.kind() else { return };
+
+        let (article, ty, var) = match adt.did() {
+            did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
+            did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
+            _ => return,
+        };
+
+        let Ok(pat_snip) = cx.sess().source_map().span_to_snippet(pat.span) else { return };
+        let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
+
+        let help_string = format!(
+            "consider replacing `for {pat_snip} in {arg_snip}` with `if let {var}({pat_snip}) = {arg_snip}`"
+        );
+        let msg = format!(
+            "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
+        );
+
+        cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
+            diag.build(msg).help(help_string).emit()
+        })
+    }
+}
+
+fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>)> {
+    if let hir::ExprKind::DropTemps(e) = expr.kind
+    && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind
+    && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind
+    && let hir::ExprKind::Loop(block, ..) = arm.body.kind
+    && let [stmt] = block.stmts
+    && let hir::StmtKind::Expr(e) = stmt.kind
+    && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
+    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind 
+    {
+        Some((field.pat, arg))
+    } else {
+        None
+    }
+    
+}
\ No newline at end of file
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f087c624917e8..d9c5d47cadbf5 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -48,6 +48,7 @@ mod context;
 mod early;
 mod enum_intrinsics_non_enums;
 mod expect;
+mod for_loop_over_fallibles;
 pub mod hidden_unicode_codepoints;
 mod internal;
 mod late;
@@ -80,6 +81,7 @@ use rustc_span::Span;
 use array_into_iter::ArrayIntoIter;
 use builtin::*;
 use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
+use for_loop_over_fallibles::*;
 use hidden_unicode_codepoints::*;
 use internal::*;
 use methods::*;
@@ -178,6 +180,7 @@ macro_rules! late_lint_mod_passes {
         $macro!(
             $args,
             [
+                ForLoopOverFallibles: ForLoopOverFallibles,
                 HardwiredLints: HardwiredLints,
                 ImproperCTypesDeclarations: ImproperCTypesDeclarations,
                 ImproperCTypesDefinitions: ImproperCTypesDefinitions,

From 810cf60fddb56056759d1a80b41c7d9dc12ad28d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 18 Jul 2022 21:08:59 +0400
Subject: [PATCH 06/36] Use structured suggestions for
 `for_loop_over_fallibles` lint

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 22 ++++++++++++-------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index c96c9efe1d8a1..d765131442f6f 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -1,6 +1,7 @@
 use crate::{LateContext, LateLintPass, LintContext};
 
 use hir::{Expr, Pat};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_middle::ty;
 use rustc_span::sym;
@@ -65,18 +66,24 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
             _ => return,
         };
 
-        let Ok(pat_snip) = cx.sess().source_map().span_to_snippet(pat.span) else { return };
         let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
 
-        let help_string = format!(
-            "consider replacing `for {pat_snip} in {arg_snip}` with `if let {var}({pat_snip}) = {arg_snip}`"
-        );
         let msg = format!(
             "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
-            diag.build(msg).help(help_string).emit()
+            diag.build(msg)
+                .multipart_suggestion_verbose(
+                    "consider using `if let` to clear intent",
+                    vec![
+                        // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
+                        (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
+                        (pat.span.between(arg.span), format!(") = ")),
+                    ],
+                    Applicability::MachineApplicable,
+                )
+                .emit()
         })
     }
 }
@@ -89,11 +96,10 @@ fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx E
     && let [stmt] = block.stmts
     && let hir::StmtKind::Expr(e) = stmt.kind
     && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
-    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind 
+    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind
     {
         Some((field.pat, arg))
     } else {
         None
     }
-    
-}
\ No newline at end of file
+}

From b6611571c4ff709e03df8089401a49808ca583ab Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 21:07:23 +0400
Subject: [PATCH 07/36] `for_loop_over_fallibles`: Suggest removing `.next()`

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 49 ++++++++++++++-----
 1 file changed, 38 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index d765131442f6f..78b6ca6a653c0 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -73,17 +73,30 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
-            diag.build(msg)
-                .multipart_suggestion_verbose(
-                    "consider using `if let` to clear intent",
-                    vec![
-                        // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
-                        (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
-                        (pat.span.between(arg.span), format!(") = ")),
-                    ],
-                    Applicability::MachineApplicable,
-                )
-                .emit()
+            let mut warn = diag.build(msg);
+
+            if let Some(recv) = extract_iterator_next_call(cx, arg)
+            && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
+            {
+                warn.span_suggestion(
+                    recv.span.between(arg.span.shrink_to_hi()),
+                    format!("to iterate over `{recv_snip}` remove the call to `next`"),
+                    "",
+                    Applicability::MaybeIncorrect
+                );
+            }
+
+            warn.multipart_suggestion_verbose(
+                "consider using `if let` to clear intent",
+                vec![
+                    // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
+                    (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
+                    (pat.span.between(arg.span), format!(") = ")),
+                ],
+                Applicability::MachineApplicable,
+            );
+
+            warn.emit()
         })
     }
 }
@@ -103,3 +116,17 @@ fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx E
         None
     }
 }
+
+fn extract_iterator_next_call<'tcx>(
+    cx: &LateContext<'_>,
+    expr: &Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    // This won't work for `Iterator::next(iter)`, is this an issue?
+    if let hir::ExprKind::MethodCall(_, [recv], _) = expr.kind
+    && cx.typeck_results().type_dependent_def_id(expr.hir_id) == cx.tcx.lang_items().next_fn()
+    {
+        Some(recv)
+    } else {
+        return None
+    }
+}

From 7cf94ad91568cc1f2e94c0cca4987dc40aa4e452 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 21:16:44 +0400
Subject: [PATCH 08/36] `for_loop_over_fallibles`: suggest `while let` loop

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 78b6ca6a653c0..1a6188827819c 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -84,6 +84,16 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                     "",
                     Applicability::MaybeIncorrect
                 );
+            } else {
+                warn.multipart_suggestion_verbose(
+                    format!("to check pattern in a loop use `while let`"),
+                    vec![
+                        // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
+                        (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
+                        (pat.span.between(arg.span), format!(") = ")),
+                    ],
+                    Applicability::MaybeIncorrect
+                );
             }
 
             warn.multipart_suggestion_verbose(

From 14b8f241cb0797032c43c1c85bb0be0f80ba2c0d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 23:46:04 +0400
Subject: [PATCH 09/36] `for_loop_over_fallibles`: suggest using `?` in some
 cases

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 68 ++++++++++++++++++-
 1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 1a6188827819c..7807fd3a2807a 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -3,8 +3,11 @@ use crate::{LateContext, LateLintPass, LintContext};
 use hir::{Expr, Pat};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_middle::ty;
-use rustc_span::sym;
+use rustc_infer::traits::TraitEngine;
+use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
+use rustc_middle::ty::{self, List};
+use rustc_span::{sym, Span};
+use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
     /// ### What it does
@@ -58,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
 
         let ty = cx.typeck_results().expr_ty(arg);
 
-        let ty::Adt(adt, _) = ty.kind() else { return };
+        let &ty::Adt(adt, substs) = ty.kind() else { return };
 
         let (article, ty, var) = match adt.did() {
             did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
@@ -96,6 +99,15 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                 );
             }
 
+            if suggest_question_mark(cx, adt, substs, expr.span) {
+                warn.span_suggestion(
+                    arg.span.shrink_to_hi(),
+                    "consider unwrapping the `Result` with `?` to iterate over its contents",
+                    "?",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
             warn.multipart_suggestion_verbose(
                 "consider using `if let` to clear intent",
                 vec![
@@ -140,3 +152,53 @@ fn extract_iterator_next_call<'tcx>(
         return None
     }
 }
+
+fn suggest_question_mark<'tcx>(
+    cx: &LateContext<'tcx>,
+    adt: ty::AdtDef<'tcx>,
+    substs: &List<ty::GenericArg<'tcx>>,
+    span: Span,
+) -> bool {
+    let Some(body_id) = cx.enclosing_body else { return false };
+    let Some(into_iterator_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { return false };
+
+    if !cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
+        return false;
+    }
+
+    // Check that the function/closure/constant we are in has a `Result` type.
+    // Otherwise suggesting using `?` may not be a good idea.
+    {
+        let ty = cx.typeck_results().expr_ty(&cx.tcx.hir().body(body_id).value);
+        let ty::Adt(ret_adt, ..) = ty.kind() else { return false };
+        if !cx.tcx.is_diagnostic_item(sym::Result, ret_adt.did()) {
+            return false;
+        }
+    }
+
+    let ty = substs.type_at(0);
+    let is_iterator = cx.tcx.infer_ctxt().enter(|infcx| {
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+
+        let cause = ObligationCause::new(
+            span,
+            body_id.hir_id,
+            rustc_infer::traits::ObligationCauseCode::MiscObligation,
+        );
+        fulfill_cx.register_bound(
+            &infcx,
+            ty::ParamEnv::empty(),
+            // Erase any region vids from the type, which may not be resolved
+            infcx.tcx.erase_regions(ty),
+            into_iterator_did,
+            cause,
+        );
+
+        // Select all, including ambiguous predicates
+        let errors = fulfill_cx.select_all_or_error(&infcx);
+
+        errors.is_empty()
+    });
+
+    is_iterator
+}

From 2bf213bb0852e8109253838a403d042b203f807a Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:17:18 +0400
Subject: [PATCH 10/36] `for_loop_over_fallibles`: remove duplication from the
 message

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 7807fd3a2807a..69d8fd84b64d5 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -69,10 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
             _ => return,
         };
 
-        let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
-
         let msg = format!(
-            "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
+            "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {

From 5128140b63bab45d6e8cba34f5a2231a5bb3d22d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:37:11 +0400
Subject: [PATCH 11/36] Add a test for the `for_loop_over_fallibles` lint

---
 src/test/ui/lint/for_loop_over_fallibles.rs   |  43 ++++++++
 .../ui/lint/for_loop_over_fallibles.stderr    | 102 ++++++++++++++++++
 2 files changed, 145 insertions(+)
 create mode 100644 src/test/ui/lint/for_loop_over_fallibles.rs
 create mode 100644 src/test/ui/lint/for_loop_over_fallibles.stderr

diff --git a/src/test/ui/lint/for_loop_over_fallibles.rs b/src/test/ui/lint/for_loop_over_fallibles.rs
new file mode 100644
index 0000000000000..43d71c2e808a9
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.rs
@@ -0,0 +1,43 @@
+// check-pass
+
+fn main() {
+    // Common
+    for _ in Some(1) {}
+    //~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+    for _ in Ok::<_, ()>(1) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+
+    // `Iterator::next` specific
+    for _ in [0; 0].iter().next() {}
+    //~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
+    //~| HELP to iterate over `[0; 0].iter()` remove the call to `next`
+    //~| HELP consider using `if let` to clear intent
+
+    // `Result<impl Iterator, _>`, but function doesn't return `Result`
+    for _ in Ok::<_, ()>([0; 0].iter()) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+}
+
+fn _returns_result() -> Result<(), ()> {
+    // `Result<impl Iterator, _>`
+    for _ in Ok::<_, ()>([0; 0].iter()) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
+    //~| HELP consider using `if let` to clear intent
+
+    // `Result<impl IntoIterator>`
+    for _ in Ok::<_, ()>([0; 0]) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
+    //~| HELP consider using `if let` to clear intent
+
+    Ok(())
+}
diff --git a/src/test/ui/lint/for_loop_over_fallibles.stderr b/src/test/ui/lint/for_loop_over_fallibles.stderr
new file mode 100644
index 0000000000000..52eac945d8571
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.stderr
@@ -0,0 +1,102 @@
+warning: for loop over an `Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:5:14
+   |
+LL |     for _ in Some(1) {}
+   |              ^^^^^^^
+   |
+   = note: `#[warn(for_loop_over_fallibles)]` on by default
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Some(_) = Some(1) {}
+   |     ~~~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = Some(1) {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:9:14
+   |
+LL |     for _ in Ok::<_, ()>(1) {}
+   |              ^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over an `Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:15:14
+   |
+LL |     for _ in [0; 0].iter().next() {}
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: to iterate over `[0; 0].iter()` remove the call to `next`
+   |
+LL -     for _ in [0; 0].iter().next() {}
+LL +     for _ in [0; 0].iter() {}
+   |
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = [0; 0].iter().next() {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:21:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter()) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:29:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter()) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider unwrapping the `Result` with `?` to iterate over its contents
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter())? {}
+   |                                        +
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:36:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0]) {}
+   |              ^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0]) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider unwrapping the `Result` with `?` to iterate over its contents
+   |
+LL |     for _ in Ok::<_, ()>([0; 0])? {}
+   |                                 +
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0]) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: 6 warnings emitted
+

From 34815a90dd06af1bdb3504caf59138c2c5a6202f Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:58:04 +0400
Subject: [PATCH 12/36] `for_loop_over_fallibles`: fix suggestion for "remove
 `.next()`" case

if the iterator is used after the loop, we need to use `.by_ref()`
---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 2 +-
 src/test/ui/lint/for_loop_over_fallibles.stderr    | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 69d8fd84b64d5..c8d5586d39f9c 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                 warn.span_suggestion(
                     recv.span.between(arg.span.shrink_to_hi()),
                     format!("to iterate over `{recv_snip}` remove the call to `next`"),
-                    "",
+                    ".by_ref()",
                     Applicability::MaybeIncorrect
                 );
             } else {
diff --git a/src/test/ui/lint/for_loop_over_fallibles.stderr b/src/test/ui/lint/for_loop_over_fallibles.stderr
index 52eac945d8571..56e3126dc09a4 100644
--- a/src/test/ui/lint/for_loop_over_fallibles.stderr
+++ b/src/test/ui/lint/for_loop_over_fallibles.stderr
@@ -37,9 +37,8 @@ LL |     for _ in [0; 0].iter().next() {}
    |
 help: to iterate over `[0; 0].iter()` remove the call to `next`
    |
-LL -     for _ in [0; 0].iter().next() {}
-LL +     for _ in [0; 0].iter() {}
-   |
+LL |     for _ in [0; 0].iter().by_ref() {}
+   |                           ~~~~~~~~~
 help: consider using `if let` to clear intent
    |
 LL |     if let Some(_) = [0; 0].iter().next() {}

From c4ab59e1921262cb9e07eaeb193192883317af04 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 01:04:27 +0400
Subject: [PATCH 13/36] `for_loop_over_fallibles`: don't use
 `MachineApplicable`

The loop could contain `break;` that won't work with an `if let`
---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index c8d5586d39f9c..6870942af941f 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                     (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
                     (pat.span.between(arg.span), format!(") = ")),
                 ],
-                Applicability::MachineApplicable,
+                Applicability::MaybeIncorrect,
             );
 
             warn.emit()

From 41fccb1517db488acd12ba8d962157da88bc4bd4 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Tue, 26 Jul 2022 14:17:15 +0400
Subject: [PATCH 14/36] allow or avoid for loops over option in compiler and
 tests

---
 compiler/rustc_ast/src/visit.rs                    | 14 ++++++--------
 .../rustc_borrowck/src/type_check/input_output.rs  |  2 +-
 src/test/ui/drop/dropck_legal_cycles.rs            | 14 +++++++-------
 src/test/ui/issues/issue-30371.rs                  |  1 +
 4 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 4b485b547f495..5820776d04f3d 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -244,14 +244,12 @@ pub trait Visitor<'ast>: Sized {
 
 #[macro_export]
 macro_rules! walk_list {
-    ($visitor: expr, $method: ident, $list: expr) => {
-        for elem in $list {
-            $visitor.$method(elem)
-        }
-    };
-    ($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => {
-        for elem in $list {
-            $visitor.$method(elem, $($extra_args,)*)
+    ($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => {
+        {
+            #[cfg_attr(not(bootstrap), allow(for_loop_over_fallibles))]
+            for elem in $list {
+                $visitor.$method(elem $(, $($extra_args,)* )?)
+            }
         }
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 4431a2e8ec60d..84d2bfe04de05 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -225,7 +225,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         debug!("{:?} normalized to {:?}", t, norm_ty);
 
-        for data in constraints {
+        if let Some(data) = constraints {
             ConstraintConversion::new(
                 self.infcx,
                 &self.borrowck_context.universal_regions,
diff --git a/src/test/ui/drop/dropck_legal_cycles.rs b/src/test/ui/drop/dropck_legal_cycles.rs
index 27a599315dc1c..6a0fe7784fbcc 100644
--- a/src/test/ui/drop/dropck_legal_cycles.rs
+++ b/src/test/ui/drop/dropck_legal_cycles.rs
@@ -1017,7 +1017,7 @@ impl<'a> Children<'a> for HM<'a> {
         where C: Context + PrePost<Self>, Self: Sized
     {
         if let Some(ref hm) = self.contents.get() {
-            for (k, v) in hm.iter().nth(index / 2) {
+            if let Some((k, v)) = hm.iter().nth(index / 2) {
                 [k, v][index % 2].descend_into_self(context);
             }
         }
@@ -1032,7 +1032,7 @@ impl<'a> Children<'a> for VD<'a> {
         where C: Context + PrePost<Self>, Self: Sized
     {
         if let Some(ref vd) = self.contents.get() {
-            for r in vd.iter().nth(index) {
+            if let Some(r) = vd.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1047,7 +1047,7 @@ impl<'a> Children<'a> for VM<'a> {
         where C: Context + PrePost<VM<'a>>
     {
         if let Some(ref vd) = self.contents.get() {
-            for (_idx, r) in vd.iter().nth(index) {
+            if let Some((_idx, r)) = vd.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1062,7 +1062,7 @@ impl<'a> Children<'a> for LL<'a> {
         where C: Context + PrePost<LL<'a>>
     {
         if let Some(ref ll) = self.contents.get() {
-            for r in ll.iter().nth(index) {
+            if let Some(r) = ll.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1077,7 +1077,7 @@ impl<'a> Children<'a> for BH<'a> {
         where C: Context + PrePost<BH<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for r in bh.iter().nth(index) {
+            if let Some(r) = bh.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1092,7 +1092,7 @@ impl<'a> Children<'a> for BTM<'a> {
         where C: Context + PrePost<BTM<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for (k, v) in bh.iter().nth(index / 2) {
+            if let Some((k, v)) = bh.iter().nth(index / 2) {
                 [k, v][index % 2].descend_into_self(context);
             }
         }
@@ -1107,7 +1107,7 @@ impl<'a> Children<'a> for BTS<'a> {
         where C: Context + PrePost<BTS<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for r in bh.iter().nth(index) {
+            if let Some(r) = bh.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
diff --git a/src/test/ui/issues/issue-30371.rs b/src/test/ui/issues/issue-30371.rs
index a1ae9a36bc1d7..880558eb5b697 100644
--- a/src/test/ui/issues/issue-30371.rs
+++ b/src/test/ui/issues/issue-30371.rs
@@ -1,5 +1,6 @@
 // run-pass
 #![allow(unreachable_code)]
+#![allow(for_loop_over_fallibles)]
 #![deny(unused_variables)]
 
 fn main() {

From 86360f41d9f350273d480986526d1c5a15673f1c Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Tue, 26 Jul 2022 16:19:58 +0400
Subject: [PATCH 15/36] allow `for_loop_over_fallibles` in a `core` test

---
 library/core/tests/option.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs
index 9f5e537dcefc0..84eb4fc0aa329 100644
--- a/library/core/tests/option.rs
+++ b/library/core/tests/option.rs
@@ -57,6 +57,7 @@ fn test_get_resource() {
 }
 
 #[test]
+#[cfg_attr(not(bootstrap), allow(for_loop_over_fallibles))]
 fn test_option_dance() {
     let x = Some(());
     let mut y = Some(5);

From d7b8a65c3aec77b8d6ccf7127cbdfdcf085a6873 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 14 Aug 2022 21:42:29 +0400
Subject: [PATCH 16/36] Edit documentation for `for_loop_over_fallibles` lint

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 44 +++++++++----------
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 6870942af941f..48a876b157bda 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -10,43 +10,41 @@ use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
-    /// ### What it does
-    ///
     /// Checks for `for` loops over `Option` or `Result` values.
     ///
-    /// ### Why is this bad?
-    /// Readability. This is more clearly expressed as an `if
-    /// let`.
+    /// ### Explanation
+    ///
+    /// Both `Option` and `Result` implement `IntoIterator` trait, which allows using them in a `for` loop.
+    /// `for` loop over `Option` or `Result` will iterate either 0 (if the value is `None`/`Err(_)`)
+    /// or 1 time (if the value is `Some(_)`/`Ok(_)`). This is not very useful and is more clearly expressed
+    /// via `if let`.
+    ///
+    /// `for` loop can also be accidentally written with the intention to call a function multiple times,
+    /// while the function returns `Some(_)`, in these cases `while let` loop should be used instead.
+    ///
+    /// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
+    /// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
+    /// to optionally add a value to an iterator.
     ///
     /// ### Example
     ///
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// for x in opt {
-    ///     // ..
-    /// }
-    ///
-    /// for x in &res {
-    ///     // ..
-    /// }
-    ///
-    /// for x in res.iter() {
-    ///     // ..
-    /// }
+    /// # let recv = || Some(1);
+    /// for x in opt { /* ... */}
+    /// for x in res { /* ... */ }
+    /// for x in recv() { /* ... */ }
     /// ```
     ///
     /// Use instead:
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// if let Some(x) = opt {
-    ///     // ..
-    /// }
-    ///
-    /// if let Ok(x) = res {
-    ///     // ..
-    /// }
+    /// # let recv = || Some(1);
+    /// if let Some(x) = opt { /* ... */}
+    /// if let Ok(x) = res { /* ... */ }
+    /// while let Some(x) = recv() { /* ... */ }
     /// ```
     pub FOR_LOOP_OVER_FALLIBLES,
     Warn,

From aed1ae44eb70f8e7c3a859d3e46d4533b2941066 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 15 Aug 2022 05:22:00 +0400
Subject: [PATCH 17/36] remove an infinite loop

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 48a876b157bda..0a6b6e4163622 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -31,7 +31,7 @@ declare_lint! {
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || Some(1);
+    /// # let recv = || None::<i32>;
     /// for x in opt { /* ... */}
     /// for x in res { /* ... */ }
     /// for x in recv() { /* ... */ }
@@ -41,7 +41,7 @@ declare_lint! {
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || Some(1);
+    /// # let recv = || None::<i32>;
     /// if let Some(x) = opt { /* ... */}
     /// if let Ok(x) = res { /* ... */ }
     /// while let Some(x) = recv() { /* ... */ }

From 71b8c89a5b467dda27ca29a91a17b8c933d5ce25 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Thu, 18 Aug 2022 11:43:10 +0400
Subject: [PATCH 18/36] fix `for_loop_over_fallibles` lint docs

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 32 ++++++-------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 0a6b6e4163622..2253546b5d357 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -10,7 +10,16 @@ use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
-    /// Checks for `for` loops over `Option` or `Result` values.
+    /// The `for_loop_over_fallibles` lint checks for `for` loops over `Option` or `Result` values.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let opt = Some(1);
+    /// for x in opt { /* ... */}
+    /// ```
+    ///
+    /// {{produces}}
     ///
     /// ### Explanation
     ///
@@ -25,27 +34,6 @@ declare_lint! {
     /// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
     /// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
     /// to optionally add a value to an iterator.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// # let opt = Some(1);
-    /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || None::<i32>;
-    /// for x in opt { /* ... */}
-    /// for x in res { /* ... */ }
-    /// for x in recv() { /* ... */ }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let opt = Some(1);
-    /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || None::<i32>;
-    /// if let Some(x) = opt { /* ... */}
-    /// if let Ok(x) = res { /* ... */ }
-    /// while let Some(x) = recv() { /* ... */ }
-    /// ```
     pub FOR_LOOP_OVER_FALLIBLES,
     Warn,
     "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"

From 313d474b35fd6e60302324f75622d77765e1cbf8 Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Sun, 21 Aug 2022 20:56:00 +0300
Subject: [PATCH 19/36] Migrate OpaqueHiddenType, E0282, E0283, E0284, E0698

---
 Cargo.lock                                    |   1 +
 .../locales/en-US/infer.ftl                   |  65 ++++
 compiler/rustc_error_messages/src/lib.rs      |   1 +
 compiler/rustc_infer/Cargo.toml               |   1 +
 compiler/rustc_infer/src/errors.rs            | 187 +++++++++++
 .../infer/error_reporting/need_type_info.rs   | 312 ++++++++++--------
 compiler/rustc_infer/src/lib.rs               |   3 +
 7 files changed, 440 insertions(+), 130 deletions(-)
 create mode 100644 compiler/rustc_error_messages/locales/en-US/infer.ftl
 create mode 100644 compiler/rustc_infer/src/errors.rs

diff --git a/Cargo.lock b/Cargo.lock
index ebacd32db4fc7..853a2976bc61d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3983,6 +3983,7 @@ dependencies = [
  "rustc_macros",
  "rustc_middle",
  "rustc_serialize",
+ "rustc_session",
  "rustc_span",
  "rustc_target",
  "smallvec",
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
new file mode 100644
index 0000000000000..9250363551dbb
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -0,0 +1,65 @@
+infer_opaque_hidden_type = 
+    opaque type's hidden type cannot be another opaque type from the same scope
+    .label = one of the two opaque types used here has to be outside its defining scope
+    .opaque_type = opaque type whose hidden type is being assigned
+    .hidden_type = opaque type being used as hidden type
+
+infer_type_annotations_needed = {$source_kind ->
+    [closure] type annotations needed for the closure `{$source_name}`
+    [normal] type annotations needed for `{$source_name}`
+    *[other] type annotations needed
+} 
+    .label = type must be known at this point
+
+infer_label_bad = {$bad_kind ->
+    *[other] cannot infer type
+    [more_info] cannot infer {$prefix_kind ->
+        *[type] type for {$prefix}
+        [const_with_param] the value of const parameter
+        [const] the value of the constant
+    } `{$name}`{$has_parent ->
+        [true] {" "}declared on the {$parent_prefix} `{$parent_name}`
+        *[false] {""}
+    }
+}
+
+infer_source_kind_subdiag_let = {$kind ->
+    [with_pattern] consider giving `{$name}` an explicit type
+    [closure] consider giving this closure parameter an explicit type
+    *[other] consider giving this pattern a type
+}{$x_kind ->
+    [has_name] , where the {$prefix_kind ->
+        *[type] type for {$prefix}
+        [const_with_param] the value of const parameter
+        [const] the value of the constant
+    } `{$arg_name}` is specified
+    [underscore] , where the placeholders `_` are specified
+    *[empty] {""}
+}
+
+infer_source_kind_subdiag_generic_label =
+    cannot infer {$is_type -> 
+    [true] type
+    *[false] the value
+    } of the {$is_type -> 
+    [true] type
+    *[false] const
+    } {$parent_exists ->
+    [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
+    *[false] parameter {$param_name} 
+    }
+
+infer_source_kind_subdiag_generic_suggestion =
+    consider specifying the generic {$arg_count -> 
+    [one] argument
+    *[other] arguments
+    }
+
+infer_source_kind_fully_qualified = 
+    try using a fully qualified path to specify the expected types
+
+infer_source_kind_closure_return =
+    try giving this closure an explicit return type
+
+infer_need_type_info_in_generator =
+    type inside {$generator_kind} must be known in this context
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 3569c7f063064..a24ab3072925f 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -38,6 +38,7 @@ fluent_messages! {
     const_eval => "../locales/en-US/const_eval.ftl",
     expand => "../locales/en-US/expand.ftl",
     interface => "../locales/en-US/interface.ftl",
+    infer => "../locales/en-US/infer.ftl",
     lint => "../locales/en-US/lint.ftl",
     parser => "../locales/en-US/parser.ftl",
     passes => "../locales/en-US/passes.ftl",
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 02ac83a5e8b25..aced787d67116 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs
new file mode 100644
index 0000000000000..7bd418ddf5f4c
--- /dev/null
+++ b/compiler/rustc_infer/src/errors.rs
@@ -0,0 +1,187 @@
+use rustc_errors::{fluent, AddSubdiagnostic};
+use rustc_hir::FnRetTy;
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{BytePos, Span};
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::opaque_hidden_type)]
+pub struct OpaqueHiddenTypeDiag {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[note(infer::opaque_type)]
+    pub opaque_type: Span,
+    #[note(infer::hidden_type)]
+    pub hidden_type: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0282")]
+pub struct AnnotationRequired<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+// Copy of `AnnotationRequired` for E0283
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0283")]
+pub struct AmbigousImpl<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+// Copy of `AnnotationRequired` for E0284
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0284")]
+pub struct AmbigousReturn<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::need_type_info_in_generator, code = "E0698")]
+pub struct NeedTypeInfoInGenerator<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub generator_kind: String,
+    #[subdiagnostic]
+    pub bad_label: InferenceBadError<'a>,
+}
+
+// Used when a better one isn't available
+#[derive(SessionSubdiagnostic)]
+#[label(infer::label_bad)]
+pub struct InferenceBadError<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub bad_kind: &'static str,
+    pub prefix_kind: &'static str,
+    pub has_parent: bool,
+    pub prefix: &'a str,
+    pub parent_prefix: &'a str,
+    pub parent_name: String,
+    pub name: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum SourceKindSubdiag<'a> {
+    #[suggestion_verbose(
+        infer::source_kind_subdiag_let,
+        code = ": {type_name}",
+        applicability = "has-placeholders"
+    )]
+    LetLike {
+        #[primary_span]
+        span: Span,
+        name: String,
+        type_name: String,
+        kind: &'static str,
+        x_kind: &'static str,
+        prefix_kind: &'static str,
+        prefix: &'a str,
+        arg_name: String,
+    },
+    #[label(infer::source_kind_subdiag_generic_label)]
+    GenericLabel {
+        #[primary_span]
+        span: Span,
+        is_type: bool,
+        param_name: String,
+        parent_exists: bool,
+        parent_prefix: String,
+        parent_name: String,
+    },
+    #[suggestion_verbose(
+        infer::source_kind_subdiag_generic_suggestion,
+        code = "::<{args}>",
+        applicability = "has-placeholders"
+    )]
+    GenericSuggestion {
+        #[primary_span]
+        span: Span,
+        arg_count: usize,
+        args: String,
+    },
+}
+
+// Has to be implemented manually because multipart suggestions are not supported by the derive macro.
+// Would be a part of `SourceKindSubdiag` otherwise.
+pub enum SourceKindMultiSuggestion<'a> {
+    FullyQualified {
+        span: Span,
+        def_path: String,
+        adjustment: &'a str,
+        successor: (&'a str, BytePos),
+    },
+    ClosureReturn {
+        ty_info: String,
+        data: &'a FnRetTy<'a>,
+        should_wrap_expr: Option<Span>,
+    },
+}
+
+impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        match self {
+            Self::FullyQualified { span, def_path, adjustment, successor } => {
+                let suggestion = vec![
+                    (span.shrink_to_lo(), format!("{def_path}({adjustment}")),
+                    (span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
+                ];
+                diag.multipart_suggestion_verbose(
+                    fluent::infer::source_kind_fully_qualified,
+                    suggestion,
+                    rustc_errors::Applicability::HasPlaceholders,
+                );
+            }
+            Self::ClosureReturn { ty_info, data, should_wrap_expr } => {
+                let (arrow, post) = match data {
+                    FnRetTy::DefaultReturn(_) => ("-> ", " "),
+                    _ => ("", ""),
+                };
+                let suggestion = match should_wrap_expr {
+                    Some(end_span) => vec![
+                        (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post)),
+                        (end_span, " }".to_string()),
+                    ],
+                    None => vec![(data.span(), format!("{}{}{}", arrow, ty_info, post))],
+                };
+                diag.multipart_suggestion_verbose(
+                    fluent::infer::source_kind_closure_return,
+                    suggestion,
+                    rustc_errors::Applicability::HasPlaceholders,
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 561d1354edd21..da0035d2519f1 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -1,6 +1,10 @@
+use crate::errors::{
+    AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
+    SourceKindMultiSuggestion, SourceKindSubdiag,
+};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
@@ -14,6 +18,7 @@ use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
 use rustc_middle::ty::{self, DefIdTree, InferConst};
 use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
+use rustc_session::SessionDiagnostic;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span};
 use std::borrow::Cow;
@@ -66,32 +71,42 @@ pub enum UnderspecifiedArgKind {
 }
 
 impl InferenceDiagnosticsData {
-    /// Generate a label for a generic argument which can't be inferred. When not
-    /// much is known about the argument, `use_diag` may be used to describe the
-    /// labeled value.
-    fn cannot_infer_msg(&self) -> String {
-        if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
-            return "cannot infer type".to_string();
-        }
-
-        let suffix = match &self.parent {
-            Some(parent) => parent.suffix_string(),
-            None => String::new(),
-        };
-
-        // For example: "cannot infer type for type parameter `T`"
-        format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
+    fn can_add_more_info(&self) -> bool {
+        !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
     }
 
-    fn where_x_is_specified(&self, in_type: Ty<'_>) -> String {
+    fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
         if in_type.is_ty_infer() {
-            String::new()
+            "empty"
         } else if self.name == "_" {
             // FIXME: Consider specializing this message if there is a single `_`
             // in the type.
-            ", where the placeholders `_` are specified".to_string()
+            "underscore"
         } else {
-            format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name)
+            "has_name"
+        }
+    }
+
+    /// Generate a label for a generic argument which can't be inferred. When not
+    /// much is known about the argument, `use_diag` may be used to describe the
+    /// labeled value.
+    fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> {
+        let has_parent = self.parent.is_some();
+        let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" };
+        let (parent_prefix, parent_name) = self
+            .parent
+            .as_ref()
+            .map(|parent| (parent.prefix, parent.name.clone()))
+            .unwrap_or_default();
+        InferenceBadError {
+            span,
+            bad_kind,
+            prefix_kind: self.kind.prefix_kind(),
+            prefix: self.kind.try_get_prefix().unwrap_or_default(),
+            name: self.name.clone(),
+            has_parent,
+            parent_prefix,
+            parent_name,
         }
     }
 }
@@ -113,18 +128,20 @@ impl InferenceDiagnosticsParentData {
     fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
         Self::for_parent_def_id(tcx, tcx.parent(def_id))
     }
-
-    fn suffix_string(&self) -> String {
-        format!(" declared on the {} `{}`", self.prefix, self.name)
-    }
 }
 
 impl UnderspecifiedArgKind {
-    fn prefix_string(&self) -> Cow<'static, str> {
+    fn prefix_kind(&self) -> &'static str {
+        match self {
+            Self::Type { .. } => "type",
+            Self::Const { is_parameter: true } => "const_with_param",
+            Self::Const { is_parameter: false } => "const",
+        }
+    }
+    fn try_get_prefix(&self) -> Option<&str> {
         match self {
-            Self::Type { prefix } => format!("type for {}", prefix).into(),
-            Self::Const { is_parameter: true } => "the value of const parameter".into(),
-            Self::Const { is_parameter: false } => "the value of the constant".into(),
+            Self::Type { prefix } => Some(prefix.as_ref()),
+            Self::Const { .. } => None,
         }
     }
 }
@@ -303,11 +320,44 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         arg_data: InferenceDiagnosticsData,
         error_code: TypeAnnotationNeeded,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let error_code = error_code.into();
-        let mut err =
-            self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code);
-        err.span_label(span, arg_data.cannot_infer_msg());
-        err
+        let source_kind = "other";
+        let source_name = "";
+        let failure_span = None;
+        let infer_subdiags = Vec::new();
+        let multi_suggestions = Vec::new();
+        let bad_label = Some(arg_data.make_bad_error(span));
+        match error_code {
+            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0283 => AmbigousImpl {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0284 => AmbigousReturn {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+        }
     }
 
     pub fn emit_inference_failure_err(
@@ -340,48 +390,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             return self.bad_inference_failure_err(failure_span, arg_data, error_code)
         };
 
-        let error_code = error_code.into();
-        let mut err = self.tcx.sess.struct_span_err_with_code(
-            span,
-            &format!("type annotations needed{}", kind.ty_msg(self)),
-            error_code,
-        );
-
-        if should_label_span && !failure_span.overlaps(span) {
-            err.span_label(failure_span, "type must be known at this point");
-        }
+        let (source_kind, name) = kind.ty_localized_msg(self);
+        let failure_span = if should_label_span && !failure_span.overlaps(span) {
+            Some(failure_span)
+        } else {
+            None
+        };
 
+        let mut infer_subdiags = Vec::new();
+        let mut multi_suggestions = Vec::new();
         match kind {
             InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
-                let suggestion_msg = if let Some(name) = pattern_name {
-                    format!(
-                        "consider giving `{}` an explicit type{}",
-                        name,
-                        arg_data.where_x_is_specified(ty)
-                    )
-                } else {
-                    format!(
-                        "consider giving this pattern a type{}",
-                        arg_data.where_x_is_specified(ty)
-                    )
-                };
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &suggestion_msg,
-                    format!(": {}", ty_to_string(self, ty)),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.prefix_kind(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
+                    type_name: ty_to_string(self, ty),
+                });
             }
             InferSourceKind::ClosureArg { insert_span, ty } => {
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &format!(
-                        "consider giving this closure parameter an explicit type{}",
-                        arg_data.where_x_is_specified(ty)
-                    ),
-                    format!(": {}", ty_to_string(self, ty)),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: String::new(),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.prefix_kind(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: "closure",
+                    type_name: ty_to_string(self, ty),
+                });
             }
             InferSourceKind::GenericArg {
                 insert_span,
@@ -393,19 +434,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 let generics = self.tcx.generics_of(generics_def_id);
                 let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
 
-                let cannot_infer_msg = format!(
-                    "cannot infer {} of the {} parameter `{}`{}",
-                    if is_type { "type" } else { "the value" },
-                    if is_type { "type" } else { "const" },
-                    generics.params[argument_index].name,
-                    // We use the `generics_def_id` here, as even when suggesting `None::<T>`,
-                    // the type parameter `T` was still declared on the enum, not on the
-                    // variant.
+                let (parent_exists, parent_prefix, parent_name) =
                     InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
-                        .map_or(String::new(), |parent| parent.suffix_string()),
-                );
+                        .map_or((false, String::new(), String::new()), |parent| {
+                            (true, parent.prefix.to_string(), parent.name)
+                        });
 
-                err.span_label(span, cannot_infer_msg);
+                infer_subdiags.push(SourceKindSubdiag::GenericLabel {
+                    span,
+                    is_type,
+                    param_name: generics.params[argument_index].name.to_string(),
+                    parent_exists,
+                    parent_prefix,
+                    parent_name,
+                });
 
                 let args = fmt_printer(self, Namespace::TypeNS)
                     .comma_sep(generic_args.iter().copied().map(|arg| {
@@ -435,15 +477,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     .unwrap()
                     .into_buffer();
 
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &format!(
-                        "consider specifying the generic argument{}",
-                        pluralize!(generic_args.len()),
-                    ),
-                    format!("::<{}>", args),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
+                    span: insert_span,
+                    arg_count: generic_args.len(),
+                    args,
+                });
             }
             InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => {
                 let printer = fmt_printer(self, Namespace::ValueNS);
@@ -468,37 +506,54 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     _ => "",
                 };
 
-                let suggestion = vec![
-                    (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")),
-                    (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
-                ];
-                err.multipart_suggestion_verbose(
-                    "try using a fully qualified path to specify the expected types",
-                    suggestion,
-                    Applicability::HasPlaceholders,
-                );
+                multi_suggestions.push(SourceKindMultiSuggestion::FullyQualified {
+                    span: receiver.span,
+                    def_path,
+                    adjustment,
+                    successor,
+                });
             }
             InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
-                let ret = ty_to_string(self, ty);
-                let (arrow, post) = match data {
-                    FnRetTy::DefaultReturn(_) => ("-> ", " "),
-                    _ => ("", ""),
-                };
-                let suggestion = match should_wrap_expr {
-                    Some(end_span) => vec![
-                        (data.span(), format!("{}{}{}{{ ", arrow, ret, post)),
-                        (end_span, " }".to_string()),
-                    ],
-                    None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))],
-                };
-                err.multipart_suggestion_verbose(
-                    "try giving this closure an explicit return type",
-                    suggestion,
-                    Applicability::HasPlaceholders,
-                );
+                let ty_info = ty_to_string(self, ty);
+                multi_suggestions.push(SourceKindMultiSuggestion::ClosureReturn {
+                    ty_info,
+                    data,
+                    should_wrap_expr,
+                });
+            }
+        }
+        match error_code {
+            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
             }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0283 => AmbigousImpl {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0284 => AmbigousReturn {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
         }
-        err
     }
 
     pub fn need_type_info_err_in_generator(
@@ -510,15 +565,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let ty = self.resolve_vars_if_possible(ty);
         let data = self.extract_inference_diagnostics_data(ty.into(), None);
 
-        let mut err = struct_span_err!(
-            self.tcx.sess,
+        NeedTypeInfoInGenerator {
+            bad_label: data.make_bad_error(span),
             span,
-            E0698,
-            "type inside {} must be known in this context",
-            kind,
-        );
-        err.span_label(span, data.cannot_infer_msg());
-        err
+            generator_kind: kind.to_string(),
+        }
+        .into_diagnostic(&self.tcx.sess.parse_sess)
     }
 }
 
@@ -579,22 +631,22 @@ impl<'tcx> InferSource<'tcx> {
 }
 
 impl<'tcx> InferSourceKind<'tcx> {
-    fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
+    fn ty_localized_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> (&'static str, String) {
         match *self {
             InferSourceKind::LetBinding { ty, .. }
             | InferSourceKind::ClosureArg { ty, .. }
             | InferSourceKind::ClosureReturn { ty, .. } => {
                 if ty.is_closure() {
-                    format!(" for the closure `{}`", closure_as_fn_str(infcx, ty))
+                    ("closure", closure_as_fn_str(infcx, ty))
                 } else if !ty.is_ty_infer() {
-                    format!(" for `{}`", ty_to_string(infcx, ty))
+                    ("normal", ty_to_string(infcx, ty))
                 } else {
-                    String::new()
+                    ("other", String::new())
                 }
             }
             // FIXME: We should be able to add some additional info here.
             InferSourceKind::GenericArg { .. }
-            | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(),
+            | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()),
         }
     }
 }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 1c515f5ee5722..2c1d339b578fe 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -23,6 +23,8 @@
 #![feature(never_type)]
 #![feature(try_blocks)]
 #![recursion_limit = "512"] // For rustdoc
+// #![deny(rustc::untranslatable_diagnostic)]
+// #![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_macros;
@@ -34,5 +36,6 @@ extern crate tracing;
 #[macro_use]
 extern crate rustc_middle;
 
+mod errors;
 pub mod infer;
 pub mod traits;

From 7e4f4337203b247bdbfbb0661e59ab1aa3bee3f1 Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Sun, 21 Aug 2022 22:39:46 +0300
Subject: [PATCH 20/36] Actually migrate OpaqueHiddenType

---
 .../rustc_infer/src/infer/opaque_types.rs     | 22 +++++--------------
 1 file changed, 6 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index e579afbf38954..233a5004a3931 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -1,3 +1,4 @@
+use crate::errors::OpaqueHiddenTypeDiag;
 use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
 use crate::traits;
 use hir::def_id::{DefId, LocalDefId};
@@ -153,22 +154,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     if let Some(OpaqueTyOrigin::TyAlias) =
                         did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span))
                     {
-                        self.tcx
-                                .sess
-                                .struct_span_err(
-                                    cause.span,
-                                    "opaque type's hidden type cannot be another opaque type from the same scope",
-                                )
-                                .span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope")
-                                .span_note(
-                                    self.tcx.def_span(def_id),
-                                    "opaque type whose hidden type is being assigned",
-                                )
-                                .span_note(
-                                    self.tcx.def_span(did2),
-                                    "opaque type being used as hidden type",
-                                )
-                                .emit();
+                        self.tcx.sess.emit_err(OpaqueHiddenTypeDiag {
+                            span: cause.span,
+                            hidden_type: self.tcx.def_span(did2),
+                            opaque_type: self.tcx.def_span(def_id),
+                        });
                     }
                 }
                 Some(self.register_hidden_type(

From 3f6cb475f7aa1f4475fdb313316a7df644113376 Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Mon, 22 Aug 2022 00:17:46 +0300
Subject: [PATCH 21/36] Use GeneratorKind::descr() instead of it's Display impl

Those are basically the same but the first one seems to fit better
---
 compiler/rustc_infer/src/errors.rs                              | 2 +-
 .../rustc_infer/src/infer/error_reporting/need_type_info.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs
index 7bd418ddf5f4c..51993f37bb3a7 100644
--- a/compiler/rustc_infer/src/errors.rs
+++ b/compiler/rustc_infer/src/errors.rs
@@ -73,7 +73,7 @@ pub struct AmbigousReturn<'a> {
 pub struct NeedTypeInfoInGenerator<'a> {
     #[primary_span]
     pub span: Span,
-    pub generator_kind: String,
+    pub generator_kind: &'static str,
     #[subdiagnostic]
     pub bad_label: InferenceBadError<'a>,
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index da0035d2519f1..daf64aeb053d3 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -568,7 +568,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         NeedTypeInfoInGenerator {
             bad_label: data.make_bad_error(span),
             span,
-            generator_kind: kind.to_string(),
+            generator_kind: kind.descr(),
         }
         .into_diagnostic(&self.tcx.sess.parse_sess)
     }

From f50d1713fd4aab1ff9d160e9fa4b20cd2f71e68a Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Tue, 23 Aug 2022 13:48:14 +0300
Subject: [PATCH 22/36] Migrate note_region_origin function

---
 .../locales/en-US/infer.ftl                   |  43 ++++++++
 compiler/rustc_infer/src/errors.rs            |  64 ++++++++++-
 .../src/infer/error_reporting/mod.rs          |  18 ++++
 .../src/infer/error_reporting/note.rs         | 101 +++++++++---------
 4 files changed, 173 insertions(+), 53 deletions(-)

diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 9250363551dbb..770eaa62b4118 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -61,5 +61,48 @@ infer_source_kind_fully_qualified =
 infer_source_kind_closure_return =
     try giving this closure an explicit return type
 
+# generator_kind  may need to be translated
 infer_need_type_info_in_generator =
     type inside {$generator_kind} must be known in this context
+
+
+infer_subtype = ...so that the {$requirement ->
+    [method_compat] method type is compatible with trait
+    [type_compat] associated type is compatible with trait
+    [const_compat] const is compatible with trait
+    [expr_assignable] expression is assignable
+    [if_else_different] `if` and `else` have incompatible types
+    [no_else] `if` missing an `else` returns `()`
+    [fn_main_correct_type] `main` function has the correct type
+    [fn_start_correct_type] #[start]` function has the correct type
+    [intristic_correct_type] intrinsic has the correct type
+    [method_correct_type] method receiver has the correct type
+    *[other] types are compatible
+}
+infer_subtype_2 = ...so that {$requirement ->
+    [method_compat] method type is compatible with trait
+    [type_compat] associated type is compatible with trait
+    [const_compat] const is compatible with trait
+    [expr_assignable] expression is assignable
+    [if_else_different] `if` and `else` have incompatible types
+    [no_else] `if` missing an `else` returns `()`
+    [fn_main_correct_type] `main` function has the correct type
+    [fn_start_correct_type] #[start]` function has the correct type
+    [intristic_correct_type] intrinsic has the correct type
+    [method_correct_type] method receiver has the correct type
+    *[other] types are compatible
+}
+
+infer_reborrow = ...so that reference does not outlive borrowed content
+infer_reborrow_upvar = ...so that closure can access `{$name}`
+infer_relate_object_bound = ...so that it can be closed over into an object
+infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long
+infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at
+infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues ->
+[true] ...
+*[false] {""}
+}
+infer_relate_param_bound_2 = ...that is required by this bound
+infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
+infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
+
diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs
index 51993f37bb3a7..1db8763e4996d 100644
--- a/compiler/rustc_infer/src/errors.rs
+++ b/compiler/rustc_infer/src/errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{fluent, AddSubdiagnostic};
+use rustc_errors::{fluent, AddSubdiagnostic, DiagnosticMessage, DiagnosticStyledString};
 use rustc_hir::FnRetTy;
 use rustc_macros::SessionDiagnostic;
 use rustc_span::{BytePos, Span};
@@ -185,3 +185,65 @@ impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> {
         }
     }
 }
+
+pub enum RegionOriginNote<'a> {
+    Plain {
+        span: Span,
+        msg: DiagnosticMessage,
+    },
+    WithName {
+        span: Span,
+        msg: DiagnosticMessage,
+        name: &'a str,
+        continues: bool,
+    },
+    WithRequirement {
+        span: Span,
+        requirement: &'static str,
+        expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>,
+    },
+}
+
+impl AddSubdiagnostic for RegionOriginNote<'_> {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        let mut label_or_note = |span, msg: DiagnosticMessage| {
+            let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
+            let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
+            let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span);
+            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
+                diag.span_label(span, msg);
+            } else if span_is_primary && expanded_sub_count == 0 {
+                diag.note(msg);
+            } else {
+                diag.span_note(span, msg);
+            }
+        };
+        match self {
+            RegionOriginNote::Plain { span, msg } => {
+                label_or_note(span, msg);
+            }
+            RegionOriginNote::WithName { span, msg, name, continues } => {
+                label_or_note(span, msg);
+                diag.set_arg("name", name);
+                diag.set_arg("continues", continues);
+            }
+            RegionOriginNote::WithRequirement {
+                span,
+                requirement,
+                expected_found: Some((expected, found)),
+            } => {
+                label_or_note(span, fluent::infer::subtype);
+                diag.set_arg("requirement", requirement);
+
+                diag.note_expected_found(&"", expected, &"", found);
+            }
+            RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => {
+                // FIXME: this really should be handled at some earlier stage. Our
+                // handling of region checking when type errors are present is
+                // *terrible*.
+                label_or_note(span, fluent::infer::subtype_2);
+                diag.set_arg("requirement", requirement);
+            }
+        };
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 59ea1f3f9de45..13951326665d7 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2811,6 +2811,7 @@ pub enum FailureCode {
 pub trait ObligationCauseExt<'tcx> {
     fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
     fn as_requirement_str(&self) -> &'static str;
+    fn as_requirement_localised(&self) -> &'static str;
 }
 
 impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
@@ -2879,6 +2880,23 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
             _ => "types are compatible",
         }
     }
+
+    fn as_requirement_localised(&self) -> &'static str {
+        use crate::traits::ObligationCauseCode::*;
+        match self.code() {
+            CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => "method_compat",
+            CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => "type_compat",
+            CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => "const_compat",
+            ExprAssignable => "expr_assignable",
+            IfExpression { .. } => "if_else_different",
+            IfExpressionWithNoElse => "no_else",
+            MainFunctionType => "fn_main_correct_type",
+            StartFunctionType => "fn_start_correct_type",
+            IntrinsicType => "intristic_correct_type",
+            MethodReceiver => "method_correct_type",
+            _ => "other",
+        }
+    }
 }
 
 /// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 8c465b0876002..36efbd6824a77 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,13 +1,17 @@
+use crate::errors::RegionOriginNote;
 use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
 use crate::infer::{self, InferCtxt, SubregionOrigin};
-use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+    fluent, struct_span_err, AddSubdiagnostic, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
+    ErrorGuaranteed,
+};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, Region};
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
-        let mut label_or_note = |span, msg: &str| {
+        let mut label_or_note = |span, msg: DiagnosticMessage| {
             let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
             let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
             let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span);
@@ -20,77 +24,70 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             }
         };
         match *origin {
-            infer::Subtype(ref trace) => {
-                if let Some((expected, found)) = self.values_str(trace.values) {
-                    label_or_note(
-                        trace.cause.span,
-                        &format!("...so that the {}", trace.cause.as_requirement_str()),
-                    );
-
-                    err.note_expected_found(&"", expected, &"", found);
-                } else {
-                    // FIXME: this really should be handled at some earlier stage. Our
-                    // handling of region checking when type errors are present is
-                    // *terrible*.
-
-                    label_or_note(
-                        trace.cause.span,
-                        &format!("...so that {}", trace.cause.as_requirement_str()),
-                    );
-                }
+            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+                span: trace.cause.span,
+                requirement: trace.cause.as_requirement_localised(),
+                expected_found: self.values_str(trace.values),
             }
+            .add_to_diagnostic(err),
             infer::Reborrow(span) => {
-                label_or_note(span, "...so that reference does not outlive borrowed content");
+                label_or_note(span, fluent::infer::reborrow);
+                RegionOriginNote::Plain { span, msg: fluent::infer::reborrow }
+                    .add_to_diagnostic(err)
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
                 let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
-                label_or_note(span, &format!("...so that closure can access `{}`", var_name));
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer::reborrow,
+                    name: &var_name.to_string(),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
             infer::RelateObjectBound(span) => {
-                label_or_note(span, "...so that it can be closed over into an object");
+                label_or_note(span, fluent::infer::relate_object_bound);
+                RegionOriginNote::Plain { span, msg: fluent::infer::relate_object_bound }
+                    .add_to_diagnostic(err);
             }
             infer::DataBorrowed(ty, span) => {
-                label_or_note(
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the type `{}` is not borrowed for too long",
-                        self.ty_to_string(ty)
-                    ),
-                );
+                    msg: fluent::infer::data_borrowed,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                label_or_note(
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the reference type `{}` does not outlive the data it points at",
-                        self.ty_to_string(ty)
-                    ),
-                );
+                    msg: fluent::infer::reference_outlives_referent,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
-            infer::RelateParamBound(span, t, opt_span) => {
-                label_or_note(
+            infer::RelateParamBound(span, ty, opt_span) => {
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the type `{}` will meet its required lifetime bounds{}",
-                        self.ty_to_string(t),
-                        if opt_span.is_some() { "..." } else { "" },
-                    ),
-                );
+                    msg: fluent::infer::relate_param_bound,
+                    name: &self.ty_to_string(ty),
+                    continues: opt_span.is_some(),
+                }
+                .add_to_diagnostic(err);
                 if let Some(span) = opt_span {
-                    err.span_note(span, "...that is required by this bound");
+                    RegionOriginNote::Plain { span, msg: fluent::infer::relate_param_bound_2 }
+                        .add_to_diagnostic(err);
                 }
             }
             infer::RelateRegionParamBound(span) => {
-                label_or_note(
-                    span,
-                    "...so that the declared lifetime parameter bounds are satisfied",
-                );
+                RegionOriginNote::Plain { span, msg: fluent::infer::relate_region_param_bound }
+                    .add_to_diagnostic(err);
             }
             infer::CompareImplItemObligation { span, .. } => {
-                label_or_note(
-                    span,
-                    "...so that the definition in impl matches the definition from the trait",
-                );
+                RegionOriginNote::Plain { span, msg: fluent::infer::compare_impl_item_obligation }
+                    .add_to_diagnostic(err);
             }
             infer::CheckAssociatedTypeBounds { ref parent, .. } => {
                 self.note_region_origin(err, &parent);

From 74f99738244fc9ba2f6ad93b8c891d44d638b0f8 Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Tue, 23 Aug 2022 15:33:06 +0300
Subject: [PATCH 23/36] Fix formating in infer.ftl to make tidy happy

---
 .../rustc_error_messages/locales/en-US/infer.ftl  | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 770eaa62b4118..6ae60d92e26ee 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -1,4 +1,4 @@
-infer_opaque_hidden_type = 
+infer_opaque_hidden_type =
     opaque type's hidden type cannot be another opaque type from the same scope
     .label = one of the two opaque types used here has to be outside its defining scope
     .opaque_type = opaque type whose hidden type is being assigned
@@ -8,7 +8,7 @@ infer_type_annotations_needed = {$source_kind ->
     [closure] type annotations needed for the closure `{$source_name}`
     [normal] type annotations needed for `{$source_name}`
     *[other] type annotations needed
-} 
+}
     .label = type must be known at this point
 
 infer_label_bad = {$bad_kind ->
@@ -38,24 +38,24 @@ infer_source_kind_subdiag_let = {$kind ->
 }
 
 infer_source_kind_subdiag_generic_label =
-    cannot infer {$is_type -> 
+    cannot infer {$is_type ->
     [true] type
     *[false] the value
-    } of the {$is_type -> 
+    } of the {$is_type ->
     [true] type
     *[false] const
     } {$parent_exists ->
     [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
-    *[false] parameter {$param_name} 
+    *[false] parameter {$param_name}
     }
 
 infer_source_kind_subdiag_generic_suggestion =
-    consider specifying the generic {$arg_count -> 
+    consider specifying the generic {$arg_count ->
     [one] argument
     *[other] arguments
     }
 
-infer_source_kind_fully_qualified = 
+infer_source_kind_fully_qualified =
     try using a fully qualified path to specify the expected types
 
 infer_source_kind_closure_return =
@@ -105,4 +105,3 @@ infer_relate_param_bound = ...so that the type `{$name}` will meet its required
 infer_relate_param_bound_2 = ...that is required by this bound
 infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
 infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
-

From 3fae3904b130272c782255066f79a13fa9fcdad6 Mon Sep 17 00:00:00 2001
From: Nikita Tomashevich <quant3234@gmail.com>
Date: Wed, 24 Aug 2022 15:46:29 +0300
Subject: [PATCH 24/36] Use `IntoDiagnosticArg` where it makes sense

---
 .../locales/en-US/infer.ftl                   |  7 +++-
 compiler/rustc_infer/src/errors.rs            | 13 +++++--
 .../src/infer/error_reporting/mod.rs          | 16 +++++---
 .../infer/error_reporting/need_type_info.rs   | 37 ++++++++++++++-----
 .../src/infer/error_reporting/note.rs         | 29 ++++-----------
 5 files changed, 61 insertions(+), 41 deletions(-)

diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 6ae60d92e26ee..60086cd6e477f 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -63,7 +63,12 @@ infer_source_kind_closure_return =
 
 # generator_kind  may need to be translated
 infer_need_type_info_in_generator =
-    type inside {$generator_kind} must be known in this context
+    type inside {$generator_kind ->
+    [async_block] `async` block
+    [async_closure] `async` closure
+    [async_fn] `async fn` body
+    *[generator] generator
+    } must be known in this context
 
 
 infer_subtype = ...so that the {$requirement ->
diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs
index 1db8763e4996d..938f8aa77a5b4 100644
--- a/compiler/rustc_infer/src/errors.rs
+++ b/compiler/rustc_infer/src/errors.rs
@@ -3,6 +3,11 @@ use rustc_hir::FnRetTy;
 use rustc_macros::SessionDiagnostic;
 use rustc_span::{BytePos, Span};
 
+use crate::infer::error_reporting::{
+    need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind},
+    ObligationCauseAsDiagArg,
+};
+
 #[derive(SessionDiagnostic)]
 #[diag(infer::opaque_hidden_type)]
 pub struct OpaqueHiddenTypeDiag {
@@ -73,7 +78,7 @@ pub struct AmbigousReturn<'a> {
 pub struct NeedTypeInfoInGenerator<'a> {
     #[primary_span]
     pub span: Span,
-    pub generator_kind: &'static str,
+    pub generator_kind: GeneratorKindAsDiagArg,
     #[subdiagnostic]
     pub bad_label: InferenceBadError<'a>,
 }
@@ -85,7 +90,7 @@ pub struct InferenceBadError<'a> {
     #[primary_span]
     pub span: Span,
     pub bad_kind: &'static str,
-    pub prefix_kind: &'static str,
+    pub prefix_kind: UnderspecifiedArgKind,
     pub has_parent: bool,
     pub prefix: &'a str,
     pub parent_prefix: &'a str,
@@ -107,7 +112,7 @@ pub enum SourceKindSubdiag<'a> {
         type_name: String,
         kind: &'static str,
         x_kind: &'static str,
-        prefix_kind: &'static str,
+        prefix_kind: UnderspecifiedArgKind,
         prefix: &'a str,
         arg_name: String,
     },
@@ -199,7 +204,7 @@ pub enum RegionOriginNote<'a> {
     },
     WithRequirement {
         span: Span,
-        requirement: &'static str,
+        requirement: ObligationCauseAsDiagArg<'a>,
         expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>,
     },
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 13951326665d7..c7e258578e4de 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -58,7 +58,7 @@ use crate::traits::{
 };
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -78,7 +78,7 @@ use std::{cmp, fmt, iter};
 
 mod note;
 
-mod need_type_info;
+pub(crate) mod need_type_info;
 pub use need_type_info::TypeAnnotationNeeded;
 
 pub mod nice_region_error;
@@ -2811,7 +2811,6 @@ pub enum FailureCode {
 pub trait ObligationCauseExt<'tcx> {
     fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
     fn as_requirement_str(&self) -> &'static str;
-    fn as_requirement_localised(&self) -> &'static str;
 }
 
 impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
@@ -2880,10 +2879,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
             _ => "types are compatible",
         }
     }
+}
+
+/// Newtype to allow implementing IntoDiagnosticArg
+pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>);
 
-    fn as_requirement_localised(&self) -> &'static str {
+impl IntoDiagnosticArg for ObligationCauseAsDiagArg<'_> {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
         use crate::traits::ObligationCauseCode::*;
-        match self.code() {
+        let kind = match self.0.code() {
             CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => "method_compat",
             CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => "type_compat",
             CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => "const_compat",
@@ -2896,6 +2900,8 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
             MethodReceiver => "method_correct_type",
             _ => "other",
         }
+        .into();
+        rustc_errors::DiagnosticArgValue::Str(kind)
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index daf64aeb053d3..e990fe7ecb504 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -4,7 +4,7 @@ use crate::errors::{
 };
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
@@ -65,6 +65,7 @@ pub struct InferenceDiagnosticsParentData {
     name: String,
 }
 
+#[derive(Clone)]
 pub enum UnderspecifiedArgKind {
     Type { prefix: Cow<'static, str> },
     Const { is_parameter: bool },
@@ -101,7 +102,7 @@ impl InferenceDiagnosticsData {
         InferenceBadError {
             span,
             bad_kind,
-            prefix_kind: self.kind.prefix_kind(),
+            prefix_kind: self.kind.clone(),
             prefix: self.kind.try_get_prefix().unwrap_or_default(),
             name: self.name.clone(),
             has_parent,
@@ -130,14 +131,18 @@ impl InferenceDiagnosticsParentData {
     }
 }
 
-impl UnderspecifiedArgKind {
-    fn prefix_kind(&self) -> &'static str {
-        match self {
+impl IntoDiagnosticArg for UnderspecifiedArgKind {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        let kind = match self {
             Self::Type { .. } => "type",
             Self::Const { is_parameter: true } => "const_with_param",
             Self::Const { is_parameter: false } => "const",
-        }
+        };
+        rustc_errors::DiagnosticArgValue::Str(kind.into())
     }
+}
+
+impl UnderspecifiedArgKind {
     fn try_get_prefix(&self) -> Option<&str> {
         match self {
             Self::Type { prefix } => Some(prefix.as_ref()),
@@ -405,7 +410,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     span: insert_span,
                     name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
                     x_kind: arg_data.where_x_is_kind(ty),
-                    prefix_kind: arg_data.kind.prefix_kind(),
+                    prefix_kind: arg_data.kind.clone(),
                     prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
                     arg_name: arg_data.name,
                     kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
@@ -417,7 +422,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     span: insert_span,
                     name: String::new(),
                     x_kind: arg_data.where_x_is_kind(ty),
-                    prefix_kind: arg_data.kind.prefix_kind(),
+                    prefix_kind: arg_data.kind.clone(),
                     prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
                     arg_name: arg_data.name,
                     kind: "closure",
@@ -568,12 +573,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         NeedTypeInfoInGenerator {
             bad_label: data.make_bad_error(span),
             span,
-            generator_kind: kind.descr(),
+            generator_kind: GeneratorKindAsDiagArg(kind),
         }
         .into_diagnostic(&self.tcx.sess.parse_sess)
     }
 }
 
+pub struct GeneratorKindAsDiagArg(pub hir::GeneratorKind);
+
+impl IntoDiagnosticArg for GeneratorKindAsDiagArg {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        let kind = match self.0 {
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block",
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure",
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn",
+            hir::GeneratorKind::Gen => "generator",
+        };
+        rustc_errors::DiagnosticArgValue::Str(kind.into())
+    }
+}
+
 #[derive(Debug)]
 struct InferSource<'tcx> {
     span: Span,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 36efbd6824a77..cffdf56bb6d48 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,40 +1,26 @@
 use crate::errors::RegionOriginNote;
-use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
+use crate::infer::error_reporting::note_and_explain_region;
 use crate::infer::{self, InferCtxt, SubregionOrigin};
 use rustc_errors::{
-    fluent, struct_span_err, AddSubdiagnostic, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
-    ErrorGuaranteed,
+    fluent, struct_span_err, AddSubdiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
 };
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, Region};
 
+use super::ObligationCauseAsDiagArg;
+
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
-        let mut label_or_note = |span, msg: DiagnosticMessage| {
-            let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
-            let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
-            let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span);
-            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
-                err.span_label(span, msg);
-            } else if span_is_primary && expanded_sub_count == 0 {
-                err.note(msg);
-            } else {
-                err.span_note(span, msg);
-            }
-        };
         match *origin {
             infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
                 span: trace.cause.span,
-                requirement: trace.cause.as_requirement_localised(),
+                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
                 expected_found: self.values_str(trace.values),
             }
             .add_to_diagnostic(err),
-            infer::Reborrow(span) => {
-                label_or_note(span, fluent::infer::reborrow);
-                RegionOriginNote::Plain { span, msg: fluent::infer::reborrow }
-                    .add_to_diagnostic(err)
-            }
+            infer::Reborrow(span) => RegionOriginNote::Plain { span, msg: fluent::infer::reborrow }
+                .add_to_diagnostic(err),
             infer::ReborrowUpvar(span, ref upvar_id) => {
                 let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 RegionOriginNote::WithName {
@@ -46,7 +32,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 .add_to_diagnostic(err);
             }
             infer::RelateObjectBound(span) => {
-                label_or_note(span, fluent::infer::relate_object_bound);
                 RegionOriginNote::Plain { span, msg: fluent::infer::relate_object_bound }
                     .add_to_diagnostic(err);
             }

From e1765a9c56d8d9b235bc5b8fda2a0f1d4e92ff49 Mon Sep 17 00:00:00 2001
From: IQuant <quant3234@gmail.com>
Date: Wed, 24 Aug 2022 17:09:07 +0300
Subject: [PATCH 25/36] Remove commented lines

---
 compiler/rustc_infer/src/lib.rs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 2c1d339b578fe..eed2efd3ee0c0 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -23,8 +23,6 @@
 #![feature(never_type)]
 #![feature(try_blocks)]
 #![recursion_limit = "512"] // For rustdoc
-// #![deny(rustc::untranslatable_diagnostic)]
-// #![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_macros;

From b508b50617bd6a976f77d33962a6f0df52eee12d Mon Sep 17 00:00:00 2001
From: Luis Cardoso <61982523+LuisCardosoOliveira@users.noreply.github.com>
Date: Wed, 24 Aug 2022 17:57:10 +0200
Subject: [PATCH 26/36] translations: rename warn_ to warning

The macro warn_ was named like that because it the
keyword warn is a built-in attribute and at the time
this macro was created the word 'warning' was also
taken.

However it is no longer the case and we can rename
warn_ to warning.
---
 .../src/diagnostics/diagnostic_builder.rs     | 31 ++++++++++---------
 .../src/diagnostics/subdiagnostic.rs          |  4 +--
 compiler/rustc_macros/src/lib.rs              |  6 ++--
 compiler/rustc_passes/src/errors.rs           | 16 +++++-----
 .../session-diagnostic/diagnostic-derive.rs   | 10 +++---
 .../diagnostic-derive.stderr                  | 28 ++++++++---------
 .../subdiagnostic-derive.rs                   |  7 ++---
 7 files changed, 52 insertions(+), 50 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 9df9fa4e9bf74..a4ccfcace1923 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -148,9 +148,9 @@ impl DiagnosticDeriveBuilder {
             // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug.
             Meta::List(MetaList { ref nested, .. }) => nested,
             // Subdiagnostics without spans can be applied to the type too, and these are just
-            // paths: `#[help]`, `#[note]` and `#[warn_]`
+            // paths: `#[help]`, `#[note]` and `#[warning]`
             Meta::Path(_) if !is_diag => {
-                let fn_name = if name == "warn_" {
+                let fn_name = if name == "warning" {
                     Ident::new("warn", attr.span())
                 } else {
                     Ident::new(name, attr.span())
@@ -163,12 +163,15 @@ impl DiagnosticDeriveBuilder {
         // Check the kind before doing any further processing so that there aren't misleading
         // "no kind specified" errors if there are failures later.
         match name {
-            "error" | "warning" | "lint" => throw_invalid_attr!(attr, &meta, |diag| {
-                diag.help("`error`, `warning` and `lint` have been replaced by `diag`")
+            "error" | "lint" => throw_invalid_attr!(attr, &meta, |diag| {
+                diag.help("`error` and `lint` have been replaced by `diag`")
             }),
-            "diag" | "help" | "note" | "warn_" => (),
+            "warn_" => throw_invalid_attr!(attr, &meta, |diag| {
+                diag.help("`warn_` have been replaced by `warning`")
+            }),
+            "diag" | "help" | "note" | "warning" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
-                diag.help("only `diag`, `help`, `note` and `warn_` are valid attributes")
+                diag.help("only `diag`, `help`, `note` and `warning` are valid attributes")
             }),
         }
 
@@ -180,7 +183,7 @@ impl DiagnosticDeriveBuilder {
             if !is_diag && nested_iter.next().is_some() {
                 throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
                     diag.help(
-                        "`help`, `note` and `warn_` struct attributes can only have one argument",
+                        "`help`, `note` and `warning` struct attributes can only have one argument",
                     )
                 });
             }
@@ -348,12 +351,12 @@ impl DiagnosticDeriveBuilder {
                 report_error_if_not_applied_to_span(attr, &info)?;
                 Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
             }
-            "note" | "help" | "warn_" => {
+            "note" | "help" | "warning" => {
                 let warn_ident = Ident::new("warn", Span::call_site());
                 let (ident, path) = match name {
                     "note" => (ident, parse_quote! { _subdiag::note }),
                     "help" => (ident, parse_quote! { _subdiag::help }),
-                    "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
+                    "warning" => (&warn_ident, parse_quote! { _subdiag::warn }),
                     _ => unreachable!(),
                 };
                 if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@@ -390,7 +393,7 @@ impl DiagnosticDeriveBuilder {
             "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
                 return self.generate_inner_field_code_suggestion(attr, info);
             }
-            "label" | "help" | "note" | "warn_" => (),
+            "label" | "help" | "note" | "warning" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
                 diag.help(
                     "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
@@ -422,14 +425,14 @@ impl DiagnosticDeriveBuilder {
                 Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
             }
             "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
-            // `warn_` must be special-cased because the attribute `warn` already has meaning and
+            // `warning` must be special-cased because the attribute `warn` already has meaning and
             // so isn't used, despite the diagnostic API being named `warn`.
-            "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
+            "warning" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
                 .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
-            "warn_" if type_is_unit(&info.ty) => {
+            "warning" if type_is_unit(&info.ty) => {
                 Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
             }
-            "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
+            "note" | "help" | "warning" => report_type_error(attr, "`Span` or `()`")?,
             _ => unreachable!(),
         }
     }
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index edf4dbed9853e..666dbc23c287c 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -37,7 +37,7 @@ enum SubdiagnosticKind {
     Note,
     /// `#[help(...)]`
     Help,
-    /// `#[warn_(...)]`
+    /// `#[warning(...)]`
     Warn,
     /// `#[suggestion{,_short,_hidden,_verbose}]`
     Suggestion(SubdiagnosticSuggestionKind),
@@ -51,7 +51,7 @@ impl FromStr for SubdiagnosticKind {
             "label" => Ok(SubdiagnosticKind::Label),
             "note" => Ok(SubdiagnosticKind::Note),
             "help" => Ok(SubdiagnosticKind::Help),
-            "warn_" => Ok(SubdiagnosticKind::Warn),
+            "warning" => Ok(SubdiagnosticKind::Warn),
             "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
             "suggestion_short" => {
                 Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 87d7ab6ed517b..8faac8ef36a53 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -132,7 +132,7 @@ decl_derive!(
         diag,
         help,
         note,
-        warn_,
+        warning,
         // field attributes
         skip_arg,
         primary_span,
@@ -149,7 +149,7 @@ decl_derive!(
         diag,
         help,
         note,
-        warn_,
+        warning,
         // field attributes
         skip_arg,
         primary_span,
@@ -166,7 +166,7 @@ decl_derive!(
         label,
         help,
         note,
-        warn_,
+        warning,
         suggestion,
         suggestion_short,
         suggestion_hidden,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 383982013d9e7..901f56ad96d17 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -28,7 +28,7 @@ pub struct IgnoredInlineAttrFnProto;
 
 #[derive(LintDiagnostic)]
 #[diag(passes::inline_ignored_constants)]
-#[warn_]
+#[warning]
 #[note]
 pub struct IgnoredInlineAttrConstants;
 
@@ -347,7 +347,7 @@ pub struct MustNotSuspend {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::cold)]
-#[warn_]
+#[warning]
 pub struct Cold {
     #[label]
     pub span: Span,
@@ -355,7 +355,7 @@ pub struct Cold {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::link)]
-#[warn_]
+#[warning]
 pub struct Link {
     #[label]
     pub span: Option<Span>,
@@ -363,7 +363,7 @@ pub struct Link {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::link_name)]
-#[warn_]
+#[warning]
 pub struct LinkName<'a> {
     #[help]
     pub attr_span: Option<Span>,
@@ -449,7 +449,7 @@ pub struct RustcDirtyClean {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::link_section)]
-#[warn_]
+#[warning]
 pub struct LinkSection {
     #[label]
     pub span: Span,
@@ -457,7 +457,7 @@ pub struct LinkSection {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::no_mangle_foreign)]
-#[warn_]
+#[warning]
 #[note]
 pub struct NoMangleForeign {
     #[label]
@@ -469,7 +469,7 @@ pub struct NoMangleForeign {
 
 #[derive(LintDiagnostic)]
 #[diag(passes::no_mangle)]
-#[warn_]
+#[warning]
 pub struct NoMangle {
     #[label]
     pub span: Span,
@@ -617,7 +617,7 @@ pub struct UnusedDuplicate {
     pub this: Span,
     #[note]
     pub other: Span,
-    #[warn_]
+    #[warning]
     pub warning: Option<()>,
 }
 
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index aaa8caa64f343..b1f557cb94de8 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -549,7 +549,7 @@ struct ErrorWithMultiSpan {
 
 #[derive(SessionDiagnostic)]
 #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")]
-#[warn_]
+#[warning]
 struct ErrorWithWarn {
     val: String,
 }
@@ -562,11 +562,11 @@ struct ErrorWithWarn {
 struct ErrorAttribute {}
 
 #[derive(SessionDiagnostic)]
-#[warning(typeck::ambiguous_lifetime_bound, code = "E0123")]
-//~^ ERROR `#[warning(...)]` is not a valid attribute
+#[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")]
+//~^ ERROR `#[warn_(...)]` is not a valid attribute
 //~| ERROR diagnostic slug not specified
-//~| ERROR cannot find attribute `warning` in this scope
-struct WarningAttribute {}
+//~| ERROR cannot find attribute `warn_` in this scope
+struct WarnAttribute {}
 
 #[derive(SessionDiagnostic)]
 #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")]
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index 866b1a1de999c..621c59f448951 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute
 LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: only `diag`, `help`, `note` and `warn_` are valid attributes
+   = help: only `diag`, `help`, `note` and `warning` are valid attributes
 
 error: diagnostic slug not specified
   --> $DIR/diagnostic-derive.rs:53:1
@@ -329,7 +329,7 @@ error: `#[error(...)]` is not a valid attribute
 LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: `error`, `warning` and `lint` have been replaced by `diag`
+   = help: `error` and `lint` have been replaced by `diag`
 
 error: diagnostic slug not specified
   --> $DIR/diagnostic-derive.rs:558:1
@@ -343,23 +343,23 @@ LL | | struct ErrorAttribute {}
    |
    = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]`
 
-error: `#[warning(...)]` is not a valid attribute
+error: `#[warn_(...)]` is not a valid attribute
   --> $DIR/diagnostic-derive.rs:565:1
    |
-LL | #[warning(typeck::ambiguous_lifetime_bound, code = "E0123")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: `error`, `warning` and `lint` have been replaced by `diag`
+   = help: `warn_` have been replaced by `warning`
 
 error: diagnostic slug not specified
   --> $DIR/diagnostic-derive.rs:565:1
    |
-LL | / #[warning(typeck::ambiguous_lifetime_bound, code = "E0123")]
+LL | / #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")]
 LL | |
 LL | |
 LL | |
-LL | | struct WarningAttribute {}
-   | |__________________________^
+LL | | struct WarnAttribute {}
+   | |_______________________^
    |
    = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]`
 
@@ -369,7 +369,7 @@ error: `#[lint(...)]` is not a valid attribute
 LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: `error`, `warning` and `lint` have been replaced by `diag`
+   = help: `error` and `lint` have been replaced by `diag`
 
 error: diagnostic slug not specified
   --> $DIR/diagnostic-derive.rs:572:1
@@ -389,7 +389,7 @@ error: `#[lint(...)]` is not a valid attribute
 LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: `error`, `warning` and `lint` have been replaced by `diag`
+   = help: `error` and `lint` have been replaced by `diag`
 
 error: diagnostic slug not specified
   --> $DIR/diagnostic-derive.rs:579:1
@@ -421,11 +421,11 @@ error: cannot find attribute `error` in this scope
 LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")]
    |   ^^^^^
 
-error: cannot find attribute `warning` in this scope
+error: cannot find attribute `warn_` in this scope
   --> $DIR/diagnostic-derive.rs:565:3
    |
-LL | #[warning(typeck::ambiguous_lifetime_bound, code = "E0123")]
-   |   ^^^^^^^
+LL | #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")]
+   |   ^^^^^ help: a built-in attribute with a similar name exists: `warn`
 
 error: cannot find attribute `lint` in this scope
   --> $DIR/diagnostic-derive.rs:572:3
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
index 16da25c402b57..ddfc0d3365df0 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -510,12 +510,11 @@ enum AX {
 }
 
 #[derive(SessionSubdiagnostic)]
-#[warn_(parser::add_paren)]
-struct AY {
-}
+#[warning(parser::add_paren)]
+struct AY {}
 
 #[derive(SessionSubdiagnostic)]
-#[warn_(parser::add_paren)]
+#[warning(parser::add_paren)]
 struct AZ {
     #[primary_span]
     span: Span,

From 4e976262a1598fa48cd08fa0e429f05299d1ae3e Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 02:00:37 +0000
Subject: [PATCH 27/36] Call them constants instead of types

---
 .../src/infer/error_reporting/mod.rs          |  5 +++
 .../generic-expr-default-concrete.stderr      |  4 +--
 ...neric-expr-default-mismatched-types.stderr |  4 +--
 .../abstract-const-as-cast-3.stderr           | 32 +++++++++----------
 .../generic_const_exprs/different-fn.stderr   |  4 +--
 .../issue-62504.full.stderr                   |  4 +--
 ...ue-72819-generic-in-const-eval.full.stderr |  8 ++---
 .../generic_const_exprs/issue-83765.stderr    |  8 ++---
 .../generic_const_exprs/issue-85848.stderr    |  4 +--
 .../const-generics/issues/issue-73260.stderr  |  8 ++---
 .../const-generics/issues/issue-79674.stderr  |  4 +--
 .../types-mismatch-const-args.full.stderr     |  4 +--
 12 files changed, 47 insertions(+), 42 deletions(-)

diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 59ea1f3f9de45..16cffb45f0f06 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1588,9 +1588,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             Mismatch::Variable(infer::ExpectedFound { expected, found }),
                         )
                     }
+                    ValuePairs::Terms(infer::ExpectedFound {
+                        expected: ty::Term::Const(_),
+                        found: ty::Term::Const(_),
+                    }) => (false, Mismatch::Fixed("constant")),
                     ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => {
                         (false, Mismatch::Fixed("trait"))
                     }
+                    ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
                     _ => (false, Mismatch::Fixed("type")),
                 };
                 let vals = match self.values_str(values) {
diff --git a/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr b/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
index e8826ce4335e7..61b3551182c90 100644
--- a/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
+++ b/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     Foo::<10, 12>
    |     ^^^^^^^^^^^^^ expected `11`, found `12`
    |
-   = note: expected type `11`
-              found type `12`
+   = note: expected constant `11`
+              found constant `12`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr b/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
index d5a3071b77d15..e83f89a60333f 100644
--- a/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
+++ b/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     Foo::<N, { N + 2 }>
    |     ^^^^^^^^^^^^^^^^^^^ expected `{ N + 1 }`, found `{ N + 2 }`
    |
-   = note: expected type `{ N + 1 }`
-              found type `{ N + 2 }`
+   = note: expected constant `{ N + 1 }`
+              found constant `{ N + 2 }`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
index 615dc875f67a3..9e1297d5ee4bf 100644
--- a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
@@ -22,8 +22,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as u128 }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as u128 }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as u128 }`
+              found constant `{ O as u128 }`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:20:19
@@ -49,8 +49,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as _ }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as _ }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as _ }`
+              found constant `{ O as u128 }`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:23:5
@@ -58,8 +58,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `12`, found `13`
    |
-   = note: expected type `12`
-              found type `13`
+   = note: expected constant `12`
+              found constant `13`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:25:5
@@ -67,8 +67,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `13`, found `14`
    |
-   = note: expected type `13`
-              found type `14`
+   = note: expected constant `13`
+              found constant `14`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:35:19
@@ -94,8 +94,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as u128 }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as u128 }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as u128 }`
+              found constant `{ O as u128 }`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:38:19
@@ -121,8 +121,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as _ }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as _ }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as _ }`
+              found constant `{ O as u128 }`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:41:5
@@ -130,8 +130,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `12`, found `13`
    |
-   = note: expected type `12`
-              found type `13`
+   = note: expected constant `12`
+              found constant `13`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:43:5
@@ -139,8 +139,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `13`, found `14`
    |
-   = note: expected type `13`
-              found type `14`
+   = note: expected constant `13`
+              found constant `14`
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr b/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
index 2aeb9b961ffde..83a2f3740b146 100644
--- a/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     [0; size_of::<Foo<T>>()]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::<T>()`, found `size_of::<Foo<T>>()`
    |
-   = note: expected type `size_of::<T>()`
-              found type `size_of::<Foo<T>>()`
+   = note: expected constant `size_of::<T>()`
+              found constant `size_of::<Foo<T>>()`
 
 error: unconstrained generic constant
   --> $DIR/different-fn.rs:10:9
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
index f2ae361dc81d9..0742db398c9c4 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |         ArrayHolder([0; Self::SIZE])
    |                     ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
    |
-   = note: expected type `X`
-              found type `Self::SIZE`
+   = note: expected constant `X`
+              found constant `Self::SIZE`
 
 error: unconstrained generic constant
   --> $DIR/issue-62504.rs:18:25
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
index d536f6fd1d557..38dfa65e4091f 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |            ^^^^^^^^^^^^^^^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error[E0308]: mismatched types
   --> $DIR/issue-72819-generic-in-const-eval.rs:20:32
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |                                ^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
index 0332e82fe0727..b693023f125a4 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
@@ -4,8 +4,8 @@ error[E0308]: method not compatible with trait
 LL |     fn size(&self) -> [usize; DIM] {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
+   = note: expected constant `Self::DIM`
+              found constant `DIM`
 
 error: unconstrained generic constant
   --> $DIR/issue-83765.rs:32:24
@@ -26,8 +26,8 @@ error[E0308]: mismatched types
 LL |         self.reference.size()
    |         ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM`
    |
-   = note: expected type `DIM`
-              found type `Self::DIM`
+   = note: expected constant `DIM`
+              found constant `Self::DIM`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
index 808b305c680f3..09bcb0860b71b 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
@@ -54,8 +54,8 @@ error[E0308]: mismatched types
 LL |     writes_to_specific_path(&cap);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }`
    |
-   = note: expected type `true`
-              found type `{ contains::<T, U>() }`
+   = note: expected constant `true`
+              found constant `{ contains::<T, U>() }`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-73260.stderr b/src/test/ui/const-generics/issues/issue-73260.stderr
index f1fc50e6e5914..3d1f90271f9a3 100644
--- a/src/test/ui/const-generics/issues/issue-73260.stderr
+++ b/src/test/ui/const-generics/issues/issue-73260.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |            ^^^^^^^^^^^^^^^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error[E0308]: mismatched types
   --> $DIR/issue-73260.rs:16:32
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |                                ^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-79674.stderr b/src/test/ui/const-generics/issues/issue-79674.stderr
index 8c029289cbb0d..344b2c5631064 100644
--- a/src/test/ui/const-generics/issues/issue-79674.stderr
+++ b/src/test/ui/const-generics/issues/issue-79674.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     requires_distinct("str", 12);
    |     ^^^^^^^^^^^^^^^^^ expected `true`, found `false`
    |
-   = note: expected type `true`
-              found type `false`
+   = note: expected constant `true`
+              found constant `false`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
index 486506239ddfd..b6a22df74369a 100644
--- a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
+++ b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {2u32 + 2u32}, {3u32}> { data: PhantomData };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `4`
    |
-   = note: expected type `2`
-              found type `4`
+   = note: expected constant `2`
+              found constant `4`
 
 error[E0308]: mismatched types
   --> $DIR/types-mismatch-const-args.rs:16:41

From 4ff587263e0a7f2081e2ad5fd3e88460a94adbb5 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 03:13:45 +0000
Subject: [PATCH 28/36] Note binding obligation causes for const equate errors

---
 compiler/rustc_middle/src/traits/mod.rs       |  7 ++++
 .../src/traits/error_reporting/mod.rs         | 19 +++++++--
 .../abstract-const-as-cast-3.stderr           | 40 +++++++++++++++++++
 ...ue-72819-generic-in-const-eval.full.stderr | 14 +++++++
 .../const-generics/issues/issue-73260.stderr  | 16 ++++++++
 .../const-generics/issues/issue-79674.stderr  |  8 ++++
 6 files changed, 101 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 9b82320e556b3..ab7e5ba3a1067 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -469,6 +469,13 @@ impl<'tcx> ObligationCauseCode<'tcx> {
             _ => None,
         }
     }
+
+    pub fn peel_match_impls(&self) -> &Self {
+        match self {
+            MatchImpl(cause, _) => cause.code(),
+            _ => self,
+        }
+    }
 }
 
 // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 70fac83325a9c..1f031d33e0653 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1506,13 +1506,26 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 .emit();
             }
             FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
-                self.report_mismatched_consts(
+                let mut diag = self.report_mismatched_consts(
                     &error.obligation.cause,
                     expected_found.expected,
                     expected_found.found,
                     err.clone(),
-                )
-                .emit();
+                );
+                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
+                if let ObligationCauseCode::BindingObligation(..)
+                | ObligationCauseCode::ItemObligation(..) = code
+                {
+                    self.note_obligation_cause_code(
+                        &mut diag,
+                        &error.obligation.predicate,
+                        error.obligation.param_env,
+                        code,
+                        &mut vec![],
+                        &mut Default::default(),
+                    );
+                }
+                diag.emit();
             }
         }
     }
diff --git a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
index 9e1297d5ee4bf..ada1050d35f35 100644
--- a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
@@ -24,6 +24,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |
    = note: expected constant `{ N as u128 }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:20:19
@@ -51,6 +56,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |
    = note: expected constant `{ N as _ }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:23:5
@@ -60,6 +70,11 @@ LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |
    = note: expected constant `12`
               found constant `13`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:25:5
@@ -69,6 +84,11 @@ LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |
    = note: expected constant `13`
               found constant `14`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:35:19
@@ -96,6 +116,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |
    = note: expected constant `{ N as u128 }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:38:19
@@ -123,6 +148,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |
    = note: expected constant `{ N as _ }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:41:5
@@ -132,6 +162,11 @@ LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |
    = note: expected constant `12`
               found constant `13`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:43:5
@@ -141,6 +176,11 @@ LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |
    = note: expected constant `13`
               found constant `14`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
index 38dfa65e4091f..f2fddfbfbb52a 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
@@ -6,6 +6,13 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-72819-generic-in-const-eval.rs:8:39
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                       ^^^^^^ required by this bound in `Arr`
 
 error[E0308]: mismatched types
   --> $DIR/issue-72819-generic-in-const-eval.rs:20:32
@@ -15,6 +22,13 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-72819-generic-in-const-eval.rs:8:39
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                       ^^^^^^ required by this bound in `Arr`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-73260.stderr b/src/test/ui/const-generics/issues/issue-73260.stderr
index 3d1f90271f9a3..7670032e5b758 100644
--- a/src/test/ui/const-generics/issues/issue-73260.stderr
+++ b/src/test/ui/const-generics/issues/issue-73260.stderr
@@ -6,6 +6,14 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-73260.rs:6:37
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where
+LL |     Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                     ^^^^^^ required by this bound in `Arr`
 
 error[E0308]: mismatched types
   --> $DIR/issue-73260.rs:16:32
@@ -15,6 +23,14 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-73260.rs:6:37
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where
+LL |     Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                     ^^^^^^ required by this bound in `Arr`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-79674.stderr b/src/test/ui/const-generics/issues/issue-79674.stderr
index 344b2c5631064..02b48b55f8b38 100644
--- a/src/test/ui/const-generics/issues/issue-79674.stderr
+++ b/src/test/ui/const-generics/issues/issue-79674.stderr
@@ -6,6 +6,14 @@ LL |     requires_distinct("str", 12);
    |
    = note: expected constant `true`
               found constant `false`
+note: required by a bound in `requires_distinct`
+  --> $DIR/issue-79674.rs:23:37
+   |
+LL | fn requires_distinct<A, B>(_a: A, _b: B) where
+   |    ----------------- required by a bound in this
+LL |     A: MiniTypeId, B: MiniTypeId,
+LL |     Lift<{is_same_type::<A, B>()}>: IsFalse {}
+   |                                     ^^^^^^^ required by this bound in `requires_distinct`
 
 error: aborting due to previous error
 

From d464d3a700138b48e13d62846a35165d661e2ea4 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 03:41:57 +0000
Subject: [PATCH 29/36] Add test for #100414

---
 .../generic_const_exprs/obligation-cause.rs   | 24 +++++++++++++++++++
 .../obligation-cause.stderr                   | 20 ++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
 create mode 100644 src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr

diff --git a/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
new file mode 100644
index 0000000000000..e7c8e4f667d05
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
@@ -0,0 +1,24 @@
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+
+trait True {}
+
+struct Is<const V: bool>;
+
+impl True for Is<true> {}
+
+fn g<T>()
+//~^ NOTE required by a bound in this
+where
+    Is<{ std::mem::size_of::<T>() == 0 }>: True,
+    //~^ NOTE required by a bound in `g`
+    //~| NOTE required by this bound in `g`
+{
+}
+
+fn main() {
+    g::<usize>();
+    //~^ ERROR mismatched types
+    //~| NOTE expected `false`, found `true`
+    //~| NOTE expected constant `false`
+}
diff --git a/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr
new file mode 100644
index 0000000000000..a253ec676f716
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/obligation-cause.rs:20:5
+   |
+LL |     g::<usize>();
+   |     ^^^^^^^^^^ expected `false`, found `true`
+   |
+   = note: expected constant `false`
+              found constant `true`
+note: required by a bound in `g`
+  --> $DIR/obligation-cause.rs:13:44
+   |
+LL | fn g<T>()
+   |    - required by a bound in this
+...
+LL |     Is<{ std::mem::size_of::<T>() == 0 }>: True,
+   |                                            ^^^^ required by this bound in `g`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.

From 8189a4536be3730ecc161c77fc655b08be179b50 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 23 Aug 2022 05:38:18 +0000
Subject: [PATCH 30/36] Use ExprItemObligation and ExprBindingObligation too

---
 .../rustc_trait_selection/src/traits/error_reporting/mod.rs   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 1f031d33e0653..e4af7022239b8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1514,7 +1514,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 );
                 let code = error.obligation.cause.code().peel_derives().peel_match_impls();
                 if let ObligationCauseCode::BindingObligation(..)
-                | ObligationCauseCode::ItemObligation(..) = code
+                | ObligationCauseCode::ItemObligation(..)
+                | ObligationCauseCode::ExprBindingObligation(..)
+                | ObligationCauseCode::ExprItemObligation(..) = code
                 {
                     self.note_obligation_cause_code(
                         &mut diag,

From 252c65e9d24f8a6e309c46547bf618a45ab43206 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Thu, 25 Aug 2022 14:03:13 +0400
Subject: [PATCH 31/36] Fix clippy tests that trigger `for_loop_over_fallibles`
 lint

---
 .../clippy/tests/ui/for_loop_unfixable.rs     |  1 +
 .../clippy/tests/ui/for_loop_unfixable.stderr |  2 +-
 .../tests/ui/for_loops_over_fallibles.rs      |  1 +
 .../tests/ui/for_loops_over_fallibles.stderr  | 22 +++++++++----------
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
index efcaffce24ea4..203656fa4d6c2 100644
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.rs
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
@@ -8,6 +8,7 @@
     clippy::for_kv_map
 )]
 #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+#[allow(for_loop_over_fallibles)]
 fn main() {
     let vec = vec![1, 2, 3, 4];
 
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
index f769b4bdc9411..50a86eaa68f7d 100644
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
@@ -1,5 +1,5 @@
 error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
-  --> $DIR/for_loop_unfixable.rs:14:15
+  --> $DIR/for_loop_unfixable.rs:15:15
    |
 LL |     for _v in vec.iter().next() {}
    |               ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
index 3390111d0a8fe..3c7733e665356 100644
--- a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::for_loops_over_fallibles)]
+#![allow(for_loop_over_fallibles)]
 
 fn for_loops_over_fallibles() {
     let option = Some(1);
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
index 8c8c022243aeb..5f5a81d24cfde 100644
--- a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
@@ -1,5 +1,5 @@
 error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:9:14
+  --> $DIR/for_loops_over_fallibles.rs:10:14
    |
 LL |     for x in option {
    |              ^^^^^^
@@ -8,7 +8,7 @@ LL |     for x in option {
    = help: consider replacing `for x in option` with `if let Some(x) = option`
 
 error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:14:14
+  --> $DIR/for_loops_over_fallibles.rs:15:14
    |
 LL |     for x in option.iter() {
    |              ^^^^^^
@@ -16,7 +16,7 @@ LL |     for x in option.iter() {
    = help: consider replacing `for x in option.iter()` with `if let Some(x) = option`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:19:14
+  --> $DIR/for_loops_over_fallibles.rs:20:14
    |
 LL |     for x in result {
    |              ^^^^^^
@@ -24,7 +24,7 @@ LL |     for x in result {
    = help: consider replacing `for x in result` with `if let Ok(x) = result`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:24:14
+  --> $DIR/for_loops_over_fallibles.rs:25:14
    |
 LL |     for x in result.iter_mut() {
    |              ^^^^^^
@@ -32,7 +32,7 @@ LL |     for x in result.iter_mut() {
    = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:29:14
+  --> $DIR/for_loops_over_fallibles.rs:30:14
    |
 LL |     for x in result.into_iter() {
    |              ^^^^^^
@@ -40,7 +40,7 @@ LL |     for x in result.into_iter() {
    = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result`
 
 error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:33:14
+  --> $DIR/for_loops_over_fallibles.rs:34:14
    |
 LL |     for x in option.ok_or("x not found") {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     for x in option.ok_or("x not found") {
    = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
 
 error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
-  --> $DIR/for_loops_over_fallibles.rs:39:14
+  --> $DIR/for_loops_over_fallibles.rs:40:14
    |
 LL |     for x in v.iter().next() {
    |              ^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     for x in v.iter().next() {
    = note: `#[deny(clippy::iter_next_loop)]` on by default
 
 error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:44:14
+  --> $DIR/for_loops_over_fallibles.rs:45:14
    |
 LL |     for x in v.iter().next().and(Some(0)) {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     for x in v.iter().next().and(Some(0)) {
    = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
 
 error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:48:14
+  --> $DIR/for_loops_over_fallibles.rs:49:14
    |
 LL |     for x in v.iter().next().ok_or("x not found") {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     for x in v.iter().next().ok_or("x not found") {
    = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
 
 error: this loop never actually loops
-  --> $DIR/for_loops_over_fallibles.rs:60:5
+  --> $DIR/for_loops_over_fallibles.rs:61:5
    |
 LL | /     while let Some(x) = option {
 LL | |         println!("{}", x);
@@ -83,7 +83,7 @@ LL | |     }
    = note: `#[deny(clippy::never_loop)]` on by default
 
 error: this loop never actually loops
-  --> $DIR/for_loops_over_fallibles.rs:66:5
+  --> $DIR/for_loops_over_fallibles.rs:67:5
    |
 LL | /     while let Ok(x) = result {
 LL | |         println!("{}", x);

From 8bb4b5f44c8b7c811e2075022e5205f927287e0a Mon Sep 17 00:00:00 2001
From: marmeladema <xademax@gmail.com>
Date: Sat, 12 Mar 2022 19:32:41 +0100
Subject: [PATCH 32/36] Support parsing IP addresses from a byte string

---
 library/std/src/net/parser.rs | 138 ++++++++++++++++++++++++++++++----
 1 file changed, 125 insertions(+), 13 deletions(-)

diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs
index 069b660998559..a38031c48c862 100644
--- a/library/std/src/net/parser.rs
+++ b/library/std/src/net/parser.rs
@@ -39,8 +39,8 @@ struct Parser<'a> {
 }
 
 impl<'a> Parser<'a> {
-    fn new(input: &'a str) -> Parser<'a> {
-        Parser { state: input.as_bytes() }
+    fn new(input: &'a [u8]) -> Parser<'a> {
+        Parser { state: input }
     }
 
     /// Run a parser, and restore the pre-parse state if it fails.
@@ -273,32 +273,106 @@ impl<'a> Parser<'a> {
     }
 }
 
+impl IpAddr {
+    /// Parse an IP address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+    ///
+    /// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
+    /// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+    ///
+    /// assert_eq!(IpAddr::parse_ascii(b"127.0.0.1"), Ok(localhost_v4));
+    /// assert_eq!(IpAddr::parse_ascii(b"::1"), Ok(localhost_v6));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+        Parser::new(b).parse_with(|p| p.read_ip_addr(), AddrKind::Ip)
+    }
+}
+
 #[stable(feature = "ip_addr", since = "1.7.0")]
 impl FromStr for IpAddr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_ip_addr(), AddrKind::Ip)
+        Self::parse_ascii(s.as_bytes())
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl FromStr for Ipv4Addr {
-    type Err = AddrParseError;
-    fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
+impl Ipv4Addr {
+    /// Parse an IPv4 address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::Ipv4Addr;
+    ///
+    /// let localhost = Ipv4Addr::new(127, 0, 0, 1);
+    ///
+    /// assert_eq!(Ipv4Addr::parse_ascii(b"127.0.0.1"), Ok(localhost));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
         // don't try to parse if too long
-        if s.len() > 15 {
+        if b.len() > 15 {
             Err(AddrParseError(AddrKind::Ipv4))
         } else {
-            Parser::new(s).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
+            Parser::new(b).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
         }
     }
 }
 
+#[stable(feature = "rust1", since = "1.0.0")]
+impl FromStr for Ipv4Addr {
+    type Err = AddrParseError;
+    fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
+        Self::parse_ascii(s.as_bytes())
+    }
+}
+
+impl Ipv6Addr {
+    /// Parse an IPv6 address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::Ipv6Addr;
+    ///
+    /// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    ///
+    /// assert_eq!(Ipv6Addr::parse_ascii(b"::1"), Ok(localhost));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+        Parser::new(b).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl FromStr for Ipv6Addr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
+        Self::parse_ascii(s.as_bytes())
+    }
+}
+
+impl SocketAddrV4 {
+    /// Parse an IPv4 socket address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::{Ipv4Addr, SocketAddrV4};
+    ///
+    /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+    ///
+    /// assert_eq!(SocketAddrV4::parse_ascii(b"127.0.0.1:8080"), Ok(socket));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+        Parser::new(b).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4)
     }
 }
 
@@ -306,7 +380,25 @@ impl FromStr for Ipv6Addr {
 impl FromStr for SocketAddrV4 {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4)
+        Self::parse_ascii(s.as_bytes())
+    }
+}
+
+impl SocketAddrV6 {
+    /// Parse an IPv6 socket address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::{Ipv6Addr, SocketAddrV6};
+    ///
+    /// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+    ///
+    /// assert_eq!(SocketAddrV6::parse_ascii(b"[2001:db8::1]:8080"), Ok(socket));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+        Parser::new(b).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6)
     }
 }
 
@@ -314,7 +406,27 @@ impl FromStr for SocketAddrV4 {
 impl FromStr for SocketAddrV6 {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6)
+        Self::parse_ascii(s.as_bytes())
+    }
+}
+
+impl SocketAddr {
+    /// Parse a socket address from a slice of bytes.
+    ///
+    /// ```
+    /// #![feature(addr_parse_ascii)]
+    ///
+    /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+    ///
+    /// let socket_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+    /// let socket_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
+    ///
+    /// assert_eq!(SocketAddr::parse_ascii(b"127.0.0.1:8080"), Ok(socket_v4));
+    /// assert_eq!(SocketAddr::parse_ascii(b"[::1]:8080"), Ok(socket_v6));
+    /// ```
+    #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+    pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+        Parser::new(b).parse_with(|p| p.read_socket_addr(), AddrKind::Socket)
     }
 }
 
@@ -322,7 +434,7 @@ impl FromStr for SocketAddrV6 {
 impl FromStr for SocketAddr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr(), AddrKind::Socket)
+        Self::parse_ascii(s.as_bytes())
     }
 }
 

From b48870b451dd9d3f3f827aa54d9becdfdd811ba3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Tue, 5 Jul 2022 00:00:00 +0000
Subject: [PATCH 33/36] Replace `Body::basic_blocks()` with field access

---
 .../rustc_borrowck/src/constraint_generation.rs  |  2 +-
 compiler/rustc_borrowck/src/dataflow.rs          |  2 +-
 .../src/diagnostics/explain_borrow.rs            |  6 +++---
 .../src/diagnostics/move_errors.rs               |  2 +-
 compiler/rustc_borrowck/src/location.rs          |  2 +-
 .../rustc_borrowck/src/region_infer/values.rs    |  4 ++--
 compiler/rustc_borrowck/src/type_check/mod.rs    |  2 +-
 compiler/rustc_codegen_cranelift/src/analyze.rs  |  2 +-
 compiler/rustc_codegen_cranelift/src/base.rs     |  4 ++--
 compiler/rustc_codegen_cranelift/src/constant.rs |  2 +-
 compiler/rustc_codegen_ssa/src/mir/analyze.rs    |  6 +++---
 compiler/rustc_codegen_ssa/src/mir/mod.rs        |  8 ++++----
 .../src/interpret/eval_context.rs                |  2 +-
 .../src/interpret/intrinsics/caller_location.rs  |  2 +-
 compiler/rustc_const_eval/src/interpret/step.rs  |  2 +-
 .../src/transform/check_consts/check.rs          |  2 +-
 .../src/transform/promote_consts.rs              |  6 +++---
 .../rustc_const_eval/src/transform/validate.rs   |  8 ++++----
 compiler/rustc_middle/src/mir/generic_graph.rs   |  4 ++--
 compiler/rustc_middle/src/mir/mod.rs             |  7 +------
 compiler/rustc_middle/src/mir/patch.rs           | 10 +++++-----
 compiler/rustc_middle/src/mir/pretty.rs          |  4 ++--
 compiler/rustc_middle/src/mir/spanview.rs        |  2 +-
 compiler/rustc_middle/src/mir/traversal.rs       |  4 ++--
 compiler/rustc_middle/src/mir/visit.rs           |  2 +-
 compiler/rustc_mir_build/src/build/mod.rs        |  2 +-
 .../rustc_mir_dataflow/src/framework/engine.rs   |  9 ++++-----
 .../rustc_mir_dataflow/src/framework/graphviz.rs |  4 ++--
 .../rustc_mir_dataflow/src/framework/tests.rs    | 10 ++++------
 .../rustc_mir_dataflow/src/move_paths/builder.rs |  2 +-
 .../rustc_mir_dataflow/src/move_paths/mod.rs     |  2 +-
 compiler/rustc_mir_dataflow/src/rustc_peek.rs    |  2 +-
 compiler/rustc_mir_dataflow/src/storage.rs       |  2 +-
 .../src/abort_unwinding_calls.rs                 |  2 +-
 .../rustc_mir_transform/src/add_call_guards.rs   |  2 +-
 .../src/add_moves_for_packed_drops.rs            |  2 +-
 compiler/rustc_mir_transform/src/const_goto.rs   |  4 ++--
 compiler/rustc_mir_transform/src/const_prop.rs   |  2 +-
 .../rustc_mir_transform/src/const_prop_lint.rs   |  4 ++--
 .../rustc_mir_transform/src/coverage/graph.rs    |  4 ++--
 compiler/rustc_mir_transform/src/coverage/mod.rs |  2 +-
 .../rustc_mir_transform/src/coverage/query.rs    |  4 ++--
 .../rustc_mir_transform/src/coverage/tests.rs    |  6 +++---
 .../src/deduplicate_blocks.rs                    |  5 ++---
 compiler/rustc_mir_transform/src/dest_prop.rs    |  6 +++---
 .../src/early_otherwise_branch.rs                |  6 +++---
 .../rustc_mir_transform/src/elaborate_drops.rs   | 12 ++++++------
 .../rustc_mir_transform/src/ffi_unwind_calls.rs  |  2 +-
 compiler/rustc_mir_transform/src/generator.rs    | 14 +++++++-------
 compiler/rustc_mir_transform/src/inline.rs       | 16 ++++++++--------
 compiler/rustc_mir_transform/src/inline/cycle.rs |  2 +-
 .../src/multiple_return_terminators.rs           |  2 +-
 .../src/normalize_array_len.rs                   |  4 ++--
 compiler/rustc_mir_transform/src/nrvo.rs         |  4 ++--
 .../src/remove_noop_landing_pads.rs              |  2 +-
 .../src/remove_uninit_drops.rs                   |  2 +-
 .../src/separate_const_switch.rs                 |  4 ++--
 compiler/rustc_mir_transform/src/simplify.rs     |  4 ++--
 .../src/simplify_comparison_integral.rs          |  2 +-
 compiler/rustc_mir_transform/src/simplify_try.rs |  4 ++--
 .../src/uninhabited_enum_branching.rs            |  6 +++---
 .../rustc_monomorphize/src/partitioning/mod.rs   |  2 +-
 compiler/rustc_ty_utils/src/ty.rs                |  2 +-
 .../clippy/clippy_lints/src/redundant_clone.rs   |  6 +++---
 .../clippy_utils/src/qualify_min_const_fn.rs     |  2 +-
 65 files changed, 131 insertions(+), 140 deletions(-)

diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs
index 5e9cec5c350c9..144fd15fc2407 100644
--- a/compiler/rustc_borrowck/src/constraint_generation.rs
+++ b/compiler/rustc_borrowck/src/constraint_generation.rs
@@ -31,7 +31,7 @@ pub(super) fn generate_constraints<'cx, 'tcx>(
         body,
     };
 
-    for (bb, data) in body.basic_blocks().iter_enumerated() {
+    for (bb, data) in body.basic_blocks.iter_enumerated() {
         cg.visit_basic_block_data(bb, data);
     }
 }
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 97d5a8d158e1e..816288eb50b29 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -143,7 +143,7 @@ struct OutOfScopePrecomputer<'a, 'tcx> {
 impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> {
     fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
         OutOfScopePrecomputer {
-            visited: BitSet::new_empty(body.basic_blocks().len()),
+            visited: BitSet::new_empty(body.basic_blocks.len()),
             visit_stack: vec![],
             body,
             regioncx,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 68f9a7c5007c8..a6b8c6057e05e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -459,7 +459,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 return outmost_back_edge;
             }
 
-            let block = &self.body.basic_blocks()[location.block];
+            let block = &self.body.basic_blocks[location.block];
 
             if location.statement_index < block.statements.len() {
                 let successor = location.successor_within_block();
@@ -518,7 +518,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
 
         if loop_head.dominates(from, &self.dominators) {
-            let block = &self.body.basic_blocks()[from.block];
+            let block = &self.body.basic_blocks[from.block];
 
             if from.statement_index < block.statements.len() {
                 let successor = from.successor_within_block();
@@ -568,7 +568,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             UseSpans::PatUse(span)
             | UseSpans::OtherUse(span)
             | UseSpans::FnSelfUse { var_span: span, .. } => {
-                let block = &self.body.basic_blocks()[location.block];
+                let block = &self.body.basic_blocks[location.block];
 
                 let kind = if let Some(&Statement {
                     kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index cb3cd479ae2b6..16c2f9ccc6aa4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -88,7 +88,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 if let Some(StatementKind::Assign(box (
                     place,
                     Rvalue::Use(Operand::Move(move_from)),
-                ))) = self.body.basic_blocks()[location.block]
+                ))) = self.body.basic_blocks[location.block]
                     .statements
                     .get(location.statement_index)
                     .map(|stmt| &stmt.kind)
diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs
index 70a3116949894..5ca3f2f4d033c 100644
--- a/compiler/rustc_borrowck/src/location.rs
+++ b/compiler/rustc_borrowck/src/location.rs
@@ -33,7 +33,7 @@ impl LocationTable {
     pub(crate) fn new(body: &Body<'_>) -> Self {
         let mut num_points = 0;
         let statements_before_block = body
-            .basic_blocks()
+            .basic_blocks
             .iter()
             .map(|block_data| {
                 let v = num_points;
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index c81ef10f7c740..de20a4bb465c2 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -25,7 +25,7 @@ impl RegionValueElements {
     pub(crate) fn new(body: &Body<'_>) -> Self {
         let mut num_points = 0;
         let statements_before_block: IndexVec<BasicBlock, usize> = body
-            .basic_blocks()
+            .basic_blocks
             .iter()
             .map(|block_data| {
                 let v = num_points;
@@ -37,7 +37,7 @@ impl RegionValueElements {
         debug!("RegionValueElements: num_points={:#?}", num_points);
 
         let mut basic_blocks = IndexVec::with_capacity(num_points);
-        for (bb, bb_data) in body.basic_blocks().iter_enumerated() {
+        for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
             basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb));
         }
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 293d847ec9ab7..c1812aa4bbab6 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2633,7 +2633,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             self.check_local(&body, local, local_decl);
         }
 
-        for (block, block_data) in body.basic_blocks().iter_enumerated() {
+        for (block, block_data) in body.basic_blocks.iter_enumerated() {
             let mut location = Location { block, statement_index: 0 };
             for stmt in &block_data.statements {
                 if !stmt.source_info.span.is_dummy() {
diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs
index 35b89358b1984..0cbb9f3ec2d80 100644
--- a/compiler/rustc_codegen_cranelift/src/analyze.rs
+++ b/compiler/rustc_codegen_cranelift/src/analyze.rs
@@ -26,7 +26,7 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec<Local, SsaKind> {
         })
         .collect::<IndexVec<Local, SsaKind>>();
 
-    for bb in fx.mir.basic_blocks().iter() {
+    for bb in fx.mir.basic_blocks.iter() {
         for stmt in bb.statements.iter() {
             match &stmt.kind {
                 Assign(place_and_rval) => match &place_and_rval.1 {
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 44c34d6c8cb79..3011813c7035b 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -73,7 +73,7 @@ pub(crate) fn codegen_fn<'tcx>(
     // Predefine blocks
     let start_block = bcx.create_block();
     let block_map: IndexVec<BasicBlock, Block> =
-        (0..mir.basic_blocks().len()).map(|_| bcx.create_block()).collect();
+        (0..mir.basic_blocks.len()).map(|_| bcx.create_block()).collect();
 
     // Make FunctionCx
     let target_config = module.target_config();
@@ -271,7 +271,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
     }
     fx.tcx.sess.time("codegen prelude", || crate::abi::codegen_fn_prelude(fx, start_block));
 
-    for (bb, bb_data) in fx.mir.basic_blocks().iter_enumerated() {
+    for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() {
         let block = fx.get_block(bb);
         fx.bcx.switch_to_block(block);
 
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 7f7fd0e9c579d..e2b68f24a21dc 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -505,7 +505,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
                 return None;
             }
             let mut computed_const_val = None;
-            for bb_data in fx.mir.basic_blocks() {
+            for bb_data in fx.mir.basic_blocks.iter() {
                 for stmt in &bb_data.statements {
                     match &stmt.kind {
                         StatementKind::Assign(local_and_rvalue) if &local_and_rvalue.0 == place => {
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 24da48ead63a2..c7617d2e464fa 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -266,7 +266,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
         result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
         mir: &mir::Body<'tcx>,
     ) {
-        for (bb, data) in mir.basic_blocks().iter_enumerated() {
+        for (bb, data) in mir.basic_blocks.iter_enumerated() {
             match data.terminator().kind {
                 TerminatorKind::Goto { .. }
                 | TerminatorKind::Resume
@@ -296,7 +296,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
     }
 
     fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, mir: &mir::Body<'tcx>) {
-        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
+        let mut funclet_succs = IndexVec::from_elem(None, &mir.basic_blocks);
 
         let mut set_successor = |funclet: mir::BasicBlock, succ| match funclet_succs[funclet] {
             ref mut s @ None => {
@@ -359,7 +359,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
         }
     }
 
-    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
+    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, &mir.basic_blocks);
 
     discover_masters(&mut result, mir);
     propagate(&mut result, mir);
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 8ee375fa9e3e9..ac0eb30e748b8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -150,13 +150,13 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let start_llbb = Bx::append_block(cx, llfn, "start");
     let mut bx = Bx::build(cx, start_llbb);
 
-    if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) {
+    if mir.basic_blocks.iter().any(|bb| bb.is_cleanup) {
         bx.set_personality_fn(cx.eh_personality());
     }
 
     let cleanup_kinds = analyze::cleanup_kinds(&mir);
     let cached_llbbs: IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>> = mir
-        .basic_blocks()
+        .basic_blocks
         .indices()
         .map(|bb| if bb == mir::START_BLOCK { Some(start_llbb) } else { None })
         .collect();
@@ -172,8 +172,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         unreachable_block: None,
         double_unwind_guard: None,
         cleanup_kinds,
-        landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
-        funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks().len()),
+        landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
+        funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
         locals: IndexVec::new(),
         debug_context,
         per_local_var_debug_info: None,
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 150d6589b0807..594bfd13f1d87 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -782,7 +782,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         assert_eq!(
             unwinding,
             match self.frame().loc {
-                Ok(loc) => self.body().basic_blocks()[loc.block].is_cleanup,
+                Ok(loc) => self.body().basic_blocks[loc.block].is_cleanup,
                 Err(_) => true,
             }
         );
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
index 5864b92155287..91f4f04251721 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -28,7 +28,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let mut source_info = *frame.body.source_info(loc);
 
             // If this is a `Call` terminator, use the `fn_span` instead.
-            let block = &frame.body.basic_blocks()[loc.block];
+            let block = &frame.body.basic_blocks[loc.block];
             if loc.statement_index == block.statements.len() {
                 debug!(
                     "find_closest_untracked_caller_location: got terminator {:?} ({:?})",
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index fea158a9fe450..683e11ff7e0e5 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -53,7 +53,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             self.pop_stack_frame(/* unwinding */ true)?;
             return Ok(true);
         };
-        let basic_block = &self.body().basic_blocks()[loc.block];
+        let basic_block = &self.body().basic_blocks[loc.block];
 
         if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
             let old_frames = self.frame_idx();
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 0adb88a180f8b..cbfdb47dd1a40 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -135,7 +135,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
         // qualifs for the return type.
         let return_block = ccx
             .body
-            .basic_blocks()
+            .basic_blocks
             .iter_enumerated()
             .find(|(_, block)| matches!(block.terminator().kind, TerminatorKind::Return))
             .map(|(bb, _)| bb);
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 161c89e3242b9..6301388d1e840 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -710,7 +710,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
     }
 
     fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
-        let last = self.promoted.basic_blocks().last().unwrap();
+        let last = self.promoted.basic_blocks.last().unwrap();
         let data = &mut self.promoted[last];
         data.statements.push(Statement {
             source_info: SourceInfo::outermost(span),
@@ -803,7 +803,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                         self.visit_operand(arg, loc);
                     }
 
-                    let last = self.promoted.basic_blocks().last().unwrap();
+                    let last = self.promoted.basic_blocks.last().unwrap();
                     let new_target = self.new_block();
 
                     *self.promoted[last].terminator_mut() = Terminator {
@@ -1041,7 +1041,7 @@ pub fn is_const_fn_in_array_repeat_expression<'tcx>(
         _ => {}
     }
 
-    for block in body.basic_blocks() {
+    for block in body.basic_blocks.iter() {
         if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) =
             &block.terminator
         {
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 1a14cd79fa069..45a94972c1134 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -140,8 +140,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         if bb == START_BLOCK {
             self.fail(location, "start block must not have predecessors")
         }
-        if let Some(bb) = self.body.basic_blocks().get(bb) {
-            let src = self.body.basic_blocks().get(location.block).unwrap();
+        if let Some(bb) = self.body.basic_blocks.get(bb) {
+            let src = self.body.basic_blocks.get(location.block).unwrap();
             match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                 // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                 (false, false, EdgeKind::Normal)
@@ -881,13 +881,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             }
             TerminatorKind::Resume | TerminatorKind::Abort => {
                 let bb = location.block;
-                if !self.body.basic_blocks()[bb].is_cleanup {
+                if !self.body.basic_blocks[bb].is_cleanup {
                     self.fail(location, "Cannot `Resume` or `Abort` from non-cleanup basic block")
                 }
             }
             TerminatorKind::Return => {
                 let bb = location.block;
-                if self.body.basic_blocks()[bb].is_cleanup {
+                if self.body.basic_blocks[bb].is_cleanup {
                     self.fail(location, "Cannot `Return` from cleanup basic block")
                 }
             }
diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs
index f3621cd99d344..d1f3561c02c5d 100644
--- a/compiler/rustc_middle/src/mir/generic_graph.rs
+++ b/compiler/rustc_middle/src/mir/generic_graph.rs
@@ -12,14 +12,14 @@ pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Grap
 
     // Nodes
     let nodes: Vec<Node> = body
-        .basic_blocks()
+        .basic_blocks
         .iter_enumerated()
         .map(|(block, _)| bb_to_graph_node(block, body, dark_mode))
         .collect();
 
     // Edges
     let mut edges = Vec::new();
-    for (source, _) in body.basic_blocks().iter_enumerated() {
+    for (source, _) in body.basic_blocks.iter_enumerated() {
         let def_id = body.source.def_id();
         let terminator = body[source].terminator();
         let labels = terminator.kind.fmt_successor_labels();
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 9da9b4e91f647..f7a1e9b286488 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -331,11 +331,6 @@ impl<'tcx> Body<'tcx> {
         body
     }
 
-    #[inline]
-    pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
-        &self.basic_blocks
-    }
-
     #[inline]
     pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
         self.basic_blocks.as_mut()
@@ -490,7 +485,7 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> {
 
     #[inline]
     fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.basic_blocks()[index]
+        &self.basic_blocks[index]
     }
 }
 
diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs
index 8aa761aae8d90..24fe3b47256e2 100644
--- a/compiler/rustc_middle/src/mir/patch.rs
+++ b/compiler/rustc_middle/src/mir/patch.rs
@@ -19,7 +19,7 @@ pub struct MirPatch<'tcx> {
 impl<'tcx> MirPatch<'tcx> {
     pub fn new(body: &Body<'tcx>) -> Self {
         let mut result = MirPatch {
-            patch_map: IndexVec::from_elem(None, body.basic_blocks()),
+            patch_map: IndexVec::from_elem(None, &body.basic_blocks),
             new_blocks: vec![],
             new_statements: vec![],
             new_locals: vec![],
@@ -29,7 +29,7 @@ impl<'tcx> MirPatch<'tcx> {
         };
 
         // Check if we already have a resume block
-        for (bb, block) in body.basic_blocks().iter_enumerated() {
+        for (bb, block) in body.basic_blocks.iter_enumerated() {
             if let TerminatorKind::Resume = block.terminator().kind && block.statements.is_empty() {
                 result.resume_block = Some(bb);
                 break;
@@ -61,7 +61,7 @@ impl<'tcx> MirPatch<'tcx> {
     }
 
     pub fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location {
-        let offset = match bb.index().checked_sub(body.basic_blocks().len()) {
+        let offset = match bb.index().checked_sub(body.basic_blocks.len()) {
             Some(index) => self.new_blocks[index].statements.len(),
             None => body[bb].statements.len(),
         };
@@ -129,7 +129,7 @@ impl<'tcx> MirPatch<'tcx> {
         debug!(
             "MirPatch: {} new blocks, starting from index {}",
             self.new_blocks.len(),
-            body.basic_blocks().len()
+            body.basic_blocks.len()
         );
         let bbs = if self.patch_map.is_empty() && self.new_blocks.is_empty() {
             body.basic_blocks.as_mut_preserves_cfg()
@@ -173,7 +173,7 @@ impl<'tcx> MirPatch<'tcx> {
     }
 
     pub fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo {
-        let data = match loc.block.index().checked_sub(body.basic_blocks().len()) {
+        let data = match loc.block.index().checked_sub(body.basic_blocks.len()) {
             Some(new) => &self.new_blocks[new],
             None => &body[loc.block],
         };
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 0ce41337b910d..da6af89b09b9b 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -318,10 +318,10 @@ where
     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
 {
     write_mir_intro(tcx, body, w)?;
-    for block in body.basic_blocks().indices() {
+    for block in body.basic_blocks.indices() {
         extra_data(PassWhere::BeforeBlock(block), w)?;
         write_basic_block(tcx, block, body, extra_data, w)?;
-        if block.index() + 1 != body.basic_blocks().len() {
+        if block.index() + 1 != body.basic_blocks.len() {
             writeln!(w)?;
         }
     }
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
index 4418b848e512a..6e64a3b80c1fb 100644
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -105,7 +105,7 @@ where
     }
     let body_span = hir_body.unwrap().value.span;
     let mut span_viewables = Vec::new();
-    for (bb, data) in body.basic_blocks().iter_enumerated() {
+    for (bb, data) in body.basic_blocks.iter_enumerated() {
         match spanview {
             MirSpanview::Statement => {
                 for (i, statement) in data.statements.iter().enumerate() {
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 627dc32f37eb5..55b2c592795bf 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -37,7 +37,7 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> {
 
         Preorder {
             body,
-            visited: BitSet::new_empty(body.basic_blocks().len()),
+            visited: BitSet::new_empty(body.basic_blocks.len()),
             worklist,
             root_is_start_block: root == START_BLOCK,
         }
@@ -71,7 +71,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         // All the blocks, minus the number of blocks we've visited.
-        let upper = self.body.basic_blocks().len() - self.visited.count();
+        let upper = self.body.basic_blocks.len() - self.visited.count();
 
         let lower = if self.root_is_start_block {
             // We will visit all remaining blocks exactly once.
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 4a85defb1ed84..7bd65f42e3f92 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -951,7 +951,7 @@ macro_rules! basic_blocks {
         $body.basic_blocks.as_mut_preserves_cfg()
     };
     ($body:ident,) => {
-        $body.basic_blocks()
+        $body.basic_blocks
     };
 }
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 461c837f6df19..684b228e87fa9 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -272,7 +272,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
         // by borrow checking.
         debug_assert!(
             !(body.local_decls.has_free_regions()
-                || body.basic_blocks().has_free_regions()
+                || body.basic_blocks.has_free_regions()
                 || body.var_debug_info.has_free_regions()
                 || body.yield_ty().has_free_regions()),
             "Unexpected free regions in MIR: {:?}",
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index f374658ceb691..ecfa90371f5ac 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -108,9 +108,9 @@ where
         // Otherwise, compute and store the cumulative transfer function for each block.
 
         let identity = GenKillSet::identity(analysis.bottom_value(body).domain_size());
-        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());
+        let mut trans_for_block = IndexVec::from_elem(identity, &body.basic_blocks);
 
-        for (block, block_data) in body.basic_blocks().iter_enumerated() {
+        for (block, block_data) in body.basic_blocks.iter_enumerated() {
             let trans = &mut trans_for_block[block];
             A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
         }
@@ -144,7 +144,7 @@ where
         apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
     ) -> Self {
         let bottom_value = analysis.bottom_value(body);
-        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
+        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), &body.basic_blocks);
         analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
 
         if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != bottom_value {
@@ -197,8 +197,7 @@ where
             ..
         } = self;
 
-        let mut dirty_queue: WorkQueue<BasicBlock> =
-            WorkQueue::with_none(body.basic_blocks().len());
+        let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
 
         if A::Direction::IS_FORWARD {
             for (bb, _) in traversal::reverse_postorder(body) {
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index c94198c56a8d1..579fe68a14935 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -108,12 +108,12 @@ where
     type Edge = CfgEdge;
 
     fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
-        self.body.basic_blocks().indices().collect::<Vec<_>>().into()
+        self.body.basic_blocks.indices().collect::<Vec<_>>().into()
     }
 
     fn edges(&self) -> dot::Edges<'_, Self::Edge> {
         self.body
-            .basic_blocks()
+            .basic_blocks
             .indices()
             .flat_map(|bb| dataflow_successors(self.body, bb))
             .collect::<Vec<_>>()
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index d9461fd3abd81..17102454a88de 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -100,9 +100,9 @@ impl<D: Direction> MockAnalysis<'_, D> {
 
     fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
         let empty = self.bottom_value(self.body);
-        let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
+        let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
 
-        for (bb, _) in self.body.basic_blocks().iter_enumerated() {
+        for (bb, _) in self.body.basic_blocks.iter_enumerated() {
             ret[bb] = self.mock_entry_set(bb);
         }
 
@@ -169,7 +169,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
     const NAME: &'static str = "mock";
 
     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
-        BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len())
+        BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks.len())
     }
 
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
@@ -271,9 +271,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
     cursor.allow_unreachable();
 
     let every_target = || {
-        body.basic_blocks()
-            .iter_enumerated()
-            .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
+        body.basic_blocks.iter_enumerated().flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
     };
 
     let mut seek_to_target = |targ| {
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 116e5c1f3ce0a..c325838622399 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -243,7 +243,7 @@ pub(super) fn gather_moves<'tcx>(
 
     builder.gather_args();
 
-    for (bb, block) in body.basic_blocks().iter_enumerated() {
+    for (bb, block) in body.basic_blocks.iter_enumerated() {
         for (i, stmt) in block.statements.iter().enumerate() {
             let source = Location { block: bb, statement_index: i };
             builder.gather_statement(source, stmt);
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
index a951c5b0b1c94..b36e268cf8bf7 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
@@ -217,7 +217,7 @@ where
     fn new(body: &Body<'_>) -> Self {
         LocationMap {
             map: body
-                .basic_blocks()
+                .basic_blocks
                 .iter()
                 .map(|block| vec![T::default(); block.statements.len() + 1])
                 .collect(),
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index f2471f37a5266..382d371840fa9 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -97,7 +97,7 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>(
 
     let mut cursor = ResultsCursor::new(body, results);
 
-    let peek_calls = body.basic_blocks().iter_enumerated().filter_map(|(bb, block_data)| {
+    let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
         PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
     });
 
diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs
index c909648ea017e..e5a0e1d312eae 100644
--- a/compiler/rustc_mir_dataflow/src/storage.rs
+++ b/compiler/rustc_mir_dataflow/src/storage.rs
@@ -7,7 +7,7 @@ use rustc_middle::mir::{self, Local};
 pub fn always_storage_live_locals(body: &mir::Body<'_>) -> BitSet<Local> {
     let mut always_live_locals = BitSet::new_filled(body.local_decls.len());
 
-    for block in body.basic_blocks() {
+    for block in &*body.basic_blocks {
         for statement in &block.statements {
             use mir::StatementKind::{StorageDead, StorageLive};
             if let StorageLive(l) | StorageDead(l) = statement.kind {
diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
index 2502e8b603c3e..d8f85d2e37982 100644
--- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
+++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
@@ -56,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
         // example.
         let mut calls_to_terminate = Vec::new();
         let mut cleanups_to_remove = Vec::new();
-        for (id, block) in body.basic_blocks().iter_enumerated() {
+        for (id, block) in body.basic_blocks.iter_enumerated() {
             if block.is_cleanup {
                 continue;
             }
diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs
index f12c8560c0e82..30966d22e2f6d 100644
--- a/compiler/rustc_mir_transform/src/add_call_guards.rs
+++ b/compiler/rustc_mir_transform/src/add_call_guards.rs
@@ -45,7 +45,7 @@ impl AddCallGuards {
         // We need a place to store the new blocks generated
         let mut new_blocks = Vec::new();
 
-        let cur_len = body.basic_blocks().len();
+        let cur_len = body.basic_blocks.len();
 
         for block in body.basic_blocks_mut() {
             match block.terminator {
diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
index 8de0aad041cc3..ffb5d8c6d95f2 100644
--- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
+++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
@@ -55,7 +55,7 @@ fn add_moves_for_packed_drops_patch<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>)
     let mut patch = MirPatch::new(body);
     let param_env = tcx.param_env(def_id);
 
-    for (bb, data) in body.basic_blocks().iter_enumerated() {
+    for (bb, data) in body.basic_blocks.iter_enumerated() {
         let loc = Location { block: bb, statement_index: data.statements.len() };
         let terminator = data.terminator();
 
diff --git a/compiler/rustc_mir_transform/src/const_goto.rs b/compiler/rustc_mir_transform/src/const_goto.rs
index 5acf939f06bf6..0a305a402095d 100644
--- a/compiler/rustc_mir_transform/src/const_goto.rs
+++ b/compiler/rustc_mir_transform/src/const_goto.rs
@@ -61,14 +61,14 @@ impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
         let _: Option<_> = try {
             let target = terminator.kind.as_goto()?;
             // We only apply this optimization if the last statement is a const assignment
-            let last_statement = self.body.basic_blocks()[location.block].statements.last()?;
+            let last_statement = self.body.basic_blocks[location.block].statements.last()?;
 
             if let (place, Rvalue::Use(Operand::Constant(_const))) =
                 last_statement.kind.as_assign()?
             {
                 // We found a constant being assigned to `place`.
                 // Now check that the target of this Goto switches on this place.
-                let target_bb = &self.body.basic_blocks()[target];
+                let target_bb = &self.body.basic_blocks[target];
 
                 // The `StorageDead(..)` statement does not affect the functionality of mir.
                 // We can move this part of the statement up to the predecessor.
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 1c087b93b4965..fdcc065f112b6 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -131,7 +131,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
 
         let dummy_body = &Body::new(
             body.source,
-            body.basic_blocks().clone(),
+            (*body.basic_blocks).clone(),
             body.source_scopes.clone(),
             body.local_decls.clone(),
             Default::default(),
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index c2ea55af48a1e..1254aee39942e 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -106,7 +106,7 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
 
         let dummy_body = &Body::new(
             body.source,
-            body.basic_blocks().clone(),
+            (*body.basic_blocks).clone(),
             body.source_scopes.clone(),
             body.local_decls.clone(),
             Default::default(),
@@ -524,7 +524,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
 impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
     fn visit_body(&mut self, body: &Body<'tcx>) {
-        for (bb, data) in body.basic_blocks().iter_enumerated() {
+        for (bb, data) in body.basic_blocks.iter_enumerated() {
             self.visit_basic_block_data(bb, data);
         }
     }
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 759ea7cd32820..782129be088b6 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -713,7 +713,7 @@ impl<
 
         ShortCircuitPreorder {
             body,
-            visited: BitSet::new_empty(body.basic_blocks().len()),
+            visited: BitSet::new_empty(body.basic_blocks.len()),
             worklist,
             filtered_successors,
         }
@@ -747,7 +747,7 @@ impl<
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let size = self.body.basic_blocks().len() - self.visited.count();
+        let size = self.body.basic_blocks.len() - self.visited.count();
         (size, Some(size))
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 2619626a5675f..299a8067b28ff 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -80,7 +80,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
             return;
         }
 
-        match mir_body.basic_blocks()[mir::START_BLOCK].terminator().kind {
+        match mir_body.basic_blocks[mir::START_BLOCK].terminator().kind {
             TerminatorKind::Unreachable => {
                 trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`");
                 return;
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index 9d02f58ae65ee..dc1e68b253e72 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -84,7 +84,7 @@ impl CoverageVisitor {
     }
 
     fn visit_body(&mut self, body: &Body<'_>) {
-        for bb_data in body.basic_blocks().iter() {
+        for bb_data in body.basic_blocks.iter() {
             for statement in bb_data.statements.iter() {
                 if let StatementKind::Coverage(box ref coverage) = statement.kind {
                     if is_inlined(body, statement) {
@@ -138,7 +138,7 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
 
 fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> {
     let body = mir_body(tcx, def_id);
-    body.basic_blocks()
+    body.basic_blocks
         .iter()
         .flat_map(|data| {
             data.statements.iter().filter_map(|statement| match statement.kind {
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index 6380f03528ae8..9c9ed5fa5105e 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -176,7 +176,7 @@ fn debug_basic_blocks<'tcx>(mir_body: &Body<'tcx>) -> String {
     format!(
         "{:?}",
         mir_body
-            .basic_blocks()
+            .basic_blocks
             .iter_enumerated()
             .map(|(bb, data)| {
                 let term = &data.terminator();
@@ -213,7 +213,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
             "digraph {} {{\n{}\n}}",
             name,
             mir_body
-                .basic_blocks()
+                .basic_blocks
                 .iter_enumerated()
                 .map(|(bb, data)| {
                     format!(
@@ -653,7 +653,7 @@ fn test_traverse_coverage_with_loops() {
 
 fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span {
     let mut some_span: Option<Span> = None;
-    for (_, data) in mir_body.basic_blocks().iter_enumerated() {
+    for (_, data) in mir_body.basic_blocks.iter_enumerated() {
         let term_span = data.terminator().source_info.span;
         if let Some(span) = some_span.as_mut() {
             *span = span.to(term_span);
diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
index d1977ed49fe15..909116a77f54f 100644
--- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
+++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
@@ -58,7 +58,7 @@ fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
     let mut duplicates = FxHashMap::default();
 
     let bbs_to_go_through =
-        body.basic_blocks().iter_enumerated().filter(|(_, bbd)| !bbd.is_cleanup).count();
+        body.basic_blocks.iter_enumerated().filter(|(_, bbd)| !bbd.is_cleanup).count();
 
     let mut same_hashes =
         FxHashMap::with_capacity_and_hasher(bbs_to_go_through, Default::default());
@@ -71,8 +71,7 @@ fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
     // When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the duplicates list
     // with replacement bb3.
     // When the duplicates are removed, we will end up with only bb3.
-    for (bb, bbd) in body.basic_blocks().iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup)
-    {
+    for (bb, bbd) in body.basic_blocks.iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup) {
         // Basic blocks can get really big, so to avoid checking for duplicates in basic blocks
         // that are unlikely to have duplicates, we stop early. The early bail number has been
         // found experimentally by eprintln while compiling the crates in the rustc-perf suite.
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 33572068f5cd9..da55510920e12 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -150,7 +150,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
             def_id,
             body.local_decls.len(),
             relevant,
-            body.basic_blocks().len()
+            body.basic_blocks.len()
         );
         if relevant > MAX_LOCALS {
             warn!(
@@ -159,11 +159,11 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
             );
             return;
         }
-        if body.basic_blocks().len() > MAX_BLOCKS {
+        if body.basic_blocks.len() > MAX_BLOCKS {
             warn!(
                 "too many blocks in {:?} ({}, max is {}), not optimizing",
                 def_id,
-                body.basic_blocks().len(),
+                body.basic_blocks.len(),
                 MAX_BLOCKS
             );
             return;
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index dba42f7aff033..32e738bbcea44 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -104,8 +104,8 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
         let mut should_cleanup = false;
 
         // Also consider newly generated bbs in the same pass
-        for i in 0..body.basic_blocks().len() {
-            let bbs = body.basic_blocks();
+        for i in 0..body.basic_blocks.len() {
+            let bbs = &*body.basic_blocks;
             let parent = BasicBlock::from_usize(i);
             let Some(opt_data) = evaluate_candidate(tcx, body, parent) else {
                 continue
@@ -316,7 +316,7 @@ fn evaluate_candidate<'tcx>(
     body: &Body<'tcx>,
     parent: BasicBlock,
 ) -> Option<OptimizationData<'tcx>> {
-    let bbs = body.basic_blocks();
+    let bbs = &body.basic_blocks;
     let TerminatorKind::SwitchInt {
         targets,
         switch_ty: parent_ty,
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 9c1fcbaa69d6b..61118ecc8ed51 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -89,13 +89,13 @@ fn find_dead_unwinds<'tcx>(
     debug!("find_dead_unwinds({:?})", body.span);
     // We only need to do this pass once, because unwind edges can only
     // reach cleanup blocks, which can't have unwind edges themselves.
-    let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
+    let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len());
     let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env)
         .into_engine(tcx, body)
         .pass_name("find_dead_unwinds")
         .iterate_to_fixpoint()
         .into_results_cursor(body);
-    for (bb, bb_data) in body.basic_blocks().iter_enumerated() {
+    for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
         let place = match bb_data.terminator().kind {
             TerminatorKind::Drop { ref place, unwind: Some(_), .. }
             | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => {
@@ -303,7 +303,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
     }
 
     fn collect_drop_flags(&mut self) {
-        for (bb, data) in self.body.basic_blocks().iter_enumerated() {
+        for (bb, data) in self.body.basic_blocks.iter_enumerated() {
             let terminator = data.terminator();
             let place = match terminator.kind {
                 TerminatorKind::Drop { ref place, .. }
@@ -358,7 +358,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
     }
 
     fn elaborate_drops(&mut self) {
-        for (bb, data) in self.body.basic_blocks().iter_enumerated() {
+        for (bb, data) in self.body.basic_blocks.iter_enumerated() {
             let loc = Location { block: bb, statement_index: data.statements.len() };
             let terminator = data.terminator();
 
@@ -515,7 +515,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
     }
 
     fn drop_flags_for_fn_rets(&mut self) {
-        for (bb, data) in self.body.basic_blocks().iter_enumerated() {
+        for (bb, data) in self.body.basic_blocks.iter_enumerated() {
             if let TerminatorKind::Call {
                 destination, target: Some(tgt), cleanup: Some(_), ..
             } = data.terminator().kind
@@ -550,7 +550,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         // drop flags by themselves, to avoid the drop flags being
         // clobbered before they are read.
 
-        for (bb, data) in self.body.basic_blocks().iter_enumerated() {
+        for (bb, data) in self.body.basic_blocks.iter_enumerated() {
             debug!("drop_flags_for_locs({:?})", data);
             for i in 0..(data.statements.len() + 1) {
                 debug!("drop_flag_for_locs: stmt {}", i);
diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs
index 7728fdaffb0dc..7522a50a8c643 100644
--- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs
+++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs
@@ -65,7 +65,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool {
 
     let mut tainted = false;
 
-    for block in body.basic_blocks() {
+    for block in body.basic_blocks.iter() {
         if block.is_cleanup {
             continue;
         }
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 5b0d9900c0fb5..c260611b40712 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -490,12 +490,12 @@ fn locals_live_across_suspend_points<'tcx>(
         .iterate_to_fixpoint()
         .into_results_cursor(body_ref);
 
-    let mut storage_liveness_map = IndexVec::from_elem(None, body.basic_blocks());
+    let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
     let mut live_locals_at_suspension_points = Vec::new();
     let mut source_info_at_suspension_points = Vec::new();
     let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
 
-    for (block, data) in body.basic_blocks().iter_enumerated() {
+    for (block, data) in body.basic_blocks.iter_enumerated() {
         if let TerminatorKind::Yield { .. } = data.terminator().kind {
             let loc = Location { block, statement_index: data.statements.len() };
 
@@ -704,7 +704,7 @@ impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
 impl StorageConflictVisitor<'_, '_, '_> {
     fn apply_state(&mut self, flow_state: &BitSet<Local>, loc: Location) {
         // Ignore unreachable blocks.
-        if self.body.basic_blocks()[loc.block].terminator().kind == TerminatorKind::Unreachable {
+        if self.body.basic_blocks[loc.block].terminator().kind == TerminatorKind::Unreachable {
             return;
         }
 
@@ -886,7 +886,7 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 
     let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env };
 
-    for (block, block_data) in body.basic_blocks().iter_enumerated() {
+    for (block, block_data) in body.basic_blocks.iter_enumerated() {
         let (target, unwind, source_info) = match block_data.terminator() {
             Terminator { source_info, kind: TerminatorKind::Drop { place, target, unwind } } => {
                 if let Some(local) = place.as_local() {
@@ -991,7 +991,7 @@ fn insert_panic_block<'tcx>(
     body: &mut Body<'tcx>,
     message: AssertMessage<'tcx>,
 ) -> BasicBlock {
-    let assert_block = BasicBlock::new(body.basic_blocks().len());
+    let assert_block = BasicBlock::new(body.basic_blocks.len());
     let term = TerminatorKind::Assert {
         cond: Operand::Constant(Box::new(Constant {
             span: body.span,
@@ -1021,7 +1021,7 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ty::ParamEn
     }
 
     // If there's a return terminator the function may return.
-    for block in body.basic_blocks() {
+    for block in body.basic_blocks.iter() {
         if let TerminatorKind::Return = block.terminator().kind {
             return true;
         }
@@ -1038,7 +1038,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
     }
 
     // Unwinds can only start at certain terminators.
-    for block in body.basic_blocks() {
+    for block in body.basic_blocks.iter() {
         match block.terminator().kind {
             // These never unwind.
             TerminatorKind::Goto { .. }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index d7d2984018804..ba00f16308eae 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -95,7 +95,7 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
         history: Vec::new(),
         changed: false,
     };
-    let blocks = BasicBlock::new(0)..body.basic_blocks().next_index();
+    let blocks = BasicBlock::new(0)..body.basic_blocks.next_index();
     this.process_blocks(body, blocks);
     this.changed
 }
@@ -217,9 +217,9 @@ impl<'tcx> Inliner<'tcx> {
             }
         }
 
-        let old_blocks = caller_body.basic_blocks().next_index();
+        let old_blocks = caller_body.basic_blocks.next_index();
         self.inline_call(caller_body, &callsite, callee_body);
-        let new_blocks = old_blocks..caller_body.basic_blocks().next_index();
+        let new_blocks = old_blocks..caller_body.basic_blocks.next_index();
 
         Ok(new_blocks)
     }
@@ -409,14 +409,14 @@ impl<'tcx> Inliner<'tcx> {
         // Give a bonus functions with a small number of blocks,
         // We normally have two or three blocks for even
         // very small functions.
-        if callee_body.basic_blocks().len() <= 3 {
+        if callee_body.basic_blocks.len() <= 3 {
             threshold += threshold / 4;
         }
         debug!("    final inline threshold = {}", threshold);
 
         // FIXME: Give a bonus to functions with only a single caller
         let diverges = matches!(
-            callee_body.basic_blocks()[START_BLOCK].terminator().kind,
+            callee_body.basic_blocks[START_BLOCK].terminator().kind,
             TerminatorKind::Unreachable | TerminatorKind::Call { target: None, .. }
         );
         if diverges && !matches!(callee_attrs.inline, InlineAttr::Always) {
@@ -434,13 +434,13 @@ impl<'tcx> Inliner<'tcx> {
 
         // Traverse the MIR manually so we can account for the effects of inlining on the CFG.
         let mut work_list = vec![START_BLOCK];
-        let mut visited = BitSet::new_empty(callee_body.basic_blocks().len());
+        let mut visited = BitSet::new_empty(callee_body.basic_blocks.len());
         while let Some(bb) = work_list.pop() {
             if !visited.insert(bb.index()) {
                 continue;
             }
 
-            let blk = &callee_body.basic_blocks()[bb];
+            let blk = &callee_body.basic_blocks[bb];
             checker.visit_basic_block_data(bb, blk);
 
             let term = blk.terminator();
@@ -541,7 +541,7 @@ impl<'tcx> Inliner<'tcx> {
                     args: &args,
                     new_locals: Local::new(caller_body.local_decls.len())..,
                     new_scopes: SourceScope::new(caller_body.source_scopes.len())..,
-                    new_blocks: BasicBlock::new(caller_body.basic_blocks().len())..,
+                    new_blocks: BasicBlock::new(caller_body.basic_blocks.len())..,
                     destination: dest,
                     callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(),
                     callsite,
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 7810218fd6744..b027f94925d2f 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -153,7 +153,7 @@ pub(crate) fn mir_inliner_callees<'tcx>(
         _ => tcx.instance_mir(instance),
     };
     let mut calls = FxIndexSet::default();
-    for bb_data in body.basic_blocks() {
+    for bb_data in body.basic_blocks.iter() {
         let terminator = bb_data.terminator();
         if let TerminatorKind::Call { func, .. } = &terminator.kind {
             let ty = func.ty(&body.local_decls, tcx);
diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
index 22b6dead99c50..3957cd92c4e39 100644
--- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
+++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
@@ -15,7 +15,7 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // find basic blocks with no statement and a return terminator
-        let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks().len());
+        let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks.len());
         let def_id = body.source.def_id();
         let bbs = body.basic_blocks_mut();
         for idx in bbs.indices() {
diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs
index c0217a105414b..a159e61717823 100644
--- a/compiler/rustc_mir_transform/src/normalize_array_len.rs
+++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs
@@ -21,10 +21,10 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // early returns for edge cases of highly unrolled functions
-        if body.basic_blocks().len() > MAX_NUM_BLOCKS {
+        if body.basic_blocks.len() > MAX_NUM_BLOCKS {
             return;
         }
-        if body.local_decls().len() > MAX_NUM_LOCALS {
+        if body.local_decls.len() > MAX_NUM_LOCALS {
             return;
         }
         normalize_array_len_calls(tcx, body)
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index 42d732730eccc..4291e81c78c25 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -89,7 +89,7 @@ fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option<Local> {
     }
 
     let mut copied_to_return_place = None;
-    for block in body.basic_blocks().indices() {
+    for block in body.basic_blocks.indices() {
         // Look for blocks with a `Return` terminator.
         if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) {
             continue;
@@ -122,7 +122,7 @@ fn find_local_assigned_to_return_place(
     body: &mut mir::Body<'_>,
 ) -> Option<Local> {
     let mut block = start;
-    let mut seen = HybridBitSet::new_empty(body.basic_blocks().len());
+    let mut seen = HybridBitSet::new_empty(body.basic_blocks.len());
 
     // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
     while seen.insert(block) {
diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
index 5c441c5b19492..41a0bfac41aed 100644
--- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -94,7 +94,7 @@ impl RemoveNoopLandingPads {
 
         let mut jumps_folded = 0;
         let mut landing_pads_removed = 0;
-        let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks().len());
+        let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks.len());
 
         // This is a post-order traversal, so that if A post-dominates B
         // then A will be visited before B.
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index 96b715402e772..78b6f714a9b0b 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -35,7 +35,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
             .into_results_cursor(body);
 
         let mut to_remove = vec![];
-        for (bb, block) in body.basic_blocks().iter_enumerated() {
+        for (bb, block) in body.basic_blocks.iter_enumerated() {
             let terminator = block.terminator();
             let (TerminatorKind::Drop { place, .. } | TerminatorKind::DropAndReplace { place, .. })
                 = &terminator.kind
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 925eb10a1f75f..190f9c1ac158c 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -62,7 +62,7 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
 pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
     let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
     let predecessors = body.basic_blocks.predecessors();
-    'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() {
+    'block_iter: for (block_id, block) in body.basic_blocks.iter_enumerated() {
         if let TerminatorKind::SwitchInt {
             discr: Operand::Copy(switch_place) | Operand::Move(switch_place),
             ..
@@ -90,7 +90,7 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
 
                 let mut predecessors_left = predecessors[block_id].len();
                 'predec_iter: for predecessor_id in predecessors[block_id].iter().copied() {
-                    let predecessor = &body.basic_blocks()[predecessor_id];
+                    let predecessor = &body.basic_blocks[predecessor_id];
 
                     // First we make sure the predecessor jumps
                     // in a reasonable way
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 7a6ca917d0ffd..bed48db959a53 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -74,7 +74,7 @@ pub struct CfgSimplifier<'a, 'tcx> {
 
 impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
     pub fn new(body: &'a mut Body<'tcx>) -> Self {
-        let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks());
+        let mut pred_count = IndexVec::from_elem(0u32, &body.basic_blocks);
 
         // we can't use mir.predecessors() here because that counts
         // dead blocks, which we don't want to.
@@ -263,7 +263,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
 
 pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     let reachable = traversal::reachable_as_bitset(body);
-    let num_blocks = body.basic_blocks().len();
+    let num_blocks = body.basic_blocks.len();
     if num_blocks == reachable.count() {
         return;
     }
diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
index bbfaace7041ec..321d8c63b6e02 100644
--- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
+++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
@@ -151,7 +151,7 @@ struct OptimizationFinder<'a, 'tcx> {
 impl<'tcx> OptimizationFinder<'_, 'tcx> {
     fn find_optimizations(&self) -> Vec<OptimizationInfo<'tcx>> {
         self.body
-            .basic_blocks()
+            .basic_blocks
             .iter_enumerated()
             .filter_map(|(bb_idx, bb)| {
                 // find switch
diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs
index d52f1261b23a2..baeb620ef2403 100644
--- a/compiler/rustc_mir_transform/src/simplify_try.rs
+++ b/compiler/rustc_mir_transform/src/simplify_try.rs
@@ -596,7 +596,7 @@ struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
 impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
     fn find(&self) -> Vec<SimplifyBranchSameOptimization> {
         self.body
-            .basic_blocks()
+            .basic_blocks
             .iter_enumerated()
             .filter_map(|(bb_idx, bb)| {
                 let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
@@ -632,7 +632,7 @@ impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
 
                 let mut iter_bbs_reachable = targets_and_values
                     .iter()
-                    .map(|target_and_value| (target_and_value, &self.body.basic_blocks()[target_and_value.target]))
+                    .map(|target_and_value| (target_and_value, &self.body.basic_blocks[target_and_value.target]))
                     .filter(|(_, bb)| {
                         // Reaching `unreachable` is UB so assume it doesn't happen.
                         bb.terminator().kind != TerminatorKind::Unreachable
diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
index 30be64f5b2f2e..96ea15f1b8097 100644
--- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
+++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
@@ -79,7 +79,7 @@ fn ensure_otherwise_unreachable<'tcx>(
     targets: &SwitchTargets,
 ) -> Option<BasicBlockData<'tcx>> {
     let otherwise = targets.otherwise();
-    let bb = &body.basic_blocks()[otherwise];
+    let bb = &body.basic_blocks[otherwise];
     if bb.terminator().kind == TerminatorKind::Unreachable
         && bb.statements.iter().all(|s| matches!(&s.kind, StatementKind::StorageDead(_)))
     {
@@ -102,10 +102,10 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         trace!("UninhabitedEnumBranching starting for {:?}", body.source);
 
-        for bb in body.basic_blocks().indices() {
+        for bb in body.basic_blocks.indices() {
             trace!("processing block {:?}", bb);
 
-            let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks()[bb], tcx, body) else {
+            let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks[bb], tcx, body) else {
                 continue;
             };
 
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
index ff2d386932858..43319dc5862e6 100644
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -481,7 +481,7 @@ fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSe
                     continue;
                 }
                 let body = tcx.instance_mir(instance.def);
-                for block in body.basic_blocks() {
+                for block in body.basic_blocks.iter() {
                     for statement in &block.statements {
                         let mir::StatementKind::Coverage(_) = statement.kind else { continue };
                         let scope = statement.source_info.scope;
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index db0d45b86fc03..acfeefb4d12d5 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -348,7 +348,7 @@ fn instance_def_size_estimate<'tcx>(
     match instance_def {
         InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
             let mir = tcx.instance_mir(instance_def);
-            mir.basic_blocks().iter().map(|bb| bb.statements.len() + 1).sum()
+            mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
         }
         // Estimate the size of other compiler-generated shims to be 1.
         _ => 1,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index eddca60457574..9fd86331ec755 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
             vis.into_map(cx, maybe_storage_live_result)
         };
 
-        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
+        for (bb, bbdata) in mir.basic_blocks.iter_enumerated() {
             let terminator = bbdata.terminator();
 
             if terminator.source_info.span.from_expansion() {
@@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                     unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
                 let loc = mir::Location {
                     block: bb,
-                    statement_index: mir.basic_blocks()[bb].statements.len(),
+                    statement_index: mir.basic_blocks[bb].statements.len(),
                 };
 
                 // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
@@ -310,7 +310,7 @@ fn find_stmt_assigns_to<'tcx>(
     by_ref: bool,
     bb: mir::BasicBlock,
 ) -> Option<(mir::Local, CannotMoveOut)> {
-    let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| {
+    let rvalue = mir.basic_blocks[bb].statements.iter().rev().find_map(|stmt| {
         if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
             return if *local == to_local { Some(v) } else { None };
         }
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 3bf75bcbee83e..74c222bbcbeb9 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -55,7 +55,7 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv:
         body.local_decls.iter().next().unwrap().source_info.span,
     )?;
 
-    for bb in body.basic_blocks() {
+    for bb in body.basic_blocks.iter() {
         check_terminator(tcx, body, bb.terminator(), msrv)?;
         for stmt in &bb.statements {
             check_statement(tcx, body, def_id, stmt)?;

From 9678751a880bc87a9beae7585ace926306a291e6 Mon Sep 17 00:00:00 2001
From: yukang <moorekang@gmail.com>
Date: Mon, 22 Aug 2022 21:45:05 +0800
Subject: [PATCH 34/36] pretty printing give proper erro message without panic

---
 compiler/rustc_driver/src/pretty.rs | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs
index f66b1a2976f1c..21a3a077a10c4 100644
--- a/compiler/rustc_driver/src/pretty.rs
+++ b/compiler/rustc_driver/src/pretty.rs
@@ -357,12 +357,16 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
     (src, src_name)
 }
 
-fn write_or_print(out: &str, ofile: Option<&Path>) {
+fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
     match ofile {
         None => print!("{}", out),
         Some(p) => {
             if let Err(e) = std::fs::write(p, out) {
-                panic!("print-print failed to write {} due to {}", p.display(), e);
+                let mut err = sess.struct_fatal(&format!(
+                    "pretty-print failed to write {} due to error `{e}`",
+                    p.display()
+                ));
+                err.emit();
             }
         }
     }
@@ -402,7 +406,7 @@ pub fn print_after_parsing(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, sess);
 }
 
 pub fn print_after_hir_lowering<'tcx>(
@@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, tcx.sess);
 }
 
 // In an ideal world, this would be a public function called by the driver after
@@ -512,7 +516,7 @@ fn print_with_analysis(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, tcx.sess);
 
     Ok(())
 }

From 2237c6c29505d45bbb7403f515b359944a95c5e0 Mon Sep 17 00:00:00 2001
From: yukang <moorekang@gmail.com>
Date: Sun, 28 Aug 2022 11:17:28 +0800
Subject: [PATCH 35/36] add UI test for unpretty

---
 compiler/rustc_driver/src/pretty.rs                    | 10 +++++-----
 compiler/rustc_driver/src/session_diagnostics.rs       |  7 +++++++
 compiler/rustc_error_messages/locales/en-US/driver.ftl |  2 ++
 src/test/ui/unpretty/avoid-crash.rs                    |  3 +++
 src/test/ui/unpretty/avoid-crash.stderr                |  4 ++++
 5 files changed, 21 insertions(+), 5 deletions(-)
 create mode 100644 src/test/ui/unpretty/avoid-crash.rs
 create mode 100644 src/test/ui/unpretty/avoid-crash.stderr

diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs
index 21a3a077a10c4..faeacd3e41040 100644
--- a/compiler/rustc_driver/src/pretty.rs
+++ b/compiler/rustc_driver/src/pretty.rs
@@ -1,5 +1,6 @@
 //! The various pretty-printing routines.
 
+use crate::session_diagnostics::UnprettyDumpFail;
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_errors::ErrorGuaranteed;
@@ -362,11 +363,10 @@ fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
         None => print!("{}", out),
         Some(p) => {
             if let Err(e) = std::fs::write(p, out) {
-                let mut err = sess.struct_fatal(&format!(
-                    "pretty-print failed to write {} due to error `{e}`",
-                    p.display()
-                ));
-                err.emit();
+                sess.emit_fatal(UnprettyDumpFail {
+                    path: p.display().to_string(),
+                    err: e.to_string(),
+                });
             }
         }
     }
diff --git a/compiler/rustc_driver/src/session_diagnostics.rs b/compiler/rustc_driver/src/session_diagnostics.rs
index fe64d0fca9b20..e9696792d051f 100644
--- a/compiler/rustc_driver/src/session_diagnostics.rs
+++ b/compiler/rustc_driver/src/session_diagnostics.rs
@@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
 #[derive(SessionDiagnostic)]
 #[diag(driver::rlink_no_a_file)]
 pub(crate) struct RlinkNotAFile;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::unpretty_dump_fail)]
+pub(crate) struct UnprettyDumpFail {
+    pub path: String,
+    pub err: String,
+}
diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_error_messages/locales/en-US/driver.ftl
index 73f084cf3290b..8ad198c86c933 100644
--- a/compiler/rustc_error_messages/locales/en-US/driver.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/driver.ftl
@@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding
 driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
 
 driver_rlink_no_a_file = rlink must be a file
+
+driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
diff --git a/src/test/ui/unpretty/avoid-crash.rs b/src/test/ui/unpretty/avoid-crash.rs
new file mode 100644
index 0000000000000..daa073b75dd17
--- /dev/null
+++ b/src/test/ui/unpretty/avoid-crash.rs
@@ -0,0 +1,3 @@
+// compile-flags: -o/tmp/ -Zunpretty=ast-tree
+
+fn main() {}
diff --git a/src/test/ui/unpretty/avoid-crash.stderr b/src/test/ui/unpretty/avoid-crash.stderr
new file mode 100644
index 0000000000000..fb52d648adb6c
--- /dev/null
+++ b/src/test/ui/unpretty/avoid-crash.stderr
@@ -0,0 +1,4 @@
+error: pretty-print failed to write `/tmp/` due to error `Is a directory (os error 21)`
+
+error: aborting due to previous error
+

From 1e005af31b078f584f7adbdebc505ba0cca52706 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Sun, 28 Aug 2022 10:34:19 +0200
Subject: [PATCH 36/36] Remove Attrs type alias

---
 src/librustdoc/clean/inline.rs | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index f367edcbf5a81..31b805f2ed7ed 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -24,8 +24,6 @@ use crate::clean::{
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
 
-type Attrs<'hir> = &'hir [ast::Attribute];
-
 /// Attempt to inline a definition into this AST.
 ///
 /// This function will fetch the definition specified, and if it is
@@ -46,7 +44,7 @@ pub(crate) fn try_inline(
     import_def_id: Option<DefId>,
     res: Res,
     name: Symbol,
-    attrs: Option<Attrs<'_>>,
+    attrs: Option<&[ast::Attribute]>,
     visited: &mut FxHashSet<DefId>,
 ) -> Option<Vec<clean::Item>> {
     let did = res.opt_def_id()?;
@@ -172,7 +170,7 @@ pub(crate) fn try_inline_glob(
     }
 }
 
-pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
+pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [ast::Attribute] {
     cx.tcx.get_attrs_unchecked(did)
 }
 
@@ -287,7 +285,7 @@ pub(crate) fn build_impls(
     cx: &mut DocContext<'_>,
     parent_module: Option<DefId>,
     did: DefId,
-    attrs: Option<Attrs<'_>>,
+    attrs: Option<&[ast::Attribute]>,
     ret: &mut Vec<clean::Item>,
 ) {
     let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
@@ -303,8 +301,8 @@ pub(crate) fn build_impls(
 pub(crate) fn merge_attrs(
     cx: &mut DocContext<'_>,
     parent_module: Option<DefId>,
-    old_attrs: Attrs<'_>,
-    new_attrs: Option<Attrs<'_>>,
+    old_attrs: &[ast::Attribute],
+    new_attrs: Option<&[ast::Attribute]>,
 ) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
     // NOTE: If we have additional attributes (from a re-export),
     // always insert them first. This ensure that re-export
@@ -331,7 +329,7 @@ pub(crate) fn build_impl(
     cx: &mut DocContext<'_>,
     parent_module: Option<DefId>,
     did: DefId,
-    attrs: Option<Attrs<'_>>,
+    attrs: Option<&[ast::Attribute]>,
     ret: &mut Vec<clean::Item>,
 ) {
     if !cx.inlined.insert(did.into()) {