about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_arena/src/lib.rs27
-rw-r--r--compiler/rustc_arena/src/tests.rs24
-rw-r--r--compiler/rustc_ast/src/ast.rs11
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs72
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs33
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs75
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs5
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs49
-rw-r--r--compiler/rustc_hir/src/intravisit.rs4
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs18
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs11
-rw-r--r--compiler/rustc_incremental/src/persist/work_product.rs46
-rw-r--r--compiler/rustc_index/src/bit_set.rs10
-rw-r--r--compiler/rustc_lint/src/context.rs33
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs5
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs7
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs66
-rw-r--r--compiler/rustc_middle/src/hir/nested_filter.rs2
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs147
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs21
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs90
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs168
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs31
-rw-r--r--compiler/rustc_mir_build/src/lints.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs16
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs2
-rw-r--r--compiler/rustc_mir_transform/src/add_call_guards.rs2
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs3
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs3
-rw-r--r--compiler/rustc_mir_transform/src/coverage/tests.rs1
-rw-r--r--compiler/rustc_mir_transform/src/dead_store_elimination.rs2
-rw-r--r--compiler/rustc_mir_transform/src/deaggregator.rs6
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs4
-rw-r--r--compiler/rustc_mir_transform/src/instcombine.rs5
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs6
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs4
-rw-r--r--compiler/rustc_mir_transform/src/normalize_array_len.rs4
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_storage_markers.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs5
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs6
-rw-r--r--compiler/rustc_mir_transform/src/separate_const_switch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/simplify_try.rs15
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs11
-rw-r--r--compiler/rustc_passes/src/check_attr.rs2
-rw-r--r--compiler/rustc_passes/src/check_const.rs2
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs41
-rw-r--r--compiler/rustc_passes/src/liveness.rs2
-rw-r--r--compiler/rustc_passes/src/loops.rs2
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs2
-rw-r--r--compiler/rustc_passes/src/stability.rs4
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs8
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs14
-rw-r--r--compiler/rustc_typeck/src/collect.rs2
-rw-r--r--library/std/src/os/windows/process.rs23
-rw-r--r--library/std/src/process.rs16
-rw-r--r--library/std/src/sys/windows/process.rs6
-rw-r--r--src/bootstrap/builder.rs10
-rw-r--r--src/bootstrap/compile.rs4
-rw-r--r--src/bootstrap/config.rs8
-rw-r--r--src/bootstrap/flags.rs13
-rw-r--r--src/bootstrap/format.rs4
-rw-r--r--src/bootstrap/lib.rs20
-rw-r--r--src/bootstrap/sanity.rs2
-rw-r--r--src/bootstrap/setup.rs4
-rw-r--r--src/bootstrap/test.rs6
-rw-r--r--src/bootstrap/tool.rs4
-rw-r--r--src/bootstrap/toolstate.rs8
-rw-r--r--src/bootstrap/util.rs6
-rw-r--r--src/librustdoc/clean/mod.rs5
-rw-r--r--src/librustdoc/config.rs3
-rw-r--r--src/librustdoc/html/render/mod.rs20
-rw-r--r--src/librustdoc/scrape_examples.rs2
-rw-r--r--src/test/incremental/split_debuginfo_cached.rs25
-rw-r--r--src/test/rustdoc/anchors.no_const_anchor.html1
-rw-r--r--src/test/rustdoc/anchors.no_const_anchor2.html1
-rw-r--r--src/test/rustdoc/anchors.no_method_anchor.html1
-rw-r--r--src/test/rustdoc/anchors.no_trait_method_anchor.html1
-rw-r--r--src/test/rustdoc/anchors.no_tymethod_anchor.html1
-rw-r--r--src/test/rustdoc/anchors.no_type_anchor.html1
-rw-r--r--src/test/rustdoc/anchors.no_type_anchor2.html1
-rw-r--r--src/test/rustdoc/anchors.rs49
-rw-r--r--src/test/ui/const-generics/issue-80471.rs13
-rw-r--r--src/test/ui/const-generics/issue-80471.stderr18
-rw-r--r--src/test/ui/dep-graph/dep-graph-struct-signature.stderr72
-rw-r--r--src/test/ui/dep-graph/dep-graph-type-alias.stderr36
-rw-r--r--src/test/ui/enum/suggest-default-attribute.rs2
-rw-r--r--src/test/ui/enum/suggest-default-attribute.stderr8
-rw-r--r--src/test/ui/extern/not-in-block.rs6
-rw-r--r--src/test/ui/extern/not-in-block.stderr32
-rw-r--r--src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr2
-rw-r--r--src/test/ui/mir/ssa-analysis-regression-50041.rs4
-rw-r--r--src/test/ui/stability-attribute/missing-const-stability.stderr12
-rw-r--r--src/tools/clippy/clippy_lints/src/duplicate_mod.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_bools.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs0
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs13
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr19
-rw-r--r--src/tools/rustfmt/src/items.rs2
-rw-r--r--src/tools/rustfmt/src/utils.rs4
120 files changed, 1102 insertions, 623 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 62995dfd2e2..a5f1cbc96da 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -19,6 +19,7 @@
 #![feature(rustc_attrs)]
 #![cfg_attr(test, feature(test))]
 #![feature(strict_provenance)]
+#![feature(ptr_const_cast)]
 
 use smallvec::SmallVec;
 
@@ -27,7 +28,7 @@ use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::marker::{PhantomData, Send};
 use std::mem::{self, MaybeUninit};
-use std::ptr;
+use std::ptr::{self, NonNull};
 use std::slice;
 
 #[inline(never)]
@@ -55,15 +56,24 @@ pub struct TypedArena<T> {
 
 struct ArenaChunk<T = u8> {
     /// The raw storage for the arena chunk.
-    storage: Box<[MaybeUninit<T>]>,
+    storage: NonNull<[MaybeUninit<T>]>,
     /// The number of valid entries in the chunk.
     entries: usize,
 }
 
+unsafe impl<#[may_dangle] T> Drop for ArenaChunk<T> {
+    fn drop(&mut self) {
+        unsafe { Box::from_raw(self.storage.as_mut()) };
+    }
+}
+
 impl<T> ArenaChunk<T> {
     #[inline]
     unsafe fn new(capacity: usize) -> ArenaChunk<T> {
-        ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
+        ArenaChunk {
+            storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(),
+            entries: 0,
+        }
     }
 
     /// Destroys this arena chunk.
@@ -72,14 +82,15 @@ impl<T> ArenaChunk<T> {
         // The branch on needs_drop() is an -O1 performance optimization.
         // Without the branch, dropping TypedArena<u8> takes linear time.
         if mem::needs_drop::<T>() {
-            ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len]));
+            let slice = &mut *(self.storage.as_mut());
+            ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len]));
         }
     }
 
     // Returns a pointer to the first allocated object.
     #[inline]
     fn start(&mut self) -> *mut T {
-        MaybeUninit::slice_as_mut_ptr(&mut self.storage)
+        self.storage.as_ptr() as *mut T
     }
 
     // Returns a pointer to the end of the allocated space.
@@ -90,7 +101,7 @@ impl<T> ArenaChunk<T> {
                 // A pointer as large as possible for zero-sized elements.
                 ptr::invalid_mut(!0)
             } else {
-                self.start().add(self.storage.len())
+                self.start().add((*self.storage.as_ptr()).len())
             }
         }
     }
@@ -274,7 +285,7 @@ impl<T> TypedArena<T> {
                 // If the previous chunk's len is less than HUGE_PAGE
                 // bytes, then this chunk will be least double the previous
                 // chunk's size.
-                new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
+                new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2);
                 new_cap *= 2;
             } else {
                 new_cap = PAGE / elem_size;
@@ -382,7 +393,7 @@ impl DroplessArena {
                 // If the previous chunk's len is less than HUGE_PAGE
                 // bytes, then this chunk will be least double the previous
                 // chunk's size.
-                new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
+                new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2);
                 new_cap *= 2;
             } else {
                 new_cap = PAGE;
diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs
index 911e577c1ed..ad61464343a 100644
--- a/compiler/rustc_arena/src/tests.rs
+++ b/compiler/rustc_arena/src/tests.rs
@@ -79,7 +79,11 @@ fn test_arena_alloc_nested() {
 #[test]
 pub fn test_copy() {
     let arena = TypedArena::default();
-    for _ in 0..100000 {
+    #[cfg(not(miri))]
+    const N: usize = 100000;
+    #[cfg(miri)]
+    const N: usize = 1000;
+    for _ in 0..N {
         arena.alloc(Point { x: 1, y: 2, z: 3 });
     }
 }
@@ -106,7 +110,11 @@ struct Noncopy {
 #[test]
 pub fn test_noncopy() {
     let arena = TypedArena::default();
-    for _ in 0..100000 {
+    #[cfg(not(miri))]
+    const N: usize = 100000;
+    #[cfg(miri)]
+    const N: usize = 1000;
+    for _ in 0..N {
         arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] });
     }
 }
@@ -114,7 +122,11 @@ pub fn test_noncopy() {
 #[test]
 pub fn test_typed_arena_zero_sized() {
     let arena = TypedArena::default();
-    for _ in 0..100000 {
+    #[cfg(not(miri))]
+    const N: usize = 100000;
+    #[cfg(miri)]
+    const N: usize = 1000;
+    for _ in 0..N {
         arena.alloc(());
     }
 }
@@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() {
     let mut arena = TypedArena::default();
     for _ in 0..10 {
         arena.clear();
-        for _ in 0..10000 {
+        #[cfg(not(miri))]
+        const N: usize = 10000;
+        #[cfg(miri)]
+        const N: usize = 100;
+        for _ in 0..N {
             arena.alloc(Point { x: 1, y: 2, z: 3 });
         }
     }
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e5b61d7000a..2820d5e6e0c 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2667,13 +2667,16 @@ impl Item {
 #[derive(Clone, Copy, Encodable, Decodable, Debug)]
 pub enum Extern {
     None,
-    Implicit,
-    Explicit(StrLit),
+    Implicit(Span),
+    Explicit(StrLit, Span),
 }
 
 impl Extern {
-    pub fn from_abi(abi: Option<StrLit>) -> Extern {
-        abi.map_or(Extern::Implicit, Extern::Explicit)
+    pub fn from_abi(abi: Option<StrLit>, span: Span) -> Extern {
+        match abi {
+            Some(name) => Extern::Explicit(name, span),
+            None => Extern::Implicit(span),
+        }
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 0ef21371694..112197c6e39 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1272,8 +1272,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
     pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
         match ext {
             Extern::None => abi::Abi::Rust,
-            Extern::Implicit => abi::Abi::FALLBACK,
-            Extern::Explicit(abi) => self.lower_abi(abi),
+            Extern::Implicit(_) => abi::Abi::FALLBACK,
+            Extern::Explicit(abi, _) => self.lower_abi(abi),
         }
     }
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 503bdbad258..3942062656f 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -13,7 +13,9 @@ use rustc_ast::walk_list;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{error_code, pluralize, struct_span_err, Applicability};
+use rustc_errors::{
+    error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+};
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
     DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
@@ -476,6 +478,17 @@ impl<'a> AstValidator<'a> {
     }
 
     fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) {
+        self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ());
+    }
+
+    fn error_item_without_body_with_help(
+        &self,
+        sp: Span,
+        ctx: &str,
+        msg: &str,
+        sugg: &str,
+        help: impl FnOnce(&mut DiagnosticBuilder<'_, ErrorGuaranteed>),
+    ) {
         let source_map = self.session.source_map();
         let end = source_map.end_point(sp);
         let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) {
@@ -483,15 +496,15 @@ impl<'a> AstValidator<'a> {
         } else {
             sp.shrink_to_hi()
         };
-        self.err_handler()
-            .struct_span_err(sp, msg)
-            .span_suggestion(
-                replace_span,
-                &format!("provide a definition for the {}", ctx),
-                sugg,
-                Applicability::HasPlaceholders,
-            )
-            .emit();
+        let mut err = self.err_handler().struct_span_err(sp, msg);
+        err.span_suggestion(
+            replace_span,
+            &format!("provide a definition for the {}", ctx),
+            sugg,
+            Applicability::HasPlaceholders,
+        );
+        help(&mut err);
+        err.emit();
     }
 
     fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
@@ -630,7 +643,8 @@ impl<'a> AstValidator<'a> {
         match (fk.ctxt(), fk.header()) {
             (Some(FnCtxt::Foreign), _) => return,
             (Some(FnCtxt::Free), Some(header)) => match header.ext {
-                Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }) | Extern::Implicit
+                Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
+                | Extern::Implicit(_)
                     if matches!(header.unsafety, Unsafe::Yes(_)) =>
                 {
                     return;
@@ -842,7 +856,7 @@ impl<'a> AstValidator<'a> {
                     .emit();
                 });
                 self.check_late_bound_lifetime_defs(&bfty.generic_params);
-                if let Extern::Implicit = bfty.ext {
+                if let Extern::Implicit(_) = bfty.ext {
                     let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
                     self.maybe_lint_missing_abi(sig_span, ty.id);
                 }
@@ -1190,8 +1204,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
                 if body.is_none() {
                     let msg = "free function without a body";
-                    self.error_item_without_body(item.span, "function", msg, " { <body> }");
+                    let ext = sig.header.ext;
+
+                    let f = |e: &mut DiagnosticBuilder<'_, _>| {
+                        if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext
+                        {
+                            let start_suggestion = if let Extern::Explicit(abi, _) = ext {
+                                format!("extern \"{}\" {{", abi.symbol_unescaped)
+                            } else {
+                                "extern {".to_owned()
+                            };
+
+                            let end_suggestion = " }".to_owned();
+                            let end_span = item.span.shrink_to_hi();
+
+                            e
+                            .multipart_suggestion(
+                                "if you meant to declare an externally defined function, use an `extern` block",
+                                vec![(*start_span, start_suggestion), (end_span, end_suggestion)],
+                                Applicability::MaybeIncorrect,
+                             );
+                        }
+                    };
+
+                    self.error_item_without_body_with_help(
+                        item.span,
+                        "function",
+                        msg,
+                        " { <body> }",
+                        f,
+                    );
                 }
+
                 self.visit_vis(&item.vis);
                 self.visit_ident(item.ident);
                 let kind =
@@ -1556,7 +1600,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let FnKind::Fn(
             _,
             _,
-            FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit, .. }, .. },
+            FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit(_), .. }, .. },
             _,
             _,
             _,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 37c4f665415..fd2dd6cf6c7 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -283,7 +283,7 @@ impl<'a> PostExpansionVisitor<'a> {
     }
 
     fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
-        if let ast::Extern::Explicit(abi) = ext {
+        if let ast::Extern::Explicit(abi, _) = ext {
             self.check_abi(abi, constness);
         }
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index ad8dbfd506d..c9e3a7edfa6 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1734,10 +1734,10 @@ impl<'a> State<'a> {
 
         match header.ext {
             ast::Extern::None => {}
-            ast::Extern::Implicit => {
+            ast::Extern::Implicit(_) => {
                 self.word_nbsp("extern");
             }
-            ast::Extern::Explicit(abi) => {
+            ast::Extern::Explicit(abi, _) => {
                 self.word_nbsp("extern");
                 self.print_literal(&abi.as_lit());
                 self.nbsp();
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 73c0bf16a1f..d2a54a646ec 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1628,7 +1628,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             location: Location,
         ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
             if location.statement_index == 0 {
-                let predecessors = body.predecessors()[location.block].to_vec();
+                let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
                 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
             } else {
                 Either::Right(std::iter::once(Location {
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index 0425c53d9dc..721fd3e1c0f 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -26,7 +26,7 @@ pub(super) fn generate_invalidates<'tcx>(
 
     if let Some(all_facts) = all_facts {
         let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
-        let dominators = body.dominators();
+        let dominators = body.basic_blocks.dominators();
         let mut ig = InvalidationGenerator {
             all_facts,
             borrow_set,
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 7d6f37340c2..338df3c70e3 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -334,7 +334,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         };
     }
 
-    let dominators = body.dominators();
+    let dominators = body.basic_blocks.dominators();
 
     let mut mbcx = MirBorrowckCtxt {
         infcx,
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index d4e61ec213b..3795378b568 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -258,7 +258,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
 
                 let block = self.cx.elements.to_location(block_start).block;
                 self.stack.extend(
-                    self.cx.body.predecessors()[block]
+                    self.cx.body.basic_blocks.predecessors()[block]
                         .iter()
                         .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb))
                         .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)),
@@ -354,7 +354,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
         }
 
         let body = self.cx.body;
-        for &pred_block in body.predecessors()[block].iter() {
+        for &pred_block in body.basic_blocks.predecessors()[block].iter() {
             debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
 
             // Check whether the variable is (at least partially)
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 05457ce15e9..50d8fc30d7d 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -66,7 +66,11 @@ fn emit_module(
     let work_product = if backend_config.disable_incr_cache {
         None
     } else {
-        rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(tcx.sess, &name, &tmp_file)
+        rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
+            tcx.sess,
+            &name,
+            &[("o", &tmp_file)],
+        )
     };
 
     ModuleCodegenResult(
@@ -82,7 +86,10 @@ fn reuse_workproduct_for_cgu(
 ) -> CompiledModule {
     let work_product = cgu.previous_work_product(tcx);
     let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str()));
-    let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &work_product.saved_file);
+    let source_file = rustc_incremental::in_incr_comp_dir_sess(
+        &tcx.sess,
+        &work_product.saved_files.get("o").expect("no saved object file in work product"),
+    );
     if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
         tcx.sess.err(&format!(
             "unable to copy {} to {}: {}",
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 72aa790c363..960e98243ac 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -151,11 +151,23 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
             return;
         }
 
-        let remove_temps_from_module = |module: &CompiledModule| {
-            if let Some(ref obj) = module.object {
-                ensure_removed(sess.diagnostic(), obj);
-            }
-        };
+        let maybe_remove_temps_from_module =
+            |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| {
+                if !preserve_objects {
+                    if let Some(ref obj) = module.object {
+                        ensure_removed(sess.diagnostic(), obj);
+                    }
+                }
+
+                if !preserve_dwarf_objects {
+                    if let Some(ref dwo_obj) = module.dwarf_object {
+                        ensure_removed(sess.diagnostic(), dwo_obj);
+                    }
+                }
+            };
+
+        let remove_temps_from_module =
+            |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module);
 
         // Otherwise, always remove the metadata and allocator module temporaries.
         if let Some(ref metadata_module) = codegen_results.metadata_module {
@@ -177,15 +189,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
         debug!(?preserve_objects, ?preserve_dwarf_objects);
 
         for module in &codegen_results.modules {
-            if !preserve_objects {
-                remove_temps_from_module(module);
-            }
-
-            if !preserve_dwarf_objects {
-                if let Some(ref obj) = module.dwarf_object {
-                    ensure_removed(sess.diagnostic(), obj);
-                }
-            }
+            maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module);
         }
     });
 
@@ -649,6 +653,7 @@ fn link_dwarf_object<'a>(
             sess.struct_err("linking dwarf objects with thorin failed")
                 .note(&format!("{:?}", e))
                 .emit();
+            sess.abort_if_errors();
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 632f07c5c2d..f4a5cac872e 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -494,12 +494,18 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
     let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
 
     for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
-        if let Some(path) = &module.object {
-            if let Some((id, product)) =
-                copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, path)
-            {
-                work_products.insert(id, product);
-            }
+        let mut files = Vec::new();
+        if let Some(object_file_path) = &module.object {
+            files.push(("o", object_file_path.as_path()));
+        }
+        if let Some(dwarf_object_file_path) = &module.dwarf_object {
+            files.push(("dwo", dwarf_object_file_path.as_path()));
+        }
+
+        if let Some((id, product)) =
+            copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice())
+        {
+            work_products.insert(id, product);
         }
     }
 
@@ -856,29 +862,50 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
     assert!(module_config.emit_obj != EmitObj::None);
 
     let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
-    let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name));
-    let source_file = in_incr_comp_dir(&incr_comp_session_dir, &module.source.saved_file);
-    debug!(
-        "copying pre-existing module `{}` from {:?} to {}",
-        module.name,
-        source_file,
-        obj_out.display()
+
+    let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| {
+        let source_file = in_incr_comp_dir(&incr_comp_session_dir, saved_path);
+        debug!(
+            "copying pre-existing module `{}` from {:?} to {}",
+            module.name,
+            source_file,
+            output_path.display()
+        );
+        match link_or_copy(&source_file, &output_path) {
+            Ok(_) => Some(output_path),
+            Err(err) => {
+                let diag_handler = cgcx.create_diag_handler();
+                diag_handler.err(&format!(
+                    "unable to copy {} to {}: {}",
+                    source_file.display(),
+                    output_path.display(),
+                    err
+                ));
+                None
+            }
+        }
+    };
+
+    let object = load_from_incr_comp_dir(
+        cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)),
+        &module.source.saved_files.get("o").expect("no saved object file in work product"),
     );
-    if let Err(err) = link_or_copy(&source_file, &obj_out) {
-        let diag_handler = cgcx.create_diag_handler();
-        diag_handler.err(&format!(
-            "unable to copy {} to {}: {}",
-            source_file.display(),
-            obj_out.display(),
-            err
-        ));
-    }
+    let dwarf_object =
+        module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
+            let dwarf_obj_out = cgcx
+                .output_filenames
+                .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name))
+                .expect(
+                    "saved dwarf object in work product but `split_dwarf_path` returned `None`",
+                );
+            load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file)
+        });
 
     WorkItemResult::Compiled(CompiledModule {
         name: module.name,
         kind: ModuleKind::Regular,
-        object: Some(obj_out),
-        dwarf_object: None,
+        object,
+        dwarf_object,
         bytecode: None,
     })
 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 5c26168b50d..24da48ead63 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -15,7 +15,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     fx: &FunctionCx<'a, 'tcx, Bx>,
 ) -> BitSet<mir::Local> {
     let mir = fx.mir;
-    let dominators = mir.dominators();
+    let dominators = mir.basic_blocks.dominators();
     let locals = mir
         .local_decls
         .iter()
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 7ff12823bf7..55ab8b08d4a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -123,7 +123,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     // Do not generate stores and GEPis for zero-sized fields.
                     if !op.layout.is_zst() {
                         let field_index = active_field_index.unwrap_or(i);
-                        let field = dest.project_field(&mut bx, field_index);
+                        let field = if let mir::AggregateKind::Array(_) = **kind {
+                            let llindex = bx.cx().const_usize(field_index as u64);
+                            dest.project_index(&mut bx, llindex)
+                        } else {
+                            dest.project_field(&mut bx, field_index)
+                        };
                         op.val.store(&mut bx, field);
                     }
                 }
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 0bf78446e37..08102585a7b 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -427,7 +427,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
             err_ub!(DanglingIntPointer(0, _)) =>
                 { "a null {kind}" },
             err_ub!(DanglingIntPointer(i, _)) =>
-                { "a dangling {kind} (address 0x{i:x} is unallocated)" },
+                { "a dangling {kind} (address {i:#x} is unallocated)" },
             err_ub!(PointerOutOfBounds { .. }) =>
                 { "a dangling {kind} (going beyond the bounds of its allocation)" },
             // This cannot happen during const-eval (because interning already detects
@@ -941,7 +941,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                                 // element that byte belongs to so we can
                                 // provide an index.
                                 let i = usize::try_from(
-                                    access.uninit_offset.bytes() / layout.size.bytes(),
+                                    access.uninit.start.bytes() / layout.size.bytes(),
                                 )
                                 .unwrap();
                                 self.path.push(PathElem::ArrayElem(i));
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 6298fa7f062..12527a9b2ae 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -856,7 +856,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     literal: ConstantKind::from_const(_const, tcx),
                 }))
             };
-            let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
+            let blocks = self.source.basic_blocks.as_mut();
+            let local_decls = &mut self.source.local_decls;
             let loc = candidate.location;
             let statement = &mut blocks[loc.block].statements[loc.statement_index];
             match statement.kind {
@@ -865,7 +866,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
                 )) => {
                     // Use the underlying local for this (necessarily interior) borrow.
-                    let ty = local_decls.local_decls()[place.local].ty;
+                    let ty = local_decls[place.local].ty;
                     let span = statement.source_info.span;
 
                     let ref_ty = tcx.mk_ref(
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 7b9c6329d32..56ecc553528 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -12,6 +12,7 @@ use rustc_middle::mir::{
     Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
 };
 use rustc_middle::ty::fold::BottomUpFolder;
+use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::always_live_locals;
@@ -275,7 +276,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                 };
 
-                match parent_ty.ty.kind() {
+                let kind = match parent_ty.ty.kind() {
+                    &ty::Opaque(def_id, substs) => {
+                        self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind()
+                    }
+                    kind => kind,
+                };
+
+                match kind {
                     ty::Tuple(fields) => {
                         let Some(f_ty) = fields.get(f.as_usize()) else {
                             fail_out_of_bounds(self, location);
@@ -299,12 +307,39 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         };
                         check_equal(self, location, f_ty);
                     }
-                    ty::Generator(_, substs, _) => {
-                        let substs = substs.as_generator();
-                        let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
-                            fail_out_of_bounds(self, location);
-                            return;
+                    &ty::Generator(def_id, substs, _) => {
+                        let f_ty = if let Some(var) = parent_ty.variant_index {
+                            let gen_body = if def_id == self.body.source.def_id() {
+                                self.body
+                            } else {
+                                self.tcx.optimized_mir(def_id)
+                            };
+
+                            let Some(layout) = gen_body.generator_layout() else {
+                                self.fail(location, format!("No generator layout for {:?}", parent_ty));
+                                return;
+                            };
+
+                            let Some(&local) = layout.variant_fields[var].get(f) else {
+                                fail_out_of_bounds(self, location);
+                                return;
+                            };
+
+                            let Some(&f_ty) = layout.field_tys.get(local) else {
+                                self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
+                                return;
+                            };
+
+                            f_ty
+                        } else {
+                            let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
+                                fail_out_of_bounds(self, location);
+                                return;
+                            };
+
+                            f_ty
                         };
+
                         check_equal(self, location, f_ty);
                     }
                     _ => {
@@ -328,6 +363,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
         {
             self.fail(location, format!("{:?}, has deref at the wrong place", place));
         }
+
+        self.super_place(place, cntxt, location);
     }
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 384a0f9c225..531d9f14040 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -19,7 +19,7 @@
 //!    - Example: Examine each expression to look for its type and do some check or other.
 //!    - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
 //!      `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
-//!      `tcx.hir().deep_visit_all_item_likes(&mut visitor)`. Within your
+//!      `tcx.hir().visit_all_item_likes_in_crate(&mut visitor)`. Within your
 //!      `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
 //!      `intravisit::walk_expr()` to keep walking the subparts).
 //!    - Pro: Visitor methods for any kind of HIR node, not just item-like things.
@@ -190,7 +190,7 @@ use nested_filter::NestedFilter;
 /// (this is why the module is called `intravisit`, to distinguish it
 /// from the AST's `visit` module, which acts differently). If you
 /// simply want to visit all items in the crate in some order, you
-/// should call `Crate::visit_all_items`. Otherwise, see the comment
+/// should call `tcx.hir().visit_all_item_likes_in_crate`. Otherwise, see the comment
 /// on `visit_nested_item` for details on how to visit nested items.
 ///
 /// If you want to ensure that your code handles every variant
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index a89b9eafaa6..93528b4514b 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -75,7 +75,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) {
             let mut visitor =
                 IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] };
             visitor.process_attrs(hir::CRATE_HIR_ID);
-            tcx.hir().deep_visit_all_item_likes(&mut visitor);
+            tcx.hir().visit_all_item_likes_in_crate(&mut visitor);
             (visitor.if_this_changed, visitor.then_this_would_need)
         };
 
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 9c325faae80..f59d8d596b9 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -161,19 +161,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
                 Decodable::decode(&mut work_product_decoder);
 
             for swp in work_products {
-                let mut all_files_exist = true;
-                let path = in_incr_comp_dir_sess(sess, &swp.work_product.saved_file);
-                if !path.exists() {
-                    all_files_exist = false;
-
-                    if sess.opts.debugging_opts.incremental_info {
-                        eprintln!(
-                            "incremental: could not find file for work \
-                                    product: {}",
-                            path.display()
-                        );
+                let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
+                    let exists = in_incr_comp_dir_sess(sess, path).exists();
+                    if !exists && sess.opts.debugging_opts.incremental_info {
+                        eprintln!("incremental: could not find file for work product: {path}",);
                     }
-                }
+                    exists
+                });
 
                 if all_files_exist {
                     debug!("reconcile_work_products: all files for {:?} exist", swp);
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 79836d66011..4059b7cfc8e 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -108,16 +108,17 @@ pub fn save_work_product_index(
     for (id, wp) in previous_work_products.iter() {
         if !new_work_products.contains_key(id) {
             work_product::delete_workproduct_files(sess, wp);
-            debug_assert!(!in_incr_comp_dir_sess(sess, &wp.saved_file).exists());
+            debug_assert!(
+                !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
+            );
         }
     }
 
     // Check that we did not delete one of the current work-products:
     debug_assert!({
-        new_work_products
-            .iter()
-            .map(|(_, wp)| in_incr_comp_dir_sess(sess, &wp.saved_file))
-            .all(|path| path.exists())
+        new_work_products.iter().all(|(_, wp)| {
+            wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
+        })
     });
 }
 
diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs
index 4789c0f581f..1b184eca964 100644
--- a/compiler/rustc_incremental/src/persist/work_product.rs
+++ b/compiler/rustc_incremental/src/persist/work_product.rs
@@ -3,6 +3,7 @@
 //! [work products]: WorkProduct
 
 use crate::persist::fs::*;
+use rustc_data_structures::stable_map::FxHashMap;
 use rustc_fs_util::link_or_copy;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_session::Session;
@@ -13,38 +14,41 @@ use std::path::Path;
 pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
     sess: &Session,
     cgu_name: &str,
-    path: &Path,
+    files: &[(&'static str, &Path)],
 ) -> Option<(WorkProductId, WorkProduct)> {
-    debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path);
+    debug!(?cgu_name, ?files);
     sess.opts.incremental.as_ref()?;
 
-    let file_name = format!("{}.o", cgu_name);
-    let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
-    let saved_file = match link_or_copy(path, &path_in_incr_dir) {
-        Ok(_) => file_name,
-        Err(err) => {
-            sess.warn(&format!(
-                "error copying object file `{}` to incremental directory as `{}`: {}",
-                path.display(),
-                path_in_incr_dir.display(),
-                err
-            ));
-            return None;
+    let mut saved_files = FxHashMap::default();
+    for (ext, path) in files {
+        let file_name = format!("{cgu_name}.{ext}");
+        let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
+        match link_or_copy(path, &path_in_incr_dir) {
+            Ok(_) => {
+                let _ = saved_files.insert(ext.to_string(), file_name);
+            }
+            Err(err) => {
+                sess.warn(&format!(
+                    "error copying object file `{}` to incremental directory as `{}`: {}",
+                    path.display(),
+                    path_in_incr_dir.display(),
+                    err
+                ));
+            }
         }
-    };
-
-    let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file };
+    }
 
+    let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files };
+    debug!(?work_product);
     let work_product_id = WorkProductId::from_cgu_name(cgu_name);
     Some((work_product_id, work_product))
 }
 
 /// Removes files for a given work product.
 pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
-    let path = in_incr_comp_dir_sess(sess, &work_product.saved_file);
-    match std_fs::remove_file(&path) {
-        Ok(()) => {}
-        Err(err) => {
+    for (_, path) in &work_product.saved_files {
+        let path = in_incr_comp_dir_sess(sess, path);
+        if let Err(err) = std_fs::remove_file(&path) {
             sess.warn(&format!(
                 "file-system error deleting outdated file `{}`: {}",
                 path.display(),
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index 976874c7cee..5b664e19c18 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -1546,6 +1546,16 @@ impl<T: Idx> GrowableBitSet<T> {
         let (word_index, mask) = word_index_and_mask(elem);
         self.bit_set.words.get(word_index).map_or(false, |word| (word & mask) != 0)
     }
+
+    #[inline]
+    pub fn iter(&self) -> BitIter<'_, T> {
+        self.bit_set.iter()
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.bit_set.count()
+    }
 }
 
 impl<T: Idx> From<BitSet<T>> for GrowableBitSet<T> {
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 83328093e9f..5725c240320 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -34,7 +34,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
-use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::Session;
 use rustc_span::lev_distance::find_best_match_for_name;
@@ -906,6 +906,29 @@ pub trait LintContext: Sized {
     ) {
         self.lookup(lint, None as Option<Span>, decorate);
     }
+
+    /// This returns the lint level for the given lint at the current location.
+    fn get_lint_level(&self, lint: &'static Lint) -> Level;
+
+    /// This function can be used to manually fulfill an expectation. This can
+    /// be used for lints which contain several spans, and should be suppressed,
+    /// if either location was marked with an expectation.
+    ///
+    /// Note that this function should only be called for [`LintExpectationId`]s
+    /// retrieved from the current lint pass. Buffered or manually created ids can
+    /// cause ICEs.
+    fn fulfill_expectation(&self, expectation: LintExpectationId) {
+        // We need to make sure that submitted expectation ids are correctly fulfilled suppressed
+        // and stored between compilation sessions. To not manually do these steps, we simply create
+        // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal
+        // expected lint diagnostic.
+        self.sess()
+            .struct_expect(
+                "this is a dummy diagnostic, to submit and store an expectation",
+                expectation,
+            )
+            .emit();
+    }
 }
 
 impl<'a> EarlyContext<'a> {
@@ -953,6 +976,10 @@ impl LintContext for LateContext<'_> {
             None => self.tcx.struct_lint_node(lint, hir_id, decorate),
         }
     }
+
+    fn get_lint_level(&self, lint: &'static Lint) -> Level {
+        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
+    }
 }
 
 impl LintContext for EarlyContext<'_> {
@@ -975,6 +1002,10 @@ impl LintContext for EarlyContext<'_> {
     ) {
         self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
     }
+
+    fn get_lint_level(&self, lint: &'static Lint) -> Level {
+        self.builder.lint_level(lint).0
+    }
 }
 
 impl<'tcx> LateContext<'tcx> {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 40601bb5aad..9fc2249b290 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -520,6 +520,11 @@ declare_lint! {
     /// The `expect` attribute can be removed if this is intended behavior otherwise
     /// it should be investigated why the expected lint is no longer issued.
     ///
+    /// In rare cases, the expectation might be emitted at a different location than
+    /// shown in the shown code snippet. In most cases, the `#[expect]` attribute
+    /// works when added to the outer scope. A few lints can only be expected
+    /// on a crate level.
+    ///
     /// Part of RFC 2383. The progress is being tracked in [#54503]
     ///
     /// [#54503]: https://github.com/rust-lang/rust/issues/54503
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 1cd19c7eaab..48f441e69d6 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -232,6 +232,13 @@ impl Level {
             Level::Deny | Level::Forbid => true,
         }
     }
+
+    pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
+        match self {
+            Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
+            _ => None,
+        }
+    }
 }
 
 /// Specification of a single lint.
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index bb4b502bded..0442cbc0c93 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -419,7 +419,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             return;
         }
 
-        self.tcx.hir().deep_visit_all_item_likes(self);
+        self.tcx.hir().visit_all_item_likes_in_crate(self);
     }
 
     fn encode_def_path_table(&mut self) {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index cda0a60fa4e..419ce2996fb 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -568,7 +568,7 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    /// Walks the contents of a crate. See also `Crate::visit_all_items`.
+    /// Walks the contents of the local crate. See also `visit_all_item_likes_in_crate`.
     pub fn walk_toplevel_module(self, visitor: &mut impl Visitor<'hir>) {
         let (top_mod, span, hir_id) = self.get_module(CRATE_DEF_ID);
         visitor.visit_mod(top_mod, span, hir_id);
@@ -588,53 +588,61 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    /// Visits all items in the crate in some deterministic (but
-    /// unspecified) order. If you need to process every item,
-    /// and care about nesting -- usually because your algorithm
-    /// follows lexical scoping rules -- then this method is the best choice.
-    /// If you don't care about nesting, you should use the `tcx.hir_crate_items()` query
-    /// or `items()` instead.
+    /// Visits all item-likes in the crate in some deterministic (but unspecified) order. If you
+    /// need to process every item-like, and don't care about visiting nested items in a particular
+    /// order then this method is the best choice.  If you do care about this nesting, you should
+    /// use the `tcx.hir().walk_toplevel_module`.
+    ///
+    /// Note that this function will access HIR for all the item-likes in the crate.  If you only
+    /// need to access some of them, it is usually better to manually loop on the iterators
+    /// provided by `tcx.hir_crate_items(())`.
     ///
     /// Please see the notes in `intravisit.rs` for more information.
-    pub fn deep_visit_all_item_likes<V>(self, visitor: &mut V)
+    pub fn visit_all_item_likes_in_crate<V>(self, visitor: &mut V)
     where
         V: Visitor<'hir>,
     {
-        let krate = self.krate();
-        for owner in krate.owners.iter().filter_map(|i| i.as_owner()) {
-            match owner.node() {
-                OwnerNode::Item(item) => visitor.visit_item(item),
-                OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item),
-                OwnerNode::ImplItem(item) => visitor.visit_impl_item(item),
-                OwnerNode::TraitItem(item) => visitor.visit_trait_item(item),
-                OwnerNode::Crate(_) => {}
-            }
+        let krate = self.tcx.hir_crate_items(());
+
+        for id in krate.items() {
+            visitor.visit_item(self.item(id));
+        }
+
+        for id in krate.trait_items() {
+            visitor.visit_trait_item(self.trait_item(id));
+        }
+
+        for id in krate.impl_items() {
+            visitor.visit_impl_item(self.impl_item(id));
+        }
+
+        for id in krate.foreign_items() {
+            visitor.visit_foreign_item(self.foreign_item(id));
         }
     }
 
-    /// If you don't care about nesting, you should use the
-    /// `tcx.hir_module_items()` query or `module_items()` instead.
-    /// Please see notes in `deep_visit_all_item_likes`.
-    pub fn deep_visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
+    /// This method is the equivalent of `visit_all_item_likes_in_crate` but restricted to
+    /// item-likes in a single module.
+    pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
     where
         V: Visitor<'hir>,
     {
         let module = self.tcx.hir_module_items(module);
 
-        for id in module.items.iter() {
-            visitor.visit_item(self.item(*id));
+        for id in module.items() {
+            visitor.visit_item(self.item(id));
         }
 
-        for id in module.trait_items.iter() {
-            visitor.visit_trait_item(self.trait_item(*id));
+        for id in module.trait_items() {
+            visitor.visit_trait_item(self.trait_item(id));
         }
 
-        for id in module.impl_items.iter() {
-            visitor.visit_impl_item(self.impl_item(*id));
+        for id in module.impl_items() {
+            visitor.visit_impl_item(self.impl_item(id));
         }
 
-        for id in module.foreign_items.iter() {
-            visitor.visit_foreign_item(self.foreign_item(*id));
+        for id in module.foreign_items() {
+            visitor.visit_foreign_item(self.foreign_item(id));
         }
     }
 
diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs
index d56e87bbb47..6896837aa91 100644
--- a/compiler/rustc_middle/src/hir/nested_filter.rs
+++ b/compiler/rustc_middle/src/hir/nested_filter.rs
@@ -8,7 +8,7 @@ use rustc_hir::intravisit::nested_filter::NestedFilter;
 /// constant arguments of types, e.g. in `let _: [(); /* HERE */];`.
 ///
 /// **This is the most common choice.** A very common pattern is
-/// to use `deep_visit_all_item_likes()` as an outer loop,
+/// to use `visit_all_item_likes_in_crate()` as an outer loop,
 /// and to have the visitor that visits the contents of each item
 /// using this setting.
 pub struct OnlyBodies(());
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
new file mode 100644
index 00000000000..78080fcd581
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -0,0 +1,147 @@
+use crate::mir::graph_cyclic_cache::GraphIsCyclicCache;
+use crate::mir::predecessors::{PredecessorCache, Predecessors};
+use crate::mir::switch_sources::{SwitchSourceCache, SwitchSources};
+use crate::mir::traversal::PostorderCache;
+use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK};
+
+use rustc_data_structures::graph;
+use rustc_data_structures::graph::dominators::{dominators, Dominators};
+use rustc_index::vec::IndexVec;
+
+#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
+pub struct BasicBlocks<'tcx> {
+    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+    predecessor_cache: PredecessorCache,
+    switch_source_cache: SwitchSourceCache,
+    is_cyclic: GraphIsCyclicCache,
+    postorder_cache: PostorderCache,
+}
+
+impl<'tcx> BasicBlocks<'tcx> {
+    #[inline]
+    pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
+        BasicBlocks {
+            basic_blocks,
+            predecessor_cache: PredecessorCache::new(),
+            switch_source_cache: SwitchSourceCache::new(),
+            is_cyclic: GraphIsCyclicCache::new(),
+            postorder_cache: PostorderCache::new(),
+        }
+    }
+
+    /// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
+    #[inline]
+    pub fn is_cfg_cyclic(&self) -> bool {
+        self.is_cyclic.is_cyclic(self)
+    }
+
+    #[inline]
+    pub fn dominators(&self) -> Dominators<BasicBlock> {
+        dominators(&self)
+    }
+
+    /// Returns predecessors for each basic block.
+    #[inline]
+    pub fn predecessors(&self) -> &Predecessors {
+        self.predecessor_cache.compute(&self.basic_blocks)
+    }
+
+    /// Returns basic blocks in a postorder.
+    #[inline]
+    pub fn postorder(&self) -> &[BasicBlock] {
+        self.postorder_cache.compute(&self.basic_blocks)
+    }
+
+    /// `switch_sources()[&(target, switch)]` returns a list of switch
+    /// values that lead to a `target` block from a `switch` block.
+    #[inline]
+    pub fn switch_sources(&self) -> &SwitchSources {
+        self.switch_source_cache.compute(&self.basic_blocks)
+    }
+
+    /// Returns mutable reference to basic blocks. Invalidates CFG cache.
+    #[inline]
+    pub fn as_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        self.invalidate_cfg_cache();
+        &mut self.basic_blocks
+    }
+
+    /// Get mutable access to basic blocks without invalidating the CFG cache.
+    ///
+    /// By calling this method instead of e.g. [`BasicBlocks::as_mut`] you promise not to change
+    /// the CFG. This means that
+    ///
+    ///  1) The number of basic blocks remains unchanged
+    ///  2) The set of successors of each terminator remains unchanged.
+    ///  3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator
+    ///     kind is not changed.
+    ///
+    /// If any of these conditions cannot be upheld, you should call [`BasicBlocks::invalidate_cfg_cache`].
+    #[inline]
+    pub fn as_mut_preserves_cfg(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        &mut self.basic_blocks
+    }
+
+    /// Invalidates cached information about the CFG.
+    ///
+    /// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`].
+    /// All other methods that allow you to mutate the basic blocks also call this method
+    /// themselves, thereby avoiding any risk of accidentaly cache invalidation.
+    pub fn invalidate_cfg_cache(&mut self) {
+        self.predecessor_cache.invalidate();
+        self.switch_source_cache.invalidate();
+        self.is_cyclic.invalidate();
+        self.postorder_cache.invalidate();
+    }
+}
+
+impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> {
+    type Target = IndexVec<BasicBlock, BasicBlockData<'tcx>>;
+
+    #[inline]
+    fn deref(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        &self.basic_blocks
+    }
+}
+
+impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> {
+    type Node = BasicBlock;
+}
+
+impl<'tcx> graph::WithNumNodes for BasicBlocks<'tcx> {
+    #[inline]
+    fn num_nodes(&self) -> usize {
+        self.basic_blocks.len()
+    }
+}
+
+impl<'tcx> graph::WithStartNode for BasicBlocks<'tcx> {
+    #[inline]
+    fn start_node(&self) -> Self::Node {
+        START_BLOCK
+    }
+}
+
+impl<'tcx> graph::WithSuccessors for BasicBlocks<'tcx> {
+    #[inline]
+    fn successors(&self, node: Self::Node) -> <Self as graph::GraphSuccessors<'_>>::Iter {
+        self.basic_blocks[node].terminator().successors()
+    }
+}
+
+impl<'a, 'b> graph::GraphSuccessors<'b> for BasicBlocks<'a> {
+    type Item = BasicBlock;
+    type Iter = Successors<'b>;
+}
+
+impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for BasicBlocks<'tcx> {
+    type Item = BasicBlock;
+    type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>;
+}
+
+impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
+    #[inline]
+    fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
+        self.predecessors()[node].iter().copied()
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 1bbd71c3f1f..ae333846f06 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -179,6 +179,11 @@ pub fn alloc_range(start: Size, size: Size) -> AllocRange {
 }
 
 impl AllocRange {
+    #[inline]
+    pub fn from(r: Range<Size>) -> Self {
+        alloc_range(r.start, r.end - r.start) // `Size` subtraction (overflow-checked)
+    }
+
     #[inline(always)]
     pub fn end(self) -> Size {
         self.start + self.size // This does overflow checking.
@@ -1095,9 +1100,9 @@ impl InitMask {
     /// Returns `Ok(())` if it's initialized. Otherwise returns a range of byte
     /// indexes for the first contiguous span of the uninitialized access.
     #[inline]
-    pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), Range<Size>> {
+    pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), AllocRange> {
         if end > self.len {
-            return Err(self.len..end);
+            return Err(AllocRange::from(self.len..end));
         }
 
         let uninit_start = self.find_bit(start, end, false);
@@ -1105,7 +1110,7 @@ impl InitMask {
         match uninit_start {
             Some(uninit_start) => {
                 let uninit_end = self.find_bit(uninit_start, end, true).unwrap_or(end);
-                Err(uninit_start..uninit_end)
+                Err(AllocRange::from(uninit_start..uninit_end))
             }
             None => Ok(()),
         }
@@ -1176,19 +1181,17 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
     ///
     /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte
     /// indexes of the first contiguous uninitialized access.
-    fn is_init(&self, range: AllocRange) -> Result<(), Range<Size>> {
+    fn is_init(&self, range: AllocRange) -> Result<(), AllocRange> {
         self.init_mask.is_range_initialized(range.start, range.end()) // `Size` addition
     }
 
     /// Checks that a range of bytes is initialized. If not, returns the `InvalidUninitBytes`
     /// error which will report the first range of bytes which is uninitialized.
     fn check_init(&self, range: AllocRange) -> AllocResult {
-        self.is_init(range).map_err(|idx_range| {
+        self.is_init(range).map_err(|uninit_range| {
             AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
-                access_offset: range.start,
-                access_size: range.size,
-                uninit_offset: idx_range.start,
-                uninit_size: idx_range.end - idx_range.start, // `Size` subtraction
+                access: range,
+                uninit: uninit_range,
             }))
         })
     }
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 2a1fd6f736e..795f23edb31 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,4 +1,4 @@
-use super::{AllocId, ConstAlloc, Pointer, Scalar};
+use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
 
 use crate::mir::interpret::ConstValue;
 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree};
@@ -162,9 +162,9 @@ impl fmt::Display for InvalidProgramInfo<'_> {
             AlreadyReported(ErrorGuaranteed { .. }) => {
                 write!(f, "encountered constants with type errors, stopping evaluation")
             }
-            Layout(ref err) => write!(f, "{}", err),
-            FnAbiAdjustForForeignAbi(ref err) => write!(f, "{}", err),
-            SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty),
+            Layout(ref err) => write!(f, "{err}"),
+            FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
+            SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
         }
     }
 }
@@ -205,14 +205,10 @@ impl fmt::Display for CheckInAllocMsg {
 /// Details of an access to uninitialized bytes where it is not allowed.
 #[derive(Debug)]
 pub struct UninitBytesAccess {
-    /// Location of the original memory access.
-    pub access_offset: Size,
-    /// Size of the original memory access.
-    pub access_size: Size,
-    /// Location of the first uninitialized byte that was accessed.
-    pub uninit_offset: Size,
-    /// Number of consecutive uninitialized bytes that were accessed.
-    pub uninit_size: Size,
+    /// Range of the original memory access.
+    pub access: AllocRange,
+    /// Range of the uninit memory that was encountered. (Might not be maximal.)
+    pub uninit: AllocRange,
 }
 
 /// Information about a size mismatch.
@@ -308,30 +304,28 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use UndefinedBehaviorInfo::*;
         match self {
-            Ub(msg) => write!(f, "{}", msg),
+            Ub(msg) => write!(f, "{msg}"),
             Unreachable => write!(f, "entering unreachable code"),
             BoundsCheckFailed { ref len, ref index } => {
-                write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
+                write!(f, "indexing out of bounds: the len is {len} but the index is {index}")
             }
             DivisionByZero => write!(f, "dividing by zero"),
             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
             DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
             RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
-            InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
+            InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
             InvalidVtableDropFn(sig) => write!(
                 f,
-                "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
-                sig
+                "invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type",
             ),
             InvalidVtableSize => {
                 write!(f, "invalid vtable: size is bigger than largest supported object")
             }
-            InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
+            InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"),
             UnterminatedCString(p) => write!(
                 f,
-                "reading a null-terminated string starting at {:?} with no null found before end of allocation",
-                p,
+                "reading a null-terminated string starting at {p:?} with no null found before end of allocation",
             ),
             PointerUseAfterFree(a) => {
                 write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
@@ -359,41 +353,36 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
             }
             AlignmentCheckFailed { required, has } => write!(
                 f,
-                "accessing memory with alignment {}, but alignment {} is required",
-                has.bytes(),
-                required.bytes()
+                "accessing memory with alignment {has}, but alignment {required} is required",
+                has = has.bytes(),
+                required = required.bytes()
             ),
             WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
             DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
             ValidationFailure { path: None, msg } => {
-                write!(f, "constructing invalid value: {}", msg)
+                write!(f, "constructing invalid value: {msg}")
             }
             ValidationFailure { path: Some(path), msg } => {
-                write!(f, "constructing invalid value at {}: {}", path, msg)
+                write!(f, "constructing invalid value at {path}: {msg}")
             }
             InvalidBool(b) => {
-                write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
+                write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
             }
             InvalidChar(c) => {
-                write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
+                write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
             }
-            InvalidTag(val) => write!(f, "enum value has invalid tag: {:x}", val),
+            InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
             InvalidFunctionPointer(p) => {
-                write!(f, "using {:?} as function pointer but it does not point to a function", p)
+                write!(f, "using {p:?} as function pointer but it does not point to a function")
             }
-            InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
-            InvalidUninitBytes(Some((alloc, access))) => write!(
+            InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
+            InvalidUninitBytes(Some((alloc, info))) => write!(
                 f,
-                "reading {} byte{} of memory starting at {:?}, \
-                 but {} byte{} {} uninitialized starting at {:?}, \
+                "reading memory at {alloc:?}{access:?}, \
+                 but memory is uninitialized at {uninit:?}, \
                  and this operation requires initialized memory",
-                access.access_size.bytes(),
-                pluralize!(access.access_size.bytes()),
-                Pointer::new(*alloc, access.access_offset),
-                access.uninit_size.bytes(),
-                pluralize!(access.uninit_size.bytes()),
-                pluralize!("is", access.uninit_size.bytes()),
-                Pointer::new(*alloc, access.uninit_offset),
+                access = info.access,
+                uninit = info.uninit,
             ),
             InvalidUninitBytes(None) => write!(
                 f,
@@ -402,8 +391,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
             DeadLocal => write!(f, "accessing a dead local variable"),
             ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
                 f,
-                "scalar size mismatch: expected {} bytes but got {} bytes instead",
-                target_size, data_size
+                "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
             ),
             UninhabitedEnumVariantWritten => {
                 write!(f, "writing discriminant of an uninhabited enum")
@@ -437,13 +425,13 @@ impl fmt::Display for UnsupportedOpInfo {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use UnsupportedOpInfo::*;
         match self {
-            Unsupported(ref msg) => write!(f, "{}", msg),
+            Unsupported(ref msg) => write!(f, "{msg}"),
             ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
             PartialPointerOverwrite(ptr) => {
-                write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr)
+                write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
             }
-            ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
-            ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
+            ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
+            ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
         }
     }
 }
@@ -526,11 +514,11 @@ impl fmt::Display for InterpError<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use InterpError::*;
         match *self {
-            Unsupported(ref msg) => write!(f, "{}", msg),
-            InvalidProgram(ref msg) => write!(f, "{}", msg),
-            UndefinedBehavior(ref msg) => write!(f, "{}", msg),
-            ResourceExhaustion(ref msg) => write!(f, "{}", msg),
-            MachineStop(ref msg) => write!(f, "{}", msg),
+            Unsupported(ref msg) => write!(f, "{msg}"),
+            InvalidProgram(ref msg) => write!(f, "{msg}"),
+            UndefinedBehavior(ref msg) => write!(f, "{msg}"),
+            ResourceExhaustion(ref msg) => write!(f, "{msg}"),
+            MachineStop(ref msg) => write!(f, "{msg}"),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index e7d7317456c..93686128101 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -5,7 +5,6 @@
 use crate::mir::interpret::{
     AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar,
 };
-use crate::mir::traversal::PostorderCache;
 use crate::mir::visit::MirVisitable;
 use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
@@ -27,8 +26,7 @@ use rustc_target::abi::{Size, VariantIdx};
 use polonius_engine::Atom;
 pub use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::graph::dominators::{dominators, Dominators};
-use rustc_data_structures::graph::{self, GraphSuccessors};
+use rustc_data_structures::graph::dominators::Dominators;
 use rustc_index::bit_set::BitMatrix;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_serialize::{Decodable, Encodable};
@@ -43,11 +41,10 @@ use std::fmt::{self, Debug, Display, Formatter, Write};
 use std::ops::{ControlFlow, Index, IndexMut};
 use std::{iter, mem};
 
-use self::graph_cyclic_cache::GraphIsCyclicCache;
-use self::predecessors::{PredecessorCache, Predecessors};
 pub use self::query::*;
-use self::switch_sources::{SwitchSourceCache, SwitchSources};
+pub use basic_blocks::BasicBlocks;
 
+mod basic_blocks;
 pub mod coverage;
 mod generic_graph;
 pub mod generic_graphviz;
@@ -189,7 +186,7 @@ pub struct GeneratorInfo<'tcx> {
 pub struct Body<'tcx> {
     /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`]
     /// that indexes into this vector.
-    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+    pub basic_blocks: BasicBlocks<'tcx>,
 
     /// Records how far through the "desugaring and optimization" process this particular
     /// MIR has traversed. This is particularly useful when inlining, since in that context
@@ -257,11 +254,6 @@ pub struct Body<'tcx> {
     /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this.
     pub is_polymorphic: bool,
 
-    predecessor_cache: PredecessorCache,
-    switch_source_cache: SwitchSourceCache,
-    is_cyclic: GraphIsCyclicCache,
-    postorder_cache: PostorderCache,
-
     pub tainted_by_errors: Option<ErrorGuaranteed>,
 }
 
@@ -289,7 +281,7 @@ impl<'tcx> Body<'tcx> {
         let mut body = Body {
             phase: MirPhase::Built,
             source,
-            basic_blocks,
+            basic_blocks: BasicBlocks::new(basic_blocks),
             source_scopes,
             generator: generator_kind.map(|generator_kind| {
                 Box::new(GeneratorInfo {
@@ -307,10 +299,6 @@ impl<'tcx> Body<'tcx> {
             span,
             required_consts: Vec::new(),
             is_polymorphic: false,
-            predecessor_cache: PredecessorCache::new(),
-            switch_source_cache: SwitchSourceCache::new(),
-            is_cyclic: GraphIsCyclicCache::new(),
-            postorder_cache: PostorderCache::new(),
             tainted_by_errors,
         };
         body.is_polymorphic = body.has_param_types_or_consts();
@@ -326,7 +314,7 @@ impl<'tcx> Body<'tcx> {
         let mut body = Body {
             phase: MirPhase::Built,
             source: MirSource::item(CRATE_DEF_ID.to_def_id()),
-            basic_blocks,
+            basic_blocks: BasicBlocks::new(basic_blocks),
             source_scopes: IndexVec::new(),
             generator: None,
             local_decls: IndexVec::new(),
@@ -337,10 +325,6 @@ impl<'tcx> Body<'tcx> {
             required_consts: Vec::new(),
             var_debug_info: Vec::new(),
             is_polymorphic: false,
-            predecessor_cache: PredecessorCache::new(),
-            switch_source_cache: SwitchSourceCache::new(),
-            is_cyclic: GraphIsCyclicCache::new(),
-            postorder_cache: PostorderCache::new(),
             tainted_by_errors: None,
         };
         body.is_polymorphic = body.has_param_types_or_consts();
@@ -354,74 +338,7 @@ impl<'tcx> Body<'tcx> {
 
     #[inline]
     pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
-        // Because the user could mutate basic block terminators via this reference, we need to
-        // invalidate the caches.
-        //
-        // FIXME: Use a finer-grained API for this, so only transformations that alter terminators
-        // invalidate the caches.
-        self.invalidate_cfg_cache();
-        &mut self.basic_blocks
-    }
-
-    #[inline]
-    pub fn basic_blocks_and_local_decls_mut(
-        &mut self,
-    ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
-        self.invalidate_cfg_cache();
-        (&mut self.basic_blocks, &mut self.local_decls)
-    }
-
-    #[inline]
-    pub fn basic_blocks_local_decls_mut_and_var_debug_info(
-        &mut self,
-    ) -> (
-        &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
-        &mut LocalDecls<'tcx>,
-        &mut Vec<VarDebugInfo<'tcx>>,
-    ) {
-        self.invalidate_cfg_cache();
-        (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
-    }
-
-    /// Get mutable access to parts of the Body without invalidating the CFG cache.
-    ///
-    /// By calling this method instead of eg [`Body::basic_blocks_mut`], you promise not to change
-    /// the CFG. This means that
-    ///
-    ///  1) The number of basic blocks remains unchanged
-    ///  2) The set of successors of each terminator remains unchanged.
-    ///  3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator
-    ///     kind is not changed.
-    ///
-    /// If any of these conditions cannot be upheld, you should call [`Body::invalidate_cfg_cache`].
-    #[inline]
-    pub fn basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(
-        &mut self,
-    ) -> (
-        &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
-        &mut LocalDecls<'tcx>,
-        &mut Vec<VarDebugInfo<'tcx>>,
-    ) {
-        (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
-    }
-
-    /// Invalidates cached information about the CFG.
-    ///
-    /// You will only ever need this if you have also called
-    /// [`Body::basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate`]. All other methods
-    /// that allow you to mutate the body also call this method themselves, thereby avoiding any
-    /// risk of accidentaly cache invalidation.
-    pub fn invalidate_cfg_cache(&mut self) {
-        self.predecessor_cache.invalidate();
-        self.switch_source_cache.invalidate();
-        self.is_cyclic.invalidate();
-        self.postorder_cache.invalidate();
-    }
-
-    /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
-    /// `START_BLOCK`.
-    pub fn is_cfg_cyclic(&self) -> bool {
-        self.is_cyclic.is_cyclic(self)
+        self.basic_blocks.as_mut()
     }
 
     #[inline]
@@ -495,14 +412,6 @@ impl<'tcx> Body<'tcx> {
         self.local_decls.drain(self.arg_count + 1..)
     }
 
-    /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
-    /// invalidating statement indices in `Location`s.
-    pub fn make_statement_nop(&mut self, location: Location) {
-        let block = &mut self.basic_blocks[location.block];
-        debug_assert!(location.statement_index < block.statements.len());
-        block.statements[location.statement_index].make_nop()
-    }
-
     /// Returns the source info associated with `location`.
     pub fn source_info(&self, location: Location) -> &SourceInfo {
         let block = &self[location.block];
@@ -539,23 +448,6 @@ impl<'tcx> Body<'tcx> {
     }
 
     #[inline]
-    pub fn predecessors(&self) -> &Predecessors {
-        self.predecessor_cache.compute(&self.basic_blocks)
-    }
-
-    /// `body.switch_sources()[&(target, switch)]` returns a list of switch
-    /// values that lead to a `target` block from a `switch` block.
-    #[inline]
-    pub fn switch_sources(&self) -> &SwitchSources {
-        self.switch_source_cache.compute(&self.basic_blocks)
-    }
-
-    #[inline]
-    pub fn dominators(&self) -> Dominators<BasicBlock> {
-        dominators(self)
-    }
-
-    #[inline]
     pub fn yield_ty(&self) -> Option<Ty<'tcx>> {
         self.generator.as_ref().and_then(|generator| generator.yield_ty)
     }
@@ -599,7 +491,7 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> {
 impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
     #[inline]
     fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        &mut self.basic_blocks_mut()[index]
+        &mut self.basic_blocks.as_mut()[index]
     }
 }
 
@@ -2890,48 +2782,6 @@ fn pretty_print_const_value<'tcx>(
     })
 }
 
-impl<'tcx> graph::DirectedGraph for Body<'tcx> {
-    type Node = BasicBlock;
-}
-
-impl<'tcx> graph::WithNumNodes for Body<'tcx> {
-    #[inline]
-    fn num_nodes(&self) -> usize {
-        self.basic_blocks.len()
-    }
-}
-
-impl<'tcx> graph::WithStartNode for Body<'tcx> {
-    #[inline]
-    fn start_node(&self) -> Self::Node {
-        START_BLOCK
-    }
-}
-
-impl<'tcx> graph::WithSuccessors for Body<'tcx> {
-    #[inline]
-    fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
-        self.basic_blocks[node].terminator().successors()
-    }
-}
-
-impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
-    type Item = BasicBlock;
-    type Iter = Successors<'b>;
-}
-
-impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> {
-    type Item = BasicBlock;
-    type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>;
-}
-
-impl<'tcx> graph::WithPredecessors for Body<'tcx> {
-    #[inline]
-    fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
-        self.predecessors()[node].iter().copied()
-    }
-}
-
 /// `Location` represents the position of the start of the statement; or, if
 /// `statement_index` equals the number of statements, then the start of the
 /// terminator.
@@ -2968,7 +2818,7 @@ impl Location {
             return true;
         }
 
-        let predecessors = body.predecessors();
+        let predecessors = body.basic_blocks.predecessors();
 
         // If we're in another block, then we want to check that block is a predecessor of `other`.
         let mut queue: Vec<BasicBlock> = predecessors[other.block].to_vec();
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 30648679dae..627dc32f37e 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -104,22 +104,25 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
 ///
 /// A Postorder traversal of this graph is `D B C A` or `D C B A`
 pub struct Postorder<'a, 'tcx> {
-    body: &'a Body<'tcx>,
+    basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>,
     visited: BitSet<BasicBlock>,
     visit_stack: Vec<(BasicBlock, Successors<'a>)>,
     root_is_start_block: bool,
 }
 
 impl<'a, 'tcx> Postorder<'a, 'tcx> {
-    pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
+    pub fn new(
+        basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+        root: BasicBlock,
+    ) -> Postorder<'a, 'tcx> {
         let mut po = Postorder {
-            body,
-            visited: BitSet::new_empty(body.basic_blocks().len()),
+            basic_blocks,
+            visited: BitSet::new_empty(basic_blocks.len()),
             visit_stack: Vec::new(),
             root_is_start_block: root == START_BLOCK,
         };
 
-        let data = &po.body[root];
+        let data = &po.basic_blocks[root];
 
         if let Some(ref term) = data.terminator {
             po.visited.insert(root);
@@ -190,7 +193,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
             };
 
             if self.visited.insert(bb) {
-                if let Some(term) = &self.body[bb].terminator {
+                if let Some(term) = &self.basic_blocks[bb].terminator {
                     self.visit_stack.push((bb, term.successors()));
                 }
             }
@@ -199,7 +202,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
 }
 
 pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> {
-    Postorder::new(body, START_BLOCK)
+    Postorder::new(&body.basic_blocks, START_BLOCK)
 }
 
 impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
@@ -211,12 +214,12 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
             self.traverse_successor();
         }
 
-        next.map(|(bb, _)| (bb, &self.body[bb]))
+        next.map(|(bb, _)| (bb, &self.basic_blocks[bb]))
     }
 
     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.basic_blocks.len() - self.visited.count();
 
         let lower = if self.root_is_start_block {
             // We will visit all remaining blocks exactly once.
@@ -263,10 +266,8 @@ pub struct ReversePostorder<'a, 'tcx> {
 
 impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
     pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
-        let blocks: Vec<_> = Postorder::new(body, root).map(|(bb, _)| bb).collect();
-
+        let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect();
         let len = blocks.len();
-
         ReversePostorder { body, blocks, idx: len }
     }
 }
@@ -334,10 +335,8 @@ impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
 impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
 
 pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
-    let blocks = body.postorder_cache.compute(body);
-
+    let blocks = body.basic_blocks.postorder();
     let len = blocks.len();
-
     ReversePostorderIter { body, blocks, idx: len }
 }
 
@@ -360,7 +359,7 @@ impl PostorderCache {
 
     /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
     #[inline]
-    pub(super) fn compute(&self, body: &Body<'_>) -> &[BasicBlock] {
+    pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] {
         self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
     }
 }
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index 5470cc1262e..d21a8c4f9b9 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::graph::iterate::{
     NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
 };
 use rustc_hir::intravisit::FnKind;
-use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind};
+use rustc_middle::mir::{BasicBlock, BasicBlocks, Body, Operand, TerminatorKind};
 use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
 use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
 use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
@@ -30,7 +30,9 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
         };
 
         let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs };
-        if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) {
+        if let Some(NonRecursive) =
+            TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis)
+        {
             return;
         }
         if vis.reachable_recursive_calls.is_empty() {
@@ -101,7 +103,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> {
     }
 }
 
-impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
+impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
     type BreakVal = NonRecursive;
 
     fn node_examined(
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 05a4d7bbf3e..5c77f3ea395 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -228,7 +228,7 @@ impl Direction for Backward {
     ) where
         A: Analysis<'tcx>,
     {
-        for pred in body.predecessors()[bb].iter().copied() {
+        for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
             match body[pred].terminator().kind {
                 // Apply terminator-specific edge effects.
                 //
@@ -316,7 +316,7 @@ where
     fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
         assert!(!self.effects_applied);
 
-        let values = &self.body.switch_sources()[&(self.bb, self.pred)];
+        let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
         let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
 
         let mut tmp = None;
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index 20e14a77c1e..180376d648a 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -101,7 +101,7 @@ where
         // transfer function for each block exactly once (assuming that we process blocks in RPO).
         //
         // In this case, there's no need to compute the block transfer functions ahead of time.
-        if !body.is_cfg_cyclic() {
+        if !body.basic_blocks.is_cfg_cyclic() {
             return Self::new(tcx, body, analysis, None);
         }
 
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index 67c16e6c084..f9fd6c9c56b 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -1,7 +1,7 @@
 //! A framework that can express both [gen-kill] and generic dataflow problems.
 //!
-//! To actually use this framework, you must implement either the `Analysis` or the
-//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
+//! To use this framework, implement either the [`Analysis`] or the
+//! [`GenKillAnalysis`] trait. If your transfer function can be expressed with only gen/kill
 //! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
 //! `impls` module contains several examples of gen/kill dataflow analyses.
 //!
@@ -96,7 +96,7 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> {
     }
 }
 
-/// Define the domain of a dataflow problem.
+/// Defines the domain of a dataflow problem.
 ///
 /// This trait specifies the lattice on which this analysis operates (the domain) as well as its
 /// initial value at the entry point of each basic block.
@@ -113,12 +113,12 @@ pub trait AnalysisDomain<'tcx> {
     /// suitable as part of a filename.
     const NAME: &'static str;
 
-    /// The initial value of the dataflow state upon entry to each basic block.
+    /// Returns the initial value of the dataflow state upon entry to each basic block.
     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain;
 
     /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`.
     ///
-    /// For backward analyses, initial state besides the bottom value is not yet supported. Trying
+    /// For backward analyses, initial state (besides the bottom value) is not yet supported. Trying
     /// to mutate the initial state will result in a panic.
     //
     // FIXME: For backward dataflow analyses, the initial state should be applied to every basic
@@ -155,9 +155,9 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// Updates the current dataflow state with an effect that occurs immediately *before* the
     /// given statement.
     ///
-    /// This method is useful if the consumer of the results of this analysis needs only to observe
+    /// This method is useful if the consumer of the results of this analysis only needs to observe
     /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
-    /// analyses should not implement this without implementing `apply_statement_effect`.
+    /// analyses should not implement this without also implementing `apply_statement_effect`.
     fn apply_before_statement_effect(
         &self,
         _state: &mut Self::Domain,
@@ -184,7 +184,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     ///
     /// This method is useful if the consumer of the results of this analysis needs only to observe
     /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
-    /// analyses should not implement this without implementing `apply_terminator_effect`.
+    /// analyses should not implement this without also implementing `apply_terminator_effect`.
     fn apply_before_terminator_effect(
         &self,
         _state: &mut Self::Domain,
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index eae9313b771..f6b5af90a85 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -153,7 +153,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
         _: &mir::Statement<'tcx>,
         loc: Location,
     ) {
-        // If we move from a place then only stops needing storage *after*
+        // If we move from a place then it only stops needing storage *after*
         // that statement.
         self.check_for_move(trans, loc);
     }
diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs
index 10d52271734..f12c8560c0e 100644
--- a/compiler/rustc_mir_transform/src/add_call_guards.rs
+++ b/compiler/rustc_mir_transform/src/add_call_guards.rs
@@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards {
 impl AddCallGuards {
     pub fn add_call_guards(&self, body: &mut Body<'_>) {
         let mut pred_count: IndexVec<_, _> =
-            body.predecessors().iter().map(|ps| ps.len()).collect();
+            body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect();
         pred_count[START_BLOCK] += 1;
 
         // We need a place to store the new blocks generated
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 0f87e638d26..5d15f03491d 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -91,7 +91,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
         super::add_call_guards::AllCallEdges.run_pass(tcx, body);
 
         let (span, arg_count) = (body.span, body.arg_count);
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
+        let basic_blocks = body.basic_blocks.as_mut();
+        let local_decls = &body.local_decls;
         let needs_retag = |place: &Place<'tcx>| {
             // FIXME: Instead of giving up for unstable places, we should introduce
             // a temporary and retag on that.
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 510f1e64ed1..759ea7cd328 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -80,7 +80,7 @@ impl CoverageGraph {
         IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
         IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
     ) {
-        let num_basic_blocks = mir_body.num_nodes();
+        let num_basic_blocks = mir_body.basic_blocks.len();
         let mut bcbs = IndexVec::with_capacity(num_basic_blocks);
         let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);
 
@@ -95,7 +95,7 @@ impl CoverageGraph {
         let mut basic_blocks = Vec::new();
         for (bb, data) in mir_cfg_without_unwind {
             if let Some(last) = basic_blocks.last() {
-                let predecessors = &mir_body.predecessors()[bb];
+                let predecessors = &mir_body.basic_blocks.predecessors()[bb];
                 if predecessors.len() > 1 || !predecessors.contains(last) {
                     // The `bb` has more than one _incoming_ edge, and should start its own
                     // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 82070b90325..423e78317aa 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -321,7 +321,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
     }
 
     fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
-        let mut initial_spans = Vec::<CoverageSpan>::with_capacity(self.mir_body.num_nodes() * 2);
+        let mut initial_spans =
+            Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2);
         for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() {
             initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data));
         }
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index 213bb6608e1..6380f03528a 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -222,6 +222,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
                         bb,
                         debug::term_type(&data.terminator().kind),
                         mir_body
+                            .basic_blocks
                             .successors(bb)
                             .map(|successor| { format!("    {:?} -> {:?};", bb, successor) })
                             .join("\n")
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index c96497abf8f..9163672f570 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
         return;
     }
 
-    let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0;
+    let bbs = body.basic_blocks.as_mut_preserves_cfg();
     for Location { block, statement_index } in patch {
         bbs[block].statements[statement_index].make_nop();
     }
diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs
index 01f490e23bf..b93fe5879f4 100644
--- a/compiler/rustc_mir_transform/src/deaggregator.rs
+++ b/compiler/rustc_mir_transform/src/deaggregator.rs
@@ -11,9 +11,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let (basic_blocks, local_decls, _) =
-            body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
-        let local_decls = &*local_decls;
+        let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
         for bb in basic_blocks {
             bb.expand_statements(|stmt| {
                 // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
@@ -38,7 +36,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
                 Some(expand_aggregate(
                     lhs,
                     operands.into_iter().map(|op| {
-                        let ty = op.ty(local_decls, tcx);
+                        let ty = op.ty(&body.local_decls, tcx);
                         (op, ty)
                     }),
                     *kind,
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index a80d2fbd644..44e3945d6fc 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -110,13 +110,13 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
 
             let patch = MirPatch::new(body);
 
-            let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
+            let local_decls = &mut body.local_decls;
 
             let mut visitor =
                 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
 
             for (block, BasicBlockData { statements, terminator, .. }) in
-                basic_blocks.iter_enumerated_mut()
+                body.basic_blocks.as_mut().iter_enumerated_mut()
             {
                 let mut index = 0;
                 for statement in statements {
diff --git a/compiler/rustc_mir_transform/src/instcombine.rs b/compiler/rustc_mir_transform/src/instcombine.rs
index ea10ec5f25c..2f3c65869ef 100644
--- a/compiler/rustc_mir_transform/src/instcombine.rs
+++ b/compiler/rustc_mir_transform/src/instcombine.rs
@@ -16,9 +16,8 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
-        let ctx = InstCombineContext { tcx, local_decls };
-        for block in basic_blocks.iter_mut() {
+        let ctx = InstCombineContext { tcx, local_decls: &body.local_decls };
+        for block in body.basic_blocks.as_mut() {
             for statement in block.statements.iter_mut() {
                 match statement.kind {
                     StatementKind::Assign(box (_place, ref mut rvalue)) => {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 9776cd0ab8b..0887775aae5 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -173,7 +173,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
             intravisit::walk_struct_def(self, v)
         }
     }
-    tcx.hir().deep_visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set });
+    tcx.hir().visit_all_item_likes_in_crate(&mut GatherCtors { tcx, set: &mut set });
 
     set
 }
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 989b94b68c1..b7ba616510c 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -11,8 +11,8 @@ pub struct LowerIntrinsics;
 
 impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
-        for block in basic_blocks {
+        let local_decls = &body.local_decls;
+        for block in body.basic_blocks.as_mut() {
             let terminator = block.terminator.as_mut().unwrap();
             if let TerminatorKind::Call { func, args, destination, target, .. } =
                 &mut terminator.kind
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 813ab4001a7..47848cfa497 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -27,12 +27,10 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     };
 
     // The one successor remains unchanged, so no need to invalidate
-    let (basic_blocks, local_decls, _) =
-        body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
-
+    let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
     for block in basic_blocks {
         // lower `<[_]>::len` calls
-        lower_slice_len_call(tcx, block, &*local_decls, slice_len_fn_item_def_id);
+        lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 7cd7d26328a..a0ba69c89b0 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -48,7 +48,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
         let def_id = body.source.def_id();
         let param_env = tcx.param_env(def_id);
 
-        let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
+        let bbs = body.basic_blocks.as_mut();
         let mut should_cleanup = false;
         'outer: for bb_idx in bbs.indices() {
             if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) {
@@ -108,7 +108,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
 
             // Introduce a temporary for the discriminant value.
             let source_info = bbs[bb_idx].terminator().source_info;
-            let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span));
+            let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span));
 
             // We already checked that first and second are different blocks,
             // and bb_idx has a different terminator from both of them.
diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs
index 3396a446df2..c0217a10541 100644
--- a/compiler/rustc_mir_transform/src/normalize_array_len.rs
+++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs
@@ -33,8 +33,8 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
 
 pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     // We don't ever touch terminators, so no need to invalidate the CFG cache
-    let (basic_blocks, local_decls, _) =
-        body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
+    let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
+    let local_decls = &mut body.local_decls;
 
     // do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]`
     let mut interesting_locals = BitSet::new_empty(local_decls.len());
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index d29d17399af..bb063915f55 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -133,7 +133,7 @@ fn find_local_assigned_to_return_place(
             return local;
         }
 
-        match body.predecessors()[block].as_slice() {
+        match body.basic_blocks.predecessors()[block].as_slice() {
             &[pred] => block = pred,
             _ => return None,
         }
diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
index 5bb4f8bb9b3..dbe082e9093 100644
--- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs
+++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
@@ -17,7 +17,7 @@ impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers {
         }
 
         trace!("Running RemoveStorageMarkers on {:?}", body.source);
-        for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 {
+        for data in body.basic_blocks.as_mut_preserves_cfg() {
             data.statements.retain(|statement| match statement.kind {
                 StatementKind::StorageLive(..)
                 | StatementKind::StorageDead(..)
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 921a11a3a06..84ccf6e1f61 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -20,11 +20,10 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
         let param_env = tcx.param_env_reveal_all_normalized(did);
         let mut should_simplify = false;
 
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
-        for block in basic_blocks {
+        for block in body.basic_blocks.as_mut() {
             let terminator = block.terminator_mut();
             if let TerminatorKind::Drop { place, target, .. } = terminator.kind {
-                let ty = place.ty(local_decls, tcx);
+                let ty = place.ty(&body.local_decls, tcx);
                 if ty.ty.needs_drop(tcx, param_env) {
                     continue;
                 }
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 34941c1907d..40be4f146db 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -18,9 +18,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
             return;
         }
         let param_env = tcx.param_env(body.source.def_id());
-        let (basic_blocks, local_decls, _) =
-            body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
-        for block in basic_blocks.iter_mut() {
+        let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
+        let local_decls = &body.local_decls;
+        for block in basic_blocks {
             for statement in block.statements.iter_mut() {
                 if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) =
                     statement.kind
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 33ea1c4ba2f..194c2794aac 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -61,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
 /// Returns the amount of blocks that were duplicated
 pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
     let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
-    let predecessors = body.predecessors();
+    let predecessors = body.basic_blocks.predecessors();
     '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),
diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs
index 6902213ddad..fca9f7eeb24 100644
--- a/compiler/rustc_mir_transform/src/simplify_try.rs
+++ b/compiler/rustc_mir_transform/src/simplify_try.rs
@@ -386,14 +386,17 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
         trace!("running SimplifyArmIdentity on {:?}", source);
 
         let local_uses = LocalUseCounter::get_local_uses(body);
-        let (basic_blocks, local_decls, debug_info) =
-            body.basic_blocks_local_decls_mut_and_var_debug_info();
-        for bb in basic_blocks {
+        for bb in body.basic_blocks.as_mut() {
             if let Some(opt_info) =
-                get_arm_identity_info(&bb.statements, local_decls.len(), debug_info)
+                get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info)
             {
                 trace!("got opt_info = {:#?}", opt_info);
-                if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) {
+                if !optimization_applies(
+                    &opt_info,
+                    &body.local_decls,
+                    &local_uses,
+                    &body.var_debug_info,
+                ) {
                     debug!("optimization skipped for {:?}", source);
                     continue;
                 }
@@ -431,7 +434,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
 
                 // Fix the debug info to point to the right local
                 for dbg_index in opt_info.dbg_info_to_adjust {
-                    let dbg_info = &mut debug_info[dbg_index];
+                    let dbg_info = &mut body.var_debug_info[dbg_index];
                     assert!(
                         matches!(dbg_info.value, VarDebugInfoContents::Place(_)),
                         "value was not a Place"
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 00002f6f59b..67e6402c0ae 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1353,7 +1353,16 @@ impl<'a> Parser<'a> {
 
     /// Parses `extern string_literal?`.
     fn parse_extern(&mut self) -> Extern {
-        if self.eat_keyword(kw::Extern) { Extern::from_abi(self.parse_abi()) } else { Extern::None }
+        if self.eat_keyword(kw::Extern) {
+            let mut extern_span = self.prev_token.span;
+            let abi = self.parse_abi();
+            if let Some(abi) = abi {
+                extern_span = extern_span.to(abi.span);
+            }
+            Extern::from_abi(abi, extern_span)
+        } else {
+            Extern::None
+        }
     }
 
     /// Parses a string literal as an ABI spec.
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 8c123c052e5..d0723c68a77 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -2428,7 +2428,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
 
 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
     let check_attr_visitor = &mut CheckAttrVisitor { tcx };
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor);
+    tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
     if module_def_id.is_top_level_module() {
         check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 996ca66de0e..31c159c1f75 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -56,7 +56,7 @@ impl NonConstExpr {
 
 fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
     let mut vis = CheckConstVisitor::new(tcx);
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut vis);
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis);
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 23ff0a91159..9deb0042ff3 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -1,9 +1,9 @@
-use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lock;
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_hir::intravisit;
 use rustc_hir::{HirId, ItemLocalId};
+use rustc_index::bit_set::GrowableBitSet;
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
@@ -15,32 +15,35 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         crate::hir_stats::print_hir_stats(tcx);
     }
 
-    let errors = Lock::new(Vec::new());
-    let hir_map = tcx.hir();
+    #[cfg(debug_assertions)]
+    {
+        let errors = Lock::new(Vec::new());
+        let hir_map = tcx.hir();
 
-    hir_map.par_for_each_module(|module_id| {
-        let mut v = HirIdValidator {
-            hir_map,
-            owner: None,
-            hir_ids_seen: Default::default(),
-            errors: &errors,
-        };
+        hir_map.par_for_each_module(|module_id| {
+            let mut v = HirIdValidator {
+                hir_map,
+                owner: None,
+                hir_ids_seen: Default::default(),
+                errors: &errors,
+            };
 
-        tcx.hir().deep_visit_item_likes_in_module(module_id, &mut v);
-    });
+            tcx.hir().visit_item_likes_in_module(module_id, &mut v);
+        });
 
-    let errors = errors.into_inner();
+        let errors = errors.into_inner();
 
-    if !errors.is_empty() {
-        let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2);
-        tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, &message);
+        if !errors.is_empty() {
+            let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2);
+            tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, &message);
+        }
     }
 }
 
 struct HirIdValidator<'a, 'hir> {
     hir_map: Map<'hir>,
     owner: Option<LocalDefId>,
-    hir_ids_seen: FxHashSet<ItemLocalId>,
+    hir_ids_seen: GrowableBitSet<ItemLocalId>,
     errors: &'a Lock<Vec<String>>,
 }
 
@@ -80,7 +83,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
         if max != self.hir_ids_seen.len() - 1 {
             // Collect the missing ItemLocalIds
             let missing: Vec<_> = (0..=max as u32)
-                .filter(|&i| !self.hir_ids_seen.contains(&ItemLocalId::from_u32(i)))
+                .filter(|&i| !self.hir_ids_seen.contains(ItemLocalId::from_u32(i)))
                 .collect();
 
             // Try to map those to something more useful
@@ -106,7 +109,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
                     missing_items,
                     self.hir_ids_seen
                         .iter()
-                        .map(|&local_id| HirId { owner, local_id })
+                        .map(|local_id| HirId { owner, local_id })
                         .map(|h| format!("({:?} {})", h, self.hir_map.node_to_string(h)))
                         .collect::<Vec<_>>()
                 )
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 80a263f4cb2..0070c0699a4 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -140,7 +140,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
 }
 
 fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx));
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx));
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 9cfef26fd03..68b2a052391 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -31,7 +31,7 @@ struct CheckLoopVisitor<'a, 'hir> {
 }
 
 fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().deep_visit_item_likes_in_module(
+    tcx.hir().visit_item_likes_in_module(
         module_def_id,
         &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal },
     );
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 40844b84af0..20765abf392 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -14,7 +14,7 @@ use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx });
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx });
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 9eaefc8b8aa..12050dceb60 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -660,7 +660,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
 /// Cross-references the feature names of unstable APIs with enabled
 /// features and possibly prints errors.
 fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -890,7 +890,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         let mut missing = MissingStabilityAnnotations { tcx, access_levels };
         missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
         tcx.hir().walk_toplevel_module(&mut missing);
-        tcx.hir().deep_visit_all_item_likes(&mut missing);
+        tcx.hir().visit_all_item_likes_in_crate(&mut missing);
     }
 
     let declared_lang_features = &tcx.features().declared_lang_features;
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 341cf8f827b..3291717c550 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -886,8 +886,12 @@ impl<K: DepKind> DepGraph<K> {
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub struct WorkProduct {
     pub cgu_name: String,
-    /// Saved file associated with this CGU.
-    pub saved_file: String,
+    /// Saved files associated with this CGU. In each key/value pair, the value is the path to the
+    /// saved file and the key is some identifier for the type of file being saved.
+    ///
+    /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to
+    /// the object file's path, and "dwo" to the dwarf object file's path.
+    pub saved_files: FxHashMap<String, String>,
 }
 
 // Index type for `DepNodeData`'s edges.
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 0f58206eee9..ec9ae63ca36 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1499,10 +1499,16 @@ impl<'a> Resolver<'a> {
             && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind
             && let Some(span) = self.opt_span(def_id)
         {
-            err.span_help(
-                self.session.source_map().guess_head_span(span),
-                "consider adding `#[derive(Default)]` to this enum",
-            );
+            let source_map = self.session.source_map();
+            let head_span = source_map.guess_head_span(span);
+            if let Ok(head) = source_map.span_to_snippet(head_span) {
+                err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect);
+            } else {
+                err.span_help(
+                    head_span,
+                    "consider adding `#[derive(Default)]` to this enum",
+                );
+            }
         }
         for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
             if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index a0cbb7c2c5a..44b9c8392f8 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -59,7 +59,7 @@ struct OnlySelfBounds(bool);
 // Main entry point
 
 fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs
index a6b75493e6e..073168cf2d2 100644
--- a/library/std/src/os/windows/process.rs
+++ b/library/std/src/os/windows/process.rs
@@ -234,3 +234,26 @@ impl ChildExt for process::Child {
         self.handle.main_thread_handle()
     }
 }
+
+/// Windows-specific extensions to [`process::ExitCode`].
+///
+/// This trait is sealed: it cannot be implemented outside the standard library.
+/// This is so that future additional methods are not breaking changes.
+#[unstable(feature = "windows_process_exit_code_from", issue = "none")]
+pub trait ExitCodeExt: Sealed {
+    /// Creates a new `ExitCode` from the raw underlying `u32` return value of
+    /// a process.
+    ///
+    /// The exit code should not be 259, as this conflicts with the `STILL_ACTIVE`
+    /// macro returned from the `GetExitCodeProcess` function to signal that the
+    /// process has yet to run to completion.
+    #[unstable(feature = "windows_process_exit_code_from", issue = "none")]
+    fn from_raw(raw: u32) -> Self;
+}
+
+#[unstable(feature = "windows_process_exit_code_from", issue = "none")]
+impl ExitCodeExt for process::ExitCode {
+    fn from_raw(raw: u32) -> Self {
+        process::ExitCode::from_inner(From::from(raw))
+    }
+}
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index ab1a1e6c76f..d6cba7e7598 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1724,6 +1724,10 @@ impl crate::error::Error for ExitStatusError {}
 #[stable(feature = "process_exitcode", since = "1.61.0")]
 pub struct ExitCode(imp::ExitCode);
 
+/// Allows extension traits within `std`.
+#[unstable(feature = "sealed", issue = "none")]
+impl crate::sealed::Sealed for ExitCode {}
+
 #[stable(feature = "process_exitcode", since = "1.61.0")]
 impl ExitCode {
     /// The canonical `ExitCode` for successful termination on this platform.
@@ -1814,6 +1818,18 @@ impl From<u8> for ExitCode {
     }
 }
 
+impl AsInner<imp::ExitCode> for ExitCode {
+    fn as_inner(&self) -> &imp::ExitCode {
+        &self.0
+    }
+}
+
+impl FromInner<imp::ExitCode> for ExitCode {
+    fn from_inner(s: imp::ExitCode) -> ExitCode {
+        ExitCode(s)
+    }
+}
+
 impl Child {
     /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`]
     /// error is returned.
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 9fd399f4ba1..02d5af4719a 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -707,6 +707,12 @@ impl From<u8> for ExitCode {
     }
 }
 
+impl From<u32> for ExitCode {
+    fn from(code: u32) -> Self {
+        ExitCode(c::DWORD::from(code))
+    }
+}
+
 fn zeroed_startupinfo() -> c::STARTUPINFO {
     c::STARTUPINFO {
         cb: 0,
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index dc6a0f6f241..2e89abe202c 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -346,11 +346,7 @@ impl StepDescription {
             eprintln!(
                 "note: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
             );
-            #[cfg(not(test))]
-            std::process::exit(1);
-            #[cfg(test)]
-            // so we can use #[should_panic]
-            panic!()
+            crate::detail_exit(1);
         }
     }
 }
@@ -1008,7 +1004,7 @@ impl<'a> Builder<'a> {
             if !help_on_error.is_empty() {
                 eprintln!("{}", help_on_error);
             }
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
     }
 
@@ -1437,7 +1433,7 @@ impl<'a> Builder<'a> {
                         "error: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
                     );
                     eprintln!("help: try `rustup component add clippy`");
-                    std::process::exit(1);
+                    crate::detail_exit(1);
                 });
                 if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") {
                     rustflags.arg("--cfg=bootstrap");
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 399be26d5ac..ed5023ac61b 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -13,7 +13,7 @@ use std::fs;
 use std::io::prelude::*;
 use std::io::BufReader;
 use std::path::{Path, PathBuf};
-use std::process::{exit, Command, Stdio};
+use std::process::{Command, Stdio};
 use std::str;
 
 use serde::Deserialize;
@@ -1377,7 +1377,7 @@ pub fn run_cargo(
     });
 
     if !ok {
-        exit(1);
+        crate::detail_exit(1);
     }
 
     // Ok now we need to actually find all the files listed in `toplevel`. We've
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 2fc18c9e79e..e71fbff14dd 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -11,7 +11,7 @@ use std::ffi::OsStr;
 use std::fmt;
 use std::fs;
 use std::path::{Path, PathBuf};
-use std::process::{exit, Command};
+use std::process::Command;
 use std::str::FromStr;
 
 use crate::builder::{Builder, TaskPath};
@@ -805,8 +805,6 @@ impl Config {
         let get_toml = |_| TomlConfig::default();
         #[cfg(not(test))]
         let get_toml = |file: &Path| {
-            use std::process;
-
             let contents =
                 t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
             // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
@@ -817,7 +815,7 @@ impl Config {
                 Ok(table) => table,
                 Err(err) => {
                     eprintln!("failed to parse TOML configuration '{}': {}", file.display(), err);
-                    process::exit(2);
+                    crate::detail_exit(2);
                 }
             }
         };
@@ -1487,7 +1485,7 @@ fn download_ci_rustc_commit(
         println!("help: maybe your repository history is too shallow?");
         println!("help: consider disabling `download-rustc`");
         println!("help: or fetch enough history to include one upstream commit");
-        exit(1);
+        crate::detail_exit(1);
     }
 
     // Warn if there were changes to the compiler or standard library since the ancestor commit.
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 7ebae55efc1..eec19ab4fc9 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -4,7 +4,6 @@
 //! has various flags to configure how it's run.
 
 use std::path::PathBuf;
-use std::process;
 
 use getopts::Options;
 
@@ -261,7 +260,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
                 // subcommand.
                 println!("{}\n", subcommand_help);
                 let exit_code = if args.is_empty() { 0 } else { 1 };
-                process::exit(exit_code);
+                crate::detail_exit(exit_code);
             }
         };
 
@@ -347,7 +346,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
             } else if verbose {
                 panic!("No paths available for subcommand `{}`", subcommand.as_str());
             }
-            process::exit(exit_code);
+            crate::detail_exit(exit_code);
         };
 
         // Done specifying what options are possible, so do the getopts parsing
@@ -379,7 +378,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
                 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
                  You may need to move some options to after the subcommand.\n"
             );
-            process::exit(1);
+            crate::detail_exit(1);
         }
         // Extra help text for some commands
         match subcommand {
@@ -600,7 +599,7 @@ Arguments:
                         eprintln!("error: {}", err);
                         eprintln!("help: the available profiles are:");
                         eprint!("{}", Profile::all_for_help("- "));
-                        std::process::exit(1);
+                        crate::detail_exit(1);
                     })
                 } else {
                     t!(crate::setup::interactive_path())
@@ -614,7 +613,7 @@ Arguments:
                 || matches.opt_str("keep-stage-std").is_some()
             {
                 eprintln!("--keep-stage not yet supported for x.py check");
-                process::exit(1);
+                crate::detail_exit(1);
             }
         }
 
@@ -805,7 +804,7 @@ fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
         Some("warn") => Some(false),
         Some(value) => {
             eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);
-            process::exit(1);
+            crate::detail_exit(1);
         }
         None => None,
     }
diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
index 60a53c28686..f25977c1d46 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/format.rs
@@ -32,7 +32,7 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
                         code, run `./x.py fmt` instead.",
                 cmd_debug,
             );
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
     }
 }
@@ -114,7 +114,7 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
 
     let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| {
         eprintln!("./x.py fmt is not supported on this channel");
-        std::process::exit(1);
+        crate::detail_exit(1);
     });
     assert!(rustfmt_path.exists(), "{}", rustfmt_path.display());
     let src = build.src.clone();
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 73bd588472d..82025efcbe0 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -109,7 +109,7 @@ use std::env;
 use std::fs::{self, File};
 use std::io;
 use std::path::{Path, PathBuf};
-use std::process::{self, Command};
+use std::process::Command;
 use std::str;
 
 use filetime::FileTime;
@@ -711,7 +711,7 @@ impl Build {
             for failure in failures.iter() {
                 eprintln!("  - {}\n", failure);
             }
-            process::exit(1);
+            detail_exit(1);
         }
 
         #[cfg(feature = "build-metrics")]
@@ -1617,7 +1617,7 @@ Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
 to download LLVM rather than building it.
 "
                 );
-                std::process::exit(1);
+                detail_exit(1);
             }
         }
 
@@ -1646,6 +1646,20 @@ fn chmod(path: &Path, perms: u32) {
 #[cfg(windows)]
 fn chmod(_path: &Path, _perms: u32) {}
 
+/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
+/// If the test is running and code is an error code, it will cause a panic.
+fn detail_exit(code: i32) -> ! {
+    // Successful exit
+    if code == 0 {
+        std::process::exit(0);
+    }
+    if cfg!(test) {
+        panic!("status code: {}", code);
+    } else {
+        std::panic::resume_unwind(Box::new(code));
+    }
+}
+
 impl Compiler {
     pub fn with_stage(mut self, stage: u32) -> Compiler {
         self.stage = stage;
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 64c5dd7aea7..cae41286f08 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -104,7 +104,7 @@ You should install cmake, or set `download-ci-llvm = true` in the
 than building it.
 "
             );
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
     }
 
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs
index 82f55440ce5..740c12ed725 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/setup.rs
@@ -94,7 +94,7 @@ pub fn setup(config: &Config, profile: Profile) {
             "note: this will use the configuration in {}",
             profile.include_path(&config.src).display()
         );
-        std::process::exit(1);
+        crate::detail_exit(1);
     }
 
     let settings = format!(
@@ -287,7 +287,7 @@ pub fn interactive_path() -> io::Result<Profile> {
         io::stdin().read_line(&mut input)?;
         if input.is_empty() {
             eprintln!("EOF on stdin, when expecting answer to question.  Giving up.");
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
         break match parse_with_abbrev(&input) {
             Ok(profile) => profile,
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 6be3da55291..4c6b5ba0afc 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -673,7 +673,7 @@ impl Step for Clippy {
         }
 
         if !builder.config.cmd.bless() {
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
 
         let mut cargo = builder.cargo(compiler, Mode::ToolRustc, SourceType::InTree, host, "run");
@@ -1021,7 +1021,7 @@ help: to skip test's attempt to check tidiness, pass `--exclude src/tools/tidy`
                     PATH = inferred_rustfmt_dir.display(),
                     CHAN = builder.config.channel,
                 );
-                std::process::exit(1);
+                crate::detail_exit(1);
             }
             crate::format::format(&builder, !builder.config.cmd.bless(), &[]);
         }
@@ -1251,7 +1251,7 @@ help: to test the compiler, use `--stage 1` instead
 help: to test the standard library, use `--stage 0 library/std` instead
 note: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
             );
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
 
         let compiler = self.compiler;
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 74a793d257e..23832b6c43e 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -2,7 +2,7 @@ use std::collections::HashSet;
 use std::env;
 use std::fs;
 use std::path::{Path, PathBuf};
-use std::process::{exit, Command};
+use std::process::Command;
 
 use crate::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
 use crate::channel::GitInfo;
@@ -204,7 +204,7 @@ impl Step for ToolBuild {
 
         if !is_expected {
             if !is_optional_tool {
-                exit(1);
+                crate::detail_exit(1);
             } else {
                 None
             }
diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs
index 3ee6a42d987..2cfeae7dc78 100644
--- a/src/bootstrap/toolstate.rs
+++ b/src/bootstrap/toolstate.rs
@@ -93,7 +93,7 @@ fn print_error(tool: &str, submodule: &str) {
     eprintln!("If you do NOT intend to update '{}', please ensure you did not accidentally", tool);
     eprintln!("change the submodule at '{}'. You may ask your reviewer for the", submodule);
     eprintln!("proper steps.");
-    std::process::exit(3);
+    crate::detail_exit(3);
 }
 
 fn check_changed_files(toolstates: &HashMap<Box<str>, ToolState>) {
@@ -108,7 +108,7 @@ fn check_changed_files(toolstates: &HashMap<Box<str>, ToolState>) {
         Ok(o) => o,
         Err(e) => {
             eprintln!("Failed to get changed files: {:?}", e);
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
     };
 
@@ -179,7 +179,7 @@ impl Step for ToolStateCheck {
         }
 
         if did_error {
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
 
         check_changed_files(&toolstates);
@@ -225,7 +225,7 @@ impl Step for ToolStateCheck {
         }
 
         if did_error {
-            std::process::exit(1);
+            crate::detail_exit(1);
         }
 
         if builder.config.channel == "nightly" && env::var_os("TOOLSTATE_PUBLISH").is_some() {
diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs
index 6f4266a7f29..b627e503789 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/util.rs
@@ -336,7 +336,7 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
 
 pub fn run(cmd: &mut Command, print_cmd_on_fail: bool) {
     if !try_run(cmd, print_cmd_on_fail) {
-        std::process::exit(1);
+        crate::detail_exit(1);
     }
 }
 
@@ -375,7 +375,7 @@ pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
 
 pub fn run_suppressed(cmd: &mut Command) {
     if !try_run_suppressed(cmd) {
-        std::process::exit(1);
+        crate::detail_exit(1);
     }
 }
 
@@ -465,7 +465,7 @@ fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
 
 fn fail(s: &str) -> ! {
     eprintln!("\n\n{}\n\n", s);
-    std::process::exit(1);
+    crate::detail_exit(1);
 }
 
 /// Copied from `std::path::absolute` until it stabilizes.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 4d33b7a376a..efa73a79e99 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1705,8 +1705,8 @@ fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option<DefI
             ImplTrait(bounds)
         }
 
-        ty::Closure(..) | ty::Generator(..) => Tuple(vec![]), // FIXME(pcwalton)
-
+        ty::Closure(..) => panic!("Closure"),
+        ty::Generator(..) => panic!("Generator"),
         ty::Bound(..) => panic!("Bound"),
         ty::Placeholder(..) => panic!("Placeholder"),
         ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
@@ -1760,7 +1760,6 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     match tcx.def_kind(parent) {
         DefKind::Struct | DefKind::Union => false,
         DefKind::Variant => true,
-        // FIXME: what about DefKind::Ctor?
         parent_kind => panic!("unexpected parent kind: {:?}", parent_kind),
     }
 }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 04638aa1af6..7ec6eb0be65 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -237,9 +237,6 @@ pub(crate) struct RenderOptions {
     pub(crate) resource_suffix: String,
     /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
     /// default.
-    //
-    // FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted
-    // once read.
     pub(crate) enable_minification: bool,
     /// Whether to create an index page in the root of the output directory. If this is true but
     /// `enable_index_page` is None, generate a static listing of crates instead.
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e62a8bcfba6..796bd771518 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1412,7 +1412,10 @@ fn render_impl(
                         id, item_type, in_trait_class,
                     );
                     render_rightside(w, cx, item, containing_item, render_mode);
-                    write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                    if trait_.is_some() {
+                        // Anchors are only used on trait impls.
+                        write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                    }
                     w.write_str("<h4 class=\"code-header\">");
                     render_assoc_item(
                         w,
@@ -1435,7 +1438,10 @@ fn render_impl(
                     id, item_type, in_trait_class
                 );
                 render_rightside(w, cx, item, containing_item, render_mode);
-                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                if trait_.is_some() {
+                    // Anchors are only used on trait impls.
+                    write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                }
                 w.write_str("<h4 class=\"code-header\">");
                 assoc_const(
                     w,
@@ -1457,7 +1463,10 @@ fn render_impl(
                 let source_id = format!("{}.{}", item_type, name);
                 let id = cx.derive_id(source_id.clone());
                 write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
-                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                if trait_.is_some() {
+                    // Anchors are only used on trait impls.
+                    write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                }
                 w.write_str("<h4 class=\"code-header\">");
                 assoc_type(
                     w,
@@ -1480,7 +1489,10 @@ fn render_impl(
                     "<section id=\"{}\" class=\"{}{} has-srclink\">",
                     id, item_type, in_trait_class
                 );
-                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                if trait_.is_some() {
+                    // Anchors are only used on trait impls.
+                    write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                }
                 w.write_str("<h4 class=\"code-header\">");
                 assoc_type(
                     w,
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index f6c599297fc..c0fe8b49cfd 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -303,7 +303,7 @@ pub(crate) fn run(
         // Run call-finder on all items
         let mut calls = FxHashMap::default();
         let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
-        tcx.hir().deep_visit_all_item_likes(&mut finder);
+        tcx.hir().visit_all_item_likes_in_crate(&mut finder);
 
         // Sort call locations within a given file in document order
         for fn_calls in calls.values_mut() {
diff --git a/src/test/incremental/split_debuginfo_cached.rs b/src/test/incremental/split_debuginfo_cached.rs
new file mode 100644
index 00000000000..25c802d5a1d
--- /dev/null
+++ b/src/test/incremental/split_debuginfo_cached.rs
@@ -0,0 +1,25 @@
+// Check that compiling with packed Split DWARF twice succeeds. This should confirm that DWARF
+// objects are cached as work products and available to the incremental compilation for `thorin` to
+// pack into a DWARF package.
+
+// ignore-tidy-linelength
+// only-x86_64-unknown-linux-gnu
+// revisions:rpass1 rpass2
+
+// [rpass1]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
+// [rpass2]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
+
+#![feature(rustc_attrs)]
+// For `rpass2`, nothing has changed so everything should re-used.
+#![rustc_partition_reused(module = "split_debuginfo_cached", cfg = "rpass2")]
+#![rustc_partition_reused(module = "split_debuginfo_cached-another_module", cfg = "rpass2")]
+
+mod another_module {
+    pub fn foo() -> &'static str {
+        "hello world"
+    }
+}
+
+pub fn main() {
+    println!("{}", another_module::foo());
+}
diff --git a/src/test/rustdoc/anchors.no_const_anchor.html b/src/test/rustdoc/anchors.no_const_anchor.html
new file mode 100644
index 00000000000..98f47e53038
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_const_anchor.html
@@ -0,0 +1 @@
+<div id="associatedconstant.YOLO" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#16">source</a></div><h4 class="code-header">const <a href="#associatedconstant.YOLO" class="constant">YOLO</a>: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></div>
diff --git a/src/test/rustdoc/anchors.no_const_anchor2.html b/src/test/rustdoc/anchors.no_const_anchor2.html
new file mode 100644
index 00000000000..6d37e8e5eee
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_const_anchor2.html
@@ -0,0 +1 @@
+<section id="associatedconstant.X" class="associatedconstant has-srclink"><span class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#42">source</a></span><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0i32</h4></section>
diff --git a/src/test/rustdoc/anchors.no_method_anchor.html b/src/test/rustdoc/anchors.no_method_anchor.html
new file mode 100644
index 00000000000..f46d3090ed3
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_method_anchor.html
@@ -0,0 +1 @@
+<section id="method.new" class="method has-srclink"><span class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#48">source</a></span><h4 class="code-header">pub fn <a href="#method.new" class="fnname">new</a>() -&gt; Self</h4></section>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_trait_method_anchor.html b/src/test/rustdoc/anchors.no_trait_method_anchor.html
new file mode 100644
index 00000000000..445a7bb560a
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_trait_method_anchor.html
@@ -0,0 +1 @@
+<div id="method.bar" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#23">source</a></div><h4 class="code-header">fn <a href="#method.bar" class="fnname">bar</a>()</h4></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_tymethod_anchor.html b/src/test/rustdoc/anchors.no_tymethod_anchor.html
new file mode 100644
index 00000000000..bb0771b1003
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_tymethod_anchor.html
@@ -0,0 +1 @@
+<div id="tymethod.foo" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#20">source</a></div><h4 class="code-header">fn <a href="#tymethod.foo" class="fnname">foo</a>()</h4></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_type_anchor.html b/src/test/rustdoc/anchors.no_type_anchor.html
new file mode 100644
index 00000000000..d317eb50050
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_type_anchor.html
@@ -0,0 +1 @@
+<div id="associatedtype.T" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#13">source</a></div><h4 class="code-header">type <a href="#associatedtype.T" class="associatedtype">T</a></h4></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_type_anchor2.html b/src/test/rustdoc/anchors.no_type_anchor2.html
new file mode 100644
index 00000000000..72a1186bf7e
--- /dev/null
+++ b/src/test/rustdoc/anchors.no_type_anchor2.html
@@ -0,0 +1 @@
+<section id="associatedtype.Y" class="associatedtype has-srclink"><h4 class="code-header">type <a href="#associatedtype.Y" class="associatedtype">Y</a> = <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
diff --git a/src/test/rustdoc/anchors.rs b/src/test/rustdoc/anchors.rs
new file mode 100644
index 00000000000..034cf8eaf4f
--- /dev/null
+++ b/src/test/rustdoc/anchors.rs
@@ -0,0 +1,49 @@
+// This test ensures that anchors are generated in the right places.
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+#![crate_name = "foo"]
+
+pub struct Foo;
+
+// @has 'foo/trait.Bar.html'
+pub trait Bar {
+    // There should be no anchors here.
+    // @snapshot no_type_anchor - '//*[@id="associatedtype.T"]'
+    type T;
+    // There should be no anchors here.
+    // @snapshot no_const_anchor - '//*[@id="associatedconstant.YOLO"]'
+    const YOLO: u32;
+
+    // There should be no anchors here.
+    // @snapshot no_tymethod_anchor - '//*[@id="tymethod.foo"]'
+    fn foo();
+    // There should be no anchors here.
+    // @snapshot no_trait_method_anchor - '//*[@id="method.bar"]'
+    fn bar() {}
+}
+
+// @has 'foo/struct.Foo.html'
+impl Bar for Foo {
+    // @has - '//*[@id="associatedtype.T"]/a[@class="anchor"]' ''
+    type T = u32;
+    // @has - '//*[@id="associatedconstant.YOLO"]/a[@class="anchor"]' ''
+    const YOLO: u32 = 0;
+
+    // @has - '//*[@id="method.foo"]/a[@class="anchor"]' ''
+    fn foo() {}
+    // Same check for provided "bar" method.
+    // @has - '//*[@id="method.bar"]/a[@class="anchor"]' ''
+}
+
+impl Foo {
+    // @snapshot no_const_anchor2 - '//*[@id="associatedconstant.X"]'
+    // There should be no anchors here.
+    pub const X: i32 = 0;
+    // @snapshot no_type_anchor2 - '//*[@id="associatedtype.Y"]'
+    // There should be no anchors here.
+    pub type Y = u32;
+    // @snapshot no_method_anchor - '//*[@id="method.new"]'
+    // There should be no anchors here.
+    pub fn new() -> Self { Self }
+}
diff --git a/src/test/ui/const-generics/issue-80471.rs b/src/test/ui/const-generics/issue-80471.rs
new file mode 100644
index 00000000000..d0af8a5eaa8
--- /dev/null
+++ b/src/test/ui/const-generics/issue-80471.rs
@@ -0,0 +1,13 @@
+#![feature(adt_const_params)]
+//~^ WARN the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
+
+#[derive(PartialEq, Eq)]
+enum Nat {
+    Z,
+    S(Box<Nat>),
+}
+
+fn foo<const N: Nat>() {}
+//~^ ERROR `Box<Nat>` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter
+
+fn main() {}
diff --git a/src/test/ui/const-generics/issue-80471.stderr b/src/test/ui/const-generics/issue-80471.stderr
new file mode 100644
index 00000000000..dbcc0b7b600
--- /dev/null
+++ b/src/test/ui/const-generics/issue-80471.stderr
@@ -0,0 +1,18 @@
+warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-80471.rs:1:12
+   |
+LL | #![feature(adt_const_params)]
+   |            ^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
+
+error[E0741]: `Box<Nat>` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter
+  --> $DIR/issue-80471.rs:10:17
+   |
+LL | fn foo<const N: Nat>() {}
+   |                 ^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/dep-graph/dep-graph-struct-signature.stderr b/src/test/ui/dep-graph/dep-graph-struct-signature.stderr
index 60bfbe94a8a..cfe1e62d931 100644
--- a/src/test/ui/dep-graph/dep-graph-struct-signature.stderr
+++ b/src/test/ui/dep-graph/dep-graph-struct-signature.stderr
@@ -17,12 +17,6 @@ LL |     #[rustc_then_this_would_need(trait_def)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: OK
-  --> $DIR/dep-graph-struct-signature.rs:32:9
-   |
-LL |         #[rustc_then_this_would_need(fn_sig)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
   --> $DIR/dep-graph-struct-signature.rs:36:5
    |
 LL |     #[rustc_then_this_would_need(fn_sig)]
@@ -53,36 +47,12 @@ LL |     #[rustc_then_this_would_need(type_of)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: OK
-  --> $DIR/dep-graph-struct-signature.rs:48:9
-   |
-LL |         #[rustc_then_this_would_need(fn_sig)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
-  --> $DIR/dep-graph-struct-signature.rs:49:9
-   |
-LL |         #[rustc_then_this_would_need(typeck)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
   --> $DIR/dep-graph-struct-signature.rs:53:5
    |
 LL |     #[rustc_then_this_would_need(type_of)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: OK
-  --> $DIR/dep-graph-struct-signature.rs:55:9
-   |
-LL |         #[rustc_then_this_would_need(fn_sig)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
-  --> $DIR/dep-graph-struct-signature.rs:56:9
-   |
-LL |         #[rustc_then_this_would_need(typeck)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
   --> $DIR/dep-graph-struct-signature.rs:61:9
    |
 LL |         #[rustc_then_this_would_need(type_of)]
@@ -107,12 +77,6 @@ LL |     #[rustc_then_this_would_need(type_of)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: no path from `WillChange` to `fn_sig`
-  --> $DIR/dep-graph-struct-signature.rs:77:9
-   |
-LL |         #[rustc_then_this_would_need(fn_sig)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: no path from `WillChange` to `fn_sig`
   --> $DIR/dep-graph-struct-signature.rs:81:5
    |
 LL |     #[rustc_then_this_would_need(fn_sig)]
@@ -130,5 +94,41 @@ error: no path from `WillChange` to `typeck`
 LL |     #[rustc_then_this_would_need(typeck)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: OK
+  --> $DIR/dep-graph-struct-signature.rs:32:9
+   |
+LL |         #[rustc_then_this_would_need(fn_sig)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: no path from `WillChange` to `fn_sig`
+  --> $DIR/dep-graph-struct-signature.rs:77:9
+   |
+LL |         #[rustc_then_this_would_need(fn_sig)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-struct-signature.rs:48:9
+   |
+LL |         #[rustc_then_this_would_need(fn_sig)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-struct-signature.rs:49:9
+   |
+LL |         #[rustc_then_this_would_need(typeck)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-struct-signature.rs:55:9
+   |
+LL |         #[rustc_then_this_would_need(fn_sig)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-struct-signature.rs:56:9
+   |
+LL |         #[rustc_then_this_would_need(typeck)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: aborting due to 22 previous errors
 
diff --git a/src/test/ui/dep-graph/dep-graph-type-alias.stderr b/src/test/ui/dep-graph/dep-graph-type-alias.stderr
index c59cf8014c3..42ac803b22e 100644
--- a/src/test/ui/dep-graph/dep-graph-type-alias.stderr
+++ b/src/test/ui/dep-graph/dep-graph-type-alias.stderr
@@ -28,12 +28,6 @@ error: no path from `TypeAlias` to `type_of`
 LL | #[rustc_then_this_would_need(type_of)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: OK
-  --> $DIR/dep-graph-type-alias.rs:36:5
-   |
-LL |     #[rustc_then_this_would_need(fn_sig)]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: no path from `TypeAlias` to `type_of`
   --> $DIR/dep-graph-type-alias.rs:42:1
    |
@@ -41,18 +35,6 @@ LL | #[rustc_then_this_would_need(type_of)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: OK
-  --> $DIR/dep-graph-type-alias.rs:44:5
-   |
-LL |     #[rustc_then_this_would_need(fn_sig)]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
-  --> $DIR/dep-graph-type-alias.rs:45:5
-   |
-LL |     #[rustc_then_this_would_need(typeck)]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: OK
   --> $DIR/dep-graph-type-alias.rs:49:1
    |
 LL | #[rustc_then_this_would_need(type_of)]
@@ -70,5 +52,23 @@ error: OK
 LL | #[rustc_then_this_would_need(typeck)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: OK
+  --> $DIR/dep-graph-type-alias.rs:36:5
+   |
+LL |     #[rustc_then_this_would_need(fn_sig)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-type-alias.rs:44:5
+   |
+LL |     #[rustc_then_this_would_need(fn_sig)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: OK
+  --> $DIR/dep-graph-type-alias.rs:45:5
+   |
+LL |     #[rustc_then_this_would_need(typeck)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/enum/suggest-default-attribute.rs b/src/test/ui/enum/suggest-default-attribute.rs
index 33bd0d24081..1d7567e60a5 100644
--- a/src/test/ui/enum/suggest-default-attribute.rs
+++ b/src/test/ui/enum/suggest-default-attribute.rs
@@ -1,4 +1,4 @@
-pub enum Test { //~ HELP consider adding `#[derive(Default)]` to this enum
+pub enum Test { //~ HELP consider adding a derive
     #[default]
     //~^ ERROR cannot find attribute `default` in this scope
     First,
diff --git a/src/test/ui/enum/suggest-default-attribute.stderr b/src/test/ui/enum/suggest-default-attribute.stderr
index 791f219e8f9..fb830d3f78b 100644
--- a/src/test/ui/enum/suggest-default-attribute.stderr
+++ b/src/test/ui/enum/suggest-default-attribute.stderr
@@ -4,11 +4,11 @@ error: cannot find attribute `default` in this scope
 LL |     #[default]
    |       ^^^^^^^
    |
-help: consider adding `#[derive(Default)]` to this enum
-  --> $DIR/suggest-default-attribute.rs:1:1
+help: consider adding a derive
+   |
+LL + #[derive(Default)]
+LL ~ pub enum Test {
    |
-LL | pub enum Test {
-   | ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/extern/not-in-block.rs b/src/test/ui/extern/not-in-block.rs
new file mode 100644
index 00000000000..d3bcafdef7b
--- /dev/null
+++ b/src/test/ui/extern/not-in-block.rs
@@ -0,0 +1,6 @@
+#![crate_type = "lib"]
+
+extern fn none_fn(x: bool) -> i32;
+//~^ ERROR free function without a body
+extern "C" fn c_fn(x: bool) -> i32;
+//~^ ERROR free function without a body
diff --git a/src/test/ui/extern/not-in-block.stderr b/src/test/ui/extern/not-in-block.stderr
new file mode 100644
index 00000000000..2544949ab17
--- /dev/null
+++ b/src/test/ui/extern/not-in-block.stderr
@@ -0,0 +1,32 @@
+error: free function without a body
+  --> $DIR/not-in-block.rs:3:1
+   |
+LL | extern fn none_fn(x: bool) -> i32;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: provide a definition for the function
+   |
+LL | extern fn none_fn(x: bool) -> i32 { <body> }
+   |                                   ~~~~~~~~~~
+help: if you meant to declare an externally defined function, use an `extern` block
+   |
+LL | extern { fn none_fn(x: bool) -> i32; }
+   | ~~~~~~~~                             +
+
+error: free function without a body
+  --> $DIR/not-in-block.rs:5:1
+   |
+LL | extern "C" fn c_fn(x: bool) -> i32;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: provide a definition for the function
+   |
+LL | extern "C" fn c_fn(x: bool) -> i32 { <body> }
+   |                                    ~~~~~~~~~~
+help: if you meant to declare an externally defined function, use an `extern` block
+   |
+LL | extern "C" { fn c_fn(x: bool) -> i32; }
+   | ~~~~~~~~~~~~                          +
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr
index ff78c252731..9322654b292 100644
--- a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr
+++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr
@@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/intrinsic-raw_eq-const-padding.rs:6:5
    |
 LL |     std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16))
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc3, but 1 byte is uninitialized starting at alloc3+0x1, and this operation requires initialized memory
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at alloc3[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/mir/ssa-analysis-regression-50041.rs b/src/test/ui/mir/ssa-analysis-regression-50041.rs
index 8e9c14a03c3..ebc3e2f8c0e 100644
--- a/src/test/ui/mir/ssa-analysis-regression-50041.rs
+++ b/src/test/ui/mir/ssa-analysis-regression-50041.rs
@@ -5,7 +5,7 @@
 #![feature(lang_items)]
 #![no_std]
 
-struct NonNull<T: ?Sized>(*mut T);
+struct NonNull<T: ?Sized>(*const T);
 
 struct Unique<T: ?Sized>(NonNull<T>);
 
@@ -23,7 +23,7 @@ unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
 }
 
 #[inline(never)]
-fn dealloc<T: ?Sized>(_: *mut T) {}
+fn dealloc<T: ?Sized>(_: *const T) {}
 
 pub struct Foo<T>(T);
 
diff --git a/src/test/ui/stability-attribute/missing-const-stability.stderr b/src/test/ui/stability-attribute/missing-const-stability.stderr
index 6f2ade0d0ab..10978728fa3 100644
--- a/src/test/ui/stability-attribute/missing-const-stability.stderr
+++ b/src/test/ui/stability-attribute/missing-const-stability.stderr
@@ -4,12 +4,6 @@ error: function has missing const stability attribute
 LL | pub const fn foo() {}
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: associated function has missing const stability attribute
-  --> $DIR/missing-const-stability.rs:15:5
-   |
-LL |     pub const fn foo() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^
-
 error: implementation has missing const stability attribute
   --> $DIR/missing-const-stability.rs:27:1
    |
@@ -19,5 +13,11 @@ LL | |     fn fun() {}
 LL | | }
    | |_^
 
+error: associated function has missing const stability attribute
+  --> $DIR/missing-const-stability.rs:15:5
+   |
+LL |     pub const fn foo() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
index c6c7b959d4f..4f49bb879f5 100644
--- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
+++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
 use rustc_errors::MultiSpan;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{FileName, Span};
 use std::collections::BTreeMap;
@@ -49,6 +49,7 @@ declare_clippy_lint! {
 struct Modules {
     local_path: PathBuf,
     spans: Vec<Span>,
+    lint_levels: Vec<Level>,
 }
 
 #[derive(Default)]
@@ -70,13 +71,30 @@ impl EarlyLintPass for DuplicateMod {
             let modules = self.modules.entry(absolute_path).or_insert(Modules {
                 local_path,
                 spans: Vec::new(),
+                lint_levels: Vec::new(),
             });
             modules.spans.push(item.span_with_attributes());
+            modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD));
         }
     }
 
     fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
-        for Modules { local_path, spans } in self.modules.values() {
+        for Modules { local_path, spans, lint_levels } in self.modules.values() {
+            if spans.len() < 2 {
+                continue;
+            }
+
+            // At this point the lint would be emitted
+            assert_eq!(spans.len(), lint_levels.len());
+            let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{
+                if let Some(id) = lvl.get_expectation_id() {
+                    cx.fulfill_expectation(id);
+                }
+
+                (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
+            })
+            .collect();
+
             if spans.len() < 2 {
                 continue;
             }
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index f7a92bc0795..453471c8cdd 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -94,7 +94,7 @@ impl ExcessiveBools {
 
     fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
         match fn_sig.header.ext {
-            Extern::Implicit | Extern::Explicit(_) => return,
+            Extern::Implicit(_) | Extern::Explicit(_, _) => return,
             Extern::None => (),
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index 6d0b9a0f03f..eddca604575 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -161,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                 // `arg` is a reference as it is `.deref()`ed in the previous block.
                 // Look into the predecessor block and find out the source of deref.
 
-                let ps = &mir.predecessors()[bb];
+                let ps = &mir.basic_blocks.predecessors()[bb];
                 if ps.len() != 1 {
                     continue;
                 }
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 6487199172e..177e754ee09 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -600,8 +600,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
 pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
     use Extern::*;
     match (l, r) {
-        (None, None) | (Implicit, Implicit) => true,
-        (Explicit(l), Explicit(r)) => eq_str_lit(l, r),
+        (None, None) | (Implicit(_), Implicit(_)) => true,
+        (Explicit(l,_), Explicit(r,_)) => eq_str_lit(l, r),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
index 79b343da247..99ca538b6e4 100644
--- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
@@ -1,3 +1,5 @@
+#[feature(lint_reasons)]
+
 mod a;
 
 mod b;
@@ -13,4 +15,15 @@ mod c3;
 mod from_other_module;
 mod other_module;
 
+mod d;
+#[path = "d.rs"]
+mod d2;
+#[path = "d.rs"]
+#[expect(clippy::duplicate_mod)]
+mod d3;
+#[path = "d.rs"]
+#[allow(clippy::duplicate_mod)]
+mod d4;
+
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
index 00d7739c8a2..61df1ad5d50 100644
--- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
@@ -1,5 +1,5 @@
 error: file is loaded as a module multiple times: `$DIR/b.rs`
-  --> $DIR/main.rs:3:1
+  --> $DIR/main.rs:5:1
    |
 LL |   mod b;
    |   ^^^^^^ first loaded here
@@ -11,7 +11,7 @@ LL | | mod b2;
    = help: replace all but one `mod` item with `use` items
 
 error: file is loaded as a module multiple times: `$DIR/c.rs`
-  --> $DIR/main.rs:7:1
+  --> $DIR/main.rs:9:1
    |
 LL |   mod c;
    |   ^^^^^^ first loaded here
@@ -25,7 +25,7 @@ LL | | mod c3;
    = help: replace all but one `mod` item with `use` items
 
 error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
-  --> $DIR/main.rs:13:1
+  --> $DIR/main.rs:15:1
    |
 LL |   mod from_other_module;
    |   ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
@@ -38,5 +38,16 @@ LL | | mod m;
    |
    = help: replace all but one `mod` item with `use` items
 
-error: aborting due to 3 previous errors
+error: file is loaded as a module multiple times: `$DIR/b.rs`
+  --> $DIR/main.rs:18:1
+   |
+LL |   mod d;
+   |   ^^^^^^ first loaded here
+LL | / #[path = "d.rs"]
+LL | | mod d2;
+   | |_______^ loaded again here
+   |
+   = help: replace all but one `mod` item with `use` items
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index bab881f4b4e..8f35068e35f 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -148,7 +148,7 @@ impl<'a> Item<'a> {
         Item {
             unsafety: fm.unsafety,
             abi: format_extern(
-                ast::Extern::from_abi(fm.abi),
+                ast::Extern::from_abi(fm.abi, DUMMY_SP),
                 config.force_explicit_abi(),
                 true,
             ),
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 58fd95c656e..4b26f4e40df 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -138,8 +138,8 @@ pub(crate) fn format_extern(
 ) -> Cow<'static, str> {
     let abi = match ext {
         ast::Extern::None => "Rust".to_owned(),
-        ast::Extern::Implicit => "C".to_owned(),
-        ast::Extern::Explicit(abi) => abi.symbol_unescaped.to_string(),
+        ast::Extern::Implicit(_) => "C".to_owned(),
+        ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(),
     };
 
     if abi == "Rust" && !is_mod {